diff options
Diffstat (limited to 'npc')
1624 files changed, 122977 insertions, 0 deletions
diff --git a/npc/000-0-0/_import.txt b/npc/000-0-0/_import.txt new file mode 100644 index 0000000..5c84e0f --- /dev/null +++ b/npc/000-0-0/_import.txt @@ -0,0 +1,5 @@ +// Map 000-0-0: Training Island +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/000-0-0/chest.txt", +"npc/000-0-0/mapflags.txt", +"npc/000-0-0/sailors.txt", diff --git a/npc/000-0-0/chest.txt b/npc/000-0-0/chest.txt new file mode 100644 index 0000000..fdd2873 --- /dev/null +++ b/npc/000-0-0/chest.txt @@ -0,0 +1,69 @@ +// TMW2 Script modified by Jesusalva +// Evol scripts. +// Authors: +// 4144 +// gumi +// omatt +// Reid +// Description: +// A box with clothes for new players. +// Variable: +// ShipQuests_Arpan +// Values: +// 1 Talked to Arpan and needs to get clothes. +// 2 Has the clothes. + +000-0-0,36,34,0 script Chest#002-1 NPC_CHEST_BIG,2,4,{ + + .@questState = getq(ShipQuests_Arpan); + + if (.@questState == 0) { + npctalk3 l("You should talk to Magic Arpan first."); + end; + } + + if (.busy == false) + { + if (.@questState == 1) { + inventoryplace CreasedShirt, 2; + setq1 ShipQuests_Arpan, 2; + getitem CreasedShirt, 1; + getitem CreasedShorts, 1; + npctalk3 l("You take the clothes from the chest."); + } + + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + .dir = .dir == 0 ? 2 : 6; // closed ? opening : closing + .busy = true; // lock the animation + initnpctimer; + } + end; + +OnTimer220: + .dir = .dir == 6 ? 0 : 4; // closing ? closed : open + end; + +OnTimer500: + .busy = false; // unlock + + if (.dir == 0) { + stopnpctimer; // stop here if the chest is closed + } + end; + +OnUnTouch: + if (getareausers(.x - 2, .y - 4, .x + 2, .y + 6) > 0 || .dir == 0) { + end; + } +OnTimer30000: + .busy = true; + .dir = 6; // closing + specialeffect(25, AREA, getnpcid()); // closing + setnpctimer 0; +OnTouch: + end; + +OnInit: + .distance = 3; + end; +} diff --git a/npc/000-0-0/mapflags.txt b/npc/000-0-0/mapflags.txt new file mode 100644 index 0000000..98847f8 --- /dev/null +++ b/npc/000-0-0/mapflags.txt @@ -0,0 +1 @@ +//000-0-0 mapflag invisible diff --git a/npc/000-0-0/sailors.txt b/npc/000-0-0/sailors.txt new file mode 100644 index 0000000..d6dd440 --- /dev/null +++ b/npc/000-0-0/sailors.txt @@ -0,0 +1,300 @@ +// TMW2 Script +// Authors: +// Jesusalva +// Description: +// Tutorial +// TODO: Do not hardcode keyboard keys. The player may have changed the keyboard or even mouse bindings. + +000-0-0,30,34,0 script Elmo#sailors NPC_ELMO,{ +OnBegin: + .@q = getq(ShipQuests_Arpan); + if (.@q == 0) goto L_Step0; + if (.@q == 1) goto L_Step1; + if (.@q == 2) goto L_Step2; + if (.@q == 3) goto L_Step3; + if (.@q == 4) goto L_Step4; + if (.@q == 5) goto L_Step5; + + goto L_Ready; + +// Tutorial not accepted +L_Step0: + deltimer("Elmo#sailors::OnSlow"); + deltimer("Elmo#sailors::OnSlow2"); + setcamnpc; + showavatar NPC_ELMO; + mesn "Elmo"; + mesq l("Hey. You. You were in need of medical attention earlier. Do you remember how to walk, talk, attack, etc?"); + next; + select + l("Actually, a tutorial would be good!"), + l("Yes, I do."); + + if (@menu == 2) { + if (!#TUTORIAL_DONE) { + mesn l("Magic Arpan"); + mesq l("Yaya, are you sure?"); + mesc l("This option will be remembered by other NPCs as well."); + next; + select + l("Yes, I am."), + l("Actually, a tutorial would be good!"); + mes ""; + if (@menu == 2) + goto L_Begin; + } + TUTORIAL=false; + mesn l("Magic Arpan"); + mesq l("Yeye don't need to train here anymore! Let's head back to the ship before the sun sets, shall we?"); + next; + goto L_Skip; + } + +L_Begin: + TUTORIAL=true; + savepoint "000-0-0", 30, 37; + setq ShipQuests_Arpan, 1; + +// Tutorial accepted +L_Step1: + showavatar NPC_MAGIC_ARPAN; + mesn l("Magic Arpan"); + mesq l("Let's start with something simple, yeye."); + next; + mesn l("Magic Arpan"); + mesq l("You can move using @@ or, at your choice, by clicking where you want to go.", b(l("arrow keys"))); + next; + setcamnpc "Chest#002-1"; + mesn l("Magic Arpan"); + mesq l("On mobile, the DPAD is at your left. Do you see the chest to my right?"); + next; + setcamnpc; + mesn l("Magic Arpan"); + mesq l("Go and talk to it."); + close; + +// Clothes taken +L_Step2: + showavatar NPC_MAGIC_ARPAN; + mesn l("Magic Arpan"); + mesq l("Yeye, very good, you took the clothes."); + next; +L_Step2Skip: + mesn l("Magic Arpan"); + mesq l("Equipping them is easy, you must open your inventory with @@ or clicking in @@, on the top right.", b(l("F3")), b(l("INV"))); + next; + mesn l("Magic Arpan"); + mesq l("Most of it should be pretty intuitive, but be sure to press @@ on the bottom-left of inventory screen.", b(l("Equip"))); + next; + mesn l("Magic Arpan"); + mesq l("Come talk to me once you manage to equip the shirt and the shorts!"); + mesc l("PS. Due a bug, they won't be displayed when you equip unless you relog."), 1; + setq1 ShipQuests_Arpan, 3; + close; + +// Clothes Check +L_Step3: + if (getequipid(equip_torso) < 0 || getequipid(equip_legs) < 0) + goto L_Step2Skip; + setcamnpc; + showavatar NPC_ELMO; + mesn "Elmo"; + mesq l("You learn fast, good job. These clothes aren't mighty armor, but they'll help."); + next; + mesn "Elmo"; + mesq l("Let's jump straight to action, shall we? I'll build a mighty @@ to fight you!", getmonsterlink(Dummy)); + next; + mesn "Elmo"; + mesq l("On a computer, you can press @@ to attack it. On mobile, that would be the big button with the number 1.", b(l("Ctrl"))); + next; + mesn "Elmo"; + mesq l("Another way to attack it is clicking on it. Now, there are some things you must know before fighting this truly dangerous foe!"); + next; + showavatar NPC_MAGIC_ARPAN; + mesn l("Magic Arpan"); + mesq l("Yayaya, by pressing @@ or clicking in @@, you'll open your character status window!", b(l("F2")), b(l("STA"))); + next; + mesn l("Magic Arpan"); + mesq l("You should allocate some attributes. You need @@, @@ and @@, on this order of importance.", b(l("Agility")), b(l("Dexterity")), b(l("Strength"))); // b(l("")), + next; + mesn l("Magic Arpan"); + mesq l("Once you allocate status points, you can fight it! Good luck, @@!", strcharinfo(0)); + mesc l("WARNING: NO EXPERIENCE WILL BE CARRIED OVER FROM THIS FIGHT."), 1; + percentheal 100,100; + + // This is not really reliable, of course >.< + .@mct=mobcount("000-0-0", "Elmo#sailors::OnStep4"); + .@x=38; .@y=43; + .@x+=.@mct; + freeloop(true); + while (.@x > 43) { + .@x-=5; .@y+=1; + } + freeloop(false); + + if (.@y <= 47) + @mobTarget=monster("000-0-0", 43, 44, l("@@ Dummy", strcharinfo(0)), Dummy, 1, "Elmo#sailors::OnStep4"); + setq1 ShipQuests_Arpan, 4; + close; + +L_Step4: + percentheal 100,100; + mesn "Elmo"; + mesq l("Did something happen?"); + mesc l("This is a menu, click on the option and then on submit. You can use arrow keys if you prefer."); + mes ""; + select + l(">.< \"The Dummy is a real killer!\""), + l("T.T \"Someone else killed my Dummy!\""), + l("'.' \"I forgot how to allocate points!\""), + l("-.- \"I forgot how to fight!\""), + l("._. \"What are these statuses useful for?\""), + l("^.^ \"Nothing is wrong, don't worry!\""); + + mes ""; + switch (@menu) { + case 1: + mesn "Elmo"; + mesq l("Well, you don't have a weapon. So, let me explain quickly."); + next; + mesn "Elmo"; + mesq l("Don't be afraid of death. Of course, in most places, dying will make you lose some Experience you gathered."); + next; + mesn "Elmo"; + mesq l("But you don't have any experience at the moment, so what do you have to lose?"); + next; + mesn "Elmo"; + mesq l("Try killing it, and if it kills you, just come back from death to continue killing it."); + mes l("What I want to say is: Kill non-stop!"); + next; + mesc l("Actually, the Dummy will not fight back. Are you afraid?"); + break; + case 2: + .@mct=mobcount("000-0-0", "Elmo#sailors::OnStep4"); + if (.@mct) { + mesn "Elmo"; + mesq l("It was just south of the island. Try walking around a bit?"); + } else { + npctalk l("Hey, this Dummy is to @@ kill.", strcharinfo(0)); + monster "000-0-0", 43, 44, l("@@ Dummy", strcharinfo(0)), Dummy, 1, "Elmo#sailors::OnStep4"; + } + break; + case 3: + showavatar NPC_MAGIC_ARPAN; + mesn l("Magic Arpan"); + mesq l("Yayaya, by pressing @@ or clicking in @@, you'll open your char status window!", b(l("F2")), b(l("STA"))); + next; + mesn l("Magic Arpan"); + mesq l("You should allocate some attributes to it. You need @@, @@ and @@, on this order of importance.", b(l("Agility")), b(l("Dexterity")), b(l("Strength"))); // b(l("")), + break; + case 4: + mesn "Elmo"; + mesq l("On a computer, you can press @@ to attack it. On mobile, that would be the big button with the number 1.", b(l("Ctrl"))); + next; + mesn "Elmo"; + mesq l("Another way to attack it is clicking on it."); + break; + case 5: + mesn "Elmo"; + mes l("@@ helps you carry more items and also gives you a more forceful blow, but ends up not being very interesting if you focus on weapons that use projectiles, such as the bow.", b(l("Strength"))); + next; + mesn "Elmo"; + mes l("Greater @@ allows you to attack faster and has a greater chance of evading attacks.", b(l("agility"))); + next; + mesn "Elmo"; + mes l("@@ determines how many blows you can take before you die. It also affects status effects, like poison.", b(l("Vitality"))); + next; + mesn "Elmo"; + mes l("@@ is very useful for alchemy and magic, but nowadays there are few opportunities to use it.", b(l("Intelligence"))); + next; + mesn "Elmo"; + mes l("Your @@ determines your ability to hit monsters and is valuable to players who prefer weapons that use projectiles.", b(l("dexterity"))); + next; + mesn "Elmo"; + mes l("Your @@ determines several small things, including critical attacks and, limited to a certain extent, affect drop rates.", b(l("luck"))); + next; + mesn "Elmo"; + mes l("But to defeat this dummy, I would say that %s should do the trick.", b(l("assigning 5 points each to str, agi, dex and luck"))); + break; + } + close; + +OnStep4: + if (getunittype(@mobTarget) != -1 && is_staff()) + dispbottom ("Hey hey, the monster seems to be alive. Mr. GM, do something about that!"); + dispbottom l("You free the world from an evil Dummy."); + compareandsetq ShipQuests_Arpan, 4, 5; + end; + +L_Step5: + showavatar NPC_MAGIC_ARPAN; + mesn l("Magic Arpan"); + mesq l("Yayaya, good job! You can collect loot by pressing @@.", b("Z")); + next; + mesn l("Magic Arpan"); + mes l("I like to talk! If yeye likes too, you can press @@ to open chat box!", b(l("Enter"))); + mes l("On mobile, you would click on the @@ icon!", b(l("keyboard"))); + next; + mesn l("Magic Arpan"); + mesq l("The @@ tab allows yeye to talk on Discord, too! Yayaya, fancy, uh?", b("#world")); + next; + mesn l("Magic Arpan"); + mesq l("Yeye can press @@ to sit, which will allow you to heal faster.", b("s")); + next; + showavatar NPC_ELMO; + mesn "Elmo"; + mesq l("You're ready. Let's head back to the ship before the sun sets, shall we?"); + next; + getitem PiouLegs, 3; // Tutorial Reward + goto L_Ready; + +L_Skip: + setq ShipQuests_Arpan, 5; + getitem CreasedShirt, 1; + getitem CreasedShorts, 1; + equip(CreasedShirt); + equip(CreasedShorts); + +L_Ready: + #TUTORIAL_DONE=true; + //clearitem(); // The dummy does not drop anything :> + if (!##VAULT) + resetlvl(2); + restorecam; + setq General_Narrator, 0; + adddefaultskills; + percentheal 100,100; + addtimer(45000,"Magic Arpan::OnSlow"); + LOCATION$ = "Candor"; + warp "002-1@Candor", 53, 38; + savepoint "000-1", 22, 22; + TUT_VAR=gettimetick(2); + closedialog; + close; + +// Prevent players from forgetting what they were meant to do +// But if they logout, this will never trigger +OnSlow: + npctalk3 l("Hey @@! You haven't talked to me yet!", strcharinfo(0)); + addtimer(30000,"Elmo#sailors::OnSlow2"); + end; + +OnSlow2: + npctalk3 l("@@, do you need help? Are you lost? Talk to me!", strcharinfo(0)); + dispbottom l("Click on Elmo or Magic Arpan to continue and begin the game..."); + addtimer(60000,"Elmo#sailors::OnSlow"); + end; + +OnInit: + .sex = G_MALE; + end; +} + +000-0-0,29,34,0 script Magic Arpan#sailors NPC_MAGIC_ARPAN,{ + doevent "Elmo#sailors::OnBegin"; + close; + +OnInit: + .sex = G_MALE; + end; +} diff --git a/npc/000-0-1/_import.txt b/npc/000-0-1/_import.txt new file mode 100644 index 0000000..fa53df4 --- /dev/null +++ b/npc/000-0-1/_import.txt @@ -0,0 +1,3 @@ +// Map 000-0-1: Sailor's Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/000-0-1/mf.txt", diff --git a/npc/000-0-1/mf.txt b/npc/000-0-1/mf.txt new file mode 100644 index 0000000..c4f4148 --- /dev/null +++ b/npc/000-0-1/mf.txt @@ -0,0 +1 @@ +000-0-1 mapflag nosave 000-0,22,22 diff --git a/npc/000-0/_import.txt b/npc/000-0/_import.txt new file mode 100644 index 0000000..d2f0652 --- /dev/null +++ b/npc/000-0/_import.txt @@ -0,0 +1,4 @@ +// Map 000-0: Ocean +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/000-0/mapflags.txt", +"npc/000-0/sailors.txt", diff --git a/npc/000-0/mapflags.txt b/npc/000-0/mapflags.txt new file mode 100644 index 0000000..9b55f66 --- /dev/null +++ b/npc/000-0/mapflags.txt @@ -0,0 +1,2 @@ +000-0 mapflag invisible +000-1 mapflag invisible diff --git a/npc/000-0/sailors.txt b/npc/000-0/sailors.txt new file mode 100644 index 0000000..65d06a7 --- /dev/null +++ b/npc/000-0/sailors.txt @@ -0,0 +1,282 @@ +// TMW2 Script +// Modified by Jesusalva + +// Evol scripts. +// Authors: +// 4144 +// Qwerty Dragon +// Reid +// Vasily_Makarov +// Jesusalva +// Description: +// Starting script of Evol Online, modified for TMW2. +// Translation: +// FR Translated + +000-0,23,19,0 script Sailors NPC_SAILORS,6,6,{ + +OnTouch: + .@q=getq(General_Narrator); + if (.@q) + end; + + //checkclientversion; + .@lang = requestlang(); + if (.@lang >= 0 && .@lang <= MAX_LANG) Lang = .@lang; + + showavatar 3; + if ($EVENT$ == "Steam") goto L_FiresOfSteam; + + mesn "Narrator"; + mesc l("You open your eyes. The remants of the salt water in your eyes is not particularly helping you see."); + mesc l("(Click next button to advance dialogs)"), 3; + next; + mesc l("What in the world is happening?! Where in Jesusalva's name are you?!"); + next; + if (getvaultid()) { + mesc l("Sometimes, you really wish you could visit new universes without having to pick some sort of role on it."); + next; + } + mesc l("Actually. Who are you again? A headache which doesn't want to pass strikes you."); + mesc l("You can hear creaking planks and a sail flapping in the storm. A ship?"); + mesc l("You hear shouting directed at you. Sailors from the ship?"); + next; + + showavatar NPC_ORC_MAN; + setcamnpc "Sailors", -64, -32; + mesn l("Orc Voice"); + mesq lg("Hey kid! Can you hear me?"); + next; + + showavatar NPC_SILVIO; + setcamnpc "Sailors", 0, -32; + mesn l("Human Voice"); + mesq lg("Hear you? How do you even know she can understand you?!", "Hear you? How do you even know he can understand you?!"); + next; + + showavatar NPC_TRITAN_MAN_SAILOR; + setcamnpc "Sailors", 144, -80; + mesn l("Tritan Voice"); + mesq l("We speak various languages on this world. Let's try gesturing!"); + mesq l("Hey, you on the dune! Can you hear us?!"); + next; + restorecam; + + asklanguage(LANG_ON_SEA); + + showavatar NPC_SAILORS; + setcamnpc; + mes ""; + mesn; + mesq lg("Oh, she's still alive!", "Oh, he's still alive!"); + next; + showavatar NPC_TRITAN_MAN_SAILOR; + setcamnpc "Sailors", 144, -80; + mesq lg("This girl needs help, we need to rescue her!", "This boy needs help, we need to rescue him!"); + next; + mesn; + mesc l("This is a menu. You can click on the desired option and press \"Send\", and double-clicking should work, too."); + mes ""; + select + l("I don't need to be rescued. I'm enjoying myself here."), + l("Thanks... I guess..."), + l("(Don't respond)"); + mes ""; + + if (@menu == 1) { + mesn l("Sailor"); + mesq l("What do you mean? Do you at least have a rowboat with you?!"); + next; + mesn l("Billy Bons"); + // Dying of thirst will set in waaaay sooner than dying of hunger. + mesq l("We can't let you die of thirst! That wouldn't be cool at all! And I'm very cool!"); + next; + } else if (@menu == 2) { + mes ""; // You don't need to thank us! + } else { + mesn l("Sailor"); + mesq lg("Oh noes! She fainted! Quick, rescue her!!", "Oh noes! He fainted! Quick, rescue him!!"); + next; + } + restorecam; + + showavatar; + mesn "Narrator"; + mesc l("The sailors take you aboard their ship."); + mesc l("Click on the NPCs (Non-Player Characters) around you to continue the introduction."); + next; + + addtimer(20000,"Elmo#sailors::OnSlow"); + warp "000-0-0", 30, 36; + + closedialog; + close; + +L_FiresOfSteam: + dispbottom l("Welcome to Moubootaur Legends: ##1Fires of Steam##0"); + warp "000-0-1", 26, 28; + sleep2(25); + atcommand("@refresh"); + asklanguage(LANG_ON_SEA); + clear; + mesn "Narrator"; + mesc l("You wake up in the middle of the night. How did you got here? WERE YOU KIDNAPPED?"); + next; + mesn "Narrator"; + mesc l("Memory slowly returns to you as you start growing aware of your surroundings. That's right, you are on a ship."); + next; + mesn "Narrator"; + mesc l("Andrei Sakar, the legendary Hurnscald Hero, as well as a group of alliance members which include yourself, are heading to the continent of Kolev, where is said to be a legendary town which helped the humans from the second era to survive."); + next; + mesn "Narrator"; + mesc l("They were sending food and supplies to the Alliance, but in the past months, there was a deadly silence. Not a single sign of life came from there."); + next; + mesn "Narrator"; + mesc l("Hoping for the best, but prepared for the worst, you set off to the legendary continent of Kolev; Your destination: Artis."); + next; + clear; + mesc l("Moubootaur Legends presents..."), 3; + mes ""; + mesc b(l(" Fires of Steam ")), 1; + mesc l("- The Death of Andrei Sakar -"), 1; + mes ""; + mesc l("An event to prepare for the Steam Release"), 2; + mes ""; + next; + mes ""; + mes ""; + mesn strcharinfo(0); + mesq l("We'll be arriving at Artis by the dawn, where Elora will be waiting for us. I can only hope, that the denizens are okay..."); + next; + clear; + GameRules(8 | 4); + clear; + closeclientdialog; +OnForceReset: + freeloop(true); + while (BaseLevel < 80) + getexp NextBaseExp, 100; + freeloop(false); + setq ShipQuests_Arpan, 5; + setq General_Narrator, 21; + consoleinfo "New account: %d (%s)", getcharid(3), getcharip(); + if (array_find($@IPBLIST$, getcharip()) >= 0) + #TUTORIAL_DONE=true; + else + array_push($@IPBLIST$, getcharip()); + if (!#TUTORIAL_DONE) { + .@acc=any(Mustache, Beard, HeartGlasses, Sunglasses, EyePatch, Shemagh, Monocle, Googles, BurglarMask, BanditMask, Shemagh); + .@ac2=any(OldTowel, SantaGlobe, RedStocking, LeatherBall, Doll, ZarkorScroll, ThetaBook, AshUrn, RubberDucky, DragonStar, BronzeQuiver, AstralCube, PlushMouboo, PlushMouboo, GraduationAlbum); + .@hat=any(TopHat, CaptainHat, SmileyCap, BowlerHat, DesertHat, PirateBandana, KnitHat, RightEyePatch, AntlersHat, BunnyEars, AxeHat, PaperBag, ShroomHat, AFKCap, BrimmedFeatherHat, CatEars, Earmuffs, CorsairHat, SailorHat, ChefHat, SkullMask, LeprechaunHat, PrsmHelmet, ImperialCrown, ClericCap); + .@ha2=any(DarkKnightHelmet, VikingHelmet, TerraniteMask, CenturionHelmet, ChemistHelmet, BullHelmet, DarkHelm, SamuraiHelmet, SamuraiHelmet); + .@che=any(RedknightArmor, AssassinChest, SaviorArmor, TerraniteArmor, GraduationRobe, GoldenWarlordPlate, RedknightArmor); + .@pan=any(JeansChaps, LeatherTrousers, AssassinPants, TerranitePants, BromenalPants, ChainmailSkirt, AssassinPants, ChainmailSkirt, AssassinPants); + .@sho=any(DeepBlackBoots, HeliosBoots, WizardMoccasins, WarlordBoots, TerraniteBoots, AssassinBoots, WitchBoots, RedStockings, DeepBlackBoots, DeepBlackBoots); + .@nec=any(BarbarianAmulet, GoldenFourLeafAmulet, PlatinumFourLeafAmulet, BarbarianMasterAmulet, MoubooPendant, LifestonePendant, AlvasusPendant, ToothNecklace); + .@rin=any(GoldenPearlRing, GoldenBlackPearlRing); + .@glo=any(WarlordGloves, AssassinGloves, TerraniteGloves, ManaGloves, SarabArmlet, LeatherGloves, MinerGloves); + .@shi=any(EnchantedHerbBag, RentCart, Barrel, MasterBola, PiouBola, AncientShield, BlueKnightShield, SteelShield, DragonShield, SnakeBola); + .@wpn=any(Setzer, Kitana, Lightsaber, BoneKnife, AncientSword, LongSword, RockKnife, DivineSword, CentaurSpear, Zambacutou, CursedScythe, Halberd, PynRifle, PynGatling, PynShotgun, PynRevolver, Dustynator, ChampionshipBow, BansheeBow, LeaderWand, ImmortalSword, MysticWand, ChampionshipBow, BansheeBow, LeaderWand, MysticWand); + .@bon=any(Pickaxe, Kanabo, ElficBow, Judgement, ThunderStaff, ReinbooWand, DarkPulsar, Skypiercer, IceGladius, RealBronzeGladius, PurpleBola, KidBola, SilkGloves, ClawPendant, Boots, LuffyxSummerShorts, UglyChristmasSweater, MinerTankTop, ContributorSweater, LinarianSoul, TuxSoul, DeliciousCookie, DarkEggshellHat, Wreath); + .@pet=any(PiouEgg, BhopEgg, MaggotCocoon, DoggyDog, CattyCat, BlackyCat, Ratte, ForestShroomEgg, FluffyEgg, DuckEgg, BatEgg, MoggunEgg, PinkieCrystal, DragonHorn, TamedSnakeEgg); + getitem .@acc, 1; + getitem .@ac2, 1; + getitem .@hat, 1; + getitem .@ha2, 1; + getitem .@che, 1; + getitem .@pan, 1; + getitem .@sho, 1; + getitem .@nec, 1; + getitem .@rin, 1; + getitem .@glo, 1; + getitem .@shi, 1; + getitem .@wpn, 1; + getitem .@bon, 1; + getitem .@pet, 1; + equip(.@acc); + equip(.@ac2); + equip(.@hat); + equip(.@ha2); + equip(.@che); + equip(.@pan); + equip(.@nec); + equip(.@rin); + equip(.@glo); + equip(.@shi); + equip(.@wpn); + Zeny+=rand2(10000, 60000); + } + adddefaultskills(); + sk_lvup(AL_DP); + sk_lvup(AL_DP); + sk_lvup(AL_DP); + sk_lvup(AL_DP); + sk_lvup(AL_DP); + sk_lvup(AL_DP); + sk_lvup(AL_DP); + sk_lvup(TMW2_TRANSMIGRATION); + sk_lvup(AM_REST); + sk_lvup(AM_RESURRECTHOMUN); + sk_lvup(AM_CALLHOMUN); + sk_lvup(TMW2_CRAFT); + sk_lvup(TMW2_CRAFT); + sk_lvup(TMW2_CRAFT); + sk_lvup(TMW2_CRAFT); + sk_lvup(TMW2_CRAFT); + sk_lvup(TMW2_ANCIENTLANGUAGES); + sk_lvup(TMW2_MANABOMB); + sk_lvup(TMW2_GROUNDSTRIKE); + sk_lvup(TMW2_NAPALMBEAT); + sk_lvup(TMW2_ARROWSHOWER); + sk_lvup(any(ALL_INCCARRY, TF_STEAL, MC_VENDING, MC_DISCOUNT, MC_OVERCHARGE)); + sk_lvup(any(AC_OWL, SA_DRAGONOLOGY, TMW2_SAGE, CR_TRUST, SM_PROVOKE, ALL_FULL_THROTTLE, SA_FREECAST, TF_BACKSLIDING, NV_TRICKDEAD, MG_FIREWALL, SO_FIREWALK, GC_DARKILLUSION)); + sk_lvup(any(AC_OWL, SA_DRAGONOLOGY, TMW2_SAGE, CR_TRUST, SM_PROVOKE, ALL_FULL_THROTTLE, SA_FREECAST, TF_BACKSLIDING, NV_TRICKDEAD, MG_FIREWALL, SO_FIREWALK, GC_DARKILLUSION)); + sk_lvup(any(TMW2_KALMURK, TMW2_DRAGOKIN, TMW2_LIMERIZER, TMW2_HALHISS)); + sk_lvup(any(TMW2_KALWULF, TMW2_FAIRYKINGDOM, TMW2_FROZENHEART, TMW2_STONEHEART)); + sk_lvup(any(TMW2_KALBOO, TMW2_KALSPIKE, TMW2_CUTEHEART, TMW2_PLANTKINGDOM, TMW2_FAIRYEMPIRE)); + sk_lvup(any(TMW2_FIRSTAID, TMW2_HEALING, TMW2_MAGNUSHEAL, EVOL_AREA_PROVOKE)); + sk_lvup(any(TMW2_FIRSTAID, TMW2_HEALING, TMW2_MAGNUSHEAL, EVOL_AREA_PROVOKE)); + sk_lvup(any(TMW2_FROSTDIVER, TMW2_NAPALMBEAT, TMW2_MAGICSTRIKE, TMW2_METEORSTRIKE, TMW2_FIREARROW, TMW2_BRAWLING, TMW2_FALKONSTRIKE, TMW2_CHARGEDARROW)); + sk_lvup(any(TMW2_FROSTDIVER, TMW2_NAPALMBEAT, TMW2_MAGICSTRIKE, TMW2_METEORSTRIKE, TMW2_FIREARROW, TMW2_BRAWLING, TMW2_FALKONSTRIKE, TMW2_CHARGEDARROW)); + sk_lvup(any(TMW2_FROSTDIVER, TMW2_NAPALMBEAT, TMW2_MAGICSTRIKE, TMW2_METEORSTRIKE, TMW2_FIREARROW, TMW2_BRAWLING, TMW2_FALKONSTRIKE, TMW2_CHARGEDARROW)); + sk_lvup(any(TMW2_FROSTNOVA, TMW2_HOLYLIGHT, TMW2_LIGHTNINGBOLT, TMW2_METEORSHOWER, TMW2_FIREBALL, TMW2_BEARSTRIKE, TMW2_SUPREMEATTACK, SN_SHARPSHOOTING)); + sk_lvup(any(TMW2_FROSTNOVA, TMW2_HOLYLIGHT, TMW2_LIGHTNINGBOLT, TMW2_METEORSHOWER, TMW2_FIREBALL, TMW2_BEARSTRIKE, TMW2_SUPREMEATTACK, SN_SHARPSHOOTING)); + sk_lvup(any(TMW2_NILFHEIM, TMW2_JUDGMENT, TMW2_TEMPEST, TMW2_GAIABREAK, TMW2_ARMAGEDDON, TMW2_ALLINONE, TMW2_GROUNDSTRIKE, TMW2_ARROWSHOWER, SN_WINDWALK)); + sk_lvup(any(TMW2_GDP_MAXPOWER, TMW2_GDP_SPREGEN)); + getitembound GuildCoin, rand2(500), 4; + getitembound any(StrengthFruit, AgilityFruit, VitalityFruit, IntelligenceFruit, DexterityFruit, LuckFruit), 1, 4; + getitembound any(StrengthFruit, AgilityFruit, VitalityFruit, IntelligenceFruit, DexterityFruit, LuckFruit), 1, 4; + getitembound any(SacredBullet, EvilBullet), 400, 4; + getitembound any(ThornArrow, PoisonArrow, CursedArrow), 400, 4; + getitembound DeathPenalty, rand2(18), 4; + getitembound ScentGrenade, rand2(4), 4; + getitembound InsuranceContract, 1, 4; + getitembound Wurtzite, 6, 4; + getitembound MercCard_AndreiSakar, 1, 4; + getitembound RecipeBook, 1, 1; + getitembound Bread, 10, 4; + percentheal 100,100; + LOCATION$ = "Artis"; + TUT_VAR=gettimetick(2); + #TUTORIAL_DONE=true; + #REG_DATE=gettimetick(2); + TUTORIAL=true; + CRAFTQUEST=true; + MPQUEST=true; + MAGIC_LVL=7; + savepoint "029-0", 202, 85; + if (!@forcereset) + warp "029-0", 202, 85; + close; + +OnForceReset2: + logmes(sprintf("%s - Reset Forced by System Admin", getcharid(3))); + #TUTORIAL_DONE=false; + @forcereset=true; + goto OnForceReset; + +OnInit: + .sex = G_MALE; + bindatcmd "sakarfr", "Sailors::OnForceReset2", 100, 99, 1; + end; +} diff --git a/npc/000-1/_import.txt b/npc/000-1/_import.txt new file mode 100644 index 0000000..ec2c18b --- /dev/null +++ b/npc/000-1/_import.txt @@ -0,0 +1,3 @@ +// Map 000-1: Jesusalva's Trap +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/000-1/exit.txt", diff --git a/npc/000-1/exit.txt b/npc/000-1/exit.txt new file mode 100644 index 0000000..aed9a8c --- /dev/null +++ b/npc/000-1/exit.txt @@ -0,0 +1,32 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Special Soul Menhir which only allows leaving the map. + +000-1,22,22,0 script Emergency Exit NPC_SOUL_CURSED,2,2,{ +OnTouch: +OnTalk: +OnTalkNearby: + // At any time, if you can't leave Nard ship, you must go to nard ship + if (!getq(General_Narrator) && getq(ShipQuests_Julia) < 3) { + warp "002-1@Candor", 53, 38; + end; + } + + // Switch LOCATION$ and warp to nearest town's Soul Menhir + .@lx=array_find($@LOCMASTER_LOC$, LOCATION$); + if (.@lx >= 0) { + warp $@LOCMASTER_MAP$[.@lx], $@LOCMASTER_X[.@lx], $@LOCMASTER_Y[.@lx]; + end; + } + + //if (getsavepoint(0) != "000-1") warp getsavepoint(0), getsavepoint(1), getsavepoint(2); + if (getsavepoint(0) != "000-1") teleporthome(); + if (getsavepoint(0) != "000-1") end; + //savepoint "002-1", 53, 38; + warp "002-1", 53, 38; + end; + +} + diff --git a/npc/001-1/_import.txt b/npc/001-1/_import.txt new file mode 100644 index 0000000..71cb54d --- /dev/null +++ b/npc/001-1/_import.txt @@ -0,0 +1,9 @@ +// Map 001-1: Floating Island of Aeros +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-1/bgmaster.txt", +"npc/001-1/eventmaster.txt", +"npc/001-1/mahul.txt", +"npc/001-1/mapflags.txt", +"npc/001-1/portal.txt", +"npc/001-1/rewards.txt", +"npc/001-1/wateranimation.txt", diff --git a/npc/001-1/bgmaster.txt b/npc/001-1/bgmaster.txt new file mode 100644 index 0000000..cd4af38 --- /dev/null +++ b/npc/001-1/bgmaster.txt @@ -0,0 +1,349 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description +// Cassia and her lieutenants manages Aeros Battlegrounds + +001-1,96,37,0 script Cassia NPC_FEMALE,{ + mesn; + + if (is_gm()) goto L_Control; + if ($@BGMaster1) goto L_Busy; + +L_Intro: + mesq l("Hello! I am Cassia, Ambassator. During the Monster War outbreak, Halinarzo was almost entirely destroyed."); + next; + mesq l("To train their soldiers, they frequently face Frostia in duels. Adventurers are welcome to join their drills."); + next; + mesq l("To join a drill, right click on one of the lieutenants and join their Battle Stations. The drill can last up to 10 minutes."); + next; + mesq l("The sides accept only one adventurer. You'll also lose access to General Chat upon joining, and will have to use #world."); + next; + mesq l("Be sure to have a friend before joining, or you may have to logout in order to be able to move again!"); + close; + +L_Close: + close; + +L_Busy: + if ($@BGMaster1 == 2) { + mesq l("The soldiers are resting at the moment."); + } else { + mesq l("People are challenging now."); + } + close; + +L_Control: + menu + l("Introduce"), L_Intro, + rif($@BGMaster1 != 1 && is_gm(),l("Enable BG")), L_On, + l("Disable BG"), L_Off, + rif(is_admin(), l("Solo Try")), L_Beta; + +L_On: + mes ""; + mes l("Determine Team Size +1 (so 1 each side, use 2)"); + input $@BG1_SIZE; + if ($@BG1_SIZE < 2) close; + + donpcevent "Lt. Randy::OnSet"; + donpcevent "Lt. Gerry::OnSet"; + kickwaitingroomall("Lt. Randy"); + kickwaitingroomall("Lt. Gerry"); + + if ($@BGMaster1 == 2) $@BGMaster1=0; + enablenpc "Lt. Randy"; + enablenpc "Lt. Gerry"; + mapannounce "001-1", l("Frostia and Halinarzo are now on a spar!"), bc_map; + mes "Event enabled."; + close; + +L_Off: + delwaitingroom("Lt. Randy"); + delwaitingroom("Lt. Gerry"); + disablenpc "Lt. Randy"; + disablenpc "Lt. Gerry"; + mes "Event disabled."; + close; + +OnPcQuit: + warp "002-4", 0, 0; + bg_leave(); + end; + +OnPcDeath: + warp "002-4", 0, 0; + bg_leave(); + end; + +OnTimer60000: + if (getmapusers("001-2") < 2 || .BGC > 10) goto L_Cancel; + + mapannounce("001-2", "Reinforcements raise!", bc_map); + //$@FKing_T1 = bg_monster($@FK_Team1, "001-2", 125, 38, "Frostia Guard", 1081, "Cassia::OnSkip"); + bg_monster($@FK_Team1, "001-2", rand(105, 108), rand(37, 80), "Frostia Guard", FallenGuard1, "Cassia::OnSkip"); + //$@FKing_T2 = bg_monster($@FK_Team2, "001-2",124, 213, "Halinarzo Guard", 1082, "Cassia::OnSkip"); + bg_monster($@FK_Team2, "001-2", rand(142, 145), rand(37, 80), "Halinarzo Guard", FallenGuard2, "Cassia::OnSkip"); + .BGC+=1; + stopnpctimer(); + initnpctimer(); + end; + +// Arena Cooldown (every 8 hours) (CET time) +OnClock0001: +OnClock0801: +OnClock1601: + if ($@BGMaster1 == 2) $@BGMaster1=0; + end; + +OnSkip: + end; + +L_Skip: + end; + +L_RestartTimer: + stopnpctimer(); + initnpctimer(); + end; + +L_Cancel: + if ($@GM_OVERRIDE) + end; + stopnpctimer(); + announce "The Fallen Kings Duel ended in a draw!", bc_all; + killmonsterall "001-2"; + + mapwarp "001-2", "001-1", 235, 27; + bg_destroy($@FK_Team1); + bg_destroy($@FK_Team2); + $@BGMaster1=2; + end; + +OnVictor1: + if (!$@BGMaster1) goto L_Skip; + stopnpctimer(); + announce "Team 1 raises victorious at the Fallen Kings Duel!", bc_all; // TODO: Give players a reward + killmonsterall "001-2"; + //delcells "Wall_0012_left"; + //delcells "Wall_0012_right"; + + mapwarp "001-2", "001-1", 117, 72; + bg_destroy($@FK_Team1); + bg_destroy($@FK_Team2); + $@BGMaster1=2; + end; + +OnVictor2: + if (!$@BGMaster1) goto L_Skip; + stopnpctimer(); + announce "Team 2 raises victorious at the Fallen Kings Duel!", bc_all; + killmonsterall "001-2"; + //delcells "Wall_0012_left"; + //delcells "Wall_0012_right"; + + mapwarp "001-2", "001-1", 117, 72; + bg_destroy($@FK_Team1); + bg_destroy($@FK_Team2); + $@BGMaster1=2; + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, GMRobe); // Dress + setunitdata(.@npcId, UDT_HEADMIDDLE, NPCEyes); // Not needed + setunitdata(.@npcId, UDT_HEADBOTTOM, LousyMoccasins); // Shoes + setunitdata(.@npcId, UDT_WEAPON, 3501); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + npcsit; + + .sex = G_FEMALE; + .distance = 7; + /* + // This script is TODO + if (!debug) { + disablenpc "Cassia"; + }*/ + end; + +OnDoEvent: + //setcells "001-2", 109, 37, 109, 213, 3, "Wall_0012_left"; + //setcells "001-2", 141, 37, 141, 213, 3, "Wall_0012_right"; + .BGC=0; + mapannounce("001-2", "May the fight begin!", bc_map); + $@FKing_T1 = bg_monster($@FK_Team1, "001-2", rand(105, 108), rand(37, 80), "Frostia King", FallenKing1, "Cassia::OnVictor2"); + $@FKing_T2 = bg_monster($@FK_Team2, "001-2", rand(142, 145), rand(37, 80), "Halinarzo King", FallenKing2, "Cassia::OnVictor1"); + bg_monster($@FK_Team1, "001-2", rand(105, 108), rand(37, 80), "Frostia Guard", FallenGuard1, "Cassia::OnSkip"); + bg_monster($@FK_Team2, "001-2",rand(142, 145), rand(37, 80), "Halinarzo Guard", FallenGuard2, "Cassia::OnSkip"); + initnpctimer(); + end; + +L_Beta: + $@BGMaster1 = 1; + + $@FK_Team1 = bgnew("001-2",rand(105, 108), rand(37, 80),"start#bat_a02::OnSide1Quit",""); + $@FK_Team2 = bgnew("001-2",rand(142, 145), rand(37, 80),"start#bat_a02::OnSide2Quit",""); + + bgjoin($@FK_Team1, "001-2", rand(105, 108), rand(37, 80), getcharid(3)); + + setbgteam $@FK_Team1, 1; + setbgteam $@FK_Team2, 2; + + //bg_warp $@FK_Team1,"001-2",rand(105, 108), rand(37, 80); + //bg_warp $@FK_Team2,"001-2",rand(142, 145), rand(37, 80); + + donpcevent "Cassia::OnDoEvent"; + //initnpctimer; + end; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +001-1,94,36,0 script Lt. Randy NPC_ELF,{ + hello; + end; + +OnSet: + waitingroom("Battle Station R", $@BG1_SIZE, "start#bat_a02::OnReadyCheck", $@BG1_SIZE-1); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, LightPlatemail); // Light armor + setunitdata(.@npcId, UDT_HEADMIDDLE, JeansShorts); // Pants + setunitdata(.@npcId, UDT_HEADBOTTOM, LousyMoccasins); // Shoes + setunitdata(.@npcId, UDT_WEAPON, BugSlayer); + setunitdata(.@npcId, UDT_HAIRSTYLE, 13); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + //waitingroom("Battle Station", 2, "start#bat_a02::OnReadyCheck", 1); + + disablenpc("Lt. Randy"); + end; + +OnEnterBG: + $@FK_Team1 = waitingroom2bg("001-2",rand(105, 108), rand(37, 80),"start#bat_a02::OnSide1Quit",""); + end; +} + +001-1,98,36,0 script Lt. Gerry NPC_ELF,{ + hello; + end; + +OnSet: + waitingroom("Battle Station G", $@BG1_SIZE, "start#bat_a02::OnReadyCheck", $@BG1_SIZE-1); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TerraniteArmor); // Terranite armor + setunitdata(.@npcId, UDT_HEADMIDDLE, JeansShorts); // Pants + setunitdata(.@npcId, UDT_HEADBOTTOM, LousyMoccasins); // Shoes + setunitdata(.@npcId, UDT_WEAPON, BugSlayer); + setunitdata(.@npcId, UDT_HAIRSTYLE, 13); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + //waitingroom("Battle Station", 2, "start#bat_a02::OnReadyCheck", 1); + + disablenpc("Lt. Gerry"); + end; + +OnEnterBG: + $@FK_Team2 = waitingroom2bg("001-2",rand(142, 145), rand(37, 80),"start#bat_a02::OnSide2Quit",""); + end; +} + +//== Tierra Gorge Battleground Engine ====================== +001-2,0,0,3 script start#bat_a02 NPC_HIDDEN,{ +OnInit: + //mapwarp "bat_a02","bat_room",154,150; + end; + +OnEnable: + end; + +OnSide1Quit: +OnSide2Quit: + bg_leave; + end; + +OnReadyCheck: + if( $@BGMaster1 ) + end; + .@Guillaume = getwaitingroomstate(0,"Lt. Randy"); + .@Croix = getwaitingroomstate(0,"Lt. Gerry"); + if( !.@Guillaume && !.@Croix ) { + donpcevent "#bat_a02_timer::OnStop"; + end; + } + else if( .@Guillaume < 1 || .@Croix < 1 ) + end; + $@BGMaster1 = 1; + donpcevent "Lt. Randy::OnEnterBG"; + donpcevent "Lt. Gerry::OnEnterBG"; + setbgteam $@FK_Team1, 1; + setbgteam $@FK_Team2, 2; + bg_warp $@FK_Team1,"001-2",rand(105, 108), rand(37, 80); + bg_warp $@FK_Team2,"001-2",rand(142, 145), rand(37, 80); + donpcevent "Cassia::OnDoEvent"; + //initnpctimer; + end; +} + diff --git a/npc/001-1/eventmaster.txt b/npc/001-1/eventmaster.txt new file mode 100644 index 0000000..add05c4 --- /dev/null +++ b/npc/001-1/eventmaster.txt @@ -0,0 +1,854 @@ +// TMW2 Script +// Author: +// Jesusalva, Saulc +// Description: +// This GM NPC controls spawns and item drops on Aeros +// Monsters are sorted alphabetically, except bif + +001-1,250,20,0 script Mana Being#001-1 NPC_ALIGE_OUTSIDE_BARREL,{ + function spawner { // (memo, ID, amount) + // First argument is a memorand for Script Writers, usually name. It must be present, but can have whatever value you want. (Unused) + + // [0] East [1] West [2] Full + switch($@AEROS_SPWN) { + case 1: + areamonster("001-1", 20, 20, 140, 140, strmobinfo(1, getarg(1)), getarg(1), getarg(2), "Mana Being#001-1::OnAerosMobDeath"); + break; + case 2: + areamonster("001-1", 20, 20, 340, 160, strmobinfo(1, getarg(1)), getarg(1), getarg(2), "Mana Being#001-1::OnAerosMobDeath"); + break; + default: + areamonster("001-1", 171, 20, 340, 160, strmobinfo(1, getarg(1)), getarg(1), getarg(2), "Mana Being#001-1::OnAerosMobDeath"); + break; + } + } + + function mkitem { // ( ID{, Amount} ) + // [0] East [1] West [2] Full makeitem + switch($@AEROS_SPWN) { + case 1: + for (.@i = 0; .@i < getarg(1,1); .@i++) + makeitem(getarg(0), 1, "001-1", rand(20,140), rand(20,140)); + break; + case 2: + for (.@i = 0; .@i < getarg(1,1); .@i++) + makeitem(getarg(0), 1, "001-1", rand(20,340), rand(20,160)); + break; + default: + for (.@i = 0; .@i < getarg(1,1); .@i++) + makeitem(getarg(0), 1, "001-1", rand(171,320), rand(158,340)); + break; + } + } + + function buryitem { // ( ID{, Amount} ) + // [0] East [1] West [2] Full makeitem + switch($@AEROS_SPWN) { + case 1: + shovel_scatter("001-1", 20, 20, 140, 140, getarg(1,1), getarg(0)); + break; + case 2: + shovel_scatter("001-1", 20, 20, 340, 160, getarg(1,1), getarg(0)); + default: + shovel_scatter("001-1",171, 20, 340, 160, getarg(1,1), getarg(0)); + } + } + + .@curmobc=mobcount("001-1", "Mana Being#001-1::OnAerosMobDeath"); + if (!is_gm()) goto L_Unauthorized; + if ($HARDCORE) goto L_Unauthorized; + + @log_spawns=0; + @log_mode=$@AEROS_SPWN; + @log_ratio=$coinsrate; + logmes "Aeros Control Panel was open.", LOGMES_ATCOMMAND; + + mesn; + mes "Tired of walking the whole Aeros to spawn monsters, I was brought to existence."; + mes "Monsters left: "+str(.@curmobc); + +L_Menu: + mes ""; + mes "Please select operation."; + menu + "Close", L_Close, + "Start/End Event", L_EventHandler, + "Spawn", L_Spawn, + rif(countitem(StrangeCoin) >= 10, "Drop stuff! (10x Coins)"), L_Drop, + "Reconfigure spawn/warp points", L_Conf, + rif(.WALL, "Open Extension"), L_DelWall, + rif(!.WALL, "Close Extension"), L_AddWall, + rif(is_master(), "Adjust coins drop rate"), L_Rate; + +L_Unauthorized: + //dispbottom l("I am too far away to talk. Weird floating thingy..."); + dispbottom l("Monsters left: @@", .@curmobc); + end; + +L_Close: + if (@log_spawns > 0) logmes "spawned "+str(@log_spawns)+" beigns at Aeros.", LOGMES_ATCOMMAND; + if (@log_ratio != $coinsrate) logmes "set aeros ratio from "+str(@log_ratio)+" to "+str($coinsrate), LOGMES_ATCOMMAND; + if (@log_mode == $@AEROS_SPWN) logmes "configured aeros spawn area to "+str($@AEROS_SPWN), LOGMES_ATCOMMAND; + logmes "Aeros Control Panel was closed normally.", LOGMES_ATCOMMAND; + close; + +L_Spawn: + mes ""; + mes "Spawn from a preset (with intended levels) from this menu. Otherwise, use @aeros with same syntax."; + mes "(agr) means Agressive Monsters on the set, DO NOT ABUSE."; + next; + menu + "Abort", L_Menu, + "Presets", L_Preset, + "Normal (Lv <50)", L_Norm, + "Normal (Lv >50)", L_Norm2, + "Plants", L_Plants, + "Looters", L_Sli, + "Aggressive", L_Agr, + //"Assistants", L_Ass, + "GM Event Only", L_EventOnly, + "Boss", L_Boss; + +// NOTE: New Presets weren't added since ... long time ago. +L_Preset: + select + "Abort", // 1 + "20x Piou, Piousse, Ratto, 10x Croc", // 2 + "20x Little Blub, 10x Plushroom Field", // 3 + "(agr) 5x Tipiu, 10x Cave Maggot, 10x Bat", // 4 + "20x Scorpion, 10x Duck, 10x Maggot", // 5 + "10x Red Scorpion, 20x Fire Goblin, 5x Mana Ghost", // 6 + "(agr) 1x Saxso Ghost, 20x House Maggot", // 7 + "(agr) 5x Slime Blast, 5x Red Slime, 10x White Slime", // 8 + + "(agr) 5x Mouboo, 4x Bandit, 2x Black Scorpion", // 9 + "10x Giant Maggot, 10x Cave Snake, 10x Mana Bug", // 10 + "1x Golden/Night Scorpion, 2x Santa Slime, 5x Copper Slime", // 11 + "(agr) 2x Fallen Guards", // 12 + + "(agr) 5x Old Snake, 4x Grass Snake, 3x Snake", // 13 + "(agr) 4x Pollet, 3x Wolvern", // 14 + "5x of each fairy", // 15 + "(agr) 1x of each slime mother", // 16 + "(agr) 1x of each skull slime", // 17 + + "20x Bat, 10x Crafty, 5x Troll", // 18 + "(agr) 5x Forain, 5x Yeti, 5x Centaur", // 19 + "(agr) 10x Gobo Bear, 10x Terra, 5x Terra Protector", // 20 + "(agr) 1x Reaper, 1x Michel, 2x JackO, 5x Skellington", // 21 + "1x of each Pixie", // 22 + "(agr) 20x Pinkie, 10x Suseran, 7x Maximus", // 23 + + "7x3 random Chests and mimics", // 24 + "10x Clover Path, 5 groups of 5 random Bifs", // 25 + "5x Bifs, 4 groups of 5 random Bifs"; // 26 + + switch (@menu) { + case 1: + goto L_Spawn; + break; + //"20x Piou, Piousse, Ratto, 10x Croc", // 2 + //"20x Little Blub, 10x Plushroom Field", // 3 + //"(agr) 5x Tipiu, 10x Cave Maggot, 10x Bat", // 4 + //"20x Scorpion, 10x Duck, 10x Maggot", // 5 + //"10x Red Scorpion, 20x Fire Goblin, 5x Mana Ghost", // 6 + //"(agr) 1x Saxso Ghost, 20x House Maggot", // 7 + //"(agr) 5x Slime Blast, 5x Red Slime, 10x White Slime", // 8 + case 2: + spawner(strmobinfo(1, Piou), Piou, 20); + spawner(strmobinfo(1, Piousse), Piousse, 20); + spawner(strmobinfo(1, Ratto), Ratto, 20); + spawner(strmobinfo(1, Croc), Croc, 10); + @log_spawns=@log_spawns+70; + break; + case 3: + spawner(strmobinfo(1, LittleBlub), LittleBlub, 20); + spawner(strmobinfo(1, PlushroomField), PlushroomField, 10); + @log_spawns=@log_spawns+30; + break; + case 4: + spawner(strmobinfo(1, Tipiu), Tipiu, 5); + spawner(strmobinfo(1, CaveMaggot), CaveMaggot, 10); + spawner(strmobinfo(1, Bat), Bat, 10); + @log_spawns=@log_spawns+25; + break; + case 5: + spawner(strmobinfo(1, Scorpion), Scorpion, 20); + spawner(strmobinfo(1, Duck), Duck, 10); + spawner(strmobinfo(1, Maggot), Maggot, 10); + @log_spawns=@log_spawns+40; + break; + case 6: + spawner(strmobinfo(1, RedScorpion), RedScorpion, 10); + spawner(strmobinfo(1, ManaBug), FireGoblin, 20); + spawner(strmobinfo(1, ManaGhost), ManaGhost, 5); + @log_spawns=@log_spawns+35; + break; + case 7: + spawner(strmobinfo(1, SaxsoGhost), SaxsoGhost, 1); + spawner(strmobinfo(1, HouseMaggot), HouseMaggot, 20); + @log_spawns=@log_spawns+21; + break; + case 8: + spawner(strmobinfo(1, SlimeBlast), SlimeBlast, 5); + spawner(strmobinfo(1, RedSlime), RedSlime, 5); + spawner(strmobinfo(1, WhiteSlime), WhiteSlime, 10); + @log_spawns=@log_spawns+20; + break; + + //"(agr) 5x Mouboo, 4x Bandit, 2x Black Scorpion", // 9 + //"10x Giant Maggot, 10x Cave Snake, 10x Mana Bug", // 10 + //"1x Golden/Night Scorpion, 2x Santa Slime, 5x Copper Slime", // 11 + //"(agr) 2x Fallen Guards", // 12 + case 9: + spawner(strmobinfo(1, Mouboo), Mouboo, 5); + spawner(strmobinfo(1, Bandit), Bandit, 4); + spawner(strmobinfo(1, BlackScorpion), BlackScorpion, 2); + @log_spawns=@log_spawns+11; + break; + case 10: + spawner(strmobinfo(1, GiantMaggot), GiantMaggot, 10); + spawner(strmobinfo(1, CaveSnake), CaveSnake, 10); + spawner(strmobinfo(1, ManaBug), ManaBug, 10); + @log_spawns=@log_spawns+30; + break; + case 11: + spawner(strmobinfo(1, GoldenScorpion), GoldenScorpion, 1); + spawner(strmobinfo(1, NightScorpion), NightScorpion, 1); + spawner(strmobinfo(1, SantaSlime), SantaSlime, 5); + spawner(strmobinfo(1, CopperSlime), CopperSlime, 10); + @log_spawns=@log_spawns+17; + break; + case 12: + spawner(strmobinfo(1, FallenGuard1), FallenGuard1, 1); + spawner(strmobinfo(1, FallenGuard2), FallenGuard2, 1); + @log_spawns=@log_spawns+2; + break; + + //"(agr) 5x Old Snake, 4x Grass Snake, 3x Snake", // 13 + //"(agr) 4x Pollet, 3x Wolvern", // 14 + //"5x of each fairy", // 15 + //"(agr) 1x of each slime mother", // 16 + //"(agr) 1x of each skull slime", // 17 + case 13: + spawner("", OldSnake, 5); + spawner("", GrassSnake, 4); + spawner("", Snake, 3); + @log_spawns=@log_spawns+12; + break; + case 14: + spawner("", Pollet, 4); + spawner("", Wolvern, 3); + @log_spawns=@log_spawns+7; + break; + case 15: + spawner("", EarthFairy, 5); + spawner("", FireFairy, 5); + spawner("", WaterFairy, 5); + spawner("", WindFairy, 5); + spawner("", PoisonFairy, 5); + @log_spawns=@log_spawns+25; + break; + case 16: + spawner("", GreenSlimeMother, 1); + spawner("", BlueSlimeMother, 1); + spawner("", CopperSlimeMother, 1); + spawner("", YellowSlimeMother, 1); + spawner("", RedSlimeMother, 1); + spawner("", ChocolateSlimeMother, 1); + spawner("", WhiteSlimeMother, 1); + spawner("", AzulSlimeMother, 1); + spawner("", SeaSlimeMother, 1); + spawner("", LavaSlimeMother, 1); + spawner("", BlackSlimeMother, 1); + @log_spawns=@log_spawns+11; + break; + case 17: + spawner("", AzulSkullSlime, 1); + spawner("", YellowSkullSlime, 1); + spawner("", RedSkullSlime, 1); + spawner("", GreenSkullSlime, 1); + spawner("", CopperSkullSlime, 1); + spawner("", LavaSkullSlime, 1); + spawner("", BlackSkullSlime, 1); + @log_spawns=@log_spawns+7; + break; + //"20x Bat, 10x Crafty, 5x Troll", // 18 + //"(agr) 5x Forain, 5x Yeti, 5x Centaur", // 19 + //"(agr) 10x Gobo Bear, 10x Terra, 5x Terra Protector", // 20 + //"(agr) 1x Reaper, 1x Michel, 2x JackO, 5x Skellington", // 21 + //"1x of each Pixie", // 22 + //"(agr) 20x Pinkie, 10x Suseran, 7x Maximus", // 23 + case 18: + spawner("", Bat, 20); + spawner("", Crafty, 10); + spawner("", Troll, 5); + @log_spawns=@log_spawns+35; + break; + case 19: + spawner("", Forain, 5); + spawner("", Yeti, 5); + spawner("", Centaur, 5); + @log_spawns=@log_spawns+15; + break; + case 20: + spawner("", GoboBear, 10); + spawner("", Terranite, 10); + spawner("", TerraniteProtector, 5); + @log_spawns=@log_spawns+25; + break; + case 21: + spawner("", Reaper, 1); + spawner("", Michel, 1); + spawner("", JackO, 2); + spawner("", Skeleton, 5); + @log_spawns=@log_spawns+9; + break; + case 22: + spawner("", VanityPixie, 1); + spawner("", HolyPixie, 1); + spawner("", ShadowPixie, 1); + spawner("", NulityPixie, 1); + @log_spawns=@log_spawns+4; + break; + case 23: + spawner("", Pinkie, 20); + spawner("", PinkieSuseran, 10); + spawner("", PinkieMaximus, 7); + @log_spawns=@log_spawns+37; + break; + + + //"7x3 random Chests and mimics", // 24 + //"10x Clover Path, 5 groups of 5 random Bifs", // 25 + //"5x Bifs, 4 groups of 5 random Bifs"; // 26 + case 24: + spawner("", rand(BronzeChest, GoldenChest), 3); + spawner("", rand(BronzeChest, MalignChest), 3); + spawner("", rand(BronzeMimic, GoldenMimic), 3); + spawner("", rand(WildxChest, MalignChest), 3); + spawner("", rand(BronzeChest, GoldenChest), 3); + spawner("", rand(BronzeChest, MalignChest), 3); + spawner("", rand(BronzeMimic, GoldenMimic), 3); + @log_spawns=@log_spawns+21; + break; + case 25: + spawner(strmobinfo(1, CloverPatch), CloverPatch, 10); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + @log_spawns=@log_spawns+35; + break; + case 26: + spawner(strmobinfo(1, Bif), Bif, 5); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + spawner(l("Mysterious Bif"), rand(1098,1118), 5); + @log_spawns=@log_spawns+25; + break; + } + + if (@menu == 1) goto L_Spawn; + mes ""; + mes "Completed."; + mes "Total spawns: "+str(@log_spawns); + next; + goto L_Preset; + +L_Norm: + // option preparatives + .@opt$="Abort"; + debugmes "Found %d entries", getarraysize(.ML_Lv50); + for (.@i=0;.@i < getarraysize(.ML_Lv50);.@i++) { + .@opt$+=":"+strmobinfo(1, .ML_Lv50[.@i]); + // .ML_Lv50[.@i]); + } + select .@opt$; + + // Select handler + if (@menu != 1) + input .@c, 0, 100; + if (.@c == 0 && @menu != 1) + @menu=99; + + if (@menu == 1) + goto L_Spawn; + if (@menu == 99) + goto L_Norm; + + // Spawn if needed + .@i=@menu-2; + spawner(("NORMAL 1 Monster"), .ML_Lv50[.@i], .@c); + + // Log the spawn and resume + @log_spawns=@log_spawns+.@c; + mes ""; + mes "Completed."; + mes "Total spawns: "+str(@log_spawns); + next; + goto L_Norm; + +L_Norm2: + // option preparatives + .@opt$="Abort"; + debugmes "Found %d entries", getarraysize(.ML_Lv99); + for (.@i=0;.@i < getarraysize(.ML_Lv99);.@i++) { + .@opt$+=":"+strmobinfo(1, .ML_Lv99[.@i]); + // .ML_Lv99[.@i]); + } + select .@opt$; + + // Select handler + if (@menu != 1) + input .@c, 0, 100; + if (.@c == 0 && @menu != 1) + @menu=99; + + if (@menu == 1) + goto L_Spawn; + if (@menu == 99) + goto L_Norm2; + + // Spawn if needed + .@i=@menu-2; + spawner(("NORMAL 2 Monster"), .ML_Lv99[.@i], .@c); + + // Log the spawn and resume + @log_spawns=@log_spawns+.@c; + mes ""; + mes "Completed."; + mes "Total spawns: "+str(@log_spawns); + next; + goto L_Norm2; + +L_Sli: + // option preparatives + .@opt$="Abort"; + debugmes "Found %d entries", getarraysize(.ML_Loot); + for (.@i=0;.@i < getarraysize(.ML_Loot);.@i++) { + .@opt$+=":"+strmobinfo(1, .ML_Loot[.@i]); + // .ML_Loot[.@i]); + } + select .@opt$; + + // Select handler + if (@menu != 1) + input .@c, 0, 100; + if (.@c == 0 && @menu != 1) + @menu=99; + + if (@menu == 1) + goto L_Spawn; + if (@menu == 99) + goto L_Sli; + + // Spawn if needed + .@i=@menu-2; + spawner(("LOOTER Monster"), .ML_Loot[.@i], .@c); + + // Log the spawn and resume + @log_spawns=@log_spawns+.@c; + mes ""; + mes "Completed."; + mes "Total spawns: "+str(@log_spawns); + next; + goto L_Sli; + + +L_Plants: + // option preparatives + .@opt$="Abort"; + debugmes "Found %d entries", getarraysize(.ML_Plants); + for (.@i=0;.@i < getarraysize(.ML_Plants);.@i++) { + .@opt$+=":"+strmobinfo(1, .ML_Plants[.@i]); + // .ML_Plants[.@i]); + } + select .@opt$; + + // Select handler + if (@menu != 1) + input .@c, 0, 100; + if (.@c == 0 && @menu != 1) + @menu=99; + + if (@menu == 1) + goto L_Spawn; + if (@menu == 99) + goto L_Plants; + + // Spawn if needed + .@i=@menu-2; + spawner(("PLANT Monster"), .ML_Plants[.@i], .@c); + + // Log the spawn and resume + @log_spawns=@log_spawns+.@c; + mes ""; + mes "Completed."; + mes "Total spawns: "+str(@log_spawns); + next; + goto L_Plants; + + +L_Agr: + // option preparatives + .@opt$="Abort"; + debugmes "Found %d entries", getarraysize(.ML_Aggr); + for (.@i=0;.@i < getarraysize(.ML_Aggr);.@i++) { + .@opt$+=":"+strmobinfo(1, .ML_Aggr[.@i]); + // .ML_Aggr[.@i]); + } + select .@opt$; + + // Select handler + if (@menu != 1) + input .@c, 0, 100; + if (.@c == 0 && @menu != 1) + @menu=99; + + if (@menu == 1) + goto L_Spawn; + if (@menu == 99) + goto L_Agr; + + // Spawn if needed + .@i=@menu-2; + spawner(("AGGRESSIVE Monster"), .ML_Aggr[.@i], .@c); + + // Log the spawn and resume + @log_spawns=@log_spawns+.@c; + mes ""; + mes "Completed."; + mes "Total spawns: "+str(@log_spawns); + next; + goto L_Agr; + + + + + + + + + +L_EventOnly: + mesc l("WARNING: Spawn these monsters with moderation!"), 1; + mesc l("This is just a short list to make GMs life easier!"), 1; + mes ""; + menuint + "Abort", -1, + "Bhop Fluffy", BhopFluffy, + "Easter Mouboo", EasterMouboo, + "Evil Scythe", EvilScythe, + "Jack'O", JackO, + "Magic Ratto", MagicRatto, + "Moonshroom", Moonshroom, + "Mouboo Slime", MoubooSlime, + "Small Magic Bif", SmallMagicBif, + "Magic Bif", MagicBif, + "Bronze Chest", BronzeChest, + "Bronze Mimic", BronzeMimic, + "Silver Chest", SilverChest, + "Silver Mimic", SilverMimic, + "Golden Chest", GoldenChest, + "Golden Mimic", GoldenMimic, + "Evil Chest", EvilChest, + "Big Magic Bif", BigMagicBif; + + .@mobId=@menuret; + + if (.@mobId > 0) + input .@c, 0, 100; + if (.@c == 0 && .@mobId > 0) + .@mobId = 0; + + if (.@mobId < 0) + goto L_Spawn; + else if (.@mobId > 0) + spawner("", .@mobId, .@c); + + @log_spawns=@log_spawns+.@c; + mes ""; + mes "Completed."; + mes "Total spawns: "+str(@log_spawns); + next; + goto L_EventOnly; + + + + + + + + +L_Boss: + // Boss option preparatives + .@opt$="Abort"; + debugmes "Found %d entries", getarraysize(.ML_Boss); + for (.@i=0;.@i < getarraysize(.ML_Boss);.@i++) { + .@opt$+=":"+strmobinfo(1, .ML_Boss[.@i]); + // .ML_Boss[.@i]); + } + select .@opt$; + + // Select handler + if (@menu != 1) + input .@c, 0, 100; + if (.@c == 0 && @menu != 1) + @menu=99; + + if (@menu == 1) + goto L_Spawn; + if (@menu == 99) + goto L_Boss; + + // Spawn if needed + .@i=@menu-2; + spawner(("BOSS Monster"), .ML_Boss[.@i], .@c); + + // Log the spawn and resume + @log_spawns=@log_spawns+.@c; + mes ""; + mes "Completed."; + mes "Total spawns: "+str(@log_spawns); + next; + goto L_Boss; + + +L_Drop: + select + "Abort", // 1 + "Bury 10x Coins", // 2 + "Toothbrush", // 4 + "Bronze Gift", // 5 + "Silver Gift", // 6 + "Golden Gift", // 7 + "Prism Gift", // 8 + "Supreme Gift"; // default + + if (@menu != 1) + input .@c, 0, countitem(StrangeCoin)/10; + if (.@c == 0 && @menu != 1) + @menu=1; + + delitem(StrangeCoin,.@c*10); + switch (@menu) { + case 1: + goto L_Menu; + break; + case 2: + buryitem(StrangeCoin, .@c*any(9,10,10,10,11,11)); break; + case 3: + mkitem(Toothbrush, .@c); break; + case 4: + mkitem(BronzeGift, .@c); break; + case 5: + mkitem(SilverGift, .@c); break; + case 6: + mkitem(GoldenGift, .@c); break; + case 7: + mkitem(PrismGift, .@c); break; + default: + mkitem(SupremeGift, .@c); break; + + } + + mes ""; + mes "Dropped "+.@c+" stuff."; + next; + goto L_Drop; + +L_Rate: + // Only Admins can change Strange Coin drop rate + if (!is_master()) + goto L_Menu; + + mes ""; + mes "Current drop rate: " + str($coinsrate); + mes "Insert drop rate (from 0 to 10000, capped at 3000 or 30%). Use -1 to disable."; + .@old = $coinsrate; + input $coinsrate; + if ($coinsrate > 3000) + $coinsrate=3000; + if ($coinsrate != .@old) + consolewarn("Aeros Coins drop rate changed - highlight %s - by %s (%d) from %d to %d", "<@&411247967064948737>", strcharinfo(0), getcharid(3), .@old, $coinsrate); + mes ""; + mes "Ratio adjusted."; + next; + goto L_Menu; + +L_EventHandler: + if($@GM_EVENT) { + announce ("The event is over!"), bc_all|bc_npc; // I haven't tested this yet. + $@GM_EVENT=0; + $@MK_SCENE=MK_NONE; + } else if (!$@MK_SCENE) { + announce ("The mana bridge to Aeros is open! To participate on event, talk to ##BSoul Menhir##b!"), bc_all|bc_npc; + channelmes("#world", "An event is happening on Aeros! Hurry up!"); + $@GM_EVENT=1; + $@MK_SCENE=MK_LOCKED; + } else { + mesc("Operation not permitted: Monster King event is in progress.", 1); + mesc("You MUST wait.", 1); + } + close; + +L_AddWall: + setcells "001-1", 169, 63, 169, 76, 3, "AerosWall"; + //setwall("001-1", 169, 63, 13, DOWN, false, "AerosWall"); + .WALL=1; + close; + +L_DelWall: + delcells "AerosWall"; + //delwall("AerosWall"); + .WALL=0; + close; + +L_Conf: + mes ""; + mesn; + mesq l("Current Spawn Mode: "+$@AEROS_SPWN); + next; + select + l("[0] East Aeros"), + l("[1] West Aeros"), + l("[2] Full Aeros"); + $@AEROS_SPWN=@menu-1; + goto L_Menu; + +OnAutoSched: + .@t=0; + // Scatter 10× Strange Coins for digging + // Also scatter some strange coins for looting + buryitem(StrangeCoin, any(9,10,10,10,11,11)); + mkitem(StrangeCoin, $@AEROS_AUTOSPAWN); + // Spawn monsters + freeloop(true); + for (.@i = 0; .@i < ($@AEROS_AUTOSPAWN * 5); .@i++) { + .@r=rand2(5, 12); + // FIXME: Use TOP3AVERAGELVL() instead of a full list + .@m=any(Piou, Piousse, Ratto, LittleBlub, Croc, PlushroomField, + Tipiu, CaveMaggot, Bat, Scorpion, Duck, Maggot, RedScorpion, + ManaBug, ManaGhost, HouseMaggot, RedSlime, WhiteSlime, + SlimeBlast, Mouboo, Bandit, BlackScorpion, GiantMaggot, + CaveSnake, ManaBug, GoldenScorpion, NightScorpion, SantaSlime, + CopperSlime, FallenGuard1, FallenGuard2, OldSnake, GrassSnake, + Snake, Pollet, Wolvern, EarthFairy, FireFairy, WaterFairy, + WindFairy, PoisonFairy, GreenSlimeMother, ChocolateSlimeMother, + CopperSlimeMother, SeaSlimeMother, AzulSkullSlime, + CopperSkullSlime, Crafty, Troll, Forain, Yeti, Centaur, GoboBear, + Terranite, TerraniteProtector, Reaper, Michel, JackO, Skeleton, + Pinkie, PinkieSuseran, CloverPatch, MagicRatto, Moonshroom, + MoubooSlime); + spawner(strmobinfo(1, .@m), .@m, .@r); + .@t += .@r; + // Bonus + .@r=rand2(1, 3); + .@m=any(SaxsoGhost, HolyPixie, BronzeChest, SilverChest, GoldenChest, + MalignChest, PrismChest, SupremeChest, WildxChest, ManaChest, + ThornChest, ViciousChest, EvilChest, BronzeMimic, SilverMimic, + GoldenMimic, Bif, EleniumBif, BigEleniumBif, RubyBif, BigRubyBif, + TopazBif, BigTopazBif, EmeraldBif, BigEmeraldBif, + DiamondBif, BigDiamondBif, AmethystBif, BigAmethystBif, + SapphireBif, BigSapphireBif, SmallMagicBif, MagicBif, BigMagicBif, + SmallMagicBif, SmallMagicBif, SmallMagicBif, SmallMagicBif, + CloverPatch, EvilScythe); + spawner(strmobinfo(1, .@m), .@m, .@r); + .@t += .@r; + } + freeloop(false); + // Open gate if full + if ($@AEROS_SPWN == 2 && .WALL) { + delcells "AerosWall"; + .WALL=0; + } + // Close gate otherwise + if ($@AEROS_SPWN != 2 && !.WALL) { + setcells "001-1", 169, 63, 169, 76, 3, "AerosWall"; + .WALL=1; + } + // We're done + $@AEROS_AUTOSPAWN = 0; + consoleinfo("script automatically spawned "+str(.@t)+" beigns at Aeros."); + end; + +OnAerosMobDeath: + // is killedrid even set? + if ($coinsrate < 0) end; + // A level 100 monster can have a 4% drop bonus against a level 0 monster. + // $coinsrate acts stablishing a basic drop value. Advised value: 600 (up to 10% for normal mobs) + if (playerattached()) { + debugmes("Mob slain: %d", killedrid); + if (rand(10000) <= $coinsrate + (getmonsterinfo(killedrid, MOB_LV)*4)) { + getmapxy(.@m$, .@x, .@y, 0); + makeitem(StrangeCoin, 1, .@m$, .@x, .@y); + } + fix_mobkill(killedrid); + } + end; + +OnInit: + if ($coinsrate == 0) + $coinsrate=400; // Default value is 4% + mob str bonus + + // Monster lists + // These arrays are filled automatically by redesign.py + // DO NOT EDIT MANUALLY + setarray .ML_Plants, 1021, 1058, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116, 1117, 1118, 1134, 1135, 1136, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1156, 1157, 1158, 1170, 1188, 1189, 1226, 1227, 1228; + setarray .ML_Boss, 1015, 1036, 1040, 1044, 1076, 1077, 1079, 1080, 1087, 1129, 1154, 1179, 1200, 1205, 1209, 1211, 1213, 1221, 1222, 1225, 1229, 1400, 1401, 1420, 1430, 1431, 1432, 1439, 1495, 1496, 1497, 1498, 1499, 1500, 1503, 1504, 1505; + setarray .ML_Aggr, 1012, 1024, 1026, 1027, 1037, 1042, 1045, 1051, 1052, 1056, 1061, 1062, 1063, 1064, 1074, 1081, 1082, 1084, 1085, 1089, 1090, 1092, 1097, 1119, 1120, 1122, 1123, 1124, 1125, 1130, 1131, 1137, 1138, 1153, 1167, 1168, 1169, 1174, 1176, 1177, 1178, 1187, 1192, 1193, 1194, 1195, 1196, 1198, 1199, 1203, 1206, 1207, 1208, 1212, 1214, 1220, 1223, 1224, 1230, 1231, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1402, 1403, 1404, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1413, 1414, 1415, 1416, 1417, 1418, 1419, 1421, 1422, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1433, 1492, 1501, 1502; + setarray .ML_Asst, 1003, 1007, 1016, 1023, 1038, 1056, 1065, 1066, 1072, 1083, 1085, 1086, 1089, 1090, 1094, 1155, 1175, 1178, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1190, 1191, 1192, 1195, 1201, 1202, 1230, 1232, 1434, 1435; + setarray .ML_Loot, 1005, 1007, 1008, 1009, 1018, 1029, 1032, 1033, 1039, 1053, 1054, 1055, 1085, 1086, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1124, 1125, 1126, 1138, 1167, 1175, 1178, 1180, 1181, 1187, 1190, 1191, 1192, 1193, 1194, 1195, 1198, 1212, 1230, 1233, 1234, 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1402, 1403, 1404, 1405, 1406, 1407, 1408; + setarray .ML_Lv50, 1002, 1006, 1010, 1011, 1017, 1022, 1025, 1028, 1030, 1031, 1034, 1035, 1041, 1043, 1049, 1050, 1060, 1067, 1068, 1070, 1071, 1073, 1075, 1121, 1127, 1128, 1132, 1133, 1152, 1172, 1173, 1204, 1210, 1219, 1436, 1437, 1438; + setarray .ML_Lv99, 1004, 1069, 1078, 1139, 1151, 1171, 1197, 1215, 1216, 1217, 1218, 1493, 1494; + end; +} + + + +- script @aeros 32767,{ + end; + +OnCall: + if (!is_gm()) + end; + if (getmapname() != "001-1") { + dispbottom "This command can only be used at aeros."; + end; + } + if ($HARDCORE) { + dispbottom "Aeros cannot be used on Hardcore servers."; + end; + } + //.@atcmd_parameters$ = strtoupper(strip( // nah + + if (.@atcmd_numparameters != 2) { + dispbottom "Use the numeric ID provided by the wiki. This is a safeguard to ensure you are not overkilling players."; + dispbottom "Talk to Mana Being for a less fine-grained but much more optimized control over monsters."; + dispbottom "This command takes exactly this syntax: Mob ID <space> amount."; + end; + } + + switch($@AEROS_SPWN) { + case 1: + areamonster("001-1", 20, 20, 140, 140, strmobinfo(1, atoi(.@atcmd_parameters$[0])), atoi(.@atcmd_parameters$[0]), atoi(.@atcmd_parameters$[1]), "Mana Being#001-1::OnAerosMobDeath"); + break; + case 2: + areamonster("001-1", 20, 20, 340, 160, strmobinfo(1, atoi(.@atcmd_parameters$[0])), atoi(.@atcmd_parameters$[0]), atoi(.@atcmd_parameters$[1]), "Mana Being#001-1::OnAerosMobDeath"); + break; + default: + areamonster("001-1", 171, 20, 340, 160, strmobinfo(1, atoi(.@atcmd_parameters$[0])), atoi(.@atcmd_parameters$[0]), atoi(.@atcmd_parameters$[1]), "Mana Being#001-1::OnAerosMobDeath"); + break; + } + logmes "@aeros "+strmobinfo(1, atoi(.@atcmd_parameters$[0])) + " " + .@atcmd_parameters$[1], LOGMES_ATCOMMAND; + dispbottom "All monsters summoned."; + + end; + +OnInit: + bindatcmd "aeros", "@aeros::OnCall", 80, 99, 0; + end; +} diff --git a/npc/001-1/mahul.txt b/npc/001-1/mahul.txt new file mode 100644 index 0000000..fa4cd28 --- /dev/null +++ b/npc/001-1/mahul.txt @@ -0,0 +1,57 @@ +// TMW-2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Mahul is the Aeros Well Master + +001-1,299,45,0 script Mahul NPC_PLAYER,{ + mesn; + mesq l("Uhul! My name is Mahul!"); + mes l("I can fill your bottle with water for only @@ gp the bottle.", .COST_PER_BOTTLE); + mes l("After all, I am the Well Master!"); + input .@count; + + if (.@count == 0) + close; + .@Cost = .@count * .COST_PER_BOTTLE; + .@empty = countitem(EmptyBottle); + + if (.@empty < .@count) + goto L_NotEnoughBottles; + if (Zeny < .@Cost) + goto L_NotEnoughMoney; + inventoryplace BottleOfDivineWater, .@count; + + Zeny=Zeny-.@Cost; + delitem EmptyBottle, .@count; + getitem BottleOfDivineWater, .@count; + close; + +L_NotEnoughBottles: + mes ""; + mesn; + mes l("You don't have that many empty bottles!"); + close; + +L_NotEnoughMoney: + mes ""; + mesn; + mes l("You don't have enough gold! You need @@ gp.", .@Cost); + close; + +OnInit: + .COST_PER_BOTTLE = 100; + .sex = G_MALE; + .distance = 7; + + .@npcId = getnpcid(.name$); + // Check items.xml for info about this + setunitdata(.@npcId, UDT_HEADTOP, InfantryHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + end; +} diff --git a/npc/001-1/mapflags.txt b/npc/001-1/mapflags.txt new file mode 100644 index 0000000..4dd9b7a --- /dev/null +++ b/npc/001-1/mapflags.txt @@ -0,0 +1,9 @@ +001-1 mapflag zone Aeros +001-2 mapflag zone Event +001-3 mapflag zone Event +001-3-1 mapflag zone Event + +001-4 mapflag zone Aeros +001-5 mapflag zone Aeros +001-11 mapflag zone Aeros +001-12 mapflag zone Aeros diff --git a/npc/001-1/portal.txt b/npc/001-1/portal.txt new file mode 100644 index 0000000..3dbcc32 --- /dev/null +++ b/npc/001-1/portal.txt @@ -0,0 +1,136 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Manages warps at Aeros + +001-1,235,25,0 script Worlds Gate NPC_HIDDEN,1,0,{ + +OnTouch: + mesn; + mes l("This Portal can send your soul back to the world, along any items, money and/or experience gained."); + mes l("Would you like to leave this place?"); + menu + l("No."), L_Close, + rif(is_master() && !$@GM_EVENT, l("Enable Event")), L_Enable, + rif(is_master() && $@GM_EVENT, l("Disable Event")), L_Disable, + l("Yes."), L_Leave; + +L_Leave: + warp getsavepoint(0), getsavepoint(1), getsavepoint(2); + goto L_Close; + +L_Enable: + mesc l("WARNING: Deprecated!"), 1; + mesc l("Please don't use this function in future!"); + $@GM_EVENT=1; + close; + +L_Disable: + $@GM_EVENT=0; + $@MK_SCENE=MK_NONE; + close; + +// Uses l() to translate utilities +L_TranslationFix: + // Mobs + mes l("Magic Maggot"); + mes l("Monster"); + mes l("Monster King"); + mes l("Monster General"); + mes l("Monster Admiral"); + mes l("Monster Major"); + mes l("Monster Captain"); + mes l("Monster Lieutenant"); + mes l("Monster Sergeant"); + mes l("Monster Soldier"); + mes l("Random Bif"); + mes l("Summoned Monster"); + mes l("Scout"); + mes l("Desert Pirate"); + mes l("Marsh Pirate"); + mes l("Buccaneer"); + mes l("Corsair"); + mes l("Pirate Lord"); + mes l("Duck Soldier"); + mes l("Duck Initiate"); + // Configs + mes l("Human"); + mes l("Ukar"); + mes l("Redy"); + mes l("Elf"); + mes l("Orc"); + mes l("Raijin"); + mes l("Tritan"); + // Messages + mes l("All monsters summoned!"); + mes l("Mercy has been granted."); + mes l("Judgement has passed."); + mes l("Warping to save point."); + mes l("Your save point has been changed."); + mes l("Warped."); + mes l("Item created."); + mes l("Welcome to TMW-2: Moubootaur Legends! We hope you have a great time in our server!"); + mes l("You have been jailed by a GM."); + mes l("A GM has discharged you from jail."); + mes l("This item cannot be dropped."); + mes l("This item cannot be sold."); + mes l("This item cannot be auctioned."); + mes l("This item cannot be traded."); + mes l("This item cannot be stored."); + // Announcements + mes l("I can't handle it anymore! NO MORE!"); + mes l("Come, my minions! Lay siege to towns! LEAVE NO OPPOSITION TO ME!"); + mes l("Burn, destroy, do whatever you need, until your last breath, my lieutenants and colonels!"); + mes l("The event is over!"); + mes l("The mana bridge to Aeros is open! To participate on event, talk to ##BSoul Menhir##b!"); + mes l("WARNING: Server will go down for scheduled maintenance in 15 minutes!"); + mes l("WARNING: Server will go down for scheduled maintenance in 10 minutes!"); + mes l("WARNING: Server will go down for scheduled maintenance in 5 minutes!"); + mes l("WARNING! WARNING! Monster Army is moving towards Hurnscald!!"); + mes l("WARNING! WARNING! Monster Army is moving towards Halinarzo!!"); + mes l("WARNING! WARNING! Monster Army is moving towards Nivalis!!"); + mes l("WARNING! WARNING! Monster Army is moving towards Tulimshar!!"); + mes l("People failed to rescue Cindy!"); + mes l("Players failed to defend the city!!"); + mes l("The city was defended with success! GG, everyone!"); + mes l("EVENT CANCELLED DUE TO PLAYER INACTIVITY"); + mes l("%s has EXILED %s from %s.", "", "", ""); + mes l("Aurora Events"); + mes l("Map cleared!"); + mes l("Moubootaur (Sealed)"); + mes l("Level Boss"); + // Legendary Messages + mesc l("WARNING: The %s is a %s. Besides being insanely powerful, no duplicate of them exist in the world. They can be tweaked freely and can hold multiple cards as well, and scale according to your level. Use its powers wisely. However, beware: This weapon cannot be traded except with \"@grantpower\" command, and if you abandon the world, the weapon will abandon you as well!", getitemlink(Acorn), b(l("legendary weapon"))), 1; + mesc l("%s, you did your best to avenge a fallen comrade. It is my wish that you continue protecting this world. Therefore, I bestow upon you, the legendary %s. Please use its powers to protect your friend and the world peace!", "", getitemlink(Acorn)), 2; + mesc l("%s, you did your best to protect this world inhabitants. It is my wish that you continue protecting this world. Therefore, I bestow upon you, the legendary %s. Please use its powers to protect your friend and the world peace!", "", getitemlink(Acorn)), 2; + mesc l("%s, you did your best to entertain me. It is my wish that you continue protecting this world. Therefore, I bestow upon you, the legendary %s. Please use its powers to protect your friend and the world peace!", "", getitemlink(Acorn)), 2; + mesc l("%s, you proved your worth today. It is my wish that you continue protecting this world. Therefore, I bestow upon you, the legendary %s. Please use its powers to protect your friend and the world peace!", "", getitemlink(Acorn)), 2; + mesc l("%s, your dedication is touching. It is my wish that you continue protecting this world. Therefore, I bestow upon you, the legendary %s. Please use its powers to protect your friend and the world peace!", "", getitemlink(Acorn)), 2; + + // Internal + mesc b(l(" 0 This file is automatically generated. Editing it will have no effect.")), 1; // ;- TRANSLATORS: Do not translate + mesc b(l(" 1 Please translate at https://transifex.com/arctic-games/moubootaur-legends/ instead.")), 1; // ;- TRANSLATORS: Do not translate + mes l("sample"); + close; + +L_Close: + closedialog; + close; +} + +// This is a copy for west Aeros +001-1,23,107,0 script World Gate 2 NPC_HIDDEN,1,0,{ +OnTouch: + mesn; + mes l("This Portal can send your soul back to the world, along any items, money and/or experience gained."); + mes l("Would you like to leave this place?"); + select + l("No."), + l("Yes."); + + if (@menu == 2) + warp getsavepoint(0), getsavepoint(1), getsavepoint(2); + closedialog; + close; +} diff --git a/npc/001-1/rewards.txt b/npc/001-1/rewards.txt new file mode 100644 index 0000000..8b96724 --- /dev/null +++ b/npc/001-1/rewards.txt @@ -0,0 +1,129 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Trades Strange Coins for useful items + +001-1,243,26,0 script Aeros Trader NPC_M_COINKEEPER,{ + mesn; + mesq l("Oh, hello there! Welcome to the Mana Plane Of Existence!"); + next; + mesn; + mesq l("In this wonderful realm, you can find and earn many @@, our currency!", getitemlink(StrangeCoin)); + next; + mesn; + mesq l("You can then trade these coins for items with me!"); + next; + openshop; + closedialog; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TopHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, GoldenWarlordPlate); + setunitdata(.@npcId, UDT_WEAPON, JeansChaps); + setunitdata(.@npcId, UDT_HEADBOTTOM, AssassinBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 25); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex = G_MALE; + .distance = 5; + + tradertype(NST_CUSTOM); + + // Rare Equipment selection. + // *these* are really rare! + // Maximum 30,000 + sellitem MysteriousFruit,10000; // This is OP + sellitem DoggyDog,9000; + sellitem CattyCat,9000; + sellitem DarkPulsar,6000; + sellitem ThunderStaff,5000; + sellitem PiouEgg,3915; + sellitem DeliciousCookie,3240; + sellitem SmileyCap, 1820; + sellitem EyePatch, 1620; + sellitem Doll, 1420; + sellitem TopHat, 1220; + sellitem HeartGlasses,960; + sellitem BowlerHat, 720; + sellitem AshUrn,630; + sellitem BrimmedFeatherHat, 540; + sellitem Googles,540; + sellitem LeatherBall, 480; + sellitem ClericCap, 440; + sellitem Barrel,400; + + // Temporary, Seasonal, for events, rare drops, next release + sellitem MercBoxEE,1200; + sellitem MercBoxDD,950; + sellitem MercBoxE,900; + sellitem MercBoxD,600; + sellitem ArcmageBoxset,500; + sellitem BoneAmmoBox,180; + sellitem ThornAmmoBox,80; + + // Dye Shop + // RARE 3 + sellitem PurpleDye, 300; + sellitem DarkRedDye, 270; + sellitem BlackDye, 250; + // RARE 2 + sellitem SilverDye, 200; + sellitem NavyBlueDye, 200; + sellitem BlueGrayDye, 200; + sellitem FuschiaDye, 200; + sellitem BrownDye, 200; + sellitem MauveDye, 200; + // Rare 1 + sellitem RedDye, 100; + sellitem CamelDye, 100; + sellitem CrimsonDye, 100; + sellitem KhakiDye, 100; + sellitem MintDye, 100; + // Normal + sellitem TealDye, 45; + sellitem PinkDye, 45; + sellitem GreenDye, 45; // Quest + sellitem LimeDye, 30; // Shop, 1200 GP + // Low rarity + sellitem BlueDye, 22; + sellitem YellowDye, 22; + sellitem ChocolateDye, 22; + sellitem OrangeDye, 22; // Shop, 495 GP + + // Consumables + sellitem MagicApple,115; + sellitem SacredLifePotion,60; + sellitem SacredManaPotion,60; + sellitem ElixirOfLife,32; + sellitem ScrollMagnusHealA,28; + sellitem WhiskeyAle,28; + sellitem YerbaMate,22; + sellitem CelestiaTea,17; + sellitem BottleOfDivineWater, 15; + sellitem PrecisionPotion,9; + sellitem DodgePotion,9; + sellitem Wurtzite,8; + sellitem Curshroom,6; + sellitem PetcaringGuide,5; // I needed to add this somewhere + sellitem ScrollSCave,3; + sellitem SmokeGrenade,3; + end; + +/* set currency to be item 828 */ +OnCountFunds: + setcurrency(countitem(StrangeCoin)); + end; + +/* @price is total cost. @points is if we accept two items as currency. */ +OnPayFunds: + //dispbottom "Hi: price="+@price+" and points="+@points; + if( countitem(StrangeCoin) < @price ) + end; + delitem StrangeCoin,@price; + purchaseok(); + end; + +} diff --git a/npc/001-1/wateranimation.txt b/npc/001-1/wateranimation.txt new file mode 100644 index 0000000..7c14358 --- /dev/null +++ b/npc/001-1/wateranimation.txt @@ -0,0 +1,29 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Water animations, splash, fishes, etc... + +001-1,254,70,0 script #water_animation_aeros0 NPC_WATER_SPLASH,{ + + fishing(3, CommonCarp, Roach, Tench, + GrassCarp); // begin or continue fishing + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +001-1,250,72,0 duplicate(#water_animation_aeros0) #water_animation_aeros1 NPC_WATER_SPLASH +001-1,254,76,0 duplicate(#water_animation_aeros0) #water_animation_aeros2 NPC_WATER_SPLASH +001-1,247,77,0 duplicate(#water_animation_aeros0) #water_animation_aeros3 NPC_WATER_SPLASH + +001-1,105,112,0 duplicate(#water_animation_aeros0) #water_animation_aeros4 NPC_WATER_SPLASH +001-1,99,112,0 duplicate(#water_animation_aeros0) #water_animation_aeros5 NPC_WATER_SPLASH +001-1,94,110,0 duplicate(#water_animation_aeros0) #water_animation_aeros6 NPC_WATER_SPLASH +001-1,90,112,0 duplicate(#water_animation_aeros0) #water_animation_aeros7 NPC_WATER_SPLASH + + diff --git a/npc/001-10-1/_import.txt b/npc/001-10-1/_import.txt new file mode 100644 index 0000000..6f5c2b5 --- /dev/null +++ b/npc/001-10-1/_import.txt @@ -0,0 +1,4 @@ +// Map 001-10-1: Dust Boss Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-10-1/mapflags.txt", +"npc/001-10-1/scripts.txt", diff --git a/npc/001-10-1/mapflags.txt b/npc/001-10-1/mapflags.txt new file mode 100644 index 0000000..c11a3fb --- /dev/null +++ b/npc/001-10-1/mapflags.txt @@ -0,0 +1,2 @@ +001-10-1 mapflag pvp +001-10-1 mapflag zone MMO diff --git a/npc/001-10-1/scripts.txt b/npc/001-10-1/scripts.txt new file mode 100644 index 0000000..080cae7 --- /dev/null +++ b/npc/001-10-1/scripts.txt @@ -0,0 +1,88 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Dusty in a Bottle (aka. Dustynator, Dustman, mr. willbelz) +// Description: +// Scripts for 001-10-1: Boss Fight! + +001-10-1,0,0,0 script #COD_BossManager NPC_HIDDEN,{ + end; + +// Spawn the Dust Boss +OnEventStart: + areamonster("001-10-1", 34, 29, 59, 52, strmobinfo(1, DustBoss), DustBoss, 1, "#COD_BossManager::OnVictory"); + initnpctimer; + end; + +// Dust boss was killed, add timers, special reward for victor +OnVictory: + if (getcharid(1) > 0) { + .pwin=getcharid(1); + getpartymember(getcharid(1)); + .@count = $@partymembercount; + .bonus=max(0, getmapusers("001-10-1")-.@count); + } else { + .win$=strcharinfo(0); + .bonus=0; + } + + getitem BottledDust, 1; + + maptimer("001-10-1", 100, "#COD_BossManager::OnReward1"); + maptimer("001-10", 100, "#COD_BossManager::OnReward2"); + donpcevent("Colonel DUSTMAN::OnCoDEnd"); + end; + +// Handle rewards: 10 dust for winner party on boss room, 1 dust for everyone else +// on boss room, 1 dust for winner party outside boss room +OnReward1: + if ((.pwin > 0 && getcharid(1) == .pwin) || strcharinfo(0) == .win$) + getitem BottledDust, 9+.bonus; + else + getitem BottledDust, 1; + // FALLTHROUGH +OnReward2: + if (.pwin && getcharid(1) == .pwin) { + setq2 LoFQuest_COD, getq2(LoFQuest_COD) + 1; + getitem BottledDust, 1; + } + warp "018-2-1", 24, 29; + specialeffect(FX_FANFARE, SELF, getcharid(3)); + end; + +// You ran out of time +OnTimeDefeat: + @COD_CHECKPOINT=0; + if (getmap() ~= "001-10") { + warp "018-2-1", 24, 29; + dispbottom l("COD: Ran out of time!"); + } + end; + +// Must reset for next challenge +OnCleanUp: + .pwin=0; // party winner + .win$=""; // fallback, if char not in party + killmonsterall("001-10-1"); + initnpctimer; + stopnpctimer; + end; + +// Every 20 seconds mobs are created if needed +OnTimer20000: + .@mi=mobcount("001-10-1", "all"); + .@pl=getmapusers("001-10-1"); + .@monsterId=any(DustRifle, DustGatling, DustRevolver); + if (.@pl > (.@mi-2)) + areamonster("001-10-1", 34, 29, 59, 52, strmobinfo(1, .@monsterId), .@monsterId, (.@pl-.@mi+2)); + initnpctimer; + end; + +// Setup +OnInit: + .pwin=0; // party winner + .win$=""; // fallback, if char not in party + end; +} + + diff --git a/npc/001-10/_import.txt b/npc/001-10/_import.txt new file mode 100644 index 0000000..eaa1599 --- /dev/null +++ b/npc/001-10/_import.txt @@ -0,0 +1,4 @@ +// Map 001-10: Call Of Dusty +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-10/mapflags.txt", +"npc/001-10/scripts.txt", diff --git a/npc/001-10/mapflags.txt b/npc/001-10/mapflags.txt new file mode 100644 index 0000000..867f5e9 --- /dev/null +++ b/npc/001-10/mapflags.txt @@ -0,0 +1,2 @@ +001-10 mapflag pvp +001-10 mapflag zone MMO diff --git a/npc/001-10/scripts.txt b/npc/001-10/scripts.txt new file mode 100644 index 0000000..305b298 --- /dev/null +++ b/npc/001-10/scripts.txt @@ -0,0 +1,147 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Dusty in a Bottle (aka. Dustynator, Dustman, mr. willbelz) +// Description: +// Scripts for 001-10: Snipers, Bottles and Warps + +// Boss room +001-10,89,43,0 script #COD_BossRoomCheck NPC_NO_SPRITE,0,0,{ + end; +OnTouch: + if (!@COD_CHECKPOINT) { + dispbottom l("Magic Barrier is active. You must give a whole circle on the desert to break it."); + } else { + deltimer("#COD_BossRoomCheck::OnTimeDefeat"); + addtimer(300000,"#COD_BossManager::OnTimeDefeat"); + // You can't return now! + warp "001-10-1", any(57, 58, 59), any(74, 75); + dispbottom l("A dangerous boss room, keep your guard up!"); + } + end; + +OnTimeDefeat: + @COD_CHECKPOINT=0; + if (getmap() ~= "001-10") { + warp "018-2-1", 24, 29; + dispbottom l("COD: Ran out of time!"); + } + end; + +} + +// Magic Barrier +001-10,132,106,0 script #COD_Checkpoint106 NPC_NO_SPRITE,0,0,{ + end; +OnTouch: + getmapxy(.@m$, .@x, .@y, 0); + slide .@x+2, .@y; + if (!@COD_CHECKPOINT) + dispbottom l("Checkpoint! Magic Barrier went down!"); + @COD_CHECKPOINT=1; + end; +} + +001-10,132,107,0 duplicate(#COD_Checkpoint106) #COD_Checkpoint107 NPC_NO_SPRITE,0,0 +001-10,132,108,0 duplicate(#COD_Checkpoint106) #COD_Checkpoint108 NPC_NO_SPRITE,0,0 +001-10,132,109,0 duplicate(#COD_Checkpoint106) #COD_Checkpoint109 NPC_NO_SPRITE,0,0 +001-10,132,110,0 duplicate(#COD_Checkpoint106) #COD_Checkpoint110 NPC_NO_SPRITE,0,0 +001-10,132,111,0 duplicate(#COD_Checkpoint106) #COD_Checkpoint111 NPC_NO_SPRITE,0,0 +001-10,132,112,0 duplicate(#COD_Checkpoint106) #COD_Checkpoint112 NPC_NO_SPRITE,0,0 +001-10,132,113,0 duplicate(#COD_Checkpoint106) #COD_Checkpoint113 NPC_NO_SPRITE,0,0 + +001-10,0,0,0 script #CODMASTER NPC_NO_SPRITE,{ + end; + function spawner { // (Event, x1, y1, x2, y2, Amount, modifier) + .@ev$=getarg(0, "#CODMASTER::OnDeath"); + .@x1=getarg(1,0); + .@y1=getarg(2,0); + .@x2=getarg(3,200); + .@y2=getarg(4,150); + .@am=getarg(5,1); + freeloop(true); + for (.@i = 0; .@i < .@am; ++.@i) { + if (!getarg(6,0)) + .@monsterId=any(DustRifle, DustGatling, DustRevolver); + else + .@monsterId=any(AngryBat, Snake, AngryBat, DesertBandit, AngryBat, Sarracenus); + areamonster("001-10", .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@monsterId), .@monsterId, 1, .@ev$); + + } + } + +// Death handlers +OnWLDeath: + if (playerattached()) { + if (rand2(0,1000) <= 20) + getitem BottledDust, 1; + } + spawner("#CODMASTER::OnWLDeath", 0, 0, 200, 150, 1, 1); + end; + +OnNDeath: + if (playerattached()) { + if (rand2(0,1000) <= 40) + getitem BottledDust, 1; + } + spawner("#CODMASTER::OnNDeath", 0, 10, 200, 25); + end; + +OnSDeath: + if (playerattached()) { + if (rand2(0,1000) <= 40) + getitem BottledDust, 1; + } + spawner("#CODMASTER::OnSDeath", 0, 129, 200, 150); + end; + +OnCDeath: + if (playerattached()) { + if (rand2(0,1000) <= 40) + getitem BottledDust, 1; + } + spawner("#CODMASTER::OnCDeath", 73, 45, 132, 86); + end; + +OnWDeath: + if (playerattached()) { + if (rand2(0,1000) <= 40) + getitem BottledDust, 1; + } + spawner("#CODMASTER::OnWDeath", 10, 25, 72, 122); + end; + +OnEDeath: + if (playerattached()) { + if (rand2(0,1000) <= 40) + getitem BottledDust, 1; + } + spawner("#CODMASTER::OnEDeath", 160, 45, 190, 130); + end; + +OnDeath: + if (playerattached()) { + if (rand2(0,1000) <= 40) + getitem BottledDust, 1; + } + spawner("#CODMASTER::OnDeath"); + end; + +///////////////////////////////////////////////////////////////////////////////// +/// on init block //// on init block ////// +///////////////////////////////////////////////////////////////////////////////// +OnInit: + // Spawn Wildlife + spawner("#CODMASTER::OnWLDeath", 0, 0, 200, 150, 12, 1); + + // Spawn other monsters + spawner("#CODMASTER::OnDeath", 0, 0, 200, 150, 3); + spawner("#CODMASTER::OnNDeath", 0, 10, 200, 25, 5); + spawner("#CODMASTER::OnSDeath", 0, 129, 200, 150, 3); + spawner("#CODMASTER::OnCDeath", 73, 45, 132, 86, 4); + spawner("#CODMASTER::OnWDeath", 10, 25, 72, 122, 5); + spawner("#CODMASTER::OnEDeath", 160, 45, 190, 130, 4); + + end; +} + diff --git a/npc/001-11/Demure.txt b/npc/001-11/Demure.txt new file mode 100644 index 0000000..06eb149 --- /dev/null +++ b/npc/001-11/Demure.txt @@ -0,0 +1,244 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Valentine Event Master + +001-11,40,25,0 script Demure#Valentine NPC_FEMALE,{ + goto L_Main; + + // Functions + function ScoreValentine { + + mes "##B"+l("Top 10 - Valentine Day")+"##b"; + mesc l("Leaderboard is refresh daily at 11:59 and 23:59!"); + mes("1." +$@valentine_name$[0]+" ("+$@valentine_value[0]+")"); + mes("2." +$@valentine_name$[1]+" ("+$@valentine_value[1]+")"); + mes("3." +$@valentine_name$[2]+" ("+$@valentine_value[2]+")"); + mes("4." +$@valentine_name$[3]+" ("+$@valentine_value[3]+")"); + mes("5." +$@valentine_name$[4]+" ("+$@valentine_value[4]+")"); + mes("6." +$@valentine_name$[5]+" ("+$@valentine_value[5]+")"); + mes("7." +$@valentine_name$[6]+" ("+$@valentine_value[6]+")"); + mes("8." +$@valentine_name$[7]+" ("+$@valentine_value[7]+")"); + mes("9." +$@valentine_name$[8]+" ("+$@valentine_value[8]+")"); + mes("10."+$@valentine_name$[9]+" ("+$@valentine_value[9]+")"); + next; + } + // $@VALENTINE_GIFTSTACKS + // (account id, gifts to receive) + // $@VALENTINE_LOVELETTER + // (account id, list of names) + function SendGift { + mesn; + mesq l("How many chocolate do you want to send? You can trade: @@", @maximus); + input @Val_Ammo; + mes ""; + + if (@Val_Ammo > @maximus || @Val_Ammo < 1) { + mesc l("You don't have that many!"); + return; + } + + do { + mesn; + mesq l("To whom you want to send @@ boxes? Cannot be yourself!", @Val_Ammo); + mesc l("Leave blank to abort."); + if (!#VALENTINE_SENT) + mesc l("(If you don't have anyone special to send these, send to @@. Perhaps they'll calm down with those T.T)", b("Monster King")), 3; + input .@ref$; + //debugmes "Sending chocolate to: "+.@ref$; + mes ""; + if (.@ref$ != "") { + .@ref=gf_accid(strip(.@ref$)); + if (.@ref > 0) { + if (.@ref == getcharid(3)) { + mesn; + mesq l("I said it cannot be yourself... </3"); + next; + } else { + // Now we do the magic, first of, delete the chocolate and the text input + delitem BoxOfChocolates, @Val_Ammo; + .@ref$=""; + + // Send the Chocolate + .@m = htget($@VALENTINE_GIFTSTACKS, str(.@ref)); + if (!.@m) + htput $@VALENTINE_GIFTSTACKS, str(.@ref), @Val_Ammo; + else + htput $@VALENTINE_GIFTSTACKS, str(.@ref), .@m+@Val_Ammo; + + // Get 1 Point per sent box + #VALENTINE_POINTS+=@Val_Ammo; + #VALENTINE_SENT+=@Val_Ammo; + + // Handle Love Letter + // TODO: Allow multiple attachment + if (countitem(LoveLetter)) { + mesn; + mesq l("Should I attach a love letter?"); + next; + .@totalneed=@Val_Ammo/10+(@Val_Ammo % 10 ? 1 : 0); + select + l("Yes, one"), + //rif(countitem(LoveLetter) >= .@totalneed, l("Yes, %d", .@totalneed)), + l("I'll input how many I want to attach."), + l("No"); + mes ""; + .@reply=@menu-1; + // Custom input + if (@menu == 2) { + mes l("How many %s should I attach?", getitemlink(LoveLetter)); + mesc l("Recommended: ##B%d##b | Max: %d", .@totalneed, countitem(LoveLetter)); + input .@totalneed; + if (.@totalneed > countitem(LoveLetter)) + .@totalneed=countitem(LoveLetter); + } + if (@menu != 3) { + delitem LoveLetter, (.@reply ? .@totalneed : 1); + + // Register your name on love letter + .@s$ = htget($@VALENTINE_LOVELETTER, str(.@ref), ""); + if (.@s$ == "") + htput $@VALENTINE_LOVELETTER, str(.@ref), strip(strcharinfo(0)); + else + htput $@VALENTINE_LOVELETTER, str(.@ref), .@s$+", "+strip(strcharinfo(0)); + + // Get the bonus + // Old rule: 1 point for 2 boxes, capped at 25 and minimum 1 extra point + // Sending over 50 boxes at once won't give bonus, give another Love Letter. + //#VALENTINE_POINTS+=min(25, max(1, @Val_Ammo/2)); + + // New rule: Double points up to 10 points + .@min=(.@reply ? .@totalneed*10 : 10); + #VALENTINE_POINTS+=min(.@min, @Val_Ammo); + + } // Love Letter OK + mesc l("Ok, done!"); + } // Not yourself + } // Valid Person + } else { + mesn; + mesq l("Oops, there is nobody known as @@ on this game.", .@ref$); + mesq l("Could you try again? There could be a typo!"); + next; + } // Invalid Input + } // Non void input + } while (.@ref$ != ""); + return; + } + function GetGift { + .@m = htget($@VALENTINE_GIFTSTACKS, str(getcharid(3))); + if (.@m > 0) { + mesq l("Hey, look, you have @@ boxes to collect!", .@m); + .@n$ = htget($@VALENTINE_LOVELETTER, str(getcharid(3)), l("Secret Admirer")); + mesq l("They were given with @@ by @@.", any(l("love"), l("passion"), l("affection")), .@n$); + + inventoryplace BoxOfChocolates, .@m; + getitem BoxOfChocolates, .@m; + #VALENTINE_RECEIVED+=.@m; + htput $@VALENTINE_GIFTSTACKS, str(getcharid(3)), 0; + htput $@VALENTINE_LOVELETTER, str(getcharid(3)), ""; + + } else { + mesq l("Sorry, you don't have any chocolate to pick up."); + } + + return; + } + +// Script begin +L_Main: + // Safety Check + if ($EVENT$ != "Valentine") { + if (is_staff()) + ScoreValentine(); + warp "Save", 0, 0; end; + } + + // Demure Main + mesn; + mesq l("That annoying guy on the side of the tree is annoying. I want to ban him."); + next; + mesn; + mesq l("Anyway, it is Valentine Day, a good day to trade @@ with your admired one!", getitemlink(BoxOfChocolates)); + next; + do { + mesc l("You currently have @@ points, @@ boxes of chocolate and @@ love letters.", #VALENTINE_POINTS, countitem(BoxOfChocolates), countitem(LoveLetter)); + @maximus=countitem(BoxOfChocolates); + if (#VALENTINE_RECEIVED-#VALENTINE_OPENED > 0) { + mesc l("@@ boxes of chocolate were given to you as a gift.", #VALENTINE_RECEIVED-#VALENTINE_OPENED), 1; + // Demure can just send Soren's Chocolate to somebody else. + // Everyone else cannot send their gift chocolates. Please don't lose the boxes. + if (!is_gm()) + @maximus-=#VALENTINE_RECEIVED-#VALENTINE_OPENED; + } + mes ""; + select + l("Information"), + l("Scoreboards"), + rif(!is_gm() && @maximus >= 0, l("Send Chocolate")), + rif(!is_gm(), l("Receive Chocolate")), + l("Okay, bye"); + mes ""; + + switch (@menu) { + case 1: + mesn; + mes lg("Valentine Day event consist in gathering @@ and sending them to your loved one.","Valentine Day event consist in gathering @@ and sending them to your loved one.", getitemlink(BoxOfChocolates)); + next; + mesn; + mes l("Don't worry if they don't correspond to you. Each box sent will grant you 1 event point."); + mes l("The prizes are only for the top 10. Loratay on Land Of Fire Village can make, for limited time, a @@ for you if you want.", getitemlink(ValentineDress)); + next; + mes l("You can optionally send a @@ along the chocolate box, so the person knows it was you who sent the chocolate.", getitemlink(LoveLetter)); + mes l("Sending the letter will give you double event points, but no more than 10 extra points per letter."); + next; + mes l("When receiving the chocolate, you must eat it before event ends to get a point for that!"); + mes l("These boxes cannot be sold, but they'll be deleted a while after the event ends."); + mes l("You also cannot send any boxes before eating any you've received, but Demure is an exception for this rule."); + next; + mes l("As with all TMW2 Major Events, the top 1 receive a special pet, and the other ranked ones get diverse rewards."); + mes l("There might be reward for anyone who doesn't gets a rank, but I never count on that. So, let's start?"); + mesc l("Note: You must have a char on the first slot to leaderboard work. Points are shared accross all chars on your account."), 1; + next; + if (gettime(GETTIME_YEAR) == 2020) { + mesc l("During Valentine 2020, you can get event equipment with the Soul Stone."); + mesc l("On the friday, the 14th, an additional hunting field will be open. Talk to Soul Stone to go there :>"); + next; + } + break; + case 2: + ScoreValentine(); + break; + case 3: + SendGift(); next; + break; + case 4: + GetGift(); next; + break; + } + } while (@menu != 5); + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, Cap); + setunitdata(.@npcId, UDT_HEADMIDDLE, RedStockings); + setunitdata(.@npcId, UDT_HEADBOTTOM, BunnyEars); + setunitdata(.@npcId, UDT_WEAPON, GMRobe); + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + + .sex = G_FEMALE; + .distance = 5; + // fallthrough + +OnClock2359: +OnClock1159: + if (season() == WINTER) + .@nb = query_sql("SELECT c.name, i.value FROM `acc_reg_num_db` AS i, `char` AS c WHERE i.key='#VALENTINE_POINTS' AND i.account_id=c.account_id AND c.char_num='0' ORDER BY i.value DESC LIMIT 10", $@valentine_name$, $@valentine_value); + end; +} + diff --git a/npc/001-11/Soren.txt b/npc/001-11/Soren.txt new file mode 100644 index 0000000..ed3e64f --- /dev/null +++ b/npc/001-11/Soren.txt @@ -0,0 +1,173 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// NPC obssessed in sending gifts to Demure + +001-11,37,25,0 script Soren xd#Valentine NPC_PLAYER,{ + mesn; + mesq l("I will harass Demure with so many chocolate that she'll leave this event map rolling!"); + if (gettime(GETTIME_YEAR) == 2020) + goto L_Purpose; + next; + +L_Main: + mesn; + mesq l("Do you want me to prepare more chocolate box for you? For that I'll need:"); + msObjective(countitem(ChocolateBar) >= 12, l("* @@/12 @@", countitem(ChocolateBar), getitemlink(ChocolateBar))); + msObjective(Zeny >= 200, l("* @@/200 GP", format_number(Zeny))); + if (countitem(ChocolateBar) < 12 || Zeny < 200) + close; + next; + if (askyesno() == ASK_NO) + close; + if (Zeny < 200) + warp "001-9", 0, 0; + + inventoryplace BoxOfChocolates, 1; + delitem ChocolateBar, 12; + Zeny=Zeny-200; + getitem BoxOfChocolates, 1; + mesn; + mesq l("Here you go!"); + if (countitem(ChocolateBar) >= 12) { + mesq l("Anything else?"); + next; + goto L_Main; + } + close; + +L_Purpose: + next; + select + l("You're creepy."), + l("Reset status"), + l("Change weapons"), + l("Open storage"), + l("Open shop"); + mes ""; + switch (@menu) { + case 2: + ConfirmStatusReset(1, false); + break; + case 3: + goto L_Swap; + break; + case 4: + closeclientdialog; + openstorage; + break; + case 5: + closeclientdialog; + shop .name$; + break; + } + close; + +L_Swap: + // Obtain item ID + if (countitem(Event1HSword)) + .@itemid=Event1HSword; + else if (countitem(Event2HSword)) + .@itemid=Event2HSword; + else if (countitem(EventBow)) + .@itemid=EventBow; + else if (countitem(EventWand)) + .@itemid=EventWand; + else + close; + + // Item list + delinventorylist(); + getinventorylist(); + .@idx=array_find(@inventorylist_id, .@itemid); + if (.@idx < 0) + Exception("Invalid index for ("+.@itemid+") "+getitemlink(.@itemid), RB_DEFAULT|RB_ISFATAL); + + select + rif(.@itemid != Event1HSword, l("One hand sword (average)")), + rif(.@itemid != Event2HSword, l("Two hands sword (strong, misses)")), + rif(.@itemid != EventBow, l("Bow (strong, slow, no evasion)")), + rif(.@itemid != EventWand, l("Wand (skill boost, pathetically weak)")), + l("Cancel"); + mes ""; + switch (@menu) { + case 1: + delitem .@itemid, 1; + getitembound2(Event1HSword, 1, 1, @inventorylist_refine[.@idx], + @inventorylist_attribute[.@idx], @inventorylist_card1[.@idx], + @inventorylist_card2[.@idx], @inventorylist_card3[.@idx], + @inventorylist_card4[.@idx], 1); + break; + case 2: + delitem .@itemid, 1; + getitembound2(Event2HSword, 1, 1, @inventorylist_refine[.@idx], + @inventorylist_attribute[.@idx], @inventorylist_card1[.@idx], + @inventorylist_card2[.@idx], @inventorylist_card3[.@idx], + @inventorylist_card4[.@idx], 1); + break; + case 3: + delitem .@itemid, 1; + getitembound2(EventBow, 1, 1, @inventorylist_refine[.@idx], + @inventorylist_attribute[.@idx], @inventorylist_card1[.@idx], + @inventorylist_card2[.@idx], @inventorylist_card3[.@idx], + @inventorylist_card4[.@idx], 1); + break; + case 4: + delitem .@itemid, 1; + getitembound2(EventWand, 1, 1, @inventorylist_refine[.@idx], + @inventorylist_attribute[.@idx], @inventorylist_card1[.@idx], + @inventorylist_card2[.@idx], @inventorylist_card3[.@idx], + @inventorylist_card4[.@idx], 1); + break; + } + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, CandorShorts); + setunitdata(.@npcId, UDT_HEADMIDDLE, CandorShirt); + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + + // Soren Shop + sleep(SHOPWAIT); + /* + sellitem AnimalBones, 90; + sellitem Candy, 35; + sellitem Piberries, 25; + sellitem OolongTea; + sellitem ChamomileTea; + sellitem TolchiArrow; + sellitem TolchiAmmoBox; + sellitem TrainingAmmoBox; + sellitem CrazyRum; + sellitem Beer; + sellitem GoldenApple, 7000; + sellitem MercBoxA, 1000; + sellitem Grenade; + sellitem SmokeGrenade; + sellitem LousyMoccasins; + sellitem LeatherQuiver, 3500; + sellitem AgiPotionA, 1000; + sellitem VitPotionA, 1000; + sellitem IntPotionA, 1000; + sellitem DexPotionA, 1000; + sellitem LukPotionA, 1000; + sellitem SacredLifePotion, 2500; + sellitem SacredManaPotion, 2500; + sellitem CandorWarpCrystal, 100; + //sellitem MagicApple; + //sellitem FishingRod; + //sellitem WurtziteOre; + // TODO: LottoBox - Random hat to distinguish players + sellitem DoggyDog; + */ + sellitem LousyMoccasins; // Debug so things don't crash + + .sex = G_MALE; + .distance = 5; + end; + +} + diff --git a/npc/001-11/_import.txt b/npc/001-11/_import.txt new file mode 100644 index 0000000..8474bc5 --- /dev/null +++ b/npc/001-11/_import.txt @@ -0,0 +1,6 @@ +// Map 001-11: Valentine Highlands +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-11/Demure.txt", +"npc/001-11/Soren.txt", +"npc/001-11/_mobs.txt", +"npc/001-11/event_soulmenhir.txt", diff --git a/npc/001-11/_mobs.txt b/npc/001-11/_mobs.txt new file mode 100644 index 0000000..5aebea5 --- /dev/null +++ b/npc/001-11/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-11: Valentine Highlands mobs +001-11,47,34,29,14 monster Lovely Fluffy 1050,8,16000,0 +001-11,48,34,32,16 monster Red Mushroom 1042,2,175000,50000 +001-11,74,48,1,2 monster Chocolate Slime 1180,1,220000,50000 diff --git a/npc/001-11/event_soulmenhir.txt b/npc/001-11/event_soulmenhir.txt new file mode 100644 index 0000000..68cd09a --- /dev/null +++ b/npc/001-11/event_soulmenhir.txt @@ -0,0 +1,164 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Special Soul Menhir which only allows leaving the map. + +001-11,37,31,0 script Soul Stone#001-11 NPC_SOUL_CLEAN,{ + mesn; + mes l("(A mystical aura surrounds this stone. It probably can return you home. What do you do?)"); + if (is_staff()) + mesc l("Current date: %d", numdate()), 9; + if (numdate() < 20200214) + mesc l("Additional Hunting Island will be released on Valentine Day!"); + + menu + l("Touch it."), L_Warp, + rif(!#VALENTINE_EQUIPMENT2020 && gettime(GETTIME_YEAR) == 2020, l("Obtain event equipment")), L_EVTC2020, + rif($@GM_OVERRIDE && getusers(1) >= 6, l("Warp to a mirror island.")), L_WarpMirror, + rif(numdate() >= 20200214, l("Warp to extra island!")), L_WarpEnchanted, + l("Leave it alone."), -; + close; + +L_Warp: + warp "Save", 0, 0; + close; + +L_WarpEnchanted: + warp "001-12", 56, 20; + close; + +// Done for multiple players +// FIXME This obviously will not work +L_WarpMirror: + .@u=getusers(1); + mesn; + mes l("To which mirrored island you want to warp?"); + .@i=0; + .@dest$="Stay here"; + // Create instances as needed + while (.@i < .@u/6) { + .@i+=1; + .@dest$+=sprintf(":Mirror Island %d", .@i); + if (!(getd("$@VALENTINE_MI"+.@i) && + isinstance(getd("$@VALENTINE_MI"+.@i)))) { + debugmes "Create Mirror Island %d", .@i; + + .@tmpist = instance_create("001-11@Mirror"+.@i, 0, IOT_NONE); + instance_attachmap("001-11", .@tmpist, 0, "001-11@MI"+.@i); + instance_set_timeout(1000000, 1000000, .@tmpist); + instance_init(.@tmpist); + setd("$@VALENTINE_MI"+.@i, .@tmpist); + } + } + select .@dest$; + if (@menu == 1) + close; + @menu-=1; + warp "001-11@MI"+@menu, 38, 32; + dispbottom l("Mirror Island %d", @menu); + /* + select + rif(.@u >= 6, l("Mirror Island 1")), + */ + closeclientdialog; + close; + +// Special labels +L_EVTC2020: + showavatar NPC_BARD_TRUMP; + mes l("Welcome! Here to pick a weapon? You'll be able to select three skill cards as well."); + next; + inventoryplace NPCEyes, 4, Iten, 1, OolongTea, 3, SpearmintTea, 5, CrazyRum, 1, DwarvenSake, 3; + mesc l("Are you sure you want to begin now? (Cannot be changed later)"), 1; + next; + if (askyesno() == ASK_NO) + close; + mes ""; + mes l("Select your preferred play-style."); + select + l("A soldier, with a sword and a shield"), + l("A warrior, with a long blade"), + l("A ranger, with a powerful bow"), + l("A wizard, with a might wand"); + mes ""; + #VALENTINE_EQUIPMENT2020=true; + switch (@menu) { + case 1: + getitembound Event1HSword, 1, 1; + getitembound any(RoundLeatherShield, LeatherShield), 1, 1; + getitem FalkonCard, 1; + break; + case 2: + getitembound Event2HSword, 1, 1; + getitem FalkonCard, 1; + Zeny+=150; + break; + case 3: + getitembound EventBow, 1, 1; + getitem ArrowShowerCard, 1; + getitem TolchiAmmoBox, 5; // 1,000 arrows should be plenty + break; + case 4: + getitembound EventWand, 1, 1; + getitem FireballCard, 1; + Zeny+=150; + break; + } + // Get misc items + getitem HealCard, 1; + getitem OolongTea, 3; + getitem SpearmintTea, 5; + getitem CrazyRum, 1; + getitem DwarvenSake, 3; + + // Ask for the skill set + mes ""; + mes l("Select a set of skills:"); + select + l("I need to survive anything!"), + l("I need support magic!"), + l("I need offensive magic!"), + l("I need supreme warrior skills!"), + l("I need supreme ranger skills!"); + mes ""; + switch (@menu) { + case 1: // Survival Magic + getitem LastStandCard, 1; + getitem CowardCard, 1; + break; + case 2: // Support Magic + getitem HighPriestCard, 1; + getitem FullPowerCard, 1; + break; + case 3: // Offensive Magic + getitem NatureWallCard, 1; + getitem MagicalMVPCard, 1; + break; + case 4: // Warrior Magic + getitem SupremeWarriorCard, 1; + getitem VersatileCard, 1; + break; + case 5: // Ranger Magic + getitem SupremeRangerCard, 1; + getitem VersatileCard, 1; + break; + } + mesn; + mes l("And, that's all."); + mesc l("Protip: You can trade the cards, but only before inserting them."), 9; + mesc l("You can make any build you want! But inserted cards cannot be removed!"), 9; + next; + mesn; + mes l("To insert a card, select the card and \"use\" it, or, drag and drop them to the weapon."); + mesc l("You can only insert up to 3 (three) cards per weapon!"), 1; + next; + mesn; + mes l("Event weapons and cards will be deleted once event ends."); + close; + +OnInit: + .distance = 5; + end; +} + diff --git a/npc/001-12/_import.txt b/npc/001-12/_import.txt new file mode 100644 index 0000000..33a1ed3 --- /dev/null +++ b/npc/001-12/_import.txt @@ -0,0 +1,4 @@ +// Map 001-12: Southeast Enchanted Forest +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-12/_mobs.txt", +"npc/001-12/event_soulmenhir.txt", diff --git a/npc/001-12/_mobs.txt b/npc/001-12/_mobs.txt new file mode 100644 index 0000000..99f05db --- /dev/null +++ b/npc/001-12/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-12: Southeast Enchanted Forest mobs +001-12,99,98,23,23 monster Clover Field 1028,2,90000,120000 +001-12,90,88,70,68 monster Lovely Fluffy 1050,80,22000,1000 +001-12,22,89,6,69 monster Duck 1029,4,60000,120000 +001-12,87,148,71,10 monster Duck 1029,4,60000,120000 +001-12,96,79,64,60 monster Red Mushroom 1042,20,175000,50000 +001-12,102,108,4,3 monster Chocolate Slime 1180,2,220000,50000 +001-12,80,139,85,28 monster Whirly Bird (BOSS) 1232,1,36000000,50000 +001-12,115,40,36,13 monster Angry Bat 1194,6,260000,50000 +001-12,94,93,4,3 monster Mana Piou 1155,1,220000,50000 +001-12,37,149,1,1 monster Mana Piou 1155,3,220000,50000 diff --git a/npc/001-12/event_soulmenhir.txt b/npc/001-12/event_soulmenhir.txt new file mode 100644 index 0000000..27a563c --- /dev/null +++ b/npc/001-12/event_soulmenhir.txt @@ -0,0 +1,29 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Special Soul Menhir which only allows leaving the map. + +001-12,54,16,0 script Soul Stone#001-12 NPC_SOUL_CLEAN,{ + mesn; + mes l("(A mystical aura surrounds this stone. It probably can return you home. What do you do?)"); + + menu + l("Touch it."), L_Warp, + l("Return to main island."), L_WarpEnchanted, + l("Leave it alone."), -; + close; + +L_Warp: + warp "Save", 0, 0; + close; + +L_WarpEnchanted: + warp "001-11", 37, 31; + close; + +OnInit: + .distance = 10; + end; +} + diff --git a/npc/001-13-0/_import.txt b/npc/001-13-0/_import.txt new file mode 100644 index 0000000..574eefa --- /dev/null +++ b/npc/001-13-0/_import.txt @@ -0,0 +1,2 @@ +// Map 001-13-0: Showdown +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/001-13-1/_import.txt b/npc/001-13-1/_import.txt new file mode 100644 index 0000000..7cdcf96 --- /dev/null +++ b/npc/001-13-1/_import.txt @@ -0,0 +1,2 @@ +// Map 001-13-1: Showdown +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/001-13-2/_import.txt b/npc/001-13-2/_import.txt new file mode 100644 index 0000000..58b5a77 --- /dev/null +++ b/npc/001-13-2/_import.txt @@ -0,0 +1,2 @@ +// Map 001-13-2: Showdown +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/001-13/_import.txt b/npc/001-13/_import.txt new file mode 100644 index 0000000..1ec37b7 --- /dev/null +++ b/npc/001-13/_import.txt @@ -0,0 +1,3 @@ +// Map 001-13: Showdown +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-13/main.txt", diff --git a/npc/001-13/main.txt b/npc/001-13/main.txt new file mode 100644 index 0000000..312cec1 --- /dev/null +++ b/npc/001-13/main.txt @@ -0,0 +1,246 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Controls boss raid showdown (Freeyorp Event System - Boss Raid) + +function script FYRaid_Select { + .@abort = getarg(0, false); + if ($EVENT$ != "Raid") return; + sleep2(100); // Anti-flood protection: Hold execution for 100ms + mes l("Current Boss: %s", $RAIDING_BOSS$); + .@id = array_find($FYRAID_OWNER, getcharid(3)); + if (.@id >= 0) { + // Same level (challenge ongoing) + if ($FYRAID_LV[.@id] == FYRAID_LV) { + // Was defeated when you weren't loooking (limited precedence) + if ($FYRAID_HP[.@id] <= 0) { + mesc l("Boss defeated!"), 3; + FYRAID_LV+=1; + $FYRAID_HP[.@id]=0; + Mobpt += FYRAID_LV * 10; + getitem EventNaftalin, FYRAID_LV; + } + // Time is running out + else if (gettimetick(2) < $FYRAID_TIME[.@id]) { + mesc l("You found a Level %d %s!", FYRAID_LV, $RAIDING_BOSS$), 2; + mesc l("Time left: %s", FuzzyTime($FYRAID_TIME[.@id])), 1; + } + // Time has expired - free for next boss + else if (gettimetick(2) > $FYRAID_TIME[.@id]) { + mesc l("The boss you discovered has ran away!"); + $FYRAID_LV[.@id] = 0; + $FYRAID_HP[.@id] = 0; + $FYRAID_TIME[.@id] = 0; + Mobpt += rand2(FYRAID_LV); + } + } + } + if (.@abort) + return; + next; + setarray .@opt$, l("Cancel"), -1; + freeloop(true); + // Build menu + for (.@i=0; .@i < getarraysize($FYRAID_OWNER) ; .@i++) { + if ($FYRAID_HP[.@i] <= 0) + continue; + if ($FYRAID_TIME[.@i] < gettimetick(2)) + continue; + .@hp=$FYRAID_HP[.@i]; + .@lv=$FYRAID_LV[.@i]; + .@on=$FYRAID_OWNER[.@i]; + array_push(.@opt$, (.@on == getcharid(3) ? "**" : "") + l("Level %d (%s HP) (Found by %s)", .@lv, .@hp, strcharinfo(0, l("offline player"), .@on)) + (.@on == getcharid(3) ? "**" : "")); + array_push(.@opt$, str(.@i)); + } + freeloop(false); + menuint2(.@opt$); + if (@menuret < 0) + return; + // Validate again if the prey is still valid + .@i = @menuret; + if ($FYRAID_HP[.@i] <= 0) { + mesc l("Someone else has already defeated this bounty."); + return; + } + if ($FYRAID_TIME[.@i] < gettimetick(2)) { + mesc l("This bounty has expired."); + return; + } + if (ispcdead()) { + mesc l("You are dead."); + return; + } + // Prepare the room (time limit = 3 minutes) + .@inst = instance_create("Showdown "+getcharid(0), getcharid(3), IOT_CHAR); + // Failed + if (.@inst < 0) { + mesc l("You can only try every %d minutes.", 3); + return; + } + // Attach map + // TODO: Different scenarios: Block Castle, Sewer, Concrete Dungeon, Crypt, Desert Castle, Mountain, Snowland, Lake Region, Ukar Shrine, Woodland... + .@lv = $FYRAID_LV[.@i]; + .@mp$="fyrb@"+getcharid(0); + instance_attachmap(sprintf("001-13-%d", (.@lv % 3)), .@inst, false, .@mp$); + + // Recreate the boss + .@mob=monster(.@mp$, 47, 33, $RAIDING_BOSS$, WanderingShadow, 1, "sBossRaid::OnBossDie"); + setunitdata(.@mob, UDT_LEVEL, min(.@lv * 7, 200)); + setunitdata(.@mob, UDT_STR, .@lv * 4); + setunitdata(.@mob, UDT_AGI, min(.@lv * 4, 255)); + setunitdata(.@mob, UDT_VIT, .@lv * 5); + setunitdata(.@mob, UDT_INT, .@lv * 2); + setunitdata(.@mob, UDT_DEX, .@lv * 6); + setunitdata(.@mob, UDT_LUK, .@lv * 5); + setunitdata(.@mob, UDT_ADELAY, max(640, 1672-(.@lv * 24))); + setunitdata(.@mob, UDT_MAXHP, 2000+.@lv*1000+(FYRAID_LV/5*500)); + setunitdata(.@mob, UDT_HP, $FYRAID_HP[.@i]); + setunitdata(.@mob, UDT_ATKMIN, 90+.@lv*10); + setunitdata(.@mob, UDT_ATKMAX, 90+.@lv*12); + setunitdata(.@mob, UDT_DEF, 10+.@lv*5); + setunitdata(.@mob, UDT_MDEF, 10+.@lv*3); + setunitdata(.@mob, UDT_HIT, (BaseLevel+.@lv)*45/10); + setunitdata(.@mob, UDT_FLEE, min((BaseLevel+.@lv)*27/10, 750)); + setunitdata(.@mob, UDT_CRIT, rand2(60, min(180, 60+.@lv))); + + // Save some persistent data + @map$=.@mp$; + @id=.@i; + @mb=.@mob; + @tm=gettimetick(2)+180; + + // Good luck! + instance_set_timeout(190, 190, .@inst); + instance_init(.@inst); + warp .@mp$, 47, 52; + addtimer 180000, "sBossRaid::OnTimeout"; + addtimer 40000, "sBossRaid::OnPump"; + dispbottom l("Time left: %s", FuzzyTime(@tm)); + closeclientdialog; + // TODO: Spawn an auxiliar every 10 levels + return; +} + +// ------------------------------------------------------------------------------ +- script sBossRaid NPC_HIDDEN,{ + +OnPump: + if (@map$ != getmap()) end; + .@msg$=l("In all the mana worlds, I alone am feared."); + .@lv=$FYRAID_LV[@id]; + .@t = min(45, 10+rand2(.@lv)); + + // Apply boss skill based on their name + if ($RAIDING_BOSS$ == "Xakabael the Dark") { + .@msg$ = l("Witness my sublime rain of death. Regeneration!"); + .@hp=getunitdata(@mb, UDT_HP); + .@mp=getunitdata(@mb, UDT_MAXHP); + setunitdata(@mb, UDT_HP, min(.@mp, .@hp+(.@lv * 50))); + .@mob=(rand2(.@lv) > 50 ? DeathCat : GreenSlime); + } else if ($RAIDING_BOSS$ == "Janeb the Evil") { + .@msg$ = l("Chaos shall be the founding stone of my town! Falling star!"); + percentheal -5, -10; + .@mob=(rand2(.@lv) > 50 ? BlackScorpion : RedSlime); + } else if ($RAIDING_BOSS$ == "Platyna the Red") { + .@msg$ = l("I, the rightful ruler, demand back this world! Tyranny!"); + percentheal -1, -1; + SC_Bonus(.@t, any(SC_BLIND, SC_POISON), 1); + .@mob=(rand2(.@lv) > 50 ? DarkLizard : Assassin); + } else if ($RAIDING_BOSS$ == "Benjamin the Frost") { + .@msg$ = l("Stop on your tracks, unfair being! Freeze!"); + SC_Bonus((.@t / 2), any(SC_FREEZE, SC_SLEEP, SC_SLEEP, SC_SLEEP), 1); + .@mob=(rand2(.@lv) > 50 ? BlueSlime : WhiteSlime); + } else if ($RAIDING_BOSS$ == "Reid the Terrific") { + .@msg$ = l("There is no free speech. Censorship!"); + SC_Bonus(.@t, SC_SILENCE, 1); + .@mob=(rand2(.@lv) > 50 ? Thug : RedMushroom); + } else if ($RAIDING_BOSS$ == "Nu'Rem the Destroyer") { + .@msg$ = l("And then... There was a quake. And all life died. Bleed!"); + SC_Bonus(.@t, SC_BLOODING, 1); + .@mob=(rand2(.@lv) > 50 ? BlackSlime : OldSnake); + } else if ($RAIDING_BOSS$ == "Golbenez the Cruel") { + .@msg$ = l("Puny mortal, do your best to entertain me! Curse!"); + SC_Bonus(.@t, SC_CURSE, 1); + .@mob=(rand2(.@lv) > 50 ? FireSkull : Skeleton); + } else if ($RAIDING_BOSS$ == "King of Typos") { + .@msg$ = l("The problem with typos is - unpredictable side effects."); + SC_Bonus(.@t, any(SC_SILENCE, SC_CURSE, SC_FREEZE, SC_BLOODING, SC_BLIND, SC_POISON, SC_DPOISON, SC_POISON, SC_BURNING, SC_SLEEP), 1); + .@mob=(rand2(.@lv) > 50 ? Swashbuckler : Bluepar); + } else { + consolewarn("Unknown raiding boss: %s. No skill will be used.", $RAIDING_BOSS$); + } + + unittalk(@mb, .@msg$); + dispbottom l("Time left: %s", FuzzyTime(@tm)); + // TODO: Maybe flush the boss HP to upstream to prevent farming? + addtimer max(20000, 46000-($FYRAID_LV[@id]*1000)), "sBossRaid::OnPump"; + // Spawn reinforcements when applicable + if (.@mob && rand2(100) < .@lv) { + monster(@map$, 47, 33, "Minion", .@mob, 1); + } + end; + +// Boss defeated +OnBossDie: + dispbottom l("Boss defeated!"); + // Clear bonus + getitem EventNaftalin, $FYRAID_LV[@id]/2+1; + getexp $FYRAID_LV[@id], $FYRAID_LV[@id]/2+1; + .@new = 0; + specialeffect(FX_FANFARE, SELF, getcharid(3)); + goto OnClose; + +// Ran out of time +OnTimeout: + dispbottom l("Time out!"); + .@new = getunitdata(@mb, UDT_HP); + goto OnClose; + +// What a noob, you died! +OnDie: + dispbottom l("Killed in action!"); + .@new = getunitdata(@mb, UDT_HP); + goto OnClose; + +// Warp you back +OnClose: + // Time Penalty every time you attack the boss yourself + if (.@new > 0 && $FYRAID_OWNER[@id] == getcharid(3)) + $FYRAID_TIME[@id] -= 300; + + // Update boss parameters + .@old = $FYRAID_HP[@id]; + .@dmg = .@old - .@new; + debugmes "Old %d New %d Damage %d", .@old, .@new, .@dmg; + // Damage Bonus + if (.@dmg / 2000 > 1) + getitem EventNaftalin, .@dmg / 2000; + + // Record new HP info and give you rewards + $FYRAID_HP[@id] = .@new; + Mobpt += $FYRAID_LV[@id]; + getexp $FYRAID_LV[@id], $FYRAID_LV[@id]/2+1; + + // Send you back + deltimer("sBossRaid::OnPump"); + deltimer("sBossRaid::OnTimeout"); + sleep2(500); + if (@aurora_map$ != "" && @aurora_x && @aurora_y) { + // TODO: Maybe not if you're dead...? (heal hack?) + warp @aurora_map$, @aurora_x, @aurora_y; + @aurora_map$=""; + @aurora_x=0; + @aurora_y=0; + } else { + teleporthome(); + } + FYRaid_Select(true); // Formally close the raid session + close; +} + +001-13 mapflag zone MMO +001-13-0 mapflag zone MMO +001-13-1 mapflag zone MMO +001-13-2 mapflag zone MMO + diff --git a/npc/001-14/_import.txt b/npc/001-14/_import.txt new file mode 100644 index 0000000..530b09c --- /dev/null +++ b/npc/001-14/_import.txt @@ -0,0 +1,6 @@ +// Map 001-14: Porthos - The Town Of Portals +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-14/_warps.txt", +"npc/001-14/ctrl.txt", +"npc/001-14/hocus.txt", +"npc/001-14/mapflags.txt", diff --git a/npc/001-14/_warps.txt b/npc/001-14/_warps.txt new file mode 100644 index 0000000..86f8a3f --- /dev/null +++ b/npc/001-14/_warps.txt @@ -0,0 +1,123 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-14: Porthos - The Town Of Portals warps +001-14,90,27,0 script #001-14_90_27 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 90,65; end; +} +001-14,62,33,0 script #001-14_62_33 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 79,70; end; +} +001-14,113,42,0 script #001-14_113_42 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 101,70; end; +} +001-14,135,57,0 script #001-14_135_57 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 110,78; end; +} +001-14,137,85,0 script #001-14_137_85 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 111,89; end; +} +001-14,135,113,0 script #001-14_135_113 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 109,102; end; +} +001-14,119,131,0 script #001-14_119_131 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 101,110; end; +} +001-14,90,139,0 script #001-14_90_139 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 90,114; end; +} +001-14,54,129,0 script #001-14_54_129 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 79,109; end; +} +001-14,39,110,0 script #001-14_39_110 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 71,101; end; +} +001-14,35,84,0 script #001-14_35_84 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 69,89; end; +} +001-14,43,46,0 script #001-14_43_46 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 71,78; end; +} +001-14,90,64,0 script #001-14_90_64 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 90,37; end; +} +001-14,101,69,0 script #001-14_101_69 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 113,49; end; +} +001-14,110,77,0 script #001-14_110_77 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 135,64; end; +} +001-14,111,90,0 script #001-14_111_90 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 137,92; end; +} +001-14,109,103,0 script #001-14_109_103 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 135,120; end; +} +001-14,101,111,0 script #001-14_101_111 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 119,138; end; +} +001-14,90,115,0 script #001-14_90_115 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 90,146; end; +} +001-14,79,110,0 script #001-14_79_110 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 54,136; end; +} +001-14,71,102,0 script #001-14_71_102 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 39,117; end; +} +001-14,69,90,0 script #001-14_69_90 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 35,91; end; +} +001-14,71,77,0 script #001-14_71_77 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 43,58; end; +} +001-14,79,69,0 script #001-14_79_69 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 62,40; end; +} +001-14,92,89,0 warp #001-14_92_89 0,0,000-1,22,22 diff --git a/npc/001-14/ctrl.txt b/npc/001-14/ctrl.txt new file mode 100644 index 0000000..070c1c5 --- /dev/null +++ b/npc/001-14/ctrl.txt @@ -0,0 +1,642 @@ +// TMW2 script +// Author: +// Jesusalva +// Description: +// Magic Olympics + +001-14,91,90,0 script #MOLY_Sign00 NPC_NO_SPRITE,{ + dispbottom l("Welcome to Porthos - The Town of Portals (Kaizei's æther / Moubootaur Legends)"); + end; + +OnInit: + .distance=2; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +// FIXME +001-14,88,27,0 script Hocus#MOLY1200 NPC_BLACKWIZARD,{ + npctalk l("Icicle Challenge"); + npctalk l("Not Yet Implemented - Sorry."); + end; + +OnInit: + .distance=7; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,111,42,0 script Hocus#MOLY0100 NPC_BLACKWIZARD,{ + if (gettimetick(2) < @moly_tick) end; + if (@moly_chall) end; + mes ".:: " + l("Energy Ball Challenge") + ":: ."; + mes l("The goal is simple - I'll spawn an energy ball and you must kill it quickly. You'll be disqualified if you equip a non-magical weapon at any time."); + dnext; + mes l("You'll have 60 seconds to kill as many balls as you can!"); + mesc l("NOTE: Beside wands and staves, %s, %s, and the %s count as magic weapons.", getitemlink(Judgement), getitemlink(DarkPulsar), getitemlink(Lightbringer)); + mes ""; + mesc l("Your progress thus far: %s sparks", fnum(FYMOLY_ENBALL)), 3; + next; + mesc l("Begin campaign?"), 1; + if (askyesno() == ASK_NO) close; + @moly_tick = gettimetick(2) + 60; + @moly_score = 0; + @moly_chall = 1; + .m$=getmap(); + @map$=getmap(); + .@m=areamonster(.m$, 108, 43, 118, 48, "Target", YellowSpark, 1, "Hocus#MOLY0100::OnClick"); + setunitdata(.@m, UDT_HP, 1); + setunitdata(.@m, UDT_MAXHP, 1); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + npctalk3 l("Countdown: 1 minute"); + addtimer 2000, instance_npcname("Hocus#MOLY0100")+"::OnCheck"; + initnpctimer; + closeclientdialog; + close; + +OnTimer15000: + npctalk "45 seconds"; + end; + +OnTimer30000: + npctalk "30 seconds"; + end; + +OnTimer45000: + npctalk "15 seconds"; + end; + +OnTimer55000: + npctalk col("5 seconds", 1); + end; + +OnTimer60000: + killmonsterall(.m$); + maptimer2(.m$, 10, "Hocus#MOLY0100::OnEnd"); + npctalk col("Time out!", 1); + stopnpctimer; + end; + +OnClick: + if (gettimetick(2) > @moly_tick) end; + if (@moly_chall != 1) end; + @moly_score+=1; + .@m=areamonster(@map$, 108, 43, 118, 48, "Target", YellowSpark, 1, "Hocus#MOLY0100::OnClick"); + .@hp=rand2(50, 100); + setunitdata(.@m, UDT_MAXHP, .@hp); + setunitdata(.@m, UDT_HP, .@hp); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + setunitdata(.@m, UDT_MODE, MD_CANMOVE); + setunitdata(.@m, UDT_DEF, 32760); + setunitdata(.@m, UDT_MDEF, 0); + setunitdata(.@m, UDT_LUK, 32760); + end; + +OnCheck: + if (@moly_chall != 1) end; + if (gettimetick(2) > @moly_tick) end; + .@wp = getequipid(EQI_HAND_R); + // Disarmed + if (.@wp < 1) { + addtimer(2000, instance_npcname("Hocus#MOLY0100")+"::OnCheck"); + end; + } + // Illegal weapon + if (.@wp != Judgement && + .@wp != DarkPulsar && + .@wp != Lightbringer && + getiteminfo(.@wp, ITEMINFO_SUBTYPE) != W_STAFF) { + @moly_score = -1; + @moly_tick = 0; + @moly_chall = 0; + dispbottom col(l("You have been disqualified - illegal weapon: %s", getitemname(.@wp)), 1); + stopnpctimer; + killmonsterall(getmap()); + end; + } + addtimer 2000, instance_npcname("Hocus#MOLY0100")+"::OnCheck"; + end; + +OnEnd: + if (@moly_chall != 1) end; + .@pts=getq2(Q_AuroraEvent); + setq2 Q_AuroraEvent, .@pts+rand2(1, 1+@moly_scr/10); + dispbottom l("End! Score this time: %d", @moly_score); + FYMOLY_ENBALL = max(FYMOLY_ENBALL, @moly_score); + @moly_score = 0; + @moly_chall = 0; + end; + +OnInit: + .distance=7; + .m$="001-14"; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,135,61,0 script Hocus#MOLY0200 NPC_BLACKWIZARD,{ + mes ".:: " + l("Intensive Mage Challenge") + ":: ."; + mes l("Description: Use a lot of magic spells, regardless of type or raw strength. Points earned by skill level."); + mesc l("Note: Passives and some skills (e.g. Resync) doesn't count."), 1; + mes ""; + mesc l("Your progress thus far: %s skills casted", fnum(FYMOLY_SPAMMY)), 3; + close; + +OnInit: + .distance=7; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +// FIXME +001-14,139,85,0 script Hocus#MOLY0300 NPC_BLACKWIZARD,{ + npctalk l("Fluffy Hunter Challenge"); + npctalk l("Not Yet Implemented - Sorry."); + end; + +OnInit: + .distance=7; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +// Note: Do player knows any chants? +001-14,135,117,0 script Hocus#MOLY0400 NPC_BLACKWIZARD,{ + mes ".:: " + l("Chanting Challenge") + ":: ."; + mes l("Description: Use chant-based magic. All chants score equally."); + mes ""; + mesc l("Your progress thus far: %s chants casted", fnum(FYMOLY_CHANTI)), 3; + close; + +OnInit: + .distance=7; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,119,135,0 script Hocus#MOLY0500 NPC_NICHOLAS,{ + mes ".:: " + l("Alchemy Master Challenge") + ":: ."; + mes l("Description: Whoever crafts more potions, regardless of type, wins this event."); + mes ""; + mesc l("Your progress thus far: %s potions baked", fnum(FYMOLY_ALCHMY)), 3; + next; + // FREE brewing during event + do { + mesc l("What will you brew today?"); + mes ""; + + if (AlchemySystem(CRAFT_PLAYER)) { + mesc l("Success!"), 3; + } else { + mesc l("That didn't work!"), 1; + } + next; + mesc l("Try again?"); + } while (askyesno() == ASK_YES); + close; + +OnInit: + .distance=7; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,88,139,0 script Hocus#MOLY0600 NPC_BLACKWIZARD,{ + if (@moly_chall) end; + mes ".:: " + l("Magic Raw Power Challenge") + ":: ."; + mes l("Description: For starters, I'll blow away all your summons and summon an ent. You must do the most damage to it within 10 seconds."); + mes l("Using potions and support magic is fine. It has high defense, so magic attacks work better."); + mesc l("Only magic weapons are allowed, but legendary ones - %s and %s - won't be tolerated.", getitemlink(Runestaff), getitemlink(Lightbringer)), 1; + mes ""; + mesc l("Your progress thus far: %s damage inflicted", fnum(FYMOLY_MPWLVL)), 3; + next; + mesc l("Begin campaign?"), 1; + if (askyesno() == ASK_NO) close; + killmonsterall(getmap()); + .@m=areamonster(getmap(), 87, 140, 93, 145, strmobinfo(1, EntAbomination), EntAbomination, 1); + setunitdata(.@m, UDT_MAXHP, 1000000); + setunitdata(.@m, UDT_HP, 1000000); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + setunitdata(.@m, UDT_MODE, MD_CANMOVE|MD_CANATTACK|MD_BOSS); + setunitdata(.@m, UDT_DEF, 999); + setunitdata(.@m, UDT_MDEF, 0); + setunitdata(.@m, UDT_LUK, 32760); + .mob = .@m; + .pid = getcharid(3); + @moly_tick = gettimetick(2) + 60; + @moly_score = 0; + @moly_chall = 6; + npctalk "10"; + addtimer 550, instance_npcname("Hocus#MOLY0600")+"::OnCheck"; + initnpctimer; + closeclientdialog; + close; + +OnCheck: + if (@moly_chall != 6) end; + if (gettimetick(2) > @moly_tick) end; + .@wp = getequipid(EQI_HAND_R); + // Disarmed + if (.@wp < 1) { + addtimer(550, instance_npcname("Hocus#MOLY0600")+"::OnCheck"); + end; + } + // Illegal weapon + if ((.@wp == Lightbringer || .@wp == Runestaff) || + (.@wp != Judgement && + .@wp != DarkPulsar && + getiteminfo(.@wp, ITEMINFO_SUBTYPE) != W_STAFF)) { + @moly_score = -1; + @moly_tick = 0; + @moly_chall = 0; + dispbottom col(l("You have been disqualified - illegal weapon: %s", getitemname(.@wp)), 1); + stopnpctimer; + killmonsterall(getmap()); + end; + } + addtimer 550, instance_npcname("Hocus#MOLY0600")+"::OnCheck"; + end; + +OnTimer1000: + npctalk "9"; end; +OnTimer2000: + npctalk "8"; end; +OnTimer3000: + npctalk "7"; end; +OnTimer4000: + npctalk "6"; end; +OnTimer5000: + npctalk "5"; end; +OnTimer6000: + npctalk "4"; end; +OnTimer7000: + npctalk col("3", 1); end; +OnTimer8000: + npctalk col("2", 1); end; +OnTimer9000: + npctalk col("1", 1); end; +OnTimer10000: + npctalk col("Time out!", 1); + stopnpctimer; + attachrid(.pid); + if (@moly_chall != 6) end; + @moly_score = (getunitdata(.mob, UDT_HP) - getunitdata(.mob, UDT_MAXHP)) * -1; + killmonsterall(getmap()); + .@pts=getq2(Q_AuroraEvent); + setq2 Q_AuroraEvent, .@pts+rand2(1, 1+@moly_score/1500); + dispbottom l("End! Score this time: %d", @moly_score); + FYMOLY_MPWLVL = max(FYMOLY_MPWLVL, @moly_score); + @moly_score = 0; + @moly_chall = 0; + end; + +OnInit: + .distance=7; + .mob = 0; + .pid = 0; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,54,133,0 script Hocus#MOLY0700 NPC_BLACKWIZARD,{ + mes ".:: " + l("Mana Experience Challenge") + ":: ."; + mes l("Description: Similar to Intensive Mage, but measures mana experience - meaning even less skills will count, and swapping skills give a better effect."); + mes ""; + mesc l("Your progress thus far: %s exp earned", fnum(FYMOLY_MANAXP)), 3; + close; + +OnInit: + .distance=7; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,39,114,0 script Hocus#MOLY0800 NPC_BLACKWIZARD,{ + if (@moly_chall) end; + mes ".:: " + l("Maze Race Challenge") + ":: ."; + mes l("Description: Reach the other side of the maze and defeat the %s as quick as possible.", getmonsterlink(EntAbomination)); + mes l("There'll be monsters, the maze is randomly generated, and the initial position for you and the Ent is not fixed. Therefore, you may begin handicapped in some cases depending on luck only."); + mesc l("NOTE: No equipment restriction. Less time is better."); + mes ""; + mesc l("Your progress thus far: %s taken.", FuzzyTime(gettimetick(2) + FYMOLY_RACERS)), 3; + next; + mesc l("Begin campaign?"), 1; + if (askyesno() == ASK_NO) close; + addtimer 2500, instance_npcname("Hocus#MOLY0800")+"::OnCheck"; + @map$ = getmap(); + @moly_tick = gettimetick(2); // Will be used to determine score + @moly_score = 0; // Actually unused + @moly_chall = 8; + + CreateMaze(IOT_CHAR); + MazeMobs((max(6, BaseLevel) - 5), false, rand2(12,16)); + InitMaze(900, false, false); + + // Create the target boss + .@mx=getmapinfo(MAPINFO_SIZE_X, MAZE_MAP$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, MAZE_MAP$)-20; + .@m=areamonster(MAZE_MAP$, .@mx-20, .@my-20, .@mx, .@my, strmobinfo(1, EntAbomination), EntAbomination, 1, "Hocus#MOLY0800::OnWin"); + setunitdata(.@m, UDT_MAXHP, 100); + setunitdata(.@m, UDT_HP, 100); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_MODE, MD_BOSS); + setunitdata(.@m, UDT_LUK, 32760); + closeclientdialog; + end; + +OnWin: + warp @map$, 39, 117; + if (@moly_tick < gettimetick(2) / 2) end; // Ticks were Corrupted + @moly_score = gettimetick(2) - @moly_tick; + if (@moly_score < 8) end; // This is impossible*, don't even bother + .@pts=getq2(Q_AuroraEvent); + setq2 Q_AuroraEvent, .@pts+rand2(1, 1+BaseLevel/20); + dispbottom l("End! Score this time: %s", FuzzyTime(@moly_tick)); + if (FYMOLY_RACERS >= 8) + FYMOLY_RACERS = min(FYMOLY_RACERS, @moly_score); + else + FYMOLY_RACERS = @moly_score; + @moly_score = 0; + @moly_chall = 0; + // *: Distance is always at least ~50 tiles in a straight line + end; + +OnCheck: + if (@moly_chall != 8) end; + // Game Over + if (ispcdead() || + getmap() != MAZE_MAP$) { // NOT a typo - starting square is OK + // Game Over! + dispbottom col(l("You have been disqualified - died or left the maze"), 1); + @moly_score = 0; + @moly_chall = 0; + end; + } + addtimer 2500, instance_npcname("Hocus#MOLY0800")+"::OnCheck"; + end; + +OnInit: + .distance=7; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,37,84,0 script Hocus#MOLY0900 NPC_BLACKWIZARD,{ + if (@moly_chall) end; + mes ".:: " + l("Hocus Said So Challenge") + ":: ."; + mes l("Description: There'll be differently colored sparks and you must kill ONLY those of the color I say so! Failure to do so will cause immediate disqualification!"); + mesc l("PS. No equipment restriction"); + mes ""; + mesc l("Your progress thus far: %s sparks killed", fnum(FYMOLY_HOCUSM)), 3; + next; + mesc l("Begin campaign?"), 1; + if (askyesno() == ASK_NO) close; + @moly_tick = gettimetick(2) + 60; + @moly_score = 0; + @moly_chall = 9; + .m$=getmap(); + @map$=getmap(); + .@m=areamonster(.m$, 38, 85, 40, 90, strmobinfo(1, YellowSpark), YellowSpark, 1, "Hocus#MOLY0900::OnClY"); + setunitdata(.@m, UDT_HP, 1); + setunitdata(.@m, UDT_MAXHP, 1); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + .@m=areamonster(.m$, 38, 85, 40, 90, strmobinfo(1, MagentaSpark), MagentaSpark, 1, "Hocus#MOLY0900::OnClM"); + setunitdata(.@m, UDT_HP, 1); + setunitdata(.@m, UDT_MAXHP, 1); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + .@m=areamonster(.m$, 38, 85, 40, 90, strmobinfo(1, YellowSpark), YellowSpark, 1, "Hocus#MOLY0900::OnClY"); + setunitdata(.@m, UDT_HP, 1); + setunitdata(.@m, UDT_MAXHP, 1); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + .@m=areamonster(.m$, 38, 85, 40, 90, strmobinfo(1, MagentaSpark), MagentaSpark, 1, "Hocus#MOLY0900::OnClM"); + setunitdata(.@m, UDT_HP, 1); + setunitdata(.@m, UDT_MAXHP, 1); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + @cl=any(YellowSpark, MagentaSpark); + npctalk3 l("Countdown: 1 minute - Kill %s!", strmobinfo(1, @cl)); + initnpctimer; + closeclientdialog; + close; + +OnTimer9000: + npctalk "Hocus commands..."; + end; + +OnTimer10000: + maptimer2(.m$, 10, "Hocus#MOLY0900::OnSeed"); + npctalk sprintf("Time left = 50 seconds"); + end; + +OnTimer19000: + npctalk "Hocus commands..."; + end; + +OnTimer20000: + maptimer2(.m$, 10, "Hocus#MOLY0900::OnSeed"); + npctalk sprintf("Time left = 40 seconds"); + end; + +OnTimer29000: + npctalk "Hocus commands..."; + end; + +OnTimer30000: + maptimer2(.m$, 10, "Hocus#MOLY0900::OnSeed"); + npctalk sprintf("Time left = 30 seconds"); + end; + +OnTimer39000: + npctalk "Hocus commands..."; + end; + +OnTimer40000: + maptimer2(.m$, 10, "Hocus#MOLY0900::OnSeed"); + npctalk sprintf("Time left = 20 seconds"); + end; + +OnTimer49000: + npctalk "Hocus commands..."; + end; + +OnTimer50000: + maptimer2(.m$, 10, "Hocus#MOLY0900::OnSeed"); + npctalk sprintf("Time left = 10 seconds"); + end; + +OnTimer55000: + npctalk col("5 seconds", 1); + end; + +OnTimer60000: + killmonsterall(.m$); + maptimer2(.m$, 10, "Hocus#MOLY0900::OnEnd"); + npctalk col("Time out!", 1); + stopnpctimer; + end; + +OnSeed: + @cl=any(YellowSpark, MagentaSpark); + dispbottom l("Kill %s!", strmobinfo(1, @cl)); + end; + +OnClY: + if (gettimetick(2) > @moly_tick) end; + if (@moly_chall != 9) end; + if (@cl != YellowSpark) { + @moly_score = -1; + @moly_tick = 0; + @moly_chall = 0; + dispbottom col(l("You have been disqualified - target was: %s", strmobinfo(1, @cl)), 1); + killmonsterall(getmap()); + // FIXME: Stop npc timer + end; + } + @moly_score+=1; + .@m=areamonster(getmap(), 38, 85, 40, 90, strmobinfo(1, YellowSpark), YellowSpark, 1, "Hocus#MOLY0900::OnClY"); + setunitdata(.@m, UDT_HP, 1); + setunitdata(.@m, UDT_MAXHP, 1); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + end; + +OnClM: + if (gettimetick(2) > @moly_tick) end; + if (@moly_chall != 9) end; + if (@cl != MagentaSpark) { + @moly_score = -1; + @moly_tick = 0; + @moly_chall = 0; + dispbottom col(l("You have been disqualified - target was: %s", strmobinfo(1, @cl)), 1); + killmonsterall(getmap()); + // FIXME: Stop npc timer + end; + } + @moly_score+=1; + .@m=areamonster(getmap(), 38, 85, 40, 90, strmobinfo(1, MagentaSpark), MagentaSpark, 1, "Hocus#MOLY0900::OnClM"); + setunitdata(.@m, UDT_HP, 1); + setunitdata(.@m, UDT_MAXHP, 1); + setunitdata(.@m, UDT_LEVEL, 1); + setunitdata(.@m, UDT_SPEED, 60); + end; + +OnEnd: + if (@moly_chall != 9) end; + .@pts=getq2(Q_AuroraEvent); + setq2 Q_AuroraEvent, .@pts+rand2(1, 1+@moly_score/10); + dispbottom l("End! Score this time: %d", @moly_score); + FYMOLY_HOCUSM = max(FYMOLY_HOCUSM, @moly_score); + @moly_score = 0; + @moly_chall = 0; + end; + +OnInit: + .distance=7; + .m$="001-14"; + .cl=Dummy; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,41,46,0 script Hocus#MOLY1000 NPC_BLACKWIZARD,{ + if (@moly_chall) end; + mes ".:: " + l("Survival Challenge") + ":: ."; + mes l("Description: Survive for the longest you can without leaving the designed region."); + mes l("Using potions and support magic is fine, as well as summons; but using a non-magical weapon will disqualify you."); + mesc l("NOTE: Beside wands and staves, %s, %s, and the %s count as magic weapons.", getitemlink(Judgement), getitemlink(DarkPulsar), getitemlink(Lightbringer)); + mes ""; + mesc l("Your progress thus far: %s survived.", FuzzyTime(gettimetick(2) + FYMOLY_SURVIV)), 3; + next; + mesc l("Begin campaign?"), 1; + if (askyesno() == ASK_NO) close; + killmonsterall(getmap()); + @moly_score = 0; + @moly_chall = 10; + @map$ = getmap(); + addtimer 500, instance_npcname("Hocus#MOLY1000")+"::OnCheck"; + closeclientdialog; + close; + +OnCheck: + if (@moly_chall != 10) end; + @moly_score += 1; + // Game Over + if (ispcdead() || + !isin(@map$, 37, 47, 49, 58)) { // NOT a typo - starting square is OK + // Game Over! + killmonsterall(getmap()); + @moly_score = @moly_score / 2; + .@pts=getq2(Q_AuroraEvent); + setq2 Q_AuroraEvent, .@pts+rand2(1, 1+@moly_score/20); + dispbottom l("End! Score this time: %s", FuzzyTime(gettimetick(2)+@moly_score)); + FYMOLY_SURVIV = max(FYMOLY_SURVIV, @moly_score); + @moly_score = 0; + @moly_chall = 0; + end; + } + /* Spawn */ + if (!(@moly_score % 2)) { + .@m=areamonster(getmap(), 37, 47, 49, 57, strmobinfo(1, EntAbomination), EntAbomination, 1); + setunitdata(.@m, UDT_MAXHP, 50 + @moly_score * 6); + setunitdata(.@m, UDT_HP, 50 + @moly_score * 6); + setunitdata(.@m, UDT_LEVEL, 1 + (@moly_score / 2)); + setunitdata(.@m, UDT_SPEED, max(50, 900-(@moly_score * 10))); + setunitdata(.@m, UDT_MODE, MD_CANMOVE|MD_CANATTACK|MD_AGGRESSIVE|MD_ANGRY); + setunitdata(.@m, UDT_DEF, min(32760, 20+@moly_score)); + setunitdata(.@m, UDT_MDEF, min(32760, @moly_score)); + setunitdata(.@m, UDT_LUK, min(32760, rand2(@moly_score) * 7)); + setunitdata(.@m, UDT_HIT, 32760); + setunitdata(.@m, UDT_FLEE, 0); + setunitdata(.@m, UDT_ATKMIN, @moly_score); + setunitdata(.@m, UDT_ATKMAX, @moly_score+20); + setunitdata(.@m, UDT_ADELAY, max(672, 1872-(@moly_score * 4))); + } + /* Weapon Check */ + .@wp = getequipid(EQI_HAND_R); + // Disarmed + if (.@wp < 1) { + addtimer 500, instance_npcname("Hocus#MOLY1000")+"::OnCheck"; + end; + } + // Illegal weapon + if (.@wp != Judgement && + .@wp != DarkPulsar && + .@wp != Lightbringer && + getiteminfo(.@wp, ITEMINFO_SUBTYPE) != W_STAFF) { + @moly_score = -1; + @moly_tick = 0; + @moly_chall = 0; + dispbottom col(l("You have been disqualified - illegal weapon: %s", getitemname(.@wp)), 1); + killmonsterall(getmap()); + end; + } + addtimer 500, instance_npcname("Hocus#MOLY1000")+"::OnCheck"; + end; + +OnInit: + .distance=7; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,62,37,0 script Hocus#MOLY1100 NPC_BLACKWIZARD,{ + mes ".:: " + l("Friendship's Strength Challenge") + ":: ."; + mes l("Description: Use support skills and win the event with the power of FRIENDSHIP!"); + mesc l("Note: Support skills used on self does not count."), 1; + mes ""; + mesc l("Your progress thus far: %s friends supported", fnum(FYMOLY_FRIEND)), 3; + close; + +OnInit: + .distance=7; + end; +} + + + diff --git a/npc/001-14/hocus.txt b/npc/001-14/hocus.txt new file mode 100644 index 0000000..3bc241f --- /dev/null +++ b/npc/001-14/hocus.txt @@ -0,0 +1,257 @@ +// TMW2 script +// Author: +// Jesusalva +// Description: +// Magic Olympics + +function script HocusScoreNew { + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_ENBALL' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly01_n$, $@moly01_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_ICBOSS' AND i.value > 0 AND i.char_id=c.char_id ORDER BY i.value ASC LIMIT 10", $@moly02_n$, $@moly02_v); // Oddball: less is more + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_FLUFFY' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly03_n$, $@moly03_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_CHANTI' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly04_n$, $@moly04_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_ALCHMY' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly05_n$, $@moly05_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_MPWLVL' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly06_n$, $@moly06_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_MANAXP' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly07_n$, $@moly07_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_RACERS' AND i.value > 5 AND i.char_id=c.char_id ORDER BY i.value ASC LIMIT 10", $@moly08_n$, $@moly08_v); // Oddball: less is more + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_HOCUSM' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly09_n$, $@moly09_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_SURVIV' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly10_n$, $@moly10_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_FRIEND' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly11_n$, $@moly11_v); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='FYMOLY_SPAMMY' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@moly12_n$, $@moly12_v); + + /* Regenerate global scores */ + deletearray $@moly_n$; + deletearray $@moly_v; + deletearray .@moly_n$; + deletearray .@moly_v; + freeloop(true); + // Challenge 01: Energy Balls + for (.@i = 0; .@i < getarraysize($@moly01_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly01_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly01_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 02: Boss Fight + for (.@i = 0; .@i < getarraysize($@moly02_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly02_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly02_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 03: Hunt the Fluffies + for (.@i = 0; .@i < getarraysize($@moly03_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly03_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly03_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 04: Chanting + for (.@i = 0; .@i < getarraysize($@moly04_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly04_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly04_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 05: Alchemy Master + for (.@i = 0; .@i < getarraysize($@moly05_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly05_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly05_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 06: Magic Power + for (.@i = 0; .@i < getarraysize($@moly06_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly06_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly06_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 07: Mana Exp + for (.@i = 0; .@i < getarraysize($@moly07_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly07_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly07_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 08: Race + for (.@i = 0; .@i < getarraysize($@moly08_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly08_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly08_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 09: Hocus Said So + for (.@i = 0; .@i < getarraysize($@moly09_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly09_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly09_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 10: Survive! + for (.@i = 0; .@i < getarraysize($@moly10_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly10_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly10_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 11: Friendship Strength + for (.@i = 0; .@i < getarraysize($@moly11_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly11_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly11_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + // Challenge 12: Intensive Mage + for (.@i = 0; .@i < getarraysize($@moly12_v); .@i++) { + .@p = 10-.@i; + .@m = array_find(.@moly_n$, $@moly12_n$[.@i]); + if (.@m < 0) + .@m = array_push(.@moly_n$, $@moly12_n$[.@i]) - 1; + .@moly_v[.@m] += .@p; + } + freeloop(false); + /* Reorder the .@moly into $@moly */ + do + { + .@t = array_highest(.@moly_v); + if (.@t < 0 || getarraysize($@moly_v) > 10) break; + if (.@moly_v[.@t] < 1) break; + array_push($@moly_n$, .@moly_n$[.@t]); + array_push($@moly_v, .@moly_v[.@t]); + .@moly_v[.@t] = 0; + } while (true); + return; +} + +///////////////////////////////////////////////////////////////////////////////// +001-14,89,89,0 script Hocus Pocus NPC_BLACKWIZARD,{ + function showScores; + function parseScores; + if ($EVENT$ != "Olympics") cwarp "Save", 0, 0; + mesn; + mesq l("Greetings, %s %s, I am Hocus Pocus the Grandmaster.", academicrank(), strcharinfo(0)); + do + { + next; + select + l("Scoreboards"), + l("Detailed Information"), + l("Thanks for your wise words."); + mes ""; + switch (@menu) { + case 1: + showScores(); + break; + case 2: + mesn; + mesq l("The Magic Olympics consist in twelve challenges with scoreboards."); + next; + mesn; + mesq l("Getting ranked #1 in a challenge yields you 10 points, getting ranked #2 yields you 9 points and so on."); + next; + mesn; + mesq l("Not participating in a challenge yields you zero points. In case of a tie, char creation date will be the decisive factor: Older adventurers will get the preference."); + next; + mesn; + mesq l("You also get points for participation, exchange them with Aurora back in Tulimshar before event ends."); + next; + mesn; + mesq l("The grand winner will get a %s. Both the first and second place will get a tuition at the Magic Academy.", getitemlink(BlackyCat)); + next; + mesn; + mesq l("The Magic Olympics happen roughly quarterly, but not always at the same dates. It begins on a monday and ends on the sunday. It usually happens two weeks after the Mining Union Research Request event."); + break; + } + } while (@menu != 3); + close; + +function t { + return gettimetick(2); +} + +function showScores { + parseScores("General Event Score", "$@moly_n$", "$@moly_v"); + mesc l("Reminder: "), 1; + mesc l("Scores are updated every 6 hours."), 1; + next; + parseScores("Energy Balls Challenge", "$@moly01_n$", "$@moly01_v"); + //parseScores("Boss Fight Challenge", "$@moly02_n$", "$@moly02_v", 0); + //parseScores("Fluffy Hunt Challenge", "$@moly03_n$", "$@moly03_v"); + parseScores("Chanting Challenge", "$@moly04_n$", "$@moly04_v"); + parseScores("Alchemy Master Challenge", "$@moly05_n$", "$@moly05_v"); + parseScores("Magic Power Challenge", "$@moly06_n$", "$@moly06_v"); + parseScores("Mana Exp Challenge", "$@moly07_n$", "$@moly07_v"); + parseScores("Obstacle Race Challenge", "$@moly08_n$", "$@moly08_v", t()); + parseScores("Hocus Commands Challenge", "$@moly09_n$", "$@moly09_v"); + parseScores("Survive! Challenge", "$@moly10_n$", "$@moly10_v", t()); + parseScores("Friendship Challenge", "$@moly11_n$", "$@moly11_v"); + parseScores("Intensive Mage Challenge", "$@moly12_n$", "$@moly12_v"); + mes ""; + mesc l("Reminder: "), 1; + mesc l("Scores close at Sunday 23:59 - No scores will be given for last minute rushes!"), 1; + return; +} + +function SCH { + .@n$ = getarg(0); + .@i = getarg(1); + return getd(sprintf("%s[%d]", .@n$, .@i)); +} + +function parseScores { + .@t$ = getarg(0); + .@n$ = getarg(1); + .@v$ = getarg(2); + .@tm = getarg(3, -1); + mes ""; + mes l("##B%s: TOP 10##b", .@t$); + if (.@tm < 0) { + mes("1."+SCH(.@n$, 0)+" ("+fnum(SCH(.@v$, 0))+")"); + mes("2."+SCH(.@n$, 1)+" ("+fnum(SCH(.@v$, 1))+")"); + mes("3."+SCH(.@n$, 2)+" ("+fnum(SCH(.@v$, 2))+")"); + mes("4."+SCH(.@n$, 3)+" ("+fnum(SCH(.@v$, 3))+")"); + mes("5."+SCH(.@n$, 4)+" ("+fnum(SCH(.@v$, 4))+")"); + mes("6."+SCH(.@n$, 5)+" ("+fnum(SCH(.@v$, 5))+")"); + mes("7."+SCH(.@n$, 6)+" ("+fnum(SCH(.@v$, 6))+")"); + mes("8."+SCH(.@n$, 7)+" ("+fnum(SCH(.@v$, 7))+")"); + mes("9."+SCH(.@n$, 8)+" ("+fnum(SCH(.@v$, 8))+")"); + mes("10."+SCH(.@n$, 9)+" ("+fnum(SCH(.@v$, 9))+")"); + } else { + mes("1."+SCH(.@n$, 0)+" ("+FuzzyTime(.@tm - SCH(.@v$, 0))+")"); + mes("2."+SCH(.@n$, 1)+" ("+FuzzyTime(.@tm - SCH(.@v$, 1))+")"); + mes("3."+SCH(.@n$, 2)+" ("+FuzzyTime(.@tm - SCH(.@v$, 2))+")"); + mes("4."+SCH(.@n$, 3)+" ("+FuzzyTime(.@tm - SCH(.@v$, 3))+")"); + mes("5."+SCH(.@n$, 4)+" ("+FuzzyTime(.@tm - SCH(.@v$, 4))+")"); + mes("6."+SCH(.@n$, 5)+" ("+FuzzyTime(.@tm - SCH(.@v$, 5))+")"); + mes("7."+SCH(.@n$, 6)+" ("+FuzzyTime(.@tm - SCH(.@v$, 6))+")"); + mes("8."+SCH(.@n$, 7)+" ("+FuzzyTime(.@tm - SCH(.@v$, 7))+")"); + mes("9."+SCH(.@n$, 8)+" ("+FuzzyTime(.@tm - SCH(.@v$, 8))+")"); + mes("10."+SCH(.@n$, 9)+" ("+FuzzyTime(.@tm - SCH(.@v$, 9))+")"); + } + dnext; + return; +} + +OnInit: + .distance=5; +// Scoreboards are refreshed every 6 hours +OnHour2359: +OnHour0559: +OnHour1159: +OnHour1759: + if ($EVENT$ != "Olympics") end; + HocusScoreNew(); + end; +} + diff --git a/npc/001-14/mapflags.txt b/npc/001-14/mapflags.txt new file mode 100644 index 0000000..7f9ecda --- /dev/null +++ b/npc/001-14/mapflags.txt @@ -0,0 +1 @@ +001-14 mapflag zone MMO diff --git a/npc/001-2/_import.txt b/npc/001-2/_import.txt new file mode 100644 index 0000000..d7c488a --- /dev/null +++ b/npc/001-2/_import.txt @@ -0,0 +1,3 @@ +// Map 001-2: Coliseum +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-2/mapflags.txt", diff --git a/npc/001-2/mapflags.txt b/npc/001-2/mapflags.txt new file mode 100644 index 0000000..b4b4f8d --- /dev/null +++ b/npc/001-2/mapflags.txt @@ -0,0 +1 @@ +001-2 mapflag battleground diff --git a/npc/001-3-1/_import.txt b/npc/001-3-1/_import.txt new file mode 100644 index 0000000..41049d0 --- /dev/null +++ b/npc/001-3-1/_import.txt @@ -0,0 +1,5 @@ +// Map 001-3-1: Saulc's Palace +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-3-1/_warps.txt", +"npc/001-3-1/alchemy.txt", +"npc/001-3-1/smith.txt", diff --git a/npc/001-3-1/_warps.txt b/npc/001-3-1/_warps.txt new file mode 100644 index 0000000..ba603fe --- /dev/null +++ b/npc/001-3-1/_warps.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-3-1: Saulc's Palace warps +001-3-1,24,62,0 warp #001-3-1_24_62 0,0,001-3,80,124 +001-3-1,117,62,0 warp #001-3-1_117_62 0,0,001-3,127,125 +001-3-1,45,127,0 warp #001-3-1_45_127 3,0,001-3,104,81 +001-3-1,45,83,0 warp #001-3-1_45_83 3,0,001-3-1,106,116 +001-3-1,107,117,0 warp #001-3-1_107_117 3,0,001-3-1,44,84 +001-3-1,116,127,0 warp #001-3-1_116_127 2,0,001-3,106,66 +001-3-1,97,127,0 warp #001-3-1_97_127 2,0,001-3,101,66 +001-3-1,24,31,0 warp #001-3-1_24_31 0,0,001-3,72,104 +001-3-1,117,31,0 warp #001-3-1_117_31 0,0,001-3,135,104 diff --git a/npc/001-3-1/alchemy.txt b/npc/001-3-1/alchemy.txt new file mode 100644 index 0000000..a9c15dd --- /dev/null +++ b/npc/001-3-1/alchemy.txt @@ -0,0 +1,27 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Debug functions for Craft Systems + + +001-3-1,32,89,0 script GM Alchemy Table NPC_NO_SPRITE,{ + if (!$@GM_OVERRIDE || $EVENT$ == "") goto L_Offline; + mesc l("Welcome to Saulc's Magic Alchemy Table!"); + mesc l("This table will prepare the potion for you, no skill required!"); + mesc l("What will you brew today?"); + if (AlchemySystem(CRAFT_NPC)) + mesc l("Success!"), 3; + else + mesc l("That didn't work!"), 1; + close; + +L_Offline: + npctalk3 l("Oops! Seems like Saulc doesn't wants you messing on his chemistry set!"); + close; + +OnInit: + .distance=5; + end; +} + diff --git a/npc/001-3-1/smith.txt b/npc/001-3-1/smith.txt new file mode 100644 index 0000000..7de825a --- /dev/null +++ b/npc/001-3-1/smith.txt @@ -0,0 +1,28 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Debug functions for Craft Systems + + +001-3-1,52,86,0 script GM Black Smithy NPC_NO_SPRITE,{ + if (!$@GM_OVERRIDE || $EVENT$ == "") goto L_Offline; + mesc l("Welcome to Saulc's Magic Smith Table!"); + mesc l("This forge will prepare equipment for you, no skill required!"); + mesc l("What will you craft today?"); + //mesc l("You need to have an @@ equipped.", getitemlink(Knife)), 1; + if (SmithSystem(CRAFT_NPC)) + mesc l("Success!"), 3; + else + mesc l("That didn't work!"), 1; + close; + +L_Offline: + npctalk3 l("Oops! Seems like Saulc doesn't wants you messing on his raging furnaces!"); + close; + +OnInit: + .distance=5; + end; +} + diff --git a/npc/001-3/_import.txt b/npc/001-3/_import.txt new file mode 100644 index 0000000..3a2cb29 --- /dev/null +++ b/npc/001-3/_import.txt @@ -0,0 +1,4 @@ +// Map 001-3: Saulc's Home +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-3/_warps.txt", +"npc/001-3/ctrl.txt", diff --git a/npc/001-3/_warps.txt b/npc/001-3/_warps.txt new file mode 100644 index 0000000..732ca93 --- /dev/null +++ b/npc/001-3/_warps.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-3: Saulc's Home warps +001-3,80,123,0 warp #001-3_80_123 0,0,001-3-1,24,61 +001-3,127,124,0 warp #001-3_127_124 0,0,001-3-1,117,61 +001-3,104,80,0 warp #001-3_104_80 3,0,001-3-1,45,126 +001-3,106,65,0 warp #001-3_106_65 0,0,001-3-1,116,126 +001-3,101,65,0 warp #001-3_101_65 0,0,001-3-1,97,126 +001-3,117,138,0 warp #001-3_117_138 0,0,000-1,22,22 +001-3,72,103,0 warp #001-3_72_103 0,0,001-3-1,24,30 +001-3,135,103,0 warp #001-3_135_103 0,0,001-3-1,117,30 +001-3,66,121,0 warp #001-3_66_121 0,2,004-2,50,61 diff --git a/npc/001-3/ctrl.txt b/npc/001-3/ctrl.txt new file mode 100644 index 0000000..99c6d1f --- /dev/null +++ b/npc/001-3/ctrl.txt @@ -0,0 +1,23 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// None + +001-3,127,64,0 script #PatreonTower NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (is_sponsor()) warp "003-0-2", 35, 42; + end; +} + +001-3,103,69,0 script Copper Chest#Saulc NPC_CHEST,{ + // Like common chest but up to +3% bonus + TreasureBox(rand2(150, 300), CopperKey); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=2; + end; +} + diff --git a/npc/001-4/_import.txt b/npc/001-4/_import.txt new file mode 100644 index 0000000..a580541 --- /dev/null +++ b/npc/001-4/_import.txt @@ -0,0 +1,7 @@ +// Map 001-4: Enchanted Forest +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-4/_mobs.txt", +"npc/001-4/event_soulmenhir.txt", +"npc/001-4/lilica.txt", +"npc/001-4/mushroom.txt", +"npc/001-4/traps.txt", diff --git a/npc/001-4/_mobs.txt b/npc/001-4/_mobs.txt new file mode 100644 index 0000000..5b74a87 --- /dev/null +++ b/npc/001-4/_mobs.txt @@ -0,0 +1,16 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-4: Enchanted Forest mobs +001-4,72,232,23,23 monster Clover Field 1028,2,60000,120000 +001-4,224,238,23,23 monster Clover Field 1028,2,60000,120000 +001-4,226,77,23,23 monster Clover Field 1028,2,60000,120000 +001-4,74,77,23,23 monster Clover Field 1028,2,60000,120000 +001-4,149,149,130,129 monster Bhop Fluffy 1049,180,60000,120000 +001-4,150,149,129,128 monster Easter Mouboo 1121,40,60000,120000 +001-4,72,132,23,23 monster Easter Angry Forain 1061,1,60000,120000 +001-4,125,76,23,23 monster Easter Angry Forain 1061,1,60000,120000 +001-4,176,72,23,23 monster Easter Angry Forain 1061,1,60000,120000 +001-4,228,131,23,23 monster Easter Angry Forain 1061,1,60000,120000 +001-4,226,188,23,23 monster Easter Angry Forain 1061,1,60000,120000 +001-4,174,237,23,23 monster Easter Angry Forain 1061,1,60000,120000 +001-4,125,238,23,23 monster Easter Angry Forain 1061,1,60000,120000 +001-4,72,184,23,23 monster Easter Angry Forain 1061,1,60000,120000 diff --git a/npc/001-4/event_soulmenhir.txt b/npc/001-4/event_soulmenhir.txt new file mode 100644 index 0000000..464212b --- /dev/null +++ b/npc/001-4/event_soulmenhir.txt @@ -0,0 +1,25 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Special Soul Menhir which only allows leaving the map. + +001-4,152,154,0 script Soul Stone#001-4 NPC_SOUL_CLEAN,{ + mesn; + mes l("(A mystical aura surrounds this stone. It probably can return you home. What do you do?)"); + + menu + l("Touch it."), L_Warp, + l("Leave it alone."), -; + close; + +L_Warp: + //warp getsavepoint(0), getsavepoint(1), getsavepoint(2); + warp "000-1", 22, 22; + close; + +OnInit: + .distance = 5; + end; +} + diff --git a/npc/001-4/lilica.txt b/npc/001-4/lilica.txt new file mode 100644 index 0000000..5d7f152 --- /dev/null +++ b/npc/001-4/lilica.txt @@ -0,0 +1,186 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Lilica is TMW-BR Scary Scary Easter Bunny and will help your trading stuff. + +001-4,139,151,0 script Lilica#easter NPC_EASTER,{ + mesn; + mesq lg("Ah, traveller! I am Lilica the Scary Bunny! I exchange many @@ and @@ for neat rewards!","Ah, traveller! I am Lilica the Scary Bunny! I exchange many @@ and @@ for neat rewards!", getitemlink(SilverEasteregg), getitemlink(GoldenEasteregg)); + next; + mesn; + mesq l("Golden Eggs are used for the grand collector prize. Ah, I love Easter! I loooooooove it!"); + mesc l("Note: Golden and Silver Eggs are deleted after the next event end."); + + menu + l("Scary..........."), -, + l("Trade Silver Eggs"), L_Silver, + l("Trade Golden Eggs"), L_Golden, + rif(getq(SQuest_Easter) != gettime(7), l("I want an Eggshell Hat!")), L_Quest, + l("View LeaderBoard"), L_Leader; + close; + +L_Silver: + openshop "#eastershop1"; + closedialog; + close; + +L_Golden: + openshop "#eastershop2"; + closedialog; + close; + +L_Leader: + mesc l("Leaderboard is refresh daily at 1 AM!"); + mes("1."+$@easter_name$[0]+" ("+$@easter_value[0]+")"); + mes("2."+$@easter_name$[1]+" ("+$@easter_value[1]+")"); + mes("3."+$@easter_name$[2]+" ("+$@easter_value[2]+")"); + mes("4."+$@easter_name$[3]+" ("+$@easter_value[3]+")"); + mes("5."+$@easter_name$[4]+" ("+$@easter_value[4]+")"); + mes("6."+$@easter_name$[5]+" ("+$@easter_value[5]+")"); + mes("7."+$@easter_name$[6]+" ("+$@easter_value[6]+")"); + mes("8."+$@easter_name$[7]+" ("+$@easter_value[7]+")"); + mes("9."+$@easter_name$[8]+" ("+$@easter_value[8]+")"); + mes("10."+$@easter_name$[9]+" ("+$@easter_value[9]+")"); + close; + +L_Quest: + setarray .@Seasonal, EggshellHat, EggshellHat, GreenEggshellHat, OrangeEggshellHat, BlueEggshellHat, EggshellHat; + .@Hat=.@Seasonal[gettime(7)%6]; // Magically choose the hat from the array + mesn; + mesq l("Good choice! This year we're having a @@!", getitemlink(.@Hat)); + next; + mesn; + mesq l("As usual, you can get only one hat yearly, for the symbolic amount of 40 @@ and 10 @@!", getitemlink(GoldenEasteregg), getitemlink(SilverEasteregg)); + next; + menu + l("Maybe later."), -, + rif(countitem(GoldenEasteregg) >= 40 && countitem(SilverEasteregg) >= 10, l("Deal.")), L_QuestDone; + close; + +L_QuestDone: + inventoryplace .@Hat, 1; + delitem GoldenEasteregg, 40; + delitem SilverEasteregg, 10; + if (rand2(10000) < 100) goto L_Unlucky; + setq SQuest_Easter, gettime(7), min(500, getq2(SQuest_Easter)+100); + npctalk3 l("Strange Coins stock on shops was restored!"); + getnameditem(.@Hat, strcharinfo(0)); + mesn; + mesq l("Here you go! Happy easter! Bhop bhop!"); + close; + +L_Unlucky: + getitem GoldenGift, 1; + mesn; + mes l("\"Oh... Sorry, @@.", strcharinfo(0)); + mes l("But in accordance to an old %s, you were unlucky.", b(l("Community Decision"))); + mes l("This means ##BAll items were lost##b, and you need to collect EVERYTHING, again, to get the hat."); + mes l("But, hm hm hm! I have a %s for you! It won't have what you wanted, but maybe you're lucky, after all?", getitemlink(GoldenGift)); + mes l("Better luck next time!\""); + close; + +OnClock0100: +OnClock1300: + if ($EVENT$ == "Easter") + .@nb = query_sql("SELECT c.name, i.amount FROM `inventory` AS i, `char` AS c WHERE i.nameid=834 AND i.char_id=c.char_id ORDER BY i.amount DESC LIMIT 10", $@easter_name$, $@easter_value); + end; + +OnInit: + .sex = G_OTHER; + .distance = 5; + + if ($EVENT$ == "Easter") + sEaster(); + end; +} + +function script EasterCoinCheck { + for (.@i=0;.@i < getarraysize(@bought_nameid); .@i++) { + + if (debug || $@GM_OVERRIDE) + debugmes("%dx %s", @bought_quantity[.@i], getitemname(@bought_nameid[.@i])); + + if (@bought_nameid[.@i] == StrangeCoin) { + .@q2=getq2(SQuest_Easter)-@bought_quantity[.@i]; + if (.@q2 < 0) { + dispbottom l("Attempted to buy %d/%d %s, operation cancelled.", + @bought_quantity[.@i], getq2(SQuest_Easter), + getitemlink(StrangeCoin)); + if (TUTORIAL) + dispbottom l("This quota is reset yearly, by completing %s's quest.", b("Lilica")); + end; + } + setq2 SQuest_Easter, .@q2; + } + } + return; +} + +// Silver Easter Egg exchange. +001-4,139,151,0 trader #eastershop1 NPC_HIDDEN,{ + end; + +OnInit: + tradertype(NST_CUSTOM); + + sellitem ChocolateMouboo,100; + sellitem StrangeCoin,50; + sellitem GoldenEasteregg,25; + sellitem EasterEgg,5; + end; + +OnCountFunds: + setcurrency(countitem(SilverEasteregg)); + end; + +OnPayFunds: + if( countitem(SilverEasteregg) < @price ) + end; + // Strange Coins are different + EasterCoinCheck(); + // Complete purchase + delitem SilverEasteregg, @price; + purchaseok(); + end; +} +// Golden Easter Egg exchange. +001-4,139,151,0 trader #eastershop2 NPC_HIDDEN,{ + end; + +OnInit: + tradertype(NST_CUSTOM); + setarray .@Seasonal, EggshellHat, EggshellHat, GreenEggshellHat, OrangeEggshellHat, BlueEggshellHat, EggshellHat; + .Hat=.@Seasonal[(gettime(7)+1)%6]; // Magically choose the hat from the array + .PrevHat=.@Seasonal[(gettime(7)-1)%6]; // Magically choose the hat from the array + + // Seasonal item + sellitem .Hat,200; + sellitem .PrevHat,500; + + // Rare and not-so-rare Items + sellitem MercBoxE,1500; + sellitem MercBoxD,1000; + sellitem MercBoxC,500; + sellitem Boots,450; + sellitem BronzeGift,100; + sellitem BunnyEars,50; + sellitem StrangeCoin,10; + sellitem SilverEasteregg,1; + end; + +OnCountFunds: + setcurrency(countitem(GoldenEasteregg)); + end; + +OnPayFunds: + if( countitem(GoldenEasteregg) < @price ) + end; + // Strange Coins are different + EasterCoinCheck(); + // Complete purchase + delitem GoldenEasteregg, @price; + purchaseok(); + end; +} + diff --git a/npc/001-4/mushroom.txt b/npc/001-4/mushroom.txt new file mode 100644 index 0000000..1ba604d --- /dev/null +++ b/npc/001-4/mushroom.txt @@ -0,0 +1,174 @@ +// TMW2 SCRIPT. +// Author: +// The Mana World Brazil +function script mushroomWarp { + if (playerattached()) + { + heal -5, -5; + warp getmapname(), 0, 0; + @n = rand(3); + if (@n == 0) dispbottom l("Uhh... What happened..."); + if (@n == 1) dispbottom l("The world is spiniiiiiiiing..."); + if (@n == 2) dispbottom l("Ah... What is happening to meeeeeeee?"); + } + else + { + unitwarp(0, "this", -1, -1); + } + return; +} + +001-4,252,184,0 script #001-4_252x184 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,259,128,0 script #001-4_259x128 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,252,255,0 script #001-4_252x255 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,180,270,0 script #001-4_180x270 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,124,267,0 script #001-4_124x267 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,44,243,0 script #001-4_44x243 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,41,180,0 script #001-4_41x180 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,58,127,0 script #001-4_58x127 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,56,51,0 script #001-4_56x51 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,114,39,0 script #001-4_114x39 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,183,37,0 script #001-4_183x37 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,244,57,0 script #001-4_244x57 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,154,136,0 script #001-4_154x136 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + +001-4,142,173,0 script #001-4_142x173 NPC_FANCY_CIRCLE,0,0,{ + mes l("A bright and mysterious mushroom!!!"); + mes l("Only getting closer to find out what kind this one is."); + close; + +OnTouchNPC: +OnTouch: + mushroomWarp(); + end; +} + diff --git a/npc/001-4/traps.txt b/npc/001-4/traps.txt new file mode 100644 index 0000000..4965fe8 --- /dev/null +++ b/npc/001-4/traps.txt @@ -0,0 +1,557 @@ +// TMW2 Scripts +// Author: +// The Mana World Brazil +// Description: +// Traps. + +001-4,275,204,0 script #001-4_275x204 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_275x204", NPC_TRAP; + end; +} + +001-4,260,182,0 script #001-4_260x182 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_260x182", NPC_TRAP; + end; +} + +001-4,231,173,0 script #001-4_231x173 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_231x173", NPC_TRAP; + end; +} + +001-4,199,171,0 script #001-4_199x171 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_199x171", NPC_TRAP; + end; +} + +001-4,200,144,0 script #001-4_200x144 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_200x144", NPC_TRAP; + end; +} + +001-4,247,127,0 script #001-4_247x127 NPC_TRAP,0,0,{ + @map$ = "001-4"; + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_247x127", NPC_TRAP; + end; +} + +001-4,265,135,0 script #001-4_265x135 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_265x135", NPC_TRAP; + end; +} + +001-4,271,110,0 script #001-4_271x110 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_271x110", NPC_TRAP; + end; +} + +001-4,190,30,0 script #001-4_190x30 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_190x30", NPC_TRAP; + end; +} + +001-4,163,33,0 script #001-4_163x33 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_163x33", NPC_TRAP; + end; +} + +001-4,177,49,0 script #001-4_177x49 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer5000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_177x49", NPC_TRAP; + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_177x49", NPC_TRAP; + end; +} + +001-4,164,89,0 script #001-4_164x89 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_164x89", NPC_TRAP; + end; +} + +001-4,130,84,0 script #001-4_130x84 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_130x84", NPC_TRAP; + end; +} + +001-4,126,47,0 script #001-4_126x47 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_126x47", NPC_TRAP; + end; +} + +001-4,130,32,0 script #001-4_130x32 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_130x32", NPC_TRAP; + end; +} + +001-4,107,33,0 script #001-4_107x33 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_107x33", NPC_TRAP; + end; +} + +001-4,33,141,0 script #001-4_33x141 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_33x141", NPC_TRAP; + end; +} + +001-4,82,149,0 script #001-4_82x149 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_82x149", NPC_TRAP; + end; +} + +001-4,42,118,0 script #001-4_42x118 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_42x118", NPC_TRAP; + end; +} + +001-4,24,106,0 script #001-4_24x106 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_24x106", NPC_TRAP; + end; +} + +001-4,79,163,0 script #001-4_79x163 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_79x163", NPC_TRAP; + end; +} + +001-4,42,175,0 script #001-4_42x175 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_42x175", NPC_TRAP; + end; +} + +001-4,26,184,0 script #001-4_26x184 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_26x184", NPC_TRAP; + end; +} + +001-4,32,198,0 script #001-4_32x198 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_32x198", NPC_TRAP; + end; +} + +001-4,102,275,0 script #001-4_102x275 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_102x275", NPC_TRAP; + end; +} + +001-4,128,274,0 script #001-4_128x274 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_128x274", NPC_TRAP; + end; +} + +001-4,130,256,0 script #001-4_130x256 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_130x256", NPC_TRAP; + end; +} + +001-4,128,220,0 script #001-4_128x220 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_128x220", NPC_TRAP; + end; +} + +001-4,156,212,0 script #001-4_156x212 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_156x212", NPC_TRAP; + end; +} + +001-4,166,246,0 script #001-4_166x246 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_166x246", NPC_TRAP; + end; +} + +001-4,176,270,0 script #001-4_176x270 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_176x270", NPC_TRAP; + end; +} + +001-4,160,270,0 script #001-4_160x270 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; + +OnTouchNPC: +OnTouch: + SteelTrap(); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay "#001-4_160x270", NPC_TRAP; + end; +} + diff --git a/npc/001-5/_import.txt b/npc/001-5/_import.txt new file mode 100644 index 0000000..5c94ec5 --- /dev/null +++ b/npc/001-5/_import.txt @@ -0,0 +1,5 @@ +// Map 001-5: Contributor's Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-5/_mobs.txt", +"npc/001-5/_warps.txt", +"npc/001-5/worker.txt", diff --git a/npc/001-5/_mobs.txt b/npc/001-5/_mobs.txt new file mode 100644 index 0000000..6a6fd7f --- /dev/null +++ b/npc/001-5/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-5: Contributor's Cave mobs +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-5: Contributor's Cave mobs +001-5,49,49,29,29 monster Blub 1008,2,35000,60000 +001-5,53,50,25,29 monster Snake 1122,1,35000,120000 +001-5,54,51,25,29 monster Night Scorpion 1077,1,35000,150000 +001-5,51,49,29,29 monster Stray Little Blub 1007,3,35000,180000 diff --git a/npc/001-5/_warps.txt b/npc/001-5/_warps.txt new file mode 100644 index 0000000..5d41810 --- /dev/null +++ b/npc/001-5/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-5: Contributor's Cave warps +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-5: Contributor's Cave warps +001-5,22,80,0 warp #001-5_22_80 0,0,000-1,22,22 diff --git a/npc/001-5/worker.txt b/npc/001-5/worker.txt new file mode 100644 index 0000000..8d21b08 --- /dev/null +++ b/npc/001-5/worker.txt @@ -0,0 +1,88 @@ +// TMW2 Script +// Author: Jesusalva +// Workers from Worker Day. + +001-5,23,73,0 script Soren NPC_CONSTR_WORKER,{ + mesn; + mesq l("You can get @@ anywhere, although here is a little easier to get.", getitemlink(Pearl)); + next; + mesn; + mesq l("You can trade them for quite nice items with my friend over there."); + close; +} + +001-5,75,69,0 script Simon NPC_CONSTR_WORKER,{ + function alreadyFinished; + .@year=getq(SQuest_WorkerDay); + .@day=getq3(SQuest_WorkerDay); + if (.@year != gettime(GETTIME_YEAR)-2000 || + .@day != gettime(GETTIME_DAYOFMONTH)) + setq SQuest_WorkerDay, + gettime(GETTIME_YEAR)-2000, 0, gettime(GETTIME_DAYOFMONTH); + + // Main Loop + do + { + .@attempts=getq2(SQuest_WorkerDay); + if (.@attempts > BaseLevel/10) + alreadyFinished(); + + mesn; + mesq l("Hey dude. During this event you can trade one @@ for more... useful items.", getitemlink(.Item)); + mesc l("Attempts for today: %d/%d", .@attempts, BaseLevel/10); + if (countitem(.Item) == 0) + close; + next; + select( + l("12x Strange Coins"), + l("2x Snake Egg"), + l("2x Bronze Gift"), + l("1600 GP"), + rif(countitem(.Item) >= 2, l("Trade 2 %s for a Silver Gift + a Bronze Gift", getitemname(.Item))), + l("Nothing right now.") + ); + switch (@menu) { + case 1: + delitem .Item, 1; + getitem StrangeCoin, 12; + setq2 SQuest_WorkerDay, .@attempts+1; + break; + case 2: + delitem .Item, 1; + getitem SnakeEgg, 2; + setq2 SQuest_WorkerDay, .@attempts+1; + break; + case 3: + delitem .Item, 1; + getitem BronzeGift, 2; + setq2 SQuest_WorkerDay, .@attempts+1; + break; + case 4: + delitem .Item, 1; + Zeny=Zeny+1600; + setq2 SQuest_WorkerDay, .@attempts+1; + break; + case 5: + delitem .Item, 2; + getitem SilverGift, 1; + getitem BronzeGift, 1; + setq2 SQuest_WorkerDay, .@attempts+1; + break; + + } + + } while (@menu < 6); + close; + +function alreadyFinished { + mesc l("I already got enough %s, thank you.", getitemlink(.Item)), 1; + close; +} + +OnInit: + .Item=Pearl; + .distance=4; + end; +} + + diff --git a/npc/001-6/_import.txt b/npc/001-6/_import.txt new file mode 100644 index 0000000..9adfa79 --- /dev/null +++ b/npc/001-6/_import.txt @@ -0,0 +1,5 @@ +// Map 001-6: Cave Of Trials +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-6/_mobs.txt", +"npc/001-6/mapflags.txt", +"npc/001-6/warp.txt", diff --git a/npc/001-6/_mobs.txt b/npc/001-6/_mobs.txt new file mode 100644 index 0000000..aa7a0b2 --- /dev/null +++ b/npc/001-6/_mobs.txt @@ -0,0 +1,14 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-6: Cave Of Trials mobs +001-6,94,24,92,38 monster Bif 1058,6,45000,35000 +001-6,159,156,6,7 monster King Of Trials 1079,1,60000,60000 +001-6,147,139,38,42 monster Giant Mutated Bat 1044,4,60000,60000 +001-6,40,143,22,37 monster Red Slime 1092,24,20000,60000 +001-6,40,75,33,37 monster Lava Slime 1097,12,30000,60000 +001-6,88,160,30,24 monster Yellow Slime 1091,8,60000,60000 +001-6,112,52,69,41 monster Snake 1122,17,60000,60000 +001-6,119,91,57,42 monster Black Scorpion 1074,23,30000,50000 +001-6,135,117,11,12 monster Dark Lizard 1051,2,60000,60000 +001-6,30,31,11,12 monster Dark Lizard 1051,1,60000,90000 +001-6,155,159,34,35 monster Lava Slime 1097,6,30000,30000 +001-6,169,77,22,37 monster Red Slime 1092,8,20000,60000 diff --git a/npc/001-6/mapflags.txt b/npc/001-6/mapflags.txt new file mode 100644 index 0000000..c6904ba --- /dev/null +++ b/npc/001-6/mapflags.txt @@ -0,0 +1 @@ +001-6 mapflag zone MMO diff --git a/npc/001-6/warp.txt b/npc/001-6/warp.txt new file mode 100644 index 0000000..b0e3879 --- /dev/null +++ b/npc/001-6/warp.txt @@ -0,0 +1,32 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// TODO + + +001-6,159,157,0 script Soren Village NPC_SUMMONING_CIRC,0,0,{ + end; + +OnTouch: + if (@SorenWarpOnline == 1) + end; + dispbottom l("Loading warp, be at this spot in 15 seconds."); + @SorenWarpOnline=1; + addtimer 15000, "Soren Village::OnWarper"; + end; + +OnWarper: + @SorenWarpOnline=0; + if (isin("001-6", 159, 157, 159, 157) && !ispcdead()) { + compareandsetq HurnscaldQuest_Celestia, 2, 3; + warp "soren", 179, 76; + } else { + dispbottom l("Failed to warp to Soren Village."); + } + end; + +OnInit: + .sex = G_OTHER; + end; +} diff --git a/npc/001-7/_import.txt b/npc/001-7/_import.txt new file mode 100644 index 0000000..733e726 --- /dev/null +++ b/npc/001-7/_import.txt @@ -0,0 +1,8 @@ +// Map 001-7: Frostia Cliffs +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-7/_mobs.txt", +"npc/001-7/_warps.txt", +"npc/001-7/barbarian.txt", +"npc/001-7/celestia_bossfight.txt", +"npc/001-7/homunculus.txt", +"npc/001-7/mapflags.txt", diff --git a/npc/001-7/_mobs.txt b/npc/001-7/_mobs.txt new file mode 100644 index 0000000..70fa829 --- /dev/null +++ b/npc/001-7/_mobs.txt @@ -0,0 +1,37 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-7: Frostia Cliffs mobs +001-7,48,95,24,11 monster Ice Fluffy 1041,8,30000,30000 +001-7,103,96,14,14 monster Wolvern 1037,8,30000,30000 +001-7,30,23,6,2 monster White Slime 1094,5,30000,30000 +001-7,49,46,8,2 monster White Slime 1094,2,30000,30000 +001-7,41,49,16,2 monster Wolvern 1037,2,30000,30000 +001-7,28,53,8,2 monster Wolvern 1037,1,30000,30000 +001-7,41,56,2,1 monster Wolvern 1037,1,30000,30000 +001-7,93,37,8,7 monster White Slime 1094,5,30000,30000 +001-7,109,53,8,8 monster Moggun 1070,6,30000,30000 +001-7,67,138,25,7 monster Ice Fluffy 1041,8,30000,30000 +001-7,70,119,1,0 monster Blue Slime 1087,2,30000,30000 +001-7,103,87,1,4 monster Blue Slime 1087,2,30000,30000 +001-7,100,108,1,4 monster Blue Slime 1087,2,30000,30000 +001-7,74,41,9,5 monster Moggun 1070,5,30000,30000 +001-7,38,78,1,1 monster Wolvern 1037,2,30000,30000 +001-7,64,70,1,1 monster Wolvern 1037,2,30000,30000 +001-7,53,73,1,1 monster Wolvern 1037,2,30000,30000 +001-7,25,76,1,1 monster Wolvern 1037,2,30000,30000 +001-7,81,72,1,1 monster Wolvern 1037,2,30000,30000 +001-7,92,69,1,1 monster Wolvern 1037,2,30000,30000 +001-7,78,78,1,1 monster Wolvern 1037,2,30000,30000 +001-7,33,137,10,7 monster Ice Fluffy 1041,3,30000,30000 +001-7,31,64,1,1 monster Wolvern 1037,2,30000,30000 +001-7,52,59,1,1 monster Wolvern 1037,2,30000,30000 +001-7,47,66,1,1 monster Wolvern 1037,2,30000,30000 +001-7,38,67,1,1 monster Wolvern 1037,2,30000,30000 +001-7,24,115,1,1 monster Wolvern 1037,1,30000,100000 +001-7,84,95,2,1 monster Ice Fluffy 1041,2,30000,30000 +001-7,89,90,2,1 monster Ice Fluffy 1041,2,30000,30000 +001-7,77,97,2,1 monster Ice Fluffy 1041,2,30000,30000 +001-7,77,140,10,3 monster Yeti 1064,4,30000,30000 +001-7,94,135,3,3 monster White Slime 1094,2,30000,30000 +001-7,35,25,5,2 monster Alpha Mouboo 1056,1,30000,30000 +001-7,106,56,1,1 monster Wolvern 1037,2,30000,30000 +001-7,90,37,1,1 monster Wolvern 1037,2,30000,30000 diff --git a/npc/001-7/_warps.txt b/npc/001-7/_warps.txt new file mode 100644 index 0000000..631b7b5 --- /dev/null +++ b/npc/001-7/_warps.txt @@ -0,0 +1,20 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-7: Frostia Cliffs warps +001-7,36,38,0 warp #001-7_36_38 0,0,001-7,26,23 +001-7,26,22,0 warp #001-7_26_22 0,0,001-7,36,39 +001-7,95,131,0 warp #001-7_95_131 0,0,001-7,77,113 +001-7,77,112,0 warp #001-7_77_112 0,0,001-7,95,132 +001-7,103,79,0 warp #001-7_103_79 0,0,001-7,36,23 +001-7,36,22,0 warp #001-7_36_22 0,0,001-7,103,80 +001-7,94,34,0 warp #001-7_94_34 0,0,001-7,114,84 +001-7,114,83,0 warp #001-7_114_83 0,0,001-7,94,35 +001-7,43,68,0 warp #001-7_43_68 0,0,001-7,67,45 +001-7,67,44,0 warp #001-7_67_44 0,0,001-7,43,69 +001-7,58,81,0 warp #001-7_58_81 0,0,001-7,31,52 +001-7,31,51,0 warp #001-7_31_51 0,0,001-7,58,82 +001-7,39,75,0 warp #001-7_39_75 0,0,001-7,61,123 +001-7,61,122,0 warp #001-7_61_122 0,0,001-7,39,76 +001-7,90,67,0 warp #001-7_90_67 0,0,001-7,104,51 +001-7,104,50,0 warp #001-7_104_50 0,0,001-7,90,68 +001-7,61,135,0 warp #001-7_61_135 0,0,001-7,98,65 +001-7,98,64,0 warp #001-7_98_64 0,0,001-7,61,136 diff --git a/npc/001-7/barbarian.txt b/npc/001-7/barbarian.txt new file mode 100644 index 0000000..128344c --- /dev/null +++ b/npc/001-7/barbarian.txt @@ -0,0 +1,75 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Wolvern Teeth Necklace Quest. Inhabits Frostia Hills. +// The necklace can also be dropped by the Wolvern normally. Any of those will do +// to advance the Barbarian Necklaces plot. (Until you get the final version of it) +// This is a BONUS QUEST and can only be done via Yeti King Quest. It is NOT +// meant to be doable outside it. It's overrewarding, yes. It's meant to be this way. +// Do not tell everybody about this one :> Bonus to those who explore! +// NOTE: Temporaly replaced with ClawPendant +// Variables: +// FrostiaQuest_WolfNecklace - quest var + + +001-7,72,39,0 script Barbarian#Frostia NPC_HALBERDBARBARIAN,{ + .@q = getq(FrostiaQuest_WolfNecklace); + mesn; + mesq l("Hello my friend."); + if (BaseLevel < 35) goto L_Weakling; + if (.@q == 0 && countitem(ToothNecklace)) goto L_Quest; + if (.@q == 1) goto L_CheckItems; + close; + +L_Weakling: + mesq l("These cliffs are no place for weak people like you. You better teleport yourself away as soon as possible!"); + close; + +L_Quest: + mesq l("Oooh, I see you have a @@ with you.", getitemlink(ToothNecklace)); + next; + mesn; + mesq l("It's done with @@ teethes. These drain your life and raise your strenght to SMASH your foes!", getmonsterlink(Wolvern)); + next; + mesn; + mesq l("If you bring me 2 @@, that Necklace, and 4000 GP, I can improve it. It'll be worth it, I warrant you!", getitemlink(WolvernTooth)); // 5% drop rate of tooth. + setq FrostiaQuest_WolfNecklace, 1; + close; + +L_CheckItems: + if (!countitem(ToothNecklace) || countitem(WolvernTooth) < 2 || Zeny < 4000) { + mesn; + mesq l("Sorry, but you don't have what I need."); + mesq l("I need 2 @@, the @@, and 4000 GP.", getitemlink(WolvernTooth), getitemlink(ToothNecklace)); + close; + } + mesn; + mesq l("Are you with what I asked for?"); + if (askyesno() == ASK_NO) + close; + mes ""; + + // No checks because I did before and cheaters must die. + if (Zeny < 4000) { + Zeny=0; + percentheal -75, -100; + mesc l("All cheaters must die."), 1; + close; + } + inventoryplace ClawPendant, 1; + delitem WolvernTooth, 2; + delitem ToothNecklace, 1; + getitem ClawPendant, 1; + Zeny=Zeny-4000; + getexp 15000, 0; + mesn; + mesq l("Here you go, thanks for doing my SECRET quest! Eh, not much to do here. But I like life this way."); + setq FrostiaQuest_WolfNecklace, 2; + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/001-7/celestia_bossfight.txt b/npc/001-7/celestia_bossfight.txt new file mode 100644 index 0000000..e67ec4f --- /dev/null +++ b/npc/001-7/celestia_bossfight.txt @@ -0,0 +1,247 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Celestia Yeti King's quest. This controls the final showdown, and brings you +// back home safely. +// +// If you cheated your way to here, you won't be able to interact with it. +// No other safety measures are in place. Lone players cannot challenge the +// Yeti King, there must be at least 2 players there to do the challenge. +// BEWARE, the Yeti King gains stronger poisons the more people are attacking him. +// +// $@GM_OVERRIDE allows a single player to challenge him, as usual with all +// co-op scripts. +// +// If you do not challenge him, the chance to challenge him again is lost. + +001-7,33,39,0 script #YetiKing NPC_YETI_KING,0,0,{ + .@q=getq(HurnscaldQuest_Celestia); + if (.@q == 5 && !mobcount(.map$, "#YetiKing::OnVictory")) goto L_Survivor; + if (.@q == 6) goto L_GoHome; + end; + +L_GoHome: + .@MLPQuest=( (##02_MLWORLD & MLP_TMW_CELESTIA) && + !(##02_MLWORLD & MLP_TMW_YETIKING) && + getvaultid()); + if (.@MLPQuest) { + mesn strcharinfo(0); + mesq l("Actually, have you ever heard of Yeti's kidnapping little girls?"); + next; + goto L_VaultQuest; + } + mesc l("Go home now?"); + if (askyesno() == ASK_YES) + warp "003-1-1", 94, 22; + closedialog; + if (!getareausers("001-7", 7)) + setnpcdisplay .name$, NPC_YETI_KING; + close; + +L_Survivor: + if (!YETIKING_WINNER) + YETIKING_WINNER = gettimetick(2); + if ($YETIKING_WINNER$ == "") { + $YETIKING_WINNER$=strcharinfo(0); + channelmes("#world", $YETIKING_WINNER$+" is the first player to finish Yeti King Quest!! GG, dude! %%N"); + announce "All hail ##B"+$YETIKING_WINNER$+"##b, first to complete the ##3Yeti King Quest!", bc_all|bc_npc; + getexp 0, 2000; + getitem PrismGift, 1; + mesc l("CONGRATULATIONS! You are the first player to finish Yeti King quest!!"), 2; + mesc l("You just gained a Prism Gift, and 2000 Job Exp for your bravery!"), 2; + next; + } + mesn col(l("The Yeti King"), 3); + mesq l("Good job, kid. You've survived both the Cave Of Trials and Soren's Village."); + next; + mesn col(l("The Yeti King"), 3); + mesq l("That was only to prove you're strong enough on yourself to do whatever you want to do. You have friends."); + next; + mesn col(l("The Yeti King"), 3); + mesq l("In this world, your friends are your strength. You deserve a reward for the victory, please choose whatever you want."); + select + l("I want a gemstone or ore"), + rif(!countitem(MirrorLakeArmor), l("I want experience")), + l("I want gold"), + l("I want coal"), + l("I want monster points"); + + mes ""; + .@r=rand2(1,100)+(@YetiKing_Challenger*5); + switch (@menu) { + case 1: + if (.@r > 70) + getitem rand2(Diamond, Amethyst), 1; + else + getitem rand2(CopperOre, (REBIRTH ? IridiumOre : TitaniumOre)), any(1,2); + break; + case 2: + .@r+=BaseLevel; + getexp .@r*80, .@r*2; // max 8000 xp and 200 jp (level 0) + break; + case 3: + .@r+=JobLevel; + Zeny=Zeny+.@r*75; // max 7500 gp (job 0) + break; + case 4: + getitem Coal, (.@r/10); // max 10 coal + break; + case 5: + .@r+=(BaseLevel+JobLevel)/2; + Mobpt+=.@r*5; // max 500 mobpt (base/job 0) + break; + } + // Completion bonus + getexp 0, 2500; + getitem StrangeCoin, 1; + compareandsetq HurnscaldQuest_Celestia, 5, 6; + mesn col(l("The Yeti King"), 3); + mesq l("Here kid. Frostia, the elf town, is somewhere near here, but I'm not sure if you can reach it from here."); + next; + if ($@CINDY_STATE > gettimetick(2)) { + mesn col(l("The Yeti King"), 3); + mesq l("Some rogue Yetis are trying to escape to Nivalis. I can't hold them back for more than @@.", FuzzyTime($@CINDY_STATE+rand2(5,95))); + next; + } + mesn col(l("The Yeti King"), 3); + mesq l("I can warp you home now."); + mes ""; + .@MLPQuest=( (##02_MLWORLD & MLP_TMW_CELESTIA) && + !(##02_MLWORLD & MLP_TMW_YETIKING) && + getvaultid()); + select + rif(!.@MLPQuest, l("Please, bring me back home.")), + rif((getareausers("001-7", 7) > 1 || $@GM_OVERRIDE) && !mobcount(.map$, "#YetiKing::OnVictory") && @YetiKing_Challenger, l("No, we challenge you to a duel!")), + rif(.@MLPQuest, l("Actually, have you ever heard of Yeti's kidnapping little girls?")), + l("I'll walk around here a little more."); + + mes ""; + switch (@menu) { + case 1: + warp "003-1-1", 94, 22; + break; + case 2: + compareandsetq HurnscaldQuest_Celestia, 6, 7; + mesn col(l("The Yeti King"), 3); + mesq l("Foolish kids, do you think violence is the answer to everything?!"); + next; + mesn col(l("The Yeti King"), 3); + mesq l("I give you five minutes to defeat me. Witness my wrath!"); + if (mobcount(.map$, "#YetiKing::OnVictory")) + close; + setnpcdisplay .name$, NPC_NO_SPRITE; + npctalk l("*Roaaaaaar!*"); + monster .map$, .x, .y, strmobinfo(1, YetiKing), YetiKing, 1, "#YetiKing::OnVictory"; + initnpctimer; + break; + case 3: + goto L_VaultQuest; + } + close; + +OnVictory: + stopnpctimer; + setnpcdisplay .name$, NPC_SUMMONING_CIRC; + //Karma=Karma+1; + Mobpt+=10000; + getitem StrangeCoin, 2; + getmapxy(.@m$, .@x, .@y, 0); + makeitem(StrangeCoin, 1, .@m$, .@x+rand2(-1,1), .@y+rand2(-1,1)); + npctalk l("Good job... You can keep the drops. Touch here to return home."); + areatimer "001-7", 20, 20, 141, 171, 10, "#YetiKing::OnDefeat"; + donpcevent "Celestia::OnClock0002"; + fix_mobkill(YetiKing); + end; + +// This allows the challenger to go back home without dying. +OnDefeat: + getexp 0, 100; + compareandsetq HurnscaldQuest_Celestia, 7, 6; + end; + +OnTimer60000: + npctalk "Time left: 4 minutes"; + end; + +OnTimer120000: + npctalk "Time left: 3 minutes"; + end; + +OnTimer180000: + npctalk "Time left: 2 minutes"; + end; + +OnTimer240000: + npctalk "Time left: 1 minute"; + end; + +OnTimer270000: + npctalk "Time left: 30 seconds"; + end; + +OnTimer290000: + npctalk "Time left: 10 seconds"; + end; + +OnTimer300000: + npctalk "Time is up!"; + areatimer "001-7", 20, 20, 141, 171, 10, "#YetiKing::OnDefeat"; + killmonster(.map$, "#YetiKing::OnVictory"); // I could use "All" as label, too + setnpcdisplay .name$, NPC_YETI_KING; + end; + +// Hourly, check if there are players and fix the sprite +OnMinute17: + if (!getareausers("001-7", 21)) + setnpcdisplay .name$, NPC_YETI_KING; + end; + +L_VaultQuest: + mesn col(l("The Yeti King"), 3); + mesq l("Every once in a while, but I guess this is not common on your world, am I right."); + next; + select + l("I came from The Mana World."), + l("That's right, this is unheard of where I come from."), + l("...How do you know I'm not from this world?"); + mes ""; + mesn col(l("The Yeti King"), 3); + mesq l("I'm not unfamiliar with the children of Merlin, thosem whom cross the Mirror Lake. In case of The Mana World, you're lucky, we're parallel, meaning we share lots of things in common."); + next; + mesn col(l("The Yeti King"), 3); + mesq l("Now, I'm sure you could find the answer you seek without the trouble of coming here, but anyway. I guess I'll explain you how things work here, first."); + next; + mesn col(l("The Yeti King"), 3); + mesq l("In this world, Angela married with the Blue Sage. I had an... incident, with the Blue Sage, which is better forgetten. Anyway, seeking to cause a political instability, opposing Yetis every once in a while kidnap their daughter, Cindy."); + next; + mesn col(l("The Yeti King"), 3); + mesq l("The trick at tracing parallel, is finding the difference. In this world, Cindy gives a %s, a personal belonging of her, to those whom rescue her. What is the reward on your world?", getitemlink(Earmuffs)); + next; + select + l("I got a Wizard Hat."), + l("I got a Wooden Staff."); + mes ""; + mesn col(l("The Yeti King"), 3); + mesq l("That's your answer. Cindy is not a mage, right? This means some mage has bewitched the Yetis to do so, and if my parallel theory is correct, they're either aiming at the Blue Sage, or at her father."); + next; + mesn col(l("The Yeti King"), 3); + mesq l("Therefore, children of Merlin, go back to your world, and ask the Blue Sage Nikolai about it. The blue sage may fake angerness or try to dodge the question, but they are a good person. Still, you should ensure you're on his good side."); + next; + mesn col(l("The Yeti King"), 3); + mesq l("If you're still not confident enough, just tell him this: %s", col(l("*whisper whisper*"), 9)); + next; + mesn col(l("The Yeti King"), 3); + mesq l("Are you ready to cross the Mirror Lake and return to your world?"); + next; + if (askyesno() == ASK_YES) { + ##02_MLWORLD=##02_MLWORLD|MLP_TMW_YETIKING; + MirrorLakeSendTo(MLP_TMW, 0); + } + close; +} + + + + + diff --git a/npc/001-7/homunculus.txt b/npc/001-7/homunculus.txt new file mode 100644 index 0000000..5a34e0f --- /dev/null +++ b/npc/001-7/homunculus.txt @@ -0,0 +1,282 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// 001-7 Monster King's Village Configuration File +// Part of Player Quest, see 023-3 scope and 024-16 +// (C) Moubootaur Legends, 2019 + +001-7,91,89,0 script #Init0233 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@q=getq(General_Narrator); + .@q2=getq2(General_Narrator); + // Cheater Detected + if (.@q < 15) { + warp "Save", 0, 0; + die(); + end; + } + if (.@q == 15) { + dispbottom lg("I'm not a coward! I must press forward!"); + end; + } + .@mapn$="023-3"; + warp .@mapn$, 48, 23; + end; +} + +001-7,59,44,0 script #Init02331 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@n=getq(General_Narrator); + .@q=getq2(FrostiaQuest_Homunculus); + // Cheater Detected + if (.@n < 15) { + warp "Save", 0, 0; + die(); + end; + } + mesn l("Magically Sealed Gate"); + mesc l("The door is sealed. The riddle says: “I drink, I become, I am. Don't say my name, but say why you know me. For, I am the best in the world.”"); + if (!(.@q & 1)) { + mesc l("You have no idea what that means."), 1; + close; + } else { + //mesc l("You know the answer can only be @@.", getitemlink(Coffee)), 3; + mesc l("Fortunately, we know the answer is..."), 3; + next; + select + l("...Actually, I'm drawing in a blank."), + l("...Tea."), + l("...Coffee."), + l("...Water."), + l("...Potion."), + l("...Blood."), + l("...Cocktail."), + l("...Poison."); + mes ""; + if (@menu != 3) { + mesc l("But unfortunately, whoever made this riddle disagrees with me."); + close; + } + } + next; + mesn l("Magically Sealed Gate"); + if (!(.@q & 256)) { + mesc l("Even after breaking the first layer, a second layer keeps active. The first layer gets back to work shortly after. What have I missed or forgotten to do?"), 1; + close; + } else { + if (.@q != 511) + Exception("Invalid quest state: "+.@q, RB_DEFAULT|RB_SPEECH|RB_ISFATAL); + mesc l("Are you sure you want to proceed? You CANNOT COME BACK!"), 1; + if (TUTORIAL) + mesc l("Reminder: Keep an eye out for traps."); + if (askyesno() == ASK_NO) + close; + } + closeclientdialog; + // We can create instance without recording the ID etc. + // Map name limit: 4 chars (hmc1) - as of homunculus + .@mapn$="hmc1@"+getcharid(0); + .@map2$="hmc2@"+getcharid(0); + .@inst = instance_create("Homunculus "+getcharid(0), getcharid(3), IOT_CHAR); + if (.@inst >= 0) { + instance_attachmap("023-3-1", .@inst, false, .@mapn$); + instance_attachmap("023-3-2", .@inst, false, .@map2$); + // Instance lasts one hour + instance_set_timeout(3600, 3600, .@inst); + instance_init(.@inst); + } + setq1 FrostiaQuest_Homunculus, 2; + warp .@mapn$, any(45,46), 79; + @instid=.@inst; + end; +} + +001-7,50,112,0 script Abandoned Fountain#MKH NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + mesn; + mesc l("At a first glance, it seems to be full of water, but inspecting closer, it is not."); + next; + mesn; + mesc l("It is difficult to describe, it is like if it was mixed with mana itself. Drinking a bit of it was enough to recover your MP."); + percentheal 0, 100; + if (!(.@q & .hcID)) + setq2 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=1; + .distance=2; + end; +} + + +001-7,48,111,0 script Sign#MKH NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + mesn; + mesc l("Welcome to") + " --_--_-___--__-_-_."; + mesc l("Yes, we have @@!", getitemlink(Coffee)); + next; + mesn; + mesc l("The village name is difficult to read."); + if (!(.@q & .hcID)) + setq2 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=2; + .distance=2; + end; +} + +001-7,41,104,0 script Abandoned House#MKH1 NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + mesn; + mesc l("The door won't budge."); + if (!(.@q & .hcID)) + setq2 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=4; + .distance=2; + end; +} + +001-7,59,104,0 script Abandoned House#MKH2 NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + mesn; + mesc l("It seems to have been abandoned a long time ago, but the chimney is still going?"); + if (!(.@q & .hcID)) + setq2 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=8; + .distance=2; + end; +} + + +001-7,41,99,0 script Abandoned House#MKH3 NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + mesn; + mesc l("The knob has... melted down? What?"); + if (!(.@q & .hcID)) + setq2 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=16; + .distance=1; + end; +} + +001-7,59,99,0 script Abandoned House#MKH4 NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + mesn; + mesc l("There seems to be signs of a fight long forgotten, but it still reeks blood."); + if (!(.@q & .hcID)) + setq2 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=32; + .distance=1; + end; +} + +001-7,33,89,0 script Apple Trees#MKH1 NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + mesn; + mesc l("It seems to be growing apples, but by the amount of magic particles..."); + next; + mesn; + mesc l("A close inspection reveals nothing out of ordinary. It seems to be well kept."); + next; + if (!(.@q & 1)) { + mesn strcharinfo(0); + mesc l("You're hesitant to pick one, they could be dangerous."); + close; + } + mesn strcharinfo(0); + mesc l("You carefully pick a @@. It looks delicious! You feel you'll need it sooner than you expect.", getitemlink(MagicApple)); + if (!(.@q & .hcID)) { + inventoryplace MagicApple, 1; + getitem MagicApple, 1; + setq2 FrostiaQuest_Homunculus, .@q|.hcID; + } + close; +OnInit: + .hcID=64; + .distance=2; + end; +} + +001-7,40,88,0 script Abandoned House#MKH6 NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + if (!(.@q & 64)) { + mesn strcharinfo(0); + mesc l("I should check the Apple Garden first."); + close; + } + // I hope this is right + if (!( + (.@q & 4) && + (.@q & 8) && + (.@q & 16) && + (.@q & 32)) ) { + mesn strcharinfo(0); + mesc l("I better not disturb the hut owner."); + close; + } + + mesn; + mesc l("It's locked. But a close inspection reveals a small key under the rug."); + next; + mesn; + mesc l("Maybe there's a locked door somewhere, and this key will fit?"); + if (!(.@q & .hcID)) + setq2 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=128; + .distance=1; + end; +} + +001-7,50,99,0 script Abandoned House#MKH5 NPC_NO_SPRITE,{ + .@q=getq2(FrostiaQuest_Homunculus); + if (!(.@q & 128)) { + mesn; + mesc l("It's locked."); + next; + mesn strcharinfo(0); + mesc l("Maybe there is a key somewhere near. I should keep looking."); + close; + } + mesn; + mesc l("It's locked."); + next; + mesn; + mesc l("You use the small key from the apple garden hut. It fits neatly."); + next; + mesn; + mesc l("The hut is... empty. And it doesn't have a fireplace, either."); + next; + mesn; + mesc l("You don't know how the chimney keeps producing smoke. It must be using hiding magic."); + next; + mesn; + mesc l("Whoever took control of this village is no ordinary mage. There's a switch on the wall."); + next; + mesn; + mesc l("You flip the switch. Nothing happens."); + // You can unflip it :> + setq2 FrostiaQuest_Homunculus, .@q^.hcID; + mesc l("Strange switch status: @@", (.@q&.hcID ? l("Inactive") : l("Active"))), 3; + close; +OnInit: + .hcID=256; + .distance=1; + end; +} + diff --git a/npc/001-7/mapflags.txt b/npc/001-7/mapflags.txt new file mode 100644 index 0000000..296862a --- /dev/null +++ b/npc/001-7/mapflags.txt @@ -0,0 +1 @@ +001-7 mapflag zone MMO diff --git a/npc/001-8/_import.txt b/npc/001-8/_import.txt new file mode 100644 index 0000000..0dce527 --- /dev/null +++ b/npc/001-8/_import.txt @@ -0,0 +1,5 @@ +// Map 001-8: Hungry Quirin Arena +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/001-8/_mobs.txt", +"npc/001-8/hub.txt", +"npc/001-8/mapflags.txt", diff --git a/npc/001-8/_mobs.txt b/npc/001-8/_mobs.txt new file mode 100644 index 0000000..24a0290 --- /dev/null +++ b/npc/001-8/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 001-8: Hungry Quirin Arena mobs +001-8,49,49,30,30 monster Piousse 1003,11,20000,20000 +001-8,28,47,10,7 monster Plushroom Field 1011,18,30000,30000 +001-8,70,51,10,7 monster Chagashroom Field 1128,18,30000,30000 +001-8,49,28,31,10 monster Manana Tree 1017,26,30000,30000 +001-8,28,70,10,10 monster Squirrel 1032,8,30000,30000 +001-8,67,72,14,9 monster Manana Tree 1017,8,30000,30000 diff --git a/npc/001-8/hub.txt b/npc/001-8/hub.txt new file mode 100644 index 0000000..9ba3cf5 --- /dev/null +++ b/npc/001-8/hub.txt @@ -0,0 +1,433 @@ +// TMW2 Script +// Author: +// Ernando <ernando.quirino@hotmail.com> (Creator) +// Jesusalva <admin@tmw2.org> +// Description: +// Hunger Games™ version for The Mana World Brazil v2, created by Ernando Quirino. + +001-8,0,0,0 script #QuirinoHUB NPC_HIDDEN,{ + end; + + // HUBspawn( mobID, amount ) + function HUBspawn { + areamonster "001-8", 20, 20, 80, 80, strmobinfo(1, getarg(0)), getarg(0), getarg(1); + return; } + + // HUBscatter( itemID ) + function HUBscatter { + makeitem(getarg(0), 1, "001-8", rand(20,80), rand(20,80)); + return; } + + // HUBarrow() + function HUBarrow { + makeitem(rand(TrainingArrow,BoneArrow), rand(10,30), "001-8", rand(20,80), rand(20,80)); + return; } + + // HUBrandwpn( full ) + function HUBrandwpn { + if (getarg(0)) { + .@x=rand(20,80); .@y=rand(20,80); + } else { + .@x=rand(44,55); .@y=rand(46,54); + } + + .@r=rand(1,8); + switch(.@r){ + case 1: + makeitem(rand(Knife, WoodenSword), 1, "001-8", .@x, .@y); break; + case 2: + makeitem(rand(SharpKnife, Dagger), 1, "001-8", .@x, .@y); break; + case 3: + makeitem(rand(ThunderStaff,Judgment), 1, "001-8", .@x, .@y); break; + case 4: + makeitem(rand(BronzeGladius,Scythe), 1, "001-8", .@x, .@y); break; + case 5: + makeitem(rand(WoodenBow, ElficBow), 1, "001-8", .@x, .@y); break; + case 6: + makeitem(rand(WoodenBow, ShortBow), 1, "001-8", .@x, .@y); break; + case 7: + makeitem(rand(ForestBow, BansheeBow), 1, "001-8", .@x, .@y); break; + default: + makeitem(WoodenShield, 1, "001-8", .@x, .@y); break; + } + + return; } + +// Only one player standing! CONGRATULATIONS, YOU HAVE WON!! +OnGameOver: + .@u=getmapusers("001-8")-1; + debugmes "OnGameOver Check: "+str(.@u); + if (.@u == 1) { + $@EQ_STATUS=2; + maptimer("001-8", 10, "#QuirinoHUB::OnVictory"); + killmonsterall("001-8"); + stopnpctimer; + cleanmap("001-8"); + } + end; + + +// Ordered by GM to start. +OnStart: + $@EQ_STATUS=1; + + // 2 Necklaces, 1 Charm and 1 Quiver + makeitem(rand(1000,1003), 1, "001-8", rand(44,55), rand(46,54)); + makeitem(rand(1006,1011), 1, "001-8", rand(44,55), rand(46,54)); + makeitem(rand(1150,1151), 1, "001-8", rand(44,55), rand(46,54)); + makeitem(rand(1172,1174), 1, "001-8", rand(44,55), rand(46,54)); + + makeitem(GoldenRing, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(SilverRing, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(SilverRing, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(rand(PolishedDiamond,PolishedEmerald), 1, "001-8", rand(44,55), rand(46,54)); + makeitem(rand(PolishedDiamond,PolishedEmerald), 1, "001-8", rand(44,55), rand(46,54)); + makeitem(rand(PolishedDiamond,PolishedEmerald), 1, "001-8", rand(44,55), rand(46,54)); + makeitem(rand(TrainingArrow,BoneArrow), 50, "001-8", rand(44,55), rand(46,54)); + + makeitem(CandorBoots, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(LousyMoccasins, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(CottonBoots, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(CreasedBoots, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(TulimsharGuardBoots, 1, "001-8", rand(44,55), rand(46,54)); + + makeitem(rand(CreasedGloves, MinerGloves), 1, "001-8", rand(44,55), rand(46,54)); + + makeitem(CottonShorts, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(JeansShorts, 1, "001-8", rand(44,55), rand(46,54)); + makeitem(PirateShorts, 1, "001-8", rand(44,55), rand(46,54)); + + + maptimer("001-8", 5000, "#QuirinoHUB::OnCount10"); + announce("##1HUNGRY QUIRIN EVENT: ##3##BCountdown: 15 seconds!", bc_all|bc_npc); + end; + +OnCount10: + // Random arrows + weapon + makeitem(rand(TrainingArrow,BoneArrow), 50, "001-8", rand(44,55), rand(46,54)); + + // Random Weapon + HUBrandwpn(0); + + // Throw some random, weighty stuff on the whole map. + HUBscatter(rand(CopperOre, TitaniumOre)); + + specialeffect(4, AREA, getcharid(3)); + addtimer(5000, "#QuirinoHUB::OnCount5"); + dispbottom l("10 seconds!"); + end; + +OnCount5: + specialeffect(4, AREA, getcharid(3)); + + // Random Helmet + .@r=rand(1,9); + switch(.@r){ + case 1: + makeitem(Bandana, 1, "001-8", rand(44,55), rand(46,54)); break; + case 2: + makeitem(Bucket, 1, "001-8", rand(44,55), rand(46,54)); break; + case 3: + makeitem(MoubooHat, 1, "001-8", rand(44,55), rand(46,54)); break; + case 4: + makeitem(CandleHelmet, 1, "001-8", rand(44,55), rand(46,54)); break; + case 5: + makeitem(PinkieHat, 1, "001-8", rand(44,55), rand(46,54)); break; + case 6: + makeitem(GraduationCap, 1, "001-8", rand(44,55), rand(46,54)); break; + case 7: + makeitem(SerfHat, 1, "001-8", rand(44,55), rand(46,54)); break; + case 8: + makeitem(MinerHat, 1, "001-8", rand(44,55), rand(46,54)); break; + default: + makeitem(PaperBag, 1, "001-8", rand(44,55), rand(46,54)); break; + } + + // Throw some random, weighty stuff on the whole map. + HUBscatter(rand(CopperIngot, TitaniumIngot)); + + addtimer(5000, "#QuirinoHUB::OnBegin"); + dispbottom l("5 seconds!"); + end; + +OnBegin: + // Scatter some healing items, specially where the weapons are, to mess drop lists; Send armor too + makeitem(rand(Acorn, Croconut), 1, "001-8", rand(44,55), rand(46,54)); + makeitem(rand(Manana, Carrot), 1, "001-8", rand(24,75), rand(26,74)); + makeitem(rand(RoastedMaggot, CherryCake), 1, "001-8", rand(24,75), rand(26,74)); + makeitem(rand(CreasedShirt, CandorShirt), 1, "001-8", rand(24,75), rand(26,74)); + HUBscatter(rand(BottleOfDivineWater, BottleOfSewerWater)); + + // Scatter around whole map a few arrows, a training bow, a knife, a creased shirt and a creased knife + HUBarrow(); + HUBscatter(TrainingBow); + HUBscatter(Knife); + HUBscatter(CreasedShirt); + HUBscatter(CreasedShorts); + + // Throw some random stuff on the whole map. + HUBscatter(rand(AquadaBox, WoodenLog)); + + // Free player, let's start! + delcells "qhubN"; + delcells "qhubS"; + setpcblock(PCBLOCK_SOFT, false); + specialeffect(FX_MGSHIELD, AREA, getcharid(3)); + specialeffect(FX_CRITICAL, AREA, getcharid(3)); + dispbottom col(l("Run! Event started!"), 1); + initnpctimer; // Restart the timer to fix any desync. + end; + +// We only have a schedule for the first 15 minutes. This accelerate stuff. +OnSendWave: + HUBspawn(AngryRedScorpion, 3); + HUBspawn(BlackScorpion, 1); + HUBscatter(rand(PiberriesInfusion,LachesisBrew)); + HUBscatter(CelestiaTea); + HUBarrow(); + // Throw some ENTIRELY random stuff on the whole map. + HUBscatter(rand(700, 713)); + HUBscatter(rand(731, 875)); + HUBrandwpn(1); + HUBspawn(SlimeBlast, 1); + end; + +// 15 seconds after, I'm sure it is synced, so start spawning monsters. +OnTimer15000: + HUBspawn(WhiteSlime, 3); + HUBspawn(SlimeBlast, 1); + // Throw some random stuff on the whole map. + HUBscatter(rand(731, 875)); + end; + + +// one minute is due. +OnTimer60000: + HUBspawn(RedSlime, 12); + HUBspawn(SlimeBlast, 4); + HUBarrow(); + // Throw some random stuff on the whole map. + HUBscatter(rand(731, 875)); + end; + +// Two minutes are due. +OnTimer120000: + HUBspawn(LavaSlime, 2); + HUBspawn(GreenSlime, 2); + HUBspawn(SeaSlime, 2); + HUBspawn(CaveMaggot, 2); + HUBspawn(Bandit, 2); + HUBarrow(); + HUBarrow(); + HUBscatter(ElixirOfLife); + HUBscatter(FatesPotion); + HUBscatter(LachesisBrew); + // Throw some random stuff on the whole map. + HUBscatter(rand(731, 875)); + end; + + +// Three minutes are due. +OnTimer180000: + HUBspawn(GreenSlime, 4); + HUBspawn(SeaSlime, 4); + HUBspawn(Squirrel, 4); + HUBspawn(Snake, 1); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBscatter(AshUrn); + HUBscatter(Monocle); + HUBscatter(GreenEggshellHat); + HUBscatter(LeatherShirt); + // Throw some random stuff on the whole map. + HUBscatter(rand(731, 875)); + end; + +// Five minutes are due. +OnTimer300000: + HUBspawn(GreenSlime, 4); + HUBspawn(AngryRedScorpion, 3); + HUBspawn(Sarracenus, 1); + HUBspawn(Snake, 2); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBscatter(LeatherGloves); + HUBrandwpn(1); + HUBrandwpn(1); + HUBscatter(GoldenPearlRing); + HUBscatter(ElixirOfLife); + // Throw some random stuff on the whole map. + HUBscatter(rand(731, 875)); + HUBscatter(rand(731, 875)); + end; + +// Nine minutes are due. +OnTimer540000: + HUBspawn(BlackScorpion, 2); + HUBspawn(DarkLizard, 3); + HUBspawn(Tipiou, 2); + HUBspawn(Snake, 2); + HUBspawn(MountainSnake, 1); + HUBspawn(SeaSlime, 2); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBrandwpn(1); + HUBrandwpn(1); + HUBscatter(GoldenBlackPearlRing); + HUBscatter(ElixirOfLife); + // Throw some random stuff on the whole map. + HUBscatter(rand(731, 875)); + HUBscatter(rand(731, 875)); + end; + +// Fifteen minutes are due. No more random stuff will show. +OnTimer900000: + HUBspawn(BlackScorpion, 4); + HUBspawn(DarkLizard, 3); + HUBspawn(Tipiou, 2); + HUBspawn(Snake, 1); + HUBspawn(MountainSnake, 2); + HUBspawn(SeaSlime, 2); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBarrow(); + HUBrandwpn(1); + HUBrandwpn(1); + HUBrandwpn(1); + HUBscatter(ElixirOfLife); + // Throw some random stuff on the whole map. + HUBscatter(rand(731, 875)); + HUBscatter(rand(731, 875)); + HUBscatter(rand(731, 875)); + end; + +OnVictory: + @qhub_victor=1; + announce(sprintf("##1HUNGRY QUIRIN EVENT: ##3##B%s has won the match! Hail!", strcharinfo(0)), bc_all|bc_npc); + if (!QUIRINO_WINNER) + QUIRINO_WINNER = gettimetick(2); +OnPCDieEvent: + @qhub_died=1; +OnPCLogoutEvent: + getmapxy(.@mapa$, .@a,.@b, 0); + if (.@mapa$ == "001-8") { + // Deal with the environment { + .@u=getmapusers("001-8")-1; + mapannounce("001-8", "Total remaining players: "+.@u,0); + HUBscatter(rand(PiberriesInfusion,AtroposMixture)); + HUBscatter(CelestiaTea); + HUBarrow(); + HUBspawn(SlimeBlast, 1); + if (.@u % 2 == 1) + HUBscatter(Piberries); + if (.@u == 2) { + mapannounce("001-8", "The wolves were released!",0); + HUBscatter(ElixirOfLife); + HUBscatter(Aquada); + HUBscatter(Piberries); + HUBscatter(Beer); + HUBarrow(); + HUBspawn(Wolvern, 4); + HUBspawn(NightScorpion, 2); + } + if (.@u == 1 && !@qhub_victor) { + donpcevent "#QuirinoHUB::OnGameOver"; + } + + // } Deal with the player { + clearitem(); + if (checkpcblock() & PCBLOCK_ATTACK) + setpcblock(PCBLOCK_SOFT, false); + + // Check if to reduce clearitem() efficiency you've used the cart in an illegal way. + getcartinventorylist(); + if (@cartinventorylist_count>=1) { + // Obviously a cheater, you should not be using the cart on the event. I HATE CHEATERS! + // Destroy everything you had on the cart + if ($@HAS_API) { + query_sql("DELETE FROM `cart_inventory` WHERE `char_id`="+getcharid(0)); + } else { + apiasync("SQL", sprintf("DELETE FROM `cart_inventory` WHERE `char_id`='%d'", getcharid(0))); + apiasync("SQLRUN", ""); + } + // Destroy the cart. Cheaters doesn't deserve it!! + setcart(0); + // Delete the storage register. You now need to pay it again, to don't cheat anymore! + setq General_Banker, 0; + // You'll also forsake any event reward. + @qhub_victor=0; + @qhub_died=0; + } + + // If you are the victor (and didn't cheat), you can now hold your reward ;-) + if (@qhub_victor) { + getitem any(MercBoxA, MercBoxA, SilverGift, MercBoxBB, GoldenGift), 1; + Zeny=Zeny+rand2(500, 2500); + } + if (@qhub_died || @qhub_victor) + getexp rand2(100,300), BaseLevel*10; // Dying on this map is enough to get a reward. Logout = no reward. + + // You'll be revived/fully healed, and then warped. + // FIXME: It will throw you in Nard's ship if you are on logout... + recovery(getcharid(3)); + // If recovery() is broken: + //atcommand "#alive \""+strcharinfo(0)+"\""; + //percentheal 100, 100; + warp "000-1", 22, 22; + } + + // TODO: Checks which doesn't belong here shouldn't be here! + HUB_Logout(@qhub_died); + + @qhub_victor=0; + @qhub_died=0; + end; + +OnCancel: + setpcblock(PCBLOCK_SOFT, false); + warp "Save", 0, 0; + end; + +// No penalty override +OnNoPenaltyCommand: + @realvalue=@deathpenalty_realvalue-readparam(BaseExp); + @realvaljob=@deathpenalty_realvaljob-readparam(JobExp); + + // GM Report + if (is_staff()) + debugmes("Old values: %d %d Current Values: %d %d Real Difference: %d %d", @deathpenalty_realvalue, @deathpenalty_realvaljob, readparam(BaseExp), readparam(JobExp), @realvalue, @realvaljob); + + // Revive and Warp you to save point or it'll have no effect + recovery(getcharid(3)); + warp "Save", 0, 0; + addtimer(500, "#QuirinoHUB::OnNoPenaltyCommand2"); + end; + +OnNoPenaltyCommand2: + // Restitute the lost experience + if (@deathpenalty_override == 1) + getexp @realvalue, @realvaljob; + else if (@deathpenalty_override == 2) + getexp @realvalue/2, @realvaljob/2; + else + dispbottom l("BUG, REPORT ME: QHUB PENALTY OVERRIDE INVALID SIGNAL @@", @deathpenalty_override); + + // Clear temporary variables + @deathpenalty_override=0; + @deathpenalty_realvalue=0; + @deathpenalty_realvaljob=0; + @realvalue=0; + @realvaljob=0; + end; +} + diff --git a/npc/001-8/mapflags.txt b/npc/001-8/mapflags.txt new file mode 100644 index 0000000..5fcbceb --- /dev/null +++ b/npc/001-8/mapflags.txt @@ -0,0 +1,5 @@ +001-8 mapflag zone MMO +001-8 mapflag pvp +001-8 mapflag pvp_noparty +001-8 mapflag pvp_noguild +001-8 mapflag nostorage diff --git a/npc/001-9/_import.txt b/npc/001-9/_import.txt new file mode 100644 index 0000000..417c111 --- /dev/null +++ b/npc/001-9/_import.txt @@ -0,0 +1,2 @@ +// Map 001-9: Eternity Maze +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/002-1/_import.txt b/npc/002-1/_import.txt new file mode 100644 index 0000000..d9d804f --- /dev/null +++ b/npc/002-1/_import.txt @@ -0,0 +1,15 @@ +// Map 002-1: Second Deck +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/002-1/_mobs.txt", +"npc/002-1/alige.txt", +"npc/002-1/arpan.txt", +"npc/002-1/billybons.txt", +"npc/002-1/chefgado.txt", +"npc/002-1/dan.txt", +"npc/002-1/devis.txt", +"npc/002-1/doors.txt", +"npc/002-1/hammock.txt", +"npc/002-1/juliet.txt", +"npc/002-1/knife.txt", +"npc/002-1/mapflags.txt", +"npc/002-1/peter.txt", diff --git a/npc/002-1/_mobs.txt b/npc/002-1/_mobs.txt new file mode 100644 index 0000000..0cc92c0 --- /dev/null +++ b/npc/002-1/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 002-1: Second Deck mobs +002-1,49,30,25,12 monster Piou 1002,2,30000,20000,PiouSpwn::OnFakeKill diff --git a/npc/002-1/alige.txt b/npc/002-1/alige.txt new file mode 100644 index 0000000..e23bfae --- /dev/null +++ b/npc/002-1/alige.txt @@ -0,0 +1,204 @@ +// TMW2 Scripts. +// +// TMW-BR Original Authors: +// Programmer: Adson Renato +// Texts: Arkanjo +// Review: Jesusalva +// +// Authors: +// Jesusalva +// Description: +// Stowaway hidden in a ship's hole. Contrabandist. Trade potions, dyes, +// food, water, and money for an item. +// In BR originals requested a four leaf clover to do luck magic, in order to not be caught. +// That would imply teaching a new skill, and I'm not felling like it, so I removed. + +002-1,45,26,0 script Alige NPC_ALIGE,{ + if (BaseLevel < 42) goto L_Weak; + .@q=getq(ShipQuests_Alige); + if (.@q == 1) goto L_Return; + if (.@q == 2) goto L_End; + mesn; + mesq lg("Hey, dude! The guards are after me. I need your help, and I can reward you."); + next; + mesn strcharinfo(0); + menu + l("Guaaaards! Sailors! Help! We have a stowaway!"), L_Revolt, + l("Not right now, I'm busy."), L_Close, + l("Did you said reward?!"), L_GoGo; + +L_Weak: + mesc l("You thought there was someone here. It must have been your imagination."); + if (@seenalige) + goto L_Noob; + @seenalige=true; + close; + +L_Noob: + mes ""; + mesc l("Protip: You need level %d or higher to do this quest.", 42), 1; + close; + +L_Revolt: + mes ""; + message strcharinfo(0), l("Guaaaards! Sailors! Help! We have a stowaway!"); + mesn; + mesq l("No, please don't! I have 3 kids to feed. They are looking for me for contraband. Please, let me go!"); + close; + +L_GoGo: + mes ""; + mesn; + mesq l("Yes, I need to get out of here the earliest possible."); + next; + mesn strcharinfo(0); + mesq l("So tell me already what you need, and what is the reward!"); + next; +// Stowaway hidden in a ship's hole. Contrabandist. Trade potions, dyes, +// food, water, and money for a hat. + mesn; + mesc l("Alige hands you an old paper patch."); + next; + mesn l("Old Paper Patch"); + mes l("* @@/30 @@", countitem(CactusPotion), getitemlink(CactusPotion)); + mes l("* @@/12 @@", countitem(Bread), getitemlink(Bread)); + mes l("* @@/12 @@", countitem(Cheese), getitemlink(Cheese)); + mes l("* @@/12 @@", countitem(CherryCake), getitemlink(CherryCake)); + mes l("* @@/8 @@", countitem(BottleOfTonoriWater), getitemlink(BottleOfTonoriWater)); + mes l("* @@/6 @@", countitem(HastePotion), getitemlink(HastePotion)); + mes l("* @@/6 @@", countitem(StrengthPotion), getitemlink(StrengthPotion)); + mes l("* @@/2 @@", countitem(YellowDye), getitemlink(YellowDye)); + mes l("* @@/2 @@", countitem(BlueDye), getitemlink(BlueDye)); + mes l("* @@/7.500 GP", Zeny); + next; + // Temporary item. This four leaf amulet can be evolved: Bromenal < Iron < Golden < Crozenite + mesn; + mesq l("In exchange for your help, I'll give you a @@!", getitemlink(CrozeniteFourLeafAmulet)); + menu + l("Of course I'll help you!"), L_Accept, + l("Have you got mad? That's too much, it's like you are trying to be perfect! No way I'll help you!"), L_Close; + +L_Accept: + mes ""; + mesn; + mesq l("\"Many thanks! I'll be waiting for you, hiding on the ship's hold!\""); + next; + mesn strcharinfo(0); + mesq l("Could you first explain me why so many items?"); + next; + mesn; + mesq l("Very well, listen to my plan!"); + next; + mesn; + mesq l("The dyes are to disguise myself, I don't want to get caught. The potions are for safety, who knows what I'll face?"); + next; + mesn; + mesq l("Water, Bread, Cheese and Cherry Cake are to eat, and money is always useful, you know."); + next; + mesn strcharinfo(0); + mesq l("You planned neatly. I'll be back."); + setq ShipQuests_Alige, 1; + close; + +L_Return: + mesn; + mesq l("Hey, psst! Have you brought me what I asked for?"); + next; + mesn strcharinfo(0); + menu + l("What I had to bring, again?"), L_Remember, + l("Yes, you can count it."), L_Check, + l("No... Not yet, sorry."), L_Close; + +L_Remember: + mes ""; + mesc l("Alige hands you an old paper patch."); + next; + mesn l("Old Paper Patch"); + mes l("* @@/30 @@", countitem(CactusPotion), getitemlink(CactusPotion)); + mes l("* @@/12 @@", countitem(Bread), getitemlink(Bread)); + mes l("* @@/12 @@", countitem(Cheese), getitemlink(Cheese)); + mes l("* @@/12 @@", countitem(CherryCake), getitemlink(CherryCake)); + mes l("* @@/8 @@", countitem(BottleOfTonoriWater), getitemlink(BottleOfTonoriWater)); + mes l("* @@/6 @@", countitem(HastePotion), getitemlink(HastePotion)); + mes l("* @@/6 @@", countitem(StrengthPotion), getitemlink(StrengthPotion)); + mes l("* @@/2 @@", countitem(YellowDye), getitemlink(YellowDye)); + mes l("* @@/2 @@", countitem(BlueDye), getitemlink(BlueDye)); + mes l("* @@/7.500 GP", Zeny); + next; + // Temporary item. This four leaf amulet can be evolved: Bromenal < Iron < Golden < Crozenite + mesn; + mesq l("In exchange for your help, I'll give you a @@!", getitemlink(CrozeniteFourLeafAmulet)); + close; + +L_Check: + if (countitem(CactusPotion) < 30 || + countitem(Bread) < 12 || + countitem(Cheese) < 12 || + countitem(CherryCake) < 12 || + countitem(BottleOfTonoriWater) < 8 || + countitem(HastePotion) < 6 || + countitem(StrengthPotion) < 6 || + countitem(YellowDye) < 2 || + countitem(BlueDye) < 2) + goto L_Missing; + if (Zeny < 7500) goto L_Missing; + + inventoryplace CrozeniteFourLeafAmulet, 1; + delitem CactusPotion, 30; + delitem Bread, 12; + delitem Cheese, 12; + delitem CherryCake, 12; + delitem BottleOfTonoriWater, 8; + delitem HastePotion, 6; + delitem StrengthPotion, 6; + delitem YellowDye, 2; + delitem BlueDye, 2; + Zeny = Zeny - 7500; + getitem CrozeniteFourLeafAmulet, 1; + getexp 32625, 50; + setq ShipQuests_Alige, 2; + mes ""; + mesn; + mesq l("Good job... Here is your reward..."); + next; + mesn; + mesq l("I have to get going now. Thanks for the help!"); + close; + +L_Missing: + mes ""; + mesn; + mes l("There's not everything I've asked for..."); + next; + goto L_Remember; + +L_End: + mesn; + mes l("I am still here, but I already did amends for my acts and don't need to flee anymore..."); + next; + mesn strcharinfo(0); + mes l("Why am I having a hard time to believe on you..."); + close; + +L_Close: + close; + +OnInit: + .sex = G_MALE; + .distance = 2; + + /* + // Preventive check against faulty update. Must be removed afterwards. + // UPDATE `quest` SET `count1` = '0' WHERE `quest`.`quest_id` = 2; + .@nb = query_sql("select `char_id` from `quest` WHERE (`count1`>=1 and `quest_id`=2) LIMIT 2", .@name$); + if (getarraysize(.@name$) > 0) { + debugmes "FATAL ERROR: Quest log not updated."; + debugmes "disabling Alige to prevent weirder bugs."; + debugmes "UPDATE `quest` SET `count1` = '0' WHERE `quest`.`quest_id` = 2"; + disablenpc .name$; + } + */ + + end; +} diff --git a/npc/002-1/arpan.txt b/npc/002-1/arpan.txt new file mode 100644 index 0000000..11de798 --- /dev/null +++ b/npc/002-1/arpan.txt @@ -0,0 +1,184 @@ +// TMW-2 Script. +// Editor: Jesusalva +// +// Evol scripts. +// Authors: +// Ablu +// Qwerty Dragon +// Description: +// Introduction NPC + +002-1,49,36,0 script LeftDoorCheck NPC_HIDDEN,0,0,{ + .@q = getq(ShipQuests_Arpan); + if (.@q == 5) doevent instance_npcname("Magic Arpan")+"::OnTalk"; + + close; +} + +002-1,54,36,0 script RightDoorCheck NPC_HIDDEN,0,0,{ + .@q = getq(ShipQuests_Arpan); + if (.@q == 5) doevent instance_npcname("Magic Arpan")+"::OnTalk"; + + close; +} + + +002-1,49,33,0 script Magic Arpan NPC_MAGIC_ARPAN,{ + showavatar NPC_MAGIC_ARPAN; + + .@q = getq(ShipQuests_Arpan); + .@s = getq2(ShipQuests_Arpan); + .@n = getq(General_Narrator); + .@q_julia = getq(ShipQuests_Julia); + +OnTalk: + showavatar NPC_MAGIC_ARPAN; + mesn; + + if (.@q > 5) goto L_Menu; + setq ShipQuests_Arpan, 6; + deltimer("Magic Arpan::OnSlow"); + + mesq lg("Yeye, are you finally ready to go?"); + if (!TUTORIAL) + mesc l("Protip: You skipped tutorial. A lot of tutorial-ish dialogs and quests will be skipped. You can change this anytime on %s > Game Settings.", b("@ucp")); + next; + + select + l("Yes, I want to find out who I am."), + l("No, but what option do I have? I'm railroaded!"); + mes ""; + + if (@menu == 2) { + mesn; + mesq l("Yeye is not paying for your food and is not a sailor like us! You should start standing on your own feet."); + next; + mesn strcharinfo(0); + mesq l("Thanks for the honestity, I guess."); + next; + } + + mes ""; + mesn; + mesq l("Anyway, our shipkeeper, Juliet, helped to heal your injuries way back."); + next; + mesn; + if (.@q_julia == 0) + setq ShipQuests_Julia, 1; + mesq lg("Yaya, you should go see her! She'll be happy to help you again."); + next; + mesc b(l(".:: Main Quest 1-1 ::.")), 3; + msObjective(false, l("Talk to @@", l("Juliet"))); + tutmes l("Juliet is east (right) of %s.", .name$), l("Protip"), false; + next; + goto L_Menu; + + +L_Menu: + mesq l("What yeye could I do for you today?"); + next; + + menu + rif(.@q_julia < 2, lg("Where can I find Juliet?")), L_Julia, + rif(!.@n, lg("Could you tell me where I am?")), L_Where, + rif(!.@n, l("Who are you?")), L_Who, + rif(!.@n, l("I need a tutorial, where can I find help?")), L_Trainer, + rif(!getq(ShipQuests_ArpanMoney), lg("Do you know what happened to the gold I had when you guys saved me?")), L_WhereMoney, + l("Nothing, sorry."), -; + + closedialog; + close; + +L_Trainer: + mes ""; + mesn; + mesq l("There is a NPC called Trainer, just outside this ship."); + next; + mesn; + mesq l("Just use the arrow key--, err, I mean, just walk to the door on the right. The one which is not guarded by Peter."); + next; + mesn; + mesq lg("You must be dressed, and talk to our captain first. He'll give you a mission and unlock the ship main door."); + next; + mesn; + mesq l("All you need to do then is walk outside, enter on the biggest house, and talk to the Trainer. He'll teach you everything."); + next; + goto L_Menu; + +L_Where: + mes ""; + mesn; + mesq lg("You're on our ship, we made port to a little island and we're actually yeyending our long merchant travelling adventure at the city of Tulimshar."); + next; + mesq l("We will be yaying there in a few days, so we will drop you off there."); + next; + mesq l("You will see, citizens are polite and you can still ask around for help. They can help find a job for you or maybe help you find out what happened to you out at sea!"); + next; + + goto L_Menu; + +L_Julia: + mes ""; + mesn; + mesq lg("Just go right, yeye can't miss her. She's the only girl in this crew, oh well, except for you now yeyeye!", "Just go right, yeye can't miss her. She's the only girl in this crew."); + next; + + mesn "Narrator"; + mesc l("Use the arrow keys to walk right and meet Juliet."); + next; + + goto L_Menu; + +L_Who: + mes ""; + mesn; + mesq l("Sorry! I forgot to introduce myself. My name is Arpan, but other sailors call me Magic Arpan because I know one or two yaing magic tricks."); + next; + mesn; + mesq l("They're not magic, but yayaya, people like to say it is! Yeyeye."); + next; + + goto L_Menu; + +L_WhereMoney: + mes ""; + mesn; + mesq l("Oh right, I totally forgot about that, here you go."); + next; + + if (BaseLevel < 4) goto L_Apana; + setq ShipQuests_ArpanMoney, 1; + Zeny = Zeny + 35; + message strcharinfo(0), l("You receive @@ GP!", 35); + + goto L_BeforeMenu; + +L_Apana: + mesn; + mesq l("On hindsight, I'll wait you get a few levels. Can't have cheaters, ya know!"); + next; + goto L_Menu; + +L_BeforeMenu: + mesn; + goto L_Menu; + +OnSlow: + npctalk3 l("@@, do you need help? Are you lost? Click me!", strcharinfo(0)); + /* + setcamnpc; + showavatar NPC_MAGIC_ARPAN; + mesn; + mesq l("Yayaya, @@ is surely slow. Do you remember how to walk? You can use arrow keys for that!", strcharinfo(0)); + next; + mesn; + mesc l("Here, come talk to me, the Magic Arpan! I'll help you get dressed."), 1; + */ + addtimer(90000,"Magic Arpan::OnSlow"); + close; + +OnInit: + .sex = G_MALE; + .distance = 6; + end; +} diff --git a/npc/002-1/billybons.txt b/npc/002-1/billybons.txt new file mode 100644 index 0000000..89683ed --- /dev/null +++ b/npc/002-1/billybons.txt @@ -0,0 +1,108 @@ +// TMW2 Script +// Evol scripts. +// Author: +// Reid +// Description: +// Drunk easteregg telling about the player's past. +// For TMW2 it is just to provide a bottle + +002-1,29,33,0 script Billy Bons#TMW2 NPC_BILLY_BONS,{ + if (rand(5) == 2) goto L_Hic; + .@q = getq(ShipQuests_Bottle); + if (.@q == 1) goto L_Hic; + + mesn; + mesq l("You? Here?"); + mesq l("How is *hic* it possible?"); + next; + + mesn "Narrator"; + mesc l("The sailor chugs his beer."); + next; + + select + lg("Excuse me? Do you know who I am?"); + + mes ""; + mesn; + mesq l("Don't do theee... *hic* with me eh!"); + next; + + menu + l("Are you ok?"), L_Quit, + l("Take the bottle?"), L_Give, + l("Alright... Bye."), -; + + mes ""; + mesn; + mesq lg("No and *hic*... No, you and you and your... *burp* stup*hic* guild!"); + next; + mesq l("You tried to get rid of me, eeh? But surprise! I'm still here... *hic* Or there..."); + next; + mesq l("But you won't *hic* me this time..."); + next; + + menu + l("What are you talking about? What guild?"), L_Quit, + l("You are full of wine, my friend..."), -, + l("You should go and get some sleep."), L_Quit; + + mes ""; + mesn; + mesq l("If I saw *hic* who you were... *hic* Would not have helped you! "); + next; + + menu + l("But who am I?"), -, + l("What am I supposed to say?"), L_Quit, + l("You should go and get some sleep."), L_Secret; + + mes ""; + mesn; + mesq l("The giant boogeyman!"); + + close; + +L_Secret: + mes ""; + mesn; + mesq l("Hear me *hic* well, what ever, whatididever you will *hic* said ab... uhm... out what?! You saw there, the Guild won't let it get public."); + next; + mesq l("Nobody can know! *burp*"); + next; + + close; + +L_Hic: + npctalk3 l("*Hic*"); + + close; + +L_Quit: + mes ""; + mesn; + mesq l("Yeah you're all like *hic* that, but you won't get me! *burp*"); + next; + mesq l("I'm not that numb eeh *hic* what did ever yous disco... ...vered there, the Guild won't get me!"); + mesq l("*burp*"); + next; + + mesn "Narrator"; + mesc l("The sailor turns his back to you."); + + close; + +L_Give: + mes ""; + inventoryplace EmptyBottle, 1; + + setq ShipQuests_Bottle, 1; + getitem EmptyBottle, 1; + + close; + +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} diff --git a/npc/002-1/chefgado.txt b/npc/002-1/chefgado.txt new file mode 100644 index 0000000..6eb08d9 --- /dev/null +++ b/npc/002-1/chefgado.txt @@ -0,0 +1,109 @@ +// TMW-2 scripts. +// Original Authors: Hal9000 & Qwerty Dragon +// TMW-2 Authors: +// Ayruss +// Jesusalva +// Description: +// La Johanne Chef. +// The cook may need help with something. +// Variable: +// ShipQuests_ChefGado +// ShipQuests_Knife +// Translation +// FR Translated + +002-1,27,28,0 script Chef Gado NPC_CHEF_GADO,{ + .@q = getq(ShipQuests_Knife); + .@p = getq(ShipQuests_ChefGado); + + // Player should have the rusty knife already + if (!.@q) goto L_Knife; + + // Piou quest + if (.@q == 1 && .@p == 0) goto L_PiouLegs; + if (.@p == 1) goto L_Continue; + if (.@p == 2) goto L_Complete; + + // An error happened! + close; + +L_Knife: + mesn; + mesq l("I hate the sea, the salty air always lets my knives rust. I already have a stockpile of rusty knives, don't even know what to do with them."); + if (TUTORIAL) mesc l("That indeed is true, you can see a pile of rusty knives on the nearby table."); + if (TUTORIAL) dnext; + tutmes l("Unlike talking, when trying to pick objects or read signs, you should be at an arms distance of them."), l("NOTE"), false; + tutmes l("This is often 1, 2 or 3 tiles. For talking, usually 4 or 5 tiles. Shouting can be done from 12 up to 18 tiles; But as shouting is rude, most NPCs won't react to it."), l("TUTORIAL"), false; + close; + +L_PiouLegs: + mesn; + mesq l("I'm trying to make good food for all the sailors here, could you help me?"); + mes ""; + menu + l("Yes."), L_Start, + l("Is there a reward?"), L_Ask, + l("No."), -; + close; + + +L_Ask: + mes ""; + mesn; + mesq l("I have a spare pair of gloves laying somewhere, you can have those if you finish the task."); + mes ""; + menu + l("Sounds good."), L_Start, + l("No thanks."), -; + close; + +L_Start: + mes ""; + mesn; + mesq l("Great, I need 11 @@. Only good food makes a good crew.",getitemlink("PiouLegs")); + setq ShipQuests_ChefGado, 1; + mes ""; + menu + l("I'll get to work."), -; + close; + + +L_Continue: + setq ShipQuests_ChefGado, 1; + mesn; + mesq l("Do you have the @@/11 @@ I requested? The sailors are starving because of you!",countitem("PiouLegs"),getitemlink("PiouLegs")); + mes ""; + menu + rif(countitem("PiouLegs") >= 11, l("Yes, take them.")), L_Reward, + l("Not yet."), -; + close; + + +L_Reward: + inventoryplace CreasedGloves, 1; + delitem PiouLegs, 11; + getitem CreasedGloves, 1; + setq ShipQuests_ChefGado,2; + Zeny = Zeny + 100; + getexp 25, 5; + mes ""; + mesn; + mesq l("Thanks. Take this spare pair of gloves and some change."); + next; + mesq l("I'm sure the crew will like to be spared from having to eat @@ again!", getitemlink(Piberries)); + close; + end; + +L_Complete: + .@r = rand2(3); + if (.@r == 0) npctalk3 l("What are you doing in my kitchen?! Get out, it's not a place for kids!"); + if (.@r == 1) npctalk3 l("Where is the salt?! This is sugar! Proper sailors need salt, not sugar!"); + if (.@r == 2) npctalk3 l("Are you going to stand here all day long? Go wash the dishes or go away."); + closedialog; + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/002-1/dan.txt b/npc/002-1/dan.txt new file mode 100644 index 0000000..be0af19 --- /dev/null +++ b/npc/002-1/dan.txt @@ -0,0 +1,169 @@ +// TMW-2 Script +// Authors: +// Qwerty Dragon and Reid (originals) +// Jesusalva +// Description: +// Dan is a sailor from Nard ship (TODO another Redy? Seriously?) who is afraid +// of Pious. +// Variables: +// ShipQuests_Dan +// Translation: +// FR Translated + +002-1,32,31,0 script Dan#002-1 NPC_REDY_MAN,{ + .@q=getq(ShipQuests_Dan); + if (.@q == 1) goto L_Report; + if (.@q == 2) goto L_Reward; + if (.@q >= 3) goto L_Finished; + + mesn; + mesq l("You see these pious around us?"); + next; + mesq l("They're scary, don't you think so?"); + next; + menu + rif(getq(ShipQuests_Julia) < 3, l("Do you know where I can find Nard?")), L_Nard, + l("Yeah, they are."), -, + l("I am not afraid of Pious."), L_Quest; + + mes ""; + mesn; + mesq l("I knew someone would agree with me!"); + close; + +L_Quest: + if (BaseLevel < 2) { + mesn; + mesq l("Well, you clearly never fought before, so remember to add stats."); + mesc l("That can be done pressing F2 and opening char screen."); + next; + } + mes ""; + mesn; + mesq l("Then could you perhaps kill 12 @@ for me?!", getmonsterlink(Piou)); + next; + menu + l("They're too strong for me!"), -, + l("Yes, of course. Without a sweat."), L_Continue; + mes ""; + mesn; + mesq l("Oh, please. They're weaker than Maggots. They will only pose a threat if you're unarmed."); + close; + +L_Continue: + mes ""; + mesn; + mesq l("I am counting on you!"); + setq ShipQuests_Dan, 1, 0; + close; + +L_Report: + mesn; + mesq l("You've killed @@/12 @@. Get rid of them!", getq2(ShipQuests_Dan),getmonsterlink(Piou)); + close; + +L_Reward: + mesn; + mesq l("Woohoo, thank you! Maybe now they'll leave me alone!"); + next; + mesn; + mesq l("Here, take this pair of boots as a reward!"); + inventoryplace CreasedBoots, 1; + getitem CreasedBoots, 1; + getexp 25, 5; + setq ShipQuests_Dan, 3, 0; + next; + mesn; + mesq l("Peter was also looking for strong people to help him to kill other monsters."); + if (BaseLevel < 8) { + next; + mesn; + mesq l("You still have a low level but I'm sure he will ask for your help once you grow up a little more."); + } + close; + +L_Finished: + mesn; + mesq l("It is no use... We are too close to a island, they will keep invading the ship..."); + next; + mesn; + mes l("(shivering) \"Ah, how I am afraid of pious!\""); + close; + +L_Nard: + mesn; + mesq l("Oh? Just go east of here. Keep going ##Bright##b, go down the stairs, and you'll be at his office already."); + next; + mesn; + mesq l("And to leave this cramped ship, just go past his office! But beware..."); + next; + mesn; + mesq l("There are Pious on the island as well! %%i"); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +// TODO this code below is bad and should be entirely rewritten +002-1,0,0,0 script PiouSpwn NPC_HIDDEN,{ + function DanCheck { + if (playerattached()) { + // Quest not in progress - nothing to do + if (getq(ShipQuests_Dan) != 1) + return; + + // If you complete, finish it. Otherwise, sum it up. + .@t=getq2(ShipQuests_Dan); + if (.@t+1 >= 12) + setq ShipQuests_Dan, 2, 0; + else + setq2 ShipQuests_Dan, .@t+1; + + // Report progress + dispbottom l("@@/@@", .@t+1, 12); + } + return; + } + +OnFakeKill: + DanCheck(); + end; + +OnRespawnPiou1: + DanCheck(); + sleep 39000; + areamonster(instance_mapname("002-1"), 52, 32, 73, 41, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou1"); + end; +OnRespawnPiou2: + DanCheck(); + sleep 41000; + areamonster(instance_mapname("002-1"), 52, 32, 73, 41, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou2"); + end; +OnRespawnPiou3: + DanCheck(); + sleep 39500; + areamonster(instance_mapname("002-1"), 52, 32, 73, 41, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou3"); + end; +OnRespawnPiou4: + DanCheck(); + sleep 41500; + areamonster(instance_mapname("002-1"), 52, 32, 73, 41, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou4"); + end; +OnRespawnPiou5: + DanCheck(); + sleep 40000; + areamonster(instance_mapname("002-3"), 31, 26, 40, 31, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou5"); + end; + +OnInstanceInit: + areamonster(instance_mapname("002-1"), 52, 32, 73, 41, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou1"); + areamonster(instance_mapname("002-1"), 52, 32, 73, 41, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou2"); + areamonster(instance_mapname("002-1"), 52, 32, 73, 41, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou3"); + areamonster(instance_mapname("002-1"), 52, 32, 73, 41, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou4"); + areamonster(instance_mapname("002-3"), 31, 26, 40, 31, "Piou", Piou, 1, instance_npcname("PiouSpwn")+"::OnRespawnPiou5"); + end; + +} diff --git a/npc/002-1/devis.txt b/npc/002-1/devis.txt new file mode 100644 index 0000000..47fc9a9 --- /dev/null +++ b/npc/002-1/devis.txt @@ -0,0 +1,17 @@ +// Evol scripts. +// Authors: +// Alige +// Reid +// Vasily_Makarov +// Description: +// Sleeping and grumbling NPC. + +002-1,32,38,0 script Devis NPC_HAMMOC,{ + asleep; + close; + +OnInit: + .sex = G_MALE; + .distance = 2; + end; +} diff --git a/npc/002-1/doors.txt b/npc/002-1/doors.txt new file mode 100644 index 0000000..03a2c0e --- /dev/null +++ b/npc/002-1/doors.txt @@ -0,0 +1,17 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Doors NPCs. + +002-1,72,30,0 script AreaNPC#002-1d NPC_HIDDEN,0,0,{ + +OnTouch: + if (LOCATION$ == "") + warp "002-3", 42, 26; + else if (LOCATION$ == "Tulim" || LOCATION$ == "Candor" || LOCATION$ == "Artis") + warp "002-3@"+LOCATION$, 42, 26; + else + LOCATION$=any("Candor", "Tulim"); + end; +} diff --git a/npc/002-1/hammock.txt b/npc/002-1/hammock.txt new file mode 100644 index 0000000..97bea91 --- /dev/null +++ b/npc/002-1/hammock.txt @@ -0,0 +1,73 @@ +// TMW2 Script +// Notes: +// Hammocks 1~5 were deleted during 002-1 ship hold remapping +// Evol scripts. +// Author: +// Reid +// Description: +// Animated hammock at the mid level of the ship. + +002-1,37,34,0 script #hammock6 NPC_RIGHT_HAMMOCK,1,0,{ + +OnTouch: + hamTouchLeft; + +OnUnTouch: + hamUnTouch; + +OnTimer5440: + hamTimerLeft; +} + +002-1,37,36,0 script #hammock7 NPC_RIGHT_HAMMOCK,1,0,{ + +OnTouch: + hamTouchLeft; + +OnUnTouch: + hamUnTouch; + +OnTimer5440: + hamTimerLeft; +} + +002-1,37,38,0 script #hammock8 NPC_RIGHT_HAMMOCK,1,0,{ + +OnTouch: + hamTouchLeft; + +OnUnTouch: + hamUnTouch; + +OnTimer5440: + hamTimerLeft; +} + +002-1,32,40,0 script #hammock9 NPC_LEFT_HAMMOCK,1,0,{ + +OnTouch: + hamTouchLeft; + +OnUnTouch: + hamUnTouch; + +OnTimer5440: + hamTimerLeft; +} + +002-1,37,40,0 script #hammock10 NPC_RIGHT_HAMMOCK,1,0,{ + +OnTouch: + hamTouchLeft; + +OnUnTouch: + hamUnTouch; + +OnTimer5440: + hamTimerLeft; +} + +002-1,58,25,0 duplicate(#hammock9) #hammock1 NPC_LEFT_HAMMOCK,1,0 +002-1,58,27,0 duplicate(#hammock9) #hammock2 NPC_LEFT_HAMMOCK,1,0 +002-1,58,29,0 duplicate(#hammock9) #hammock3 NPC_LEFT_HAMMOCK,1,0 + diff --git a/npc/002-1/juliet.txt b/npc/002-1/juliet.txt new file mode 100644 index 0000000..3ddc8dc --- /dev/null +++ b/npc/002-1/juliet.txt @@ -0,0 +1,171 @@ +// TMW2 scripts. +// Authors: +// 4144 +// Qwerty Dragon +// Vasily_Makarov +// Jesusalva +// Description: +// Allows to change language and talks about what happened to him. +// Modified by Jesusalva for TMW2. She is the nurse and also does other minor tasks. +// Variables: +// 0 ShipQuests_Julia +// Values: +// Julia: +// 0 Default, haven't started the game yet. +// 1 Need to see Julia. +// 2 Has been registered by Julia. +// 3 Has talked with Nard + +002-1,67,26,0 script Juliet NPC_JULIA,2,10,{ + + + function sellFood { + closeclientdialog; + openshop; + close; + return; + } + + + + function basicSkill { + mes ""; + mesn; + mesq l("Let me check into it..."); + next; + adddefaultskills; + mesq l("Here you go, everything is fixed."); + emotion E_HAPPY; + next; + return; + } + + function chooseLang { + mes ""; + mesn; + mesq l("Of course! But beware that %s are always in demand!", "[@@https://www.transifex.com/arctic-games/moubootaur-legends/|"+l("Translators")+"@@]"); + next; + mesq l("Tell me which language you speak and I will change the note on the ship passenger list."); + next; + + asklanguage(LANG_IN_SHIP); + + mes ""; + mesn; + mesq l("Ok, done."); + next; + return; + } + + function whereAmI { + mes ""; + mesn; + mesq l("You're on a ship, we're on our way to the oldest human city, Tulishmar."); + next; + mesq l("We should be there in a few days. For now, you can relax on the ship, or visit the island we're docked at! Its a small island, but a good place to get some exercise and stretch your legs."); + next; + return; + } + + function whatHappened { + mes ""; + mesn; + mesq l("We thought that you could help us understand this, all we know is that we found you cast in the sea, in a sand bank."); + next; + mesq lg("You were in bad shape, you should be happy we found you before the sea killed you."); + next; + return; + } + + function readRules { + mes ""; + mesn; + mesq l("Of course, they are on the left wall, go have a look at them."); + next; + return; + } + + function mainMenu { + do + { + .@q4 = getq(General_Narrator); + + select + l("I am hungry. Can I buy some food here?"), + rif(getskilllv(NV_BASIC) < 6, l("Something is wrong with me, I can't smile nor sit.")), + lg("I made a mistake, I would like to change my language."), + rif(!.@q4, l("Could you explain to me where I am?")), + rif(!.@q4, l("What happened to me?")), + l("Can I read these rules again?"), + l("Nothing, sorry."); + + switch (@menu) { + case 1: sellFood; break; + case 2: basicSkill; break; + case 3: chooseLang .@s$; break; + case 4: whereAmI; break; + case 5: whatHappened; break; + case 6: readRules; break; + case 7: closedialog; end; + } + } while (1); + } + + mesn; + mesq lg("Hello dear!"); + next; + if (getq(ShipQuests_Julia) < 3) + mesq l("Have you already talked to our captain? He should be downstairs waiting for you!"); + mesq l("What do you want today?"); + next; + + mainMenu; + +OnTouch: + .@q = getq(ShipQuests_Julia); + if (.@q > 1) end; + + checkclientversion; + + mesn; + mesq l("Hi, nice to see you!"); + next; + mesq l("My name is Juliet, it is me who took care of you after we found you in the sea. I'm glad to see you're okay."); + next; + if (getq(ShipQuests_Julia) < 2) { + mesq l("I'm sure that you've got some questions for me, feel free to ask them, but first I need to tell you the rules all adventurers must respect on this world."); + next; + + GameRules 8 | 4; + + mesn; + mesq l("Oh, and I almost forgot! Do not share passwords or pincodes, not even with staff! And do not use the same password somewhere else, they can be stolen!"); + next; + mesn; + mesq l("If you want to read this page again, there is a copy up on the wall."); + next; + mesn; + mesq l("Also, take this book so you don't forget the rules. You can always read it, or type ##B@rules##b on the chat."); + // No inventoryplace here. + getitem BookOfLaws, 1; + setq ShipQuests_Julia, 2; + next; + mesq l("I think I'm done with that now. You should now look for captain Nard downstairs. He'll be waiting for you."); + mesq l("Do you have any questions?"); + next; + mesc b(l(".:: Main Quest 1-2 ::.")), 3; + msObjective(false, l("Talk to @@", l("Captain Nard"))); + next; + } + mainMenu; + end; + +OnInit: + .sex = G_FEMALE; + .distance = 10; + sellitem Cheese; + sellitem Aquada; + sellitem Piberries; + sellitem Bread; + end; +} diff --git a/npc/002-1/knife.txt b/npc/002-1/knife.txt new file mode 100644 index 0000000..de0e90b --- /dev/null +++ b/npc/002-1/knife.txt @@ -0,0 +1,49 @@ +// Evol scripts. +// Authors: +// Ablu +// Saulc +// Qwerty Dragon +// Description: +// Knife on the table. +// Variable: +// ShipQuests_Knife +// Values: +// 0 Default, not taken. +// 1 Knife taken. + +002-1,31,28,0 script #knife NPC_KNIVES,{ + .@q = getq(ShipQuests_Knife); + if (.@q) end; + + mesn l("Narrator"); + mesc l("There are some old rusty knives on the table. Would you like to take one?"); + next; + + menu + l("Yeah!"), L_Give, + l("Nah."), -; + + closedialog; + close; + +L_Give: + mes ""; + inventoryplace RustyKnife, 1; + + setq ShipQuests_Knife, 1; + getitem RustyKnife, 1; + + // TODO: replace keys with variables since the player may have remapped them, possibly replace the messages below with tutorial messages? + mesn l("Narrator"); + mesc l("To open your inventory, use the F3 key or use your mouse to select it in the above menu in your client."); + next; + mesc l("When your inventory is open, you can equip an item by selecting it and clicking 'Equip'. You can do the same to unequip an item by clicking on 'Unequip'."); + next; + mesc l("Items have different effects. Some will heal you, some can be used as weapons or armor, and some can be sold for gold."); + + close; + +OnInit: + .distance = 2; + end; +} diff --git a/npc/002-1/mapflags.txt b/npc/002-1/mapflags.txt new file mode 100644 index 0000000..c9d8f8d --- /dev/null +++ b/npc/002-1/mapflags.txt @@ -0,0 +1 @@ +002-1 mapflag town diff --git a/npc/002-1/peter.txt b/npc/002-1/peter.txt new file mode 100644 index 0000000..a5058de --- /dev/null +++ b/npc/002-1/peter.txt @@ -0,0 +1,307 @@ +// TMW2 script +// Author: +// Jesusalva +// Original evol script authors: 4144, Ablu, Alastrim, Qwerty Dragon, Reid, Vasily_Makarov +// Description: +// Rat hunter. +// Variable: +// ShipQuests_Peter +// Values: +// 0 Doesn't know the quest. +// & 1 Task given. +// & 2 Task completed (easy level). +// & 4 Task completed (medium level). +// & 8 Task completed (hard level). +// Setq2: +// Number of killed Rattos: +// & 1 - Ratto 1 +// & 2 - Ratto 2 +// & 4 - Ratto 3 +// & 8 - Ratto 4 +// = 15: All rattos killed +// Setq3: +// Instance ID (so we can destroy it later) +// +// Others: +// @peter = Accepted Task ID +// @pt_mob = ID of the monster you were tasked with killing + +// FIXME: ugly workaround, causes lots of bugs (specially instancing) +002-1,35,24,0 script AreaNPC#Peter NPC_HIDDEN,0,1,{ + end; +OnTouch: + doevent "Peter::OnPeterMain"; + close; +} + +002-1,33,25,0 script Peter NPC_RATTO_SAILOR,{ + goto L_Main; + +OnPeterMain: +L_Main: + .@q = getq(ShipQuests_Peter); + .@q2 = getq2(ShipQuests_Peter); + .@q3 = getq3(ShipQuests_Peter); + if (BaseLevel < 8) goto OnTooWeak; + + if (!.@q || !isinstance(.@q3) || .@q3 <= 0) goto L_Task; + if (instanceowner(.@q3) != getcharid(3)) goto L_Task; + if (.@q2 < 15) goto L_ReturnFail; + dispbottom l("I am broken?! Please report! Debug data: @@ (@@)", .@q, .@q2); + close; + +OnGiveTask: +L_Task: + if (!.@q) + setq ShipQuests_Peter, 1, 0, 0; + mesn; + mesq lg("Hey, girl!", "Hey, man!"); + next; + mesq l("I need somebody who can rid the hold of the ship of these creatures. Can you help me?"); + next; + + menu + l("Yeah, but what reward will I get?"), L_BonusTask, + l("No, they are way too dangerous for me!"), -; + + mes ""; + mesn; + mesq l("Hehe, hehe. Well, come back if you change your mind."); + + close; + +OnLowTime: + if ((compare(getmap(), "002-2")) || (compare(getmap(), "nard"))) + dispbottom l("Time is running out... Hurry up!"); + end; + +OnTooWeak: + mesn; + mesq lg("I need someone to help me clean the edge of the ship, but you aren't strong enough for now."); + close; + +OnStop: + slide 35, 26; + + mesn; + mesq l("You can't go there!"); + + close; + +OnReturnFail: +L_ReturnFail: + .@q3 = getq3(ShipQuests_Peter); + //instance_destroy(.@q3); + setq2 ShipQuests_Peter, 0; + setq3 ShipQuests_Peter, 0; + deltimer("RattosControl::OnRatto1Respawn"); + deltimer("RattosControl::OnRatto2Respawn"); + deltimer("RattosControl::OnRatto3Respawn"); + deltimer("RattosControl::OnRatto4Respawn"); + mesn; + mesq l("I see it's not so easy to get rid of the monsters. Do you want to try again?"); + next; + + menu + l("Yeah, but I would like to make sure I get a reward."), L_BonusTask, + l("No, they are way too dangerous for me!"), -; + + mes ""; + mesn; + mesq l("Hehe, hehe. Well, come back if you change your mind."); + + close; + +L_BonusTask: + mes ""; + mesn; + mesq l("There are three kind of monsters which frequently attacks our fair vessel."); + mesc l("You need to kill all the %d monsters to complete a bounty.", 4); + next; + mesn; + .@q = getq(ShipQuests_Peter); + if (!(.@q & 2)) { + mes l("- I currently need your help with @@.", getmonsterlink(Squirrel)); // 750 HP, don't attack, 110 ms + mes l("I'll give you @@ GP for this job.", 350); + mes ""; + } + if (!(.@q & 4)) { + mes l("- I currently need your help with @@.", getmonsterlink(Ratto)); // 500 HP, 80 DMG, 1572 dps, 120 ms + mes l("I'll give you @@ GP for this job.", 550); + mes ""; + } + if (!(.@q & 8)) { + mes l("- I currently need your help with @@.", getmonsterlink(Croc)); // 1900 HP, 145 DMG, 1872 dps, 600 ms + mes l("I'll give you @@ GP for this job.", 1000); + mes ""; + } + // TODO: This could be done a daily quest + if (.@q == 15) { + mes l("- I currently need your help with @@, but there's no reward.", getmonsterlink(Ratto)); + } + next; + + select + l("I'm not feeling like it today... Sorry."), + rif(!(.@q & 2), l("I will take the @@ Bounty.", "Squirrel")), + rif(!(.@q & 4), l("I will take the @@ Bounty.", "Ratto")), + rif(!(.@q & 8), l("I will take the @@ Bounty.", "Croc")), + rif(.@q == 15, l("Why not, I need to train anyway.")); + + if (@menu == 1) + close; + + @peter=@menu; + + goto L_Start; + + +L_Start: + mes ""; + mesc l("Some of them are pretty strong. Do you need an explanation about hit'n'run and the monster you're about to face?"); + if (askyesno() == ASK_YES) { + mes ""; + mesc l(".:: Hit'n'Run Tactic ::."); + mesc l("Sometimes, you just cannot afford to be hit. But even with a melee weapon, you don't need to be hit."); + next; + mesc l("The strategy is simple. When you hit the enemy, walk one or two tiles backwards."); + mesc l("If you miss, walk two or three tiles backwards. This way, you avoid being hit."); + next; + if (@peter == 2) + mesc l("Squirrels are healthy, but they never attack. They run away from you, so good luck catching it!"); + else if (@peter == 4) + mesc l("Crocs are dangerous and very healthy, but very slow. Use that on your advantage."); + else + mesc l("Rattos are very fast. They walk fast and attack fast. I advise bringing some healing items!"); + next; + } else { + mes ""; + } + +// Init Instance +OnStartOutside: + .@ID=getcharid(0); + @MAP_NAME$="nard@"+str(.@ID); // Max 4 chars for map name + .@INSTID = instance_create("002-2@a"+(.@ID), getcharid(3), IOT_CHAR); + if (.@INSTID < 0) + .@instanceMapName$ = ""; + else + .@instanceMapName$ = instance_attachmap("002-2", .@INSTID, 0, @MAP_NAME$); + + // Instance already exists, or something went wrong + if (.@instanceMapName$ == "") { + mesn; + mesq l("Actually, you just took a bounty, right?"); + next; + mesn; + mesq l("Why don't you take a break? Breath in some fresh air. The basement is pretty damp."); + close; + } + + setq2 ShipQuests_Peter, 0; + setq3 ShipQuests_Peter, .@INSTID; + + // It'll be self-destroyed when time runs out (3 minutes) + instance_set_timeout(180, 180, .@INSTID); + instance_init(.@INSTID); + + // Save in a less reliable way the challenge you took + if (@peter == 2) { + @peter=2; + @pt_mob=Squirrel; + } else if (@peter == 3) { + @peter=4; + @pt_mob=Ratto; + } else if (@peter == 4) { + @peter=8; + @pt_mob=Croc; + } else { + @peter=0; + @pt_mob=Ratto; + } + + warp @MAP_NAME$, 33, 24; + addtimer(120000, "Peter::OnLowTime"); + addtimer(140000, "Peter::OnTimeout"); + + // Spawn the Monsters + areamonster @MAP_NAME$, 23, 19, 57, 40, strmobinfo(1, @pt_mob), @pt_mob, 1, "RattosControl::OnRatto1Death"; + areamonster @MAP_NAME$, 23, 19, 57, 40, strmobinfo(1, @pt_mob), @pt_mob, 1, "RattosControl::OnRatto2Death"; + areamonster @MAP_NAME$, 23, 19, 57, 40, strmobinfo(1, @pt_mob), @pt_mob, 1, "RattosControl::OnRatto3Death"; + areamonster @MAP_NAME$, 23, 19, 57, 40, strmobinfo(1, @pt_mob), @pt_mob, 1, "RattosControl::OnRatto4Death"; + + dispbottom l("Okay, you can start!"); + closeclientdialog; + close; + +OnTimeout: + if (!(compare(getmap(), "002-2")) && !(compare(getmap(), "nard"))) + end; + warp "002-1@"+LOCATION$, 35, 26; + .@q3 = getq3(ShipQuests_Peter); + //instance_destroy(.@q3); + setq2 ShipQuests_Peter, 0; + setq3 ShipQuests_Peter, 0; + deltimer("RattosControl::OnRatto1Respawn"); + deltimer("RattosControl::OnRatto2Respawn"); + deltimer("RattosControl::OnRatto3Respawn"); + deltimer("RattosControl::OnRatto4Respawn"); + mesn; + mesq l("Hey! Be careful. You can't stay in this basement for so long, you're going to get sick. Come outside and take a break, maybe you can try again later."); + close; + +OnDone: + .@q3 = getq3(ShipQuests_Peter); + //instance_destroy(.@q3); + setq ShipQuests_Peter, getq(ShipQuests_Peter)|@peter, 0, 0; + + .@q = getq(ShipQuests_Peter); + if (@peter) { + mesn; + mesq l("Good job!") + " " + l("Here's your reward!"); + getexp @peter*52, @peter; + switch (@peter) { + case 2: @peter=350; break; + case 4: @peter=550; break; + case 8: @peter=1000; break; + } + + Zeny = Zeny + @peter; + message strcharinfo(0), l("You receive @@ GP!", @peter); + } else { + mesn; + mesq l("Good job!") + " " + l("Thanks for helping!"); + getexp 0, (JobLevel > 6 ? (15+JobLevel) : 4); + PETER_REPEAT += 1; + + // Every 3 repeats (10 minutes) gives you 50 GP + if (PETER_REPEAT % 3 == 0) { + Zeny += 50; + mesq l("It is not much, but here is %d GP. Should buy you a snack!", 50); + } + + // Special bonus (but unlike Kreist, this one is decorative only) + // (Takes about 6 hours non-stop to obtain) + if (PETER_REPEAT == 100) { + mesq l("I'm moved to tears by your dedication to help us. Unrewarded, even. Here, have this rare %s. For free!", getitemlink(Cap)); + getitem Cap, 1; + } + } + deltimer("Peter::OnLowTime"); + deltimer("Peter::OnTimeout"); + @peter=0; + if (!getq(ShipQuests_Dan)) { + next; + mesn; + mesq l("You're strong, maybe you could help Dan. He is a good sailor but he is too afraid from harmless Pious."); + next; + mesn; + mesq l("Look at that pathetic scene, he just put his chair over the desk... Chef Gado won't like it."); + } + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/002-2/_import.txt b/npc/002-2/_import.txt new file mode 100644 index 0000000..5b662cf --- /dev/null +++ b/npc/002-2/_import.txt @@ -0,0 +1,5 @@ +// Map 002-2: Hold +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/002-2/doors.txt", +"npc/002-2/mapflags.txt", +"npc/002-2/ratto.txt", diff --git a/npc/002-2/doors.txt b/npc/002-2/doors.txt new file mode 100644 index 0000000..8986865 --- /dev/null +++ b/npc/002-2/doors.txt @@ -0,0 +1,49 @@ +// TMW2 Script +// Authors: +// Jesusalva +// +// Originally an Evol script authored by: +// Ablu, Alastrim and Reid +// +// Description: +// Doors NPCs. + +002-2,33,23,0 script 0022#DoorUpwards NPC_HIDDEN,0,0,{ + +OnTouch: + if (mobcount("002-2","all") > 0) goto L_Warn; + + goto L_Warp; + +L_Warn: + .@q = getq(ShipQuests_Peter); + if (.@q >= 15) goto L_Warp; + + mesn "Narrator"; + mesc l("There are still some monsters left! Do you want to abort the quest?"); + next; + + if (askyesno() == ASK_YES) + goto L_Warp; + + slide 33, 25; + closeclientdialog; + close; + +L_Warp: + if (LOCATION$ == "") + warp "002-1", 35, 26; + else + warp "002-1@"+LOCATION$, 35, 26; + + deltimer("Peter::OnLowTime"); + deltimer("Peter::OnTimeout"); + closeclientdialog; + close; +} + +002-2,43,25,0 script #Alige0022 NPC_ALIGE_BARREL,{ + npctalkonce l("This barrel seems suspicious..."); + end; +} + diff --git a/npc/002-2/mapflags.txt b/npc/002-2/mapflags.txt new file mode 100644 index 0000000..4effdfe --- /dev/null +++ b/npc/002-2/mapflags.txt @@ -0,0 +1,2 @@ +002-2 mapflag nosave 000-1,22,22 +002-2 mapflag nopenalty diff --git a/npc/002-2/ratto.txt b/npc/002-2/ratto.txt new file mode 100644 index 0000000..1c09212 --- /dev/null +++ b/npc/002-2/ratto.txt @@ -0,0 +1,80 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Originally an Evol script authored by: Ablu, Alastrim, Reid +// Description: +// Ratto killer. + +002-2,0,0,0 script RattosControl NPC_HIDDEN,{ + end; + +OnRatto1Respawn: + .@q2=getq2(ShipQuests_Peter); + setq2 ShipQuests_Peter, .@q2^1; + areamonster @MAP_NAME$, 23, 19, 57, 40, strmobinfo(1, @pt_mob), @pt_mob, 1, "RattosControl::OnRatto1Death"; + end; + +OnRatto2Respawn: + .@q2=getq2(ShipQuests_Peter); + setq2 ShipQuests_Peter, .@q2^2; + areamonster @MAP_NAME$, 23, 19, 57, 40, strmobinfo(1, @pt_mob), @pt_mob, 1, "RattosControl::OnRatto2Death"; + end; + +OnRatto3Respawn: + .@q2=getq2(ShipQuests_Peter); + setq2 ShipQuests_Peter, .@q2^4; + areamonster @MAP_NAME$, 23, 19, 57, 40, strmobinfo(1, @pt_mob), @pt_mob, 1, "RattosControl::OnRatto3Death"; + end; + +OnRatto4Respawn: + .@q2=getq2(ShipQuests_Peter); + setq2 ShipQuests_Peter, .@q2^8; + areamonster @MAP_NAME$, 23, 19, 57, 40, strmobinfo(1, @pt_mob), @pt_mob, 1, "RattosControl::OnRatto4Death"; + end; + +OnRatto1Death: + .@q2=getq2(ShipQuests_Peter); + setq2 ShipQuests_Peter, .@q2|1; + .@q2=getq2(ShipQuests_Peter); + if (.@q2 == 15) + goto L_Victor; + addtimer(85000, "RattosControl::OnRatto1Respawn"); + end; + +OnRatto2Death: + .@q2=getq2(ShipQuests_Peter); + setq2 ShipQuests_Peter, .@q2|2; + .@q2=getq2(ShipQuests_Peter); + if (.@q2 == 15) + goto L_Victor; + addtimer(85000, "RattosControl::OnRatto2Respawn"); + end; + +OnRatto3Death: + .@q2=getq2(ShipQuests_Peter); + setq2 ShipQuests_Peter, .@q2|4; + .@q2=getq2(ShipQuests_Peter); + if (.@q2 == 15) + goto L_Victor; + addtimer(85000, "RattosControl::OnRatto3Respawn"); + end; + +OnRatto4Death: + .@q2=getq2(ShipQuests_Peter); + setq2 ShipQuests_Peter, .@q2|8; + .@q2=getq2(ShipQuests_Peter); + if (.@q2 == 15) + goto L_Victor; + addtimer(85000, "RattosControl::OnRatto4Respawn"); + end; + +L_Victor: + unitskilluseid(getcharid(3), BS_GREED, 1, getcharid(3)); // Auto-Looting + warp "002-1@"+LOCATION$, 35, 26; + deltimer("RattosControl::OnRatto1Respawn"); + deltimer("RattosControl::OnRatto2Respawn"); + deltimer("RattosControl::OnRatto3Respawn"); + deltimer("RattosControl::OnRatto4Respawn"); + doevent("Peter::OnDone"); + end; +} diff --git a/npc/002-3/_import.txt b/npc/002-3/_import.txt new file mode 100644 index 0000000..6d7316f --- /dev/null +++ b/npc/002-3/_import.txt @@ -0,0 +1,8 @@ +// Map 002-3: First Deck +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/002-3/_mobs.txt", +"npc/002-3/doors.txt", +"npc/002-3/elmo.txt", +"npc/002-3/mapflags.txt", +"npc/002-3/nard.txt", +"npc/002-3/note.txt", diff --git a/npc/002-3/_mobs.txt b/npc/002-3/_mobs.txt new file mode 100644 index 0000000..9e5e45b --- /dev/null +++ b/npc/002-3/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 002-3: First Deck mobs +002-3,35,28,4,2 monster Piou 1002,1,30000,20000,PiouSpwn::OnFakeKill diff --git a/npc/002-3/doors.txt b/npc/002-3/doors.txt new file mode 100644 index 0000000..e762b11 --- /dev/null +++ b/npc/002-3/doors.txt @@ -0,0 +1,83 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Nard's ship Doors NPCs. + +002-3,30,28,0 script AreaNPC#doors4 NPC_HIDDEN,0,0,{ + +OnTouch: + if (!getq(General_Narrator) && getq(ShipQuests_Julia) < 3) { + showavatar NPC_NARD; + setcamnpc "Nard"; + mesn l("Nard"); + mesq l("Where do you think you are going, without talking to me first?"); + next; + mesc l("Talk to Nard to unlock the door."); + next; + restorecam; + close; + } + if (getvaultid() && !(##02_MLQUEST & MLP_ML_NARD)) { + mesc l("Alright. A new world. What mysteries await for me behind that wooden door?"); + next; + mesc l("Visiting a new world is always exciting. Actually, the opportunity of taking a new life in a new universe isn't that bad either."); + next; + mesc l("That's why we must stop... THEM... at any costs. I don't know what they're plotting, but it is not in this world inhabitants best interests... nor any other world."); + next; + mesc l("Anyway... Let's see how this world works!"); + next; + ##02_MLQUEST = ##02_MLQUEST | MLP_ML_NARD; + closeclientdialog; + } + + if (LOCATION$ == "Candor") { + warp "005-1", 49, 117; + close; + } + + if (LOCATION$ == "Artis") { + warp "029-0", 203, 85; + close; + } + + if (LOCATION$ == "Tulim") { + if ($@MK_SCENE == MK_SIEGE_TULIM && BaseLevel < 30) { + showavatar NPC_NARD; + setcamnpc "Nard"; + mesn l("Nard"); + mesc l("The Monster Army is currently sieging Tulimshar. There are hundreds of dangerous monsters out there right now."), 1; + next; + mesn l("Nard"); + mesc l("I would advise you to remain here in the ship, they should leave in a few minutes, but I will not force you."), 1; + next; + mesn l("Nard"); + mesc l("Just beware that if you leave, you might get killed really quickly. No death penalty, though."), 1; + next; + mesc l("Leave the ship? The town have no death penalty and is under a global event."), 1; + if (askyesno() == ASK_NO) + close; + // Boost their evade and HP so they don't get instantly KO'ed (5 min) + sc_start SC_INCFLEE, 300000, 200; + sc_start SC_INCMHP, 300000, 1000; + heal 1000, 0; + dispbottom l("Queen Of Dragons: It is dangerous to go out alone, I'll raise your evasion and life!"); // QoD = “Demure”. + } + warp "003-1", 81, 68; + close; + } + Exception("Script error: 002-3 door"); + close; + + +} + +002-3,42,25,0 script AreaNPC#002-3d NPC_HIDDEN,0,0,{ + +OnTouch: + if (LOCATION$ == "") + warp "002-1", 72, 29; + else + warp "002-1@"+LOCATION$, 72, 29; + close; +} diff --git a/npc/002-3/elmo.txt b/npc/002-3/elmo.txt new file mode 100644 index 0000000..03a5f8c --- /dev/null +++ b/npc/002-3/elmo.txt @@ -0,0 +1,163 @@ +// TMW2 Script. +// Authors: +// Jesusalva +// Description: +// Elmo's second dialog. He is Nard's deputy and second-in-command. +// Elmo was created in Evol by Qwerty Dragon and Reid +// TODO: allows smart noobs (<15) get EXP Bonus (20%) + +002-3,32,24,0 script Elmo NPC_ELMO,{ + function ExpBoost; + showavatar NPC_ELMO; // this is handled by avatars.xml + + // Core functions + if (BaseLevel < 20) + ExpBoost(); + + if (getq(ShipQuests_Julia) < 3) goto L_NotYet; + if (getq(CandorQuest_Sailors) == 2) goto L_Party; + if (LOCATION$ == "Candor" && rand(1,7) != 5) goto L_Candor; + + // TODO: NPC looking at task schedule and stuff + mesn; + if (rand(1,2) == 1) + mesq l("Ah, @@ seems to be behind the @@ schedule again...", any("Juliet", "Billy Bons", "Chef Gado", "Dan", "Devis", "Peter"), any(l("cleaning"), l("stocking"))); + else + mesq l("Uhm, @@ seems to have finished their scheduled tasks again... If they were so diligent in cleaning and stocking, though...", any("Nard", "Elmo", "Juliet", "Chef Gado", "Dan", "Peter")); + + close; + +L_NotYet: + mesn; + mes l("I'm not the Captain, Nard is."); + mes l("You should talk to him instead."); + close; + +L_Candor: + mesn; + if (getq(General_Narrator) < 1) mes l("\"Hey, have you already got the money necessary for the travel?"); + if (getq(General_Narrator) < 1) mes l("If you haven't, maybe there are a few things you can do besides selling items.\""); + if (getq(General_Narrator) >= 1) mesq l("Maybe there are things in Candor which still require your attention? I might have overheard some of them."); + if (reputation("Candor") >= 100) mes l("...Although that's unlikely, I admit."); + next; + + mes ""; + + // Valon Quest + .@q=getq(CandorQuest_Trainer); + if (.@q < 1) { + mesc l("##BFirst and foremost, you should talk to Trainer, inside the big house.##b"), 1; + mes l("Besides being able to train you, he is a walking encyclopedia - Ask him anything you are unsure about!"); + next; + mes l("To find him, just leave the ship and turn left. You should also touch the Soul Menhir when you leave this ship."); + mes l("The Soul Menhir will attach your soul, so when you die, you'll appear where you last touched it."); + close; + } else if (.@q < 12) { + mes l("- Inside the big house is someone who can train you. All experience is handy!"); + } + + // Barrel Quest + .@q=getq(CandorQuest_Barrel); + if (.@q < 4) + mes l("- I think you can help the storehouse for some quick cash."); + + // Kids Quest + .@q=getq(CandorQuest_HAS); + if (.@q < 4) + mes l("- You can always play with kids. Not very profitable, though."); + + // Sailors Quest + .@q=getq(CandorQuest_Sailors); + if (.@q < 3) + mes l("- Some of our crew are missing. They're probably wasting their time at beach."); + + // Vincent Quest + .@q=getq(CandorQuest_Vincent); + if (.@q < 2) + mes l("- I overheard rumors about a festival. Maybe someone needs help with their figurine?"); + + // Tolchi Quest + .@q=getq(CandorQuest_Tolchi); + if (.@q < 2) + mes l("- The weapon master, Tolchi, could use your help. But she will most likely force you to visit Tulimshar in the end."); + + // Maya Quest + .@q=getq(CandorQuest_Maya); + if (.@q < 4) + mes l("- There is a woman walking on the island, called Maya. Once she realises you're willing to help, she'll start paying well."); + + // Rosen Quest + .@q=getq(CandorQuest_Rosen); + if (.@q < 3) + mes l("- The weapon seller, Rosen, wanted to help new players to improve their equipment."); + + // Nylo Quest + .@q=getq(CandorQuest_Marggo); + if (.@q < 1) + mes l("- The farmer Nylo, who loves beer and money, seems to be having troubles with his crops."); + + // Ship Quests + .@q1=getq(ShipQuests_Dan); + .@q2=getq(ShipQuests_ChefGado); + .@q3=getq(ShipQuests_Peter); + if (.@q1 < 3 || .@q2 < 2 || .@q3 < 7) + mes l("- Some sailors within this ship may need your help: Chef Gado, Dan, Peter... help them all and collect rewards!"); + + // Report in an abstract way to the player how good they are at getting travel + // discounts, and how much work is left to do. Some points are easy/required to get (eg. Dan, Peter, HAS, etc.) + next; + closeclientdialog; + .@n=nard_reputation(); + if (.@n >= 15) + npctalk3 l("Nard is truly amazed at you. I am impressed, too."); + else if (.@n >= 13) + npctalk3 l("Nard is amazed at you."); + else if (.@n >= 11) + npctalk3 l("Nard is very impressed, you're really a hard worker. Congrats!"); + else if (.@n >= 9) + npctalk3 l("Nard is impressed, you're a hard worker."); + else if (.@n >= 7) + npctalk3 l("Nard noticed your hard work."); + else if (.@n >= 5) + npctalk3 l("Nard likes people who work hard. Work harder!"); + else if (.@n >= 3) + npctalk3 l("You really should do some tasks to impress our captain."); + else + npctalk3 l("Nard doesn't like people who gets money without working for it."); + + end; + +L_Party: + mesn; + mesq l("What? A party?"); + next; + setq CandorQuest_Sailors, 3; + getexp 25, 0; + Zeny = (Zeny + 1000); + mesq l("Alright, I'll show up later. Thanks for calling me. Here's 1000 GP for your efforts."); // With this, the final cost is 50 GP + close; + +function ExpBoost { + mesn; + mesq l("Hey there, @@! I see you are still a noob!", strcharinfo(0)); + next; + mesn; + mesq l("Well, I'll give you a hour of EXP RATE UP! How cool is that? Enjoy!"); + mesc l("This boost can be used until level 20."), 9; + next; + // Get the average level of top players to calculate EXP Boost + // Level 100 ("max") = 25% EXP BOOST (max) + // Current (2019-04-27) top is 80/80/75, meaning a 19% EXP Boost. + // New rules which give 2x exp boost + // Current (2019-08-31) top is 85/84/83, meaning a 42% EXP/DROP Boost. + .@BONUS=NewcomerEXPDROPUP(); + specialeffect FX_SPECIAL, SELF, getcharid(3); + mesc l("EXP Gain raised in @@% for one hour!", .@BONUS), 2; + return; +} + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/002-3/mapflags.txt b/npc/002-3/mapflags.txt new file mode 100644 index 0000000..e274aa7 --- /dev/null +++ b/npc/002-3/mapflags.txt @@ -0,0 +1 @@ +002-3 mapflag nosave 000-1,22,22 diff --git a/npc/002-3/nard.txt b/npc/002-3/nard.txt new file mode 100644 index 0000000..56a6274 --- /dev/null +++ b/npc/002-3/nard.txt @@ -0,0 +1,407 @@ +// TMW2 scripts. +// Authors: +// Qwerty Dragon +// Reid +// Jesusalva +// Description: +// Captain Nard dialogs. +// Nard is a fair merchant ship's captain. +// Original Nard's from Evol by Qwerty Dragon and Reid + +002-3,36,25,0 script Nard NPC_NARD,{ + showavatar NPC_NARD; // this is handled by avatars.xml + + .@narrator = getq(General_Narrator); + +L_Checker: + if (.@narrator) goto L_Travel; + if (getq(ShipQuests_Julia) >= 3) goto L_NotYet; + if (debug) goto L_TestServer; + // Introduction + mesn; + mesq l("Hello."); + next; + mesq l("Let me introduce myself, I am Nard, captain of this ship."); + next; + mesq lg("I am pleased to see that you have woken up and are active. Elmo came here to tell me this good news!"); + next; + mesq l("So, how do you feel? I see that Juliet did a marvellous job! You look like you're in good health now."); + next; + + select + lg("I feel ok."), + l("Who's this Juliet?"), + lg("I'm a bit sick..."); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("Good to know."); + next; + break; + case 2: + mesn; + mesq lg("You have an awful case of amnesia. She is the nurse and shipkeeper of this ship, and took care of you when you were unconscious."); + next; + break; + case 3: + mesn; + mesq l("Well, you'll need to get used to. Being seasick is annoying, so you might want to leave the ship as soon as possible."); + next; + break; + } + +L_Main: + mesn; + mesq l("We have made a stop at a little island, before making it on to the port of Tulimshar."); + next; + mesn; + mesq l("Ship travels are not free. Also, I have a few friends on the Island, and I would like you to check out on them."); + next; + showavatar NPC_ELMO; + setcamnpc instance_npcname("Elmo"); + mesn l("Elmo"); + mesq l("I, Elmo, captain's deputy, will help you to make the maximum possible money in Candor!"); + mesc l("Elmo has given you an EXP UP and DROP UP Boost until level 20!"), 2; + mesc l("It also expires after two hours. In such case, talk to him again!"), 2; + // Actually, why don't we apply it right now...? + .@BONUS=NewcomerEXPDROPUP(); + specialeffect FX_SPECIAL, SELF, getcharid(3); + mesc l("EXP Gain raised in @@% for one hour!", .@BONUS), 2; + next; + showavatar NPC_NARD; + setcamnpc; + mesn; + mesq l("After that, we're going to Tulimshar. Tulim is the most important city on the world, and the Alliance have an office there."); + next; + mesn; + mesq l("The Alliance can help you in finding out about who you are, why you are here, or from where you came from. So, about the tasks I want completed."); + LOCATION$ = "Candor"; + setq ShipQuests_Julia, 3; + // Event handling + if ($EVENT$ == "Event") + getitem MercCard_EH, 1; + // Welcome handling + if (!#REG_DATE) { + $@WELCOME_TIMER=gettimetick(2)+900; // 15 minutes + kamibroadcast("Hey, look, I've rescued \""+strcharinfo(0)+"\" from the sea! Who will @welcome them?!", "Nard"); + #REG_DATE=gettimetick(2); + } + next; + .@price=800; + mesc b(l(".:: Main Quest 1-3 ::.")), 3; + msObjective(getq(CandorQuest_HAS) >= 4, l("* Help Ayasha to take care of the kids.")); + msObjective(getq(CandorQuest_Trainer) >= 12, l("* Get trained by Valon, in the big house.")); + msObjective(getq(CandorQuest_Barrel) >= 4, l("* Ask Zegas, the mayoress, if she needs help.")); + msObjective(Zeny >= .@price, l("* Collect @@/@@ GP", Zeny, .@price)); + +L_Referral: + // Welcome handling + if (!#REG_DATE) { + $@WELCOME_TIMER=gettimetick(2)+600; // 10 minutes + kamibroadcast("Hey, look, I've rescued \""+strcharinfo(0)+"\" from the sea! Who will @welcome them?!", "Nard"); + #REG_DATE=gettimetick(2); + } + // Referral program + if (#REFERRAL_PROG == 0 && $REFERRAL_ENABLED) { + next; + clear; + showavatar NPC_LOF_RICH; + mesc l("But before, a message from our developers!"), 3; + next; + mesn l("TMW2 Staff"); + mesc l("Hello, and welcome to TMW2: Moubootaur Legends!"), 3; + next; + mesn l("TMW2 Staff"); + mesc l("Did you came here by someone advise? If yes, write their name down here!"), 3; + next; + mesc l("If this is not the case, just click on \"Send\"."), 3; + .@ref$=""; + do + { + input .@ref$; + //debugmes "Player invite: "+.@ref$; + mes ""; + if (.@ref$ != "") { + .@ref=gf_accid(strip(.@ref$)); + if (.@ref > 0) { + if (.@ref == getcharid(3)) { + mesn l("TMW2 Staff"); + mesc l("Hahah, silly, that's yourself!"), 3; + mesc l("Try again!"), 3; + next; + .@ref$=""; + } else { + #REFERRAL_PROG=.@ref; + getitembound FriendGift, 1, 1; + mesn l("TMW2 Staff"); + mesc l("Well, welcome to the game! If you have any doubt, shout on #world for help!"), 3; + mesc l("Your friend also sent you a gift - open it when you get level 5!"), 3; + next; + } + } else { + mesn l("TMW2 Staff"); + mesc l("Oops, there is nobody known as @@ on this game.", .@ref$), 3; + mesc l("Could you try again? There could be a typo!"), 3; + next; + .@ref$=""; + } + } else { + .@ref$="None"; + mesn l("TMW2 Staff"); + mesc l("I see. Well, welcome to the game! If you have any doubt, shout on #world for help!"), 3; + next; + } + } while (.@ref$ == ""); + showavatar NPC_NARD; + } + + close; + + +L_NeedHelp: + mes ""; + mesn; + mesq l("You're pretty much stranded on this forsaken island if you don't help me!"); + next; + mesq l("Also, I believe hard work always pay off."); + next; + goto L_NotYet; + +L_CandorIsland: + mes ""; + mesn; + mesq l("I come here frequently to trade. It is not deserted nor boring."); + next; + mesq l("This is ##BCandor Island##b. A very small rich community lives here."); + next; + mesq l("If they were any bigger, monsters would come and kill everyone."); + next; + goto L_NotYet; + +L_NotYet: + .@price=800; + mesc b(l(".:: Main Quest 1-1 ::.")), 3; + msObjective(getq(CandorQuest_HAS) >= 4, l("* Help Ayasha to take care of the kids.")); + msObjective(getq(CandorQuest_Trainer) >= 12, l("* Get trained by Valon, in the big house.")); + msObjective(getq(CandorQuest_Barrel) >= 4, l("* Ask Zegas, the mayoress, if she needs help.")); + msObjective(Zeny >= .@price, l("* Collect @@/@@ GP", Zeny, .@price)); + mes ""; + select + rif(Zeny >= .@price, l("I've brought the money you've asked for.")), + rif(#REFERRAL_PROG == 0 && $REFERRAL_ENABLED && BaseLevel <= 10, + l("I forgot to say earlier, but indeed, I was invited by someone!")), + l("Captain, why have you brought me to a deserted boring island?!"), + l("I don't want to help your \"friends\", bring me to somewhere useful!"), + l("Please excuse me, captain."); + + mes ""; + if (@menu == 2) + goto L_Referral; + if (@menu == 3) + goto L_CandorIsland; + if (@menu == 4) + goto L_NeedHelp; + if (@menu == 5) + close; + + mesn; + if (TUTORIAL && (getq(CandorQuest_HAS) < 4 || + getq(CandorQuest_Barrel) < 4 || + getq(CandorQuest_Trainer) < 12)) { + mesq l("You didn't help all my friends yet, and without trainment, I can't send you to such dangerous place as Tulimshar."); + mesc l("NOTE: It is possible to play the game as a crafter/merchant/fisherman, avoiding to kill as much as possible. However, it is not possible to play the game with a total kill count of zero."); + close; + } + // If you did less than 50% Candor quests, please be warned. + if (reputation("Candor") < 50) { + mesc l("WARNING: You have done less than 50% of Candor Quests!"), 1; + mesc l("It may be expensive to return here. Are you sure?"), 1; + next; + if (askyesno() == ASK_NO) + close; + } + if (Zeny >= .@price) { + inventoryplace TulimMap, 1; + mesq l("Ten, fifty, thousand... Yep, this is the amount I've asked for."); + next; + getvaultexp(10); + setq General_Narrator, 1; + // Double sure + setq ShipQuests_Julia, 3; + Zeny = Zeny-.@price; + EnterTown("Tulim"); + getitem TulimMap, 1; + mesq l("Set sail! We're going to Tulimshar!"); + next; + PC_DEST$="Tulim"; + addtimer nard_time(PC_DEST$)+800, .name$+"::OnNardStage"; + addtimer nard_time(PC_DEST$), "#NardShip::OnEvent"; + @timer_navio_running = 1; + warp "002-5", 39, 26; + closeclientdialog; + end; + } else { + mesq l("You still haven't completed your tasks."); + mes ""; + mesc l("You still need @@ GP for the trip to Tulimshar.", (.@price-Zeny)); + } + close; + +L_Travel: + // Current nard_reputation() value for 100% discount: 15 + .@price=800; + .@price-=min(780, nard_reputation()*54); + + mesn; + mesq l("Hi @@.", strcharinfo(0)); + next; + mesq l("You are currently at @@.", LOCATION$); + mes ""; + mes l("A ship travel will cost you @@ GP.", .@price); + if (ST_TIER == 8) + mesc l("What are you doing? Go talk to @@ and bring me a @@!", b("Elanore"), getitemlink(Lifestone)); + + if (Zeny >= .@price || ((ST_TIER == 7 || ST_TIER == 9) && gettimetick(2) < QUEST_ELEVARTEMPO)) { + menu + rif(Zeny >= .@price && LOCATION$ != "Candor", l("To Candor Island.")), L_TCandor, + rif(Zeny >= .@price && LOCATION$ != "Tulim", l("To Tulimshar.")), L_TTulim, + rif(Zeny >= .@price && LOCATION$ != "Artis" && $FIRESOFSTEAM && getq(General_Narrator) >= 20, l("To Artis.")), L_TArtis, + rif(ST_TIER == 7 && gettimetick(2) < QUEST_ELEVARTEMPO ,l("Help me, I need Jesusaves Grimorie!")), L_Tier2, + rif(ST_TIER == 9 && countitem(Lifestone) && gettimetick(2) < QUEST_ELEVARTEMPO ,l("Help me, I need Jesusaves Grimorie!")), L_Tier2Ok, + l("No, I'll save my money."), -; + } else { + mes l("You still need @@ GP to afford it.", (.@price-Zeny)); + } + + close; + +L_TCandor: + Zeny=Zeny-.@price; + PC_DEST$="Candor"; + + mes ""; + mesn; + mesq l("Candor Island, then? Yes, that is a pretty island, right?"); + next; + mesq l("I was planning to go there soon, anyway. All aboard!"); + close2; + addtimer nard_time(PC_DEST$), "#NardShip::OnEvent"; + @timer_navio_running = 1; + warp "002-5", 39, 26; + end; + +L_TTulim: + Zeny=Zeny-.@price; + PC_DEST$="Tulim"; + @timer_navio_running = 1; + + mes ""; + mesn; + mesq l("Tulimshar, right? The oldest human city-state!"); + next; + mesq l("I was planning to go there soon, anyway. All aboard!"); + close2; + addtimer nard_time(PC_DEST$), "#NardShip::OnEvent"; + @timer_navio_running = 1; + warp "002-5", 39, 26; + end; + +L_TArtis: + Zeny=Zeny-.@price; + PC_DEST$="Artis"; + @timer_navio_running = 1; + + mes ""; + mesn; + mesq l("Artis, right? So Andrei Sakar summoned you?"); + next; + mesq l("Hahaha, good luck out there, my friend!"); + close2; + addtimer nard_time(PC_DEST$), "#NardShip::OnEvent"; + @timer_navio_running = 1; + warp "002-5", 39, 26; + end; + +L_Tier2: + mes ""; + mesn; + mesq l("WHAT?! ARE YOU OUT OF MIND?!?!"); + next; + mesn; + if (nard_reputation() < 8) { + mesq l("THAT GRIMORIE IS A SUPER DUPER MEGA UPER RARE BOOK, I CANNOT GIVE IT TO ANYBODY ASKING ME ABOUT!!"); + next; + mesn; + mesq l("GET OUT OF HERE, YOUR NOBODY!"); + close; + } + mesq l("That is a rare, precious book, which writes itself!"); + next; + mesn; + mesq l("I cannot just give it to you for nothing. Run to Elanore and fetch me a @@. You need to make a new one, an old one I won't accept.", getitemlink(Lifestone)); + ST_TIER=8; + close; + +L_Tier2Ok: + mes ""; + mesn; + mesc l("*tut*"); + next; + inventoryplace JesusalvaGrimorium, 1; + delitem Lifestone, 1; + getitem JesusalvaGrimorium, 1; + getexp 200, 0; + ST_TIER=10; + mesn; + mesq l("Here, take it. If the mana goes out of your body, I'll have your class master to return the book to me."); + close; + +OnNardStage: + showavatar NPC_NARD; // this is handled by avatars.xml + mesn; + mesq l("Welcome to Tulimshar, @@!", strcharinfo(0)); + mesc l("Nard gives you a map of the city so you don't get lost."); + next; + if (GSET_SOULMENHIR_MANUAL) { + mesn; + mesq l("You can explore the city as you want, but if I were you, I would ##Btouch the Soul Menhir##b, north of here, to don't respawn at Candor."); + next; + } else { + mesn; + mesq l("If you die, you'll appear near Tulimshar's Soul Menhir, which is the nearest Menhir to you. You can use @@ if you ever want to change this behavior.", b("@ucp")); + next; + } + mesn; + mesq l("Other than that, you can explore the city as you want, but as you had a memory loss, You should visit the townhall."); + next; + mesn; + mesq l("When you get out of the ship, it is the first building you'll see. Talk to ##BLua##b, she is an alliance representative."); + next; + mesn; + mesq l("The Alliance have records of everyone. And if you need another trip, talk to me!"); + tutmes l("PROTIP: Every quest you complete in a location, will make ship travels to and from them cheaper!"), "Protip", false; + next; + addtimer 2500, "Inac::OnShout"; + closeclientdialog; + close; + +L_TestServer: + mesc l("THIS IS MOUBOOTAUR LEGENDS TEST SERVER."), 1; + mesc l("Progress on this server may be %s.", b(l("lost forever"))), 1; + mes ""; + mesc l("Unless you know what you are doing, please go to Main Server instead."), 1; + mesc l("Main server is: %s", b("server.moubootaurlegends.org")), 2; + mes ""; + mesf "@@help://test-server|%s@@", l("more information about test server ->"); + next; + goto L_Main; + +L_Close: + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/002-3/note.txt b/npc/002-3/note.txt new file mode 100644 index 0000000..32cdf4d --- /dev/null +++ b/npc/002-3/note.txt @@ -0,0 +1,51 @@ +// TMW-2 Script. +// Evol scripts. +// Authors: +// gumi +// Qwerty Dragon +// Reid +// WildX +// Jesusalva +// Description: +// A small note presenting the rules and game-world release notes of TMW-2. + +002-3,40,25,0 script Note#johanne NPC_PAPER_NOTE,{ + narrator S_LAST_NEXT, + l("The La Johanne always have interesting notes."); + + do { + select + l("Read the News."), + l("Read the Rules."), + rif($EVENT$ != "", l("Event Information")), + l("Leave."); + + switch (@menu) { + case 1: + GameNews(); + break; + case 2: + GameRules(8 | 4); + break; + case 3: + EventHelp(); + break; + + } + } while (@menu != 4); + + + narrator S_NO_NPC_NAME, + l("Following these lines are some other writings on this paper."), + l("Do not give your password to anybody! Keep it secret and try not to use the same one anywhere else in the future. - Juliet"), + l("People from the press always know what is happening in the world. Talk to them to learn about the latest news! - Jerican from the Press"), + l("I love you, Silvia! - Swezanne"), + (!rand2(50) ? ("\"Come meager fire, and devastate spot!\" "+l("-- Grandmaster")) : ""), + l("Other things are written but are not legible anymore."); + + close; + +OnInit: + .distance = 2; + end; +} diff --git a/npc/002-4/_import.txt b/npc/002-4/_import.txt new file mode 100644 index 0000000..c2cede2 --- /dev/null +++ b/npc/002-4/_import.txt @@ -0,0 +1,3 @@ +// Map 002-4: Nard's Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/002-4/doors.txt", diff --git a/npc/002-4/doors.txt b/npc/002-4/doors.txt new file mode 100644 index 0000000..2d7f945 --- /dev/null +++ b/npc/002-4/doors.txt @@ -0,0 +1,15 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Doors NPCs. + +002-4,19,27,0 script AreaNPC#002-4d NPC_HIDDEN,0,0,{ + +OnTouch: + if (LOCATION$ == "") + warp "002-3", 43, 28; + else + warp "002-3@"+LOCATION$, 43, 28; + close; +} diff --git a/npc/002-5/_import.txt b/npc/002-5/_import.txt new file mode 100644 index 0000000..757883b --- /dev/null +++ b/npc/002-5/_import.txt @@ -0,0 +1,5 @@ +// Map 002-5: Ocean +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/002-5/_mobs.txt", +"npc/002-5/main.txt", +"npc/002-5/mapflags.txt", diff --git a/npc/002-5/_mobs.txt b/npc/002-5/_mobs.txt new file mode 100644 index 0000000..cdfc371 --- /dev/null +++ b/npc/002-5/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 002-5: Ocean mobs +002-5,32,26,8,2 monster Ocean Croc 1133,2,36000,30000 diff --git a/npc/002-5/main.txt b/npc/002-5/main.txt new file mode 100644 index 0000000..6e5094f --- /dev/null +++ b/npc/002-5/main.txt @@ -0,0 +1,68 @@ +// TMW2 script +// Originals from TMW-BR +// Imported by Jesusalva + +002-5,28,27,0 script #NardShip NPC_HIDDEN,117,29,{ + //if (PC_DEST$ != "" && @timer_navio_running == 0) goto L_Timer; + end; + +OnEvent: + if (@timer_navio_running == 0) end; + if (PC_DEST$ == "Candor") goto L_Candor; + else if (PC_DEST$ == "Tulim") goto L_Tulim; + else if (PC_DEST$ == "Artis") goto L_Artis; + else goto L_Error; + end; + +L_Candor: + PC_DEST$ = ""; + LOCATION$ = "Candor"; + @timer_navio_running = 0; + warp "005-1", 49, 117; + message strcharinfo(0), l("%s disembarks at %s.", strcharinfo(0), l("Candor Island")); + end; + +L_Tulim: + PC_DEST$ = ""; + EnterTown("Tulim"); + @timer_navio_running = 0; + warp "003-1", 81, 68; + message strcharinfo(0), l("%s disembarks at %s.", strcharinfo(0), l("Tulimshar")); + end; + +L_Artis: + PC_DEST$ = ""; + EnterTown("Artis"); + @timer_navio_running = 0; + warp "029-0", 203, 85; + message strcharinfo(0), l("%s disembarks at %s.", strcharinfo(0), l("Artis")); + end; + +L_Error: + PC_DEST$ = ""; + @timer_navio_running = 0; + warp "000-1", 22, 22; + Exception("PLAYER INVALID PC_DEST ON #NardShip: " + PC_DEST$, RB_DEFAULT | RB_ISFATAL); + end; + +OnTouch: + if (PC_DEST$ != "" && @timer_navio_running == 0) { + addtimer nard_time(PC_DEST$), "#NardShip::OnEvent"; + @timer_navio_running = 1; + } + end; +} + +// Do not add .distance here. +002-5,40,26,0 script Elmo#002-5 NPC_ELMO,{ + npctalk3 l("It won't be long before we reach our destination..."); + // If player logged out during travel, this Elmo can reset player travel time. + if (PC_DEST$ != "" && @timer_navio_running == 0) goto L_Timer; + end; + +L_Timer: + addtimer nard_time(PC_DEST$), "#NardShip::OnEvent"; + @timer_navio_running = 1; + end; + +} diff --git a/npc/002-5/mapflags.txt b/npc/002-5/mapflags.txt new file mode 100644 index 0000000..889a0d2 --- /dev/null +++ b/npc/002-5/mapflags.txt @@ -0,0 +1 @@ +002-5 mapflag zone ship diff --git a/npc/003-0-1/_import.txt b/npc/003-0-1/_import.txt new file mode 100644 index 0000000..5aa9264 --- /dev/null +++ b/npc/003-0-1/_import.txt @@ -0,0 +1,9 @@ +// Map 003-0-1: The Magic Guild +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-0-1/_warps.txt", +"npc/003-0-1/audsbel.txt", +"npc/003-0-1/guards.txt", +"npc/003-0-1/maxime.txt", +"npc/003-0-1/professor.txt", +"npc/003-0-1/researcher.txt", +"npc/003-0-1/statues.txt", diff --git a/npc/003-0-1/_warps.txt b/npc/003-0-1/_warps.txt new file mode 100644 index 0000000..6a19e6c --- /dev/null +++ b/npc/003-0-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-0-1: The Magic Guild warps +003-0-1,49,52,0 warp #003-0-1_49_52 3,0,003-1,51,36 diff --git a/npc/003-0-1/audsbel.txt b/npc/003-0-1/audsbel.txt new file mode 100644 index 0000000..05954af --- /dev/null +++ b/npc/003-0-1/audsbel.txt @@ -0,0 +1,499 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// One of the Mana Magic Leaders +// Planned, there are only the five Mana Wizards: Auldsbel (Tulimshar), Sagratha (Woodlands), Morgan (Candor), Blue Sage (Nivalis), Lalica (LoF). + +// Gives #parum +// Gives #mkpot +// Quest step 11 doesn't exist - legacy debug + +003-0-1,81,27,0 script Auldsbel NPC_AULDSBEL,{ + mesn l("Auldsbel the Wizard"); + mesq l("Welcome back, Padric!"); + mesq l("Do you believe I've lost my @@? I can't see anything well without that! %%a", getitemlink(Googles)); + if (countitem(Googles)) + mesq l("I'm not interested in yours, by the way."); + next; + mesn; + mesq l("Anyway, I am one of the few Mana Wizard, and I love Transmutation!"); + next; + +L_Main: + select + rif(MAGIC_LVL, l("What's the difference from a Mana Wizard and a Mage?")), + l("Who are you? Where are you from?"), + rif(MAGIC_LVL, l("How do I advance in Mana Magic? How it works?")), + rif(MAGIC_LVL, l("What do you know about other Mana Magic Professors?")), + rif(MAGIC_LVL, l("Can you teach me Mana Magic?")), + l("Do you still need help with your experiments?"), + l("Actually, I gotta go, see ya!"); + + mes ""; + switch (@menu) { + case 1: // What's the difference from a Mana Wizard and a Mage? + if (MAGIC_LVL < 2) { + mesn; + mesn; + mesq l("%%4 Haven't you read your Grimorium yet?!"); + next; + mesn; + mesq l("%%@ You can get Magic by joining class and subclass, and with some NPCs. These work out-of-box. Easy."); + next; + mesn; + mesq l("And there is the Mana Magic, for the pro %%e You'll waste your life on that, as it have an experience system."); + next; + mesn; + mesq l("%%G Just like normal magic, you need power from the Mana Stone, which is based on your levels, intelligence, and mana."); + next; + mesn; + mesq l("I like Mana Magic the best, as we can both summon creatures as transmutate stuff. %%N"); + next; + } else { + mesn; + mesq l("Well, long story short, we have two magic systems. One works out of box. The later one, is the Mana Magic."); + next; + mesn; + mesq l("Mana Magic is less reliable and have an experience system. It's the most common for summoning and transmutation."); + next; + mesn; + mesq l("I could explain this better, but you are just a noob at magic, if you understand me."); + next; + } + mesn; + mesq l("To be honest, you should use both. And remember, all Mana Magic skills can be used like they were @sk-commands. %%H"); + next; + break; + case 2: // Who are you? Where are you from? + mesn; + mesq l("Well, speaking a bit about myself can't hurt. I am a member from the Magic Council, but one day I decided to take a vacations."); + next; + mesn; + mesq l("I moved to Hurnscald, stayed there for over a decade, until Lord Transmogrifier Pontorias the Plaid (May His Shape Reflect His Soul Forever) died."); + next; + mesn; + mesq l("Then I returned to Tulimshar. Now I do research, vote on the Magic Council meetings, and I also teach young mages about Transmutation magic."); + next; + break; + case 3: // How do I advance in Mana Magic? How it works? + mesn; + mesq l("Mana Magic works similar to regular magic: You can find it on your skill window, and can drag it to your shortcut list."); + next; + mesn; + mesq l("To get more experience and magic power, you must practice magical spells. Make sure to vary them; you will learn nothing if you cast the same spell over and over. Also, spells that consume no components seem not to be very instructive in practice."); + next; + mesn; + mesq l("Usually, you can find magic on these buildings, but watch out, there is magic to be found elsewhere, and some items are bound with it!"); + next; + mesn; + mesq l("While on normal magic you must take care with all attributes, on mana magic, you usually only need to worry with reagents and intelligence."); + next; + mesn; + mesq l("Also, unlike regular magic which may cause delay before and after, Mana Magic usually only have a cooldown. But it is hard to know how long that cooldown is..."); // We may use addtimer() on one or other spell. + next; + break; + case 4: // What do you know about Sagratha? + mesn; + mesq l("Lemme see... Sagratha is often regarded as a kind and rather powerful elf lady mage... who hate people."); + next; + mesn; + mesq l("And I'm not talking only about humans here! But perhaps, if you build a good reputation, she teaches you something."); + next; + mesn; + mesq l("There's also Morgan, who lives in Candor. She is a Redy, and is married with Zitoni. They are great alchemists."); + next; + mesn; + mesq l("By last, there was the Blue Sage, living on Nivalis... I never met him outside the Council."); + next; + mesn; + mesq l("As you see, you can count the mages with the fingers of one hand. It's not just Mana Magic, either - Magic in overall is almost dead, with almost every Mana Stone on the power of the Monster King."); + next; + mesn; + mesq l("Defeating him would not only stop monster invasions, but it would also bring magic back... And probably another war, over the mana stones. %%S"); + next; + break; + // Teaching and helping are bound one to other, to save space on variable e.e + case 5: // Can you teach me Mana Magic? + case 6: // Do you still need help with your experiments? + goto L_Magic; + break; + default: // Actually, I gotta go, see ya! + goodbye; + closedialog; + close; + } + goto L_Main; + +L_Magic: + .@q=getq(General_Auldsbel); + switch (.@q) { + // Help on research to gain his favor + case 0: + mesn; + mesq l("I actually need help. Padric and I were doing some research with catalysts, you see."); + next; + mesn; + mesq l("Now I need 20 @@, 20 @@ and 60 @@ to finish my research. Easy materials, except for the Shadow Herb.", getitemlink(MauveHerb), getitemlink(ShadowHerb), getitemlink(SilkCocoon)); + next; + mesn; + mesq l("Shadow Herb only grows on dangerous places, and is mostly found on the Land Of Fire, or underground of very very deep caves."); + next; + select + l("I'll try to find them."), + l("I actually have them, here."); + if (@menu == 2) { + mes ""; + mesn; + mesq l("Excellent! Let me see..."); + next; + if ( + countitem(MauveHerb) < 20 || + countitem(ShadowHerb) < 20 || + countitem(SilkCocoon) < 60) + goto L_Lie; + delitem MauveHerb, 20; + delitem ShadowHerb, 20; + delitem SilkCocoon, 60; + getexp 2500, 0; + Zeny=Zeny+250; + setq General_Auldsbel, 1; + mesn; + mesq l("Yes, many thanks. This will help me a lot."); + mesc l("Gained 2500 XP and 250 GP"); + } + break; + // Learn #parum + case 1: + if (MAGIC_LVL < 1) + goto L_Magicless; + mesn; + mesq l("Hmm, I think I can teach you a basic Mana Skill now. That one is pretty simple."); + next; + skill(TMW2_PARUM,1,0); + setq General_Auldsbel, 2; + mesn; + mesq l("This is the @@ skill. It transmutes a single @@ in a @@.", "##B@sk-parum##b", getitemlink(RawLog), getitemlink(MoubooFigurine)); + next; + mesn; + mesq l("It may also create some @@ or a @@, with enough skill.", getitemlink(Arrow), getitemlink(WoodenLog)); + next; + mesn; + mesq l("So! Please transmute a @@ and bring it to me. You may need to switch with another mana skill, until you are successful.", getitemlink(MoubooFigurine)); + break; + // Bring the Mouboo figurine back + case 2: + mesn; + mesq l("Have you managed to transmute the @@ I asked for?", getitemlink(MoubooFigurine)); + next; + if (askyesno() == ASK_YES) { + if (!countitem(MoubooFigurine)) goto L_Lie; + if (!MAGIC_EXP) goto L_Lame; + delitem MoubooFigurine, 1; + getexp 5000, 0; + setq General_Auldsbel, 3; + mesn; + mesq l("Very well - Congratulations! That was very easy, though, and this one is full of imperfections."); + mesc l("Gained 5000 XP"); + next; + mesn; + mesq l("That skill was only to allow you to practice. Now listen well: Transmutation is ##BNOT##b crafting!"); + next; + mesn; + mesq l("Magic is sacred. With transmutation, you can create convenience items, specially reagents for other magic skills."); + next; + mesn; + mesq l("But it will not help you to craft something as complex as weapons or armors. If we catch you profaning this magic... I'll have you returned to the sea %%e"); + next; + mesn; + mesq l("This is just a friendly advise. We don't take magic lightly. And you shouldn't, either."); + } + break; + // Help on research to gain his favor + case 3: + mesn; + mesq l("I actually need help. I am a mage, and I'm feeling lazy to get the stuff I need."); + next; + mesn; + mesq l("You don't seem to have anything better to do, anyway."); + mesq l("Now please bring me @@/2 @@, @@/20 @@, @@/30 @@ and @@/70 @@ so I don't need to leave here and start travelling everywhere...", countitem(IcedBottle), getitemlink(IcedBottle), countitem(Root), getitemlink(Root), countitem(Potatoz), getitemlink(Potatoz), countitem(Moss), getitemlink(Moss)); + next; + select + l("I'll try to find them."), + l("I actually have them, here."); + if (@menu == 2) { + mes ""; + mesn; + mesq l("Excellent! Let me see..."); + next; + if ( + countitem(IcedBottle) < 2 || + countitem(Root) < 20 || + countitem(Potatoz) < 30 || + countitem(Moss) < 70) + goto L_Lie; + delitem IcedBottle, 2; + delitem Root, 20; + delitem Potatoz, 30; + delitem Moss, 70; + getexp 28692, 0; + Zeny=Zeny+550; + setq General_Auldsbel, 4; + mesn; + mesq l("Yes, many thanks. This will help me a lot."); + } + break; + // Learn #mkpot + case 4: + if (MAGIC_LVL < 2) + goto L_Magicless; + mesn; + mesq l("Hmm, I think I can teach you a basic Mana Skill now. This one is more advanced, though."); + next; + skill(TMW2_TRANSMIGRATION,1,0); + setq General_Auldsbel, 5; + mesn; + mesq l("This is the @@ skill. It transmutes stuff into other stuff. I'll teach you some more recipes as class drag on.", "##B@sk-trans##b"); + next; + mesn; + mesq l("It may fail, and you might end up with something entirely unexpected, or nothing at all!"); + next; + mesn; + mesq l("Unlike Parum, you can use the skill points you get every time your job level rises. That will lower the mana cost and increase success chances."); + next; + mesn; + mesq l("But please be picky with how you spend job points. They are hard to come by, and I'm not entirely sure you can change it later."); + next; + mesn; + mesq l("Also, job levels get really hard to obtain after a while. The decision is up to you, just be aware there's that possibility."); + break; + // Help on research to gain his favor + case 5: + mesn; + mesq l("Well, I decided to resume an old research of mine, now that I have a minion (you) to gather stuff for me."); + next; + mesn; + mesq l("Now please be a good helper, and aid me by bringing:"); + mesc l("* @@/@@ @@", countitem(HastePotion), 10, getitemlink(HastePotion)); + mesc l("* @@/@@ @@", countitem(StrengthPotion), 10, getitemlink(StrengthPotion)); + mesc l("* @@/@@ @@", countitem(HerbalTea), 10, getitemlink(HerbalTea)); + mesc l("* @@/@@ @@", countitem(RedScorpionStinger), 25, getitemlink(RedScorpionStinger)); + mesc l("* @@/@@ @@", countitem(SilkCocoon), 100, getitemlink(SilkCocoon)); + next; + select + l("I'll try to find them."), + l("I actually have them, here."); + if (@menu == 2) { + mes ""; + mesn; + mesq l("Excellent! Let me see..."); + next; + if (!transcheck( + HastePotion, 10, + StrengthPotion, 10, + HerbalTea, 10, + RedScorpionStinger, 25, + SilkCocoon, 100)) + goto L_Lie; + getexp 7500, 0; + Zeny=Zeny+1250; + setq General_Auldsbel, 6; + mesn; + mesq l("Thanks. I'm actually conducting experiments with scorpions. Please come back later."); + mesc l("Gained 7500 XP and 1250 GP"); + } + break; + // Learn transmigration: Scorpion Stinger and Claw + case 6: + if (MAGIC_LVL < 2) + goto L_Magicless; + mesn; + mesq l("Well, I'm currently researching scorpions, as you can imagine."); + next; + setq General_Auldsbel, 7; + mesn; + mesq l("I'll teach you how to transmute some parts of theirs. You can use it to convert a @@ into a @@, but not the other way around, for example.", getitemlink(BlackScorpionStinger), getitemlink(RedScorpionStinger)); + next; + mesn; + mesq l("This have many uses. Maybe. Anyway, I'm soon done with my experiment, so please come back later."); + close; // On purpose + break; + // Help on research to gain his favor + case 7: + mesn; + mesq l("Hmm... See, the thing is that transmuting living beings is not normally something that transmutation magic can do."); + next; + mesn; + mesq l("But I will not give up on my little experiment. Incidentally, Snakes are shaddy enough for my experiment. I promise you, I'll succeed this time."); + next; + mesn; + mesq l("Oh. And don't mention anyone what I'm researching here. No need to fuss over minor things, don't you agree? It's totally not shaddy. Not shaddy at all!"); + next; + mesn; + mesq l("Now please be a good helper, and aid me by bringing:"); + mesc l("* @@/@@ @@", countitem(MountainSnakeTongue), 15, getitemlink(MountainSnakeTongue)); + mesc l("* @@/@@ @@", countitem(SnakeTongue), 15, getitemlink(SnakeTongue)); + mesc l("* @@/@@ @@", countitem(CaveSnakeTongue), 15, getitemlink(CaveSnakeTongue)); + mesc l("* @@/@@ @@", countitem(MountainSnakeEgg), 15, getitemlink(MountainSnakeEgg)); + mesc l("* @@/@@ @@", countitem(SnakeEgg), 15, getitemlink(SnakeEgg)); + mesc l("* @@/@@ @@", countitem(CaveSnakeEgg), 15, getitemlink(CaveSnakeEgg)); + next; + select + l("I'll try to find them."), + l("I actually have them, here."); + if (@menu == 2) { + mes ""; + mesn; + mesq l("Excellent! Let me see..."); + next; + if (!transcheck( + MountainSnakeTongue, 15, + SnakeTongue, 15, + CaveSnakeTongue, 15, + MountainSnakeEgg, 15, + SnakeEgg, 15, + CaveSnakeEgg, 15)) + goto L_Lie; + getexp 20000, 0; + Zeny=Zeny+2500; + setq General_Auldsbel, 8; + mesn; + mesq l("Thanks. Snakes seems promising indeed! Maybe they work where scorpions failed. If I succeed, I promise I'll teach you the spell. But for now..."); + mesc l("Gained 20000 XP and 2500 GP"); + } + break; + // Learn transmigration: Snake Egg, Tongue, Skin + case 8: + if (MAGIC_LVL < 3) + goto L_Magicless; + mesn; + mesq l("Well, I have not finished my research on snakes yet, but I'm pretty sure in how to transmute their parts."); + next; + setq General_Auldsbel, 9; + mesn; + mesq l("Here, look at how it is done. Focus. You can use it to convert a @@ into a @@, but not the other way around, for example.", getitemlink(MountainSnakeSkin), getitemlink(SnakeSkin)); + next; + mesn; + mesq l("Be careful as not everybody likes transmuted monster parts. Some may even see it as a foul thing. Anyway. Come back later."); + close; // On purpose + break; + // Help on research to gain his favor + case 9: + mesn; + // Obviously wrong, have you never read about butterflies? + mesq l("So... I think I'm almost done! My plan is to force a @@ into being a pretty Butterfly. Hey, I like cute things!", getitemlink(SilkCocoon)); + next; + mesn; + mesq l("Besides, there are no Butterflies in Tulimshar, and they could help the farm in getting producing food."); + next; + mesn; + mesq l("This time, I only a few last reagents and I'll finally attempt it... I want you to witness it. I'll teach you the spell later, of course."); + mesc l("* @@/@@ @@", countitem(ManaPiouFeathers), 5, getitemlink(ManaPiouFeathers)); + mesc l("* @@/@@ @@", countitem(IceCube), 1, getitemlink(IceCube)); + mesc l("* @@/@@ @@", countitem(OceanCrocClaw), 1, getitemlink(OceanCrocClaw)); + next; + select + l("I'll try to find them."), + l("I actually have them, here."); + if (@menu == 2) { + mes ""; + mesn; + mesq l("Excellent! Let me see..."); + next; + if (!transcheck( + ManaPiouFeathers, 5, + IceCube, 1, + OceanCrocClaw, 1)) + goto L_Lie; + mesn; + mesq l("Now lo and behold... The ultimate... TRANSMUTATION!"); + // Pray that you don't get disconnected now + next; + getexp 10000, 500; + setq General_Auldsbel, 10; + npctalk l("@@... I think something went wrong... RUN!!", strcharinfo(0)); + .@mob=monster(.map$, .x, .y, "Failed Experiment", GrassSnake, 1, .name$+"::OnSnakeDeath"); + specialeffect FX_MAGIC, SELF, getcharid(3); + specialeffect FX_MGWARP, SELF, .name$; + specialeffect FX_ATTACK, AREA, .@mob; + // TODO: Maybe we should use unitattack()? Not need but... + // We could also reconfigure this snake Mode to exclude ChangeTarget + closeclientdialog; + close; + } + break; + // Learn Halhiss, completing Audsbel Quest + case 10: + // Actually, that's lv 60... Learning a lv 40 spell '-' + if (MAGIC_LVL < 3) + goto L_Magicless; + if (mobcount(.map$, .name$+"::OnSnakeDeath")) { + mesn; + mesq l("Could you please dispose my failed experiment, first?"); + close; + } + mesn; + mesq l("*sigh* Yet another failure... Transmutation and Nature Magic doesn't marry well."); + next; + skill(TMW2_HALHISS,1,0); + setq General_Auldsbel, 12; + mesn; + mesq l("Next time, I'll ask Sagratha to help. Heh. I doubt that's going to happen. By the way, this is the @@ spell. It summons snakes...", b("@sk-halhiss")); + next; + mesn; + mesq l("You need a @@ for it. Ah, back to research I guess...", getitemlink(SnakeEgg)); + next; + break; + default: + mesn; + mesq l("Uhm, no, not really. Maybe later, who knows?"); + break; + } + next; + goto L_Main; + + +// Fallbacks +L_Magicless: + mesn; + mesq l("Well, you helped me. That's great! One hand washes the other, so, I'm willing to share knowledge with you."); + next; + mesn; + mesq l("But unless you touch a Mana Stone and get stronger magic, that would be as useful as teaching magic to a wall. No offense."); + next; + mesn; + mesq l("So, please, come to me with stronger magic powers. And then, I'll teach you a new magic spell."); + close; + +L_Lie: + mesn; + mesq l("Really interesting, how I am NOT seeing the items I asked for..."); + next; + mesn; + mesq l("Say, what do you think if I transmuted your head into the missing materials? I can warrant your soul won't return to the Soul Menhir, either!"); + close; + +L_Lame: + setparam(MaxHp, readparam(MaxHp)-50); // I want to see how permanent this is + setparam(MaxSp, readparam(MaxSp)-25); // I want to see how permanent this is + //setparam(Karma, readparam(Karma)-1); // testing + mesn; + mesq l("%%3 You sadden me. That was so, so lame. I will need to punish you. Sorry. Superior orders. %%S"); + next; + mesn strcharinfo(0); + mesq l("%%i What, my maximum life and mana just decreased! Noooo!!"); + next; + mesn; + mesq l("%%1 Cheer up, these should go back to normal when you level up. Just don't do that again!"); + // If that is true or not, remains to be checked. Uh... I never used setparam() before! :D + close; + +OnSnakeDeath: + end; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/003-0-1/guards.txt b/npc/003-0-1/guards.txt new file mode 100644 index 0000000..0c34c52 --- /dev/null +++ b/npc/003-0-1/guards.txt @@ -0,0 +1,61 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Magic Council Guard + +003-0-1,50,24,0 script Guard#00301A NPC_BRGUARD_SPEAR,{ + if ($MOST_HEROIC$ == strcharinfo(0)) + goto L_Hero; + if (getgmlevel()) + goto L_Sponsor; + if ($FIRESOFSTEAM) + goto L_Steam; + + mesn; + mesq l("Past this grates, is the Magic Council Room."); + next; + mesn; + mesq l("Unless you're an Alliance member or have authorization, you cannot go in."); + if (getvaultid()) + mesc l("Alliance members are those who [@@https://tmw2.org/contact|sponsor us@@]."); + else + mesc l("Alliance members are those who [@@https://patreon.com/TMW2|sponsor us@@]."); + mesc l("There's nothing in the Magic Council Room, though. It's just a perk."); + close; + +L_Hero: + mesn; + mesq l("Greetings, %s. Do you have business on the Alliance Council Room?", strcharinfo(0)); + mesc l("Warp to Council room?"); + if (askyesno() == ASK_YES) { + warp "003-0-2", 34, 42; + } + closeclientdialog; + close; + +L_Sponsor: + mesn; + mesq l("Alliance members (sponsors) are allowed inside the Council Room."); + mesc l("Warp to Council room?"); + if (askyesno() == ASK_YES) { + warp "003-0-2", 34, 42; + } + closeclientdialog; + close; + +L_Steam: + mesn; + mesq l("The Council is not in session, it has dispersed since Andrei Sakar went to an expedition to Artis. However, it was decided to allow others inside."); + mesc l("Warp to Council room?"); + if (askyesno() == ASK_YES) { + warp "003-0-2", 34, 42; + } + closeclientdialog; + close; + +OnInit: + .distance=4; + end; +} + diff --git a/npc/003-0-1/maxime.txt b/npc/003-0-1/maxime.txt new file mode 100644 index 0000000..ed3204b --- /dev/null +++ b/npc/003-0-1/maxime.txt @@ -0,0 +1,108 @@ +// TMW-2 Script. +// Author: +// Saulc +// Jesusalva +// Notes: +// Bakes Tonori Delight + +003-0-1,77,40,0 script Maxime NPC_PLAYER,{ + function MaximeOven; + mesn; + mesq l("Hello. I know the secrets of the legendary @@.", getitemlink(TonoriDelight)); + next; + mesn; + mesq l("I could easily bake one for you, provided you bring me the following:"); + mesc l("@@/12 @@", countitem(MaggotSlime), getitemlink(MaggotSlime)); + mesc l("@@/8 @@", countitem(Plushroom), getitemlink(Plushroom)); + mesc l("@@/4 @@", countitem(MushroomSpores), getitemlink(MushroomSpores)); + mesc l("@@/3 @@", countitem(ScorpionStinger), getitemlink(ScorpionStinger)); + mesc l("@@/2 @@", countitem(CactusDrink), getitemlink(CactusDrink)); + mesc l("@@/1 @@", countitem(RoastedMaggot), getitemlink(RoastedMaggot)); + mesc l("@@/120 GP", format_number(Zeny)); + next; + .@q=getq(HalinarzoQuest_LifeDelight); + menuint + l("I have the items, please bake for me"), 1, + rif(.@q > 10, l("I want two batches!")), 2, + rif(.@q > 20, l("I want THREE batches!")), 3, + rif(.@q > 30, l("I need FIVE batches!")), 5, + rif(.@q > 50, l("I have a supplier, gimme TEN batches!")), 10, + rif(.@q > 75, l("Do as much as possible.")), 100, + l("Ah, nice to know."), 0; + + mes ""; + + if (!@menuret) + goto L_Close; + + for (.@i=0; .@i < @menuret; .@i++) { + if (!MaximeOven()) + goto L_Missing; + } + + mesn; + mesq l("Here you go, fresh from the oven!"); + next; + +L_Close: + closedialog; + goodbye; + close; + +L_Missing: + if (@menuret == 100) close; + mesn; + mesq l("You don't have everything I asked you for."); + next; + mesn; + mesq l("I always wonder if I should raise my price to teach bad kids to don't lie."); + close; + +function MaximeOven { + if ( + countitem(MaggotSlime) < 12 || + countitem(Plushroom) < 8 || + countitem(MushroomSpores) < 4 || + countitem(ScorpionStinger) < 3 || + countitem(CactusDrink) < 2 || + countitem(RoastedMaggot) < 1 || + Zeny < 120) return false; + + // 4~7 normaly, 5~10 during Summer + // Xanthem Patch: +20% craft and -20% effect + // New values: 5~9 normal or 6~12 during Summer + inventoryplace TonoriDelight, 12; + delitem MaggotSlime, 12; + delitem Plushroom, 8; + delitem MushroomSpores, 4; + delitem ScorpionStinger, 3; + delitem CactusDrink, 2; + delitem RoastedMaggot, 1; + getitem TonoriDelight, rand2(5,9); + if (season() == SUMMER) { + getitem TonoriDelight, rand2(1,3); + mesc l("During summer, more Tonori Delight can be produced."); + } else if (getequipid(EQI_HEAD_TOP) == ChefHat) { + getitem TonoriDelight, any(1, 1, 2); + mesc l("You are a master chef, looking at you inspires Maxime."); + } + Zeny=Zeny-120; + getexp rand2(30, 60), 0; + return true; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, ChefHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, AssassinBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 2); + + .sex = G_MALE; + .distance = 4; + npcsit; + end; +} + diff --git a/npc/003-0-1/professor.txt b/npc/003-0-1/professor.txt new file mode 100644 index 0000000..dff4fa9 --- /dev/null +++ b/npc/003-0-1/professor.txt @@ -0,0 +1,114 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Professor - allows you to gain EXP for idling (Speech skill) + +003-0-1,35,29,0 script Professor NPC_PLAYER,{ + mesn; + mesq l("I've mastered the art of speech and communication."); + next; + mesn; + mesq l("Sit on the rug in front of me and learn wisdom! Maybe you'll learn something this way."); + if (AFKING) + mesc l("Thus far, you've heard about %d hours and %d minutes of wisdom.", AFKING/1200, AFKING%1200/60*3); + if (!TUTORIAL) close; + next; + mes ".:: AFK EXP AREA ::."; + mesc l("By sitting in the rug in front of the professor (the one with benches)"); + mesc l("and \"listening\" to his speech, you'll get EXP!"); + mes ""; + mesc l("If you get too much exp this way, a skill will level up and you'll get even more!"); + mesc l("But remember: %s", b(l("You cannot sit idle for too long gaining EXP!"))); + mesc l("After about 30 minutes AFK, you won't get EXP anymore."); + mesc l("But don't worry, because as they say: The more you learn, the better you get at learning!"); + mes ""; + mesc l("In overall, you'll get more experience by fighting monsters."); + mesc l("But if you only want to sit down and chat, this area is ideal for you!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, GraduationCap); + setunitdata(.@npcId, UDT_HEADMIDDLE, GraduationRobe); + setunitdata(.@npcId, UDT_HEADBOTTOM, DeepBlackBoots); + setunitdata(.@npcId, UDT_WEAPON, CottonGloves); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex=G_MALE; + .distance=5; + initnpctimer; + end; + +OnTimer3100: + areatimer2("003-0-1", 29, 34, 41, 38, 10, .name$+"::OnSpeeching"); + initnpctimer; + end; + +OnSpeeching: + deltimer(.name$+"::OnSpeeching"); // safeguard + // Max AFK time is determined as 30 minutes + 1 second every 10 minutes AFKed + // Capped at 2 hours (you've AFK'ed 37 days and 12 hours - 900 hours) + .@maxafk=min(7200, 1800+(AFKING/600)); + // If you have been IDLE for at least 2 seconds you'll get the EXP. + // Note you don't need to sit, only be idle. + if (is_between(2, .@maxafk, checkidle())) { + .@sk=getskilllv(TMW2_SPEECH); + .@bxp=min(1+.@sk, 12); + .@jxp=min(1+(.@sk/3), 6); + + getexp .@bxp, .@jxp; + + // (Extra) Experience for Homunculus + if (gethominfo(0)) + gethomunexp(1); + + // Research happens roughly every five minutes, based on Study skill + if (getskilllv(TMW2_STUDY)) { + if (AFKING % 100 == 0) + MAGIC_RP+=getskilllv(TMW2_STUDY); + } + + // If you are learning TMW2_READANCIENTLANGUAGES + if (ANCIENTLANGUAGEBOUNCER) { + ANCIENTLANGUAGEBOUNCER-=1; + if (ANCIENTLANGUAGEBOUNCER == 1) { + ANCIENTLANGUAGEBOUNCER=0; + skill TMW2_ANCIENTLANGUAGES, 1, 0; + dispbottom l("It was a boring lesson, but you have mastered Mananese."); + } + } + + // You're being annoying, now + // 1 hour, 1200 exp, enough from level 1 to level 10 + AFKING+=1; + switch (AFKING) { + case 28800: + // 24 hours, 28,800 exp + .@x=1; + case 201600: + // +1 week, +403,200 exp + .@x=(AFKING >= 201600 ? 2 : .@x); + case 864000: + // +1 month, +2,592,000 exp + .@x=(AFKING >= 864000 ? 3 : .@x); + case 2592000: + // +3 months, +10,368,000 exp + .@x=(AFKING >= 2592000 ? 4 : .@x); + // Note: In the needed time (~4 months) you should be over 90 if you + // were fighting instead... + skill(TMW2_SPEECH, .@x, 0); + AFKING+=1; + npctalk3 l("%s, you're a good student. You will have a bright future if you keep studying.", strcharinfo(0)); + dispbottom l("Learning from seeing (aka. AFK-ing) skill LEVEL UP!!"); + break; + } + // There are no further upgrades to TMW2_SPEECH skill + // You'll need to get the AFK Set if you want to level from 4 to 10. + + } + deltimer(.name$+"::OnSpeeching"); // safeguard + end; +} + diff --git a/npc/003-0-1/researcher.txt b/npc/003-0-1/researcher.txt new file mode 100644 index 0000000..5a0e12c --- /dev/null +++ b/npc/003-0-1/researcher.txt @@ -0,0 +1,91 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Researcher - allows you to decrypt Ancient Blueprints (Ancient Lang. skill) + +003-0-1,58,29,0 script Researcher NPC_BLACKALCHEMIST,{ + mesn; + mesq l("I've mastered the art of reading ancient languages."); + next; + mesn; + mesq l("For only @@ GP, I'll decrypt any ancient text file you have. Or you can learn how to read that yourself, for @@ GP.", .price, .learn); + next; + select + l("I want you to decrypt something"), + rif(!getskilllv(TMW2_ANCIENTLANGUAGES) && !ANCIENTLANGUAGEBOUNCER, l("I want to learn reading ancient languages")), + l("Nothing, sorry."); + switch (@menu) { + case 1: + if (Zeny < .price) { + mesc l("You don't have enough GP."), 1; + close; + } + + mes b(l("Drag and drop an item from your inventory.")); + .@id = requestitem(); + + // If ID is invalid + if (.@id < 1) { + mesc l("You give up."); + close; + } + // No item, or bound item + if (countitem(.@id) < 1 || checkbound(.@id)) { + if (checkbound(.@id)) + mesc l("You cannot part with this item!"); + else + mesc l("You give up."); + close; + } + + switch (.@id) { + case AncientBlueprint: + Zeny-=.price; + delitem AncientBlueprint, 1; + MakeRandomBlueprint(); + break; + case DesertTablet: + mesn; + mesq l("Sorry, it looks like Saulc wrote on this stone. It is entirely illegible... for now."); + break; + default: + mesn; + mesq l("Uhm, I don't think this item needs my skills."); + break; + } + break; + case 2: + if (Zeny < .learn) { + mesc l("You don't have enough GP."), 1; + close; + } + + mesn; + mesq l("Wait, it is not so simple as just giving me the money."); + next; + mesn; + mesq l("What do you think learning is, magic?! No!"); + next; + mesn; + mesq l("What do you need to learn something besides money for tuitions fee? Time and Dedication."); + next; + Zeny-=.learn; + ANCIENTLANGUAGEBOUNCER=13; // 120s / 5 = 12 + 1 for node + mesn; + mesq l("Do you see the professor on the next room? Listen to his boring speech for @@. There are the materials for the class.", b(l("two minutes"))); + next; + mesn; + mesq l("If you pay attention, you'll learn the skill."); + break; + } + close; + +OnInit: + .sex=G_MALE; + .distance=5; + .price=350; + .learn=5000; + end; +} + diff --git a/npc/003-0-1/statues.txt b/npc/003-0-1/statues.txt new file mode 100644 index 0000000..e127e2c --- /dev/null +++ b/npc/003-0-1/statues.txt @@ -0,0 +1,127 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// These statues are of great honor to whoever have their name written in them! + +003-0-1,63,30,0 script Fortune Statue NPC_STATUE_BANKER,{ + if (.rate_limit >= gettimetick(2)) + end; + .rate_limit=gettimetick(2); + + HallOfFortune(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + .rate_limit=0; + end; +} + +003-0-1,53,30,0 script Strength Statue NPC_STATUE_GUARD,{ + if (.rate_limit >= gettimetick(2)) + end; + .rate_limit=gettimetick(2); + + HallOfLevel(); + next; + HallOfJob(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + .rate_limit=0; + end; +} + +003-0-1,56,26,0 script Hero Statue NPC_STATUE_ANDREI,{ + + mes l("This statue was built for memory of Andrei Sakar, the greatest hero this world has even seen."); + mes l("For defending Hurnscald alone and saving all its inhabitants."); + mes l("For fighting against the Monster King once and getting out alive to tell the story."); + mes l("For all his great deeds, and thousands of lives he saved, this statue is in his honor."); + if ($MOST_HEROIC$ == "") + goto L_Fame; + next; + mes l("Also in honor of @@, who did a great act of bravery recently. May they keep protecting our world!", $MOST_HEROIC$); + // TODO: Must find a better place for this + next; + mes l("And in honor of all brave LoF players, to be known to all, the fluffly hunters."); + mes "BunnyBear (239) - 2017-11-07 10:04:29"; + mes "Scorpius (190) - 2017-01-09 21:33:00"; + mes "Billr (177) - 2016-05-21 23:53:22"; + mes "Naburudanga (153) - 2017-07-28 22:14:07"; + mes "Axzell (150) - 2017-01-09 22:23:00"; + +L_Fame: + next; + mesq l("All hail the ones who proven their worth before the whole Alliance!"); + + HallOfHonor(); + HallOfGuild(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +003-0-1,60,26,0 script Worker Statue NPC_STATUE_CONTRIBUTOR,{ + + HallOfSponsor(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + + +003-0-1,68,28,0 script #Statue2018 NPC_STATUE_2018,{ + + HallOf2018(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +003-0-1,67,27,0 script #Statue2019 NPC_STATUE_2019,{ + + HallOf2019(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +003-0-1,68,26,0 script #Statue2020 NPC_STATUE_2020,{ + + HallOf2020(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +003-0-1,67,25,0 script #Statue2021 NPC_STATUE_2021,{ + + HallOf2021(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + diff --git a/npc/003-0-2/_import.txt b/npc/003-0-2/_import.txt new file mode 100644 index 0000000..1551645 --- /dev/null +++ b/npc/003-0-2/_import.txt @@ -0,0 +1,6 @@ +// Map 003-0-2: The Magic Guild Council Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-0-2/_warps.txt", +"npc/003-0-2/manastone.txt", +"npc/003-0-2/misc.txt", +"npc/003-0-2/saves.txt", diff --git a/npc/003-0-2/_warps.txt b/npc/003-0-2/_warps.txt new file mode 100644 index 0000000..01d46e7 --- /dev/null +++ b/npc/003-0-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-0-2: The Magic Guild Council Room warps +003-0-2,35,43,0 warp #003-0-2_35_43 3,0,003-0-1,48,24 diff --git a/npc/003-0-2/manastone.txt b/npc/003-0-2/manastone.txt new file mode 100644 index 0000000..6ab980c --- /dev/null +++ b/npc/003-0-2/manastone.txt @@ -0,0 +1,76 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Mana Stone owned by the Magic Council +// +// Variables: +// $MANA_BINT => Base Intelligence for Mana Stone +// $MANA_BLVL => Base Level for Mana Stone +// $MANA_JLVL => Base Job Level for Mana Stone +// .int => Int Increment +// .lvl => Lvl Increment +// .jlvl => Jlv Increment + +003-0-2,35,19,0 script Mana Stone#Tulim NPC_MANA_STONE,{ + + // You need 50% more base level + if (BaseLevel < $MANA_BLVL*15/10) goto L_NotWorthy; + mesn; + mes l("The mighty Mana Stone does not reacts against you."); + mes l("Although this particular one seems to hate everyone and everything, it recognizes your strength."); + mes l("If you fell ready, perhaps you should touch it?"); + mes ""; + menu + l("Touch it!"), L_Level, + l("Take it!"), L_NotWorthy2, + l("Break it!"), L_NotWorthy2, + l("Leave it alone!"), -; + close; + + + +L_NotWorthy2: + npctalk3 l("You are not worthy!"); + percentheal -20, -50; + if (!MAGIC_LVL) + dispbottom l("I should train my intelligence, have full MP, and don't neglect even Job Level."); + end; + +L_Level: + // See functions/util.txt for *mstone() details + if (mstone(0)) goto L_LevelUp; + if (mstone(1)) goto L_LevelUp; + if (mstone(2)) goto L_LevelUp; + if (mstone(3)) goto L_LevelUp; + if (mstone(4)) goto L_LevelUp; + if (mstone(5)) goto L_LevelUp; + if (mstone(6)) goto L_LevelUp; + if (MAGIC_LVL >= 7) npctalk3 l("You already got all power I could grant you!"); + if (is_gm()) percentheal -20, -50; + if (MAGIC_LVL >= 7 || is_gm()) close; + +L_NotWorthy: + if (readparam(Sp) != readparam(MaxSp)) + dispbottom l("I must have full MP to touch it... Which I don't."); + else + dispbottom l("I should train my intelligence, and level up, both my base as my Job Level."); + npctalk3 l("You are not worthy!"); + percentheal min(-10, -70+BaseLevel), min(-10, -100+BaseLevel); + end; + +L_LevelUp: + mes ""; + mes l("A great rush of mana flows though you."); + if (!MAGIC_LVL) mes l("Magic Power is granted to you, but you die from it."); + if (MAGIC_LVL) mes l("More Magic Power is granted to you, but you die from it."); + MAGIC_LVL = MAGIC_LVL+1; + sk_lvup(AL_DP); + die(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 2; + end; +} diff --git a/npc/003-0-2/misc.txt b/npc/003-0-2/misc.txt new file mode 100644 index 0000000..e4c2f35 --- /dev/null +++ b/npc/003-0-2/misc.txt @@ -0,0 +1,16 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Miscellaneous utilities for the magic council + +003-0-2,28,42,0 script Lloyd NPC_LLOYD,{ + Banker(.name$, "Magic Guild", 1000000); + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + end; +} + diff --git a/npc/003-0-2/saves.txt b/npc/003-0-2/saves.txt new file mode 100644 index 0000000..ba9b5bb --- /dev/null +++ b/npc/003-0-2/saves.txt @@ -0,0 +1,168 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Alliance High Council + +003-0-2,22,24,0 script HC Jesusalva NPC_LOF_NOBLEMAN,{ + mesn l("Councilor Jesusalva"); + mesq l("Greetings, %s %s, to the Alliance High Council.", ($MOST_HEROIC$ == strcharinfo(0) ? l("Great Hero") : (ACADEMIC_RANK ? academicrank() : lg("miss", "mister"))), strcharinfo(0)); + next; + mesn l("Councilor Jesusalva"); + mesq l("I am currently in charge of overseeing the world state and the Monster King advancements, as well as planning and dealing with emergencies. The Alliance High Council is currently composed by %s members and %s honorary ones.", l("five"), l("two")); + next; + mesn l("Councilor Jesusalva"); + mesq l("If you found anything which requires immediate attention, like broken roads which lead nowhere, or cliffs with weird collisions, invisible rocks, scamming NPCs or the sorts, please write down a %s and I'll see it. If the interface is too difficult to use, you can also do so %s or send an email to %s.", "[@@https://git.themanaworld.org/ml/serverdata/-/issues|ticket@@]", "[@@https://discord.gg/BQNTe68|here@@]", "bug@tmw2.org"); + if (.saul && .jak1 && .lawn && .craz) + close; + next; + mesn l("Councilor Jesusalva"); + mesq l("The other councilors?"); + if (!.saul) + mes l("─ Saulc should be on his residence, going over the plans again."); + if (!.jak1) + mes l("─ Jak1 should be on the Mirror Lake or verifying the infrastructure."); + if (!.lawn) + mes l("─ LawnCable is likely on his room doing research, I wouldn't disturb him if I were you."); + if (!.craz) + mes l("─ Crazyfefe is likely patrolling the world, aiding Constables and catching criminals."); + // The honor commanders, Pihro and Pyndragon, are likely managing their town. Ever been to Land of Fire? I heard they plan in building a floating island, too! + // The "Diamond" is, uh, I guess he's on the study room, the academy or perhaps the library. He said he is not qualified enough to be on the high command and decided to study some more. + // Demure is now part of game lore, how do I even make her a member XD + close; + +OnDeloc: + if (isin("003-0-2", 20, 20, 49, 37)) { + slide 35, 38; + sleep2(200); + dispbottom l("The High Council is now on session, you were forced to leave the meeting chamber."); + } + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + .session = false; + +OnMinute17: +OnMinute44: + disablenpc "HC Saulc"; + disablenpc "HC Jak1"; + disablenpc "HC LawnCable"; + disablenpc "HC Crazyfefe"; + .saul = false; + .jak1 = false; + .lawn = false; + .craz = false; + + // Define who will be at Council Room + .@r = rand2(100); + + if (.session) { + .@r -= 5; + .session = false; + disablenpc "High Council Secretary"; + delcells "HCouncilGate"; + npctalk "The council is dismissed!"; + } + + if (.@r >= 97 && !$FIRESOFSTEAM) { + // Council Session + enablenpc "HC Saulc"; + enablenpc "HC Jak1"; + enablenpc "HC LawnCable"; + enablenpc "HC Crazyfefe"; + enablenpc "High Council Secretary"; + setcells "003-0-2", 32, 35, 37, 37, 1, "HCouncilGate"; + .session = true; + npctalk "The council is now in session."; + maptimer("003-0-2", 100, "HC Jesusalva::OnDeloc"); + end; + // TODO: Council - make the NPCs speak etc + } + + // Not a council session, so some members may show up + if (.@r % 30) { + enablenpc "HC LawnCable"; + .lawn = true; + } + + if (.@r % 10) { + enablenpc "HC Jak1"; + .jak1 = true; + } + + if (.@r % 5) { + enablenpc "HC Crazyfefe"; + .craz = true; + } + + if (.@r % 2) { + enablenpc "HC Saulc"; + .saul = true; + } + end; +} + +003-0-2,34,27,0 script HC Saulc NPC_HALBERDBARBARIAN,{ + mesn l("Councilor Saulc"); + mesq l("These battle plans are all wrong; They trace routes on the Canyon which doesn't exist..."); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +003-0-2,22,30,0 script HC Jak1 NPC_LOF_DOCTOR,{ + mesn l("Councilor Jak1"); + mesq l("Why everything is so broken, couldn't the Monster King try to do less collateral damage?! These things will take years to be fixed!"); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +003-0-2,47,24,0 script HC LawnCable NPC_BLACKALCHEMIST,{ + mesn l("Councilor LawnCable"); + mesq l("Maybe if... No... Hmm... I better try this the next time I'm at my laboratory..."); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +003-0-2,47,30,0 script HC Crazyfefe NPC_LOF_RICH,{ + mesn l("Councilor Crazyfefe"); + mesq l("I want to arrest the Monster King one day, but until then, I'm happy getting rid of petty scammers, spammers, and general evildoers."); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +// NPC_BLACKWIZARD NPC_MIRAJ +// NPC_BELLA » Secretary +// When the council is in session, a lock raises +// And Bella explains the council is in session, to come back later +// NPCs may also speak stuff randomly + +003-0-2,32,38,0 script High Council Secretary NPC_BELLA,{ + mesn; + mesq l("Greetings. The council is currently in session, no one is allowed in the inner chambers."); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + disablenpc .name$; + end; +} + diff --git a/npc/003-0/_import.txt b/npc/003-0/_import.txt new file mode 100644 index 0000000..d7fe2f9 --- /dev/null +++ b/npc/003-0/_import.txt @@ -0,0 +1,7 @@ +// Map 003-0: Magic Academy +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-0/_mobs.txt", +"npc/003-0/_warps.txt", +"npc/003-0/mainquest.txt", +"npc/003-0/recepcionist.txt", +"npc/003-0/trickmaster.txt", diff --git a/npc/003-0/_mobs.txt b/npc/003-0/_mobs.txt new file mode 100644 index 0000000..23f6637 --- /dev/null +++ b/npc/003-0/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-0: Magic Academy mobs +003-0,45,32,24,2 monster Piou 1002,3,60000,30000 +003-0,45,30,24,1 monster Croc 1006,3,60000,30000 diff --git a/npc/003-0/_warps.txt b/npc/003-0/_warps.txt new file mode 100644 index 0000000..0d4ddff --- /dev/null +++ b/npc/003-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-0: Magic Academy warps +003-0,48,52,0 warp #003-0_48_52 2,0,003-1,34,34 diff --git a/npc/003-0/mainquest.txt b/npc/003-0/mainquest.txt new file mode 100644 index 0000000..28f49a8 --- /dev/null +++ b/npc/003-0/mainquest.txt @@ -0,0 +1,178 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Main Quest + +003-0,49,35,0 script #MQ25Trigger NPC_HIDDEN,3,0,{ + end; + +OnTouch: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + if (is_night()) + doevent instance_npcname(.name$)+"::OnBegin"; + else + addtimer 1000, instance_npcname(.name$)+"::OnCheck"; + end; + +// Checks if you are in designated ambush zone. If not, keep hidden +OnCheck: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + if (!isin(.@m$, 44, 24, 54, 34)) + end; + // goto OnBegin; + +// Begin +OnBegin: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + .PLAYER=getcharid(3); + + // Is assassin ambushing you or professor? + if (is_night()) { + .@x=any(46, 52); + .@y=34; + } else { + getmapxy(.@m$, .@x, .@y, 0); + .@y-=3; + if (.@y < 34) + .@y=34; + } + setcells .@m$, 47, 35, 51, 35, 3, "MQ2Wall"+getcharid(0); + if (isin(.@m$, 44, 24, 54, 34)) { + warp .@m$, 49, 33; + atcommand("@refresh"); + } + .ASSASSIN=monster(.@m$, .@x, .@y, l("Assassin"), Assassin, 1, .@n$+"::OnAssassinDefeat"); + unittalk(.ASSASSIN, l("Die now!!")); + // Nerf ATK, boost HP + /* + Hp: 4211 + Attack: [198, 214] + */ + setunitdata(.@REF, UDT_MAXHP, 4750); + setunitdata(.@REF, UDT_HP, 4750); + setunitdata(.@REF, UDT_ATKMIN, 150); + setunitdata(.@REF, UDT_ATKMAX, 180); + + // TODO: unitwalk + /* + // TODO: Energy Balls. Should they spawn at (21,31) and (69,31)? + // At this time they are utterly deadly. You could use a skill to + // disable them... Perhaps... A grenade? + dispbottom col(l("SCRIPT ERROR (%s/%s)", .@m$, .@n$), 1); + dispbottom l("An error happened: unitwalk failed"); + */ + // TODO: Assassinate + initnpctimer; + end; + +OnTimer15000: + .@n$=instance_npcname(.name$); + unittalk(.ASSASSIN, ("Heh... He gave me a ball, but...")); + end; + +OnTimer20000: + .@n$=instance_npcname(.name$); + unittalk(.ASSASSIN, ("I can do this alone!")); + end; + +OnTimer60000: + .@n$=instance_npcname(.name$); + unittalk(.ASSASSIN, ("Tsc...! I'll need reinforcements!")); + end; + +OnTimer65000: + .@n$=instance_npcname(.name$); + unittalk(.ASSASSIN, ("How was the summoning again...?!")); + end; + +OnTimer75000: + .@n$=instance_npcname(.name$); + unittalk(.ASSASSIN, ("Oh, that's right!")); + end; + +OnTimer82000: + .@n$=instance_npcname(.name$); + unittalk(.ASSASSIN, ("ENEEEEEEEEERGY BALLLLLLL!")); + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, .ASSASSIN); + .@REF=monster(.@m$, .@x, .@y, "Energy Ball", EnergyBall, 1); + + // Nerf the monster, be careful, it has a powerful skill! + setunitdata(.@REF, UDT_AGI, 1); + setunitdata(.@REF, UDT_LUK, 1); + setunitdata(.@REF, UDT_DEF, 10); + setunitdata(.@REF, UDT_MDEF, 20); + setunitdata(.@REF, UDT_MAXHP, 1000); + setunitdata(.@REF, UDT_HP, 1000); + end; + +OnAssassinDefeat: + stopnpctimer; + .@m$=getmap(); + .@n$=instance_npcname(.name$); + delcells "MQ2Wall"+getcharid(0); + LUA_ASKED_TO_SAVE_PROFESSOR=false; + getexp 16, 5; // Extra bonus :> + + if (is_night()) { + unittalk(getnpcid(instance_npcname(.name$)), l("Thanks for the help, I guess...?")); + } else { + dispbottom l("A mission well done. I should report to lua now."); + } + // Extra extra EXTRA bonus + CsysNpcCraft(SmallKnife, IOPT_SPLASHDAMAGE, 1, 0,0, 0,0, VAR_MAXHPPERCENT, -5); + addtimer 100, instance_npcname(.name$)+"::OnAbort"; + specialeffect(FX_FANFARE, SELF, getcharid(3)); + end; + +OnAbort: + stopnpctimer; + end; + +OnInit: + disablenpc .name$; + end; + +OnInstanceInit: + if (is_night()) { + .@n$=instance_npcname("Professor#003-0"); + enablenpc .@n$; + } + .ASSASSIN=0; + .PLAYER=0; + end; +} + +003-0,49,24,4 script Professor#003-0 NPC_PLAYER,{ + npctalkonce l("I wonder if it'll take too long for the ship to arrive..."); + end; + +OnTouchNPC: + npctalk l("An error happened: professor_was_assasinated() error"); + npctalk ("SCRIPT ERROR (OnTouch Game Over)"); + end; + +OnInit: + .@ini=true; +OnInstanceInit: + if (.@ini) + .@npcId = getnpcid(.name$); + else + .@npcId = getnpcid(instance_npcname(.name$)); + + setunitdata(.@npcId, UDT_HEADTOP, GraduationCap); + setunitdata(.@npcId, UDT_HEADMIDDLE, GraduationRobe); // TODO: Bathrobe + setunitdata(.@npcId, UDT_HEADBOTTOM, Slippers); // Hey hey! + setunitdata(.@npcId, UDT_WEAPON, CottonGloves); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex=G_MALE; + .distance=5; + disablenpc .name$; + end; +} + diff --git a/npc/003-0/notes b/npc/003-0/notes new file mode 100644 index 0000000..f5ce8d6 --- /dev/null +++ b/npc/003-0/notes @@ -0,0 +1,138 @@ +FFA - FREE FOR ALL + AL_DP (Divine Protection, +DEF vs undead/evil) (raised at Mana Stone) + +---- +AOE (DESTRUCTIVE MAGIC) + MG_NAPALMBEAT + MG_FIREBALL + WZ_FROSTNOVA (38%+5% freeze, 73%+7%/lv MATK) + +SINGLE TARGET (D.M.) + AL_HOLYLIGHT (standard holy magic attack - 125% of MATK) + MG_SOULSTRIKE (+5% MATK, +1 hit each 2 levels) + MG_COLDBOLT (ele) + MG_FIREBOLT (ele) + MG_LIGHTININGBOLT (ele) + WZ_EARTHSPIKE (ele / not a bolt for PF_DOUBLECASTING) + MG_FROSTDIVER (38%+3% freeze, +10% MATK) + + + +AOE (P.S.) + ASC_METEORASSAULT (area: self 2X2, 80%+40%/lv ATK, 10+5%/lv stun/blind/bleed) + AC_SHOWER (3x3 attack for BOWS) + +SINGLE TARGET (PHYSICAL SCIENCE) + SM_BASH (+30% ATK, HIT +5%, após lv 6: stun (5%/lv)) + MC_MAMMONITE (+50% atk/lv) + KN_AUTOCOUNTER (contra-ataque, TOP) + + AC_CHARGEARROW (Ranged, 150% DMG, 6 tiles knockback, NEED GWENDOLYN) + SN_SHARPSHOOTING (ULTIMATE ranged attack) + + + +SUPPORT SCIENCE / SCHOLARSHIP (self) + AC_OWL (raise 1 DEX per level) + SA_DRAGONOLOGY (PV: +4% ATK, +2% MATK, +1 INT and +4% resist against DRAGONS) + TMW2_SAGE (PV: grants up to 1.5 extra mana exp point/level) + MG_SRECOVERY (PV: small increase to SP Recovery while idle each 10 sec.) + SM_RECOVERY (PV: small increase to HP Recovery while idle each 10 sec.) + + SN_WINDWALK (raise walking speed +2%/lv and flee rate +1/2lv) + + CR_TRUST (PV: MaxHP +200/lv, Holy DEF +5%/lv) + AC_VULTURE (PV: raise range and cth with BOWS) + +SUPPORT SCIENCE / SCHOLARSHIP (MMO) + AL_HEAL (basic healing) + AL_INCAGI (raise agi in 3+1/lv) + HW_MAGICPOWER (aumenta poder da próxima skill em MATK 5%/lv) + SM_PROVOKE (provoca um monstro especifico - exceto BOSS) + **AL_ANGELUS (14x14, party, def +5%/lv) + TF_DETOXIFY (cancels poison. 40% MP.) + AC_CONCENTRATION (self: agi/dex 3+1%/lv, unhide enemies 3x3) + AB_HIGHNESSHEAL (REAL healing skill) + ALL_RESURRECTION (reviver, requer permitir target players mortos) + EVOL_MASS_PROVOKE (provoca mobs em área) + + PR_ASPERSIO (bestow holy element on weapon for 1m. Holy dmg to undead/evil.) + SA_FLAMELAUNCHER (bestow fire element on weapon for 2m, 70% cth) + SA_FROSTWEAPON (bestow water element on weapon for 2m, 70% cth) + SA_LIGHTNINGLOADER (bestow wind element on weapon for 2m, 70% cth) + SA_SEISMICWEAPON (bestow earth element on weapon for 2m, 70% cth) + + GC_DARKCROW (Max Lv 1: DMG +100%, Short Range DMG +30%) + + + +TRICKS SCIENCE + SA_FREECAST (move after casting) + TF_BACKSLIDING (pulo pra trás) + MG_FIREWALL (wall of fire: 50% dmg x3+1/lv hits, knockback 2 cells) + ALL_FULL_THROTTLE (Stat +20%, full heal, move speed x2 - w/ rebound, last resort) + GC_DARKILLUSION (tp pra cima do mob/player com um ataque, pode causar dano extra - 4%/lv) + NV_TRICKDEAD (se finge de morto) + SO_FIREWALK (deixa fogo aonde passa - 60% MATK) + + + + + + +UNUSED/SPECIAL + BA_PANGVOICE (confuse, EXTREME DESYNC) + GS_SNAKEEYE (→AC_VULTURE) + +THIEF/MERCHANT-POLICE + TF_STEAL + ALL_INCCARRY + TF_MISS / TF_DOUBLE ? + MC_DISCOUNT / MC_OVERCHARGE (FIXME) ? + TF_HIDING? + MC_PUSHCART? + MC_VENDING? + + + +BROKEN + PF_HPCONVERSION (10% HP vira MP (10%/lv)) + HP_MEDITATIO (PV: MP Regen +3%/lv, MSP +1%/lv, AL_HEAL +2%/lv stronger) + GS_MAGICALBULLET (adds MATK to your regular attack) Does'nt works? + CR_DEFENDER (less damage from ranged attacks, but lowers move & attack speed. Req. Shield) - max lv 4 plz. Last 3m? + +USEFUL COMMANDS + npcskill(<skill id>, <skill lvl>, <stat point>, <NPC level>) + npcskill(AL_HEAL, 10, 99, 60); + + unitskilluseid(<GID>, <skill id>, <skill lvl>{, <target id>}) + unitskillusepos(<GID>, <skill id>, <skill lvl>, <x>, <y>) + + *defpattern(<set number>, "<regular expression pattern>", "<event label>") + *activatepset(<set number>) + *deactivatepset(<set number>) + *deletepset(<set number>) + Regex commands (iilia) + + *statusup2(<stat>, <amount>) + Needs testing, said to be permanent but will it survive a status reset? + Actually, we could add an extra memory and re-cast this, if we control + usage correctly. + + *autobonus2(<bonus script>, <rate>, <duration>{, <flag>, {<other script>}}) + May cast a bonus when attacked :> + + SC_TENSIONRELAX + Tension relax allows the user to recover HP while overweight at 1x speed. + Also, looks like sitting _should_ allow you to regen even overweight, need + plugin check for possible overrides on this behavior. status_natural_heal + + SC_VITATA_500 + Apparently regens SP? Requires further studies. + + SC_NOEQUIPWEAPON [Evol] + Forces your opponent to fight without a weapon. + + + + diff --git a/npc/003-0/recepcionist.txt b/npc/003-0/recepcionist.txt new file mode 100644 index 0000000..90c4a42 --- /dev/null +++ b/npc/003-0/recepcionist.txt @@ -0,0 +1,162 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Magic School receptionist (TODO: Give S. Badge) + +003-0,61,44,4 script Receptionist#003-0 NPC_FEMALE,{ + mesn; + mesq l("Hello, and welcome to the Magic School of Tulimshar."); + next; + mesn; + mesq l("Actually I'm responsible of dispatching mages to the Magic Academy."); + next; + select + l("What is the Magic Academy?"), + l("I am a mage. How do I get to Academy?"), + l("What magic classes are there?"), + l("Erm... Good bye."); + mes ""; + switch (@menu) { + case 1: + goto L_Prologue; + case 2: + goto L_Academy; + case 3: + goto L_Classes; + } + closeclientdialog; + goodbye; + close; + +////////////// +/* Prologue */ +////////////// +L_Prologue: + mesn l("Magic Academy Tutorial"); + mesc l("The Magic Academy System is responsible for learning most skills ingame. For that, you need two things: Magic Skill Points and a reagent."); + next; + mesn l("Magic Academy Tutorial"); + mesc l("The reagent is always the same for the same class. If you do not have enough reagents but have an @@, it'll be used to continue.", getitemlink(ScholarshipBadge)); + next; + mesn l("Magic Academy Tutorial"); + mesc l("Magic Skill Points can be obtained in three ways: By touching a Mana Stone, by signing up in a Special Class (if you have enough magic power) and by having high amounts of Job Level."); + next; + mesn l("Magic Academy Tutorial"); + mesc l("Upgrading a skill level can be done the same away and will always cost a single Magic Skill Point. Job Level points are obtained only after Lv @@, and is a single point each @@ levels.", 15+12, 12); + next; + if (!MGQUEST) { + inventoryplace ScholarshipBadge, 1; + mesn; + mesq l("That being said, I'll give you an @@, which allows you to learn a skill even if you can't pay for it.", getitemlink(ScholarshipBadge)); + next; + } + mesn; + mesq l("Please note unless you have Magic Powers, obtained from the Mana Seed, all you will be able to learn are small tricks, so please make a wise choice."); + if (!MGQUEST) { + MGQUEST=true; + getitem ScholarshipBadge, 1; + } + tutmes l("Magic skills allows for a more advanced gameplay, but they are meant for level 30 onward."); + tutmes l("If you follow %s's quest, you will eventually reach the requirements for here.", b("Lua")); + close; + + +////////////// +/* Academy */ +////////////// +L_Academy: + if (!MAGIC_LVL) { + mesn; + mesq l("Oh please. Even if you know a trick or two, you don't have real magic."); + next; + mesn; + mesq l("You need to touch a Mana Stone to get magic. Certainly the only Mana Stone on Tulimshar is highly protected by the Council, so no chances here."); + next; + mesn; + mesq l("Well, the biggest mana stones mine was in %s, but I'm afraid it has already depleted... Besides, it is very dangerous.", b("Halinarzo")); + } else { + mesn; + mesq l("Oh, just walk on the dock, and you'll be warped there."); + next; + mesn; + mesq l("I mean, a ship should come to pick you up, but the arch-wizards said they were too lazy to take care of those small details. So you'll be warped."); + } + close; + + +////////////// +/* Classes */ +////////////// +L_Classes: + mesn; + mesq l("We have four classes, but you can make a mix between them."); + next; + mesc ".:: "+l("Physical Science")+" ::.", 3; + mes l("They use magic to boost their bodies, allowing them to do very, VERY powerful physical attacks."); + mes l("They also don't need much intelligence or magic equipment."); + mes ""; + mes l("Physical Science Institute is on the %s area of the campus.", b(l("west"))); + next; + mesc ".:: "+l("Wizardry")+" ::.", 3; + mes l("They use mana to create magic attacks and blast their foes away."); + mes l("They rely entirely in intelligence and magic equipment."); + mes ""; + mes l("Wizardry Institute is on the %s area of the campus.", b(l("north"))); + next; + mesc ".:: "+l("Scholarship Science")+" ::.", 3; + mes l("Training to be sages, their knowledge is unparalleled. They're masters of boosting skills."); + mes l("Most of their skills are stat-independent, but not all of them."); + mes ""; + mes l("Scholarship Institute is on the %s area of the campus.", b(l("north"))); + next; + mesc ".:: "+l("Tricks")+" ::.", 3; + mes l("They cheat! They make silly tricks which look like magic."); + mes ""; + mes l("The trickster can be found right here."); + close; + + +/// Core code +OnTimer1000: + domovestep; + +OnInit: +OnInstanceInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SorcererRobe); + //setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + //setunitdata(.@npcId, UDT_WEAPON, JeansShorts); + setunitdata(.@npcId, UDT_HAIRSTYLE, any(8,11,20)); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + + // Small movement + initpath "move", 61, 44, + "dir", UP, 0, + "wait", 7, 0, + "move", 62, 46, + "dir", DOWN, 0, + "wait", 7, 0, + "move", 63, 44, + "dir", UP, 0, + "wait", 7, 0, + "move", 61, 44; + initialmove; + initnpctimer; + + .sex=G_FEMALE; + .distance=5; + end; +} + + +003-0,48,24,0 script #MagicAcademy NPC_HIDDEN,1,0,{ + end; + +OnTouch: + if (MAGIC_LVL && $HURNS_LIBDATE) + warp "027-1", any(89,90), 155; + end; +} + diff --git a/npc/003-0/trickmaster.txt b/npc/003-0/trickmaster.txt new file mode 100644 index 0000000..1488587 --- /dev/null +++ b/npc/003-0/trickmaster.txt @@ -0,0 +1,125 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Trickmaster of Tricksters Class + +003-0,35,42,0 script Trickmaster NPC_SITTED_NINJA,{ + function basicMagic; + function standardMagic; + function advancedMagic; + mes l(".:: Trickster Class ::."); + mesc l("Specialized in miscellaneous skills."); + next; + mesn; + mesc l("You have @@ magic skill points available.", sk_points()); + select + l("Basic Tricks"), + l("Standard Tricks"), + l("Advanced Tricks"); + mes ""; + .@lv=@menu; + do + { + // Display appropriate menu + if (.@lv == 1) + basicMagic(); + else if (.@lv == 2) + standardMagic(); + else if (.@lv == 3) + advancedMagic(); + + // Handle result + mes ""; + if (@menuret) { + if (!learn_magic(@menuret)) { + mesc l("You do not meet all requisites for this skill."), 1; + next; + } + } else { + closeclientdialog; + } + + } while (@menuret); + close; + +L_NoMagic: + next; + mesn; + mesq l("You do not have enough magic power for these classes."); + next; + if ($FIRESOFSTEAM < 9) { + mesn; + mesq l("Besides the Magic Council, Andrei Sakar have his own Mana Stone, but I doubt he would train the likes of you, or share his Mana Stone."); + next; + } + mesn; + mesq l("Perhaps, in the city, someone knows rumors about Mana Stones and can teach you. Other than that, you're on your own."); + close; + +function basicMagic { + mes l(".:: Mana Bomb ::."); + mesc l("Converts all your mana in damage. Damages all enemies in same tile."); + mes ""; + mes l(".:: Backsliding ::."); + mesc l("Instantly jumps 5 tiles backwards."); + mes ""; + mes l(".:: Nature Wall ::."); + mesc l("Create a natural wall under the cursor, to delay your enemies."); + mes ""; + mes l(".:: Archers Eye ::."); + mesc l("Increase bow range and accuracy."); + mes ""; + mes l(".:: First Aid ::."); + mesc l("Recover some HP."); + mes ""; + menuint + l("Mana Bomb"), TMW2_MANABOMB, + l("Backsliding"), TF_BACKSLIDING, + l("Nature Wall"), MG_FIREWALL, + l("Archers Eye"), AC_VULTURE, + l("First Aid"), TMW2_FIRSTAID, + l("Cancel"), 0; + return; +} + +function standardMagic { + if (!MAGIC_LVL) goto L_NoMagic; + mes l(".:: Free Cast ::."); + mesc l("Allows to attack right after casting."); + mes ""; + mes l(".:: Full Throttle ::."); + mesc l("An emergency skill which temporarily raises all your stats."); + mes ""; + mes l(".:: Sudden Attack ::."); + mesc l("Instantly jumps to target and delivers an attack."); + mes ""; + mes l(".:: Trick Dead ::."); + mesc l("Plop dead in the ground. Enemies won't attack you this way."); + mes ""; + menuint + l("Free Cast"), SA_FREECAST, + l("Full Throttle"), ALL_FULL_THROTTLE, + l("Sudden Attack"), GC_DARKILLUSION, + l("Trick Dead"), NV_TRICKDEAD, + l("Cancel"), 0; + return; +} + +function advancedMagic { + if (MAGIC_LVL < 2) goto L_NoMagic; + mes l(".:: There are no skills ::."); + mesc l("You can bug Jesusalva to extend the Battlefield Control skills."); + mes ""; + menuint + l("Cancel"), 0; + return; +} + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; + +} + diff --git a/npc/003-1-1/_import.txt b/npc/003-1-1/_import.txt new file mode 100644 index 0000000..56c456a --- /dev/null +++ b/npc/003-1-1/_import.txt @@ -0,0 +1,7 @@ +// Map 003-1-1: Tulimshar Sewers +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-1-1/_mobs.txt", +"npc/003-1-1/_warps.txt", +"npc/003-1-1/downpath.txt", +"npc/003-1-1/treasure.txt", +"npc/003-1-1/yetiking.txt", diff --git a/npc/003-1-1/_mobs.txt b/npc/003-1-1/_mobs.txt new file mode 100644 index 0000000..930a9e4 --- /dev/null +++ b/npc/003-1-1/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-1-1: Tulimshar Sewers mobs +003-1-1,108,76,68,75 monster Cave Maggot 1027,44,35000,300000 +003-1-1,85,72,65,72 monster Black Scorpion 1074,23,35000,300000 +003-1-1,80,87,71,71 monster Ratto 1005,32,35000,300000 +003-1-1,41,55,15,14 monster Little Blub 1007,5,35000,200000 diff --git a/npc/003-1-1/_warps.txt b/npc/003-1-1/_warps.txt new file mode 100644 index 0000000..d2cbe9c --- /dev/null +++ b/npc/003-1-1/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-1-1: Tulimshar Sewers warps +003-1-1,29,62,0 warp #003-1-1_29_62 0,0,004-1,55,21 +003-1-1,57,99,0 warp #003-1-1_57_99 0,0,003-1,56,84 +003-1-1,44,143,0 warp #003-1-1_44_143 0,0,008-0,41,23 +003-1-1,143,127,0 warp #003-1-1_143_127 0,0,003-1,115,111 diff --git a/npc/003-1-1/downpath.txt b/npc/003-1-1/downpath.txt new file mode 100644 index 0000000..b68bca4 --- /dev/null +++ b/npc/003-1-1/downpath.txt @@ -0,0 +1,32 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Notes: +// TW: Tulimshar, West +// TE: Tulimshar, East + +003-1-1,100,58,0 script #SewerDD-TW NPC_NO_SPRITE,{ + mesc l("There's a small, damp corritor, which you could crawl though."); + mesc l("It's not possible to see any light, and it seems to small and damp to have monsters."); + mesc l("You could barely fit on it, and your clothes will be ruined, in need of washing."); + next; + mesc l("Descend into the small corritor?"); + mesc l("Note: You'll be vulnerable for a short while!"), 1; + if (askyesno() == ASK_YES) { + closedialog; + sc_start SC_STUN, 3000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, getcharid(3); + sleep2(3000); + if (ispcdead()) end; + warp "003-1-3", 45, 36; + dispbottom l("At long last, you see the end of the corritor."); + RegEasterEgg(EE_SEWERS, 3); + end; + } + close; +} + +003-1-1,150,140,0 script #SewerDD-TE NPC_NO_SPRITE,{ + dispbottom l("The sewer mouth is locked."); + end; +} + diff --git a/npc/003-1-1/treasure.txt b/npc/003-1-1/treasure.txt new file mode 100644 index 0000000..59c2364 --- /dev/null +++ b/npc/003-1-1/treasure.txt @@ -0,0 +1,61 @@ +// TMW2 Script + +// (Random) Treasure Chest +// Authored by Jesusalva +// Regenerates every 6 hours + +003-1-1,0,0,0 script #chest_003110 NPC_CHEST,{ + + if (!.busy && !.empty) { + TreasureBox(); + + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + .dir = .dir == 0 ? 2 : 6; // closed ? opening : closing + .busy = true; // lock until available again + initnpctimer; + } else if (!.busy) { + mesc l("Someone looted this treasure box already..."); + } else { + end; + } + close; + +OnTimer160: + .dir = .dir == 6 ? 0 : 4; // closing ? closed : open + end; + +OnTimer500: + .busy = false; // unlock + if (.dir == 0 || .dir == 4) + stopnpctimer; // stop here if the chest is closed + end; + +OnInit: + .busy = false; + .distance = 2; + .empty = false; + +OnClock0156: +OnClock0756: +OnClock1356: +OnClock1956: + // Try to warp randomly to a walkable spot, up to 20 attempts + // Otherwise, it'll stay where it already is (but will close and refill). + .@e=0; .@x=0; .@y=0; + while (!checkcell(.map$, .@x, .@y, cell_chkpass)) + { + if (.@e == 20) { + .@x=.x; + .@y=.y; + break; + } + // Remember the +20 -20 margin adjustment + .@x = rand(20, 160); + .@y = rand(20, 150); + ++.@e; + } + .busy=false; + .empty=false; + movenpc .name$, .@x, .@y, 0; + end; +} diff --git a/npc/003-1-1/yetiking.txt b/npc/003-1-1/yetiking.txt new file mode 100644 index 0000000..55d2d89 --- /dev/null +++ b/npc/003-1-1/yetiking.txt @@ -0,0 +1,167 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Celestia Yeti King's quest. Designed so if you're with 4 players, all 4 can +// summon the Yeti King, helping you while doing the quest by themselves. + +// Notes: +// $@GM_OVERRIDE +// Only possible with @set command, overrides the co-op requeriment. + + +003-1-1,94,21,0 script #DahYetiKing NPC_SUMMONING_CIRC,{ + .@q=getq(HurnscaldQuest_Celestia); + if (.@q > 1 && .@q < 99) + setq HurnscaldQuest_Celestia, 1; + if (.@q == 1 && !.inUse) goto L_Summon; + if (!.@q) dispbottom l("I do not know how to trigger this summoning circle."); + end; + +L_Summon: + if (countitem(EverburnPowder) < 1 && $EVENT$ != "Celestia") { + dispbottom l("I need to pour the @@ to summon the Yeti King.", getitemlink(EverburnPowder)); + end; + } + if (getareausers("003-1-1", 5) < 2 && !$@GM_OVERRIDE) { + dispbottom l("I cannot be alone to summon the Yeti King."); + end; + + } + if ($EVENT$ != "Celestia") + delitem EverburnPowder, 1; + dispbottom l("Emoc otem itey gnik!"); // come to me yeti king, spelled backwards. Sorry. + callfunc "FYE_Olympics_CH"; + .inUse=1; + addtimer(9000, "#DahYetiKing::OnSummonTalk"); + initnpctimer; + end; + +L_Die: + npctalk3 l("You're playing with fire. Or ice. Or whatever."); + percentheal(-80, -100); + closedialog; + close; + +OnTimer1000: + setnpcdisplay .name$, NPC_YETI_KING; + end; + +OnTimer2000: + npctalk("Whom dares to disturb my slumber?!"); + end; + +OnTimer5000: + if (getareausers("003-1-1", 5) < 2 && !$@GM_OVERRIDE) { + npctalk("A lone adventurer? Pft. I'm back to my slumber!"); + .inUse=2; + } else { + npctalk("You're courageous to summon me, I'll give you that."); + } + end; + +// Now we check if time ran out (standard inUse) because cowardice, or if you tried +// to do the quest alone. And this is a Co-Op quest with free player numbers. +// Therefore, we must return the Yeti King to his summon circle. +OnTimer8000: + if (.inUse == 2) { + setnpcdisplay .name$, NPC_SUMMONING_CIRC; + .inUse=0; + stopnpctimer; + } + end; + +OnTimer60000: + if (.inUse == 1) { + setnpcdisplay .name$, NPC_SUMMONING_CIRC; + .inUse=0; + stopnpctimer; + } + end; + +// You can only talk to him with SummonTalk. Only one player will summon. +OnSummonTalk: + if (!.inUse) + end; + + .@q=getq(HurnscaldQuest_Celestia); + // If you had to return, erase quest progress + if (.@q > 1 && .@q < 99) + setq HurnscaldQuest_Celestia, 1; + + if (.@q != 1) + end; + mesn l("Dah Yeti King!!"); + mesq l("Why do you summon me? Speak."); + mes ""; + select + l("I'm sorry, these words just came to my mind."), + l("I did not summon you, I'm just a passer-by. Sorry."), + rif(.@q == 1, l("Celestia asks for your help.")); + + mes ""; + + if (@menu == 1) + goto L_Die; + if (@menu == 2) + close; + + mesn l("Dah Yeti King!!"); + mesq l("Yeah yeah yeah, you're not the first one to come talking about that to me."); + next; + mesn l("Dah Yeti King!!"); + mesq l("My answer is still a no, and it won't change. Don't test my patience any further."); + next; + select + l("You'll come with me!"), + l("I can prove you my worth!"), + l("Sorry! Sorry!"); + + mes ""; + if (@menu == 1) + goto L_Die; + if (@menu == 3) + close; + if (BaseLevel < 35 && !countitem(MirrorLakeArmor)) { + mesn l("Dah Yeti King!!"); + mesq l("You? Have you ever looked in the mirror? You're not even level 35. Begone."); + close; + } + + mesn l("Dah Yeti King!!"); + mesq l("Well, then I'll give you a task. We may meet again in Soren Village."); + next; + mesn l("Dah Yeti King!!"); + mesq l("I'll warp you to the Cave Of Trials. Pass all trials, and meet me on Soren's House. Hahah!"); + setq HurnscaldQuest_Celestia, 2; + @YetiKing_Challenger=1; + areatimer "003-1-1", 93, 20, 97, 25, 15000, "#DahYetiKing::OnWarper"; + npctalk l("Listen to me! Whoever wants to follow foolish @@ on their suicide quest, stay here for 15 seconds!", strcharinfo(0)); + close; + +OnWarper: + if (ispcdead()) + end; + // If you had to return, erase quest progress + if (.@q > 2 && .@q < 99) { + @YetiKing_Challenger=0; + } + + mesc l("Warp to the Cave Of Trials?"); + mesc l("There is no EXP penalty, but you cannot go back without either completing the cave, or dying."); + mesc l("If you die, you'll need to start over everything again!"); + if (askyesno() == ASK_YES) { + setq HurnscaldQuest_Celestia, 2; // This is a fix + warp "001-6", 27, 180; + } + setnpcdisplay .name$, NPC_SUMMONING_CIRC; + .inUse=0; + closedialog; + close; + +OnInit: + .sex = G_OTHER; + .distance = 1; + .inUse=0; // Prevent multiple summons and etc. + end; +} diff --git a/npc/003-1-2/_import.txt b/npc/003-1-2/_import.txt new file mode 100644 index 0000000..6237ae1 --- /dev/null +++ b/npc/003-1-2/_import.txt @@ -0,0 +1,4 @@ +// Map 003-1-2: Tulimshar Wall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-1-2/_mobs.txt", +"npc/003-1-2/_warps.txt", diff --git a/npc/003-1-2/_mobs.txt b/npc/003-1-2/_mobs.txt new file mode 100644 index 0000000..fbebe95 --- /dev/null +++ b/npc/003-1-2/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-1-2: Tulimshar Wall mobs +003-1-2,34,36,15,3 monster Black Scorpion 1074,2,35000,150000 diff --git a/npc/003-1-2/_warps.txt b/npc/003-1-2/_warps.txt new file mode 100644 index 0000000..6fd6be4 --- /dev/null +++ b/npc/003-1-2/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-1-2: Tulimshar Wall warps +003-1-2,33,34,0 warp #003-1-2_33_34 0,0,003-1,81,119 +003-1-2,19,34,0 warp #003-1-2_19_34 0,0,003-1,53,119 +003-1-2,48,34,0 warp #003-1-2_48_34 0,0,003-1,116,119 diff --git a/npc/003-1-3/_import.txt b/npc/003-1-3/_import.txt new file mode 100644 index 0000000..3c6504e --- /dev/null +++ b/npc/003-1-3/_import.txt @@ -0,0 +1,4 @@ +// Map 003-1-3: Magic School's Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-1-3/_mobs.txt", +"npc/003-1-3/_warps.txt", diff --git a/npc/003-1-3/_mobs.txt b/npc/003-1-3/_mobs.txt new file mode 100644 index 0000000..4dc3b00 --- /dev/null +++ b/npc/003-1-3/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-1-3: Magic School's Cave mobs +003-1-3,55,58,29,22 monster Cave Snake 1035,11,35000,150000 +003-1-3,56,52,34,19 monster Small Topaz Bif 1101,2,37000,160000 +003-1-3,54,54,54,54 monster Cave Maggot 1027,6,40000,200000 +003-1-3,50,52,22,23 monster Plushroom Field 1011,6,35000,250000 diff --git a/npc/003-1-3/_warps.txt b/npc/003-1-3/_warps.txt new file mode 100644 index 0000000..a556411 --- /dev/null +++ b/npc/003-1-3/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-1-3: Magic School's Cave warps +003-1-3,83,82,0 warp #003-1-3_83_82 0,0,003-1,59,54 +003-1-3,37,86,0 warp #003-1-3_37_86 0,0,003-1,25,46 diff --git a/npc/003-1/_import.txt b/npc/003-1/_import.txt new file mode 100644 index 0000000..b55813a --- /dev/null +++ b/npc/003-1/_import.txt @@ -0,0 +1,43 @@ +// Map 003-1: Tulimshar +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-1/_mobs.txt", +"npc/003-1/_warps.txt", +"npc/003-1/aahna.txt", +"npc/003-1/aidan.txt", +"npc/003-1/ched.txt", +"npc/003-1/constableperry.txt", +"npc/003-1/eomie.txt", +"npc/003-1/eugene.txt", +"npc/003-1/events.txt", +"npc/003-1/gladys.txt", +"npc/003-1/hasan.txt", +"npc/003-1/inac.txt", +"npc/003-1/inar.txt", +"npc/003-1/ishi.txt", +"npc/003-1/itka.txt", +"npc/003-1/jakod.txt", +"npc/003-1/jerican.txt", +"npc/003-1/lieutenantdausen.txt", +"npc/003-1/magic.txt", +"npc/003-1/mahoud.txt", +"npc/003-1/malivox.txt", +"npc/003-1/mapflags.txt", +"npc/003-1/mariusthebard.txt", +"npc/003-1/michel.txt", +"npc/003-1/neko.txt", +"npc/003-1/ninathetraveler.txt", +"npc/003-1/oldwell.txt", +"npc/003-1/quirino.txt", +"npc/003-1/sailors.txt", +"npc/003-1/sarah.txt", +"npc/003-1/sewer.txt", +"npc/003-1/ship.txt", +"npc/003-1/shop.txt", +"npc/003-1/silvia.txt", +"npc/003-1/soul-menhir.txt", +"npc/003-1/swezanne.txt", +"npc/003-1/taree.txt", +"npc/003-1/tinris.txt", +"npc/003-1/town.txt", +"npc/003-1/wateranimation.txt", +"npc/003-1/well.txt", diff --git a/npc/003-1/_mobs.txt b/npc/003-1/_mobs.txt new file mode 100644 index 0000000..e31299d --- /dev/null +++ b/npc/003-1/_mobs.txt @@ -0,0 +1,20 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-1: Tulimshar mobs +003-1,0,0,0,0 monster Maggot 1030,65,35000,450000 +003-1,81,103,2,3 monster Croc 1006,1,35000,90000 +003-1,72,70,7,4 monster Piou 1002,2,20000,20000 +003-1,85,136,8,11 monster Duck 1029,2,30000,20000 +003-1,110,145,7,4 monster Giant Maggot 1031,1,30000,20000 +003-1,67,121,2,3 monster Croc 1006,1,35000,90000 +003-1,39,101,8,11 monster Blub 1008,2,35000,150000 +003-1,25,73,8,25 monster Little Blub 1007,7,35000,150000 +003-1,37,70,3,13 monster Toppy Blub 1009,2,35000,150000 +003-1,64,58,4,1 monster Croc 1006,1,35000,90000 +003-1,109,24,9,4 monster Piou 1002,3,30000,20000 +003-1,82,116,9,11 monster Duck 1029,4,30000,20000 +003-1,109,45,12,13 monster Duck 1029,4,30000,20000 +003-1,110,42,4,1 monster Croc 1006,1,35000,90000 +003-1,70,106,6,3 monster Crocotree 1010,2,5000,150000 +003-1,63,76,3,4 monster Croc 1006,1,35000,120000 +003-1,99,75,2,7 monster Croc 1006,1,35000,120000 +003-1,67,27,3,4 monster Four Leaf 1028,1,30000,20000 diff --git a/npc/003-1/_warps.txt b/npc/003-1/_warps.txt new file mode 100644 index 0000000..a724dd2 --- /dev/null +++ b/npc/003-1/_warps.txt @@ -0,0 +1,26 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-1: Tulimshar warps +003-1,60,157,0 warp #003-1_60_157 4,0,004-1,69,57 +003-1,54,137,0 warp #003-1_54_137 0,0,003-4,39,38 +003-1,70,137,0 warp #003-1_70_137 0,0,003-9,62,135 +003-1,64,130,0 warp #003-1_64_130 0,0,003-9,36,56 +003-1,51,120,0 warp #003-1_51_120 0,0,003-1-2,19,35 +003-1,56,99,0 warp #003-1_56_99 0,0,003-3,39,40 +003-1,46,72,0 warp #003-1_46_72 0,0,003-2,32,40 +003-1,49,72,0 warp #003-1_49_72 0,0,003-2,35,40 +003-1,52,72,0 warp #003-1_52_72 0,0,003-2,38,40 +003-1,51,65,0 warp #003-1_51_65 0,0,003-2,42,34 +003-1,25,45,0 warp #003-1_25_45 0,0,003-1-3,37,85 +003-1,58,54,0 warp #003-1_58_54 0,0,003-1-3,82,82 +003-1,52,35,0 warp #003-1_52_35 1,0,003-0-1,48,51 +003-1,49,23,0 warp #003-1_49_23 0,0,003-0-1,22,25 +003-1,54,23,0 warp #003-1_54_23 0,0,003-0-1,69,24 +003-1,115,88,0 warp #003-1_115_88 0,0,003-8,41,28 +003-1,110,100,0 warp #003-1_110_100 0,0,003-8,31,43 +003-1,116,120,0 warp #003-1_116_120 0,0,003-1-2,48,35 +003-1,81,120,0 warp #003-1_81_120 0,0,003-1-2,33,35 +003-1,111,136,0 warp #003-1_111_136 0,0,003-7,39,40 +003-1,107,132,0 warp #003-1_107_132 0,0,003-7,29,31 +003-1,96,143,0 warp #003-1_96_143 0,0,003-5,38,40 +003-1,106,146,0 warp #003-1_106_146 0,0,003-6,32,38 +003-1,112,146,0 warp #003-1_112_146 0,0,003-6,40,38 diff --git a/npc/003-1/aahna.txt b/npc/003-1/aahna.txt new file mode 100644 index 0000000..8d88923 --- /dev/null +++ b/npc/003-1/aahna.txt @@ -0,0 +1,44 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Pookie +// Description: +// This NPC serves no purpose currently. She is here to make Tulimshar more crowded. +// Now a believer to announce Halinarzo and Alvasus Quest. +// TODO: During rainy days, she could ask for an Umbrella + +003-1,53,128,0 script Aahna NPC_ELVEN_FEMALE,{ + mesn; + mesq l("Hello, What a lovely day for a stroll."); + next; + mesn; + mesq l("I heard every Sunday there's a party at Halinarzo Church, but to get there you need to pass through the Desert Canyon."); + next; + mesn; + mesq l("I hope one day, they decide build a church here. It's a pain to travel there every time..."); + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; + +// Server Happy Hour +// Used to be Sunday 18:00~20:00 +// Then it is Sunday 16:00~20:00 +// Currently it is the whole weekend +OnSat0000: + if (debug) end; + $@EXP_EVENT=10; + $@EXP_EVENT_TIME=48; + donpcevent "@exprate::OnPlayerCall"; + end; + +OnSun0000: + disablenpc .name$; + end; + +OnMon0000: + enablenpc .name$; + end; +} diff --git a/npc/003-1/aidan.txt b/npc/003-1/aidan.txt new file mode 100644 index 0000000..400529d8 --- /dev/null +++ b/npc/003-1/aidan.txt @@ -0,0 +1,128 @@ +// TMW-2 Script +// Author: Crazyfefe, Jesusalva +// Desc: Originally a Tmw script + +003-1,95,97,0 script Aidan NPC_PLAYER_TONORI,{ + + function Register + { + mesn l("Aidan, the Monster Guide"); + mesq l("Oh my, you don't seem to be registered as a Monster Hunting Quest Participant. Would you like to register?"); + next; + mesn l("Aidan, the Monster Guide"); + mesq l("The register fee is 2000 GP."); + + do + { + select + rif(Zeny >= 2000, l("Register")), + rif(Zeny < 2000, l("Where do I get so much money?!")), + l("Not at the moment"), + l("Information"); + + switch (@menu) { + case 1: + mes ""; + Zeny=Zeny-2000; + MPQUEST=1; + mesn l("Aidan, the Monster Guide"); + mesq l("Give me a second to look over your paperwork."); + next; + mesn l("Aidan, the Monster Guide"); + mes l("\"Well, looks like you qualify!"); + mes l("Welcome to the questing world!\""); + close; + break; + case 2: + mes ""; + mesn l("Aidan, the Monster Guide"); + mesq l("Sell old equipment and items you won't use. For example, what should you do with a @@ or an @@? Sell it!", getitemlink(Ruby), getitemlink(ScorpionStinger)); + close; + break; + case 3: + mes ""; + mesn l("Aidan, the Monster Guide"); + mesq l("Very well, you don't know what you're missing."); + close; + break; + case 4: + mes ""; + mesn l("Aidan, the Monster Guide"); + mesq l("You see, because the Monster King, monsters have been running rampant. If they grow too much in numbers, cities may be overrun."); + next; + mesn l("Aidan, the Monster Guide"); + mesq l("Therefore, the Alliance created a system so when you kill a monster, depending on its strength, you'll get Monster Points."); + next; + mesn l("Aidan, the Monster Guide"); + mesq l("To prevent abuse, a registering fee is charged. Nothing major."); + next; + mesn l("Aidan, the Monster Guide"); + mesq l("So whaddaya say, sign up won't you?"); + next; + mes ""; + Register; + break; + } + } while (@menu != 4); + } + + if (BaseLevel < 10) goto L_Weak; + + if (MPQUEST == 0) + Register; + + mesn l("Aidan, the Monster Guide"); + mesq l("You currently have @@ Monster Points. These points are acquired while killing monsters.", Mobpt); + if (getq(General_Hunter) == 0 && !GHQUEST) goto L_Register; + if (getq(General_Hunter) == 0) goto L_Assign; + mes ""; + goto L_Assign; + close; // Will never be reached. + +L_Weak: + mesn; + mesq l("How did you even get here? Go back to Candor, where you belong!"); + percentheal -20, 0; + close; + +L_Register: + next; + mesn; + mesq l("The alliance also have a special program, called ##BGrand Hunter Quest##b, where you kill 10,000 of a monster and get great rewards."); + next; + mesn; + mesq l("You can gain rare treasures, even. Come register for this special program. It's free!"); + if (askyesno() == ASK_YES) { + GHQUEST=1; + setarray GHMEMO, 0, 0, 0; + mesn; + mesq l("Registered, welcome to the Grand Hunter Quest!"); + next; + goto L_Assign; + } else { + mes ""; + mesn; + mesq l("A pity..."); + close; + } + +L_Assign: + GHQ_Assign(Maggot, "Tulimshar"); + end; + + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyesT); + setunitdata(.@npcId, UDT_HEADMIDDLE, CopperArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 3); + setunitdata(.@npcId, UDT_HAIRCOLOR, 3); + + .sex = G_MALE; + .distance = 5; + end; +} + + diff --git a/npc/003-1/ched.txt b/npc/003-1/ched.txt new file mode 100644 index 0000000..5fb4602 --- /dev/null +++ b/npc/003-1/ched.txt @@ -0,0 +1,202 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Ched is from a quest. +// But as he is now unused, he'll become someone else on summer. + +003-1,62,48,0 script Ched NPC_PLAYER_TONORI,{ + function ValidSSC { + return countitem(CactusCocktail)+countitem(CherryCocktail)+countitem(AppleCocktail); + } + function ScoreSSC { + mesc l("All leaderboards are refreshed hourly."), 1; + mesc l("Your current score: @@", getq2(SQuest_Ched)), 3; + mes ""; + mes b(l("Top 10 - Summer Ched's Event")); + mes("1."+$@ched_name$[0]+" ("+$@ched_value[0]+")"); + mes("2."+$@ched_name$[1]+" ("+$@ched_value[1]+")"); + mes("3."+$@ched_name$[2]+" ("+$@ched_value[2]+")"); + mes("4."+$@ched_name$[3]+" ("+$@ched_value[3]+")"); + mes("5."+$@ched_name$[4]+" ("+$@ched_value[4]+")"); + mes("6."+$@ched_name$[5]+" ("+$@ched_value[5]+")"); + mes("7."+$@ched_name$[6]+" ("+$@ched_value[6]+")"); + mes("8."+$@ched_name$[7]+" ("+$@ched_value[7]+")"); + mes("9."+$@ched_name$[8]+" ("+$@ched_value[8]+")"); + mes("10."+$@ched_name$[9]+" ("+$@ched_value[9]+")"); + next; + } + function InfoSSC { + mesc l("@@ - @@ point(s)", getitemlink(CactusCocktail), "1"); + mesc l("@@ - @@ point(s)", getitemlink(CherryCocktail), "3"); + mesc l("@@ - @@ point(s)", getitemlink(AppleCocktail), "5"); + next; + mes ".:: " + l("Prizes") + " ::."; + mes getitemlink(MasterBola); + mesc l("Min. Position: ")+l("top 1"), 3; + mesc l("Min. Score: "+1000); + mes ""; + mes getitemlink(PiouBola); + mesc l("Min. Position: ")+l("top 3"), 3; + mesc l("Min. Score: "+1000); + mes ""; + mes getitemlink(SnakeBola); + mesc l("Min. Position: ")+l("top 5"), 3; + mesc l("Min. Score: "+700); + mes ""; + mes getitemlink(TulimsharBola); + mesc l("Min. Position: ")+l("top 7"), 3; + mesc l("Min. Score: "+400); + mes ""; + mes getitemlink(PurpleBola); + mesc l("Min. Position: ")+l("top 10"), 3; + mesc l("Min. Score: "+200); + mes ""; + mes getitemlink(CandorBola); + mesc l("Min. Position: ")+l("any"), 3; + mesc l("Min. Score: "+100); + mes ""; + mes getitemlink(KidBola); + mesc l("Min. Position: ")+l("any"), 3; + mesc l("Min. Score: "+25); + next; + } + function DepositSSC { + .@pts=.@pts+countitem(CactusCocktail)*1; + .@pts=.@pts+countitem(CherryCocktail)*3; + .@pts=.@pts+countitem(AppleCocktail)*5; + + delitem CactusCocktail, countitem(CactusCocktail); + delitem CherryCocktail, countitem(CherryCocktail); + delitem AppleCocktail, countitem(AppleCocktail); + + getexp rand2(.@pts-1, .@pts*11/10), rand2(0,.@pts/25); + + setq2 SQuest_Ched, @ched+.@pts; + @ched=getq2(SQuest_Ched); + mesc l("Gained @@ points.", .@pts), 3; + next; + mesc l("Your Score: @@", @ched), 1; + mes ""; + ScoreSSC; + closedialog; + goodbye; + close; + } + + // Begin: Ched + .@year=getq(SQuest_Ched); + if (.@year != (gettime(GETTIME_YEAR)-2000)) + setq SQuest_Ched, (gettime(GETTIME_YEAR)-2000), 0, 0; + + @ched=getq2(SQuest_Ched); + .@claimed=getq3(SQuest_Ched); // Required to prevent rewriting scoreboards + + if (season() == SUMMER && !$@GM_OVERRIDE) goto L_Summer; + if ((season() == AUTUMN && !.@claimed)) goto L_Autumn; + if (rand(0,10) == 6) + npctalk3("I wanted to go to the beach, but I can't find the cave entrance. They told me to look around here... What am I doing wrong?"); + else + hello; + end; + +// Summer Event +L_Summer: + if (BaseLevel < 25) { + mesn; + if (rand2(0,10) == 6) + mesq l("I wanted to go to the beach, but I can't find the cave entrance. They told me to look around here... What am I doing wrong?"); + else + mesq l("Get Rekt Noob."); + close; + } + + // Main Core + do + { + mesn; + mesc l("Current score: @@", @ched), 1; + mesc l("Thus far you have collected @@ @@, @@ @@ and @@ @@.", countitem(CactusCocktail), getitemlink(CactusCocktail), countitem(AppleCocktail), getitemlink(AppleCocktail), countitem(CherryCocktail), getitemlink(CherryCocktail)), 2; + mesc l("You can convert these items in event points and claim rewards at autumn."), 2; + next; + select + l("Scoreboards"), + l("Information"), + rif(ValidSSC(), l("Deposit all")), + l("Abort"); + mes ""; + if (@menu == 1) + ScoreSSC; + if (@menu == 2) + InfoSSC; + if (@menu == 3) + DepositSSC; + + } while (@menu < 3); + close; + +// Summer Quest Claim Rewards Time +L_Autumn: + mesc l("Your Score: @@", @ched), 1; + mes ""; + ScoreSSC; + + // Ensure you have free space on your inv. + inventoryplace NPCEyes, 1; + + // Are you entitled for a Boia? + if (@ched >= 25) { + mesc l("Boias, unlike common shields, does not have any penalty!"); + mesc l("They are filled with a strange gas which makes they deflect attacks. They are done from a material which cannot be cut easily."); + mesc l("They can be a bit lacking in defensive power, however."); + mes ""; + } + + .@pos=array_find($@ched_name$, strcharinfo(0)); + .@pos=(.@pos >= 0 ? .@pos+1 : 0); + + // Give you the due boia + if (.@pos <= 1 && @ched > 1000) + getitem MasterBola, 1; + else if (.@pos <= 3 && @ched >= 1000) + getitem PiouBola, 1; + else if (.@pos <= 5 && @ched >= 700) + getitem SnakeBola, 1; + else if (.@pos <= 7 && @ched >= 400) + getitem TulimsharBola, 1; + else if (.@pos <= 10 && @ched >= 200) + getitem PurpleBola, 1; + else if (@ched >= 100) + getitem CandorBola, 1; + else if (@ched >= 25) + getitem KidBola, 1; + + // Give extra on GP and EXP rewards on 2018 Summer due extensive amount of bugs + if (gettime(7) == 2018) + @ched=@ched*12/10; + + // Give you experience and money reward. + // Each cocktail is worth 15~30 gp, so we'll give 10 GP per point + Zeny+=@ched*10; + getexp BaseLevel*@ched, @ched; + + // The quest is complete for the year. + setq3 SQuest_Ched, 1; + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, SamuraiHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyesT); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/003-1/constableperry.txt b/npc/003-1/constableperry.txt new file mode 100644 index 0000000..6108095 --- /dev/null +++ b/npc/003-1/constableperry.txt @@ -0,0 +1,111 @@ +// TMW2 Scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Constable Perry invites players to the TMW2 Project +// Part of the THIEF/MERCHANT branches + +003-1,47,75,0 script Constable Perry NPC_MOUBOO,{ + if (getequipid(EQI_HEAD_TOP) == AFKCap) goto L_Quest; + +L_Intro: + mesn; + mesq l("Hello there! I am a constable. I keep law and order here."); + next; + mesq l("Yes, I am a mouboo. Why? Can't a mouboo be a law and order enforcer?!"); + next; + mesq l("Have you ever felt stuck? Lost? Didn't know about a quest, or an item is troubling you?"); + mesq l("Have no idea where in the world you are? Or what a certain foe drops, or if you should even dare to challenge it?"); + next; + mesn; + mesq l("Fear not! You can check our [@@https://wiki.moubootaurlegends.org|Wiki@@] to find that and other awesome stuff!"); + mesc l("(To see the rules, use ##B@rules##b.)"); + next; + mesq l("You can even join the project there. Contributors are greatly appreciated! %%N"); + close; + +L_Quest: + // Quest Requirement of 48 hours or idle before speaking for 60 min + if (AFKING < 57600 && checkidle() < 3600) { + npctalkonce l("What's that? They seem to be AFK but are not?"); + if (.@touchevent) + end; + else + goto L_Intro; + } + // No warning given + if (!@afkbotwarned) { + mesn; + mesq l("Stop right there, miscreant. I can see you're AFK, it is written on your hat!"); + next; + mesn; + mesq l("Moving while AFK is botting! I'll send you to a %s if you move while AFK! ##BYOU HAVE BEEN WARNED!##b", b(l("cold, bleak and isolated cell"))); + @afkbotwarned=true; + close; + } + // Now you've done it! + if (@afkbotwarned) + goto L_NowYouHaveDoneIt; + close; + +L_NowYouHaveDoneIt: + mesn; + mesq l("##BNOW YOU HAVE DONE IT!##b By the powers to me vested as a Constable and Game Master, I sentence you to HALF AN HOUR IN A %s!", b(strtoupper(l("cold, bleak and isolated cell")))); + next; + mesn; + mesq l("Do you have any last wishes before being JAILED for your CRIMES?!"); + next; + select + l("Please tell my mom I love her!"), + l("Pinkies forever! Oh yeah!"), + l("I did nothing wrong! I am not AFK!"), + l("Can I have a Blanket, at least?"), + l("Please tell everyone I am a rogue person who likes to break rules!"), + l("I am a thief! I want to repent for my sins!"), + l("No, sir."); + mes ""; + mesn; + switch (@menu) { + case 2: + mesq l("Hmph, you are the second activist I see today! TO THE BRIG!"); + break; + case 3: + mesq l("Every bot says that."); + break; + case 4: + .@q=getq(FrostiaQuest_AFKCap); + if (.@q == 2) { + mesq l("Uh, sure. We mouboos are not cruel. But it might not be your size."); + getitem Blanket, 1; + setq1 FrostiaQuest_AFKCap, 3; + } else { + mesq l("This is not the first time you've been arrested for AFK botting, so, ##BNO!##b"); + } + break; + case 5: + mesq l("Hmph, that would only incite bad behavior! No way!"); + break; + case 6: + mesq l("You have no proof that you have robbed vaults or sided with Ben Parkison, the Thieves Guild Master."); + break; + default: + mesq l("..."); + break; + } + atcommand("@jailfor 30mn "+strcharinfo(0)); + dispbottom l("Use %s to see how long you need to wait.", b("@jailtime")); + close; + +OnTouch: + .@touchevent=true; + if (getequipid(EQI_HEAD_TOP) == AFKCap) + goto L_Quest; + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/003-1/eomie.txt b/npc/003-1/eomie.txt new file mode 100644 index 0000000..8cf7f68 --- /dev/null +++ b/npc/003-1/eomie.txt @@ -0,0 +1,97 @@ +// TMW2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Part of Anwar Field quest +// Notes: Eomie did the bug bomb at Candor + +003-1,68,24,0 script Eomie NPC_ELF_F,{ + .@q=getq(TulimsharQuest_AnwarField); + if (.@q == 10) goto L_Gift; + if (.@q == 7) goto L_FirstAid; + if (.@q == 1) goto L_NotMe; + + hello; + end; + +L_NotMe: + mesn strcharinfo(0); + mesq l("Hello Ms. Eomie, kind sir Anwar sent me to fetch some fertilizers to save Tulimshar from famine, if you may?"); + next; + mesn; + mesq lg("Sorry kind lady @@, but no.", "Sorry kind sir @@, but no.", strcharinfo(0)); + next; + mesn; + mesq l("Or rather, I can't. I would love to help you, just like everybody else, but I don't know how to make fertilizers."); + next; + mesn; + mesq l("Tinris probably could do that, he is young but very talented. He is a greedy elf, but if you help him, he'll likely help you back."); + setq TulimsharQuest_AnwarField, 2; + close; + +L_FirstAid: + mesn; + mesq l("The crops are under attack? That's terrible!"); + next; + mesn; + mesq l("I can do a bug bomb right away, but I still need a few things for it!"); + next; + mesn; + mesq l("Do you, perchance, have 2 @@ and 3 @@?", getitemlink(ScorpionClaw), getitemlink(Moss)); + if (askyesno() != ASK_YES) + close; + mes ""; + + if (countitem(ScorpionClaw) < 2 || + countitem(Moss) < 3) { + mesn; + mesq l("The situation is too serious to you be lying... Please, go fetch the items..."); + close; + } + + delitem ScorpionClaw, 2; + delitem Moss, 3; + setq TulimsharQuest_AnwarField, 8; + + mesn; + mesq l("Quick, deliver this to Anwar!"); + close; + + +L_Gift: + .@q2=getq2(TulimsharQuest_AnwarField); + if (.@q2 & 2) { + mesn; + mesq l("Thanks for the nice gift!"); + close; + } + // Tip. WHAT DID YOU DID WITH THE BOUND ITEM? IT SHOULD BE HARD TO GET RID OF IT... + if (countitem(TortugaShell) < 1) { + mesn; + mesq l("Ah, I wish I got something for helping people out..."); + close; + } + mesn strcharinfo(0); + mesq l("Anwar sent you this, erm, hum... @@.", getitemlink(TortugaShell)); + next; + setq2 TulimsharQuest_AnwarField, .@q2+2; + delitem TortugaShell, 1; + getexp 75, 10; + mesn; + mesq l("WOW, THIS IS AWESOME! Many, many thanks!!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + //setunitdata(.@npcId, UDT_HEADTOP, PinkieHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, ValentineDress); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + //setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 10); + setunitdata(.@npcId, UDT_HAIRCOLOR, 12); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/003-1/eugene.txt b/npc/003-1/eugene.txt new file mode 100644 index 0000000..4891dc0 --- /dev/null +++ b/npc/003-1/eugene.txt @@ -0,0 +1,173 @@ +// TMW2 scripts. +// Authors: +// Reid +// Travolta +// Saulc +// Description: +// Fishman NPC +// Quest variable: +// TulimsharQuests_Fishman +// Quest stages: +// 0 - not started +// 1 - Eugene asked for items +// 2 - completed + +003-1,80,127,0 script Eugene NPC_EUGENE,{ + + narrator S_LAST_NEXT, + l("You see a raijin boy, sitting on the edge of the dock."), + l("He's holding a fishing rod, while gazing out at the sea."); + + .@q = getq(TulimsharQuests_Fishman); + if (.@q == 1) goto L_CheckItems; + if (.@q == 2) goto L_QuestDone; + + speech S_LAST_BLANK_LINE, + l("Ahoi."), + l("Hey, check out my brand new fishing rod. I bought it just today."), + l("I was so excited, I wanted to try it as soon as possible."), + l("So in a hurry, I forgot to take enough bait for fishing."), + lg("Be a friend and bring me @@ @@.", "Be a friend and bring me @@ @@.", .BaitCount, getitemlink(.BaitID)); + + switch (select(l("I'll be back in no time."), + l("Sorry, I'm doing other things at the moment."))) + { + case 1: + setq TulimsharQuests_Fishman, 1; + speech S_FIRST_BLANK_LINE, + l("Thank you. I'll wait here."); + next; + mesc l("Protip: @@ are dropped by @@. That monster helps each other, so don't attack when they are in packs.", getitemlink(.BaitID), getmonsterlink(LittleBlub)); + close; + case 2: + speech S_FIRST_BLANK_LINE, + l("But I'm almost out of @@...", getitemlink(.BaitID)); + close; + } + +L_CheckItems: + if (countitem(.BaitID) < .BaitCount) + { + speech + l("Sorry, but you don't have what I need."), + l("I need @@ @@.", .BaitCount, getitemlink(.BaitID)); + close; + } + + speech + l("That's exactly what I needed!"), + l("To thank you, accept my old fishing rod."), + l("It's not as good as my new one, but still very useful."), + l("Just look at that water! There's a whole bunch of fish down there."), + l("Oh, and you will need this book too, it will help you learn the basics of fishing."), + lg("You might even get lucky, and get a @@.", + "You might even get lucky, and get a @@.", getitemlink(GrassCarp)), + l("Have a good time fishing!"); + + delitem .BaitID, .BaitCount; + getitem FishingRod, 1; + getitem FishingGuideVolI, 1; + getexp 62, 5; + setq TulimsharQuests_Fishman, 2, 99, gettimeparam(GETTIME_DAYOFMONTH); + close; + +L_QuestDone: + // Time check/fix + .@q3=getq3(TulimsharQuests_Fishman); + if (.@q3 < gettimeparam(GETTIME_DAYOFMONTH)) + setq TulimsharQuests_Fishman, 2, 0, gettimeparam(GETTIME_DAYOFMONTH); + + .@maxcarps=1+(BaseLevel/8); + .@q2=getq2(TulimsharQuests_Fishman); + // Idea for future: Eugene telling fishman jokes. + speech + l("Ahoy, @@!", strcharinfo(0)), + l("Are the fish biting today?"); + + select + l("Yes, everything is going great, thank you!"), + l("Actually, I have bad luck. Could you sell me a box full of fresh fish?"); + + switch (@menu) { + case 1: + if (.@q2 < .@maxcarps) + goto L_Sidequest; + speech S_FIRST_BLANK_LINE, + l("Glad to hear. I swear, the fish I picked before you arrive was THAT big!"); + close; + case 2: + speech S_FIRST_BLANK_LINE, + l("Earlier I hadn't any, but now that I have the baits, I will be glad to sell some to you!"); + npcshopattach(.name$); + openshop; + closedialog; + } + close; + +L_Sidequest2: + mesn; + mesc l("Eugene bows politely, but he thinks he can use more."); + goto L_SidequestSub; + +L_Sidequest: + mesn; + mesq l("Maybe I'm using a too low quality bait, I can't fish %s.", getitemlink(GrassCarp)); + next; + +L_SidequestSub: + mesn; + mesq l("I offer you %d GP for one. What do you say?", .SuperPrice); + if (!countitem(GrassCarp)) + close; + if (askyesno() == ASK_NO) + close; + delitem GrassCarp, 1; + Zeny+=.SuperPrice; + setq2 TulimsharQuests_Fishman, .@q2+1; + .@q2=getq2(TulimsharQuests_Fishman); + + // Maybe you can repeat, if so, do this immediately + if (.@q2 < .@maxcarps) + goto L_Sidequest2; + + // Maxed for the day, give you a hint + mesn; + mesq l("Thanks. Maybe I should stop using Maggot Slime as a bait."); + close; + +OnInit: + .BaitID = SmallTentacles; + .BaitCount = 10; + // 600% profit (normal is 500%, but fishing is harder) + .SuperPrice = getiteminfo(GrassCarp, ITEMINFO_SELLPRICE)*6; + + tradertype(NST_MARKET); + sellitem SmallFishingNet, -1, 1; + sellitem FishBox, -1, 5; + sellitem CommonCarp, -1, 3; + + .sex = G_MALE; + .distance = 6; + end; + +OnClock0611: +OnClock1200: +OnClock1801: +OnClock0003: + restoreshopitem SmallFishingNet, 1; + restoreshopitem FishBox, 5; + restoreshopitem CommonCarp, 3; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} + diff --git a/npc/003-1/events.txt b/npc/003-1/events.txt new file mode 100644 index 0000000..d6843d1 --- /dev/null +++ b/npc/003-1/events.txt @@ -0,0 +1,499 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Controls events, part of Aurora Event System +// +// See also: functions/aurora.txt, functions/seasons.txt, command/event.txt, +// event.txt, functions/soulmenhir.txt and, of course, the event maps (if any). +// +// Script Variables: +// Q_AuroraEvent +// Quest Variable: FYEVENT_CYCLE, Score, ClaimedControl +// +// TODO: Use duplicate() command to make it available in other towns as well + +// Easter +003-1,47,53,0 script Aurora NPC_FEMALE,{ + function handleEaster; + function handleValentine; + function handleStPatrick; + + // Aurora Event functions + function auroraRankings; + function auroraCurrentRankings; + function auroraSubmit; + function auroraListRewards; + + // Handle annuals + //.@v_stday = getvariableofnpc(.valentine_stday, "#EventCore"); + //.@v_stmon = getvariableofnpc(.valentine_stmon, "#EventCore"); + .@v_endday = getvariableofnpc(.valentine_endday, "#EventCore"); + .@v_endmon = getvariableofnpc(.valentine_endmon, "#EventCore"); + + //.@e_stday = getvariableofnpc(.easter_stday, "#EventCore"); + //.@e_stmon = getvariableofnpc(.easter_stmon, "#EventCore"); + .@e_endday = getvariableofnpc(.easter_endday, "#EventCore"); + .@e_endmon = getvariableofnpc(.easter_endmon, "#EventCore"); + + .@dy=gettime(GETTIME_DAYOFMONTH); + .@mo=gettime(GETTIME_MONTH); + + // Debug Overrides + if ($@DEBUG_OD) + .@dy=$@DEBUG_OD; + if ($@DEBUG_OM) + .@mo=$@DEBUG_OM; + + // Annual rewards can only be claimed until the month ends + if ($EVENT$ == "Patrick") + handleStPatrick(); + else if (.@mo == .@v_endmon && .@dy > .@v_endday) + handleValentine(); + else if (.@mo == .@e_endmon && .@dy > .@e_endday) + handleEaster(); + + // Another event is going on, smoothly handle it + if ($EVENT$ != "") + goto L_Aurora; + else + mesc l("Currently, there is no event going on."), 1; + close; + + +// OnRestore causes OnInit to start again +OnRestore: + setnpcdisplay .name$, "Aurora", NPC_FEMALE; +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, MiniSkirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, RedStockings); + //setunitdata(.@npcId, UDT_HEADBOTTOM, BlueRoseHat); + setunitdata(.@npcId, UDT_WEAPON, UglyChristmasSweater); // (Blue) Bathrobe? + setunitdata(.@npcId, UDT_HAIRSTYLE, any(8, 8, 8, 20, 20, 11)); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + .sex = G_FEMALE; + .distance = 5; + end; + +// Override for Valentine Day - There should be no Aurora +OnValentine: + .@npcId = getnpcid(.name$); + setnpcdisplay .name$, "Demure#ValentineFinal", NPC_FEMALE; + //.@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, Cap); + setunitdata(.@npcId, UDT_HEADMIDDLE, RedStockings); + setunitdata(.@npcId, UDT_HEADBOTTOM, BunnyEars); + setunitdata(.@npcId, UDT_WEAPON, GMRobe); + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + end; + + + + + + + + + + + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +function handleEaster { + // Handle rewards before anything else + if (EASTER_YEAR != gettime(GETTIME_YEAR)) { + EASTER_YEAR=gettime(GETTIME_YEAR); + if (strcharinfo(0) == $@easter_name$[0]) { + makepet BhopFluffy; + mesn; + mesc l("For the first place in Easter, you gained a Bhopper Fluffy."), 3; + mesc l("Remember to give it a balanced diet of Aquadas to make it happy."), 3; + next; + } else { + .@pos=array_find($@easter_name$, strcharinfo(0)); + // 0 (aka top 1) is not an appliable winner + if (.@pos > 0) { + // Reverse it so top 10 value is 2, and top 2 value is 10. + .@pos=11-.@pos; + getitem StrangeCoin, .@pos*10; + } + } + } + mesn; + mesq l("Easter is over! I am the last chance to get rid of eggs!!"); + mesc l("Note: Golden and Silver Eggs are deleted after the next event end."), 1; + // Heartbeat + select + l("Trade Silver Eggs"), + l("Trade Golden Eggs"), + l("View LeaderBoard"), + l("Thanks Lilica."); + mes ""; + switch (@menu) { + case 1: + openshop "#eastershop1"; + closedialog; + break; + case 2: + openshop "#eastershop2"; + closedialog; + break; + case 3: + mesn l("Easter @@", gettime(GETTIME_YEAR)); + mes("1."+$@easter_name$[0]+" ("+$@easter_value[0]+")"); + mes("2."+$@easter_name$[1]+" ("+$@easter_value[1]+")"); + mes("3."+$@easter_name$[2]+" ("+$@easter_value[2]+")"); + mes("4."+$@easter_name$[3]+" ("+$@easter_value[3]+")"); + mes("5."+$@easter_name$[4]+" ("+$@easter_value[4]+")"); + mes("6."+$@easter_name$[5]+" ("+$@easter_value[5]+")"); + mes("7."+$@easter_name$[6]+" ("+$@easter_value[6]+")"); + mes("8."+$@easter_name$[7]+" ("+$@easter_value[7]+")"); + mes("9."+$@easter_name$[8]+" ("+$@easter_value[8]+")"); + mes("10."+$@easter_name$[9]+" ("+$@easter_value[9]+")"); + break; + } + return; +} + + + + + + + + + + + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +function handleValentine { + mesn; + mesq l("Valentine Day is over!"); + next; + mes "##B"+l("Top 10 - Valentine Day")+"##b"; + mes("1." +$@valentine_name$[0]+" ("+$@valentine_value[0]+")"); + mes("2." +$@valentine_name$[1]+" ("+$@valentine_value[1]+")"); + mes("3." +$@valentine_name$[2]+" ("+$@valentine_value[2]+")"); + mes("4." +$@valentine_name$[3]+" ("+$@valentine_value[3]+")"); + mes("5." +$@valentine_name$[4]+" ("+$@valentine_value[4]+")"); + mes("6." +$@valentine_name$[5]+" ("+$@valentine_value[5]+")"); + mes("7." +$@valentine_name$[6]+" ("+$@valentine_value[6]+")"); + mes("8." +$@valentine_name$[7]+" ("+$@valentine_value[7]+")"); + mes("9." +$@valentine_name$[8]+" ("+$@valentine_value[8]+")"); + mes("10."+$@valentine_name$[9]+" ("+$@valentine_value[9]+")"); + + if (#VALENTINE_SENT+#VALENTINE_OPENED <= 0) + return; + next; + + // Handle rewards + #VALENTINE_SENT=0; + #VALENTINE_OPENED=0; + #VALENTINE_RECEIVED=0; + copyarray(.@name$[0], $@valentine_name$[0], 10); + if (strcharinfo(0) == .@name$[0]) { + makepet DoggyDog; + getitem PrismGift, 1; + getitem StrangeCoin, 10; + } else if (strcharinfo(0) == .@name$[1] || strcharinfo(0) == .@name$[2]) { + getitem PrismGift, 1; + getitem GoldenGift, 1; + getitem StrangeCoin, 10; + } else if (strcharinfo(0) == .@name$[3] || strcharinfo(0) == .@name$[4]) { + getitem GoldenGift, 1; + getitem SilverGift, 1; + getitem StrangeCoin, 10; + } else if (strcharinfo(0) == .@name$[5] || strcharinfo(0) == .@name$[6]) { + getitem SilverGift, 1; + getitem BronzeGift, 1; + getitem StrangeCoin, 10; + } else if (strcharinfo(0) == .@name$[7] || strcharinfo(0) == .@name$[8] || strcharinfo(0) == .@name$[9]) { + getitem BronzeGift, 1; + getitem StrangeCoin, 10; + } else { + getitem StrangeCoin, 5; + } + + getexp #VALENTINE_SENT+#VALENTINE_RECEIVED, #VALENTINE_SENT; + Zeny=Zeny+#VALENTINE_OPENED; + + if (strcharinfo(0) == .@name$[0]) + mesc l("You gained a @@ for the #1 place on the event. Remember to feed it @@, or it may run away from you.", getitemlink(DoggyDog), getitemlink(AnimalBones)); + return; +} + + + + + + + + + + + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +function handleStPatrick { + // Check if St. Patrick day is over D: + if ($EVENT$ != "Patrick") + goto OnRestore; + // Okay, it is still St. Patrick :3 + mesn; + mesc l("It's St. Patrick Event!"), 3; + mes l("At 00:00, 06:00, 12:00, 15:00, 18:00 and 21:00 server time"); + mes l("Several special clovers will show up at forests."); + next; + mes l("They have 10x more chance to drop a @@, so it is a great deal!", getitemlink(FourLeafClover)); + mes l("Also, hidden in a forest which is not hot nor cold, is the Gold Pot Cauldron..."); + mes l("You can get daily something from it, but unless you're green like me, you will have no luck..."); + next; + return; +} + + + + + + + + + + + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +L_Aurora: + // Define script variables + .@WHAT$=l("event"); + + // Fill them with specific details, if available + if ($EVENT$ == "Expo") { + .@WHAT$=l("world expo"); + } else if ($EVENT$ == "Fishing") { + .@WHAT$=l("golden fish hunt"); + } else if ($EVENT$ == "Kamelot") { + .@WHAT$=l("kamelot raid"); + } else if ($EVENT$ == "Regnum") { + .@WHAT$=l("regnum's blessing"); + } else if ($EVENT$ == "Mining") { + .@WHAT$=l("miners union request"); + } else if ($EVENT$ == "Candor") { + .@WHAT$=l("candor battle season"); + } else if ($EVENT$ == "Celestia") { + .@WHAT$=l("yeti king hunt season"); + } else if ($EVENT$ == "Gemini") { + .@WHAT$=l("gemini season"); + } else if ($EVENT$ == "Rebirth") { + .@WHAT$=l("rebirth season"); + } else if ($EVENT$ == "Tower") { + .@WHAT$=l("dream tower apparition"); + } else if ($EVENT$ == "Raid") { + .@WHAT$=l("boss raid"); + } else if ($EVENT$ == "Olympics") { + .@WHAT$=l("magic olympics"); + } + + mesn; + mesq l("Hello! I am Aurora, and I oversee the %s!", .@WHAT$); + next; + mesn; + mesq l("So, how can I help you today?"); + do + { + next; + select + l("Event Details"), + l("Event Ranking Rewards"), + rif(FYEventUsesRanking(), l("Current Rankings")), + rif(FYEventUsesRanking(), l("List & Claim rewards")), + l("That's all, thanks!"); + mes ""; + switch (@menu) { + case 1: + EventHelp(); + if (FYEventUsesRanking()) { + mesc l("You must claim all rewards and use any event item BEFORE it ends."), 1; + mesc l("Left-overs will be deleted shortly after."), 1; + mesc l("Any eventual ranking reward will be sent by the banker's mail."), 1; + } else { + mesn; + mesq l("Have fun!"); + } + break; + + case 2: + auroraRankings(); + break; + + case 3: + auroraCurrentRankings(); + break; + + case 4: + auroraSubmit(); + auroraListRewards(); + break; + + default: + close; + } + // The code block is done + } while (true); +close; + + + + + + + + + + + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +function auroraRankings { + setnpcdialogtitle l("Aurora Events")+" - "+$EVENT$; + setskin "aurora_"+$EVENT$; + mes "There is no ranking information available for this event."; + select("Ok"); + setskin ""; + clear; + auroraCurrentRankings(); // More to silence a bug than whatelse + return; +} + + +function auroraCurrentRankings { + if (FYEventUsesRanking()) { + HallOfAurora(); + } else { + mesn; + mesq l("Personally, I like unranked events more than ranked ones..."); + } + return; +} + + +function auroraListRewards { + .@s=getq2(Q_AuroraEvent); + .@r=getq3(Q_AuroraEvent); + mesn; + freeloop(true); + for (.@i=0; .@i < getarraysize($FYREWARD_PT); .@i++) { + mes l("%s %d pts - %d %s", + (.@r > .@i ? "%%A" : (.@r == .@i ? "%%E" : "%%B")), // Status Indicator + $FYREWARD_PT[.@i], $FYREWARD_AM[.@i], getitemlink($FYREWARD_ID[.@i])); + // You're at this milestone? Hmm + if (.@r == .@i) { + // Your score is enough: rank you up + if (.@s >= $FYREWARD_PT[.@i]) { + inventoryplace $FYREWARD_ID[.@i], $FYREWARD_AM[.@i]; + getitem $FYREWARD_ID[.@i], $FYREWARD_AM[.@i]; + .@r+=1; + setq3 Q_AuroraEvent, .@r; + } + } + } + freeloop(false); + return; +} + + +function auroraSubmit { + .@q2=getq2(Q_AuroraEvent); + FYE_Submit(); + mesc l("Event score: %d -> %d", .@q2, getq2(Q_AuroraEvent)); + return; +} + + + + + + + + + + + + + + + + + + + + + + + +// DO NOT REMOVE +} + diff --git a/npc/003-1/gladys.txt b/npc/003-1/gladys.txt new file mode 100644 index 0000000..2262b92 --- /dev/null +++ b/npc/003-1/gladys.txt @@ -0,0 +1,99 @@ +// Author: +// Jesusalva + +003-1,63,105,0 script Gladys NPC_FEMALE_TONORI,{ + + mesn; + mesq l("Hello darling. The Alliance sucks, but don't tell anyone I told you that."); + mes ""; + menu + l("What is the Alliance?"), L_What, + l("Why does it suck?"), L_Why, + l("Well, too bad!"), L_Close; + +L_What: + mes ""; + mesn; + mesq l("What Alliance? The Alliance which rules over the World Of Mana!"); + next; + +L_AllianceMenu: + mes ""; + menu + l("Who composes the Alliance?"), L_Members, + l("What are the tasks of the Alliance?"), L_Tasks, + l("Do you think I could join the Alliance?"), L_Join, + l("I have to go. See you later."), L_Close; + +L_Why: + mes ""; + mesn; + mesq l("Because they control everything! They even determine taxes!"); + next; + mesq l("They also hold control over the Mana Stones, but this is not the problem."); + next; + mesq l("In the end, they decide the defensive measures, and they even set curfews!"); + next; + mesq l("They also determine rules, and enforce them. We live under a disguised dictatorship."); + next; + mesq l("But what I really hate is that they forbid gossiping."); + next; + goto L_AllianceMenu; + +L_Members: + mes ""; + mesn; + mesq l("Oh, every settlement is part of it. Candor, Tulimshar, Hurnscald, Frostia, Halinarzo, Artis, Nivalis..."); + next; + mesq l("Each settlement names a representative, which forms the Alliance Council. There is also the High Council, and the Magic Council."); + next; + mesq l("The Magic Council can be found at this city. They are the most powerful mages. If you have an impressive magical affinity, they may entitle you to have access to a Mana Stone."); + next; + mesq l("The Alliance Council rarely meets. It's thanks to them that the cities are well protected, if you want my opinion."); + next; + mesq l("The High Council acts in the Alliance stead, as it is not possible to reunite every city leader for all minor matters."); + next; + mesq l("Saulc, Crazyfefe and Jesus Saves are the three High Councillors. Don't do anything illegal while they're watching!"); + next; + goto L_AllianceMenu; + +L_Tasks: + mes ""; + mesn; + mesq l("Their main tasks are ensuring fair trading, protecting the cities from monster invasions, and guarding the Mana Stones."); + next; + mesq l("They also recently took over some city affairs. It's good they're protecting us, but I'm afraid of them creeping into our lives!"); + next; + goto L_AllianceMenu; + +L_Join: + mes ""; + mesn; + mesq l("Unless you are an exceptionally talented mage or an influentical politician, it is unlikely."); + next; + mesq l("The High Council's have some subordinates, but those are hand-picked by them."); + next; + mesq l("You could try getting into a city administration. If you can get a citizenship, you can try to elect for an office."); + next; + mesq l("Otherwise, you could ask the Constable. They act in the Alliance stead to enforce law and order, and may have... special arrangements for you."); + next; + goto L_AllianceMenu; + +L_Close: + mes ""; + mesq l("Good bye, darling!"); + close; + +OnInit: + .@npcId = getnpcid("Gladys"); + setunitdata(.@npcId, UDT_HEADTOP, TrapperHat); // Whaaaaaaaaat + setunitdata(.@npcId, UDT_HEADMIDDLE, ValentineDress); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 20); + setunitdata(.@npcId, UDT_HAIRCOLOR, 20); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/003-1/hasan.txt b/npc/003-1/hasan.txt new file mode 100644 index 0000000..658e4d3 --- /dev/null +++ b/npc/003-1/hasan.txt @@ -0,0 +1,224 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Hasan is the city bully and steals from the player. He lost his father to a Murderer +// Scorpion. Will respect player if they kill one. His mother is Sorfina, who +// explains this and cries that he never touched a Soul Menhir. Allows the player +// to kill the scorpion on Mahoud's Basement. +// +// Player must report theft first to Dausen, who will tell the player that's +// normal and nobody messes with Hasan, and giving player a pointer that they should +// tell his mother, Sorfina. +// +// Reward: Cotton Short, Hasan won't steal from you anymore. +// +// GETQ1 Variable Value; +// 0 - Hasan has never stolen from the player +// 1 - Hasan already stole from the player +// 2 - Dausen pointed player to Sorfina +// 3 - Sorfina unlocked Mahoud's Basement +// 4 - Player killed Murderer Scorpion +// 5 - Quest is complete +// +// $HASAN_ST +// Number of Scorpions killed during Autumn +// $HASAN_GP +// Total money Hasan collected for Autumn event +// $HASAN_LT +// Previous collected money, for rewards calc +// +// PS. $@GM_OVERRIDE will disable Season restrictions, but values from 20/09 and 21/12 +// must be set manually. + +// Temporary sprite +003-1,62,148,0 script Hasan NPC_ELVEN_MAN_RED,2,2,{ + if (getq(TulimsharQuest_Hasan) == 0) goto L_Safe; + if (getq(TulimsharQuest_Hasan) == 4) goto L_Finish; + if (getq(TulimsharQuest_Hasan) >= 5) goto L_Complete; + mesn strcharinfo(0); + mesq l("Give me back what you stole, thief!"); + next; + mesn; + mesq l("I stole nothing from you. Do you have any proof?"); + next; + menu + l("I have a print screen!"), L_Print, + l("No..."), L_Close; + close; + +L_Away: + mesn; + mesq l("Hey... Go bother someone else."); + close; + +L_Print: + mes ""; + mesn; + mesq l("Let me analyze that."); + mesc l("Hasan takes your print screen and analyzes it."); + next; + mesn; + mesq l("Photoshopped. Definitely. Good luck convincing somebody with that! %%a"); + next; + mesn; + mesq l("If you go to Lieutenant Dausen, he'll say that you used GIMP. Just give up."); + close; + +// TODO +L_Finish: + mesn; + // The scorpion and not a scorpion because of dramatic buildup. + mesq l("Is that... Wait... Wow. You... Killed the murderer scorpion."); + next; + mesn; + mesq l("I promise I'll never steal from you again. Here, take these shorts."); + mesq l("Good job, man!"); + setq TulimsharQuest_Hasan, 5; + getitem CottonShorts, 1; + getexp 99, 55; + close; + +L_Complete: + mesn; + mesq l("Thanks for avenging my father."); + next; + mesn; + mesq l("I am collecting money for the Yearly Autumn Scorpion Hunter quest."); + mesq l("You're, of course, invited. Thus far, the total prize money I've collected is @@ GP.", $HASAN_GP); + if (season() == AUTUMN || $@GM_OVERRIDE || is_staff()) { + mesq l("You've killed @@ scorpions, and a total of @@ were killed this season.", fnum(getq2(SQuest_Autumn)), fnum($HASAN_ST)); + mesq l("If the event ended now, you would get @@ GP.", getq2(SQuest_Autumn)*$HASAN_GP/$HASAN_ST); + mes ""; + mesc l("Note: Poll may increase more than total money donated depending on current date."); + menuint + l("Thanks."), 0, + rif(Zeny >= 5, l("Donate 5 GP for prize")), 5, + rif(Zeny >= 15, l("Donate 15 GP for prize")), 15, + rif(Zeny >= 50, l("Donate 50 GP for prize")), 50, + rif(Zeny >= 100, l("Donate 100 GP for prize")), 100, + rif(Zeny >= 1000, l("Donate 1k GP for prize")), 1000, + rif(Zeny >= 10000, l("Donate 10k GP for prize")), 10000, + rif(Zeny >= 25000, l("Donate 25k GP for prize")), 25000, + rif(Zeny >= 100000, l("Donate 100k GP for prize")), 100000, + rif(Zeny >= 250000, l("Donate 250k GP for prize")), 250000; + if (@menuret > 0) { + Zeny=Zeny-@menuret; + .@poll=@menuret; + .@bonus=10000; + .@bonus+=rand(gettime(GETTIME_MONTH) == DECEMBER ? 2000 : 4000); + .@bonus+=(gettime(GETTIME_MONTH) == DECEMBER ? 0 : 1000); + .@poll=.@poll*.@bonus/10000; + $HASAN_GP=$HASAN_GP+.@poll; + } + } + + .@q=getq(SQuest_Autumn); + if (season() == WINTER && (.@q == gettime(GETTIME_YEAR) || (.@q == gettime(GETTIME_YEAR)-1 && gettime(GETTIME_MONTH) <= MARCH))) { + .@p=getq2(SQuest_Autumn)*$HASAN_LT/$HASAN_ST; + mesn l("Summary"); + mes l("Scorpions killed: @@/@@ scorpions", getq2(SQuest_Autumn), $HASAN_ST); + mes l("Total money collected: @@ GP", $HASAN_LT); + mesc l("Your prize: %s GP", fnum(.@p)), 2; + if (getq2(SQuest_Autumn) > 20) + getexp (getq2(SQuest_Autumn)/20), 0; + if (.@p >= 1) + Zeny+=.@p; + + // Top 5 reward + if (strcharinfo(0) == $@hasn_name$[0]) + getitem SupremeGift, 1; + if (strcharinfo(0) == $@hasn_name$[1]) + getitem PrismGift, 1; + if (strcharinfo(0) == $@hasn_name$[2]) + getitem GoldenGift, 1; + if (strcharinfo(0) == $@hasn_name$[3]) + getitem SilverGift, 1; + if (strcharinfo(0) == $@hasn_name$[4]) + getitem BronzeGift, 1; + + // Quest is closed + setq1 SQuest_Autumn, 1970; + next; + mes ".:: Scoreboards ::."; + mesf("1. %s (%d)", $@hasn_name$[0], $@hasn_value[0]); + mesf("2. %s (%d)", $@hasn_name$[1], $@hasn_value[1]); + mesf("3. %s (%d)", $@hasn_name$[2], $@hasn_value[2]); + mesf("4. %s (%d)", $@hasn_name$[3], $@hasn_value[3]); + mesf("5. %s (%d)", $@hasn_name$[4], $@hasn_value[4]); + } + close; + +L_TouchComplete: + if (@hasan_antiflood < gettimetick(2)) { + npctalkonce l("Hey there, @@! Good luck killing monsters!", strcharinfo(0)), 3; + @hasan_antiflood=gettimetick(2)+600; + } + end; + +L_Close: + close; + +OnTouch: + if (getq(TulimsharQuest_Hasan) >= 5) goto L_TouchComplete; + if (Zeny > 15) goto L_Steal; + end; + +L_Steal: + npctalkonce(l("*whistles*")); + if (getq(TulimsharQuest_Hasan) == 0) goto L_DoSteal; + if (getq(TulimsharQuest_Hasan) <= 4 && rand(0,100) < 20) goto L_DoSteal; + end; + +L_DoSteal: + .@s=rand(3,12); + $HASAN_GP=$HASAN_GP+.@s; + Zeny=Zeny-.@s; + if (getq(TulimsharQuest_Hasan) == 0) + setq TulimsharQuest_Hasan, 1; + dispbottom l("##BYou were robbed##b by an evil NPC."); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + + //npcspeed(150); + //npcwalkto(<x>, <y>); + end; + +// Restart hasan status on 20/09 (double-sure) +OnDay0920: + $HASAN_ST=0; + end; + +// Once autumn is over (21/12) restart GP poll and send value to backup +OnDay1221: + $HASAN_LT=0+$HASAN_GP; + $HASAN_GP=2000+(TOP3AVERAGELVL()*100); + end; +} + +function script SQuest_Hasan { + if (season() != AUTUMN && !$@GM_OVERRIDE) + return; + if (getq(SQuest_Autumn) != gettime(GETTIME_YEAR)) + setq SQuest_Autumn, gettime(GETTIME_YEAR), 0; + + // All scorpions are counting for Hasan Autumn Quest + if ( + killedrid == Scorpion || + killedrid == RedScorpion || + killedrid == BlackScorpion || + killedrid == CandorScorpion || + killedrid == AngryScorpion || + killedrid == AngryRedScorpion || + killedrid == NightScorpion || + killedrid == GoldenScorpion || + killedrid == MurdererScorpion) { + setq2 SQuest_Autumn, getq2(SQuest_Autumn)+1; + $HASAN_ST=$HASAN_ST+1; + } + return; +} + diff --git a/npc/003-1/inac.txt b/npc/003-1/inac.txt new file mode 100644 index 0000000..45cc335 --- /dev/null +++ b/npc/003-1/inac.txt @@ -0,0 +1,142 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Inac Give a sharp knife for 20 Piou Feathers to player over level 12, +// before Tycoon give it but only bafter level 18 that was too high ! + +003-1,55,82,0 script Inac NPC_PLAYER_TONORI,{ + + .@Inac = getq(TulimsharQuest_Inac); + if (BaseLevel < 12) goto L_TooWeak; + if (.@Inac == 1) goto L_Check; + if (.@Inac == 2) goto L_Complete; + +L_GiveTask: + mesn; + mesq lg("Hello, wanderer! Welcome to Tulimshar."); + next; + mesq l("Was it Nard who brought you here?"); + next; + + menu + l("Yeah, Nard's sailors saved my life and brought me here!"), L_Quest, + l("I need to go."), L_Quit; + +L_Quest: + mes ""; + mesn; + mesq l("Quite benevolent, they are. Nard likes to help anyone who works hard. Anyway, there was something important I needed to say."); + next; + mesn; + .@g$=lg("girl", "boy"); + mesq l("Tulimshar is surrounded by strong monsters. If you go out with your poor equipment, you're going to be dead in no time, @@.", .@g$); + tutmes l("You can use \"@monsterinfo <monster name>\" to gauge a monster strength."), l("Protip"), false; + next; + mesn; + mesq l("I probably can give you my Sharp Knife, though! If you want to help me, that is."); + next; + + menu + l("Really? That would be great!"), L_Start, + l("I better do this some other time..."), L_Quit; + + +L_Start: + setq TulimsharQuest_Inac, 1; + mes ""; + mesn; + mesq l("Well, if you come from Candor, you probably will have some fluffy's yellow feathers. I use them to make pillows."); + goto L_List; + +L_Quit: + mes ""; + mesn; + mesq l("Have a good day, then."); + close; + +L_List: + mes ""; + mesn; + mes l("I can give you my knife if you bring me this:"); + mes l("@@/20 @@", countitem(PiouFeathers), getitemlink(PiouFeathers)); + close; + +L_Check: + mesn; + mesq l("Did you brought me the feathers?"); + next; + menu + l("Yes! Here you are."), L_Give, + l("I forgot what you need!"), L_List, + l("Later."), L_Quit; + +L_Give: + if ( + countitem(PiouFeathers) < 20 + ) goto L_Lying; + + inventoryplace SharpKnife, 1; + + delitem(PiouFeathers, 20); + + getitem(SharpKnife, 1); + getexp(200, 5); + setq(TulimsharQuest_Inac, 2); + + mes ""; + mesn; + mesq l("Enjoy this new weapon, I hope you're successful."); + close; + +L_Complete: + mesn; + mesq l("Many stories are told about this city sewers."); + next; + mesn; + mesq l("One of them is really crazy, though: They say that there is a legendary monster down there."); + next; + mesn; + mesq l("That's just nonsense though, I've been there thousands of times and saw no such thing."); + if (getq(TulimsharQuest_Sewers) > 0) + close; + next; + mesn; + mesq l("Anyway, there's a barrier to prevent monsters from attacking the city from below."); + next; + mesn; + mesq l("To enter on them, you need to be level 25 and use the following chant: \"Blame Saulc\". I don't know why, but that open doors!"); + setq TulimsharQuest_Sewers, 1; + close; + +L_Lying: + mesn; + mesq l("No no no, that's wrong."); + next; + mesc l("I need more feathers for a fair deal."); + next; + goto L_List; + +L_TooWeak: + mesn; + mesq l("Hello little kid. Watch out, there's a maggot behind you %%a !"); + tutmes l("You need at least level %s to do this quest.", b(12)), l("WARNING"), false; + close; + +OnShout: + npctalk3 l("Ahoy! Hey, you, new person! Could you come here?!"); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, Bandana); + setunitdata(.@npcId, UDT_HEADMIDDLE, VneckJumper); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/003-1/inar.txt b/npc/003-1/inar.txt new file mode 100644 index 0000000..79c7753 --- /dev/null +++ b/npc/003-1/inar.txt @@ -0,0 +1,46 @@ +// TMW2 Script +// Author: +// Saulc + +003-1,109,109,0 script Inar NPC_PLAYER_TONORI,{ + + mesn; + mesq l("Only finest wares!"); + mes ""; + menu + l("Trade"), -, + l("Leave"), L_Close; + + closedialog; + shop2 "Shop#bazar1"; + close; + +L_Close: + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyesT); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + //setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); // TODO + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 4); + setunitdata(.@npcId, UDT_HAIRCOLOR, 1); + + .sex = G_MALE; + .distance = 5; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} diff --git a/npc/003-1/ishi.txt b/npc/003-1/ishi.txt new file mode 100644 index 0000000..ed16ed3 --- /dev/null +++ b/npc/003-1/ishi.txt @@ -0,0 +1,232 @@ +// TMW-2 Script +// Author: +// Crazyfefe +// Jesusalva +// +// Do not add void items to the array. +// note: the rare item system sucks. + +003-1,97,97,0 script Ishi NPC_PLAYER_ARGAES,{ + + if (MPQUEST == 0) { + mesn l("Ishi, the Rewards Master"); + mesq l("Hey, it seems like you didn't register as a Monster Hunting Quest participant yet! You can sign up with Aidan."); + close; + } + if (BaseLevel < 10) { + // TODO: make jail stuff a function and replace this with the new function + dispbottom l("##1You abused a bug and will be jailed. If this was done in error, you have found a bug. Contact the nearest developer if this is the case."); + atcommand "@jailfor 5mn "+strcharinfo(0); + end; + } + + if (BaseLevel < 37) { + @mpq_cost=((BaseLevel*2/3) ** 2); + } else if (BaseLevel <= 50) { + // From level 37 onwards, we will notice a small drop on price increase factor (until level 50) + @mpq_cost=((BaseLevel*2/3) ** 2)-(BaseLevel*2); + } else { + // After level 50, the formula changes. We don't have exponential anymore. + // Previous increase reached 67 and will stop at this value. + @mpq_cost=((50*2/3) ** 2)-(50*2); + @mpq_cost+=(BaseLevel-50)*67; + // Of course... We still need to act as if exponent was still there... + // So we raise it a bit each 3 levels + @mpq_cost+=(BaseLevel/3)*3; + } + + if (Mobpt < @mpq_cost) + { + mesn l("Ishi, the Rewards Master"); + mesq l("Welcome! I see you have %d Monster Points. But that isn't enough to get items at your current level, sorry!", Mobpt); + mesc l("I need at minimum %d Monster Points to get items at current level.",@mpq_cost); + close; + } + + if (BaseLevel < 25) { + setarray @Items, Bread, + Candy,Orange,BugLeg, + CobaltHerb,GambogeHerb, + MauveHerb,MaggotSlime,ScorpionStinger,SilkCocoon, + RustyKnife,Coral,PiouLegs,Cheese,RoastedMaggot,RawLog; + } else { + setarray @Items, Bread, Croconut,Plushroom, + RedApple,Beer,Candy,Orange,ChocolateBar,BugLeg,CoinBag, + Coal,SnakeSkin,CottonCloth,GrassSeeds,HardSpike,CobaltHerb,GambogeHerb, + MauveHerb,CopperOre,MaggotSlime,RawLog,ScorpionStinger,SilkCocoon,TreasureKey, + WhiteFur,EmptyBottle,RustyKnife,Coral,PiouLegs,Cheese,SnakeEgg,RoastedMaggot,BlueDye; + } + setarray @Rares, BrimmedHat, 5, IronIngot, 10, BronzeGift, 10, ScrollSMaggot, 15, MercBoxB, 15; + + // Push blueprints based on your Job Level + array_push(@Rares, AlchemyBlueprintA); + array_push(@Rares, (JobLevel/3)); + array_push(@Rares, EquipmentBlueprintA); + array_push(@Rares, (JobLevel/3)); + + // Additional Loot for level 45+ + if (BaseLevel >= 45) { + array_push(@Items, IronOre); + array_push(@Items, SilverOre); + array_push(@Items, TinOre); + array_push(@Items, ChocolateMouboo); + array_push(@Items, MoubooSteak); + array_push(@Items, Milk); + array_push(@Items, TolchiAmmoBox); + array_push(@Items, ShadowHerb); + + array_push(@Rares, AlchemyBlueprintB); + array_push(@Rares, (JobLevel/6)); + array_push(@Rares, EquipmentBlueprintB); + array_push(@Rares, (JobLevel/6)); + } + + // Additional Loot for level 75+ + if (BaseLevel >= 75) { + array_push(@Items, LeadOre); + array_push(@Items, TitaniumOre); + array_push(@Items, IridiumOre); + array_push(@Items, GoldOre); + array_push(@Items, IronAmmoBox); + + array_push(@Rares, TulimWarpCrystal); + array_push(@Rares, 40); + array_push(@Rares, MercBoxA); + array_push(@Rares, 40); + array_push(@Rares, AlchemyBlueprintC); + array_push(@Rares, (JobLevel/9)); + array_push(@Rares, EquipmentBlueprintC); + array_push(@Rares, (JobLevel/9)); + } + + mesn l("Ishi, the Rewards Master"); + // Highlight the amount of monster points in bold for better visibility since players will probably exchange monster points a lot + mesq l("Welcome! I see you have ##B%d Monster Points##b. Would you like to exchange some of those for items?", Mobpt); + next; + if (@mpq_cost > 0) + mesc l("You can get up to %d items.", (Mobpt/@mpq_cost)); + + menuint + rif(Mobpt >= @mpq_cost, "1"), 1, + rif(Mobpt >= (@mpq_cost)*2, "2"), 2, + rif(Mobpt >= (@mpq_cost)*3, "3"), 3, + rif(Mobpt >= (@mpq_cost)*4, "4"), 4, + rif(Mobpt >= (@mpq_cost)*5, "5"), 5, + rif(Mobpt >= (@mpq_cost)*6, "6"), 6, + rif(Mobpt >= (@mpq_cost)*7, "7"), 7, + rif(Mobpt >= (@mpq_cost)*8, "8"), 8, + rif(Mobpt >= (@mpq_cost)*9, "9"), 9, + rif(Mobpt >= (@mpq_cost)*10,"10"), 10, + rif(Mobpt >= (@mpq_cost)*11,l("Gimme as many as I deserve!")), -1, + l("Sorry, I have to go now."), 0; + + .@var=@menuret; + + // Special cases + if (!.@var) + goto L_Close; + if (.@var < 0) + goto L_Give_all; + + goto L_Items; + +L_Close: + .@var=0; + closedialog; + goodbye; + close; + +L_Items: + //debugmes "Reaching item loop"; + freeloop(true); + for (.@i = 0; .@i < .@var; .@i ++) + { + //debugmes "Items: "+str(.@var); + .@lucked=0; + .@reward=0; + + if (BaseLevel > 25) { + //debugmes "Testing rares"; + @lucky = rand(10000) + 1; + for (.@b = 0; .@b < getarraysize(@Rares); .@b=.@b+2) { + //debugmes "Checking "+@Rares[.@b]+" - b is now "+.@b; + //debugmes l("Check @@ <= @@", @lucky, @Rares[.@b+1]); + if (.@b == 0) + @control = 0; + if(@lucky >= (@control + 1) && @lucky <= @control + (@Rares[.@b+1])) + { + .@lucked=1; + .@reward = @Rares[.@b]; + } + @control = @control + (@Rares[.@b+1]); + } + } + + //debugmes "Setting reward"; // could be if (!.@lucked) but for sanity... + if (!.@reward) + .@reward=any_of(@Items); + + //debugmes "Check weight"; + inventoryplace .@reward, 1; + + //debugmes "Processing..."; + Mobpt = Mobpt - @mpq_cost; + getitem .@reward,1; + + //debugmes "Printing..."; + if (.@lucked) { + mes ""; + mes l("Wow!"); + mes l("I can't believe it."); + mes l("You got lucky and got a(n) %s!", getitemlink(.@reward)); + mes ""; + } else { + mesq l("You received one %s!", getitemlink(.@reward)); + } + + } + freeloop(false); + close; + +L_Continue: + mesq l("You still have ##B%d Monster Points##b! Do you want more items?", Mobpt); + select + l("Yes"), + l("No"); + + switch (@menu) + { + case 1: + goto L_Give_all; + break; + case 2: + goto L_Close; + break; + } + close; + +L_Give_all: + .@var = Mobpt / @mpq_cost; + if (.@var > 50) { // limit to avoid lag server. Probably a bad idea. + .@var = 50; + mes l("You have too many points. I can't allow you to take all at once right now. I'll try to give you 50, and you can come back later!"); + next; + } + + goto L_Items; + close; + +OnInit: + .@npcId = getnpcid(.name$); + //setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, CopperArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 4); + setunitdata(.@npcId, UDT_HAIRCOLOR, 13); + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/003-1/itka.txt b/npc/003-1/itka.txt new file mode 100644 index 0000000..e6ac006 --- /dev/null +++ b/npc/003-1/itka.txt @@ -0,0 +1,51 @@ +// TMW2 Script. +// Author: +// Jesusalva + +003-1,63,141,0 script Itka NPC_F_SHOPKEEPER,{ + + mesn; + mesq lg("Hello, madam!", "Hello, sir!"); + next; + mesq l("Did you knew the casino on the inn up here was one of the most expensive on the whole world?"); + next; + mesq l("Anyway, I am selling Cherry Cakes to sponsor my studies. Please buy as many as you want!"); + tutmes l("Ducks and Giant Maggots also drops Cherry Cakes."), l("Protip"), false; + next; + npcshopattach(.name$); + openshop; + closedialog; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, VneckSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // I prefer 1801 but the short doesn't match + setunitdata(.@npcId, UDT_HAIRSTYLE, 17); + setunitdata(.@npcId, UDT_HAIRCOLOR, 19); + + // NOTE: To limit selling items must change trader type to NST_MARKET + tradertype(NST_MARKET); + sellitem CherryCake, 200, 50; // 50 cakes for 200 GP each + + .sex = G_FEMALE; + .distance = 5; + end; + +OnMinute16: + restoreshopitem CherryCake, 200, 50; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} diff --git a/npc/003-1/jakod.txt b/npc/003-1/jakod.txt new file mode 100644 index 0000000..bc3d703 --- /dev/null +++ b/npc/003-1/jakod.txt @@ -0,0 +1,77 @@ +// TMW2 script. +// Author: +// Saulc +// Jesusalva +// Description: +// Jakod replaces Luca. As there are no focus skills, Jakod teaches player about +// magic and is their tutor on the matters of the Mana Stone. + +003-1,109,129,0 script Jakod NPC_PLAYER_TONORI,{ + mesn; + if (BaseLevel < $MANA_BLVL) mesq l("Hello there. I am looking for strong people, but you are not strong enough."); + if (BaseLevel < $MANA_BLVL) close; + + if ($EVENT$ == "Rebirth") { + mesc l("Are you looking for Rebirth? Limited time only!"), 1; + if (askyesno() == ASK_YES) { + doevent "Phoenix Rebirth::OnMain"; + closeclientdialog; + end; + } + clear; + } + + mes l("\"Ah, hello there, @@. You've grown quite skilled lately.", strcharinfo(0)); + if (MAGIC_LVL) { + mes l("I also see you have a level @@ magic skill!\"", MAGIC_LVL); + next; + mes l("Did you got an @@ yet? That book have great insight on several details concerning mana and its usages!", getitemlink(JesusalvaGrimorium)); + next; + mes l("Anyway, you can see how skilled using Mana Magic with @@. Mana Magic is the one which requires concentration, to bring things into existence, for example.", b("@abizit")); + next; + mes l("Well, if you ever need help, do not hesit reading the wiki ;-)"); + close; + } else { + mes l("But you lack magic! That's a pity.\""); + select + l("How do I get magic?"), + l("Good bye."); + if (@menu == 2) + close; + mes ""; + } + + mes ""; + mesn; + mesq l("Well, the first thing would be to get access to a Mana Stone. Halinarzo had the biggest mana stone mines, but it's now depleted."); + next; + mesn; + mes l("\"That's the only hard part. As long that you do not neglect Intelligence nor Job level..."); + mes l("...If you're lucky, you can touch it and receive magic power.\""); + next; + mesn; + mesq l("Having magic power is useless by itself, so you must visit the Academy. I guess that involves approval and acknowledgment of the Mage Council that you are a mage!"); + next; + mesn; + mesq l("Keep in mind, the more levels and intelligence you have, more likely the Mana Stone will grant you more Magic Power. But that means nothing."); + next; + mesn; + mesq l("Because in the end, you are in the hands of your class leader!"); // Rare: Some loner NPCs may grant you EXTRA skills beyond that. + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyesT); + setunitdata(.@npcId, UDT_HEADMIDDLE, ForestArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 13); + //setunitdata(.@npcId, UDT_HAIRSTYLE, 24); + //setunitdata(.@npcId, UDT_HAIRCOLOR, 2); + + .sex = G_MALE; + .distance = 3; + npcsit; + end; +} diff --git a/npc/003-1/jerican.txt b/npc/003-1/jerican.txt new file mode 100644 index 0000000..e25cab1 --- /dev/null +++ b/npc/003-1/jerican.txt @@ -0,0 +1,15 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Jerican gives the player latest news on the world + +003-1,93,125,0 script Jerican NPC_JOURNALMAN,{ + Journalman(.name$); + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/003-1/lieutenantdausen.txt b/npc/003-1/lieutenantdausen.txt new file mode 100644 index 0000000..f112602 --- /dev/null +++ b/npc/003-1/lieutenantdausen.txt @@ -0,0 +1,956 @@ +// TMW2 scripts. +// Authors: +// Saulc +// Jesusalva +// acsvln +// gnulinux +// Description: +// Help Tulimshar guards +// Quest variable: +// TulimsharQuests_Guards +// Quest stages: +// 0 - not started +// 1 - Lieutenant Dausen asked for help Tulimshar guards +// 2 - completed +// 3 - Reward given + +003-1,111,84,0 script Lieutenant Dausen NPC_PLAYER_TONORI,{ + function DausenMobTutorial; + + .@q = getq(TulimsharQuest_WaterForGuard); + .@t = getq(TulimsharQuest_MobTutorial); + + switch (.@q) { + case 0: + mesn; + mesq l("Greetings, wanderer. I am @@, chief of the Tulimshar guards. My wards are dying from dehydration in the sun. Bring them water and you will earn a reward.", .name$); + break; + case 1: + mesn; + mesq l("Please help my wards!"); + break; + case 2: + goto L_Reward; + break; + case 3: + mesn; + mesq l("Thank you for your help."); + break; + default: + end; + } + next; + select + rif(!.@q, l("Yes sir. I will help them.")), + rif(getq(TulimsharQuest_Hasan) == 1, l("A guy named Hasan stole from me!")), + rif (strcharinfo(2) == "Monster King", l("I'm with the Monster King.")), + l("What can you say about the monsters here?"), + l("Good bye, sir."); + mes ""; + switch (@menu) { + // Thristy Guards Quest + case 1: + setq TulimsharQuest_WaterForGuard, 1; + mes ""; + mesn; + mesq l("Good luck! Come for remuneration when you finish!"); + next; + mesc l("Protip: You need an @@ full of water to get a reply from guards.", getitemlink(EmptyBottle)); + break; + // Hasan Quest + case 2: + setq TulimsharQuest_Hasan, 2; + speech S_FIRST_BLANK_LINE, lg("Ah, Hasan... Sorry pal, afraid I can't do anything for you. Try talking to his mother Sorfina, she is in Mahoud's house, near the Inn."); + break; + // The Monster King guild have a special menu + case 3: + if (strcharinfo(2) == "Monster King") goto L_MKControl; + break; + // Monster info + case 4: + DausenMobTutorial(); + break; + default: + closedialog; + goodbye; + break; + } + close; + +// Reward for quest completion +L_Reward: + mesn; + mesq l("Thank you, here is your reward."); + + inventoryplace TulimsharGuardBoots, 1, TulimsharGuardCard, 1; + getitem TulimsharGuardBoots, 1; + getitem TulimsharGuardCard, 1; + setq TulimsharQuest_WaterForGuard, 3; + + next; + + speech 0x0, + l("Wait a minute..."), + l("The Tulimshar guards needs an freelance employee who would help us with our work. We are searching for people like you."), + l("Take this badge, so you can enter the guard house. You will find more work there. Bye, and good luck!"); + close; + +// Mob Tutorial Quest +function DausenMobTutorial { + if (BaseLevel < 9) { + mesn; + mesq l("They're strong, so keep fighting Maggots and Scorpions which you're used to, until you get stronger."); + close; + } + .@t = getq(TulimsharQuest_MobTutorial); + .@k = getq2(TulimsharQuest_MobTutorial); + // You need a Guard Card, Dausen have a bad memory :p + if (!countitem(TulimsharGuardCard)) { + mesn; + mesq l("Well, I do not trust you yet. You literally were just brought by the shore, and even if Nard and Lua seems to trust you..."); + next; + mesn; + mesq l("...I still need to cover up if they're mistaken. Give me a reason to tell you secrets, show me you're concerned with Tulimshar safety and don't want just to be strong."); + next; + mesn; + mesq l("Power in the wrong hands is nothing but a burden and a reason for others to cry. That's exactly what I don't want in this town."); + close; + } + switch (.@t) { + case 0: + case 1: + // Reward + if (.@k >= 10) { + mesn; + mesq l("Welp, you killed 10 Crocs. They're not dangerous, as you see. That's why we don't bother in cleaning them up."); + setq TulimsharQuest_MobTutorial, 2, 0; + // 30% of exp values + getexp 75, 0; + Zeny+=300; + close; + } + // Quest Body + mesn; + mesq l("So. Uhm. The monsters here have varying levels of strength... I think the best way is to witness that yourself."); + next; + mesn; + mesq l("Do you see the crocs, with their claws and hard shell? They have high defense, this means your attacks deal less damage."); + next; + mesn; + mesq l("They're not too dangerous, but they can take a lot of hits. So, if you kill 10 of them, I'll know you're dedicated in learning which monsters are out there."); + next; + mesn; + mesq l("Can you do that? I'll be waiting!"); + compareandsetq TulimsharQuest_MobTutorial, 0, 1; + close; + case 2: + case 3: + // TODO: Blubs and Ducks + // Level Requeriment (same as blubs) + if (BaseLevel < 14) { + mesn; + mesq l("There's some diversity, but you should keep aiming at helping people and killing small-fry. If you ever want a challenge, there's a Giant Maggot inside the town which can kill you in one hit."); + next; + mesn; + mesq l("Have you found Tulimshar's Secret Beach yet? Tulimshar is full of secrets. Some NPCs which only say hi may say something else depending on your level or insistence."); + close; + } + // Reward + if (.@k >= 37) { + mesn; + mesq l("Hmm, that's some progress. Nobody goes to the beach because of these slimes, but it looks like this might change sometime soon."); + next; + mesn; + mesq l("Also, the Inn folks said the Ducks keep bothering them, but they've noticed a small decrease already."); + next; + mesn; + mesq l("I don't know what you did, but both the Ducks and Blubs did got scared. I thank you in the name of the city guard. Please come back later."); + setq TulimsharQuest_MobTutorial, 4, 0; + // 30% of exp values + getexp 210, 0; + Zeny+=700; + close; + } + // Quest Body + mesn; + mesq l("West of here is a beach. There's a secret passage to it, underground. In there you'll find slime-like creatures called Blubs."); + next; + mesn; + mesq l("Some are small, others are bigger. But they all fight together, so be mindful when they're in groups."); + next; + mesn; + mesq b(l("The big one without hat"))+" "+l("is your target. They spawn smaller versions of themselves! Kill @@ of them and make the beach safe for tourists.", 37); + next; + mesn; + mesq l("Or make yourself useful by killing some Ducks. They snatch all the Cherry Cake from the Inn and the staff is getting angry at me. It'll serve, too."); + compareandsetq TulimsharQuest_MobTutorial, 2, 3; + close; + case 4: + case 5: + // TODO: Desert Log Head, Desert Bandits, Sarracenus + if (BaseLevel < 22) { + mesn; + mesq l("Have you visited the mines already? Tycoon is in charge of the security operations in there. He might need your help more than I do."); + close; + } + // Reward + if (.@k >= 100) { + mesn; + mesq l("Not bad. These are the main threat we have to fend off, along snakes and black scorpions."); + setq TulimsharQuest_MobTutorial, 6, 0; + // 30% of exp values + getexp 600, 0; + Zeny+=1200; + close; + } + // Quest Body + mesn; + mesq l("So... Have you tried visiting the Canyons? I know, it is a crazy idea."); + next; + mesn; + mesq l("But if you want to be guard, you must be brave! Go fight your fears, and go kill some bandits like a decent guard."); + next; + mesn; + mesq l("Well, if you're scared, you can kill Desert Log Heads, but they'll have a smaller worth."); + next; + mesn; + mesq l("Bring me @@ Bandits or Sarracenus heads, or the double of that in Desert Log Heads, and I'll consider you brave enough.", 50); + compareandsetq TulimsharQuest_MobTutorial, 4, 5; + close; + case 6: + case 7: + if (BaseLevel < 29) { + mesn; + mesq l("You're brave but weak. Go grind some levels, go make a wooden sword, I don't know."); + close; + } + // Reward + if (.@k >= 300) { + mesn; + mesq l("Alright, you've not only proven your worth, but you've went through most monsters in the desert close to the town."); + next; + mesn; + mesq l("Knowledge is power... And now you have both. Use them wisely."); + next; + inventoryplace Coal, 6; + getitem Coal, 6; + setq TulimsharQuest_MobTutorial, 8, 0; + // ~30% of exp values + getexp 2100, 0; + Zeny+=2400; + mesn; + mesq l("This coal will aid you to craft better weapons later. It's a token of appreciation. Good job."); + close; + } + // Quest Body + mesn; + mesq l("You've passed the test of courage. Bravure you have, but are you a real adventurer?"); + next; + mesn; + mesq l("You have good intentions, be brave, but in a world where power is measured in levels, numbers have more meaning than they should."); + next; + mesn; + mesq l("I'll present you three kind of strong monsters. Snakes are fast and dangerous. Giant Maggots are slow and dangerous. Black Scorpions have average speed and are... deadly."); + next; + mesn; + mesq l("Giant Maggots are worth 1 point, Snakes are worth 5 points and Black Scorpions are worth 10 points. The ones in caves doesn't count. Bring me 300 points. Good luck!"); + compareandsetq TulimsharQuest_MobTutorial, 6, 7; + default: + mesn; + mesq l("Eh? Well, you're in a desert. You can see Maggots and Scorpions, they're very common on these parts. Giant Maggots are very dangerous, but also very slow. If you know how to fight, they will yield you lots of experience."); + next; + mesn; + mesq l("West of here is a beach. In said beach there are blubs, they don't attack but they walk in packs. Be careful if you provoke too many of them."); + next; + mesn; + mesq l("Also, south of here are mines. Talk to Tycoon for information about it."); + next; + mesn; + mesq l("By last, east of here are the Canyons. Do not go there before level 20, and even then, do not engage snakes in combat. They are fast and very dangerous."); + close; + break; + } + return; +} + +// The Monster King guild have a special menu +L_MKControl: + mesn; + mes l("Oh noes! You've found the Tulimshar control panel!"); + next; + select + l("Abort"), + l("Initiate a siege"); + mes ""; + if (@menu == 2) { + doevent "Lieutenant Dausen::OnStartSiege"; + closedialog; + } + close; + +OnGuardDeath: + end; + +OnMKSiege: + $@SIEGE_ABORTED = false; +OnStartSiege: + kamibroadcast(col("WARNING! WARNING! Monster Army moving towards Tulimshar!!",1)); + do_siege("003-1", "004-1", "TULIM", TP_TULIM, .name$, .siegetime); + initnpctimer; + end; + +// Timers +OnTimer5000: + .siegetime+=5; + do_siege("003-1", "004-1", "TULIM", TP_TULIM, .name$, .siegetime); + switch (.siegetime) { + // Monster Army arrives in town + case 60: + disablenpc "Ched"; + disablenpc "Aahna"; + disablenpc "Constable Perry"; + disablenpc "Cyndala"; + disablenpc "Eomie"; + disablenpc "Eugene"; + disablenpc "Gladys"; + disablenpc "Inac"; + disablenpc "Ishi"; + disablenpc "Itka"; + disablenpc "Jakod"; + disablenpc "Jerican"; + disablenpc "Mahoud"; + disablenpc "Marius The Bard"; + disablenpc "Michel"; + disablenpc "Neko"; + disablenpc "Nina The Traveler"; + disablenpc "Sarah"; + disablenpc "Silvia"; + disablenpc "Tamiloc"; + disablenpc "Tinris"; + disablenpc "#water_animation0"; + disablenpc "Sailors#003-1"; + disablenpc "Guard Philip"; + disablenpc "Guard Defou"; + disablenpc "Guard Avou"; + disablenpc "Guard Benji"; + disablenpc "Guard Valou"; + disablenpc "Guard Nutelo"; + disablenpc "Guard Moustacha"; + disablenpc "Guard Popaul"; + disablenpc "Guard Yen"; + disablenpc "Guard Maxim"; + disablenpc "Guard Totor"; + disablenpc "Guard Roukin"; + disablenpc "Guard Falko"; + disablenpc "Guard Froma"; + disablenpc "Guard Tetric"; + disablenpc "Guard Biscop"; + + // Create guards + monster("003-1", 98, 100, ("Guard Philip"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 98, 121, ("Guard Defou"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1",102, 121, ("Guard Avou"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 58, 158, ("Guard Benji"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 62, 158, ("Guard Valou"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 71, 138, ("Guard Nutelo"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 58, 128, ("Guard Moustacha"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 58, 100, ("Guard Popaul"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 51, 73, ("Guard Yen"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 54, 52, ("Guard Maxim"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 50, 36, ("Guard Totor"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 53, 36, ("Guard Roukin"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1", 35, 34, ("Guard Falko"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1",111, 101, ("Guard Froma"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1",102, 80, ("Guard Tetric"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + monster("003-1",107, 80, ("Guard Biscop"), FallenGuard3, 1, "Lieutenant Dausen::OnGuardDeath", Size_Medium, 2); + break; + // Monster Army deployed in town + case 90: + disablenpc "Aidan"; + disablenpc "Inar"; + disablenpc "Malivox"; + disablenpc "Estard"; + disablenpc "Malindou"; + disablenpc "Jhedia"; + disablenpc "Swezanne"; + disablenpc "Luca"; + disablenpc "Colin"; + break; + // Monster army have withdrawn completly + case MK_SIEGE_DURATION: + .siegetime=0; + announce(("Tulimshar siege is over!"), bc_all); + enablenpc "Ched"; + enablenpc "Aahna"; + enablenpc "Constable Perry"; + enablenpc "Cyndala"; + enablenpc "Eomie"; + enablenpc "Eugene"; + enablenpc "Gladys"; + enablenpc "Inac"; + enablenpc "Ishi"; + enablenpc "Itka"; + enablenpc "Jakod"; + enablenpc "Jerican"; + enablenpc "Mahoud"; + enablenpc "Marius The Bard"; + enablenpc "Michel"; + enablenpc "Neko"; + enablenpc "Nina The Traveler"; + enablenpc "Sarah"; + enablenpc "Silvia"; + enablenpc "Swezanne"; + enablenpc "Tamiloc"; + enablenpc "Tinris"; + enablenpc "#water_animation0"; + enablenpc "Aidan"; + enablenpc "Inar"; + enablenpc "Malivox"; + enablenpc "Luca"; + enablenpc "Colin"; + enablenpc "Estard"; + enablenpc "Malindou"; + enablenpc "Jhedia"; + enablenpc "Sailors#003-1"; + enablenpc "Guard Philip"; + enablenpc "Guard Defou"; + enablenpc "Guard Avou"; + enablenpc "Guard Benji"; + enablenpc "Guard Valou"; + enablenpc "Guard Nutelo"; + enablenpc "Guard Moustacha"; + enablenpc "Guard Popaul"; + enablenpc "Guard Yen"; + enablenpc "Guard Maxim"; + enablenpc "Guard Totor"; + enablenpc "Guard Roukin"; + enablenpc "Guard Falko"; + enablenpc "Guard Froma"; + enablenpc "Guard Tetric"; + enablenpc "Guard Biscop"; + stopnpctimer; + end; + break; + } + + // Loop again + initnpctimer; + end; + +OnInit: + // Check items.xml for info about this. + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, BullHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, LieutenantArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_SHIELD, LousyMoccasins); // TODO FIXME: Display Boots + setunitdata(.@npcId, UDT_WEAPON, Backsword); + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .siegetime=0; + .sex = G_MALE; + .distance = 4; + end; +} + +// dausen_mobtutorial (killedrid) +// updates dausen quest +function script dausen_mobtutorial { + .@mobId=getarg(0, killedrid); + .@t = getq(TulimsharQuest_MobTutorial); + .@k = getq2(TulimsharQuest_MobTutorial); + .@v = 1; + .@upd=false; + + switch (.@mobId) { + case Croc: + if (.@t == 1) { + setq2 TulimsharQuest_MobTutorial, .@k+.@v; + .@upd=true; + } + break; + case Blub: + case Duck: + if (.@t == 3) { + setq2 TulimsharQuest_MobTutorial, .@k+.@v; + .@upd=true; + } + break; + case DesertBandit: + case Sarracenus: + .@v=2; + case DesertLogHead: + if (.@t == 5) { + setq2 TulimsharQuest_MobTutorial, .@k+.@v; + .@upd=true; + } + break; + case BlackScorpion: + .@v=10; + case Snake: + if (.@mobId != BlackScorpion) + .@v=5; + case GiantMaggot: + if (.@t == 7) { + setq2 TulimsharQuest_MobTutorial, .@k+.@v; + .@upd=true; + } + break; + } + if (.@upd) { + if (.@k+.@v % 10 == 0) + dispbottom l("Dausen Quest - @@ @@ killed", .@k+.@v, strmobinfo(1, .@mobId)); + } + return; +} + +// Render random guard answer after bringing him water +function script GuardsGratitude { + + switch (rand2(6)) + { + case 0: + .@message$ = l("God bless you! You have saved me from sweltering!"); + break; + case 1: + .@message$ = l("I am happy that such responsible citizens live in Tulimshar. Thank you for your help. It's really hot nowdays!"); + break; + case 2: + .@message$ = l("Thanks, this is very handy."); + break; + case 3: + .@message$ = l("Our service is dangerous and difficult. But I would not want any other. Thanks for the help."); + break; + case 4: + .@message$ = l("My mother told me, do not go work like a guard. You will die from overheating in the sun during the summer time."); + break; + case 5: + .@message$ = l("Who are you? Thanks for the help."); + break; + default: + .@message$=l("Thank you!"); + break; + } + + if (Sex != getvariableofnpc(.sex, strnpcinfo(0))) { + .@narrator_message$ = l("The Guard sends an air kiss to you."); + } else { + .@narrator_message$ = l("The Guard patted you on the back."); + } + + speech S_LAST_BLANK_LINE, .@message$; + narrator S_LAST_BLANK_LINE, .@narrator_message$; + + return; +} + +// Do TulimsharQuest_WaterForGuard quest +function script CheckGuard { + .@guard_id = getarg(0); + .@guard_count = 0; + .@count_tmp = 0; + + if ($@GM_OVERRIDE) + npctalk3 l("Hello, I am G-@@, of the @@ order.", .@guard_id, $@GuardBits[.@guard_id]); + + if (GUARDS_DONE_BITARRAY & $@GuardBits[.@guard_id]) + { + mesn; + mesq l("Thanks for help! Other guards may need help too!"); + close; + } else { + while (.@count_tmp < 18) + { + if (GUARDS_DONE_BITARRAY & $@GuardBits[.@count_tmp]) + .@guard_count = (.@guard_count + 1); + + .@count_tmp = (.@count_tmp + 1); + } + + if ( countitem(BottleOfTonoriWater) == 0 ) { + legiontalk; + } else { + delitem BottleOfTonoriWater, 1; + getitem EmptyBottle, 1; + + getexp 32, 2; + Zeny = (Zeny + 30); + + GUARDS_DONE_BITARRAY = GUARDS_DONE_BITARRAY | $@GuardBits[.@guard_id]; + setq2 TulimsharQuest_WaterForGuard, .@guard_count; // Update questlog + + if (.@guard_count >= 17) { + message strcharinfo(0), "That must have been the last guard."; + setq TulimsharQuest_WaterForGuard, 2; + } + + GuardsGratitude(); + narrator(l("You receive 32 exp and 30 GP.")); + close; + } + } + + return; +} + +// Handle Guard's logic +function script GuardHandler { + if (getq(TulimsharQuest_WaterForGuard) == 1) { + CheckGuard(getarg(0)); + } else { + legiontalk; + } + + return; +} + +003-1,98,100,0 script Guard Philip NPC_GUARD1,{ + GuardHandler(0); + + end; + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 98, 104, + "dir", DOWN, 0, + "wait", 60, 0, + "move", 84, 100, + "dir", DOWN, 0, + "wait", 45, 0, + "move", 93, 110, + "dir", DOWN, 0, + "wait", 70, 0, + "move", 101, 106, + "dir", RIGHT, 0, + "wait", 12, 0, + "move", 98, 104, + "dir", DOWN, 0, + "wait", 68, 0, + "move", 93, 110, + "dir", DOWN, 0, + "wait", 90, 0, + "move", 111, 109, + "dir", DOWN, 0, + "wait", 11, 0; + initialmove; + initnpctimer; + .distance = 5; + .sex = G_MALE; + + setarray $@GuardBits, 1, (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9), (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15), (1 << 16), (1 << 17); +} +003-1,98,121,0 script Guard Defou NPC_GUARD1,{ + GuardHandler(1); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-1,102,121,0 script Guard Avou NPC_GUARD1,{ + GuardHandler(2); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-1,58,158,0 script Guard Benji NPC_GUARD1,{ + GuardHandler(3); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-1,62,158,0 script Guard Valou NPC_GUARD1,{ + GuardHandler(4); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-1,71,138,0 script Guard Nutelo NPC_GUARD1,{ + GuardHandler(5); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-1,58,128,0 script Guard Moustacha NPC_GUARD1,{ + GuardHandler(6); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-1,58,100,0 script Guard Popaul NPC_GUARD1,{ + GuardHandler(7); + end; + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 58, 100, + "dir", DOWN, 0, + "wait", 100, 0, + "move", 62, 100, + "dir", DOWN, 0, + "wait", 35, 0, + "move", 62, 93, + "dir", DOWN, 0, + "wait", 48, 0, + "move", 58, 104, + "dir", DOWN, 0, + "wait", 55, 0, + "move", 54, 102, + "dir", RIGHT, 0, + "wait", 82, 0; + + initialmove; + initnpctimer; + .distance = 5; + .sex = G_MALE; +} +003-1,51,73,0 script Guard Yen NPC_GUARD1,{ + GuardHandler(8); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-2,41,37,0 script Guard Yuna NPC_GUARD2,{ + if (getq(TulimsharQuest_WaterForGuard) == 1) + { + CheckGuard(9); + } else { + npctalkonce l("I like to sing."); + } + end; +OnInit: + .sex = G_FEMALE; + .distance = 3; + end; +} +003-1,54,52,0 script Guard Maxim NPC_GUARD1,{ + GuardHandler(10); + end; + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 54, 52, + "dir", DOWN, 0, + "wait", 85, 0, + "move", 54, 45, + "dir", DOWN, 0, + "wait", 70, 0, + "move", 67, 30, + "dir", LEFT, 0, + "wait", 11, 0, + "move", 49, 46, + "dir", DOWN, 0, + "wait", 55, 0, + "move", 31, 41, + "dir", LEFT, 0, + "wait", 6, 0, + "move", 35, 36, + "dir", UP, 0, + "wait", 9, 0, + "move", 49, 52, + "dir", DOWN, 0, + "wait", 70, 0; + initialmove; + initnpctimer; + .distance = 5; + .sex = G_MALE; + +} +003-1,50,36,0 script Guard Totor NPC_GUARD1,{ + GuardHandler(11); + end; + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 50, 36, + "dir", DOWN, 0, + "wait", 45, 0, + "move", 50, 39, + "dir", DOWN, 0, + "wait", 35, 0, + "move", 35, 40, + "dir", UP, 0, + "wait", 1, 0, + "move", 33, 34, + "dir", DOWN, 0, + "wait", 12, 0; + initialmove; + initnpctimer; + .distance = 5; + .sex = G_MALE; +} +003-1,53,36,0 script Guard Roukin NPC_GUARD1,{ + GuardHandler(12); + end; + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 53, 36, + "dir", DOWN, 0, + "wait", 60, 0, + "move", 56, 59, + "dir", DOWN, 0, + "wait", 1, 0, + "move", 61, 76, + "dir", RIGHT, 0, + "wait", 5, 0, + "move", 61, 105, + "dir", RIGHT, 0, + "wait", 8, 0, + "move", 59, 129, + "dir", LEFT, 0, + "wait", 4, 0, + "move", 60, 103, + "dir", UP, 0, + "wait", 1, 0, + "move", 56, 82, + "dir", LEFT, 0, + "wait", 7, 0, + "move", 56, 59, + "dir", UP, 0, + "wait", 1, 0, + "move", 53, 39, + "dir", DOWN, 0, + "wait", 25, 0; + initialmove; + initnpctimer; + .distance = 5; + .sex = G_MALE; +} +003-1,35,34,0 script Guard Falko NPC_GUARD1,{ + GuardHandler(13); + end; + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 35, 34, + "dir", DOWN, 0, + "wait", 60, 0, + "move", 51, 38, + "dir", DOWN, 0, + "wait", 10, 0; + initialmove; + initnpctimer; + .distance = 5; + .sex = G_MALE; +} +003-3,39,37,0 script Guard Malindax NPC_GUARD1,{ + GuardHandler(14); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-1,111,101,0 script Guard Froma NPC_GUARD1,{ + GuardHandler(15); + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} +003-1,102,80,0 script Guard Tetric NPC_GUARD1,{ + GuardHandler(16); + end; + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 110, 84, + "dir", RIGHT, 0, + "wait", 1, 0, + "move", 110, 101, + "dir", RIGHT, 0, + "wait", 0, 0, + "move", 99, 121, + "dir", LEFT, 0, + "wait", 1, 0, + "move", 89, 136, + "dir", DOWN, 0, + "wait", 1, 0, + "move", 65, 152, + "dir", DOWN, 0, + "wait", 1, 0, + "move", 71, 139, + "dir", RIGHT, 0, + "wait", 1, 0, + "move", 59, 128, + "dir", LEFT, 0, + "wait", 1, 0, + "move", 47, 79, + "dir", LEFT, 0, + "wait", 1, 0, + "move", 80, 73, + "dir", RIGHT, 0, + "wait", 1, 0, + "move", 53, 52, + "dir", RIGHT, 0, + "wait", 1, 0, + "move", 52, 37, + "dir", RIGHT, 0, + "wait", 1, 0, + "move", 35, 35, + "dir", RIGHT, 0, + "wait", 1, 0, + "move", 62, 105, + "dir", DOWN, 0, + "wait", 1, 0; + initialmove; + initnpctimer; + .distance = 5; + .sex = G_MALE; +} +003-1,107,80,0 script Guard Biscop NPC_GUARD1,{ + if (getq(TulimsharQuest_WaterForGuard) == 1) + { + CheckGuard(17); + } else { + npctalkonce l("No one is allowed past this point."); + } + end; +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} + + +003-1,114,83,0 script #tulim-guardhouse NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (countitem(TulimsharGuardCard) >= 1) goto L_Warp; + dispbottom l("Only Tulimshar Guards are allowed in this building."); + end; + +L_Warp: + warp "003-10", 42, 79; + end; +} diff --git a/npc/003-1/magic.txt b/npc/003-1/magic.txt new file mode 100644 index 0000000..3eb9367 --- /dev/null +++ b/npc/003-1/magic.txt @@ -0,0 +1,45 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Controlled Warp to Magic School (+whatever magic stuff we need to add later) + +003-1,34,33,0 script #MagicSchoolGate1 NPC_HIDDEN,0,0,{ + end; +OnTouch: + // Event Restrictions + if (countitem(Event1HSword)+countitem(Event2HSword)+countitem(EventBow)+countitem(EventWand)+countitem(RentCart)) { + mesc l("You cannot enter here while you have event weapons or a cart."), 1; + close; + } + // Main Quest Event + if (LUA_ASKED_TO_SAVE_PROFESSOR) { + .@mapn$="0030@"+getcharid(0); + // Instance doesn't exists + if (!(isinstance(LUA_ASKED_TO_SAVE_PROFESSOR)) || instanceowner(LUA_ASKED_TO_SAVE_PROFESSOR) != getcharid(3)) { + .@inst = instance_create("Academy "+getcharid(0), getcharid(3), IOT_CHAR); + instance_attachmap("003-0", .@inst, false, .@mapn$); + // Instance lasts 15 minutes + instance_set_timeout(900, 900, .@inst); + instance_init(.@inst); + LUA_ASKED_TO_SAVE_PROFESSOR=.@inst; + } + /* else { + // Instance already exists: You've failed previously? + instance_set_timeout(900, 900, .@inst); + } + */ + warp .@mapn$, 48, 51; + end; + } + /* + if (countitem(JesusalvaGrimorium) <= 0 || MAGIC_LVL < 2) { + slide 36, 39; + percentheal -5, -10; + dispbottom l("Powerful magic repels you away from this magic place!"); + } + */ + warp "003-0", 48, 51; + end; +} + diff --git a/npc/003-1/mahoud.txt b/npc/003-1/mahoud.txt new file mode 100644 index 0000000..db41e5a --- /dev/null +++ b/npc/003-1/mahoud.txt @@ -0,0 +1,59 @@ +// TMW-2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Mahoud is the Tulimshar Well Master +// TODO: Chance to lose a bit of GP by getting water with money inside. +// I think water should not be so OP, and some minor RNG would be nice here + +003-1,46,79,0 script Mahoud NPC_PLAYER_ARGAES,{ + mesn; + mes l("I can fill your bottle with water for only @@ gp the bottle.", .COST_PER_BOTTLE); + mes l("After all, I am the Well Master!"); + input .@count; + + if (.@count == 0) + close; + .@Cost = .@count * .COST_PER_BOTTLE; + .@empty = countitem(EmptyBottle); + + if (.@empty < .@count) + goto L_NotEnoughBottles; + if (Zeny < .@Cost) + goto L_NotEnoughMoney; + getinventorylist; + inventoryplace BottleOfTonoriWater, .@count; + + Zeny=Zeny-.@Cost; + delitem EmptyBottle, .@count; + getitem BottleOfTonoriWater, .@count; + close; + +L_NotEnoughBottles: + mes ""; + mesn; + mes l("You don't have that many empty bottles!"); + close; + +L_NotEnoughMoney: + mes ""; + mesn; + mes l("You don't have enough gold! You need @@ gp.", .@Cost); + close; + +OnInit: + .COST_PER_BOTTLE = 50; + .sex = G_MALE; + .distance = 7; + + .@npcId = getnpcid(.name$); + // Check items.xml for info about this + setunitdata(.@npcId, UDT_HEADTOP, InfantryHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + end; +} diff --git a/npc/003-1/malivox.txt b/npc/003-1/malivox.txt new file mode 100644 index 0000000..f35653a --- /dev/null +++ b/npc/003-1/malivox.txt @@ -0,0 +1,46 @@ +// Author: +// Saulc + +003-1,114,106,0 script Malivox NPC_PLAYER_TONORI,{ + + speech S_LAST_NEXT, + l("I am @@, an alchemist specialized in reset potions.", .name$); + + select + l("Can you reset my stats please?"), + l("You are weird, I have to go sorry."); + + switch (@menu) + { + case 1: + goto L_ResetStats; + case 2: + goto L_Quit; + } + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + ConfirmStatusReset(); + goto L_Quit; + + +L_Quit: + goodbye; + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/003-1/mapflags.txt b/npc/003-1/mapflags.txt new file mode 100644 index 0000000..5b342e2 --- /dev/null +++ b/npc/003-1/mapflags.txt @@ -0,0 +1,23 @@ +003-1 mapflag mask 1 +003-1 mapflag town +003-1 mapflag nopenalty +003-2 mapflag zone indoors +003-3 mapflag zone indoors +003-4 mapflag zone indoors +003-5 mapflag zone indoors +003-6 mapflag zone indoors +003-7 mapflag zone indoors +003-8 mapflag zone indoors +//003-9 mapflag zone indoors +003-10 mapflag zone indoors +//003-11 mapflag zone indoors +//003-12 mapflag zone indoors +003-13 mapflag zone indoors + +// Magic Council +003-0 mapflag zone indoors +003-0-1 mapflag zone indoors +003-0-2 mapflag zone indoors + +// Town Walls are a no penalty zone +003-1-2 mapflag zone indoors diff --git a/npc/003-1/mariusthebard.txt b/npc/003-1/mariusthebard.txt new file mode 100644 index 0000000..8a2701c --- /dev/null +++ b/npc/003-1/mariusthebard.txt @@ -0,0 +1,231 @@ +// TMW2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Marius the Bard is a bard. Helps player with the world's background story. + +003-1,88,135,0 script Marius The Bard NPC_ELVEN_MAN_TRADER,{ + mesn; + mesq l("Hello adventurer, what may this humble minstrel do for you today?"); + next; + +L_Main: + menu + l("Who, or what are you?"), L_Who, + l("Where exactly am I?"), L_Where, + l("Can you sing me a song?"), L_Music, + l("Actually, nothing. Bye!"), L_Bye; + +L_Who: + mes ""; + mesn; + mesq l("I am a bard! I used to be an elf trader, but music got me for good."); + next; + mesq l("Now I travel by the world, composing songs about the things I see."); + next; + mesq l("I try to improve my skills every day. One day, I will sing a song no one will ever forget."); + next; + goto L_Main; + +L_Where: + mes ""; + mesn; + mesq l("This is Tulishmar, the oldest of human cities."); + next; + mesq l("It is a port city at the south of the continent. Main economic activities are mining and spices trade."); + next; + mesq l("The biggest mine, where you could find Mana Stones in the past, is now entirely depleted. Not a single stone left."); + next; + mesq l("The magic council have only a handful set of them, and they let only the most skilled ones get it."); + next; + mesq l("If you want, you can ask there for one. They are at the big building at northwest."); + next; + goto L_Main; + +L_Music: + mesc l("But do you want to hear a song about the world, or about grinding?"); + menu + l("Sing me about the world!"), L_Music_World, + l("Sing me the legend of the five heroes!"), L_Music_Heroes, + l("Sing me the fate of the weapons!"), L_Music_Fates, + l("Sing me about grinding!"), L_Music_Grind, + l("Sing me about slaying Pinkies!"), L_Music_Pinkie, + l("Sing me about miscellaneous monsters!"), L_Music_Misc; + // ??? + mes ""; + goto L_Music; + +// NOTE: Rewriting this song is always OK. +// The last line is always bigger than the previous ones. +L_Music_World: + mes ""; + mesn; + mesq l("I will sing a song about the Mana War and current times."); + next; + mes l("In times of the fire, magic came to save us."); + mes l("But we took every stone, and restricted the magic."); + mes l("Ah! Greedy humans! Why had we to desire?"); + mes l("Ah! Greedy humans! How ignorant were we, to ever do that?"); + next; + mes l("Saul and Fefe did a great revolution,"); + mes l("They wanted magic to be once again free!"); + mes l("Ah! Greedy humans! Why did we had to fight?"); + mes l("Ah! Greedy humans! Couldn't we happy with little?"); + next; + mes l("The mana war stroke, and many people died,"); + mes l("The Mana Stones were stolen, in evil hands have fallen!"); + mes l("Ah! Greedy humans! Just how big is our greed?"); + mes l("Ah! Greedy humans! Things will never be as they used to be!"); + next; + mes l("The mage thief tried to all power absorb,"); + mes l("But instead of dying a Monster King he became!"); + mes l("Ah! Greedy humans! We pay the price for our actions,"); + mes l("Ah! Greedy humans! The Monster War will now rage the globe!"); + next; + mes l("But hope is not lost, said the Sages of Fate!"); + mes l("Because heroes are not born, rather, they are made!"); + mes l("Ah! Greedy humans! Stand up to save our world!"); + mes l("Ah! Greedy humans! End this war which our greed has stroke!"); + next; + goto L_Main; + +L_Music_Heroes: + mes ""; + mesn; + mesq l("I will sing a song about the Five Legendary Heroes myth."); + next; + mes l("Once upon a time, five mighty heroes emerged;"); + mes l("Aegis with their steadfast shield, nigh impregnable;"); + mes l("The tyrant, which could shot from any distance;"); + mes l("The mightiest mage, which had control over the runes;"); + mes l("And Demure, the Queen of Dragons."); + next; + mes l("Their fight was not damned,"); + mes l("And help to obtain they were able."); + mes l("Wielding the lightbringer came the ace,"); + mes l("Whom put an end at their foes,"); + mes l("And brought peace to the land."); + next; + mes l("It was said that these five heroes will once again emerge;"); + mes l("And a great threat they shall defeat."); + mes l("Led by the Lightbringer"); + mes l("The innominable they shall face"); + mes l("And rejoice will come from their feats."); + next; + mes l("However, watch out!"); + mes l("For their weapons has a will on them."); + mes l("Don't be deemed unworthy,"); + mes l("For yourself can obtain tandem!"); + next; + goto L_Main; + +L_Music_Fates: + mes ""; + mesn; + mesq l("I will sing a song about the fate of five legendary weapons."); + next; + mes l("Once their fight was over,"); + mes l("The heroes met their rest."); + mes l("The axe, returned to its owner,"); + mes l("The runes, burried with its owner."); + next; + mes l("The aegis, entrusted to the tree,"); + mes l("Tyranny as well, but to a different tree."); + mes l("And the lightbringer,"); + mes l("King of all heroes,"); + mes l("Now wanders over the land."); + next; + mes l("The harsh pinkie holds secrets,"); + mes l("While the tyrant desires to see skill."); + mes l("Demure accepts challengers,"); + mes l("And the runes wait for the grave to be found."); + next; + mes l("Neither difficult, neither clear,"); + mes l("For extreme power they hold."); + mes l("Once the five are once again assembled,"); + mes l("The engine of fate will once again move!"); + next; + goto L_Main; + +L_Music_Grind: + mes ""; + mes l("Grind grind grind the slime!"); + mes l("Tunnel through the caves!"); + mes l("In TMW2 there is"); + mes l("Nothing so sublime!"); + next; + mes l("♪There is a server♪"); + mes l("TMW2"); + mes l("♪Hi ho, no one on♫"); + mes l("Then along came a soul as happy as can be"); + next; + mesc l("yawns"); + mes l("So what can I say, except, you're welcome"); + mes l("For all the feedback, you know"); + mes l("There's no thank me, its'kay you're welcome"); + mes l("I guess I love the server, we all know ♪"); + next; + mes l("Far over, the misty Nivalis cold ♪"); + mes l("To dungeon deep, and cavern old"); + mes l("We must away"); + mes l("ee break of day"); + mes l("To seek our pale, enchanted gold"); + mes l("Why does death embrance me so much today?"); + next; + mesc l("Authorship: Xanthem (DiamondPython)"); + mesc l("Date: 2019-03-05, 03:14 UTC"); + next; + goto L_Main; + +L_Music_Pinkie: + mes ""; + mes l("Pinkie, pinkie,pinkie, oh how i luvs thee;"); + mes l("So fine and pink, tasty too!"); + next; + mes l("♪ Pinkie,pinkie,pinkie, you're so fine."); + mes l("Pinkie,pinkie,pinkie, your blood runs like wine. ♪"); + next; + mes l("There I was knee deep in pinkie pattie,"); + mes l("Lost without an acorn."); + next; + mes l("If life gets so a person cannot laugh,"); // Out of context + mes l("Life isn't worth the living."); + mes l("What else like a Pinkie?"); // Not from Cordo + next; + mesc l("Authorship: Former_Cordo (Cordo)"); + mesc l("Date: 2020-03-27, 01:30 UTC"); + next; + goto L_Main; + +L_Music_Misc: + mes ""; + mes l("Ohhh moubiii"); + mes l("Ohhh moubiii"); + mes l("You make me dance like a monkey"); + mes l("Ohh Moubi Moubi you are too pink, that my eyes get shrink"); + mes ""; + mesc l("Authorship: Manatauro"); + mesc l("Date: 2021-02-24, 02:36 UTC"); + next; + mes l("Oh, I was wee lad on the beach singing the blub song"); + mes l("One day I saw myself a rich man ♪"); + mes l("Blub, blub, blub"); + mes l("That's the Blub Song"); + mes ""; + mesc l("Authorship: Former_Cordo (Cordo)"); + mesc l("Date: 2020-04-05, 14:40 UTC"); + next; + goto L_Main; + +L_Bye: + mes ""; + mesn; + mesq lg("Good bye my friend, and safe travels!"); + close(); + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/003-1/michel.txt b/npc/003-1/michel.txt new file mode 100644 index 0000000..056bc82 --- /dev/null +++ b/npc/003-1/michel.txt @@ -0,0 +1,73 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// This stuff should be moved somewhere else... + +003-1,89,109,0 script Michel NPC_BACCHUS,{ + + mesn; + mesq l("These people have lots of unspent Strange Coins! Waw!"); + + HallOfCoins(); + + // Anyone with GM Level, staff or not, is allowed to latest GM logs + if (!getgmlevel()) goto L_Main; + + next; + HallOfGMLog(); + + // Only Admins are allowed to change the Referral Scoreboard + if (!is_admin()) goto L_Main; + if (is_admin()) goto L_GMOnce; + close; + +L_Main: + if (!$REFERRAL_IDS[#REFERRAL_PROG]) + close; + next; + mesn; + mesq l("Hey, I see you have appointed @@ players to this game. Good job!", $REFERRAL_IDS[#REFERRAL_PROG]); + if ($REFERRAL_IDS[#REFERRAL_PROG] > 20) + mesc l("Uhm, just remember it'll be manually verified before prizes are given... Don't even think on cheating!"), 1; + close; + +L_GMOnce: + next; + .@nb = query_sql("SELECT SUM(value) FROM `mapreg` WHERE varname='$REFERRAL_IDS' LIMIT 2", .@value); + @total=.@value[0]; +L_GM: + mesc "Referral Event status: " + ($REFERRAL_ENABLED ? "##2ACTIVE##0" : "##1INACTIVE##0"); + mesc "Total refers count: "+@total; + mes ""; + select + "Close", + "Toggle Referral Event", + "Scoreboard", + "CLEAR REFERRAL ID ARRAY", + "Close"; + mes ""; + switch (@menu) { + case 2: + $REFERRAL_ENABLED=!$REFERRAL_ENABLED; logmes "Enabled REFER event.", LOGMES_ATCOMMAND; break; + case 3: + HallOfReferral(); break; + case 4: + mesc "Are you sure?", 1; + next; + if (askyesno() == ASK_YES) { + deletearray($REFERRAL_IDS); + logmes "##1Deleted PERMANENTLY the REFER event scoreboard.##0", LOGMES_ATCOMMAND; + .@nb = query_sql("SELECT SUM(value) FROM `mapreg` WHERE varname='$REFERRAL_IDS' LIMIT 2", .@value); + @total=.@value[0]; + } + default: close; + } + next; + goto L_GM; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/003-1/neko.txt b/npc/003-1/neko.txt new file mode 100644 index 0000000..fd461a4 --- /dev/null +++ b/npc/003-1/neko.txt @@ -0,0 +1,121 @@ +// TMW-2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Another shopkeeper which babbles about Jean Shorts, how Cave Snake drops them +// and encourage players to find it. He pays 6.000 GP and gives 1620 xp for one. +// In future, this could grant you discounts on his shop, or allow a quest with +// a family member from his. + +003-1,103,106,0 script Neko NPC_M_SHOPKEEPER,{ + .@q=getq(TulimsharQuest_Neko); + mesn; + mesq l("Only finest wares!"); + mes ""; + select + l("Trade"), + rif(BaseLevel > 20 && .@q == 0, l("Why do you only use the silk robe? Don't you have a shorts or something?")), + rif(.@q == 1 && countitem(JeansShorts) >= 1, l("Hey... I found a Jeans Shorts. Is it yours?")), + l("Leave"); + + if (@menu == 2) { + mesn; + mesq l("Oh, that's a long story."); + next; + mesn; + mesq l("You see, I was walking in the secret caves near Ched, and... well..."); + next; + mesn; + mesq l("The @@ stole my @@. Hahah. I can't find anywhere else to buy it.", getmonsterlink(CaveSnake), getitemlink(JeansShorts)); + next; + setq TulimsharQuest_Neko, 1; + mesn; + mesq l("If you bring friends, I'm sure they'll be no match. Ah... How many people already lost their shorts to Cave Snakes?"); + next; + if (getq(TulimsharQuest_AnwarField) < 10 && TUTORIAL) { + mesn; + mesq l("Actually, Anwar offered me some ugly, bright orange pants. I thanked him, of course, I prefer this robe than... that."); + next; + mesn; + mesq l("Thinking well, I've heard Cyndala could bleach these pants for free; Must be because their ugliness."); + next; + } + } + if (@menu == 3) { + getexp rand(120,200), 0; + setq TulimsharQuest_Neko, 2; + mesn; + mesq l("Oh... Wow! I'm surprised."); + next; + mesn; + mesq l("Please, keep it. I don't need it at all!"); + next; + mesn; + mesq l("This silk robe is more than enough, and you are fighting, you need the def bonus more than me. %%2"); + next; + } + + closedialog; + if (@menu == 1) { + npcshopattach(.name$); + shop .name$; + } + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + //setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); // FIXME: LeatherTrousers are BROKEN! + setunitdata(.@npcId, UDT_WEAPON, AssassinBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 2); + + sleep(SHOPWAIT); + tradertype(NST_MARKET); + sellitem EarthPowder, 12000, 1; + sellitem ShortTankTop, 8000, 1; + sellitem TrainingBow, 990, 2; + sellitem SilkGloves, -1, 1; + sellitem LeatherTrousers, -1, 1; + sellitem CottonCloth, -1, 3; + sellitem RoastedMaggot, -1, 2; + sellitem ArrowAmmoBox,-1,rand(5,10); + sellitem Arrow, -1, rand(10000,30000); + sellitem CroconutBox, rand(2650,2950), 4; + sellitem EmptyBox, -1, 2; + sellitem ChamomileTea, getiteminfo(ChamomileTea, ITEMINFO_BUYPRICE)*15/10, 10; + + .sex = G_MALE; + .distance = 5; + end; + +OnClock1149: + restoreshopitem SilkGloves, -1, 1; + restoreshopitem LeatherTrousers, -1, 1; +OnClock2359: + restoreshopitem EarthPowder, 12000, 1; + restoreshopitem ShortTankTop, 8000, 1; + restoreshopitem TrainingBow, 990, 2; + restoreshopitem CottonCloth, 3; + restoreshopitem RoastedMaggot, 2; + restoreshopitem ArrowAmmoBox,rand(5,10); + restoreshopitem Arrow, rand(10000,30000); + restoreshopitem CroconutBox, rand(2650,3000), 4; + restoreshopitem EmptyBox, 2; + restoreshopitem ChamomileTea, getiteminfo(ChamomileTea, ITEMINFO_BUYPRICE)*15/10, 10; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} diff --git a/npc/003-1/ninathetraveler.txt b/npc/003-1/ninathetraveler.txt new file mode 100644 index 0000000..3ef244e --- /dev/null +++ b/npc/003-1/ninathetraveler.txt @@ -0,0 +1,111 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// The Travelers travel around the world telling stories. + +003-1,56,143,0 script Nina The Traveler NPC_F_COINKEEPER,{ + + mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Wow! Are you @@? Everyone, in every city, talks about you!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + + mesq l("Hello. I am @@, and I am from a family of travellers. We travel though the whole world, looking for exotic goods.", .name$); + next; + mesq l("You can buy rare items with me, or I can tell you about different cities in our world."); + +L_Menu: + mes ""; + menu + l("I want to trade with you."), L_Trade, + l("Tell me about Tulimshar."), L_Tulim, + l("Tell me about Hurnscald."), L_Hurns, + l("Tell me about Artis."), L_Artis, + l("Tell me about Halinarzo."), L_Halin, + l("Sorry, I'll pass."), L_Close; + +L_Tulim: + mes ""; + mesn; + mesq l("Tulimshar is the oldest human city, and its foundation is the year zero of our calendar."); + next; + mesq l("The city only flourished because Janett Platinum had the idea to build city walls surrounding this city."); + next; + mesq l("The desert climate means you'll find mostly maggots and scorpions. Their drops include cactus drinks, cake, knifes, black pearls, gold, and other common things."); + next; + mesq l("You can find for a good price desert equipment and some kind of dyes. You find all sort of crafters, artisans and warriors here."); + next; + goto L_Menu; + +L_Hurns: + mes ""; + mesn; + mesq l("Hurnscald was founded after Tulimshar, in more fertile lands. Their walls are not so sturdy as the ones of Tulimshar."); + next; + mesq l("Under the leadership of King Wusher, they were the first to accept immigrants from other races. You will find humans and non-humans there."); + next; + mesq l("The fertile climate is ideal for mushrooms. You can also find lots of wood."); + next; + mesq l("Their economy provide many edible items and potions."); + next; + goto L_Menu; + +L_Artis: + mes ""; + mesn; + mesq l("Artis is a city port founded after the Great Fire on the other continent."); + next; + mesq l("People say it is the second biggest city from the world."); + next; + mesq l("Different kind of monsters live near the city. For example, blubs. I have no idea of what are those."); + next; + mesq l("People usually dock there when travelling to the second continent. Nothing exceptional about economy."); + next; + if ($FIRESOFSTEAM) { + mesq l("They used to export food and other things but there has been radio silence recentely; Which is why Andrei Sakar and a group of adventurers borrowed Nard's ship and went to investigate."); + next; + } + goto L_Menu; + +L_Halin: + mes ""; + mesn; + mesq l("Halinarzo was founded to explore Mana Stones."); + next; + mesq l("You can find both huge swamps, as huge desertic areas near and on it."); + next; + mesq l("Lizards are the main monster found, and they steal gold from innocent bypassers."); + next; + mesq l("Without any mana stone left, and because the walls were not very strong, most of the city was destroyed."); + next; + mesq l("Unlike many other cities, if you want people in eternal need of items, there is a good place to look."); + next; + goto L_Menu; + + +L_Trade: + mesn; + mesq l("Use your @@ as currency!", getitemlink(StrangeCoin)); + tutmes l("%s is obtained during events, daily logins, heroic deeds, gifts, etc. But cannot be bought with real money.", getitemlink(StrangeCoin)); + next; + openshop "Aeros Trader"; + closedialog; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, UglyChristmasSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 27); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/003-1/oldwell.txt b/npc/003-1/oldwell.txt new file mode 100644 index 0000000..ff96e8a --- /dev/null +++ b/npc/003-1/oldwell.txt @@ -0,0 +1,38 @@ +// TMW-2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Free well that give sewer water. a good place for tulim kids to miss school :b + +003-1,53,144,0 script Old Well#003-1 NPC_NO_SPRITE,{ + + mesc l("You found an old well with a bucket on it! It's time to fill plenty of @@!", getitemlink(EmptyBottle)); + input .@count; + + if (.@count == 0) + close; + .@empty = countitem(EmptyBottle); + + if (.@empty < .@count) + goto L_NotEnoughBottles; + getinventorylist; + inventoryplace BottleOfSewerWater, .@count; + + delitem EmptyBottle, .@count; + getitem BottleOfSewerWater, .@count; + closeclientdialog; + dispbottom("Eek, Sewer Water! What the?! Better not drink this!"); + close; + +L_NotEnoughBottles: + mesc l("You don't have that many empty bottles!"); + close; + + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + diff --git a/npc/003-1/quirino.txt b/npc/003-1/quirino.txt new file mode 100644 index 0000000..2e78ebd --- /dev/null +++ b/npc/003-1/quirino.txt @@ -0,0 +1,264 @@ +// TMW2 Script +// Author: +// Ernando <ernando.quirino@hotmail.com> (Creator) +// Jesusalva <admin@tmw2.org> +// Description: +// Hunger Games™ version for The Mana World Brazil v2, created by Ernando Quirino. +// Entrance to Hungry Quirin Arena +// Variables: +// EQ - Ernando Quirino +// $@EQ_TIMER +// < gettimetick(2): Can be open! +// > gettimetick(2): Registration open! +// = gettimetick(2): Event start! +// $@EQ_STATUS +// 0: Register open +// 1: Fight happening +// 2: Event finished + +003-1,48,73,0 script Quirin NPC_ERNANDO,{ + function quirinInfo; + function quirinHonor; + if (is_admin()) + goto L_Hub; + + if ($@EQ_STATUS) goto L_NoSeason; + if ($@EQ_STATUS == 0) { + if ($@EQ_TIMER < gettimetick(2)) + goto L_Hub; + else + goto L_Register; + } + + npctalk3 l("Bug, report me!"); + end; + +L_NoSeason: + // If it finished, it can be done again in one hour + if ($@EQ_STATUS == 2) { + if ($@EQ_TIMER+3600 < gettimetick(2)) { + $@EQ_STATUS=0; + npctalkonce l("A sec... And... Done! I just finished cleaning it up!"); + } else { + npctalkonce l("I'm currently cleaning the arena, wait just @@ more.", FuzzyTime($@EQ_TIMER+3600)); + openstorage(2, STORAGE_ACCESS_GET); + end; + } + } else { + npctalkonce l("I'm currently hosting a fight."); + } + end; + +L_Register: + mesn; + mesq l("Hello player, do you want to participate on HUNGRY QUIRIN event?!"); + if ($@EQ_TIMER+180 < gettimetick(2)) + mesc l("Event will start in @@", FuzzyTime($@EQ_TIMER)); + else + mesc l("Event will start in @@", FuzzyTime($@EQ_TIMER)), 1; + next; + select + l("Yeah, sign me up!"), + l("No, not at the moment."), + l("Does it counts to Honor Ranking?"), + l("You are holding a few items of mine..."), + l("Information"); + + switch (@menu) { + case 1: + goto L_SignUp; + case 2: + close; + case 3: + quirinHonor(); + break; + case 4: + openstorage(2, STORAGE_ACCESS_GET); + closeclientdialog; + close; + break; + case 5: + quirinInfo(); + } + goto L_Register; + +function quirinInfo { + mesn col(l("Hungry Quirin Arena Rules"), 3); + mesc l("1- You must not be carrying anything with you."); + mesc l("2- You must not use a cart. If you do, YOU WILL BE SEVERELY PENALIZED."); + mesc l("3- All items from the Arena are from the Arena. You won't carry any of them back with you."); + mesc l("4- Experience and Gold earned during this event can be kept."); + next; + mesn col(l("Hungry Quirin Arena Information"), 3); + mesc l("1- Survive. If you die, you will gain nothing. And people want to kill you."); + mesc l("2- Take everything you can find. You'll be warped without equip or healing items! Kill monsters to get some stuff too!"); + mesc l("3- Trust nobody. There can be only one winner, and it must be you."); + mesc l("4- Take Care. Wildlife can kill you too. There can be traps."); + mesc l("5- Trust yourself. You will lose the moment you enter in panic. This arena is not for the weak-willed!"); + next; + return; +} + +function quirinHonor { + mesn; + mesq l("Of course it does. And the same level rules applies."); + next; + mesn; + mesq l("But if you believe in such cool challenge like mine there should be no dishonorable fight, comment it on Discord!"); + next; + mesn; + mesq l("And it might be changed in a future patch %%g"); + tutmes l("If you kill an opponent stronger than you, you will gain honor points. But if the oponent is 15 levels weaker than you, it will be NEGATIVE!"), l("About Scoreboards and Honor Points"); + tutmes l("If you kill the same person within 30 minutes, honor will not fluctuate. The whole honor system is very experimental."), l("About Scoreboards and Honor Points"); + tutmes l("Honor only applies to PvP fights. It can be seen on \"%s\" and rewards are given monthly for it.", b("@scoreboards")); + next; + return; +} + +L_SignUp: + // Player cannot be carrying anything + if(Weight >= 1) goto L_Full; + getcartinventorylist(); + if(@cartinventorylist_count>=1) goto L_Full; + + // Warp player + if (rand2(1,2) == 1) + warp "001-8", rand2(42, 57), 42; + else + warp "001-8", rand2(42, 57), 57; + + // Prevent further movements! + setpcblock(PCBLOCK_SOFT, true); + //dispbottom l("Stay ready!"); + dispbottom l("##1DON'T MOVE until the signal. Stay ready! If you move, you will desync the client!"); + close; + +L_Full: + mesn; + mesq l("You cannot bring anything to the arena. Please put everything into the storage."); + next; + mesn; + mesq l("I can do that for you, but you'll still need to go to storage to get them back."); + next; + select + l("Off to storage I go."), + l("PLEASE STORE ALL MY ITEMS"), + l("Alright, I don't like PVP anyways..."); + mes ""; + if (@menu == 2) { + closeclientdialog; + charcommand("@storeall 2"); + if (Weight == 0) + goto L_SignUp; + } + close; + +L_Hub: + mesn; + mesc l("Welcome to HUNGRY QUIRIN ARENA mangment panel."); + next; + mesn; + mesq l("I am Quirino Voraz, and my arena is the coolest PVP Arena on all Mana Worlds."); + mesq l("However, I need a tax to start, and you need to arrange players. There are no refunds."); + next; + mes l("Current player count: @@/5 must be online.", getusers(1)); + mes l("Current arena player count: @@ on map. (Min. 3 to begin event)", getmapusers("001-8")); + + // Open event? Minimum 5 connections or GM_OVERRIDE flag. + if ($@EQ_TIMER < gettimetick(2) && !$@EQ_STATUS && (getusers(1) >= 5 || $@GM_OVERRIDE)) { + next; + mesc l("Activate event?"), 1; + mesc l("It'll cost @@ GP", .price); + if (Zeny < .price) + close; + menuint + l("NO"), -1, + l("Give players 5 minutes"), 300, + l("Give players 10 minutes"), 600, + l("Give players 15 minutes"), 900, + l("Give players 20 minutes"), 1200, + l("Give players 25 minutes"), 1500, + l("Give players 30 minutes"), 1800, + l("You are holding a few items of mine..."), -2; + mes ""; + if (@menuret > 0) { + Zeny=Zeny-.price; + $@EQ_TIMER=gettimetick(2)+@menuret; + initnpctimer; + setcells "001-8", 41, 58, 41, 43, 6, "qhubN"; + setcells "001-8", 41, 58, 56, 58, 6, "qhubS"; + announce("##1HUNGRY QUIRIN EVENT: ##3##BRegister is now open! Talk to Quirin, on Tulimshar Councilroom!", bc_all|bc_npc); + channelmes("#world", strcharinfo(0)+" invites everyone to HUNGER QUIRIN PVP ARENA in Tulimshar. It'll start in "+FuzzyTime($@EQ_TIMER)); + } + if (@menuret == -2) { + openstorage(2, STORAGE_ACCESS_GET); + closeclientdialog; + } + close; + } + + // Main Control menu. Not using l() on purpose. + + select + rif($@EQ_STATUS == 0 && (getmapusers("001-8") >= 3 || $@GM_OVERRIDE) && is_gm(), "Start Event at once!"), + rif($@EQ_STATUS == 1 && is_master(), "Send wave of items and monsters!"), + rif($@EQ_STATUS == 0 && $@GM_OVERRIDE && is_admin(), "[DEBUG] Join Event"), + rif($@EQ_STATUS == 0 && $@GM_OVERRIDE && is_admin(), "[DEBUG] Join & Start Event Now"), + "I'm done."; + + if (@menu == 1) + donpcevent("#QuirinoHUB::OnStart"); + if (@menu == 2) + donpcevent("#QuirinoHUB::OnSendWave"); + if (@menu == 3) + goto L_Register; + if (@menu == 4) { + addtimer 1000, "Quirin::OnDebugReg"; + goto L_SignUp; + } + + close; + +OnDebugReg: + donpcevent("#QuirinoHUB::OnStart"); + end; + +// Each minute +OnTimer60000: + // We must autostart event now + if ($@EQ_TIMER <= gettimetick(2)) { + if (getmapusers("001-8") >= 3 || $@GM_OVERRIDE) { + donpcevent("#QuirinoHUB::OnStart"); + } else { + delcells "qhubN"; + delcells "qhubS"; + maptimer("001-8", 1000, "#QuirinoHUB::OnCancel"); + announce("##1HUNGRY QUIRIN EVENT: ##3##BCancelled due lack of players!", bc_all|bc_npc); + } + end; + } + mapannounce "003-1", "Hungry Quirin starts in " + FuzzyTime($@EQ_TIMER) + " and there are " + getmapusers("001-8") + "/3 player(s) standing by." , 0; + mapannounce "001-8", "Hungry Quirin starts in " + FuzzyTime($@EQ_TIMER), 0; + initnpctimer; + end; + +OnInit: + .price=570; + .sex=G_MALE; + .distance=5; + end; +} + + +/* +Blockwalls TileCondition and masks for setcells +manaplus/src/enums/resources/map/blockmask.h + + WALL = 0x80, // 1000 0000 = 128 + AIR = 0x04, // 0000 0100 = 4 + WATER = 0x08, // 0000 1000 = 8 + GROUND = 0x10, // 0001 0000 = 16 + GROUNDTOP = 0x20, // 0010 0000 = 32 + PLAYERWALL = 0x40, // 0100 0000 = 64 + MONSTERWALL = 0x02 // 0000 0010 = 2 +*/ diff --git a/npc/003-1/sailors.txt b/npc/003-1/sailors.txt new file mode 100644 index 0000000..45b5966 --- /dev/null +++ b/npc/003-1/sailors.txt @@ -0,0 +1,180 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// The major reward from this one is knowledge about secret passages on Tulimshar. +// The quest pays what you spend, except for travel fees. The real reward here +// is knowledge, the fishing rod you'll need to make anyway, 150 XP and 20 JExp. +// 1 - First Quest assigned +// 2 - First Quest Complete +// 3 - Second Quest assigned +// 4 - Second Quest Complete + +003-1,112,93,0 script Sailors#003-1 NPC_ELVEN_MAN_TRADER_SITTING,{ + if (BaseLevel < 12) goto L_Complete; + .@q = getq(TulimsharQuest_Sailors); + if (.@q == 4) + goto L_Complete; + + if (.@q == 1) + goto L_Report1; + if (.@q == 3) + goto L_Report2; + mesn; + mesq lg("Hey, pal. This spot is very good, I can see the whole town from here."); + next; + mesn; + mesq lg("I am a trader from Nard's ship. We actually need supplies. Help us, and I'll help you."); + next; + mesn strcharinfo(0); + menu + rif(.@q == 0, l("What do you need?")), L_Quest1, + rif(.@q == 2, l("What do you need?")), L_Quest2, + l("Maybe another time."), -; + close; + +L_Complete: + hello; + end; + +L_Quest1: + mes ""; + mesn; + mesq l("You're willing to help? Perfect. We need @@ @@, @@ @@ and @@ @@ for travel.", 1, getitemlink(FishBox), 2, getitemlink(CroconutBox), 3, getitemlink(CottonCloth)); + next; + mesn; + mesq l("Cotton Cloth is sold at Candor, Croconut Boxes can be bought at the market. Fish, however, is a little more tricky."); + next; + mesn; + mesq l("If you hug the wall, there's a secret passage somewhere there. Well, there are many secret passages at Tulimshar."); + next; + mesn; + mesq l("You will find a fisher, maybe he is having a good time and can sell you a few boxes. And remember to keep your eye open."); + setq TulimsharQuest_Sailors, 1; + next; + mesn; + mesq l("There are secret caves on this city. In fact, Tulimshar holds many misteries."); + mesc l("Take care: Secret passages can be dangerous places (or not)."); + tutmes l("You can get Croconut from Crocotree, and fill @@ purchased in the market.", getitemlink(EmptyBox)), l("Protip"), false; + close; + +L_Report1: + mesn; + mes l("@@/1 @@", countitem(FishBox), getitemlink(FishBox)); + mes l("@@/2 @@", countitem(CroconutBox), getitemlink(CroconutBox)); + mes l("@@/3 @@", countitem(CottonCloth), getitemlink(CottonCloth)); + if (countitem(FishBox) < 1 || + countitem(CroconutBox) < 2 || + countitem(CottonCloth) < 3) + close; + next; + select + l("[Deliver all goods.]"), + l("[Maybe another time.]"); + mes ""; + if (@menu == 2) + close; + if (countitem(FishBox) < 1 || + countitem(CroconutBox) < 2 || + countitem(CottonCloth) < 3) { + mesn; + mesq l("Liar. Where are the goods? Are you trying to cheat me?!"); + next; + mesn; + mesq l("You know Jesus Saves hates cheaters, right? If Saulc didn't asked me to double-check stuff..."); + next; + mesn; + mesq l("For short, you would have lost all your items, wouldn't get anything, and there would be no refunds."); + next; + mesn; + mesq l("That's just a friendly advise. That's how things works around here."); + close; + } + inventoryplace Dagger, 1, CottonShirt, 1; + delitem FishBox, 1; // 7.500 GP → 3.750 GP + delitem CroconutBox, 2; // 12.000 GP → 10.600~11.800 GP + delitem CottonCloth, 3; // 2.000 GP + getexp 1508, 15; + Zeny=Zeny+3750; // Profit: 1800 GP → 2.400~1.200 GP + getitem Dagger, 1; + getitem CottonShirt, 1; + setq TulimsharQuest_Sailors, 2; + mesn; + mesq lg("Many thanks, your help has been invaluable. We're now capable to travel at ease."); + next; + mesn; + mesq l("Take care. Here, take this @@. You can sell it for some quick-spot cash, or use it as a weapon.", getitemlink(Dagger)); + close; + +// Second Quest +L_Quest2: + mes ""; + mesn; + mesq l("You're willing to help? Perfect. We need @@ @@, @@ @@ and @@ @@ for travel.", 2, getitemlink(FishBox), 5, getitemlink(CroconutBox), 7, getitemlink(CottonCloth)); + next; + mesn; + mesq l("Cotton Cloth is sold at Candor, Croconut Boxes can be bought at the market. Fish, however, is a little more tricky."); + next; + mesn; + mesq l("If you hug the wall, there's a secret passage somewhere there. Well, there are many secret passages at Tulimshar."); + next; + mesn; + mesq l("You will find a fisher, maybe he is having a good time and can sell you a few boxes. And remember to keep your eye open."); + setq TulimsharQuest_Sailors, 3; + next; + mesn; + mesq l("There are secret caves on this city. In fact, Tulimshar holds many misteries."); + mesc l("Take care: Secret passages can be dangerous places (or not)."); + close; + +L_Report2: + mesn; + mes l("@@/2 @@", countitem(FishBox), getitemlink(FishBox)); + mes l("@@/5 @@", countitem(CroconutBox), getitemlink(CroconutBox)); + mes l("@@/7 @@", countitem(CottonCloth), getitemlink(CottonCloth)); + if (countitem(FishBox) < 2 || + countitem(CroconutBox) < 5 || + countitem(CottonCloth) < 7) + close; + next; + select + l("[Deliver all goods.]"), + l("[Maybe another time.]"); + mes ""; + if (@menu == 2) + close; + if (countitem(FishBox) < 2 || + countitem(CroconutBox) < 5 || + countitem(CottonCloth) < 7) { + mesn; + mesq l("Liar. Where are the goods? Are you trying to cheat me?!"); + next; + mesn; + mesq l("You know Jesus Saves hates cheaters, right? If Saulc didn't asked me to double-check stuff..."); + next; + mesn; + mesq l("For short, you would have lost all your items, wouldn't get anything, and there would be no refunds."); + next; + mesn; + mesq l("That's just a friendly advise. That's how things works around here."); + close; + } + delitem FishBox, 2; // 7.500 GP → 3.750 GP + delitem CroconutBox, 5; // 12.000 GP → 10.600~11.800 GP + delitem CottonCloth, 7; // 2.000 GP + getexp 1508, 15; + Zeny=Zeny+15750; // Profit: 1800 GP → 2.400~1.200 GP + setq TulimsharQuest_Sailors, 4; + mesn; + mesq lg("Many thanks, your help has been invaluable. We're now capable to travel at ease."); + next; + mesn; + mesq l("Take care. This is a dangerous world, but I think that worse than being attacked by sea monsters, is starving from hunger!"); + close; + + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} diff --git a/npc/003-1/sarah.txt b/npc/003-1/sarah.txt new file mode 100644 index 0000000..6038389 --- /dev/null +++ b/npc/003-1/sarah.txt @@ -0,0 +1,108 @@ +// TMW2 Script +// Author: +// Saulc +// Jesusalva +// DangerDuck +// Description: +// Random NPC without any purpose but to give SerfHat. Uh. +// TODO: Could be repeatable quest (eg. 60 GP for a cake every day, so you can have a 10 GP profit selling cakes) + +003-1,90,144,0 script Sarah NPC_FEMALE_TONORI,{ + function quest_completed; + function quest_open; + function quest_started; + function AssignGHQ; + + // Main Loop + do + { + .@q = getq(TulimsharQuest_Sarah); + if (.@q == 1) + quest_completed(); + select + rif(!.@q, l("Hello, I'm new here! Can I help you?")), + menuaction(l("Quit")); + + switch (@menu) { + case 1: + quest_started(); + break; + } + } while (@menu != 2); + + closedialog; + goodbye; + close; + +// Quest completed +function quest_completed { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("It was so tasty, I can't eat anything more... Thank you."); + AssignGHQ(); + return; +} + +// Quest Core +function quest_started { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Oh, Welcome then."); + speech S_LAST_NEXT, + l("Can you bring me 5 pieces of Cherry Cake? Pretty please?"); + do + { + select + l("Here they are!"), + menuaction(l("Quit")); + + switch (@menu) { + case 1: + quest_open(); + break; + case 2: + mesc l("Protip: @@ is dropped by @@. It is a tough monster, you might need some strategy to kill it. @@ can be bought in shops.", getitemlink(CherryCake), getmonsterlink(GiantMaggot)); + mesc l("%s can also be obtained from %s, at a lower drop rate.", getitemlink(CherryCake), getmonsterlink(Duck)); + next; + break; + } + } while (@menu != 2); + return; +} + +// Quest check +function quest_open { + if (countitem(CherryCake) >= 5) { + speech S_FIRST_BLANK_LINE, + l("You brought me 5 @@ ! Here is your @@, as promised.",getitemlink(CherryCake), getitemlink(SerfHat)); + delitem CherryCake,5; + getitem SerfHat,1; + getexp 80, 2; + setq TulimsharQuest_Sarah, 1; + close; + } else { + speech S_FIRST_BLANK_LINE, + l("Sorry, that is not the cake I love."); + close; + } + return; +} + +// Grand Hunter Quest (post-quest) +function AssignGHQ { + GHQ_Assign(Duck, "Holiday"); + end; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TerraniteArmor); + setunitdata(.@npcId, UDT_HEADMIDDLE, RaidTrousers); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyesT); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 19); + setunitdata(.@npcId, UDT_HAIRCOLOR, 16); + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/003-1/sewer.txt b/npc/003-1/sewer.txt new file mode 100644 index 0000000..a5faabd --- /dev/null +++ b/npc/003-1/sewer.txt @@ -0,0 +1,49 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Notes: +// TW: Tulimshar, West +// TE: Tulimshar, East + +003-1,56,84,0 script #Sewer-TW NPC_NO_SPRITE,{ + if (getq(TulimsharQuest_Sewers) == 0 && !countitem(MirrorLakeArmor)) { + dispbottom l("The sewer mouth is locked."); + end; + } + if (BaseLevel < 25 && !countitem(MirrorLakeArmor)) end; + + mesc l("Descend into Tulimshar sewers?"); + if (askyesno() == ASK_YES) { + closedialog; + warp "003-1-1", 57, 100; + dispbottom l("Blame Saulc."); + end; + } + close; +} + +003-1,115,111,0 script #Sewer-TE NPC_NO_SPRITE,{ + if (getq(TulimsharQuest_Sewers) == 0 && !countitem(MirrorLakeArmor)) { + dispbottom l("The sewer mouth is locked."); + end; + } + if (BaseLevel < 25 && !countitem(MirrorLakeArmor)) end; + + mesc l("Descend into Tulimshar sewers?"); + if (askyesno() == ASK_YES) { + closedialog; + warp "003-1-1", 143, 128; + dispbottom l("Blame Saulc."); + end; + } + close; +} + +003-1,73,133,0 script #ToTheater NPC_HIDDEN,0,0,{ + end; + +OnTouch: + warp "003-9-1", 27, 37; + RegEasterEgg(EE_THEATER, 2); + end; +} diff --git a/npc/003-1/ship.txt b/npc/003-1/ship.txt new file mode 100644 index 0000000..e23765d --- /dev/null +++ b/npc/003-1/ship.txt @@ -0,0 +1,44 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// This script controls access to Ships, fixing variables. + +003-1,82,68,0 script TulimShip NPC_HIDDEN,0,0,{ + +OnTouch: + EnterTown("Tulim"); + goto L_Warp; + +L_Warp: + warp "002-3@"+LOCATION$, 31, 28; + closedialog; + close; +} + +003-1,120,25,0 script TulimShip#M NPC_HIDDEN,0,0,{ + +OnTouch: + EnterTown("Tulim"); + goto L_Warp; + +L_Warp: + /* Is Hurnscald already liberated? Precendence. */ + if (!$HURNS_LIBDATE) { + .@online=$@BG1_SIZE; + if (is_gm()) + dispbottom l("GMs are NOT allowed on Hurnscald Liberation day."); + else if (.@online) + dispbottom l("Right click on the NPC to join the Liberation Force on Hurnscald."); + else + dispbottom l("The ship is locked, probably unable to leave port."); + + if (!.@online) + npctalk3 col(l("A Game Master is required to begin the Liberation Day."), 1); + end; + } + + warp "016-1@"+LOCATION$, 21, 26; + closedialog; + close; +} diff --git a/npc/003-1/shop.txt b/npc/003-1/shop.txt new file mode 100644 index 0000000..86ca29b --- /dev/null +++ b/npc/003-1/shop.txt @@ -0,0 +1,67 @@ +// TMW2 scripts. +// Author: +// Saulc + +003-1,108,110,0 trader Shop#bazar1 NPC_NO_SPRITE,{ + +OnInit: + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem YellowDye, -1, 2; + sellitem RoundLeatherShield, -1, 2; + sellitem Knife, -1, 5; + sellitem TrainingAmmoBox, -1, rand(2,5); + sellitem ArrowAmmoBox, -1, rand(2,4); + sellitem Arrow, -1, 30000; + sellitem TreasureMap, 600, 1; + sellitem DesertHelmet, -1, 1; + sellitem InfantryHelmet, -1, 1; + sellitem LeatherGloves, -1, 1; + sellitem DesertHat, -1, 8; + sellitem SilkRobe, -1, 1; + sellitem CottonCloth, -1, 2; + sellitem Bread, -1, 15; + sellitem CroconutBox, rand(2800,2900), 6; + sellitem EmptyBottle, -1, 3; // You can buy some empty bottles here, but they're scarse + sellitem EmptyBox, -1, 4; + + .sex = G_OTHER; + .distance = 3; + end; + +OnClock0621: + restoreshopitem DesertHelmet, 1; + restoreshopitem InfantryHelmet, 1; + restoreshopitem LeatherGloves, 1; +OnClock1210: +OnClock1757: +OnClock0001: + restoreshopitem YellowDye, 2; + restoreshopitem RoundLeatherShield, 2; + restoreshopitem Knife, 5; + restoreshopitem TrainingAmmoBox, rand(2,5); + restoreshopitem ArrowAmmoBox, rand(2,4); + restoreshopitem Arrow, rand(10000,30000); + restoreshopitem TreasureMap, 600, 1; + restoreshopitem DesertHat, 8; + restoreshopitem SilkRobe, 1; + restoreshopitem CottonCloth, 2; + restoreshopitem Bread, 15; + restoreshopitem CroconutBox, rand(2800,3050), 6; + restoreshopitem EmptyBottle, 3; + restoreshopitem EmptyBox, 4; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} + diff --git a/npc/003-1/silvia.txt b/npc/003-1/silvia.txt new file mode 100644 index 0000000..a2f31d5 --- /dev/null +++ b/npc/003-1/silvia.txt @@ -0,0 +1,131 @@ +// TMW2 Script. +// Author: +// Saulc +// Jesusalva + +003-1,109,150,0 script Silvia NPC_FEMALE_ARGAES,{ + + if (strcharinfo(0) == $MOST_HEROIC$) npctalk l("Oh my, the great @@ has come to talk to me!", $MOST_HEROIC$); + if (getq(TulimsharQuest_Swezanne) == 4) goto L_Message; + .@q2=getq2(TulimsharQuest_Swezanne); + if (.@q2 < santime()) goto L_Unallowed; + if (getq(TulimsharQuest_Swezanne) == 1 && getq(TulimsharQuest_Lifestone) < 2) goto L_Lifestone; + if (strcharinfo(0) != $MOST_HEROIC$) hello; + if (getq(MineQuest_Naem) == 1) goto L_Hubby; + end; + +L_Message: + mesn strcharinfo(0); + mesq l("Your mother asked me to say that she loves you."); + next; + mesn; + mesq l("Oh no, not another stranger she sends me to tell that!"); + next; + mesn; + mesq l("She never leaves the shade of that tree, she is always sending messages by other people!!"); + next; + mesn; + mesq l("Oh well... That's my mother, and this is why I love her."); + next; + inventoryplace CottonGloves, 1; + getitem CottonGloves, 1; + getexp 105,0; + setq1 TulimsharQuest_Swezanne, 1; + mesn; + mesq l("Thank you, @@. Please take this pair of gloves as a thank you.", strcharinfo(0)); + close; + +L_Lifestone: + mesn; + mesq l("Hey! Good to see you. I was thinking how I could repay for what you've done for my mother."); + next; + mesn; + mesq l("I can make you a @@, and for that I will want a @@ and 500 GP.", getitemlink(LifestonePendant), getitemlink(Lifestone)); + if (getq(TulimsharQuest_Lifestone) == 0) { + next; + mesn; + mesq l("I am not sure of who makes or haves Lifestones. Try looking outside the city. Who knows."); + close; + } + menu + rif(Zeny >= 500 && countitem(Lifestone) > 0, l("Yes, I accept the pendant!")), -, + l("Not now, but I may be back later."), L_Close; + + // Whaaaat, this is a major error affecting several scripts! + if (Zeny < 500 || countitem(Lifestone) < 0) { + atcommand("@request Someone is cheating, call Jesusalva at once!"); + atcommand("@ban \""+strcharinfo(0)+"\" 15mn"); // I truly hope they bother Jesusalva they were banned + disablenpc "Silvia"; + close; + } + Zeny-=500; + delitem Lifestone, 1; + getitem LifestonePendant, 1; + setq(TulimsharQuest_Lifestone, 2); + mes ""; + mesn; + mesq l("There you go! Thanks for all the help!"); + close; + +L_Unallowed: + mesn; + mesq l("Ah, I wonder how my mother Swezanne is faring..."); + next; + mesn; + mesq l("She must be thristy, fighting monsters on this sun... If somebody could give her Cactus Potions..."); + close; + +L_Hubby: + mesc l("Did we brought everything Naem asked?"); + mesf l("%d/%d %s",countitem(Aquada), 7, getitemlink(Aquada)); + mesf l("%d/%d %s",countitem(PiouLegs), 6, getitemlink(PiouLegs)); + mesf l("%d/%d %s",countitem(Cheese), 3, getitemlink(Cheese)); + mesf l("%d/%d %s",countitem(HalfCroconut), 3, getitemlink(HalfCroconut)); + mesf l("%d/%d %s",countitem(PurpleBlobime), 1, getitemlink(PurpleBlobime)); + next; + if (askyesno() == ASK_NO) { closeclientdialog; end; } + if (countitem(Aquada) < 7 || + countitem(PiouLegs) < 6 || + countitem(Cheese) < 3 || + countitem(HalfCroconut) < 3 || + countitem(PurpleBlobime) < 1) { + mesc l("You lied, and this caused a headache! You should NEVER lie to NPCs, because they may steal your items!"); + callfunc("SC_Bonus", 15, SC_POISON, 10); + close; + } + inventoryplace IcedBottle, 1; + delitem Aquada, 7; + delitem PiouLegs, 6; + delitem Cheese, 3; + delitem HalfCroconut, 3; + delitem PurpleBlobime, 1; + getitembound IcedBottle, 1, 4; + setq MineQuest_Naem, 2; + mesn; + mesq l("Hm, groceries? Well, figure out Naem would ask someone to deliver them."); + next; + mesn; + mesq l("Thanks, please tell him I'll make his favorite dish. Oh, could you also deliver him this %s?", getitemlink(IcedBottle)); + next; + mesn; + mesq l("The mines are over a Volcano. Unless you're a Redy, the heat will slowly chip away your health if you go deep. But drinking iced water can improve temporarily your situation."); + close; + +L_Close: + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyesD); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + //setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); // TODO + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 21); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/003-1/soul-menhir.txt b/npc/003-1/soul-menhir.txt new file mode 100644 index 0000000..5430c6e --- /dev/null +++ b/npc/003-1/soul-menhir.txt @@ -0,0 +1,20 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Soul Menhir + +003-1,40,48,0 script Soul Menhir#tulim NPC_SOUL_DESERT,{ + @map$ = "003-1"; + setarray @Xs, 40, 41, 40; + setarray @Ys, 49, 49, 49; + @x = 0; + @y = 0; + SoulMenhir(); + @map$ = ""; + deletearray @Xs; + deletearray @Ys; + @x = 0; + @y = 0; + close; +} diff --git a/npc/003-1/swezanne.txt b/npc/003-1/swezanne.txt new file mode 100644 index 0000000..8e40d63 --- /dev/null +++ b/npc/003-1/swezanne.txt @@ -0,0 +1,146 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Note: +// DO NOT USE QUEST STATUS 2. Thing from past. +// Structure: +// GEQ1 +// 0 - Not Accepted +// 1 - Finished Once +// 3 - Silvia Arc +// GEQ2 +// Timer +// GEQ3 +// Times complete + +003-1,70,100,0 script Swezanne NPC_FEMALE,{ + .@q=getq(TulimsharQuest_Swezanne); + mesn; + mesq l("Hi."); + next; + .@q2=getq2(TulimsharQuest_Swezanne); + if (.@q == 0) goto L_Quest; + else if (.@q == 3) goto L_Silvia; + else if (.@q2 < santime()) goto L_Repeat; + if ($FIRESOFSTEAM < 10) + mesq l("I fight every day in hopes to meet Andrei, the famous hero who prevented Hurnscald from total destruction against a horde of monsters alone."); + else + mesq l("I fight every day in hopes to be like Andrei, the famous hero who prevented Hurnscald from total destruction against a horde of monsters alone."); + if ($MOST_HEROIC$ != "") goto L_Heroics; + close; + +L_Heroics: + next; + mesn; + .@d=rand2(1,6); + + if (.@d == 1) .@deed$="protected our cities!"; + else if (.@d == 2) .@deed$="did great acts of bravery!"; + else if (.@d == 3) .@deed$="is just awesome!"; + else if (.@d == 4) .@deed$="killed a monster army single-handed!"; + else if (.@d == 5) .@deed$="proved their worth in battlefield!"; + else if (.@d == 6) .@deed$="impressed even the High Council!"; + else .@deed$="is awesome like me!"; // Should not happen + + mesq l("I hope to one day be like @@, who @@", $MOST_HEROIC$, .@deed$); + close; + +L_Quest: + mesq l("Ah, fighting monsters under this desert heat makes me thirsty. But someone must do this job, otherwise Tulimshar could fall."); + next; + mesq l("Maybe you could bring me 5 delicious @@? They have a great effect in quenching thirst and recovering vigour.", getitemlink(CactusDrink)); + menu + rif(countitem(CactusDrink) >= 5, l("Here they are!")), L_Finish, + l("I'll get to it."), L_Close; + close; // double sure + +L_Repeat: + mesq l("Ah, fighting monsters under this desert heat makes me thirsty. But someone must do this job, otherwise Tulimshar could fall."); + next; + mesq l("Maybe you could bring me 5 delicious @@? They have a great effect in quenching thirst and recovering vigour.", getitemlink(CactusPotion)); + menu + rif(countitem(CactusPotion) >= 5, l("Here they are!")), L_Finish2, + l("I'll get to it."), L_Close; + close; + +L_Finish2: + delitem CactusPotion, 5; + getexp 37, 0; + Zeny = (Zeny + 350); // 5*35 = 175 base (+100%) + .@q3=getq3(TulimsharQuest_Swezanne)+1; + setq2 TulimsharQuest_Swezanne, gettimetick(2)+60*60*24; + setq3 TulimsharQuest_Swezanne, .@q3; + if (!(.@q3 % 30)) { + //inventoryplace MercCard_Swezanne, 1; + getitem MercCard_Swezanne, 1; + mesn; + mesq l("Thanks for helping me this month. Here is my card, just call me if you need."); + } + close; + +L_Finish: + delitem CactusDrink, 5; + getexp 63, 4; // 4 Job points! A "great" reward. + Zeny = (Zeny + 250); // 5*25 = 125 base (+100%) + setq TulimsharQuest_Swezanne, 3; + setq2 TulimsharQuest_Swezanne, gettimetick(2)+60*60*24; + close; + +L_Silvia: + mesn; + mesq l("Ah, my daughter Silvia is so far away... But I don't want to leave the shade of this tree..."); + next; + mesn; + mesq l("Perhaps you could tell her how much I love her? It is already some days since I last talked to her."); + next; + mesn; + mesq l("I don't want her to think that I am a bad mother."); + mes ""; + menu + l("Sure, I'll do it."), L_SilviaAccept, + l("One day more or less won't make a difference."), L_Close; + +L_SilviaAccept: + mes ""; + mesn; + mesq l("Oh, thank you! My daughter means a lot to me."); + mesc l("Silvia is in Noble District of Tulimshar."); + setq TulimsharQuest_Swezanne, 4; + close; + +L_Close: + if (.@q == 0) { + mesc l("Protip: @@ can be found from @@ and @@. For the later one, ask Lua for strategies.", getitemlink(CactusDrink), getmonsterlink(Maggot), getmonsterlink(GiantMaggot)); + mesc l("Protip 2: Drop rates are low. Try other quests while you slay mobs for this one."); + next; + } + + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, BromenalChest); + setunitdata(.@npcId, UDT_HEADMIDDLE, CottonTrousers); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .sex = G_FEMALE; + .distance = 5; + + + /* + // Preventive check against faulty update. Must be removed afterwards. + // UPDATE `quest` SET `count1` = '3' WHERE `quest`.`quest_id` = 54 AND `count1` != '1'; + .@nb = query_sql("select `char_id` from `quest` WHERE ((`count1`=1 or `count1`=2) and `quest_id`=54) LIMIT 2", .@name$); + if (getarraysize(.@name$) > 0) { + debugmes "FATAL ERROR: Quest log not updated."; + debugmes "disabling Swezanne to prevent weirder bugs."; + disablenpc .name$; + } + */ + end; +} diff --git a/npc/003-1/taree.txt b/npc/003-1/taree.txt new file mode 100644 index 0000000..1aa0593 --- /dev/null +++ b/npc/003-1/taree.txt @@ -0,0 +1,17 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Mouboo Renting (var MOUBOO_RENTTIME ) + +003-1,63,31,0 script Taree NPC_ELVEN_MAN_MOUBOO_SHOP,{ + MoubooRent(); + closeclientdialog; + goodbye(); + close; + +OnInit: + .distance=4; + end; +} + diff --git a/npc/003-1/tinris.txt b/npc/003-1/tinris.txt new file mode 100644 index 0000000..92404da --- /dev/null +++ b/npc/003-1/tinris.txt @@ -0,0 +1,202 @@ +// TMW2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Part of Anwar Field quest + +003-1,66,30,0 script Tinris NPC_ELF,{ + .@q=getq(TulimsharQuest_AnwarField); + + // Results: 6 - nothing. 7- bug feast. + // On status 7, you need to talk to Eomie. + // Then Eomie will finish stuff for you, and it's reward time. + if (.@q == 10) goto L_Gift; + if (.@q == 7) goto L_Success; + if (.@q == 6) goto L_Fail; + if (.@q == 4) goto L_Craft; + if (.@q == 3) goto L_Back; + if (.@q == 2) goto L_Start; + if (.@q == 1) goto L_Refuse; + + // Placeholder dialog + if (.@q == 5) + mesc l("I still have Anwar's fertilizer with me."); + mesn; + mesq l("We elves have greater affinity for magic than humans."); + next; + mesq l("However, our skill growth rate is much smaller than those of humans."); + next; + mesq l("In the end, elves would have quite the age to rivalize with human wizards."); + close; + +// READ THE HOLY DIALOGS!! +L_Refuse: + mesn strcharinfo(0); + mesq l("Hello! Anwar sent me to get fertilizer to save Tulimshar from famine, could you help me?"); + next; + mesn; + mesq l("No."); + close; + +L_Start: + mesn strcharinfo(0); + mesq l("Hello, could I help you in exchanger of fertilizer?"); + next; + mesn; + mesq l("I like people like you, straight to the subject."); + next; + mesn; + mesq l("The birthday of me and my girlfriend is coming up. Bring me 12 @@ and I'll make the fertilizer for you.", getitemlink(CherryCake)); + setq TulimsharQuest_AnwarField, 3; + close; + +L_Back: + mesn; + mesq l("So, did you brought me the twelve cherry cakes?"); + mes ""; + mesn strcharinfo(0); + if (askyesno() != ASK_YES) { + close; + } + mes ""; + if (countitem("CherryCake") < 12) + goto L_Lying; + + delitem CherryCake, 12; + getexp 200, 5; + setq TulimsharQuest_AnwarField, 4; + mesn; + mesq l("Okay, that is very useful. However, I do need a few reagents to make it."); + next; + mesn; + mesq l("Please, come back later. I'll see whatever I can fetch for that."); + close; + +L_Craft: + mesn; + mesq l("So, for the fertilizer. The thing is, all that thing is unstable."); + next; + mesn; + mesq l("This fertilizer is projected to protect the plants from plagues, bugs, scorpions and maggots, trying to not be a plague itself."); + next; + mesn; + mesq l("That's very, very risky. I need @@ @@ and @@ @@ to make a Potion to you, and I won't warrant it will work.", 3, getitemlink(Plushroom), 9, getitemlink(MaggotSlime)); + next; + mesn; + mesq l("Give that to whoever needs them, and see if it works. Then come tell me the result. Do you have the reagents?"); + if (askyesno() != ASK_YES) + close; + mes ""; + + if (countitem(Plushroom) < 3|| + countitem(MaggotSlime) < 9) + goto L_Lying; + + delitem Plushroom, 3; + delitem MaggotSlime, 9; + setq1 TulimsharQuest_AnwarField, 5; + + mesn; + mesq l("Here it is. Come back to report the results."); + close; + +L_Fail: + setq1 TulimsharQuest_AnwarField, 4; + .@q2=getq2(TulimsharQuest_AnwarField); + if (.@q2 < 10) + getexp 180-(.@q2*10), 0; + else + getexp 90, 0; + mesn; + switch (.@q2) { + case 1: + mesq l("Well, that could fail, I said. Here is some experience."); + break; + case 2: + mesq l("Don't worry, third time is the charm. Here is some experience. Let's try again."); + break; + case 3: + mesq l("Okay, here is some experience, and forgot what I've said before. We can try again."); + break; + case 4: + mesq l("Don't worry, I've tweaked my formula this time. Here is some experience and let's try again!"); + break; + case 5: + mesq l("I'm sorry, I just... Maybe if...? Aha! Here's the EXP, ready for a next go?"); + break; + case 6: + mesq l("Uhm, maybe I mashed the Plushroom too hard this time. Here's EXP as usual, let's try again?"); + break; + case 7: + mesq l("I shall not fail any further, I think my new formula is perfect! Here's the EXP, but I need material to use it!"); + break; + case 8: + mesq l("I never knew you could fail THAT hard. I've took Saulc's Fertilizer's recipe, success chance is of 100% if you want to try again."); + break; + default: + mesq l("Well, that could fail, I said. Here is some experience."); + break; + } + next; + if (countitem(Plushroom) >= 3 && + countitem(MaggotSlime) >= 9) + goto L_Craft; + mesn; + mesq l("Now go, and fetch the materials again. I'll make another fertilizer for you."); + close; + +L_Success: + mesn; + mesq l("WHAT? The farm is plagued with insects?!"); + next; + mesn; + mesq l("Quick, tell that to Eomie. She knows how to do Bug Bombs which won't harm the plants!"); + close; + +L_Gift: + .@q2=getq2(TulimsharQuest_AnwarField); + if (.@q2 & 1) { + mesn; + mesq l("Thanks for the nice gift!"); + close; + } + // Tip. WHAT DID YOU DID WITH THE BOUND ITEM? IT SHOULD BE HARD TO GET RID OF IT... + if (countitem(TortugaShell) < 1) { + mesn; + mesq l("Ah, I wish I got something for helping people out..."); + close; + } + mesn strcharinfo(0); + mesq l("Anwar sent you this, erm, hum... @@.", getitemlink(TortugaShell)); + next; + setq2 TulimsharQuest_AnwarField, .@q2+1; + delitem TortugaShell, 1; + getexp 95, 1; + mesn; + mesq l("WOW, THIS IS AWESOME! Many, many thanks!!"); + close; + +L_Lying: + mesn; + mesq l("Ah, so you think you can fool me?"); + next; + percentheal -5, -5; + warp "Save", 0, 0; + dispbottom l("Ah... Was I warped?"); + closedialog; + close; + +OnInit: + .@npcId = getnpcid(.name$); + //setunitdata(.@npcId, UDT_HEADTOP, 2929); // TODO: This NPC is an Elf and therefore, CANNOT use NPCEyes. + setunitdata(.@npcId, UDT_HEADMIDDLE, ForestArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 3); + setunitdata(.@npcId, UDT_HAIRCOLOR, 19); + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/003-1/town.txt b/npc/003-1/town.txt new file mode 100644 index 0000000..593785d --- /dev/null +++ b/npc/003-1/town.txt @@ -0,0 +1,33 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Reset LOCATION$ when entering a town + +003-1,60,157,0 script #LocTulim NPC_HIDDEN,4,1,{ +OnTouch: + EnterTown("Tulim"); end; +} + +// Crocotree spawn experiment +003-1,0,0,0 script #CrocotreeTulim01 NPC_HIDDEN,{ + end; +// Spawn the crocotrees +OnInit: + areamonster "003-1", 20, 20, 140, 160, strmobinfo(1, CrocoTree), CrocoTree, 1, .name$+"::OnRespawn"; + end; + +// Script Generated +OnRespawn: + initnpctimer; + end; +OnTimer61000: + stopnpctimer; + areamonster "003-1", 20, 20, 140, 160, strmobinfo(1, CrocoTree), CrocoTree, 1, .name$+"::OnRespawn"; + end; +} + +003-1,0,0,0 duplicate(#CrocotreeTulim01) #CrocotreeTulim02 NPC_NO_SPRITE +003-1,0,0,0 duplicate(#CrocotreeTulim01) #CrocotreeTulim03 NPC_NO_SPRITE +003-1,0,0,0 duplicate(#CrocotreeTulim01) #CrocotreeTulim04 NPC_NO_SPRITE +003-1,0,0,0 duplicate(#CrocotreeTulim01) #CrocotreeTulim05 NPC_NO_SPRITE diff --git a/npc/003-1/wateranimation.txt b/npc/003-1/wateranimation.txt new file mode 100644 index 0000000..b3b58e8 --- /dev/null +++ b/npc/003-1/wateranimation.txt @@ -0,0 +1,61 @@ +// TMW2 scripts. +// Author: +// gumi +// Reid +// Saulc +// Description: +// Water animations, splash, fishes, etc... + +003-1,83,128,0 script #water_animation0 NPC_WATER_SPLASH,{ + + fishing; // begin or continue fishing + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +003-1,87,125,0 duplicate(#water_animation0) #water_animation1 NPC_WATER_SPLASH +003-1,88,120,0 duplicate(#water_animation0) #water_animation2 NPC_WATER_SPLASH +003-1,76,120,0 duplicate(#water_animation0) #water_animation3 NPC_WATER_SPLASH +003-1,75,127,0 duplicate(#water_animation0) #water_animation4 NPC_WATER_SPLASH +003-1,79,111,0 duplicate(#water_animation0) #water_animation5 NPC_WATER_SPLASH +003-1,82,105,0 duplicate(#water_animation0) #water_animation6 NPC_WATER_SPLASH +003-1,85,110,0 duplicate(#water_animation0) #water_animation7 NPC_WATER_SPLASH +003-1,88,113,0 duplicate(#water_animation0) #water_animation8 NPC_WATER_SPLASH +003-1,86,126,0 duplicate(#water_animation0) #water_animation9 NPC_WATER_SPLASH +003-1,87,132,0 duplicate(#water_animation0) #water_animation10 NPC_WATER_SPLASH +003-1,83,111,0 duplicate(#water_animation0) #water_animation11 NPC_WATER_SPLASH +003-1,78,144,0 duplicate(#water_animation0) #water_animation12 NPC_WATER_SPLASH +003-1,83,140,0 duplicate(#water_animation0) #water_animation13 NPC_WATER_SPLASH +003-1,72,147,0 duplicate(#water_animation0) #water_animation14 NPC_WATER_SPLASH +003-1,72,122,0 duplicate(#water_animation0) #water_animation15 NPC_WATER_SPLASH + + +003-1,71,58,0 script #lowsea_tulim0 NPC_WATER_SPLASH,{ + + fishing; // begin or continue fishing + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + .fishing_rod=SmallFishingNet; + .net_ratio=3; + .catch_time=8000; // You have 3 more seconds to pull here + .wait_time_min=8000; + .wait_time_max=21000; + .pull_rand_max=1600; + .regen_time=30; + end; +} + +003-1,95,47,0 duplicate(#lowsea_tulim0) #lowsea_tulim1 NPC_WATER_SPLASH +003-1,121,48,0 duplicate(#lowsea_tulim0) #lowsea_tulim2 NPC_WATER_SPLASH +003-1,121,36,0 duplicate(#lowsea_tulim0) #lowsea_tulim3 NPC_WATER_SPLASH +003-1,32,107,0 duplicate(#lowsea_tulim0) #lowsea_tulim4 NPC_WATER_SPLASH +003-1,25,78,0 duplicate(#lowsea_tulim0) #lowsea_tulim5 NPC_WATER_SPLASH +003-1,16,50,0 duplicate(#lowsea_tulim0) #lowsea_tulim6 NPC_WATER_SPLASH + diff --git a/npc/003-1/well.txt b/npc/003-1/well.txt new file mode 100644 index 0000000..c745a16 --- /dev/null +++ b/npc/003-1/well.txt @@ -0,0 +1,73 @@ +// TMW2 Script +// Author: +// Saulc +// Jesusalva + +003-1,45,80,0 script Well#003-1 NPC_NO_SPRITE,{ + if (countitem(Bucket) <= 0) { + mesc l("This well is too deep and you don't have a bucket."); + } else { + mesc l("Hey you have a bucket! Too bad there are, you know, HOLES on it, so you can see."); + mesc l("That's exactly why you can't use it to get water. Silly."); + } + if (!Zeny) + close; + next; + mesc l("Throw a coin?"); + if (askyesno() != ASK_YES) + close; + + mes ""; + Zeny=Zeny-1; + mesc l("You throw a coin into the well."); + next; + + .@n=rand(0, 10000); + if (.@n <= 10) { + getitem StrangeCoin, 1; + mes l("##9Hey wait... Your coin turned into a @@!", getitemlink(StrangeCoin)); + } else if (.@n <= 50) { + getitem CasinoCoins, 1; + mes l("##9Hey wait... Your coin turned into a @@!", getitemlink(CasinoCoins)); + } else if (.@n <= 100) { + Zeny=Zeny+2; + mesc l("Hey wait... You found 2 GP!"); + } else if (.@n <= 250) { + percentheal 100, 100; + mesc l("Hey wait... You're enveloped by a bright light and fully healed!"); + } else if (.@n <= 300) { + getexp rand(1,BaseLevel), rand(1,BaseLevel); + mesc l("Hey wait... You're enveloped by a bright light and gain experience!"); + } else if (.@n > 9900) { + mesc l("Hey wait... A monster!! Run for your life!!"); + getmapxy(.@m$, .@x, .@y, 0); + .@mobGID = monster(.@m$, .@x, .@y, "Croc", Croc, 1); + unitattack(.@mobGID, getcharid(CHAR_ID_ACCOUNT)); // Order the summoned monster to engage!! + } else if (.@n > 9850) { + mesc l("Hey wait... A monster!! Run for your life!!"); + getmapxy(.@m$, .@x, .@y, 0); + .@mobGID = monster(.@m$, .@x, .@y, "Blub", Blub, 1); + unitattack(.@mobGID, getcharid(CHAR_ID_ACCOUNT)); // Order the summoned monster to engage!! + } else if (.@n > 9800) { + mesc l("Hey wait... A monster!! Run for your life!!"); + getmapxy(.@m$, .@x, .@y, 0); + .@mobGID = monster(.@m$, .@x, .@y, "Red Scorpion", RedScorpion, 1); + unitattack(.@mobGID, getcharid(CHAR_ID_ACCOUNT)); // Order the summoned monster to engage!! + } else if (.@n > 9700) { + mesc l("Hey wait... A monster!! Run for your life!!"); + getmapxy(.@m$, .@x, .@y, 0); + .@mobGID = monster(.@m$, .@x, .@y, "Bat", Bat, 1); + unitattack(.@mobGID, getcharid(CHAR_ID_ACCOUNT)); // Order the summoned monster to engage!! + } else { + mesc l("Nothing happens."); + next; + mesc l("What did you expect?"); + } + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + diff --git a/npc/003-10/_import.txt b/npc/003-10/_import.txt new file mode 100644 index 0000000..32c8028 --- /dev/null +++ b/npc/003-10/_import.txt @@ -0,0 +1,8 @@ +// Map 003-10: Guard's House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-10/_warps.txt", +"npc/003-10/arnea.txt", +"npc/003-10/guarddevoir.txt", +"npc/003-10/kreist.txt", +"npc/003-10/officer.txt", +"npc/003-10/slots.txt", diff --git a/npc/003-10/_warps.txt b/npc/003-10/_warps.txt new file mode 100644 index 0000000..d0cb1fe --- /dev/null +++ b/npc/003-10/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-10: Guard's House warps +003-10,34,43,0 warp #003-10_34_43 1,0,003-10,33,58 +003-10,34,57,0 warp #003-10_34_57 1,0,003-10,33,42 +003-10,42,80,0 warp #003-10_42_80 0,0,003-1,114,84 diff --git a/npc/003-10/arnea.txt b/npc/003-10/arnea.txt new file mode 100644 index 0000000..8327e56 --- /dev/null +++ b/npc/003-10/arnea.txt @@ -0,0 +1,215 @@ +// TMW2 Scripts +// Author: +// 4144 +// Jesusalva +// Description: +// Arena for Duels and PVP (003-13,31,31) + +003-10,26,60,0 script Arnea NPC_ELF_F,{ + npctalk3 l("Hello!"); + if (gettime(GETTIME_MONTH) == DECEMBER) + mes("It's December! The arena will close for new year, be warned!"); + +L_Menu: + mes ""; + mesn; + mesq l("Welcome to the Arena. Select your action"); + menu + l("Create new arena"), L_NewArena, + l("Join existing arena"), L_JoinArena, + rif(is_staff(), l("Debug Information")), L_Debug, + l("Information"), L_Info, + l("Hall Of Honor"), L_Scoreboards, + l("Leave"), L_Quit; + +L_NewArena: + .@price=.price / max(1, reputation("Tulim")); + mes ""; + mesn; + if (Zeny < .@price) { + mesq l("You need @@ GP to use this arena.", .@price); + goto L_Menu; + } + + mesq l("Okay, which arena will you rent? Cost is @@ GP.", .@price); + menu + rif(Zeny > .@price, l("Rent arena")), -, + l("Give Up"), L_Quit; + + mes ""; + mesn; + mesq l("Please type a password for your Arena, it must be unique."); + input .@user_password$; + + // Leaving blank will close + if (.@user_password$ == "") + close; + + .@m = htget($@ARENAS, .@user_password$, -1); + if (.@m > 0) { + mes ""; + mesn; + mesq l("Sorry, this password was already used on another arena."); + next; + /* + mesn; + mesq l("You'll need to think on a new password!"); + next; + mesn; + mesq l("If you don't know what to use, try using the current date or something."); + next; + */ + mesn; + mesq l("Now, let's try again."); + next; + goto L_NewArena; + } + + + // XXX - Important Note - XXX + // map name MUST be only 4 chars long (eg. "abcd") on char instances + .@ID=.curinst; + .curinst+=1; + htput($@ARENAS, .@user_password$, .@ID); + + .@MAP$="ARENA@"+str(.@ID); + + // Create the arena + .@INSTID = instance_create("ARX@"+(.@ID), 0, IOT_NONE); + .@instanceMapName$ = instance_attachmap("003-13", .@INSTID, 0, .@MAP$); + instance_set_timeout(1800, 1800, .@INSTID); + instance_init(.@INSTID); + + // You are only charged once arena is all set + Zeny=Zeny-.@price; + + dispbottom l("Arena created, it can be used for 30 minutes."); + dispbottom l("Room password: @@", .@user_password$); + mes ""; + mesn; + mes l("Arena created, it can be used for 30 minutes."); + mes l("Room password: @@", .@user_password$); + if (is_staff()) mes l("Inst @@ Map @@", .@INSTID, .@instanceMapName$); + next; + + goto L_Menu; + + +L_JoinArena: + // FIXME + /* + warp "003-13", 31, 31; + close; + */ + mes ""; + mesn; + mesq l("Okay, to join an arena, you need the unique password. Leave blank if you don't know."); + + input .@user_password$; + .@m = htget($@ARENAS, .@user_password$, -1); + if (.@m > 0) { + /* + if(has_instance2("ARENA@"+.@m) >= 0) { + warp "ARENA@"+str(.@m), 31,31; + */ + if (.@m < .curinst-2000) { + // Delete the arena entry so this doesn't happens anymore + htput $@ARENAS, .@user_password$, 0; + // Explain this arena has expired + mes ""; + mesn; + mesq l("Sorry, that arena is already closed."); + next; + mesn; + mesq l("All arenas stay open for only 30 minutes after being purchased."); + } else { + closeclientdialog; + // If the arena has expired, this will fail. + // But because dialog was closed, players won't notice anything. + // It won't have any visual effect, just like when you don't use a password. + // However, a debug message will be printed to console. + // If I start seeing spam of this debug message, I'll take appopriate + // measures, including and not limited to permanent ban, including IP ban. + warp "ARENA@"+.@m, 31, 31; + dispbottom l("Good luck!"); + } + } + close; + + +L_Debug: + mes "npc name: " + .name$; + mes "npc ext name: " + .extname$; + mes "npc id: " + .id; + mes "npc parent id: " + .parent; + mes "npc src id: " + .srcId; + mes "char id 3: " + getcharid(3); + if (instance_id() >= 0) + mes "instance id: " + instance_id(); + goto L_Menu; + +L_Info: + mesn; + mesq lg("Hello darling."); + next; + mesq l("I am @@, and I take care of the Arena.", .name$); + next; + mesq l("Guards use it to spar against each other on friendly matches, to see who is stronger."); + next; + mesq l("We arranged a small underground room for that, because the Colliseum is too far away."); + tutmes l("If you kill an opponent stronger than you, you will gain honor points. But if the oponent is 15 levels weaker than you, it will be NEGATIVE!"), l("About Scoreboards and Honor Points"); + tutmes l("You will also LOSE honor if the opponent is below level 30. If you are a bandit (negative honor), all fights versus you will be honorable."), l("About Scoreboards and Honor Points"); + tutmes l("If you kill the same person within 30 minutes, honor will not fluctuate. The whole honor system is very experimental."), l("About Scoreboards and Honor Points"); + next; + goto L_Menu; + +L_Scoreboards: + mesc l("All scoreboards are refreshed hourly."), 1; + HallOfHonor(); + next; + goto L_Menu; + +L_Quit: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, DarkHelm); + setunitdata(.@npcId, UDT_HEADMIDDLE, CopperArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansChaps); + setunitdata(.@npcId, UDT_WEAPON, RockKnife); + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + + .sex = G_FEMALE; + .distance = 5; + //.alwaysVisible = true; // This is dumb, why Jesusalva put it here? + .price=1000; + .curinst=1001; + npcsit; + + // create hashtable + $@ARENAS = htnew(); + // Structure: + // Password, Instance Control ID + end; + +// Every half hour, advance curinst in 1000. +// This way, we don't need to use/rely-on hasinstance2() +OnMinute30: +OnMinute60: + .curinst+=1000; + end; + +// Protect bugs by disabling NPC on new year. This NPC have two days off for vacations B-) +OnDay1231: +OnDay0101: + disablenpc(.name$); + end; + +OnDay0102: + enablenpc(.name$); + end; + +} + diff --git a/npc/003-10/guarddevoir.txt b/npc/003-10/guarddevoir.txt new file mode 100644 index 0000000..c9965d6 --- /dev/null +++ b/npc/003-10/guarddevoir.txt @@ -0,0 +1,101 @@ +// TMW2 Script +// Author: +// Jesusalva +// Saulc +// Description: +// Guard ask you to clean the cave of red scorpion. bring her some red scorpion stingers to prove you do it well. +// Variable: +// TulimsharQuest_Devoir + +003-10,38,60,0 script Guard Devoir NPC_GUARD2,{ + + mesn; + mesq l("That's terrible! Monsters are piling up near city gates! We need to stop their progression."); + if (BaseLevel >= 25) goto L_Menu; + close; + +L_Menu: + mesn; + mesq l("Would you like to help us to protect the town from a red scorpion invasion??"); + mes ""; + menu + l("Yeah, sure! I'm brave as Simon."),L_Quest, // famous player name + l("Where can I find them?"),L_Where, + l("No, thanks."),L_Close; + +L_Quest: + mes ""; + .@q=getq(TulimsharQuest_Devoir); + mesq l("Nice! I want you to kill some red scorpions, as I said!"); + next; + if (.@q == 0) goto L_Continue; + .@q2=getq2(TulimsharQuest_Devoir) + 60 * 60 * 36; + if (santime() >= .@q2) goto L_Repeat; // Repats every 36 hours + mesn; + mesq l("But, it wouldn't look nice if I let you do all the killing! The other guards are working right now!"); + next; + mesn; + mesq l("Come back in a few hours, and we can fix that!"); + close; + +L_Continue: + mesq l("Red Scorpion breed as fast as the Ratto! With our current numbers, it's nearly impossible to take over."); + next; + mesq l("I usually ask for 7, but to prove you are going to help us in this purge, I will ask you for 14 @@! I will reward you for your bravure.", getitemlink(RedScorpionStinger)); + mes ""; + menu + rif(countitem(RedScorpionStinger) >= 14, l("Here they are, miss!")), L_Finish, + l("I'll get to it."), L_Close; + close; // double sure + +L_Repeat: + mesq l("How is your purge going? I hope you had success at it!"); + next; + mesq l("Did you brought me 7 @@?", getitemlink(RedScorpionStinger)); + mes ""; + menu + rif(countitem(RedScorpionStinger) >= 7, l("Here they are miss!")), L_Finish2, + l("I'll get to it."), L_Close; + close; + +// First Time Only +L_Finish: + delitem RedScorpionStinger, 14; + getexp 2203, 11; // r7.5 1703 → 2203 + Zeny = (Zeny + 1960); // 70*14 = 980 base (100% bonus) + setq TulimsharQuest_Devoir, 1, santime(); + mes ""; + mesn; + mesq l("Many thanks! Come back later to bring me extra @@!", getitemlink(RedScorpionStinger)); + close; + +// Repeat +L_Finish2: + delitem RedScorpionStinger, 7; + getexp 1193, 0; // r7.5 993 → 1193 + Zeny = (Zeny + 980); // 70*7 = 490 base (100% bonus) + setq TulimsharQuest_Devoir, 1, santime(); + mes ""; + mesn; + mesq l("Many thanks! Come back later to bring me extra @@!", getitemlink(RedScorpionStinger)); + close; + +L_Where: + mes ""; + mesq l("Ah, there are lots on the miners cave."); + next; + mes l("Take care though, don't attack them when they are in group!"); + next; + mes l("Good luck."); + goto L_Close; + +L_Close: + closedialog; + goodbye; + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/003-10/kreist.txt b/npc/003-10/kreist.txt new file mode 100644 index 0000000..c4ca9b3 --- /dev/null +++ b/npc/003-10/kreist.txt @@ -0,0 +1,250 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Tulimshar Guardhouse Taskmaster + +003-10,41,63,0 script Kreist NPC_PLAYER,{ + .@d=gettimeparam(GETTIME_DAYOFMONTH); + mesn l("Kreist, Taskmaster"); + mesq l("Hello, and welcome to the Tulimshar guard house."); + next; + mesn l("Kreist, Taskmaster"); + mesq l("My name is @@ and I am the daily task master. Interested in keeping this world free from monsters, or are you here to report back?", .name$); + next; + tutmes l("All quests expire at 00:00 server time, be sure to finish AND report back before that!"); + // Borrowed from Arkim Code + mesc l("Time remaining to report completion: ")+FuzzyTime($@ARKIM_TIMER+86400), 1; + + select + l("I want an EASY task!"), + l("I want a MODERATE task!"), + l("I want an EXPERT task!"), + l("It was nice seeing you."); + mes ""; + switch (@menu) { + case 1: + .@q1=getq(General_MobHunting1); + .@q2=getq2(General_MobHunting1); + .@q3=getq3(General_MobHunting1); + .@q=General_MobHunting1; + .@lv=3; + goto L_QuestMaster; + case 2: + .@q1=getq(General_MobHunting2); + .@q2=getq2(General_MobHunting2); + .@q3=getq3(General_MobHunting2); + .@q=General_MobHunting2; + .@lv=5; + goto L_QuestMaster; + case 3: + .@q1=getq(General_MobHunting3); + .@q2=getq2(General_MobHunting3); + .@q3=getq3(General_MobHunting3); + .@q=General_MobHunting3; + .@lv=12; + goto L_QuestMaster; + } + closeclientdialog; + goodbye(); + close; + +L_QuestMaster: + // It's a new day, so we must generate a new quest! + if (.@q1 != .@d) { + .@q1=.@d; + if (.@lv == 3) { + .@q2=any(Maggot, Scorpion, Croc, FireGoblin, CaveMaggot, Duck); + } else if (.@lv == 5) { + .@q2=any(CaveSnake, DesertBandit, Sarracenus, DesertLogHead, RedScorpion); + } else if (.@lv == 12) { + .@q2=any(GiantMaggot, BlackScorpion, Snake, MountainSnake); + } else { + Exception("Bad setting for GMH.LV: "+.@lv, RB_DEFAULT|RB_SPEECH|RB_ISFATAL); + } + setq .@q, .@q1, .@q2, 0; + } + + // Maybe there is no monster to kill + if (!.@q2) { + mesn l("Kreist, Taskmaster"); + mesq l("You've already completed this quest today. Try again tomorrow."); + close; + } + + // Maybe you finished the quest? + if (.@q3 && get_byte(.@q3, 1) >= get_byte(.@q3, 0)) { + mesn l("Kreist, Taskmaster"); + mesq l("Good job, you've killed the %d %s and reported back in time!", get_byte(.@q3, 0), getmonsterlink(.@q2)); + next; + inventoryplace MercBoxA, 1; + if (MERCENARY_DAILYQUEST == 100) { + inventoryplace BountyHunterHelmet, 1; + getitem BountyHunterHelmet, 1; + } + mesn l("Kreist, Taskmaster"); + mesq l("It's not much of a reward, but doesn't it feel great to help others in need?! HAHAHA!"); + .@overkill=get_byte(.@q3, 1)-get_byte(.@q3, 0); + Zeny+=.@lv*(72+MERCENARY_DAILYQUEST); + // Raise LV according to monster level + .@lv+=getmonsterinfo(.@q2, MOB_LV)+MERCENARY_DAILYQUEST*11/10; + getexp BaseLevel*.@lv, .@lv+.@overkill; + setq2 .@q, 0; + setq3 .@q, 0; + MERCENARY_DAILYQUEST+=1; + if (MERCENARY_DAILYQUEST % 5 == 0) { + getitem MercBoxA, 1; + } + close; + } + + mesn l("Kreist, Taskmaster"); + // amount to kill + set_byte(.@q3, 0, 15); + // amount already killed + // Explanation why the city needs that + if (.@q2 == Maggot ) { + mesq l("%ss have infested the store houses, spilling onto the streets. Stop them from destroying even more food.", getmonsterlink(.@q2)); + set_byte(.@q3, 0, 30); + } else if (.@q2 == GiantMaggot) { + mesq l("The %ss are the cause for all the little maggots to appear, so we need you to stop the problem at the source.", getmonsterlink(.@q2)); + set_byte(.@q3, 0, 10); + } else if (.@q2 == Duck) { + mesq l("This request was made by the inn: The %ss are stealing their cherry cakes, causing disruption to their business.", getmonsterlink(.@q2)); + } else if (.@q2 == Croc) { + mesq l("The %ss have been reproducing rapidly and their natural habitat, our Croconut trees, are being damaged by the excessive number of them.", getmonsterlink(.@q2)); + } else if (.@q2 == FireGoblin) { + mesq l("Some %ss have buried themselves in the sand near the path to the mine, causing burns to people stepping on them.", getmonsterlink(.@q2)); + } else if (.@q2 == CaveMaggot) { + mesq l("The %ss have been multiplying, crawling from the cave into the magic school, causing damage.", getmonsterlink(.@q2)); + set_byte(.@q3, 0, 20); + } else if (.@q2 == CaveSnake) { + mesq l("Researchers from the magic school complained about %ss disturbing whatever they are studying in the cave.", getmonsterlink(.@q2)); + } else if (.@q2 == Scorpion) { + mesq l("All the %ss have become a real nuisance to the citizens and people are getting stung more often now.", getmonsterlink(.@q2)); + set_byte(.@q3, 0, 20); + } else if (.@q2 == DesertBandit) { + mesq l("A group of %ss are threatening the city. Go to the Desert Canyon and teach them a lesson.", getmonsterlink(.@q2)); + set_byte(.@q3, 0, 20); + } else if (.@q2 == Sarracenus) { + mesq l("%ss are lead by the %ss. Disposing of them would bring the bandits into disarray.", getmonsterlink(DesertBandit), getmonsterlink(.@q2)); + set_byte(.@q3, 0, 10); + } else if (.@q2 == BlackScorpion) { + mesq l("A large number of %ss have been sighted. Both the sewer and the mines have been infested, they have even been spotted in the city wall!", getmonsterlink(.@q2)); + } else if (.@q2 == RedScorpion) { + mesq l("Some %ss have left the mines and made themselves comfortable in the desert, coming closer than ever to us. When there are none left in the desert, go after their nest in the mines.", getmonsterlink(.@q2)); + } else if (.@q2 == DesertLogHead) { + mesq l("The %ss are growing and disturbing the already rather infertile desert soil, preventing our crops from growing.", getmonsterlink(.@q2)); + } + setq3 .@q, .@q3; + mesq l("So, please kill %d/%d %ss for us and make the city a better place!", get_byte(.@q3, 1), get_byte(.@q3, 0), getmonsterlink(.@q2)); + close; + +OnInit: + // Check items.xml for info about this. + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, WarlordHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, LieutenantArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_SHIELD, LousyMoccasins); // TODO FIXME: Display Boots + setunitdata(.@npcId, UDT_WEAPON, Backsword); + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 15); + + npcsit; + .sex = G_MALE; + .distance = 4; + end; + +} + +// What is with this stupidly long quest name? +function script Guardhouse_RandQuestCheck { + if (!playerattached()) + return; + + .@mobId=getarg(0, killedrid); + .@d=gettimeparam(GETTIME_DAYOFMONTH); + + // Easy Task + if (getq(General_MobHunting1) == .@d && + getq2(General_MobHunting1) == .@mobId) { + .@q3=getq3(General_MobHunting1); + .@killamount=get_byte(.@q3, 1); + set_byte(.@q3, 1, min(255, .@killamount+1)); + setq3 General_MobHunting1, .@q3; + if (!(.@killamount % 5)) + dispbottom l("%d/%d %s killed", .@killamount+1, get_byte(.@q3, 0), getmonsterlink(.@mobId)); + } + + // Medium Task + if (getq(General_MobHunting2) == .@d && + getq2(General_MobHunting2) == .@mobId) { + .@q3=getq3(General_MobHunting2); + .@killamount=get_byte(.@q3, 1); + set_byte(.@q3, 1, .@killamount+1); + setq3 General_MobHunting2, .@q3; + if (!(.@killamount % 5)) + dispbottom l("%d/%d %s killed", .@killamount+1, get_byte(.@q3, 0), getmonsterlink(.@mobId)); + } + + // Hard Task + if (getq(General_MobHunting3) == .@d && + getq2(General_MobHunting3) == .@mobId) { + .@q3=getq3(General_MobHunting3); + .@killamount=get_byte(.@q3, 1); + set_byte(.@q3, 1, .@killamount+1); + setq3 General_MobHunting3, .@q3; + if (!(.@killamount % 5)) + dispbottom l("%d/%d %s killed", .@killamount+1, get_byte(.@q3, 0), getmonsterlink(.@mobId)); + } + + // Frostia's Task + if (getq(General_MobHunting4) == .@d && + getq2(General_MobHunting4) == .@mobId) { + .@q3=getq3(General_MobHunting4)+1; + setq3 General_MobHunting4, .@q3; + if (!(.@q3 % 10)) + dispbottom l("@@/@@ @@ killed", .@q3, 50, getmonsterlink(.@mobId)); + } + + + // Frostia - Medium Task + if (getq(General_MobHunting5) == .@d && + getq2(General_MobHunting5) == .@mobId) { + .@q3=getq3(General_MobHunting5)+1; + setq3 General_MobHunting5, .@q3; + if (!(.@q3 % 10)) + dispbottom l("@@/@@ @@ killed", .@q3, 50, getmonsterlink(.@mobId)); + } + + // Frostia - Hard Task + if (getq(General_MobHunting6) == .@d && + getq2(General_MobHunting6) == .@mobId) { + .@q3=getq3(General_MobHunting6)+1; + setq3 General_MobHunting6, .@q3; + if (!(.@q3 % 10)) + dispbottom l("@@/@@ @@ killed", .@q3, 50, getmonsterlink(.@mobId)); + } + + // Frostia - Nightmare Task + if (getq(General_MobHunting7) == .@d && + getq2(General_MobHunting7) == .@mobId) { + .@q3=getq3(General_MobHunting7)+1; + setq3 General_MobHunting7, .@q3; + if (!(.@q3 % 10)) + dispbottom l("@@/@@ @@ killed", .@q3, 50, getmonsterlink(.@mobId)); + } + + // Frostia - Boss Task + if (getq(General_MobHunting8) == .@d && + getq2(General_MobHunting8) == .@mobId) { + if (!.@q3) + dispbottom l("%s slain!", getmonsterlink(.@mobId)); + .@q3=getq3(General_MobHunting8)+1; + setq3 General_MobHunting8, .@q3; + } + + return; +} + diff --git a/npc/003-10/officer.txt b/npc/003-10/officer.txt new file mode 100644 index 0000000..e06afe0 --- /dev/null +++ b/npc/003-10/officer.txt @@ -0,0 +1,57 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Wooden Sword quest. Pays back earlier players + +003-10,30,79,4 script Veteran Officer NPC_PLAYER_ARGAES,{ + .@q=getq(TulimsharQuest_WoodenSword); + if (.@q >= 2 || BaseLevel < 18) { + legiontalk; + close; + } + if (.@q == 0) { + mesn; + mesq l("Hey. If you want to kill monsters, you need a good sword. I think, a @@ would be the perfect fit for you.", getitemlink(WoodenSword)); + next; + mesn; + mesq l("I think, for just 25 @@, I could arrange one for you. Eh... Just don't think that you have an amazing weapon, alright?", getitemlink(RawLog)); + next; + mesn; + mesq l("You can kill @@ on the desert. It's a dangerous area though, so take care.", getmonsterlink(DesertLogHead)); + setq TulimsharQuest_WoodenSword, 1; + } else { + mesn; + mesq l("I think, for just 25 @@, I could arrange a @@ for you.", getitemlink(RawLog), getitemlink(WoodenSword)); + } + mes ""; + select + rif(countitem(RawLog) >= 25, l("Here they are!")), + l("Maybe later."); + mes ""; + if (@menu == 1) { + delitem RawLog, 25; + //getitem WoodenSword, 1; + CsysNpcCraft(WoodenSword, IOPT_EXPGAIN, 10, IOPT_RICHNESS, 10); + setq TulimsharQuest_WoodenSword, 2; + getexp 580, 20; + mesn; + mesq lg("Here's your sword, novice. Now go kill monsters."); + } + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, DesertHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, Chainmail); + setunitdata(.@npcId, UDT_HEADBOTTOM, WoodenSword); + setunitdata(.@npcId, UDT_WEAPON, JeansShorts); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + npcsit; + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/003-10/slots.txt b/npc/003-10/slots.txt new file mode 100644 index 0000000..add4b10 --- /dev/null +++ b/npc/003-10/slots.txt @@ -0,0 +1,90 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Slot Machine for bets + +003-10,22,37,0 script Slot Machine#003-10a NPC_SLOTMACHINE,{ + function symbol{ + switch (getarg(0)) { + case 1: + mesn "%%A"; + break; + case 2: + mesn "%%B"; + break; + case 3: + mesn "%%C"; + break; + case 4: + mesn "%%D"; + break; + case 5: + mesn "%%E"; + break; + case 6: + mesn "%%F"; + break; + case 7: + mesn "7"; + break; + default: + mesn "%%@"; + break; + } + } + +L_Menu: + mesn; + mesc l("Spin three symbols, and jackpot great rewards!"); + mesc l("Just one coin for spin."); + next; + menu + rif(countitem(CasinoCoins) >= 1, l("Spin!")), L_Spin, + l("Prizes"), L_Info, + l("Leave"), -; + close; + +L_Info: + mes ""; + mesc l("Prizes:"); + mes l("##9 777: @@.", getitemlink(Monocle)); + mesc l("Three equal: @@.", "18 casino coins"); + mesc l("Two equal: 1 casino coin."); + next; + goto L_Menu; + + +L_Spin: + mesc l("Spinning..."); + next; + delitem CasinoCoins, 1; + .@a=rand2(1,7); + .@b=rand2(1,7); + .@c=rand2(1,7); + symbol(.@a); + symbol(.@b); + symbol(.@c); + next; + mesn; + if (.@a == .@b && .@a == .@c && .@a == 7) { + getitem Monocle, 1; + mesc l("Jackpot! You got the Monocle!"), 3; + } else if (.@a == .@b && .@a == .@c) { + getitem CasinoCoins, 18; + mesc l("Congrats! A pity it was not 777..."), 3; + } else if (.@a == .@b || .@a == .@c || .@b == .@c) { + getitem CasinoCoins, 1; + mesc l("Lucky! You got the coin back!"), 3; + } else { + mesc l("It wasn't this time..."), 3; + } + next; + goto L_Menu; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + diff --git a/npc/003-13/_import.txt b/npc/003-13/_import.txt new file mode 100644 index 0000000..97f13c4 --- /dev/null +++ b/npc/003-13/_import.txt @@ -0,0 +1,5 @@ +// Map 003-13: duel map +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-13/_warps.txt", +"npc/003-13/arnea.txt", +"npc/003-13/mapflags.txt", diff --git a/npc/003-13/_warps.txt b/npc/003-13/_warps.txt new file mode 100644 index 0000000..76857da --- /dev/null +++ b/npc/003-13/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-13: duel map warps +003-13,31,44,0 warp #003-13_31_44 0,0,003-1,100,100 diff --git a/npc/003-13/arnea.txt b/npc/003-13/arnea.txt new file mode 100644 index 0000000..4dfc591 --- /dev/null +++ b/npc/003-13/arnea.txt @@ -0,0 +1,235 @@ +// TMW2 Scripts +// Author: +// 4144 +// Jesusalva +// Description: +// Arena for Duels and PVP (temporary map) + +003-13,29,29,0 script Arnea#003-13 NPC_ELF_F,{ + /* + // FIXME + warp "003-10", 22, 62; + close; + */ + if (is_staff()) { + mes "npc name: " + .name$; + mes "npc ext name: " + .extname$; + mes "npc id: " + .id; + mes "npc parent id: " + .parent; + mes "npc src id: " + .srcId; + mes "char id 3: " + getcharid(3); + mes "instance id: " + instance_id(); + mes "Map ID: " + instance_mapname("003-13"); + } + if (instance_id() >= 0) { + goto L_Manage; + } else { + // Non staff and on 003-13? That's a bug! + if (!is_staff()) { + atcommand "@request Hey hey hey, player found in 003-13 - regular map! Report this to Jesusalva at once!"; + warp "Save", 0, 0; + close; + } + mes "npc not in instance"; + } + + close; + +L_Manage: + mesn; + select + l("warp back"), + rif(!'UDTf, l("begin Doppelganger Challenge")), + l("Doppelganger Challenge Ranking"), + l("cancel"); + + switch (@menu) + { + case 1: + warp "003-10", 22, 62; + break; + case 2: + mesc l("spawn challenge monster! How far can you go?"), 2; + mesc l("The Doppelganger Challenge will create a Gladiator Monster which should somehow emulate a PvP experience."); + mesc l("In other words, it'll make a copy of youself."); + mesc l("Defeating it will advance the round. How far can you survive?"); + next; + mesc l("Really begin the Doppelganger Challenge?"); + mesc l("Switching from strong to weak equipments WON'T make it go any easier on you!"), 1; + if (askyesno() == ASK_NO) + break; + + npctalk l("Doppelganger Challenge, @@ began the fight!", strcharinfo(0)); + // Save permanent data + 'udt_blv=BaseLevel; + 'udt_Str=readparam2(bStr); + 'udt_Agi=readparam2(bAgi); + 'udt_Vit=readparam2(bVit); + 'udt_Int=readparam2(bInt); + 'udt_Dex=readparam2(bDex); + 'udt_Luk=readparam2(bLuk); + 'udt_Dly=battleparam(UDT_ADELAY); + 'udt_Rng=battleparam(UDT_ATKRANGE); + + // Save (b)ase data + 'udt_bhp=MaxHp; + 'udt_bAtk1=battleparam(UDT_ATKMIN); + 'udt_bAtk2=battleparam(UDT_ATKMAX); + 'udt_bMatk=battleparam(UDT_MATKMAX); + 'udt_bDef=battleparam(UDT_DEF); + 'udt_bMdef=battleparam(UDT_MDEF); + 'udt_bHit=battleparam(UDT_HIT); + 'udt_bFlee=battleparam(UDT_FLEE); + 'udt_bCrit=battleparam(UDT_CRIT); + + // Save (p)rogression data + 'udt_php='udt_bhp/5; + 'udt_pAtk1='udt_bAtk1/10; + 'udt_pAtk2='udt_bAtk2/10; + 'udt_pMatk='udt_bMatk/5; + 'udt_pDef='udt_bDef/5; + 'udt_pMdef='udt_bMdef/5; + 'udt_pHit='udt_bHit/10; + 'udt_pFlee='udt_bFlee/15; + 'udt_pCrit='udt_bCrit/10; + + /* + .@a1=battleparam(UDT_ATKRANGE); + .@a2=battleparam(UDT_ATKMIN); + .@a3=battleparam(UDT_ATKMAX); + .@d1=battleparam(UDT_DEF); + .@d2=battleparam(UDT_MDEF); + .@c1=battleparam(UDT_HIT); + .@c2=battleparam(UDT_FLEE); + .@m1=battleparam(UDT_MATKMIN); + .@m2=battleparam(UDT_MATKMAX); + .@m3=battleparam(UDT_ELETYPE); + .@s1=battleparam(UDT_STR); + .@s2=battleparam(UDT_ADELAY); + .@s3=battleparam(UDT_DMOTION); + + debugmes "Unit Data %d (Str %d)", getcharid(3), .@s1; + debugmes "Atk (%d~%d) Range %d", .@a2, .@a3, .@a1; + debugmes "Def %d MDef %d", .@d1, .@d2; + debugmes "Hit %d Flee %d", .@c1, .@c2; + debugmes "MAtk (%d~%d) Element %d", .@m1, .@m2, .@m3; + debugmes "Attack delay %d Stun %d", .@s2, .@s3; + */ + + // Begin the fight + doevent instance_npcname(.name$)+"::OnGladius"; + addtimer 5000, instance_npcname(.name$)+"::OnVerify"; + closeclientdialog; + break; + case 3: + mesc l("All leaderboards are refreshed hourly."), 1; + mesc l("Your current score: @@", UDTRANK), 3; + HallOfUDT(); + break; + case 4: + break; + } + close; + +OnGladius: + sleep(800); + .@mg=monster(instance_mapname("003-13"), 38, 32, "Gladiator", Gladiator, 1, instance_npcname(.name$)+"::OnGladius"); + // Set "permanent" data + setunitdata(.@mg, UDT_ADELAY, 'udt_Dly-'UDTf); + setunitdata(.@mg, UDT_ATKRANGE, 'udt_Rng+limit(0, 'UDTf/10, 3)); + + // Set base data + setunitdata(.@mg, UDT_LEVEL, 'udt_blv+'UDTf); + setunitdata(.@mg, UDT_STR, 'udt_Str+'UDTf); + setunitdata(.@mg, UDT_AGI, 'udt_Agi+'UDTf); + setunitdata(.@mg, UDT_VIT, 'udt_Vit+'UDTf); + setunitdata(.@mg, UDT_INT, 'udt_Int+'UDTf); + setunitdata(.@mg, UDT_DEX, 'udt_Dex+'UDTf); + setunitdata(.@mg, UDT_LUK, 'udt_Luk+'UDTf); + + // Set variable data + setunitdata(.@mg, UDT_MAXHP, 'udt_bhp+'udt_php*('UDTf-1)); + setunitdata(.@mg, UDT_HP, 'udt_bhp+'udt_php*('UDTf-1)); + + setunitdata(.@mg, UDT_ATKMIN, 'udt_bAtk1+'udt_pAtk1*('UDTf-1)); + setunitdata(.@mg, UDT_ATKMAX, 'udt_bAtk2+'udt_pAtk2*('UDTf-1)); + setunitdata(.@mg, UDT_MATKMIN, 'udt_bMatk+'udt_pMatk*('UDTf-1)); + setunitdata(.@mg, UDT_MATKMAX, 'udt_bMatk+'udt_pMatk*('UDTf-1)); + setunitdata(.@mg, UDT_DEF, 'udt_bDef+'udt_pDef*('UDTf-1)); + setunitdata(.@mg, UDT_MDEF, 'udt_Mdef+'udt_pMdef*('UDTf-1)); + setunitdata(.@mg, UDT_HIT, 'udt_bHit+'udt_pHit*('UDTf-1)); + setunitdata(.@mg, UDT_FLEE, 'udt_bFlee+'udt_pFlee*('UDTf-1)); + setunitdata(.@mg, UDT_CRIT, 'udt_bCrit+'udt_pCrit*('UDTf-1)); + + setunitdata(.@mg, UDT_PDODGE, min(30, 'udt_Luk/10+('UDTf/3))); + + 'UDTf+=1; + npctalk ("Doppelganger Challenge, wave " + 'UDTf + "!"); + maptimer(instance_mapname("003-13"), 10, instance_npcname(.name$)+"::OnUDTUpdate"); + end; + +OnUDTUpdate: + if ('UDTf > UDTRANK) + UDTRANK='UDTf; + getexp 'UDTf*7, 'UDTf*7; + Mobpt+='UDTf*7; + end; + +// Check for possible cheats, and update default values +OnVerify: + if (battleparam(UDT_ATKRANGE) > 'udt_Rng) + 'udt_Rng=battleparam(UDT_ATKRANGE); + + if (battleparam(UDT_ATKMAX) > 'udt_bAtk1) { + 'udt_bAtk1=battleparam(UDT_ATKMIN); + 'udt_bAtk2=battleparam(UDT_ATKMAX); + 'udt_pAtk1='udt_bAtk1/10; + 'udt_pAtk2='udt_bAtk2/10; + } + + if (battleparam(UDT_DEF) > 'udt_bDef) { + 'udt_bDef=battleparam(UDT_DEF); + 'udt_pDef='udt_bDef/5; + } + + if (battleparam(UDT_MDEF) > 'udt_bMdef) { + 'udt_bMdef=battleparam(UDT_MDEF); + 'udt_pMdef='udt_bMdef/5; + } + + if (battleparam(UDT_MATKMAX) > 'udt_bMatk) { + 'udt_bMatk=battleparam(UDT_MATKMAX); + 'udt_pMatk='udt_bMatk/5; + } + + if (battleparam(UDT_ADELAY) < 'udt_bDly) + 'udt_bDly=battleparam(UDT_DELAY); + + addtimer 5000, instance_npcname(.name$)+"::OnVerify"; + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, DarkHelm); + setunitdata(.@npcId, UDT_HEADMIDDLE, CopperArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansChaps); + setunitdata(.@npcId, UDT_WEAPON, RockKnife); + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + + .sex = G_FEMALE; + .distance = 9; + end; + +OnInstanceInit: + .@npcId = getnpcid(instance_npcname(.name$)); + setunitdata(.@npcId, UDT_HEADTOP, DarkHelm); + setunitdata(.@npcId, UDT_HEADMIDDLE, CopperArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansChaps); + setunitdata(.@npcId, UDT_WEAPON, RockKnife); + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + end; + +} + diff --git a/npc/003-13/mapflags.txt b/npc/003-13/mapflags.txt new file mode 100644 index 0000000..481fda8 --- /dev/null +++ b/npc/003-13/mapflags.txt @@ -0,0 +1,2 @@ +003-13 mapflag pvp +003-13 mapflag nopenalty diff --git a/npc/003-2-0/_import.txt b/npc/003-2-0/_import.txt new file mode 100644 index 0000000..1ad459d --- /dev/null +++ b/npc/003-2-0/_import.txt @@ -0,0 +1,3 @@ +// Map 003-2-0: Nard's Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-2-0/_warps.txt", diff --git a/npc/003-2-0/_warps.txt b/npc/003-2-0/_warps.txt new file mode 100644 index 0000000..c88e5c3 --- /dev/null +++ b/npc/003-2-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-2-0: Nard's Room warps +003-2-0,19,27,0 warp #003-2-0_19_27 0,0,003-2-1,32,28 diff --git a/npc/003-2-1/_import.txt b/npc/003-2-1/_import.txt new file mode 100644 index 0000000..a23c26c --- /dev/null +++ b/npc/003-2-1/_import.txt @@ -0,0 +1,6 @@ +// Map 003-2-1: #Leave-Complaints +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-2-1/_mobs.txt", +"npc/003-2-1/_warps.txt", +"npc/003-2-1/demure.txt", +"npc/003-2-1/mapflags.txt", diff --git a/npc/003-2-1/_mobs.txt b/npc/003-2-1/_mobs.txt new file mode 100644 index 0000000..e4ea840 --- /dev/null +++ b/npc/003-2-1/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-2-1: #Leave-Complaints mobs +003-2-1,46,45,16,11 monster Green Dragon 1195,5,3600,36000,NotSoBot::OnHydra diff --git a/npc/003-2-1/_warps.txt b/npc/003-2-1/_warps.txt new file mode 100644 index 0000000..c66a782 --- /dev/null +++ b/npc/003-2-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-2-1: #Leave-Complaints warps +003-2-1,47,33,0 warp #003-2-1_47_33 1,0,003-2,28,40 diff --git a/npc/003-2-1/demure.txt b/npc/003-2-1/demure.txt new file mode 100644 index 0000000..357577d --- /dev/null +++ b/npc/003-2-1/demure.txt @@ -0,0 +1,59 @@ +// TMW2 Script +// Author: +// Crazyfefe +// Jesusalva + +003-2-1,51,41,0 script NotSoBot NPC_FEMALE,{ + + RegEasterEgg(EE_DEMURE, 5); + + // Let's try without freeloop + mesn any("NotSoBot", "Demure"); + if (rand(1,5) <= 2) + mes "Blame Saulc"; + + .@mx=rand(6,12); + for (.@i = 0; .@i < .@mx; ++.@i) { + mes "leave Complaints"; + if (rand(1,5) == 3) + mes ""; + if (.@i == 10) { + next; + mesn any("NotSoBot", "Demure"); + mes any("leave Complaints", "Blame Saulc"); + } + } + if (rand(1,5) >= 4) + mes "Blame Saulc"; + close; + +OnHydra: + areamonster "003-2-1", 20, 20, 65, 60, strmobinfo(1, GreenDragon), GreenDragon, any(1,1,2), "NotSoBot::OnHydra2"; + setq Q_DragonFarm, getq(Q_DragonFarm)+1; + if (getq(Q_DragonFarm) > 100) goto L_Kick; + end; + +OnHydra2: + setq Q_DragonFarm, getq(Q_DragonFarm)+1; + if (getq(Q_DragonFarm) > 100) goto L_Kick; + end; + +L_Kick: + .@t=900+min(2700, getq3(Q_DragonFarm)*60); + setq Q_DragonFarm, 1, gettimetick(2)+.@t, getq3(Q_DragonFarm)+1; + warp "003-2", 28, 41; + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, Cap); + setunitdata(.@npcId, UDT_HEADMIDDLE, RedStockings); + setunitdata(.@npcId, UDT_HEADBOTTOM, BunnyEars); + setunitdata(.@npcId, UDT_WEAPON, GMRobe); + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/003-2-1/mapflags.txt b/npc/003-2-1/mapflags.txt new file mode 100644 index 0000000..f66ee66 --- /dev/null +++ b/npc/003-2-1/mapflags.txt @@ -0,0 +1 @@ +003-2-1 mapflag nopenalty diff --git a/npc/003-2/_import.txt b/npc/003-2/_import.txt new file mode 100644 index 0000000..41a622e --- /dev/null +++ b/npc/003-2/_import.txt @@ -0,0 +1,9 @@ +// Map 003-2: Tulimshar Guild +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-2/_warps.txt", +"npc/003-2/eistein.txt", +"npc/003-2/estard.txt", +"npc/003-2/hiddenwarp.txt", +"npc/003-2/lua.txt", +"npc/003-2/mapflags.txt", +"npc/003-2/politics.txt", diff --git a/npc/003-2/_warps.txt b/npc/003-2/_warps.txt new file mode 100644 index 0000000..822f56e --- /dev/null +++ b/npc/003-2/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-2: Tulimshar Guild warps +003-2,32,41,0 warp #003-2_32_41 0,0,003-1,46,73 +003-2,35,41,0 warp #003-2_35_41 0,0,003-1,49,73 +003-2,38,41,0 warp #003-2_38_41 0,0,003-1,52,73 +003-2,42,33,0 warp #003-2_42_33 0,0,003-1,51,66 diff --git a/npc/003-2/eistein.txt b/npc/003-2/eistein.txt new file mode 100644 index 0000000..d8af9e3 --- /dev/null +++ b/npc/003-2/eistein.txt @@ -0,0 +1,88 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Eistein rewards players for getting level landmarks. + +003-2,35,34,0 script Eistein NPC_UKAR,{ + + // Level, Reward{, Reward2} + function is_level { + if (BaseLevel >= getarg(0)) { + getitem getarg(1),1; + if (getarg(2, 0)) + getitem getarg(2),1; + setq TulimsharQuest_Eistein, getq(TulimsharQuest_Eistein)+1; + mes ""; + mesn; + mesq l("Congrats you passed the level cap of @@! Here is a(n) @@, you deserve it.",getarg(0), getitemlink(getarg(1))); + if (getarg(2,0)) + mesc l("Item obtained: @@", getitemlink(getarg(2))); + } else { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("You are level @@/@@. Keep building levels, you need them!", BaseLevel, getarg(0)); + } + } + + + function quest_open { + .@q = getq(TulimsharQuest_Eistein); + switch (.@q) { + case 0: + is_level(25, BronzeGift); break; + case 1: + is_level(50, SilverGift, GraduationCap); break; + case 2: + is_level(75, GoldenGift, GraduationRobe); break; + case 3: + is_level(100, PrismGift, GraduationAlbum); break; + case 4: + is_level(125, SupremeGift); break; + case 5: + is_level(150, MysteriousFruit); break; + default: + mesn; + mesq l("Waw, you are level @@! Many congratulations. If there were people like you, ukarania wouldn't have been destroyed...", BaseLevel); + break; + } + } + + speech S_LAST_NEXT, + l("Ah, welcome. Please, don't be afraid of my look, Saulc GM assigned me to here."), + l("I'm Eistein, survivor from Ukarania. I reward brave adventurers who kill monsters, which plague our lands."), + l("I'll give you a reward, in the name of Saulc, once you reach the following levels: 25, 50, 75, 100, 125 and 150."); + do + { + select + l("Interesting! can I be rewarded for my help?"), + l("What about job levels and job experience?"), + l("Quit"); + + switch (@menu) { + case 1: + quest_open; + break; + case 2: + mesn; mesq l("Job levels already boosts all your status. It gives +1 on each status every 10 job levels."); next; + break; + } + } while (@menu != 3); + + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, GraduationCap); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 0); + npcsit; + + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/003-2/estard.txt b/npc/003-2/estard.txt new file mode 100644 index 0000000..4b4cfda --- /dev/null +++ b/npc/003-2/estard.txt @@ -0,0 +1,127 @@ +// TMW-2 Scripts. +// Author: +// Jesusalva +// Description: +// Manages party and guild. +// General_Guild +// 0 - Allows Party Creation +// 1 - Allows Guild Creation, Party is already allowed + +// Evol scripts. +// Author: +// Reid + +003-2,32,34,0 script Estard NPC_PLAYER_ARGAES,{ + + function create_party { + .@party_price = 6500; + .@party_price-=reputation("Candor")*10; + .@party_price-=reputation("Tulim")*10; + .@party_price-=reputation("Hurns")*10; + .@party_price-=reputation("Nival")*10; + .@party_price-=reputation("Halin")*10; + .@party_price-=reputation("LoF")*10; + + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Creating a party has some advantages, it's a pretty good choice!"), + l("If the members level difference is less than 30, and you enable exp sharing, you'll all get a bonus experience!"), + l("You also get access to a party chat, and there are no compromises, so it is a perfect choice for a raid."), + lg("The cost to create a party is @@ GP, are you interested?", + "The cost to create a party is @@ GP, are you interested?", .@party_price); + + if (askyesno() == ASK_YES) { + if (Zeny < .@party_price) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, l("You don't have enough money, bring me @@ GP.", .@party_price); + } else { + Zeny = Zeny - .@party_price; + setq General_Guild, 1; + skill NV_BASIC, 7, 0; + getexp 0, 100; + + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, l("Awesome, come back if you ever want to create a larger group!"); + } + } + } + + + function create_guild { + .@guild_price = 50000; + + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Creating a guild is not for the faint of heart. You cannot share experience or drops."), + l("But it is a massive organization, recognized by everyone. Guilds train with other guilds, for huge profits."), + l("It is often easier to get invited by someone, but if you want to create it, you're free to. Remember guilds also have a player limit."), + l("Just like parties, you can create from Social menu, but there is a catch: I will give you a document, which allows you to create ONE GUILD."), + l("Shall you disband your guild for whatever reason, you'll need to pay again. No refunds. No complaining."), + l("The cost to create a guild is @@ GP.", .@guild_price); + + if (askyesno() == ASK_YES) { + if (Zeny < .@guild_price) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, l("You don't have enough money, bring me @@ GP or join an already existing one.", .@guild_price); + } else { + inventoryplace Emperium, 1; + Zeny = Zeny - .@guild_price; + getitem Emperium, 1; + getexp 0, 1000; + + speech(S_FIRST_BLANK_LINE | S_LAST_NEXT, l("Awesome, here is the certificate! Choose guild name wisely, because there are no refunds, even if you lose it!")); + } + } + } + + function explain_service { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Welcome to the Town Hall. I am @@, officer in charge of public associations.", .name$), + l("We offer party and guild certifications."); + return; + } + + mesn; + mesq lg("Welcome miss.", "Good day sir."); + + //if (getcharid(1) > 0) + // mes l("You're in the \"@@\" party, I know!", getpartyname(getcharid(1))); + if (getcharid(2) > 0) + mesq l("You are part of the \"@@\" guild.", getguildname(getcharid(2))); + next; + + do + { + select + l("What service do you offer?"), + rif(!getq(General_Guild), l("I would like to create a party.")), + rif(getq(General_Guild) && getcharid(2) <= 0 && countitem(Emperium) == 0, l("I would like to create a guild.")), + menuaction(l("Quit")); + + switch (@menu) + { + case 1: + explain_service; + break; + case 2: + create_party; + break; + case 3: + create_guild; + break; + } + } while (@menu < 3); + + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, BowlerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansChaps); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 0); + npcsit; + + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/003-2/hiddenwarp.txt b/npc/003-2/hiddenwarp.txt new file mode 100644 index 0000000..6868269 --- /dev/null +++ b/npc/003-2/hiddenwarp.txt @@ -0,0 +1,18 @@ +// TMW2 Script +// Author: +// Crazyfefe +// Jesusalva +// This warp is enabled after waiting for lua + +003-2,28,41,0 script #LeaveComplaints NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if ((BaseLevel >= 40 && getq(General_Narrator) >= 2 && getq2(Q_DragonFarm) < gettimetick(2)) || is_gm()) + warp "003-2-1", 47, 34; + else + npctalk3 l("Complaints Depto. temporaly closed, come back later"); + if (getq2(Q_DragonFarm) > gettimetick(2)) + npctalk3 l("Time remaining: %s", FuzzyTime(getq2(Q_DragonFarm))); + end; +} diff --git a/npc/003-2/lua.txt b/npc/003-2/lua.txt new file mode 100644 index 0000000..e975ff9 --- /dev/null +++ b/npc/003-2/lua.txt @@ -0,0 +1,877 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Lua is an Alliance Officer. She takes care of miscelanneous administrative tasks, including Strange Coin generation. +// She can also change the world's hero, give Sponsor their necklace, and is part of main storyline. + +003-2,38,34,0 script Lua#003-2 NPC_FEMALE,{ + // Main Quest 2-1 until Main Quest 2-5 (?) + function luaAssignTask1; // Dausen & Aidan + function luaTask1; + function luaCheckTask1; + function luaAssignTask2; // Fishing & Crafting + function luaTask2; + function luaCheckTask2; + function luaAssignTask3; // Minimum level/job to begin + function luaTask3; + function luaCheckTask3; + function luaAssignTask4; // Inn Arc + function luaTask4; + function luaCheckTask4; + function luaAssignTask5; // Your Report, the plan + function luaTask5; + function luaCheckTask5; + function luaCReward; // Contributor Reward + // Then we jump to L_Finish/L_Complete + .@n = getq(General_Narrator); + + mesn; + mesq l("Hello, I act on the Alliance's behalf."); + mesc l("If you want to know what the Alliance is up to, you might have better luck talking to Jerican, from the Press."); + +L_Menu: + mes ""; + menu + l("Ok, see you later."),L_Close, + rif(.@n == 1, l("I was found near Candor Island, on a raft, and can't remember anything. Do you know who I am?")), L_Info, + rif(.@n == 2, l("What you asked me for, again?")), L_Task, + rif(.@n == 3, l("Where I needed to go, again?")), L_Complete, + rif(.@n >= 4, l("Do you think I'm ready to go to Halinarzo?")), L_Check, + rif(is_gm(), l("I need a GM set, please!")), L_GMItems, + rif(is_gm() && #GMEVENT_T <= gettimetick(2), l("I plan in doing an event! Give me the coins!")), L_GMEvent, + rif(is_gm() && #GMEVENT_T > gettimetick(2), l("Trade my coins in Gift Boxes, pretty please!")), L_GMEventShop, + rif(is_staff(), l("Tell people the name of a new hero.")), L_NewHero, + rif((getgmlevel() == 1 || is_admin()) && #T_SPONSOR <= gettimetick(2), l("I want a Sponsor Necklace.")), L_Sponsor, + l("Do you have any tips for beginners?"), L_Tips, + l("I am a contributor, and I want contributor stuff."), L_Contributor, + l("What alliance?"),-; + mes ""; + mesn; + mesq l("The Alliance which was formed after the war!"); + close; + + + + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +// GM Functions +L_GMItems: + getitembound GMRobe, 1, 1; + getitembound GMCap, 1, 1; + logmes(strcharinfo(0)+" just took a GM set."); + logmes(strcharinfo(0)+" just took a GM set.", LOGMES_ATCOMMAND); + consoleinfo("%s retrieved a GM set.", strcharinfo(0)); + mes ""; + mesn; + mesq lg("Here they are, miss.", "Here they are, mister."); + mes ""; + goto L_Menu; + +L_GMEvent: +OnGMCoinClaim: + if (!is_gm() || #GMEVENT_T > gettimetick(2)) { + dispbottom l("ERROR: You already took the coins today. Please wait @@ more.", FuzzyTime(#GMEVENT_T)), 1; + end; + } + #GMEVENT_T=gettimetick(2)+(60*60*24); + getitem StrangeCoin, 30; + //logmes(strcharinfo(0)+" just took thirty event coins."); + //logmes(strcharinfo(0)+" just took thirty event coins.", LOGMES_ATCOMMAND); + mes ""; + mesn; + mesq lg("Here they are, miss.", "Here they are, mister."); + mes ""; + goto L_Menu; + +OnGMEventShop: +L_GMEventShop: + // Inflaction: Non-admins pay more out of main events (for whatever reasons) + .@inf=1; + if (!is_master() && $EVENT$ == "") + .@inf+=1; + + // Main Code + .@s=countitem(StrangeCoin); + mes ""; + mesn; + mesq l("You currently have @@ @@.", countitem(StrangeCoin), getitemlink(StrangeCoin)); + mes ""; + mesc l("@@ - @@ coins", getitemlink(BronzeGift), 10*.@inf); + mesc l("@@ - @@ coins", getitemlink(SilverGift), 20*.@inf); + mesc l("@@ - @@ coins", getitemlink(GoldenGift), 40*.@inf); + mesc l("@@ - @@ coins", getitemlink(PrismGift), 80*.@inf); + mesc l("@@ - @@ coins", getitemlink(SupremeGift), 160*.@inf); + mes ""; + mesc l("Fruits - @@ coins", 100*.@inf); + mes ""; + menuint + "Return to main menu", -1, + rif(.@s > 10*.@inf, "Bronze Gift"), 10, + rif(.@s > 20*.@inf, "Silver Gift"), 20, + rif(.@s > 40*.@inf, "Golden Gift"), 40, + rif(.@s > 80*.@inf, "Prism Gift"), 80, + rif(.@s > 160*.@inf, "Supreme Gift"), 160, + rif(.@s > 100*.@inf, "Statusup Fruits"), 100; + + if (@menuret == -1) + goto L_Menu; + + delitem StrangeCoin, @menuret*.@inf; + switch (@menuret) { + case 10: + getitem BronzeGift, 1; break; + case 20: + getitem SilverGift, 1; break; + case 40: + getitem GoldenGift, 1; break; + case 80: + getitem PrismGift, 1; break; + case 160: + getitem SupremeGift, 1; break; + case 100: + menuint + "Strength Fruit",StrengthFruit, + "Agility Fruit",AgilityFruit, + "Vitality Fruit",VitalityFruit, + "Intelligence Fruit",IntelligenceFruit, + "Dexterity Fruit",DexterityFruit, + "Luck Fruit",LuckFruit, + "Mysterious Fruit",MysteriousFruit; + mes ""; + getitem @menuret, 1; break; + default: + mesc(l("A script error happened, please report: @@", @menuret), 1); break; + } + goto L_GMEventShop; + +L_NewHero: + mes ""; + mes l("Current hero: @@", $MOST_HEROIC$); + input .@MH$; + logmes "Changing hero from "+$MOST_HEROIC$+" to "+.@MH$; + logmes(strcharinfo(0)+" changed the world's hero ("+$MOST_HEROIC$+"->"+.@MH$+")", LOGMES_ATCOMMAND); + consoleinfo("%s changed the world hero.", strcharinfo(0)); + $MOST_HEROIC$ = .@MH$; + goto L_Menu; + +L_Sponsor: + #T_SPONSOR=gettimetick(2)+(60*60*24*30); + rentitem "SponsorNecklace", (60*60*24*30); + logmes(strcharinfo(0)+" just took a Sponsor Necklace."); + logmes(strcharinfo(0)+" just took a Sponsor Necklace.", LOGMES_ATCOMMAND); + mes ""; + mesn; + mesq lg("Here it is, miss.", "Here it is, mister."); + mes col("This item will be returned in 30 days counting from now, and you will need to take another.", 9); + mes col("It cannot be dropped, traded, sold, but you can store it and use with same account.", 9); + mes ""; + goto L_Menu; + +OnLuaInfo: + @lua_isok=1; + end; + + + + + + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +// Main Story Functions +L_Info: + if (!@lua_isok) { + mes ""; + mesn; + mesq l("Of course, give me just a minute to look up for your data, @@.", strcharinfo(0)); + next; + mesn; + mesq l("Please wait here on the meanwhile. Try asking Estard about parties, or talk to Eistein."); + //mesq l("You can explore the city on the meanwhile, I need to inspect the data."); + if (!@lua_init) { + @lua_init=1; + addtimer(rand(55000,65000),"Lua#003-2::OnLuaInfo"); + } + close; + } + mes "..."; + next; + mes "... ..."; + next; + mes "... ... ..."; + next; + mesn; + mesq l("No criminal record, no bank account, not even a tax payment declaration."); + next; + mesq l("I found your birth record, but it is not complete. Here says you were born on @@.", b("Halinarzo")); + next; + mesq l("The problem is that Halinarzo was destroyed in the never-ending war and was just recently rebuilt."); + next; + mesq l("Almost all documents were lost, destroyed or damaged. I cannot even find your parents name."); + next; + mesq l("All I can say is that you were born there, and moved by the age of 4, but to where? I don't know."); + next; + mesn; + mesq l("Sorry. At best, you can try your luck in Halinarzo, but the odds of someone recognizing you are pretty slim."); + next; + mesn; + mesq l("However..."); + mesq l("You will be killed on the road if you try to go like this. The Canyon route is dangerous, and the Swamps route claimed many souls."); + mesq l("Not to say about bandits and robbers you'll find."); + next; + getexp BaseLevel*30, JobLevel*5; // Reference Levels: (10, 3) + setq General_Narrator, 2, 0; + mesn; + mesq l("Lucky for you, the Alliance have means to assist you. You'll be a grown up, full fledged adventurer in no time at all, and will be able to visit a friend of mine, who will then send you there."); + next; + goto L_Task; + +L_Task: + .@q2=getq2(General_Narrator); + if (.@q2 == 0) { + luaAssignTask1(); + setq2 General_Narrator, 1; + .@q2=getq2(General_Narrator); + } else if (.@q2 == 2) { + luaAssignTask2(); + setq2 General_Narrator, 3; + .@q2=getq2(General_Narrator); + } else if (.@q2 == 4) { + luaAssignTask3(); + setq2 General_Narrator, 5; + .@q2=getq2(General_Narrator); + } else if (.@q2 == 6) { + luaAssignTask4(); + setq2 General_Narrator, 7; + .@q2=getq2(General_Narrator); + } else if (.@q2 == 8) { + luaAssignTask5(); + setq2 General_Narrator, 9; + .@q2=getq2(General_Narrator); + } + + mesc b(l(".:: Main Quest 2-%d ::.", (.@q2+1)/2)), 3; + if (.@q2 == 1) { + luaTask1(); + next; + if (luaCheckTask1()) { + setq2 General_Narrator, 2; + goto L_Task; + } + } else if (.@q2 == 3) { + luaTask2(); + next; + if (luaCheckTask2()) { + setq2 General_Narrator, 4; + goto L_Task; + } + } else if (.@q2 == 5) { + luaTask3(); + next; + if (luaCheckTask3()) { + setq2 General_Narrator, 6; + goto L_Task; + } + } else if (.@q2 == 7) { + luaTask4(); + next; + if (luaCheckTask4()) { + setq2 General_Narrator, 8; + goto L_Task; + } + } else if (.@q2 == 9) { + luaTask5(); + next; + if (luaCheckTask5()) { + setq2 General_Narrator, 10; + goto L_Finish; + } + } + //Exception("Something went wrong, this is not a valid MQ2 state: "+.@q2); + mesn; + mesq l("Safe travels!"); + close; + + + + + +// Declare functions +function luaAssignTask1 { + mesn; + mesq l("First and foremost, you should get yourself some reputation. I mean, right now, you are a complete nobody who was found on the sea."); + next; + mesn; + mesq l("As far as we are concerned, you could have been an exiled prisoner who managed to escape! But there are ways to prove to the Alliance that your intentions are good."); + next; + mesn; + mes l("%s is by becoming a %s.", b(l("The first way,")), b(l("Monster Hunter"))); + // If you skipped Tutorial: You already know all this + tutmes l("Monster Hunters receive small bounties on a special currency, of Monster Points, for each monster they kill. The amount gained is based on the monster level."), l("Monster Hunter"); + tutmes l("You can become a Monster Hunter by signing up with %s, near the Market.", b("Aidan")), l("Monster Hunter"); + next; + mesn; + mes l("%s is by impressing the town guard.", b(l("The second way,"))); + // If you skipped Tutorial: You already know all this + tutmes l("Also near the market, look for %s. He is the chief of the City Guard. Ask if he need help, and help him!", b(l("Lieutenant Dausen"))), l("Monster Hunter"); + tutmes l("If he gives you a guard card, that'll allow access to the Guard House, where you can pick Daily Bounties for monsters. These will pay you in GP."), l("The Guard House"); + tutmes l("Tulimshar is the strongest city because Dausen teaches every newcomer about the monsters around and how to defeat them. Take his special training if you get the chance!"), l("The Guard House"); + next; + mesn; + mesq l("You don't need to do both right now, even if you probably will want to. Once any (or both) of them tell me you're an OK person, I'll start helping you."); + next; + return; +} // Dausen & Aidan + +function luaTask1 { + msObjective(countitem(TulimsharGuardCard), l("* @@/@@ @@", countitem(TulimsharGuardCard), 1, getitemlink(TulimsharGuardCard))); + mes l("--- OR ---"); + msObjective(MPQUEST, l("* Register as a Monster Hunter")); + return; +} + +function luaCheckTask1 { + return (MPQUEST || + countitem(TulimsharGuardCard)); +} + + + + + +function luaAssignTask2 { + mesn; + mesq l("Great! Here, I'll give you %d GP so you can start here with more ease.", 700); + tutmes l("You can get money in several ways, including by selling monster parts, doing daily quests, gambling, and even becoming the mayor of a town. Read the [@@help://faq|FAQ@@] (opens ingame) for help."), l("Getting Money"); + next; + mesn; + mesq l("Well, I have a small problem. Sorry, but my superior officer did not like you. This happens, not everyone will like us, right? A lot of adventurers actually hate me %%R"); + next; + mesn; + mes l("Anyway, he had a point. What will you do once the Monster War is over?"); + if (MPQUEST) + mes l("The monster hunting program will end, and what will you do?"); + if (countitem(TulimsharGuardCard)) + mes l("The city guard won't need help from strangers anymore."); + next; + mesn; + mesq l("So, you need an alternative way to earn a living. So I looked at what is at highest demand right now, and which will remain once the war ends, and I've found you... %s!", b(l("two jobs"))); + next; + mesn; + mes l("%s is by becoming a %s.", b(l("The first job,")), b(l("Fisherman/Fisherwoman"))); + mes l("Food is, and should always be, in demand."); + // If you skipped Tutorial: You already know all this + tutmes l("Fishing is a boring task. Throw the bait, wait for fish to bite. Pull before it is too late. Then sell the fish for money."), l("Fishing"); + next; + mes l("However, you need a %s or a Fishing Net. There's one fisherman in Tulimshar Center, called Eugene. He should have one, but he lives isolated on a small island... Good luck getting to him.", getitemlink(FishingRod)); + tutmes l("The path to reach Eugene is hidden. However, if you pay attention to the map, you should figure out how to reach that island without swimming."), l("Secret Passages"); + next; + mesn; + mes l("%s is by becoming a %s.", b(l("The second job,")), b(l("Craftsman/Craftswoman"))); + tutmes l("One of the most important jobs on Moubootaur Legends, crafted equipment is much stronger than regular ones. Specially weapons."), l("Crafting"); + tutmes l("To craft an item, you'll need to learn its blueprint. Blueprints can be very hard to obtain! Being a crafter will take a lot of your time."), l("Crafting"); + next; + mesn; + mesq l("I think Intense Beard, on Tulimshar's Forge, was taking new apprentices. Try talking to him!"); + next; + mesn; + mesq l("Again, you don't need to do both right now, but you probably will want to. Once you have a stable alternative income, I'll have... This paperwork sorted."); + // Rewards from previous quest + next; + Zeny+=700; + getexp 200, 20; + return; +} // Fishing & Crafting + +function luaTask2 { + msObjective(countitem(FishingRod), l("* @@/@@ @@", countitem(FishingRod), 1, getitemlink(FishingRod))); + mes l("--- OR ---"); + msObjective(CRAFTQUEST, l("* Register as a Craftsman/Craftswoman.")); + return; +} + +function luaCheckTask2 { + return (CRAFTQUEST || + countitem(FishingRod)); +} + + + + + +function luaAssignTask3 { + inventoryplace Bread, 10, BottleOfTonoriWater, 1, SmokeGrenade, 3, CrazyRum, 1, EarthPowder, 1; + + mesn; + mesq l("Congratulations! I've managed to process your paperwork. Here, you'll receive the starter kit!"); + mesc l("* %d %s", 10, getitemlink(Bread)); + mesc l("* %d %s", 3, getitemlink(SmokeGrenade)); + mesc l("* %d %s", 1, getitemlink(BottleOfTonoriWater)); + mesc l("* %d %s", 1, getitemlink(CrazyRum)); + mesc l("* %d %s", 1, getitemlink(EarthPowder)); + next; + mesn; + mesq l("I've also got in contact the other alliance representatives, and I've found out there's a way to bring you to Halinarzo, using Hurnscald route!"); + next; + mesn; + mesq l("...Well, but even if Hurnscald is nearby, you are too weak. You know sometimes, ships get attacked by pirates, right?"); + next; + mesn; + mesq l("So, I'll have to ask you to get strong and some money for the trip. So, go train now! I wish you the best of the luck!"); + + // Rewards from previous quest + next; + getitem Bread, 10; + getitem BottleOfTonoriWater, 1; + getitem SmokeGrenade, 3; + getitem CrazyRum, 1; + getitem EarthPowder, 1; + Zeny+=350; + getexp 250, 25; + return; +} // Minimum level/job to begin + +function luaTask3 { + msObjective(BaseLevel >= 20, l("* @@/@@ Base Level", BaseLevel, 20)); + + msObjective(JobLevel >= 7, l("* @@/@@ Job Level", JobLevel, 7)); + + msObjective(Zeny >= 1500, l("* @@/@@ GP", Zeny, 1500)); + return; +} + +function luaCheckTask3 { + return (BaseLevel >= 20 && + JobLevel >= 7 && + Zeny >= 1500); +} + + + + + +function luaAssignTask4 { + mesn; + mesc l("*cheerful*"); + mesq l("Congratulations on reaching level 20!"); + next; + mesn; + mesq l("I bet you are dead tired, and the ship travel takes a long time."); + next; + mesn; + mesq l("But don't worry! South of here we have an Inn."); + next; + mesn; + mesq l("It's the least I could do for having you to go through all this struggle. I've already paid everything, so just talk to the receptionist for your room :>"); + + // Rewards from previous quest + next; + LUA_ASKED_TO_SLEEP=true; + getexp 200, 20; + return; +} // Inn Arc + +function luaTask4 { + msObjective(!LUA_ASKED_TO_SLEEP, l("* Rest at the Inn")); + return; +} + +function luaCheckTask4 { + return (!LUA_ASKED_TO_SLEEP); +} + + + + + +function luaAssignTask5 { + inventoryplace MercBoxAA, 1; + + mesn; + mesc l("You tell Lua everything you've heard at the Inn."); + next; + + mesn; + mesq l("The Professor? Wait, THE Professor? As in, the wisest person on Tulimshar?"); + next; + mesn; + mesq l("The Professor is so wise that even if you fall asleep while listening to his invisible speech, you'll still learn!!"); + next; + + select + l("Who is this professor?"), + l("What can I do?"); + mes ""; + if (@menu == 1) { + mesn; + mesq l("Who is the Professor? I've heard he was the father of a certain Arthur... No idea."); + tutmes l("The professor is always on the Magic Council, the northmost building. If you sit on the area in front of him (marked with a red rug), you'll gain Experience, even while AFK."); + next; + mesn; + mesq l("But who cares? We need to save the Professor! During night, monsters are stronger, so they'll probably attack at night time!"); + } else { + mesn; + mesq l("You need to save the Professor! During night, monsters are stronger, so they'll probably attack at night time!"); + } + next; + mesn; + mesq l("During night the professor also goes to the Magic Academy, using the hidden north port, on the building to left of the Magic Council..."); + if (!is_night()) + mesc l("However, it's also possible they're there already. Be careful for ambushes!"); + next; + + mesn; + mesq l("Here, I'll give you a Mercenary Box. Open it, get the mercenary card, and summon it when needed. You absolutely CANNOT let the Professor be assassinated!"); + tutmes l("Explain briefly about mercenaries... I mean, we have a Mercenaries section on @tutorial right?"); // <-- TODO and add on the wiki as well! + tutmes l("You can only have one mercenary active at a time, they die and will leave after 10 minutes - logged in or not."); + tutmes l("Still, they can be useful when you're trying to solo a boss and need help. The number of stars on the card is the strength and rarity. Use them wisely."); + + next; + mesn; + mesq l("Now go! Lives are at stack!"); + mesc l("NOTE: The Energy Ball explodes and Kills. Very. Well. Make them a priority!"), 1; + + next; + getitem MercBoxAA, 1; + LUA_ASKED_TO_SAVE_PROFESSOR=true; + return; +} // Your Report, the plan + +function luaTask5 { + mesc l("NOTE: This quest is still a work in progress. \"Bugs\" will show up on purpose."); + mes ""; + + msObjective(!LUA_ASKED_TO_SAVE_PROFESSOR, l("* Defeat the Assassin")); + mes ""; + mesc l("NOTE: The Energy Ball explodes and Kills. Very. Well. Make them a priority!"); + next; + mesn; + mesq l("During night the professor also goes to the Magic Academy, using the hidden north port, on the building to left of the Magic Council..."); + if (!is_night()) + mesc l("However, it's also possible they're there already. Be careful for ambushes!"); + return; +} + +function luaCheckTask5 { + return (!LUA_ASKED_TO_SAVE_PROFESSOR); +} + + + + + +// Then we jump to L_Complete +// When you finish the last quest from Lua +L_Finish: + inventoryplace TreasureKey, 1; + getexp BaseLevel*60, JobLevel*10; // Reference Levels: (20, 6) + setq General_Narrator, 3, 0; + getitem TreasureKey, 1; + getvaultexp(10); + mesn; + mesq l("Well done! I hope no more of those assassins show up. I'm afraid I cannot give you a proper reward, but..."); + next; + mesn; + mesq l("Ah, you might have seen a treasure chest on the mines. There are several of these chests to loot, and loot again! Here is a @@. Try it!", getitemlink(TreasureKey)); + next; + mesn; + mesq l("Hopefully, you'll find something interesting on it to serve as a payment. It's up to luck, I guess...."); + next; +L_Complete: + mesn; + mesq l("You see the docks on the other side of the town? There should be a ship docked there."); + next; + if (!$HURNS_LIBDATE) { + mesn; + mesq l("The ship can bring you to the town of Hurnscald, but we have a problem: It is currently being occupied by a Monster Army."); + next; + mesn; + mesq l("That would be your next destination, but first, the monster army must be driven out!"); + next; + mesn; + mesq l("The Game Masters are assembling a party to storm Hurnscald and liberate it. You should try joining it!"); + next; + mesc l(">> Hurnscald must be Liberated first, to continue this story <<"), 1; + close; + } + mesn; + mesq l("You already got the money. Go there and pay ##B Hurnscald ##b a visit. The ##B mayor ##b should be able to help you."); + next; + mesn; + mesq l("Ship travels are quite expensive, so perhaps you may think it is worth to complete the other quests here?"); + next; + .@r=reputation("Tulim"); + mesn; + if (.@r <= 35) + mesq l("Try helping the city guard. Talk to NPCs around the city. Outside the city walls too. And there are some people on the mines."); + else if (.@r <= 60) + mesq l("You are doing some progress. There are about 4 quests on the mines, 1 outside walls, and 10 on the town. Some require level, so talk to people again sometimes."); + else if (.@r <= 85) + mesq l("You'll soon run out of stuff to do around here, so yeah, you should extend your horizons or you may get bored soon enough."); + else + mesq l("Tulimshar doesn't have a lot more of quests to offer you, so you should definitely explore new places."); + if (BaseLevel < 30) + mesq l("I'm sure there are a lot of sidequests here until you reach level 30, but that's optional."); + close; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +// Beginner Guide Functions +L_Tips: + mesn; + mes l("Hmm... First of, I would really add some agility. Agility helps you to don't be hit, and not being hit is awesome."); + mes l("Hm, I would also do some quests if I needed money... On other hand, maybe I could go fishing? Do I know any fisher?"); + mes l("Ah, getting experience is hard, too... I would stay within the city. Hit and Run tactics works the best."); + next; + mesn; + mes l("Hit'n'run is not so hard... One hit, one step back... One hit, another step back... One miss, two steps back..."); + mes l("That works with most of monsters. Well, but we all know from @@ that it is better to stay put and let mobs kill us, right?", "Apane"); + mes l("Heh, there is death penalty outside the town. You better watch out. The mobs are on a foul mood today, very easy to get killed."); + next; + mes l("Failing all else, you can stay at the Magic Council getting experience for sitting. Hopefully you'll gain a level and this will help you a bit."); + mes l("Of course, if your problem is only experience, then you should look for %s and other EXP-boosting drinks. Some are sold, some are made, some are dropped, some are... magical...", getitemlink(Beer)); + next; + mesn; + mesc l("%s thinks if she knows any major, good quest to do.", .name$); + if (!countitem(TulimsharGuardCard)) { + mes l("Tulimshar Guard Card will prove good intentions. Talk to the Lieutenant in front of the Guard House."); + mesc l("Completing this quest will allow access to the Guard House, where you can pick Daily Bounties for monsters."); + mes ""; + } + if (!countitem(FishingRod)) { + mes l("The fishing rod allows a quick income by selling fishes. However, reaching the fisherman might require some exploration... The whole world is full of secrets."); + mes ""; + } + if (!countitem(LifestonePendant)) { + mes l("The lifestone pendant allows you a quicker HP recovery. Life is more important than defense."); + mes l("I have no idea about who would make a pendant, but lifestone can be done just outside the town walls."); + mes ""; + } + if (!getq(TulimsharQuest_Sailors) < 2) { + mes l("There are some sailors near the forge. Think outside the box - or rather, inside it, unless you have a fortune to buy the items."); + mes ""; + } + if (!(CRAFTQUEST || MPQUEST)) { + mes lg("To register as a craftswoman, you need to talk to Intense Beard in Terranite Forge. The forge is in the Bazar.", "To register as a craftsman, you need to talk to Intense Beard in Terranite Forge. The forge is in the Bazar."); + mes l("To register as a hunter, talk to Aidan, also in the marketplace - the Bazar."); + mesc l("You can, and @@, register on both programs.", b(l("SHOULD"))); + mes ""; + } + if (BaseLevel < 20) { + mes l("If you are in need to get lots of experience quickly, you should try to engage the bigger and stronger monsters in the town. However, if they hit you, it's Soul Menhir for you."); + next; + mes l("Another way to quickly get experience at lower levels, is visiting %s and talking to Professor. I heard those whom sit at the rug gain exp just by listening.", b(l("Magic Council"))); + next; + mes l("But the most useful thing might be drinking %s and the like. They also boost drop rates, but weak drinks effects are low and short.", getitemlink(Beer)); + mes ""; + } + mesc l("She can't remember if anything else right now."); + close; + +L_Check: + mesn; + mesq l("Hmm... That's a though question!"); + next; + mesn; + if (BaseLevel < 20) + mesq l("Currently, you stand no chance against the Snakes on the Desert Canyon."); + else if (BaseLevel < 25) + mesq l("Currently, you should not attempt the Desert Canyon because low level."); + else if (BaseLevel < 30) + mesq l("Currently, you stand no chance against the Snakes on the Desert Canyon, but you probably can cross it."); + else if (BaseLevel < 40) + mesq l("I'm pretty confident you can attempt the Desert Canyon, but expect to die a few times."); + else if (BaseLevel < 50) + mesq l("You have a high level. Go to Halinarzo already! You could even, I don't know, search for magic?!"); + else + mesq l("Your level is so high, I'm surprised you haven't went there already. But then, most quests around there are for your level..."); + next; + mesn; + mesq l("Bows are good, but if you're going to the Desert Canyon, I would instead invest on a light shield. Heavy ones make you walk slower."); + next; + .@r=reputation("Tulim"); + mesn; + if (.@r <= 35) + mesq l("Try helping the city guard. Talk to NPCs around the city. Outside the city walls too. And there are some people on the mines."); + else if (.@r <= 60) + mesq l("You are doing some progress. There are about 4 quests on the mines, 1 outside walls, and 10 on the town. Some require level, so talk to people again sometimes."); + else if (.@r <= 85) + mesq l("You'll soon run out of stuff to do around here, so yeah, you should extend your horizons or you may get bored soon enough."); + else + mesq l("Tulimshar doesn't have a lot more of quests to offer you, so you should definitely explore new places."); + close; + + + + + + + + + + + + + + +///////////////////////////////////////////////////////////////////////////////// +// Contributor Functions +L_Contributor: + mes ""; + .@m = htget($@CONTRIBUTORS, strtolower(strcharinfo(0)), 0); + if (!.@m) { + mesn; + mesq l("Ah... Sorry, your name is not on the contributor list."); + next; + mesn; + mesq l("You can colaborate with our project though! Just ask on #tmw2-dev, be it on Discord or IRC!"); + close; + } + mesn; + mesq l("Ah, welcome @@. You have @@ Contributor Points.", strcharinfo(0), .@m); + mesq l("Let's see if you can pick a reward!"); + select + rif(!(#CRW & 128) && .@m >= 250, l("30x Bug Leg")), + rif(!(#CRW & 64) && .@m >= 100 && Zeny > 10000, l("(10,000 GP) Epic Mount")), + rif(!(#CRW & 32) && .@m >= 50, l("Developer Cap")), + rif(!(#CRW & 16) && .@m >= 40, l("Delicious Cookie")), + rif(!(#CRW & 8) && .@m >= 30, l("20x Bug Leg")), + rif(!(#CRW & 4) && .@m >= 20, l("Contributor Sweater")), + rif(!(#CRW & 2) && .@m >= 10, l("Community Shirt")), + rif(!(#CRW & 1) && .@m, l("5x Strange Coins")), + l("Which rewards are available?"), + l("Ok, thanks."); + + mes ""; + switch (@menu) { + case 1: + luaCReward(BugLeg, 128, false, 30); break; + case 2: + Zeny=Zeny-10000; luaCReward(LegendaryMouboo, 64, true); break; + case 3: + luaCReward(DEVCap, 32); break; + case 4: + luaCReward(DeliciousCookie, 16); break; + case 5: + luaCReward(BugLeg, 8, false, 20); break; + case 6: + luaCReward(ContributorSweater, 4); break; + case 7: + luaCReward(CommunityShirt, 2); break; + case 8: + luaCReward(StrangeCoin, 1, false, 5); break; + case 9: + mesf("250 - 30x %s", getitemlink(BugLeg)); + mesf("100 (+10,000 GP) - %s", getitemlink(LegendaryMouboo)); + mesf("50 - %s", getitemlink(DEVCap)); + mesf("40 - %s", getitemlink(DeliciousCookie)); + mesf("30 - 20x %s", getitemlink(BugLeg)); + mesf("20 - %s", getitemlink(ContributorSweater)); + mesf("10 - %s", getitemlink(CommunityShirt)); + mesf("1 - 5x %s", getitemlink(StrangeCoin)); + next; + goto L_Contributor; + break; + } + close; + +function luaCReward { + .@it=getarg(0); + .@cr=getarg(1); + .@bd=getarg(2, false); + .@am=getarg(3, 1); + if (!.@bd) + getitem .@it, .@am; + else + getitembound .@it, .@am, 1; + #CRW=#CRW|.@cr; + logmes(sprintf("%s has claimed a(n) %s with Contributor Points.", + strcharinfo(0), getitemname(.@it))); + logmes(sprintf("%s has claimed a(n) %s with Contributor Points.", + strcharinfo(0), getitemname(.@it)), LOGMES_ATCOMMAND); + return; +} + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, GMRobe); + setunitdata(.@npcId, UDT_HEADBOTTOM, LousyMoccasins); + setunitdata(.@npcId, UDT_WEAPON, BugSlayer); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + bindatcmd "strangecoin", "Lua#003-2::OnGMCoinClaim", 60, 100, 1; + bindatcmd "strangeshop", "Lua#003-2::OnGMEventShop", 60, 100, 1; + + .sex = G_FEMALE; + .distance = 4; + end; +} diff --git a/npc/003-2/mapflags.txt b/npc/003-2/mapflags.txt new file mode 100644 index 0000000..8637eac --- /dev/null +++ b/npc/003-2/mapflags.txt @@ -0,0 +1 @@ +003-2 mapflag town diff --git a/npc/003-2/politics.txt b/npc/003-2/politics.txt new file mode 100644 index 0000000..2ea738e --- /dev/null +++ b/npc/003-2/politics.txt @@ -0,0 +1,58 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Town Administrator file, see npc/functions/politics.txt +// User variables: +// #POL_APPLYWEEK = Week of last application +// #POL_VOTEDAY = Day of last vote + +003-2,29,35,0 script Tulimshar Office NPC_POLITICS,{ +do +{ + mesc ".:: "+l("Tulimshar Townhall")+" ::.", 2; + mesc l("Current Town Administrator: ")+$TULIM_MAYOR$, 3; + POL_TownInfo("TULIM"); + mesc l("Application fee: @@ GP", .applytax); + next; + select + l("Information"), + rif(strcharinfo(0) == $TULIM_MAYOR$, l("Manage Town")), + rif(#POL_APPLYWEEK != gettimeparam(GETTIME_WEEKDAY), l("Apply for the office!")), + l("View Candidate List and cast a vote"), + l("[Quit]"); + + switch (@menu) { + case 1: + POL_Information(); + break; + case 2: + POL_Manage("TULIM"); + break; + case 3: + // array_push might be too sensible for getd/setd + if (Zeny < .applytax) + break; + Zeny-=.applytax; + $TULIM_MONEY+=.applytax; + #POL_APPLYWEEK=gettimeparam(GETTIME_WEEKDAY); + array_push($TULIM_CANDIDATE$, strcharinfo(0)); + array_push($TULIM_VOTES, 0); + mesc l("Application successful!"), 3; + next; + break; + case 4: + POL_Candidate("TULIM"); + break; + default: + close; + } +} while (true); +end; + +OnInit: + .applytax=120; + .distance=4; + end; +} + diff --git a/npc/003-3/_import.txt b/npc/003-3/_import.txt new file mode 100644 index 0000000..d371450 --- /dev/null +++ b/npc/003-3/_import.txt @@ -0,0 +1,4 @@ +// Map 003-3: Tulimshar Storage +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-3/_warps.txt", +"npc/003-3/malindou.txt", diff --git a/npc/003-3/_warps.txt b/npc/003-3/_warps.txt new file mode 100644 index 0000000..3f24581 --- /dev/null +++ b/npc/003-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-3: Tulimshar Storage warps +003-3,39,41,0 warp #003-3_39_41 0,0,003-1,56,100 diff --git a/npc/003-3/malindou.txt b/npc/003-3/malindou.txt new file mode 100644 index 0000000..e6d1c03 --- /dev/null +++ b/npc/003-3/malindou.txt @@ -0,0 +1,862 @@ +// TMW-2 Script. +// Author: +// Saulc +// Jesusalva +// Notes: +// Tulim banker, and also handles PCLogin events: CheckClientVersion, and bank +// Take care of server updates, but instances are defined on their ships. + +003-3,36,34,0 script Malindou NPC_LLOYD,{ + Banker(.name$, "Tulimshar", 9000); + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + + // Update handler (use `date +%s` for this) + // Current UPDATE value: Qui Jun 7 08:10:55 -03 2018 + if ($UPDATE < 1528369855) { + $UPDATE=1528369855; + debugmes "Warning."; + debugmes "Warning."; + debugmes "Warning: This introduces a server update:"; + debugmes ""; + debugmes "* Automatic variable setup"; + debugmes ""; + $HASAN_GP=2000; + $MANA_BINT=10; + $MANA_BLVL=40; + $MANA_JLVL=20; + $VAULT_01738039=25; + $TULIM_MAYOR$="Jesus Saves"; + $TULIM_TAX=100; + $TULIM_EXPORT=5000; + $TULIM_REPUTATION=50; + $HALIN_MAYOR$="Jesus Saves"; + $HALIN_TAX=100; + $HALIN_EXPORT=2000; + $HALIN_REPUTATION=50; + $HURNS_MAYOR$="Jesus Saves"; + $HURNS_TAX=100; + $HURNS_EXPORT=3000; + $HURNS_REPUTATION=50; + $LOF_MAYOR$="Jesus Saves"; + $LOF_TAX=100; + $LOF_EXPORT=4000; + $LOF_REPUTATION=50; + $NIVAL_MAYOR$="Jesus Saves"; + $NIVAL_TAX=100; + $NIVAL_EXPORT=3000; + $NIVAL_REPUTATION=50; + $FROSTIA_MAYOR$="Jesus Saves"; + $FROSTIA_TAX=100; + $FROSTIA_EXPORT=3000; + $FROSTIA_REPUTATION=50; + $CANDOR_MAYOR$="Jesus Saves"; + $CANDOR_TAX=1000; + $CANDOR_EXPORT=0; + $CANDOR_REPUTATION=100; + $BCONFN_SPAWN = 70; + $BCONFD_SPAWN = 100; + $BCONFN_MOBHP = 100; + $BCONFD_MOBHP = 90; + $BCONFB_EXPR = 100; + $BCONFN_EXPR = 5; + $BCONFD_EXPR = 0; + $BCONFB_DROP = 100; + $BCONFN_DROP = 7; + $BCONFD_DROP = 0; + $BETASERVER = false; + $AUTORESTART = false; + $ALLIANCE_TAX1 = 7500; + $ALLIANCE_TAX2 = 60; + $FIRESOFSTEAM[1] = 2000; + $FIRESOFSTEAM[2] = 2000; + $FIRESOFSTEAM[3] = 2000; + $FIRESOFSTEAM[4] = 2000; + $FIRESOFSTEAM[5] = 2000; + } + /* + // Current UPDATE value: Dom Jun 17 21:32:45 -03 2018 + if ($UPDATE < 1529281965) { + $UPDATE=1529281965; + debugmes ""; + debugmes "* Please set #BankP for bankers"; + debugmes "* Automatically creatining minimal GP reserves for Hasan Autumn's Quest"; + debugmes ""; + $HASAN_GP=rand(3,12)+rand(3,12)+rand(3,12); + } + // Current UPDATE value: Dom Jun 30 22:44:46 -03 2018 + if ($UPDATE < 1530409486) { + $UPDATE=1530409486; + debugmes ""; + debugmes "* Clearing invalid (deleted) items"; + debugmes ""; + DelItemFromEveryPlayer(729); + } + // Current UPDATE value: Qui Jul 12 02:40:02 -03 2018 + if ($UPDATE < 1531374002) { + $UPDATE=1531374002; + debugmes ""; + debugmes "* WARNING! WARNING! WARNING!"; + debugmes "* ITEM ID VIOLATION DETECTED!"; + debugmes ""; + debugmes "* ALL POLISHED EMERALDS BECOME POLISHED AMETHYSTS, AND VICE-VERSA!"; + debugmes "* This should not have an effect ingame."; + debugmes ""; + } + // Current UPDATE value: Qui Ago 2 13:58:17 -03 2018 + if ($UPDATE < 1533229097) { + $UPDATE=1533229097; + debugmes ""; + debugmes "* ERASING all victories with Fafi Dragon"; + debugmes ""; + query_sql("UPDATE `quest` SET `count1` = '1' WHERE `quest`.`quest_id`="+LoFQuest_Fairy+" AND `count1` = '2'"); + query_sql("UPDATE `quest` SET `count1` = '2' WHERE `quest`.`quest_id`="+LoFQuest_Fairy+" AND `count1` = '3'"); + DelItemFromEveryPlayer(SkullMask); + } + // Current UPDATE value: Qua Ago 8 11:04:10 -03 2018 + if ($UPDATE < 1533737050) { + $UPDATE=1533737050; + debugmes ""; + debugmes "* REMOVING now rare items."; + debugmes ""; + DelItemFromEveryPlayer(SilverMirror); + } + // Current UPDATE value: Seg Ago 27 20:20:21 -03 2018 + if ($UPDATE < 1535412021) { + $UPDATE=1535412021; + debugmes ""; + debugmes "* SETUP mana stone default values."; + debugmes ""; + $MANA_BINT=30; + $MANA_BLVL=30; + $MANA_JLVL=15; + } + // Current UPDATE value: Qui Set 06 13:50:37 -03 2018 + if ($UPDATE < 1536252637) { + $UPDATE=1536252637; + debugmes ""; + debugmes "* Clearing invalid (deleted) items"; + debugmes "* Initial Vault Values"; + debugmes ""; + DelItemFromEveryPlayer(802); + DelItemFromEveryPlayer(557); + $VAULT_01738039=rand(5,25); + } + // Current UPDATE value: Qui Set 13 14:17:50 -03 2018 + if ($UPDATE < 1536859070) { + $UPDATE=1536859070; + debugmes ""; + debugmes "* Aisen already have a completed node at PETMEMO."; + debugmes "* This was handled automatically, no special care is required."; + debugmes ""; + } + // Current UPDATE value: Dom Set 16 22:21:05 -03 2018 + if ($UPDATE < 1537147265) { + $UPDATE=1537147265; + debugmes ""; + debugmes "* My Money quest state modified"; + debugmes "* Foxhound Famine quest state modified"; + debugmes "* Life Delight quest state modified"; // “A True Chef” is the LoF version of this quest + debugmes ""; + query_sql("UPDATE `quest` SET `count1` = '1' WHERE `quest`.`quest_id`="+ShipQuests_ArpanMoney+" AND `count1` = '2'"); + query_sql("UPDATE `quest` SET `count2` = `count1` WHERE `quest`.`quest_id`="+HalinarzoQuest_Foxhound+" AND `count1` >= '6'"); + query_sql("UPDATE `quest` SET `count1` = '6' WHERE `quest`.`quest_id`="+HalinarzoQuest_Foxhound+" AND `count1` > '6'"); + query_sql("UPDATE `quest` SET `count2` = `count1` WHERE `quest`.`quest_id`="+HalinarzoQuest_LifeDelight); + query_sql("UPDATE `quest` SET `count1` = '1' WHERE `quest`.`quest_id`="+HalinarzoQuest_LifeDelight+" AND `count1` > '1'"); + } + // Current UPDATE value: Seg Set 17 16:02:27 -03 2018 + if ($UPDATE < 1537210947) { + $UPDATE=1537210947; + debugmes ""; + debugmes "* Referral Hack System"; + debugmes ""; + setarray $REFERRAL_IDS, 0; + } + // Current UPDATE value: Ter Out 16 17:17:20 -03 2018 + // This is a hack because I'm too lazy to be bothered with standard SQL Upgrade system + if ($UPDATE < 1539721040) { + query_sql("CREATE TABLE IF NOT EXISTS `discord` (`account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',`discord_id` VARCHAR(255) NOT NULL DEFAULT '',`discord_name` VARCHAR(255) NOT NULL DEFAULT '',`verified` ENUM('0', '1') NOT NULL DEFAULT '0') ENGINE=MyISAM"); + $UPDATE=1539721040; + //$NIVALIS_LIBDATE=1539721040; + debugmes ""; + debugmes "* SQL Upgrade (no effect on new installations)"; + //debugmes "* Nivalis Liberation Day event skipped"; + debugmes ""; + } + // Current UPDATE value: Dom Nov 18 17:40:30 -02 2018 + // Update players position + if ($UPDATE < 1542570030) { + query_sql("UPDATE `char` SET `last_x` = '35' WHERE `char`.`last_map`='005-1'"); + query_sql("UPDATE `char` SET `last_y` = '102' WHERE `char`.`last_map`='005-1'"); + query_sql("UPDATE `char` SET `save_x` = '35' WHERE `char`.`save_map`='005-1'"); + query_sql("UPDATE `char` SET `save_y` = '102' WHERE `char`.`save_map`='005-1'"); + $UPDATE=1542570030; + debugmes ""; + debugmes "* Update Candor players position"; + debugmes ""; + } + // Current UPDATE value: Sab Dez 15 21:45:15 -02 2018 + // Upgrade every Wooden Sword in a Bug Slayer + if ($UPDATE < 1544917515) { + ReplaceItemFromEveryPlayer(WoodenSword, BugSlayer); + $UPDATE=1544917515; + debugmes ""; + debugmes "* Replaced Wooden Swords with Bug Slayer"; + debugmes ""; + } + // Current UPDATE value: Sab Dez 17 13:52:54 -02 2018 + // Fix a critical bug because Nard's ship doors logic was changed. Upgrade premium weapons in rare weapons + if ($UPDATE < 1545061974) { + //query_sql("UPDATE `quest` SET `count1` = '3' WHERE `quest`.`quest_id`='0' AND `quest`.`count1` = '2' AND `quest`.`char_id` IN (SELECT `quest`.`char_id` WHERE `quest`.`quest_id` = '12' AND `quest`.`count1`>='1')"); + + ReplaceItemFromEveryPlayer(MiereCleaver, LEGACY_MiereCleaver); + ReplaceItemFromEveryPlayer(Broadsword, LEGACY_Broadsword); + $UPDATE=1545061974; + debugmes ""; + debugmes "* Replaced Miere Cleaver and Broad Sword with a special version specific for old players"; + debugmes "* Fix critical bug with Nard (not needed on Main Server)"; + debugmes ""; + } + // Current UPDATE value: Qua Dez 26 21:45:10 -02 2018 + // Update item id + if ($UPDATE < 1545867910) { + ReplaceItemFromEveryPlayer(6000, 9990); + ReplaceItemFromEveryPlayer(6001, 9991); + ReplaceItemFromEveryPlayer(6004, 9994); + ReplaceItemFromEveryPlayer(6005, 9995); + + ReplaceItemFromEveryPlayer(9990, 6001); + ReplaceItemFromEveryPlayer(9991, 6000); + ReplaceItemFromEveryPlayer(9994, 6005); + ReplaceItemFromEveryPlayer(9995, 6004); + + $UPDATE=1545867910; + debugmes ""; + debugmes "* Update Bows IDs"; + debugmes ""; + } + // Current UPDATE value: Seg Dez 31 11:45:35 -02 2018 + // Update players position (002-x). Update Peter Quest. Update Main quest. + if ($UPDATE < 1546263935) { + query_sql("UPDATE `char` SET `last_x` = '53' WHERE `char`.`last_map`='002-1'"); + query_sql("UPDATE `char` SET `last_y` = '38' WHERE `char`.`last_map`='002-1'"); + query_sql("UPDATE `char` SET `last_x` = '53' WHERE `char`.`last_map`='002-3'"); + query_sql("UPDATE `char` SET `last_y` = '38' WHERE `char`.`last_map`='002-3'"); + query_sql("UPDATE `char` SET `last_x` = '53' WHERE `char`.`last_map`='002-4'"); + query_sql("UPDATE `char` SET `last_y` = '38' WHERE `char`.`last_map`='002-4'"); + + query_sql("UPDATE `quest` SET `count1` = '2' WHERE `quest`.`quest_id`="+ShipQuests_Peter+" AND `count1` >= '3'"); + query_sql("UPDATE `quest` SET `count1` = '1' WHERE `quest`.`quest_id`="+General_Narrator+" AND `count1` >= '2'"); + $UPDATE=1546263935; + debugmes ""; + debugmes "* Update players position (Nard's Ship)"; + debugmes "* Update Peter Quest"; + debugmes ""; + } + // Current UPDATE value: Ter Jan 8 11:14:04 -02 2019 + // Update arrow IDs + if ($UPDATE < 1546953244) { + ReplaceItemFromEveryPlayer(CursedArrow, 9990); + ReplaceItemFromEveryPlayer(IronArrow, 9991); + ReplaceItemFromEveryPlayer(9990, IronArrow); + ReplaceItemFromEveryPlayer(9991, CursedArrow); + + // Maybe we should make Tolchi Arrow greater than Training Arrow? + + // PlatinumQuiver - DragonStar - MichelSoul + // Terranite Quiver was broken in two, they'll keep the weaker version + ReplaceItemFromEveryPlayer(1174, 9991); + ReplaceItemFromEveryPlayer(1175, 9992); + ReplaceItemFromEveryPlayer(1176, 9993); + + ReplaceItemFromEveryPlayer(9991, 1175); + ReplaceItemFromEveryPlayer(9992, 1176); + ReplaceItemFromEveryPlayer(9993, 1177); + + $UPDATE=1546953244; + debugmes ""; + debugmes "* Update Arrows IDs"; + debugmes "* Update Quiver IDs"; + debugmes ""; + } + // Current UPDATE value: Ter Jan 15 02:07:40 -02 2019 + // Reset all thief/merc ranks back to 1. + // If + if ($UPDATE < 1547525260) { + + // Already rank 2? Give enough exp to get rank 2 easily. + query_sql("UPDATE `char_reg_num_db` SET `value` = '30' WHERE `char_reg_num_db`.`key`='THIEF_EXP' AND `char_reg_num_db`.`char_id` IN (SELECT `char_reg_num_db`.`char_id` WHERE `char_reg_num_db`.`key` = 'THIEF_RANK' AND `char_reg_num_db`.`value`>='2')"); + // Didn't wanted to rank up? Reset exp to 30! + query_sql("UPDATE `char_reg_num_db` SET `value` = '30' WHERE `char_reg_num_db`.`key`='THIEF_EXP' AND `char_reg_num_db`.`value` >= '32' AND `char_reg_num_db`.`char_id` IN (SELECT `char_reg_num_db`.`char_id` WHERE `char_reg_num_db`.`key` = 'THIEF_RANK' AND `char_reg_num_db`.`value`<='1')"); + // Already rank 2? Return to rank 1. + query_sql("UPDATE `char_reg_num_db` SET `value` = '1' WHERE `char_reg_num_db`.`key`='THIEF_RANK' AND `char_reg_num_db`.`value` >= '2'"); + + // The same thing, now for merchants + + // Already rank 2? Give enough exp to get rank 2 easily. + query_sql("UPDATE `char_reg_num_db` SET `value` = '30' WHERE `char_reg_num_db`.`key`='MERC_EXP' AND `char_reg_num_db`.`char_id` IN (SELECT `char_reg_num_db`.`char_id` WHERE `char_reg_num_db`.`key` = 'MERC_RANK' AND `char_reg_num_db`.`value`>='2')"); + // Didn't wanted to rank up? Reset exp to 30! + query_sql("UPDATE `char_reg_num_db` SET `value` = '30' WHERE `char_reg_num_db`.`key`='MERC_EXP' AND `char_reg_num_db`.`value` >= '32' AND `char_reg_num_db`.`char_id` IN (SELECT `char_reg_num_db`.`char_id` WHERE `char_reg_num_db`.`key` = 'MERC_RANK' AND `char_reg_num_db`.`value`<='1')"); + // Already rank 2? Return to rank 1. + query_sql("UPDATE `char_reg_num_db` SET `value` = '1' WHERE `char_reg_num_db`.`key`='MERC_RANK' AND `char_reg_num_db`.`value` >= '2'"); + + $UPDATE=1547525260; + debugmes ""; + debugmes "* Merc/Thief ranks reset to 1"; + debugmes "Improving past rank 2 helps on minigame"; + debugmes ""; + } + // Current UPDATE value: Qui Fev 28 19:42:12 -03 2019 + // Late fix for Nard Ship Bug + if ($UPDATE < 1551393732) { + query_sql("UPDATE `quest` SET `count1` = '3' WHERE `quest`.`quest_id`="+ShipQuests_Julia+" AND `count1` <= '2'"); + $UPDATE=1551393732; + debugmes ""; + debugmes "* Late fix for Nard Ship Bug"; + debugmes ""; + } + // Current UPDATE value: Qui Mar 7 23:15:54 -03 2019 + // Map Update + if ($UPDATE < 1552011354) { + query_sql("UPDATE `char` SET `last_x` = '22' WHERE `char`.`last_map`='014-3'"); + query_sql("UPDATE `char` SET `last_y` = '22' WHERE `char`.`last_map`='014-3'"); + query_sql("UPDATE `char` SET `last_map` = '000-1' WHERE `char`.`last_map`='014-3'"); + + query_sql("UPDATE `char` SET `last_x` = '22' WHERE `char`.`last_map`='003-1'"); + query_sql("UPDATE `char` SET `last_y` = '22' WHERE `char`.`last_map`='003-1'"); + query_sql("UPDATE `char` SET `last_map` = '000-1' WHERE `char`.`last_map`='003-1'"); + $UPDATE=1552011354; + debugmes ""; + debugmes "* Map Updates"; + debugmes ""; + } + // Current UPDATE value: Sáb Mar 23 11:11:32 -03 2019 + // Soul Menhir savepoint replaced + if ($UPDATE < 1553350292) { + query_sql("UPDATE `char` SET `save_map` = '000-1'"); + query_sql("UPDATE `char` SET `save_x` = '22'"); + query_sql("UPDATE `char` SET `save_y` = '22'"); + $UPDATE=1553350292; + debugmes ""; + debugmes "* Soul Menhir Save Point replaced"; + debugmes ""; + } + // Current UPDATE value: Sex Abr 26 00:12:35 -03 2019 + // Monster King & Main Storyline System + if ($UPDATE < 1556248355) { + query_sql("UPDATE `quest` SET `count3` = '0' WHERE `quest`.`quest_id`="+HurnscaldQuest_Arkim); + query_sql("UPDATE `quest` SET `count2` = '0' WHERE `quest`.`quest_id`="+HurnscaldQuest_Arkim); + $GAME_STORYLINE=2; + + $UPDATE=1556248355; + debugmes ""; + debugmes "* Main Storyline system updated"; + debugmes "* Arkim updated"; + debugmes ""; + } + // Current UPDATE value: Sab Mai 04 02:21:58 -03 2019 + // Remove Return Crystals + if ($UPDATE < 1556947318) { + query_sql("UPDATE `quest` SET `count2` = '7' WHERE `quest`.`quest_id`="+General_Narrator+" AND `count1` >= '6'"); + query_sql("UPDATE `quest` SET `count1` = '5' WHERE `quest`.`quest_id`="+General_Narrator+" AND `count1` >= '6'"); + + DelItemFromEveryPlayer(ReturnPotion); + + $UPDATE=1556947318; + debugmes ""; + debugmes "* Player Storyline modified"; + debugmes "* Return Crystals removed"; + debugmes ""; + } + // Current UPDATE value: Seg mai 06 08:54:55 -03 2019 + // Remove Legendary Weapons + if ($UPDATE < 1557143695) { + DelItemFromEveryPlayer(Lightbringer); + //DelItemFromEveryPlayer(DemureAxe); // Demure's Exception! + DelItemFromEveryPlayer(Tyranny); + DelItemFromEveryPlayer(Runestaff); + DelItemFromEveryPlayer(AegisShield); + + $UPDATE=1557143695; + debugmes ""; + debugmes "* Legendary Weapons removed"; + debugmes ""; + } + // Current UPDATE value: Dom mai 26 23:00:52 -03 2019 + // Shovel Update + if ($UPDATE < 1558922452) { + DelItemFromEveryPlayer(527); // Old pumpkish juice nobody have + ReplaceItemFromEveryPlayer(526, 527); + + $UPDATE=1558922452; + debugmes ""; + debugmes "* Shovel Update"; + debugmes ""; + } + // Current UPDATE value: Seg mai 27 10:57:02 -03 2019 + // Picklog Update + if ($UPDATE < 1558965422) { + query_sql("ALTER TABLE picklog MODIFY COLUMN `opt_val0` SMALLINT(5) NOT NULL DEFAULT '0';"); + query_sql("ALTER TABLE picklog MODIFY COLUMN `opt_val1` SMALLINT(5) NOT NULL DEFAULT '0';"); + query_sql("ALTER TABLE picklog MODIFY COLUMN `opt_val2` SMALLINT(5) NOT NULL DEFAULT '0';"); + query_sql("ALTER TABLE picklog MODIFY COLUMN `opt_val3` SMALLINT(5) NOT NULL DEFAULT '0';"); + query_sql("ALTER TABLE picklog MODIFY COLUMN `opt_val4` SMALLINT(5) NOT NULL DEFAULT '0';"); + + // Replace all BugSlayer, ShortGladius, RealBronzeGladius and Backsword + // With a version containing the critical damage option (bCritAtkRate) + // auction table have no faulty items, nor do carts nor do I care w/ rodex + // by the time this query was writen + // IOPT_CRITDMG id is 199 + query_sql("UPDATE `inventory` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+BugSlayer); + query_sql("UPDATE `storage` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+BugSlayer); + query_sql("UPDATE `guild_storage` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+BugSlayer); + + query_sql("UPDATE `inventory` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+ShortGladius); + query_sql("UPDATE `storage` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+ShortGladius); + query_sql("UPDATE `guild_storage` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+ShortGladius); + + query_sql("UPDATE `inventory` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+RealBronzeGladius); + query_sql("UPDATE `storage` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+RealBronzeGladius); + query_sql("UPDATE `guild_storage` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+RealBronzeGladius); + + query_sql("UPDATE `inventory` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+Backsword); + query_sql("UPDATE `storage` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+Backsword); + query_sql("UPDATE `guild_storage` SET `opt_idx0` = '199', `opt_val0` = '30'"+ + " WHERE `nameid`="+Backsword); + + $UPDATE=1558965422; + debugmes ""; + debugmes "* picklog options Update"; + debugmes ""; + } + // Current UPDATE value: Sex Jun 21 13:20:21 -03 2019 + // Tulimshar Sailors quest was split in two + if ($UPDATE < 1561134021) { + query_sql("UPDATE `quest` SET `count1` = '4' WHERE `quest`.`quest_id`="+TulimsharQuest_Sailors+" AND `count1` >= '2'"); + + query_sql("UPDATE `char` SET `last_x` = '22' WHERE `char`.`last_map`='005-1'"); + query_sql("UPDATE `char` SET `last_y` = '22' WHERE `char`.`last_map`='005-1'"); + query_sql("UPDATE `char` SET `last_map` = '000-1' WHERE `char`.`last_map`='005-1'"); + $UPDATE=1561134021; + debugmes ""; + debugmes "* Tulimshar Sailors split in two"; + debugmes ""; + } + // Current UPDATE value: Seg Jul 15 22:10:00 -03 2019 + // Candor Redesign + if ($UPDATE < 1563239400) { + query_sql("UPDATE `char` SET `last_x` = '22' WHERE `char`.`last_map`='005-1'"); + query_sql("UPDATE `char` SET `last_y` = '22' WHERE `char`.`last_map`='005-1'"); + query_sql("UPDATE `char` SET `last_map` = '000-1' WHERE `char`.`last_map`='005-1'"); + query_sql("UPDATE `skill` SET `id` = '20010' WHERE `skill`.`id`='20011'"); + $UPDATE=1563239400; + debugmes ""; + debugmes "* Candor Redesign"; + debugmes "* Magic Class Removed"; + debugmes ""; + } + // Current UPDATE value: Sab Ago 24 17:42:24 -03 2019 + // Political System + if ($UPDATE < 1566679344) { + $UPDATE=1566679344; + // $LOC_MAYOR$ - Stores the name of current Hurnscald Mayor + // $LOC_MONEY - Total money reserves of Hurnscald + // $LOC_TAX - How much in % is charged as taxes. (OnBuy income) + // $LOC_EXPORT - Defines how much Hurnscald exports (weekly income) + // $LOC_REPUTATION - Town reputation. Affects Max Tax and Weekly Income. + + // Tulim + $TULIM_MAYOR$="Saulc GM"; + $TULIM_TAX=100; + $TULIM_EXPORT=5000; + $TULIM_REPUTATION=50; + // Halin + $HALIN_MAYOR$="Saulc GM"; + $HALIN_TAX=100; + $HALIN_EXPORT=2000; + $HALIN_REPUTATION=50; + // Hurns + $HURNS_MAYOR$="Saulc GM"; + $HURNS_TAX=100; + $HURNS_EXPORT=3000; + $HURNS_REPUTATION=50; + // LoF + $LOF_MAYOR$="Jesus Saves"; + $LOF_TAX=100; + $LOF_EXPORT=4000; + $LOF_REPUTATION=50; + // Nival + $NIVAL_MAYOR$="Jesus Saves"; + $NIVAL_TAX=100; + $NIVAL_EXPORT=3000; + $NIVAL_REPUTATION=50; + // Frostia + $FROSTIA_MAYOR$="Jesus Saves"; + $FROSTIA_TAX=100; + $FROSTIA_EXPORT=3000; + $FROSTIA_REPUTATION=50; + + debugmes ""; + debugmes "* Political System Base"; + debugmes ""; + } + // Current UPDATE value: Sex Jan 17 14:42:37 BRT 2020 + // Remove Junk + if ($UPDATE < 1579282957) { + DelItemFromEveryPlayer(748); + DelItemFromEveryPlayer(1166); + $UPDATE=1579282957; + debugmes ""; + debugmes "* Blanket fix"; + debugmes ""; + } + // Current UPDATE value: Dom Fev 16 14:54:30 BRT 2020 + // Language over simplification + if ($UPDATE < 1581875670) { + $UPDATE=1581875670; + query_sql("DELETE FROM `char_reg_num_db` WHERE `key`='INN_REGISTER'"); + DelAccRegFromEveryPlayer("#RARE_POINTS"); + debugmes ""; + debugmes "* Variable cleanup"; + debugmes ""; + } + // Current UPDATE value: Sex Fev 24 12:15:12 BRT 2020 + // Release 11.1 final + if ($UPDATE < 1582557312) { + $UPDATE=1582557312; + ReplaceItemFromEveryPlayer(RiceHat, BullHelmet); + DelItemFromEveryPlayer(SilverEasteregg); + query_sql("UPDATE `quest` SET `count1` = '2' WHERE `quest`.`quest_id`="+General_Narrator+" AND `count1` = '3'"); + query_sql("UPDATE `quest` SET `count2` = '0' WHERE `quest`.`quest_id`="+General_Narrator+" AND `count1` = '2'"); + $BCONFN_SPAWN = 70; + $BCONFD_SPAWN = 100; + $BCONFN_MOBHP = 95; + $BCONFD_MOBHP = 85; + $BCONFB_EXPR = 100; + $BCONFN_EXPR = 5; + $BCONFD_EXPR = 0; + $BCONFB_DROP = 100; + $BCONFN_DROP = 7; + $BCONFD_DROP = 0; + debugmes ""; + debugmes "* Bull Helmet Fix"; + debugmes "* Remove Silver Easter Eggs"; + debugmes "* Lua Quest Reset"; + debugmes "* Battle Configuration is now volatile"; + debugmes ""; + } + // Current UPDATE value: Dom Abr 26 12:41:55 BRT 2020 [1587915715] + // Current UPDATE value: Qua Mai 27 18:56:15 BRT 2020 + // Fix Tolchi mess + if ($UPDATE < 1590616575) { + if ($UPDATE < 1587915715) { + query_sql("UPDATE `inventory` SET `opt_val0` = '20', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx0`='189' OR `opt_idx0`='200') AND (`opt_idx1`='189' OR `opt_idx1`='200') AND (`opt_val0` > '18' AND `opt_val1` > '18')"); + query_sql("UPDATE `cart_inventory` SET `opt_val0` = '20', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx0`='189' OR `opt_idx0`='200') AND (`opt_idx1`='189' OR `opt_idx1`='200') AND (`opt_val0` > '18' AND `opt_val1` > '18')"); + query_sql("UPDATE `storage` SET `opt_val0` = '20', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx0`='189' OR `opt_idx0`='200') AND (`opt_idx1`='189' OR `opt_idx1`='200') AND (`opt_val0` > '18' AND `opt_val1` > '18')"); + query_sql("UPDATE `guild_storage` SET `opt_val0` = '20', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx0`='189' OR `opt_idx0`='200') AND (`opt_idx1`='189' OR `opt_idx1`='200') AND (`opt_val0` > '18' AND `opt_val1` > '18')"); + query_sql("UPDATE `rodex_items` SET `opt_val0` = '20', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx0`='189' OR `opt_idx0`='200') AND (`opt_idx1`='189' OR `opt_idx1`='200') AND (`opt_val0` > '18' AND `opt_val1` > '18')"); + query_sql("UPDATE `auction` SET `opt_val0` = '20', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx0`='189' OR `opt_idx0`='200') AND (`opt_idx1`='189' OR `opt_idx1`='200') AND (`opt_val0` > '18' AND `opt_val1` > '18')"); + } + + // Destroy duplicates (should not exist) + query_sql("UPDATE `inventory` SET `opt_val0` = '15', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx1`=`opt_idx0`) AND (`opt_val0` > '15' AND `opt_val1` > '15')"); + query_sql("UPDATE `storage` SET `opt_val0` = '15', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx1`=`opt_idx0`) AND (`opt_val0` > '15' AND `opt_val1` > '15')"); + query_sql("UPDATE `guild_storage` SET `opt_val0` = '15', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx1`=`opt_idx0`) AND (`opt_val0` > '15' AND `opt_val1` > '15')"); + query_sql("UPDATE `rodex_items` SET `opt_val0` = '15', `opt_val1` = '0', `opt_idx1` = '0' WHERE (`opt_idx1`=`opt_idx0`) AND (`opt_val0` > '15' AND `opt_val1` > '15')"); + + // Magic v3 + $REBIRTH_WINNER$="Crazyfefe"; + DelChrRegFromEveryPlayer("MAGIC_SUBCLASS"); + ReplaceSkillFromEveryPlayer(MG_FIREBALL, TMW2_FIREARROW); // 1 MSP + + // These skills MSP cost was switched + ReplaceSkillFromEveryPlayer(AL_HOLYLIGHT, TMW2_NAPALMBEAT); + ReplaceSkillFromEveryPlayer(MG_NAPALMBEAT, TMW2_HOLYLIGHT); + + ReplaceSkillFromEveryPlayer(MG_SOULSTRIKE, TMW2_MAGICSTRIKE); + ReplaceSkillFromEveryPlayer(WZ_EARTHSPIKE, TMW2_METEORSTRIKE); + + ReplaceSkillFromEveryPlayer(MG_COLDBOLT, TMW2_FROSTDIVER); + ReplaceSkillFromEveryPlayer(MG_FROSTDIVER, TMW2_FROSTNOVA); + ReplaceSkillFromEveryPlayer(WZ_FROSTNOVA, TMW2_NILFHEIM); + + ReplaceSkillFromEveryPlayer(AL_HEAL, TMW2_FIRSTAID); + ReplaceSkillFromEveryPlayer(AB_HIGHNESSHEAL, TMW2_HEALING); + + debugmes ""; + debugmes "* Tolchi mess cleanup"; + debugmes "* Rebirth Hero"; + debugmes ""; + $UPDATE=1590616575; + } + // Current UPDATE value: Ter Jul 28 22:49:52 BRT 2020 + // Tulimshar Volcano + if ($UPDATE < 1595987392) { + $UPDATE=1595987392; + ReplaceItemFromEveryPlayer(816, IcedBottle); + debugmes ""; + debugmes "* Iced Water re-id"; + debugmes ""; + } + // Current UPDATE value: Ter Mar 9 18:45:00 BRT 2021 + // Easter Upgrade + if ($UPDATE < 1615326300) { + $UPDATE=1615326300; + DelChrRegFromEveryPlayer("EASTER_EVENT"); + debugmes ""; + debugmes "* Easter changes"; + debugmes ""; + } + // Current UPDATE value: Qua Abr 7 13:32:35 BRT 2021 + // Skill Update + if ($UPDATE < 1617813155) { + $UPDATE=1617813155; + ReplaceSkillFromEveryPlayer(SM_BASH, TMW2_FALKONSTRIKE); + ReplaceSkillFromEveryPlayer(ASC_METEORASSAULT, TMW2_GROUNDSTRIKE); + ReplaceSkillFromEveryPlayer(MC_MAMMONITE, TMW2_SUPREMEATTACK); + ReplaceSkillFromEveryPlayer(AC_CHARGEARROW, TMW2_CHARGEDARROW); + ReplaceSkillFromEveryPlayer(AC_SHOWER, TMW2_ARROWSHOWER); + debugmes ""; + debugmes "* Skill changes"; + debugmes ""; + } + */ + // Current UPDATE value: Qui Set 23 18:46:36 BRT 2021 + // Variable Update + if ($UPDATE < 1632433596) { + $UPDATE=1632433596; + $ALLIANCE_TAX1 = 7500; + $ALLIANCE_TAX2 = 60; + debugmes ""; + debugmes "* New global variables"; + debugmes ""; + } + // Current UPDATE value: Qui Dez 20 13:47:10 BRT 2021 + // Homun Skill Update + if ($UPDATE < 1640882830) { + $UPDATE=1640882830; + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8047 WHERE `id`=8013"); // HVAN_CAPRICE + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8048 WHERE `id`=8002"); // HLIF_AVOID + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8050 WHERE `id`=8008"); // HAMI_BLOODLUST + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8049 WHERE `id`=8006"); // HAMI_DEFENSE + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8051 WHERE `id`=8009"); // HFLI_MOON + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8053 WHERE `id`=8011"); // HFLI_SPEED + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8052 WHERE `id`=8010"); // HFLI_FLEET + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8054 WHERE `id`=8020"); // MH_POISON_MIST + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8055 WHERE `id`=8032"); // MH_GOLDENE_FERSE + query_sql("UPDATE IGNORE `skill_homunculus` SET `id`=8056 WHERE `id`=8031"); // MH_STAHL_HORN + debugmes ""; + debugmes "* New homunculus skills"; + debugmes ""; + } + + + // This mensures Contributors Credits, and changes only during updates. + // All names in lower case. Only TMW2 direct work! Non-TMW2 contributors + // should be disregarded if they're not involved with the project. + // Standard patch (DIY); 10 points + // Reports: 1 point if relevant enough + + // You may get more points for complexity, difficulty, usability, etc. + // You may get less points if you don't do it yourself, eg. a bug report. + // Relevance of the contribution is also to be taken in account. + + // See 003-2/lua.txt for rewards + + $@CONTRIBUTORS = htnew; + htput($@CONTRIBUTORS, "jesusalva", 250); + htput($@CONTRIBUTORS, "saulc", 250); + htput($@CONTRIBUTORS, "lawncable", 180); + htput($@CONTRIBUTORS, "crazyfefe", 70); + htput($@CONTRIBUTORS, "mishana", 70); + htput($@CONTRIBUTORS, "esteria", 60); + htput($@CONTRIBUTORS, "arthur", 50); + htput($@CONTRIBUTORS, "rakinorf", 40); + htput($@CONTRIBUTORS, "dangerduck", 40); + htput($@CONTRIBUTORS, "pookie", 36); + htput($@CONTRIBUTORS, "xanofire", 32); + htput($@CONTRIBUTORS, "kolchak", 30); + htput($@CONTRIBUTORS, "jak1", 30); + htput($@CONTRIBUTORS, "dustman", 18); + htput($@CONTRIBUTORS, "povo", 10); + htput($@CONTRIBUTORS, "minegamerbr", 10); + htput($@CONTRIBUTORS, "yuckfou", 10); + htput($@CONTRIBUTORS, "kytty", 10); + htput($@CONTRIBUTORS, "ledmitz", 10); + htput($@CONTRIBUTORS, "liangtai", 4); + htput($@CONTRIBUTORS, "demure", 2); + htput($@CONTRIBUTORS, "seeds", 2); + htput($@CONTRIBUTORS, "lilanna", 1); + + htput($@CONTRIBUTORS, "manatauro reborn", 10); + htput($@CONTRIBUTORS, "hocus pocus fidibus", 40); + htput($@CONTRIBUTORS, "cadis etrama di raizel", 20); + //htput($@CONTRIBUTORS, "", 1); + end; + +OnClock0500: + if (gettime(GETTIME_DAYOFMONTH) >= 7) + query_sql("DELETE FROM `chatlog` WHERE `time` < '"+sqldate(-7)+"'"); + query_sql("DELETE FROM `picklog` WHERE `time` < '"+sqldate(0, -3)+"'"); + end; + +// This is for HUB +OnSkillInvoke: + if (!playerattached()) + end; + HUB_SkillInvoke(); + @skillId=0; + end; + +OnPCBonusEvent: + if (!playerattached()) + end; + HUB_PCBonus(); + end; + +OnGlobalChat: + .@msg$ = @chat$; + + // We don't care with punctuation or capitalization + .@msg$ = strtolower(.@msg$); + .@msg$ = replacestr(.@msg$, ",", ""); + .@msg$ = replacestr(.@msg$, ".", ""); + .@msg$ = replacestr(.@msg$, "!", ""); + .@msg$ = replacestr(.@msg$, ":", ""); + .@msg$ = replacestr(.@msg$, ";", ""); + .@msg$ = replacestr(.@msg$, "(", ""); + .@msg$ = replacestr(.@msg$, ")", ""); + .@msg$ = replacestr(.@msg$, "<", ""); + .@msg$ = replacestr(.@msg$, ">", ""); + .@msg$ = replacestr(.@msg$, "*", ""); + + SK_Scripture(.@msg$); + end; + +// Level up events +OnPCBaseLvUpEvent: + switch (BaseLevel) { + case 3: + case 5: + case 7: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + // Recalculate the bonus + NewcomerEXPDROPUP(); + break; + case 20: + if (!REBIRTH) { + sc_end SC_CASH_PLUSEXP; + sc_end SC_CASH_RECEIVEITEM; + } + break; + // Events + case 40: + if (countitem(MercCard_EH)) + delitem MercCard_EH, countitem(MercCard_EH); + break; + // Referral and Eisten + case 25: + if (#REFERRAL_PROG && BaseLevel == 25 && #REFERRAL_CTRL < 1) { + #REFERRAL_CTRL=1; + rodex_sendmail(atoi(gf_charid(#REFERRAL_PROG)), "TMW2 Team", "Recruited Player got Lv 25!", strcharinfo(0)+" just got level 25!\nAs they get stronger, more rewards will be sent to you!", 0, SilverGift, 1); + } + case 50: + if (#REFERRAL_PROG && BaseLevel == 50 && #REFERRAL_CTRL < 2) { + #REFERRAL_CTRL=2; + rodex_sendmail(atoi(gf_charid(#REFERRAL_PROG)), "TMW2 Team", "Recruited Player got Lv 50!", strcharinfo(0)+" just got level 50!\nAs they get stronger, more rewards will be sent to you!", 0, ArcmageBoxset, 1); + } + case 75: + if (#REFERRAL_PROG && BaseLevel == 75 && #REFERRAL_CTRL < 3) { + #REFERRAL_CTRL=3; + rodex_sendmail(atoi(gf_charid(#REFERRAL_PROG)), "TMW2 Team", "Recruited Player got Lv 75!", strcharinfo(0)+" just got level 75!\nAs they get stronger, more rewards will be sent to you!", 0, PrismGift, 1); + } + case 100: + if (#REFERRAL_PROG && BaseLevel == 100 && #REFERRAL_CTRL < 4) { + #REFERRAL_CTRL=4; + rodex_sendmail(atoi(gf_charid(#REFERRAL_PROG)), "TMW2 Team", "Recruited Player got Lv 100!", strcharinfo(0)+" just got level 100!\nTime to reap what you've sow for so long!", 0, SupremeGift, 1); + } + case 125: + case 150: + dispbottom l("Milestone levelup: A reward can now be claimed in Tulimshar."); + break; + } + end; + +// Every time the server starts, clean up possibly broken data about treasures. +// NPC ID might have changed. +OnInterIfInitOnce: + atcommand("@channel setopt #world MessageDelay 1"); + $@HAS_API=apiasync("PING", ""); + query_sql("DELETE FROM `char_reg_num_db` WHERE `key`='RNGTREASURE_DATE'"); + query_sql("DELETE FROM `char_reg_num_db` WHERE `key`='PVP_COOLDOWN'"); + query_sql("DELETE FROM `char_reg_num_db` WHERE `key`='CHAREG_CLEANUP'"); + end; + +// This control all PC Login events +// Position matters! +OnPCLoginEvent: + // Vault override + if (#MerchantBank) { + if (#MerchantBank < 0) + consolebug("Negative bank data for %s!", strcharinfo(0)); + BankVault += #MerchantBank; + #MerchantBank = 0; + } + + // Cannot login with outdated client + checkclientversion(); + + // Message of the Day have priority + MOTDHandler(); + + // Scheduled Announces + StoneBoardRead(); + + // TODO: npc/commands/rate-management.txt + + // Main update handler + clientupdater(); + + // Position and gameplay fixes + HUB_Login(); + + // Alcohol system reset + ALCReset(); + + // Newbie bonuses recalc + if (BaseLevel < 20 || REBIRTH) NewcomerEXPDROPUP(); + + // Daily rewards (The first with dialog interactions) + daily_login_bonus_handler(); + + // Guild Weekly Login Bonus + guild_login_bonus(); + + // Thanksgiving Event + sThanksgiving(); + end; +} + diff --git a/npc/003-4-1/_import.txt b/npc/003-4-1/_import.txt new file mode 100644 index 0000000..de17f1c --- /dev/null +++ b/npc/003-4-1/_import.txt @@ -0,0 +1,4 @@ +// Map 003-4-1: Forgotten Shop +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-4-1/_mobs.txt", +"npc/003-4-1/_warps.txt", diff --git a/npc/003-4-1/_mobs.txt b/npc/003-4-1/_mobs.txt new file mode 100644 index 0000000..4066fd0 --- /dev/null +++ b/npc/003-4-1/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-4-1: Forgotten Shop mobs +003-4-1,35,36,6,5 monster House Maggot 1084,2,35000,300000 diff --git a/npc/003-4-1/_warps.txt b/npc/003-4-1/_warps.txt new file mode 100644 index 0000000..52e274e --- /dev/null +++ b/npc/003-4-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-4-1: Forgotten Shop warps +003-4-1,35,26,0 warp #003-4-1_35_26 1,0,003-4,38,32 diff --git a/npc/003-4/_import.txt b/npc/003-4/_import.txt new file mode 100644 index 0000000..b7ae42d --- /dev/null +++ b/npc/003-4/_import.txt @@ -0,0 +1,5 @@ +// Map 003-4: Forgotten Shop +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-4/_warps.txt", +"npc/003-4/sorfina.txt", +"npc/003-4/warp.txt", diff --git a/npc/003-4/_warps.txt b/npc/003-4/_warps.txt new file mode 100644 index 0000000..2334fc9 --- /dev/null +++ b/npc/003-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-4: Forgotten Shop warps +003-4,39,39,0 warp #003-4_39_39 0,0,003-1,54,138 diff --git a/npc/003-4/sorfina.txt b/npc/003-4/sorfina.txt new file mode 100644 index 0000000..be0dbb4 --- /dev/null +++ b/npc/003-4/sorfina.txt @@ -0,0 +1,144 @@ +// TMW2 Script. +// Author: +// Saulc +// Jesusalva +// Description: +// Sorfina is Hasan's mother. +// +// GETQ1 Variable Value; +// 0 - Never stolen by Hasan +// 1 - Already stolen by Hasan +// 2 - Dausen pointed player to Sorfina +// 3 - Sorfina unlocked Mahoud's Basement +// 4 - Player killed Murderer Scorpion +// 5 - Quest is complete + +003-4,39,33,0 script Sorfina NPC_FEMALE_ARGAES,{ + if (getq(TulimsharQuest_Hasan) == 2) goto L_Trouble; + if (getq(TulimsharQuest_Hasan) == 3) goto L_Basement; + if (getq(TulimsharQuest_Hasan) == 4) goto L_Finished; + // Status 5 returns to default message + if (getq(TulimsharQuest_Hasan) == 5) goto L_Reward2; + mesn; + mesq l("Hello, darling!"); + close; + +L_Trouble: + mesn strcharinfo(0); + mesq l("Your son, Hasan, has stolen me!"); + next; + mesn; + mesq l("Oh, he has been a little rascal since his father died."); + mesc l("*cries*"); + next; + mesn; + mesq l("You see, after a monster attack... My husband went to the desert, to check how things were..."); + mesc l("*cries*"); + next; + mesn; + mesq l("He never touched the Soul Menhir... He died... For real..."); + mesc l("*cries*"); + next; + mesn; + mesc l("Sorfina recomposes herself."); + mesq l("There is a rare kind of scorpion, the Murderer Scorpion. Very dangerous."); + setq TulimsharQuest_Hasan, 3, 0; + next; + goto L_Basement; + +L_Basement: + if (BaseLevel < 13) goto L_TooWeak; + .@q3=getq3(TulimsharQuest_Hasan); + if (.@q3 > santime()) goto L_Wait; + mesn; + mesq l("There's one on the basement of this house. If you kill it, Hasan will think on you as a hero and won't steal you anymore."); + next; + menu + l("I will kill one."), L_OpenDoors, + l("Maybe later."), L_Close; + +L_OpenDoors: + .@ID=getcharid(0); + .@MAP_NAME$="hasn@"+str(.@ID); // Max 4 chars for map name + .@INSTID = instance_create("003-4-1@a"+(.@ID), getcharid(3), IOT_CHAR); + .@instanceMapName$ = instance_attachmap("003-4-1", .@INSTID, 0, .@MAP_NAME$); + setq TulimsharQuest_Hasan, 3, .@INSTID, santime()+300; + + // Debug + if (.@instanceMapName$ == "") consolewarn "Error: Map 003-4-1 X failed"; + debugmes "Created map: "+ str(.@instanceMapName$); + + // You have 5 minutes to complete the quest. + instance_set_timeout(300, 300, .@INSTID); + instance_init(.@INSTID); + + .@x=34;//rand(29, 45); + .@y=33;//rand(29, 41); + monster(.@MAP_NAME$, .@x, .@y, l("Murderer Scorpion"), MurdererScorpion, 1, "Sorfina::OnKillMurderer"); + addtimer(60000, "Sorfina::OnWarn"); + + mesn; + mesq l("Just go to the basement, past this door. Good luck, @@!", strcharinfo(0)); + close; + +L_Wait: + mesn; + mesq l("Good luck killing it! And don't forget to touch the Soul Menhir before you go!"); + close; + +L_TooWeak: + mesn; + mesq l("You are too weak to kill one and impress him, so please, bear my son."); + close; + +L_Close: + close; + +L_Finished: + mesn; + mesq l("Please, show Hasan the claw. Just talk to him."); + close; + +L_Reward2: + mesn; + mesq l("Thanks for helping my son. Here, take 300 GP for your troubles."); + setq1 TulimsharQuest_Hasan, 6; + Zeny=Zeny+300; + close; + +OnKillMurderer: + setq TulimsharQuest_Hasan, 4; + dispbottom l("I will take a claw and show Hasan."); + end; + +OnWarn: + /* removed: not in map. You can leave the map. + if (!compare(getmap(), "hasn@")) + end; + */ + // Quest state is invalid + .@q=getq(TulimsharQuest_Hasan); + if (.@q != 3) + end; + + .@q3=getq3(TulimsharQuest_Hasan); + dispbottom l("Time left: @@ remaining", FuzzyTime(.@q3)); + if (.@q3-gettimetick(2) > 60) + addtimer(60000, "Sorfina::OnWarn"); + else if (.@q3-gettimetick(2) > 15) + addtimer(15000, "Sorfina::OnWarn"); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyesD); + setunitdata(.@npcId, UDT_HEADMIDDLE, ValentineDress); //TODO + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 16); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/003-4/warp.txt b/npc/003-4/warp.txt new file mode 100644 index 0000000..9a5d26a --- /dev/null +++ b/npc/003-4/warp.txt @@ -0,0 +1,14 @@ +// TMW2 Scripts. + +003-4,38,31,0 script Mahoud Basement NPC_HIDDEN,0,0,{ + +OnTouch: + // Instance 0 is reserved by system + if (instanceowner(getq2(TulimsharQuest_Hasan)) == getcharid(3)) + warp "hasn@"+str(getcharid(0)), 34, 27; + else if (getq(TulimsharQuest_Hasan) > 3) + warp "003-4-1", 34, 27; + else + dispbottom l("This door is locked."); + end; +} diff --git a/npc/003-5/_import.txt b/npc/003-5/_import.txt new file mode 100644 index 0000000..018a083 --- /dev/null +++ b/npc/003-5/_import.txt @@ -0,0 +1,4 @@ +// Map 003-5: Tulimshar Jeweler +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-5/_warps.txt", +"npc/003-5/jeweler.txt", diff --git a/npc/003-5/_warps.txt b/npc/003-5/_warps.txt new file mode 100644 index 0000000..5e43457 --- /dev/null +++ b/npc/003-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-5: Tulimshar Jeweler warps +003-5,38,41,0 warp #003-5_38_41 0,0,003-1,96,145 diff --git a/npc/003-5/jeweler.txt b/npc/003-5/jeweler.txt new file mode 100644 index 0000000..a1b2b9d --- /dev/null +++ b/npc/003-5/jeweler.txt @@ -0,0 +1,218 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Sells and crafts jewels +// +// Notes: +// Gold/Silver/Copper+Tin Ore (dropped by bifs) → Pieces or Ingot, depends on what you want to forge +// Jeweler only works with Gold/Silver/Bronze PIECES. The Ingots are for forges. +// Jeweler can also polish raw gemstones for a comission ($$$) +// Polished gemstones can be inserted on rings. +// The less precious the ring, heavier and less defense value. +// +// All three rings can be swapped by a Light Ring (you will lose any gemstone on them!). +// Still pending discussion. + +003-5,33,37,0 script Jeweler NPC_FEMALE_TONORI,{ + mesn l("Inya, the Jeweler"); + mesq l("Hello!"); + goto L_Menu; + +L_Menu: + mes ""; + mesn strcharinfo(0); + menu + l("I want to shop."), -, + l("I want to craft rings."), L_Craft, + l("I want to create polished gemstones."), L_Gemstone, + l("I want to remove all stuff applied to my ring!"), L_Clear, + l("Nothing, sorry."), L_Close; + + npcshopattach(.name$); + openshop; + closedialog; + close; + +L_Craft: + mes ""; + mesn l("Inya, the Jeweler"); + mesq l("Of course, I actually have two options:"); + mes l("- 5 @@, with 1200 GP, for a @@.", getitemlink(GoldPieces), getitemlink(GoldenRing)); + mes l("- 5 @@, with 1200 GP, for a @@.", getitemlink(SilverIngot), getitemlink(SilverRing)); // TODO + next; + menu + rif(countitem(GoldPieces) >= 5 && Zeny >= 1200, l("Yes, deal. Gimme the gold ring.")), -, + rif(countitem(SilverIngot) >= 5 && Zeny >= 1200, l("Yes, deal. Gimme the silver ring.")), -, + l("...I'll be back later."), L_Close; + + switch (@menu) { + case 1: + inventoryplace GoldenRing, 1; + delitem GoldPieces, 5; + Zeny=Zeny-1200; + getitem GoldenRing, 1; + getexp 100, 25; + break; + case 2: + inventoryplace SilverRing, 1; + delitem SilverIngot, 5; // TODO + Zeny=Zeny-1200; + getitem SilverRing, 1; + getexp 50, 15; + break; + } + mes ""; + mesn l("Inya, the Jeweler"); + mesq l("Here you go! Anything else?"); + next; + goto L_Menu; + +L_Gemstone: + mes ""; + mesn l("Inya, the Jeweler"); + mesq l("Polished gemstones can be dragged and insert on rings. They'll give special stats to rings, but these gems cannot be removed easily."); + mesq l("I charge a service fee of @@ GP, and two gemstones.", 300); + next; + if (Zeny < 300) { + mesn l("Inya, the Jeweler"); + mesq lg("You clearly don't have money, so let's not bother with that right now."); + next; + goto L_Menu; + } + if (!countitem(GoldenRing) && !countitem(SilverRing)) { + mesn l("Inya, the Jeweler"); + mesq l("You don't have a ring but I can polish the gemstones nonetheless."); + next; + } + select + l("Not today, sorry."), + rif(countitem(Diamond) >= 2 , l("Diamond (+2 vit)")), + rif(countitem(Ruby) >= 2 , l("Ruby (+2 str)")), + rif(countitem(Emerald) >= 2 , l("Emerald (+2 luck)")), + rif(countitem(Sapphire) >= 2, l("Sapphire (+2 int)")), + rif(countitem(Topaz) >= 2 , l("Topaz (+2 agi)")), + rif(countitem(Amethyst) >= 2, l("Amethyst (+2 dex)")); + + mes ""; + if (@menu == 1) + goto L_Menu; + + // As usual, I don't care with cheaters, so if you somehow cheat money or gemstones, YOU WILL LOSE ALL REAGENTS. No refunds. + inventoryplace PolishedDiamond-2+@menu, 1; + delitem Diamond-2+@menu, 2; + Zeny-=300; + getitem PolishedDiamond-2+@menu, 1; + getexp 800, 0; + goto L_Menu; + + +/* TODO Special rings +L_Pearl: + inventoryplace GoldenPearlRing; + delitem GoldenRing, 1; + Zeny=Zeny-1000; + delitem Pearl, 3; + getitem GoldenPearlRing, 1; + getexp 1000, 0; + goto L_Menu; + +L_BlackPearl: + inventoryplace GoldenBlackPearlRing; + delitem GoldenRing, 1; + Zeny=Zeny-1000; + delitem BlackPearl, 1; + getitem GoldenBlackPearlRing, 1; + getexp 1000, 0; + goto L_Menu; +*/ + +L_Clear: + mesn l("Inya, the Jeweler"); + mesq l("Oh... Of course! I'll even do this for free!"); + next; + mesn l("Inya, the Jeweler"); + mesq l("Just be really, REALLY sure you want to do it. I mean, the gemstone will break."); + mesq l("It's not warranted you'll be able to get the powder back, either."); + next; + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + .@item_index = requestitemindex(); + if (.@item_index < 0) + goto L_Menu; + + // Check if we received an item + getinventorylist(); + .@item=@inventorylist_id[.@item_index]; + + if (is_staff()) + mesf "Item Index %d (%s) - ID %d", .@item_index, getitemlink(.@item), .@item; + + // Is it the one we're looking for? + if (.@item != GoldenRing && .@item != SilverRing) { + mesn; + mesq l("Well, that's not a ring."); + next; + goto L_Menu; + } + + // Retrieve the gemstone ID. Must be on slot 1 (aka. 0)! + .@gem=getcardbyindex(.@item_index, 0); + // Skip bound rings. It would be stupid '-' + if (!checkbound(.@item)) { + failedremovecardsindex .@item_index, 1; + // No inventoryplace check here + // DiamondPowder = 858. PolishedDiamond = 5031 + if (.@gem > 5000) + getitem .@gem-4173, 1; + mesn; + mesq l("Well... Here you go!"); + next; + } else { + mesn; + mesq l("I don't work with bound items."); + next; + } + + goto L_Menu; + +L_Close: + closedialog; + goodbye; + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyesT); + setunitdata(.@npcId, UDT_HEADMIDDLE, VneckJumper); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 3); + setunitdata(.@npcId, UDT_HAIRCOLOR, 8); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + + sleep(SHOPWAIT); + tradertype(NST_MARKET); + sellitem WeddingRing, 5000, 50; + sellitem GoldenBlackPearlRing, 500000, 1; // I'm joking of course. Don't tell me you'll pay half MILLION for... this? + end; + +OnClock2214: + restoreshopitem WeddingRing, 5000, 50; + restoreshopitem GoldenBlackPearlRing, 500000, 1; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} diff --git a/npc/003-6/_import.txt b/npc/003-6/_import.txt new file mode 100644 index 0000000..95f4096 --- /dev/null +++ b/npc/003-6/_import.txt @@ -0,0 +1,5 @@ +// Map 003-6: Laundry +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-6/_warps.txt", +"npc/003-6/cyndala.txt", +"npc/003-6/tamiloc.txt", diff --git a/npc/003-6/_warps.txt b/npc/003-6/_warps.txt new file mode 100644 index 0000000..d540cc5 --- /dev/null +++ b/npc/003-6/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-6: Laundry warps +003-6,40,39,0 warp #003-6_40_39 0,0,003-1,112,148 +003-6,32,39,0 warp #003-6_32_39 0,0,003-1,106,148 diff --git a/npc/003-6/cyndala.txt b/npc/003-6/cyndala.txt new file mode 100644 index 0000000..9dd039f --- /dev/null +++ b/npc/003-6/cyndala.txt @@ -0,0 +1,249 @@ +// TMW2 Script. +// Author: +// Saulc +// Jesusalva +// Dye Quest added by: +// Povo +// Description: +// Will be with dye functions for a while... May be wrong. Only Card2 is available per hercules rules + +// Original code from evol +// Authors: +// Reid + +003-6,33,30,0 script Cyndala NPC_FEMALE_ARGAES,{ + function explain_dyes; + function item_is_bleachable; + function remove_cards_from_item; + function give_yellow_dye; + function deny_yellow_dye; + function craft_yellow_dye; + + mesn; + mesq l("Hello, darling!"); + mes ""; + + do + { + .@q = getq(TulimsharQuest_Cyndala); + select + l("Excuse me."), + l("Could you bleach my equipment?"), + l("What can you say about dyes?"), + rif(.@q > 0, l("I would like to order some dye.")), + rif(is_staff(), "Technical problem, gimme info about an item."); + + switch (@menu) { + case 2: + remove_cards_from_item(); + break; + case 3: + explain_dyes(); + break; + case 4: + craft_yellow_dye(); + break; + case 5: + .@item = requestitemindex(); + mes "Item index selected: " + str(.@item); + mes "slots=" + str(MAX_SLOTS); + for (.@i = 0; .@i < MAX_SLOTS; .@i++) + { + mes "slot " + str(.@i) + " = " + str(getcardbyindex(.@item, .@i)); + } + mes str(@inventorylist_card1[.@item]); + mes str(YellowDye); + mes "item options:"; + for (.@i = 0; .@i < 5; .@i ++) + { + mes sprintf("%d: Option: %d, Value: %d", .@i, getitemoptionidbyindex(.@item, .@i), + getitemoptionvaluebyindex(.@item, .@i)); + } + mes "Note named items (Card1 254 and 255) have Card3 and Card4 reserved"; + break; + default: + mes ""; + mesn; + mesq l("I wish you a good time in town."); + next; + break; + } + + } while (@menu != 1); + + + closeclientdialog; + goodbye; + close; + +///////////////////////////////////////////////////////////////////////////////// +function explain_dyes { + .@q = getq(TulimsharQuest_Cyndala); + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Dyes are a special kind of ink to make certain objects fancier."), + l("Once you have the appropriate colorant for the item, ##bdrag the colorant##b to the material."), + l("Example:"), + l("Drag and drop a %s in a %s, and you will obtain a %s.", getitemlink(YellowDye), + getitemlink(ArtisTankTop), getitemlink(ArtisTankTop, YellowDye)), + l("Dye cards are not the only thing which exist, but they are the coolest!"); + if (.@q < 1) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("By the way, did you know that the official color of Tulimshar is yellow?"), + l("In fact, the Tulimshar Noble District is world-renowned for crafting high-quality yellow garments!"), + l("Normally I would offer to make you some %s, but economic problems in Halinarzo have caused a shortage of supplies...", + getitemlink(YellowDye)); + if (BaseLevel > 20 && + .@q < 1) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Wait a minute! I have an idea!"), + l("You look like you have enough experience to know your way around town..."), + l("If you can manage to gather the supplies I need, I can surely craft some %s for you!", + getitemlink(YellowDye)), + l("Of course, I do charge a nominal fee of for the service. %s GP to be exact.", fnum(50)), + l("In addition to my fee, I will also need the following items:"); + mesq l("%d/%d %s", countitem(BottleOfSewerWater), 1, getitemlink(BottleOfSewerWater)); + mesq l("%d/%d %s", countitem(PiouFeathers), 50, getitemlink(PiouFeathers)); + next; + mesq l("I am sure that an adventurer like you can gather everything in no time at all!"); + setq TulimsharQuest_Cyndala, 1; + } + } +} + +function item_is_bleachable +{ + .@item_index = getarg(0); + if (.@item_index < 0) + return false; + + // Collect the item ID + delinventorylist(); + getinventorylist(); + .@x=@inventorylist_id[.@id]; + + // Debug info + if (is_staff()) + mesf "Item Index %d (%s) - ID %d", .@item_index, + getitemlink(@inventorylist_id[.@item_index]), + @inventorylist_id[.@item_index]; + + // No duplicates + if (countitem(.@x) > 1) { + mesc l("You are carrying duplicates of the same item. Sorry, but I have no idea which one you want to tweak."), 1; + return false; + } + + // Must have a card, d'oh + if (@inventorylist_card1[.@item_index] == 0) + return false; + + return true; +} + +function remove_cards_from_item +{ + .@item_index = -1; + + speech S_FIRST_BLANK_LINE, + l("What item would you like to bleach?"); + + narrator S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("You can drag and drop an item to the NPC window or select an item through your inventory."); + + .@item_index = requestitemindex(); + if (!item_is_bleachable(.@item_index)) { + speech S_LAST_NEXT, + l("You should know this, an item like this can't be bleached."); + + return; + } + .@item_id = @inventorylist_id[.@item_index]; + + speech S_LAST_NEXT, + l("Your mind is set? You will probably lose all the dyes and/or cards during on the item during this process. You're bleaching a %s by the way.", getitemlink(.@item_id)); + + switch (askyesno()) { + case ASK_YES: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Ok, let me see..."), + l("..."); + + // FIXME: Register this on picklog + if (rand2(1, 800) > readparam2(bLuk)) { + failedremovecardsindex .@item_index, 1; + logmes("Deleted Cards from item: "+.@item_id); + } else { + successremovecardsindex(.@item_index); + logmes("Removed Cards from item: "+.@item_id); + } + + // First option slot of weapon: Raises STR in 5 + // setequipoption(EQI_HAND_R, 1, VAR_STRAMOUNT, 5); + // This is an option :3 + + speech S_LAST_NEXT | S_NO_NPC_NAME, + l("..."), + l("Here it is, clean like a whistle!"); + break; + default: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Is it truly a hard choice to make?"); + break; + } + return; +} + +function give_yellow_dye { + .@q = getq(TulimsharQuest_Cyndala); + + inventoryplace YellowDye, 1, EmptyBottle, 1; + Zeny = Zeny - 50; + delitem BottleOfSewerWater, 1; + delitem PiouFeathers, 50; + + mesn; + mesq l("Here you are, you won't find a dye more yellow than this!"); + getitem(YellowDye, 1); + getitem(EmptyBottle, 1); + + if (.@q == 1) { + setq TulimsharQuest_Cyndala, 2; + getexp 80, 0; + } + return; +} + +function deny_yellow_dye { + mesn; + mesc l("I am sorry, but it does not look like you have everything. You will need to bring:"); + next; + mesc l("%d/%d %s", countitem(BottleOfSewerWater), 1, getitemlink(BottleOfSewerWater)); + mesc l("%d/%d %s", countitem(PiouFeathers), 50, getitemlink(PiouFeathers)); + mesc l("%s/%s GP", fnum(Zeny), fnum(50)); + next; + return; +} + +function craft_yellow_dye { + if (countitem(BottleOfSewerWater) < 1 || + countitem(PiouFeathers) < 50 || + Zeny < 50) + deny_yellow_dye(); + else + give_yellow_dye(); + return; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyesD); + setunitdata(.@npcId, UDT_HEADMIDDLE, ValentineDress); //TODO + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 16); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/003-6/tamiloc.txt b/npc/003-6/tamiloc.txt new file mode 100644 index 0000000..6f88398 --- /dev/null +++ b/npc/003-6/tamiloc.txt @@ -0,0 +1,83 @@ +// TMW2 scripts. +// Authors: +// Saulc +// Jesusalva +// Reid +// Travolta +// Description: +// Tamiloc is the barber. + +003-6,46,30,0 script Tamiloc NPC_ELVEN_FEMALE_ARMOR_SHOP,{ + function setRace { + clear; + setnpcdialogtitle l("%s - Modify Race", .name$); + mes l("Race") + ": " + get_race(); + next; + mes l("Please select the desired race."); + select + l("Kaizei Human"), + l("Argaes Human"), + l("Tonori Human"), + l("Elf"), + l("Orc"), + l("Raijin"), + l("Tritan"), + l("Ukar"), + l("Redy"), + l("Savior"); + switch (@menu) + { + default: + jobchange max(0, @menu-1); + } + return; + } + + + mesn; + mesq l("Hi! Do you want a hair cut?"); + + do + { + select + l("What is my current hairstyle and hair color?"), + l("I'd like to get a different style."), + l("Can you do something with my color?"), + rif(is_gm() || REBIRTH >= 5, l("I want to change my Race!")), + l("I'm fine for now, thank you."); + + switch (@menu) + { + case 1: + BarberSayStyle 3; + break; + case 2: + BarberChangeStyle; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Enjoy your new style."); + l("Anything else?"); + break; + case 3: + BarberChangeColor; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("I hope you like this color."); + l("Anything else?"); + break; + case 4: + setRace; + break; + case 5: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Feel free to come visit me another time."); + + goodbye; + } + } while (1); + close; + + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/003-7/_import.txt b/npc/003-7/_import.txt new file mode 100644 index 0000000..84b72bb --- /dev/null +++ b/npc/003-7/_import.txt @@ -0,0 +1,4 @@ +// Map 003-7: Tulimshar Storage +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-7/_mobs.txt", +"npc/003-7/_warps.txt", diff --git a/npc/003-7/_mobs.txt b/npc/003-7/_mobs.txt new file mode 100644 index 0000000..50a64eb --- /dev/null +++ b/npc/003-7/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-7: Tulimshar Storage mobs +003-7,35,36,7,5 monster House Maggot 1084,4,40000,200000 diff --git a/npc/003-7/_warps.txt b/npc/003-7/_warps.txt new file mode 100644 index 0000000..55844fe --- /dev/null +++ b/npc/003-7/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-7: Tulimshar Storage warps +003-7,39,41,0 warp #003-7_39_41 0,0,003-1,111,138 +003-7,29,30,0 warp #003-7_29_30 0,0,003-1,107,131 diff --git a/npc/003-8/_import.txt b/npc/003-8/_import.txt new file mode 100644 index 0000000..e43b017 --- /dev/null +++ b/npc/003-8/_import.txt @@ -0,0 +1,6 @@ +// Map 003-8: Terranite Forge +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-8/_warps.txt", +"npc/003-8/craft.txt", +"npc/003-8/intensebeard.txt", +"npc/003-8/jhedia.txt", diff --git a/npc/003-8/_warps.txt b/npc/003-8/_warps.txt new file mode 100644 index 0000000..7df19cb --- /dev/null +++ b/npc/003-8/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-8: Terranite Forge warps +003-8,31,44,0 warp #003-8_31_44 0,0,003-1,110,101 +003-8,41,27,0 warp #003-8_41_27 0,0,003-1,115,89 diff --git a/npc/003-8/craft.txt b/npc/003-8/craft.txt new file mode 100644 index 0000000..f5bd7a4 --- /dev/null +++ b/npc/003-8/craft.txt @@ -0,0 +1,42 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Rentable Craft Bench + +003-8,35,30,0 script Crafting Table NPC_NO_SPRITE,{ + do { + mesc l("What will you craft today?"); + mesc l("It costs 600 GP to use."), 1; + if (Zeny < 600) + close; + + if (SmithSystem(CRAFT_PLAYER)) { + if (Zeny < 600) { + mesc l("WARNING, you have been detected cheating and thus, violating Tulimshar Anti-Theft Policy."), 1; + mesc l("You were jailed and now need a GM to get you out of there."), 1; + logmes "WARNING, "+strcharinfo(0)+" found out cheating, only had "+Zeny+"/600 GP for craft table. Jailed.", LOGMES_ATCOMMAND; + consoleinfo("%s jailed - no money when crafting.", strcharinfo(0)); + atcommand("@jail "+strcharinfo(0)); + // Apply a more realistic penalty + Zeny=0; + CRAFTSYS_CURRENT=0; + close; + } + Zeny-=600; + mesc l("Success!"), 3; + } else { + mesc l("That didn't work!"), 1; + } + next; + mesc l("Try again?"); + } while (askyesno() == ASK_YES); + + close; + +OnInit: + .distance=3; + end; +} + + diff --git a/npc/003-8/intensebeard.txt b/npc/003-8/intensebeard.txt new file mode 100644 index 0000000..8c3a08f --- /dev/null +++ b/npc/003-8/intensebeard.txt @@ -0,0 +1,273 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Intense Beard is the crafting master + +003-8,27,42,4 script Intense Beard NPC_HUMAN_M_ARTIS,{ + function IntenseBeardTutorial; + if (BaseLevel < 18) + goto L_SemNivel; + if (!CRAFTQUEST) + goto L_SemQuest; + if (!getskilllv(TMW2_CRAFT)) + goto L_Learn; + if (!countitem(RecipeBook) && !countitem(JesusalvaGrimorium)) + goto L_SemLivro; + + mesn; + mesq l("Hello my friend! I see you've brought your @@ with you this time!", getitemlink(RecipeBook)); + next; + mesn; + mesq l("Let's learn new recipes! Do you wanna?"); + mesc l("Bonus recipes must be enabled with @@ after learned!", b("@ucp")); + next; + .@bool=false; + do { + csysGUI_Report(.@bool); + mesc l("Learn what?"); + mes ""; + .@opt$="Nothing"; + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_BASE); + + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_ATK); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_DEF); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_ACC); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_EVD); + + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_REGEN); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_SPEED); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_DOUBLE); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_MAXPC); + + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_SCRESIST); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_SCINFLICT); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_MANAUSE); + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_BOSSATK); + + .@opt$+=":"+csysGUI_OptLearnMenu(CRGROUP_FINAL); + + .@opt$+=":I would like a tutorial"; + + select (.@opt$); + mes ""; + switch (@menu) { + case 1: + break; + + case 2: + if (csysGUI_RaiseOpt(CRGROUP_BASE)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + + case 3: + if (csysGUI_RaiseOpt(CRGROUP_ATK)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 4: + if (csysGUI_RaiseOpt(CRGROUP_DEF)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 5: + if (csysGUI_RaiseOpt(CRGROUP_ACC)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 6: + if (csysGUI_RaiseOpt(CRGROUP_EVD)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + + case 7: + if (csysGUI_RaiseOpt(CRGROUP_REGEN)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 8: + if (csysGUI_RaiseOpt(CRGROUP_SPEED)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 9: + if (csysGUI_RaiseOpt(CRGROUP_DOUBLE)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 10: + if (csysGUI_RaiseOpt(CRGROUP_MAXPC)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + + case 11: + if (csysGUI_RaiseOpt(CRGROUP_SCRESIST)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 12: + if (csysGUI_RaiseOpt(CRGROUP_SCINFLICT)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 13: + if (csysGUI_RaiseOpt(CRGROUP_MANAUSE)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + case 14: + if (csysGUI_RaiseOpt(CRGROUP_BOSSATK)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + + case 15: + if (csysGUI_RaiseOpt(CRGROUP_FINAL)) + mesc l("Success!"), 3; + else + mesc l("Not enough Monster Points!"), 1; + break; + default: + IntenseBeardTutorial(); + break; + } + + // Update boolean + if (!.@bool) + .@bool=true; + } while (@menu != 1); + close; + +L_SemNivel: + mesn; + mesq l("Please don't disturb me, I'm busy crafting my own customized power mega blaster weapon. I just keep failing to apply +100% exp gain bonus on it!"); + close; + +L_SemLivro: + mesn; + mesq l("Heya, noob. Where is your @@? Are you really so noob to have forgot such important book?!", getitemlink(RecipeBook)); + next; + mesn; + mesq l("That's too bad, because I could teach you some amazing craft skills! This will need to wait another day, I guess. Oh well."); + close; + +L_SemQuest: + mesn; + mesq l("Heya, noob. Where is your @@? Are you really so noob to have forgot such important book?!", getitemlink(RecipeBook)); + next; + mesn strcharinfo(0); + mesc l("Was I supposed to have one? How should I reply?"); + mes ""; + select + l("Erm... I don't have a recipe book."), + l("That's nice, but... I don't have a recipe book."), + l("Pardon me, what is a recipe book?"); + mes ""; + mesn; + mesq l("WHAT? Have you not gained one at birth? That's absurd!"); + next; + select + l("Erm... I don't know my parents."), + l("Maybe, but... I don't know my parents."), + l("Pardon me, I don't remember my parents."); + mes ""; + mesn; + mesq l("WHAT? That's twice absurd! Now you'll say that you were found stranded on a desert island without equipment and can't remember anything but your own name!"); + next; + select + l("Erm... You're right."), + l("Well... You're right."), + l("How did you guess that?"); + mes ""; + mesn; + mesq l("... ... ..."); + next; + mesn; + mesq l("...Okay, this is not fun anymore."); + next; + mesn; + mesq l("Here, you can have this spare one. Hahah! Crafting is an art, and I help teaching it. Just be sure to don't lose it, you will NEVER get another copy EVER again, was I clear?!"); + next; + mesn; + mesq l("While we're at that, do you know how to craft? No? First, you'll need a Cauldron, in case you've rented a house. Otherwise, you can rent a work table on this Forge."); + next; + mesn; + mesq l("You need to read this recipe. Without the essential crafting skill you won't go very far. There will be no bonuses on the items you craft because you are not a craftsman."); + // Obtain a blueprint and a recipe book + inventoryplace EquipmentBlueprintA, 1, RecipeBook, 1; + getitem EquipmentBlueprintA, 1; + getitem RecipeBook, 1; + CRAFTQUEST=1; + next; + +L_Learn: + mesn; + mesq l("I see you don't know how to craft. Do you want me to teach the basic for you? I'll charge 1000 GP."); + if (Zeny < 1000) + close; + next; + if (askyesno() == ASK_YES) { + // Pay the money + Zeny-=1000; + + // Give you the craft skill itself + skill TMW2_CRAFT, 1, 0; + + // We should aid you getting basic skills - for free! + if (!CRAFTSYS[CRGROUP_BASE]) + CRAFTSYS[CRGROUP_BASE]+=1; + if (!CRAFTSYS_CURRENT) + CRAFTSYS_CURRENT=CRAFTSYS_CURRENT|CRGROUP_BASE; + + mesn; + mesq l("There, now you can not only craft items, but craft AWESOME ITEMS!"); + + RECIPES_EQUIPMENT[CraftDagger]=true; + mesc l("You've also learned how to craft Daggers! It serves solely for training purposes. You get crafting points for every 40 levels crafted on the item."); + } + close; + +function IntenseBeardTutorial { + mesn; + mesq l("Well, first of, you'll need an @@ and an Equipment Recipe.", getitemlink(RecipeBook)); + next; + mesn; + mesq l("You can craft items in forges. You can use it on your house, or within this forge for a tax."); + next; + mesn; + mesq l("That will help you to make your very first first craft! Remember to use @@ to change which bonuses can be applied to your craft items.", b("@ucp")); + next; + mesn; + mesq l("I can teach you these bonuses, but more powerful bonuses require more skill to learn... And I don't know anyone who could improve your crafting skill..."); + next; + mesn; + mesq l("...The ones who know a lot are dwarves, but they don't live here - they live with elves, it seems."); + next; + return; +} + +OnInit: + .sex=G_MALE; + .distance=5; + npcsit; + end; + +} + + diff --git a/npc/003-8/jhedia.txt b/npc/003-8/jhedia.txt new file mode 100644 index 0000000..a9d09fe --- /dev/null +++ b/npc/003-8/jhedia.txt @@ -0,0 +1,197 @@ +// TMW2 scripts. +// Author: +// Crazyfefe +// Saulc +// Jesusalva +// Description: +// Jhedia takes care of Tulimshar forge and makes ingots +//.@karim = getq(Karim_Quest); + +003-8,28,30,0 script Jhedia NPC_ELVEN_FEMALE,{ + goto L_Menu; + + // ingot_create( BaseItem, PrizeItem, Amount, Amount_Coal, Price ) + function ingot_create { + .@basei=getarg(0); + .@prize=getarg(1); + .@oream=getarg(2); + .@coalm=getarg(3); + .@price=getarg(4); + + // Adjust price + .@price=POL_AdjustPrice(.@price); + + mesn; + mesq l("Do you want to craft @@? For that I will need @@ @@, @@ @@ and @@ gp.", + getitemlink(.@prize), .@oream, getitemlink(.@basei), .@coalm, getitemlink(Coal), .@price); + + askyesno(); + mes ""; + + if (@menu == ASK_NO) goto L_Menu; + + mesn; + mesq l("How many ingots do you want to make?"); + input .@amount; + + if (.@amount < 1) + close; + + if (countitem(.@basei) >= .@amount * .@oream && countitem(Coal) >= .@amount * .@coalm && Zeny >= .@price * .@amount) { + inventoryplace .@prize, .@amount; + delitem .@basei, .@amount * .@oream; + delitem Coal, .@amount * .@coalm; + POL_PlayerMoney(.@amount * .@price); + getitem .@prize, .@amount; + getexp (JobLevel+.@oream+.@coalm)*.@amount, .@amount; + + mes ""; + mesn; + mesq l("Many thanks! Come back soon."); + close; + } + + speech S_FIRST_BLANK_LINE,// | S_LAST_NEXT, + l("You don't have enough material, sorry."); + close; + + } + +L_Menu: + mesn; + mesq l("Hello! I am @@, and I take care of Tulimshar forge. I usually make ingots from various materials. So, what do you want today?", .name$); + next; + select + l("Iron Ingot"), + l("Copper Ingot"), + l("Tin Ingot"), + l("Lead Ingot"), + l("Titanium Ingot"), + rif(BaseLevel >= 60, l("Iridium Ingot")), + rif(BaseLevel >= 60, l("Platinum Ingot")), + l("Silver Ingot"), + l("Gold Ingot"), + l("Gold Pieces"), + rif(BaseLevel >= 70, l("Terranite Ingot")), + l("Nothing, thanks."); + + // ingot_create(Ore, Ingot, Nº of Ore, Nº of Coal, Price); + switch (@menu) { + case 1: + ingot_create(IronOre, IronIngot, 6, 9, 740); + break; + case 2: + ingot_create(CopperOre, CopperIngot, 4, 7, 750); + break; + case 3: + ingot_create(TinOre, TinIngot, 4, 9, 750); + break; + case 4: + ingot_create(LeadOre, LeadIngot, 4, 1, 760); + break; + case 5: + ingot_create(TitaniumOre, TitaniumIngot, 4, 9, 800); + break; + case 6: + ingot_create(IridiumOre, IridiumIngot, 4, 10, 1100); + break; + case 7: + ingot_create(PlatinumOre, PlatinumIngot, 3, 10, 1650); + break; + case 8: + ingot_create(SilverOre, SilverIngot, 3, 7, 650); + break; + case 9: + ingot_create(GoldOre, GoldIngot, 3, 9, 850); + break; + case 10: + ingot_create(GoldOre, GoldPieces, 1, 20, 1100); + break; + case 11: + ingot_create(TerraniteOre, TerraniteIngot, 10, 20, 2000); + break; + default: + goto L_Close; + break; + } + +L_Close: + tutmes l("You can also obtain ingots by melting down equipment. The Meltdown forge is in Nivalis, very far from Tulimshar."), l("Protip"), false; + if (BaseLevel < 20) + tutmes l("You cannot visit Nivalis before obtaining level 20."), l("Note"), false; + closedialog; + goodbye; + close; + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 28, 30,//C + "dir", DOWN, 0, + "wait", 70, 0, + "move", 35, 28,//D + "dir", DOWN, 0, + "wait", 10, 0, + "move", 30, 29,//B + "dir", UP, 0, + "wait", 4, 0, + "move", 36, 36,//H + "dir", LEFT, 0, + "wait", 2, 0, + "move", 27, 29,//A + "dir", UP, 0, + "wait", 10, 0, + "move", 30, 34,//K + "dir", UP, 0, + "wait", 13, 0, + "move", 27, 29,//A + "dir", UP, 0, + "wait", 14, 0, + "move", 39, 31,//G + "dir", UP, 0, + "wait", 25, 0, + "move", 30, 29,//B + "dir", UP, 0, + "wait", 11, 0, + "move", 26, 42,//M + "dir", UP, 0, + "wait", 8, 0, + "move", 30, 29,//B + "dir", UP, 0, + "wait", 7, 0, + "move", 39, 36,//I + "dir", RIGHT, 0, + "wait", 2, 0, + "move", 35, 31,//F + "dir", UP, 0, + "wait", 1, 0, + "move", 27, 29,//A + "dir", UP, 0, + "wait", 15, 0, + "move", 29, 38,//L + "dir", DOWN, 0, + "wait", 21, 0, + "move", 30, 29,//B + "dir", UP, 0, + "wait", 10, 0, + "move", 39, 28,//E + "dir", DOWN, 0, + "wait", 13, 0, + "move", 28, 34,//J + "dir", UP, 0, + "wait", 10, 0, + "move", 30, 29,//B + "dir", UP, 0, + "wait", 1, 0, + "move", 29, 42,//N + "dir", UP, 0, + "wait", 16, 0, + "move", 27, 29,//A + "dir", UP, 0, + "wait", 6, 0; + + initialmove; + initnpctimer; + .distance = 5; +} diff --git a/npc/003-9-1/_import.txt b/npc/003-9-1/_import.txt new file mode 100644 index 0000000..073f5c7 --- /dev/null +++ b/npc/003-9-1/_import.txt @@ -0,0 +1,3 @@ +// Map 003-9-1: Tulimshar Theater +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-9-1/_warps.txt", diff --git a/npc/003-9-1/_warps.txt b/npc/003-9-1/_warps.txt new file mode 100644 index 0000000..5d7acc8 --- /dev/null +++ b/npc/003-9-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-9-1: Tulimshar Theater warps +003-9-1,26,38,0 warp #003-9-1_26_38 0,0,003-1,72,133 diff --git a/npc/003-9/_import.txt b/npc/003-9/_import.txt new file mode 100644 index 0000000..49e21f7 --- /dev/null +++ b/npc/003-9/_import.txt @@ -0,0 +1,6 @@ +// Map 003-9: Tulimshar Inn +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/003-9/_warps.txt", +"npc/003-9/gambler.txt", +"npc/003-9/recepcionist.txt", +"npc/003-9/scripted.txt", diff --git a/npc/003-9/_warps.txt b/npc/003-9/_warps.txt new file mode 100644 index 0000000..dd9e5a9 --- /dev/null +++ b/npc/003-9/_warps.txt @@ -0,0 +1,14 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 003-9: Tulimshar Inn warps +003-9,62,136,0 warp #003-9_62_136 0,0,003-1,70,138 +003-9,48,92,0 script #003-9_48_92 NPC_HIDDEN,1,0,{ + end; +OnTouch: + slide 47,56; end; +} +003-9,48,57,0 script #003-9_48_57 NPC_HIDDEN,1,0,{ + end; +OnTouch: + slide 48,93; end; +} +003-9,36,57,0 warp #003-9_36_57 0,0,003-1,64,131 diff --git a/npc/003-9/gambler.txt b/npc/003-9/gambler.txt new file mode 100644 index 0000000..eacbc77 --- /dev/null +++ b/npc/003-9/gambler.txt @@ -0,0 +1,54 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Tulimshar Inn Receptionist + +003-9,42,123,0 script Gambler Master NPC_LOF_NOBLEMAN,{ + mesn; + mesq l("Hello! Welcome to the Inn! I am an Alliance officer, and I oversee gambling all over the world!"); + next; + mesn; + mesq l("So how can I help you?"); + do + { + next; + select + l("How does Gambling works?"), + l("How can I obtain casino coins?"), + l("Nothing, thanks."); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("You find a slot machine or another gambler, and make a bet!"); + next; + mesn; + mesq l("If you're right, you'll gain more Casino Coins! And even other nice things!"); + next; + mesn; + mesq l("But if you're wrong, you'll have to pay up! There's no refunds. This is gambling, if you lose you lose!"); + next; + mesn; + mesq l("If you're afraid of losing everything, then DEFINITELY do not gamble. %%2"); + break; + case 2: + mesn; + mesq l("I dunno. I don't think they're sold anywhere."); + next; + mesn; + mesq l("I know a few bandits stole a few, and Lava Slimes love to eat Casino Coins."); + next; + mesn; + mesq l("I've even heard of people whom dug Casino Coins out of the ground, but as I said, I don't know. But you'll certainly come across a few o your adventures!"); + break; + } + } while (@menu != 3); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/003-9/recepcionist.txt b/npc/003-9/recepcionist.txt new file mode 100644 index 0000000..cb6a81c --- /dev/null +++ b/npc/003-9/recepcionist.txt @@ -0,0 +1,64 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Tulimshar Inn Recepcionist + +003-9,62,128,0 script Recepcionist#TInn NPC_LOF_BARKEEPER,{ + // You're on Main Quest - total priority + if (LUA_ASKED_TO_SLEEP) + goto L_Cutscene; + + // Otherwise, you cannot sleep here before finishing Lua's Quest + if (getq(General_Narrator) < 3) { + npctalk3 l("Hello, and welcome to Tulimshar Inn."); + emotion E_HAPPY; + end; + } + + .@price=limit(400, 700+(BaseLevel*3)-getq(General_Narrator)*6, 1200); + .@price=limit(250, POL_AdjustPrice(.@price), 1200); + + mesn; + mesq l("Good %s. Would you like to sleep here? It's only %d GP!", (is_night() ? l("evening") : l("day")), .@price); + mesc l("Sleeping will fully replenish your health."); + mesc l("It'll also boost your %s for %s.", b(l("MAX HP")), l("15 minutes.")); + mesc l("Sleeping at night, the bonus will be stronger!"); + next; + if (askyesno() == ASK_YES && Zeny > .@price) { + POL_PlayerMoney(.@price); + percentheal 100, 100; + .@buff=(is_night() ? 15 : 10); + SC_Bonus(900, SC_INCMHPRATE, .@buff, .@buff); // FIXME: We don't want stacking + closeclientdialog; + warp "003-9", 36, 47; + dispbottom l("You feel refreshed!"); + } + close; + +L_Cutscene: + mesn strcharinfo(0); + mesq l("Hello, Lua told me she booked a room for me."); + next; + mesn; + mesq l("Ah, so you must be %s, the new guy! Yes, it is on the rooms upstairs. I'll bring you there, don't you worry a thing!", strcharinfo(0)); + tutmes l("During cutscenes, you cannot move. If you do, you'll be forcibly pushed back."), l("Cutscenes"); + next; + closeclientdialog; + .@mapn$="tinn@"+getcharid(0); + .@inst = instance_create("Tulim Inn "+getcharid(0), getcharid(3), IOT_CHAR); + instance_attachmap("003-9", .@inst, false, .@mapn$); + // Instance lasts 6 minutes + instance_set_timeout(360, 360, .@inst); + instance_init(.@inst); + warp .@mapn$, 36, 47; + setpcblock(PCBLOCK_HARD, true); // NOTE: No commands?! I'm not sure... + addtimer 1000, "#TulimInnStory::OnEvent1"; + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/003-9/scripted.txt b/npc/003-9/scripted.txt new file mode 100644 index 0000000..f8ad67b --- /dev/null +++ b/npc/003-9/scripted.txt @@ -0,0 +1,167 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Scripted functions (Lua) + +003-9,58,121,0 script #TInnClockPassage NPC_NO_SPRITE,{ + dispbottom l("If I only could find another way in..."); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +003-9,0,0,0 script #TulimInnStory NPC_HIDDEN,{ + end; + +OnEvent1: + .@mapn$="tinn@"+getcharid(0); + @tinn_assa=monster(.@mapn$, 47, 56, "???", Assassin, 1); + @tinn_boss=monster(.@mapn$, 48, 56, "???", HoodedAssassin, 1); + unitwalk(@tinn_boss, 40, 47); + addtimer 400, .name$+"::OnEvent2"; + end; + +OnEvent2: + .@mapn$="tinn@"+getcharid(0); + setunitdata(@tinn_boss, UDT_SPEED, 190); // So they walk at same pace + setunitdata(@tinn_assa, UDT_SPEED, 190); // So they walk at same pace + unitwalk(@tinn_assa, 40, 48); + addtimer 1860, .name$+"::OnEvent3"; // Roughly 9/11 tiles time + end; + +OnEvent3: + .@mapn$="tinn@"+getcharid(0); + //unitwarp(@tinn_boss, .@mapn$, 40, 47); + sc_start(SC_STUN, 17240, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, @tinn_boss); + unittalk(@tinn_boss, l("*hurry up, your moron!*")); + addtimer 440, .name$+"::OnEvent4"; // Roughly 2/11 tiles time + end; + +OnEvent4: + .@mapn$="tinn@"+getcharid(0); + //unitwarp(@tinn_assa, .@mapn$, 40, 48); + sc_start(SC_STUN, 22000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, @tinn_assa); + unittalk(@tinn_assa, l("*sorry, boss!*")); + //atcommand("@refresh"); // Can't use @commands at block 255 + addtimer 2000, .name$+"::OnEvent5"; + end; + +OnEvent5: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_boss, l("*is everything ready?*")); + addtimer 2000, .name$+"::OnEvent6"; + end; + +OnEvent6: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_assa, l("*Yes, boss!*")); + addtimer 1500, .name$+"::OnEvent7"; + end; + +OnEvent7: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_boss, l("*Don't shout, you moron!*")); + addtimer 2000, .name$+"::OnEvent8"; + end; + +OnEvent8: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_assa, l("*Yes, boss!*")); + addtimer 2000, .name$+"::OnEvent9"; + end; + +OnEvent9: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_boss, l("*I'll give you two energy balls.*")); + addtimer 1000, .name$+"::OnEvent10"; + end; + +OnEvent10: + .@mapn$="tinn@"+getcharid(0); + @tinn_ball=monster(.@mapn$, 41, 47, l("Energy Ball"), EnergyBall, 1); + @tinn_ba11=monster(.@mapn$, 41, 48, l("Energy Ball"), EnergyBall, 1); + sc_start(SC_STUN, 11500, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, @tinn_ball); + sc_start(SC_STUN, 11500, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, @tinn_ba11); + setunitdata(@tinn_ball, UDT_SPEED, 200); // So they walk at same pace + setunitdata(@tinn_ba11, UDT_SPEED, 200); // So they walk at same pace + addtimer 2000, .name$+"::OnEvent11"; + end; + +OnEvent11: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_boss, l("*Don't fail me. If you do, ensure you're well past dead before.*")); + addtimer 2800, .name$+"::OnEvent12"; + end; + +OnEvent12: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_assa, l("*Yes, boss!*")); + addtimer 1500, .name$+"::OnEvent13"; + end; + +OnEvent13: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_boss, l("*I'll take my leave, then.*")); + unitwalk(@tinn_boss, 47, 56); + addtimer 2200, .name$+"::OnEvent14"; + end; + +OnEvent14: + .@mapn$="tinn@"+getcharid(0); + unitwarp(@tinn_boss, "boss", 22, 22); + unitkill(@tinn_boss); + addtimer 200, .name$+"::OnEvent15"; + end; + +OnEvent15: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_assa, l("*hehehe...*")); + addtimer 800, .name$+"::OnEvent16"; + end; + +OnEvent16: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_assa, l("*The Professor will never know what got him...*")); + addtimer 2000, .name$+"::OnEvent17"; + end; + +OnEvent17: + .@mapn$="tinn@"+getcharid(0); + unittalk(@tinn_assa, l("*hehehe...*")); + unitwalk(@tinn_assa, 47, 56); + addtimer 200, .name$+"::OnEvent18"; + end; + +OnEvent18: + unitwalk(@tinn_ball, 47, 56); + unitwalk(@tinn_ba11, 48, 56); + addtimer 2000, .name$+"::OnEvent19"; + end; + +OnEvent19: + .@mapn$="tinn@"+getcharid(0); + unitwarp(@tinn_assa, "boss", 22, 22); + unitwarp(@tinn_ball, "boss", 22, 22); + unitwarp(@tinn_ba11, "boss", 22, 22); + unitkill(@tinn_assa); + unitkill(@tinn_ball); + unitkill(@tinn_ba11); + addtimer 200, .name$+"::OnEvent20"; + end; + +OnEvent20: + dispbottom l("*are they gone...?*"); + addtimer 1000, .name$+"::OnEvent21"; + end; + +OnEvent21: + setpcblock(PCBLOCK_HARD, false); + dispbottom l("I better report this to Lua!"); + LUA_ASKED_TO_SLEEP=false; + end; +} + diff --git a/npc/004-1/_import.txt b/npc/004-1/_import.txt new file mode 100644 index 0000000..f53a354 --- /dev/null +++ b/npc/004-1/_import.txt @@ -0,0 +1,7 @@ +// Map 004-1: Tulimshar +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-1/_mobs.txt", +"npc/004-1/_warps.txt", +"npc/004-1/anwar.txt", +"npc/004-1/elanore.txt", +"npc/004-1/sign.txt", diff --git a/npc/004-1/_mobs.txt b/npc/004-1/_mobs.txt new file mode 100644 index 0000000..261dc2b --- /dev/null +++ b/npc/004-1/_mobs.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-1: Tulimshar mobs +004-1,0,0,0,0 monster Maggot 1030,50,35000,420000 +004-1,48,35,5,13 monster Croc 1006,3,25000,60000 +004-1,49,77,10,21 monster Scorpion 1071,20,35000,270000 +004-1,109,66,15,8 monster Giant Maggot 1031,4,35000,270000 +004-1,112,113,12,5 monster Golden Scorpion 1078,1,99000,300000 +004-1,84,98,18,15 monster Fire Goblin 1067,6,45000,45000 +004-1,45,106,19,12 monster Red Scorpion 1072,3,95000,60000 diff --git a/npc/004-1/_warps.txt b/npc/004-1/_warps.txt new file mode 100644 index 0000000..4acf882 --- /dev/null +++ b/npc/004-1/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-1: Tulimshar warps +004-1,126,95,0 warp #004-1_126_95 0,2,004-2,31,51 +004-1,55,20,0 warp #004-1_55_20 0,0,003-1-1,29,61 +004-1,69,56,0 warp #004-1_69_56 4,0,003-1,60,156 +004-1,39,109,0 warp #004-1_39_109 0,0,007-1,56,36 +004-1,98,114,0 warp #004-1_98_114 0,0,007-1,90,51 +004-1,119,107,0 warp #004-1_119_107 0,0,007-1-1,35,37 diff --git a/npc/004-1/anwar.txt b/npc/004-1/anwar.txt new file mode 100644 index 0000000..ca5371e --- /dev/null +++ b/npc/004-1/anwar.txt @@ -0,0 +1,222 @@ +// TMW2 Script +// Author: +// TMW Org. +// Jesusalva +// Description: +// Part of Anwar Field quest + +004-1,113,67,0 script Anwar NPC_RAIJIN,{ + .@q=getq(TulimsharQuest_AnwarField); + if (BaseLevel < 18) goto L_Weak; + + if (.@q > 10) goto L_Complete; + if (.@q == 10) goto L_SecondReward; + if (.@q == 9) goto L_FirstReward; + if (.@q == 8) goto L_SecondTry; + if (.@q == 7) goto L_AnwarField; + if (.@q == 6) goto L_FirstTry; + if (.@q == 5) goto L_TryIt; + if (.@q >= 1) goto L_FirstTry; + + mesn; + mesq l("Hi. Could you perhaps be interested in doing some small errand for me?"); + menu + l("Sure!"),L_Sure, + l("I'm busy, sorry."),L_Close; + +L_Complete: + mesn; + mesq l("Thanks for saving Tulimshar from a famine. I'll be forever grateful."); + next; + mesn; + mesq l("Dealing with elves is too bothersome to me."); + close; + +L_AnwarField: + mesn; + mesq l("My crops! Hurry up, and talk to Eomie!!"); + close; + +L_Sure: + mes ""; + mesn; + mesq l("Great! Eomie, the girl on Tulimshar's magic academy, is an alchemist. She probably makes fertilizers."); + next; + mesn; + mesq l("This farm is dying due constant monster attacks, and without them, Tulimshar might face a famine."); + next; + mesn; + mesq l("Please, talk to her. Maybe she understands the direness of the situation and help... But you know. Elves."); + setq TulimsharQuest_AnwarField, 1; + close; + +L_FirstTry: + mesn; + mesq l("Good luck getting the fertilizer from Eomie! Many elves simply refuse to cooperate until it affects them directly."); + close; + +L_TryIt: + .@q2=getq2(TulimsharQuest_AnwarField); + mesn; + mesq l("You've brought me fertilizer! Let me see if it works..."); + next; + setq2 TulimsharQuest_AnwarField, .@q2+1; + + // Fail chances are 100% - 13% per attempt + if (rand2(0,100) < 100-(.@q2*13)) { + setq1 TulimsharQuest_AnwarField, 6; + mesc l("Nothing happens."); + next; + mesn; + mesq l("Uh... Something should happen, right? Can you get another one?"); + } else { + setq1 TulimsharQuest_AnwarField, 7; + mesc l("Evil worms crawl from earth and starts devouring the plants!"); + next; + mesn; + mesq l("Uh... That should not happen, right? RIGHT?"); + next; + mesn; + mesq l("Don't just stand here! Go fetch help, NOW!!"); + } + + close; + +L_SecondTry: + mesn strcharinfo(0); + mesq l("Here is the bug bomb! Eomie just gave me. Hurry up!"); + next; + getexp 20, 0; + specialeffect(51); + setq TulimsharQuest_AnwarField, 9; + mesn; + mesq l("Thanks God... The crops are safe. Not only that, but the fertilizer works!"); + next; + mesn; + mesq l("Ah, that was tiresome... I'll go make a reward for them, talk to me again later."); + close; + +L_FirstReward: + mesn; + mesq l("Here are two @@. Please deliver it to them. I hope they'll like it.", getitemlink(TortugaShell)); + setq TulimsharQuest_AnwarField, 10, 0; + getitembound(TortugaShell, 2, 4); // Prevent accidental item loss + close; + +L_SecondReward: + .@q2=getq2(TulimsharQuest_AnwarField); + if (.@q2 < 3){ + mesn; + mesq l("Please deliver the two @@ to Tinris and Eomie, and then I'll give you something for your help.", getitemlink(TortugaShell)); + close; + } + setq TulimsharQuest_AnwarField, 11, 0; + getitem2(FarmerPants, 1, 1, 0, 0, OrangeDye, 0,0,0); // EXPERIMENTAL, required for Inspector Quest + getexp 750, 0; + mesn; + mesq l("Many thanks for your help! Here, take this. I'm sure it can be very useful later. It always is."); + close; + +L_Weak: + hello; + end; + +L_Close: + close; + +OnTimer1000: + domovestep; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FarmerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, DesertShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); + setunitdata(.@npcId, UDT_WEAPON, Boots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 6); + setunitdata(.@npcId, UDT_HAIRCOLOR, 14); + + .sex = G_MALE; + .distance = 5; + + initpath "move", 99, 84,//A + "dir", RIGHT, 0, + "wait", 30, 0, + "move", 90, 60,//C + "dir", UP, 0, + "wait", 12, 0, + "move", 123, 64,//K + "dir", LEFT, 0, + "wait", 8, 0, + "move", 101, 84,//B + "dir", DOWN, 0, + "wait", 29, 0, + "move", 98, 68,//R + "dir", DOWN, 0, + "wait", 14, 0, + "move", 100, 80,//F + "dir", UP, 0, + "wait", 13, 0, + "move", 136, 73,//I + "dir", DOWN, 0, + "wait", 14, 0, + "move", 101, 84,//B + "dir", DOWN, 0, + "wait", 28, 0, + "move", 99, 64,//D + "dir", UP, 0, + "wait", 11, 0, + "move", 114, 60,//M + "dir", RIGHT, 0, + "wait", 8, 0, + "move", 99, 84,//A + "dir", RIGHT, 0, + "wait", 27, 0, + "move", 104, 60,//O + "dir", UP, 0, + "wait", 18, 0, + "move", 95, 62,//P + "dir", DOWN, 0, + "wait", 7, 0, + "move", 101, 84,//B + "dir", DOWN, 0, + "wait", 26, 0, + "move", 91, 66,//Q + "dir", UP, 0, + "wait", 21, 0, + "move", 95, 82,//G + "dir", UP, 0, + "wait", 10, 0, + "move", 99, 84,//A + "dir", RIGHT, 0, + "wait", 25, 0, + "move", 111, 78,//H + "dir", UP, 0, + "wait", 17, 0, + "move", 120, 63,//L + "dir", UP, 0, + "wait", 1, 0, + "move", 101, 84,//B + "dir", DOWN, 0, + "wait", 24, 0, + "move", 115, 69,//J + "dir", DOWN, 0, + "wait", 15, 0, + "move", 110, 63,//N + "dir", LEFT, 0, + "wait", 1, 0, + "move", 101, 84,//B + "dir", DOWN, 0, + "wait", 23, 0, + "move", 100, 80,//F + "dir", UP, 0, + "wait", 9, 0, + "move", 81, 63,//S + "dir", UP, 0, + "wait", 15, 0; + + initialmove; + initnpctimer; + end; +} + diff --git a/npc/004-1/elanore.txt b/npc/004-1/elanore.txt new file mode 100644 index 0000000..8f690be --- /dev/null +++ b/npc/004-1/elanore.txt @@ -0,0 +1,118 @@ +// TMW-2 Script +// Author: +// Jesusalva + +004-1,80,62,0 script Elanore the Healer NPC_FEMALE,{ + .@price=Nurse(.name$, 10, 5, 1); + mesq l("For you, it'll be @@ GP.", .@price); + mes ""; + menu + rif(Zeny >= .@price, l("Please heal me!")), L_Healing, + l("Do you make Lifestones?"), L_Lifestones, + l("Another time, maybe."), -; + close; + + +L_Healing: + Nurse(.name$, 10, 5, 2); + close; + +L_Lifestones: + mes ""; + mesn; + mesq l("Lifestones have the potential to heal the user, and thus are used for potions."); + next; + mesq l("I will need 3 @@, 5 @@ and 100 GP to craft a single stone.", getitemlink(BugLeg), getitemlink(MaggotSlime)); + next; + mes ""; + menu + l("OK, I will get them."), -, + l("No way!"), -, + l("Here you are!"), L_Trade, + l("I want more than one!"), L_Trade_Input; + close; + +L_Trade: + if (countitem(BugLeg) < 3) goto L_Trade_Missing; + if (countitem(MaggotSlime) < 5) goto L_Trade_Missing; + if (Zeny < 100) goto L_Trade_Missing; + + inventoryplace Lifestone, 1; + + delitem BugLeg, 3; + delitem MaggotSlime, 5; + set Zeny, Zeny - 100; + + getitem Lifestone, 1; + goto L_TradeOK; + +L_Trade_Input: + mesc l("How many?"); + input .@am; + if (.@am <= 0) + close; + + if (countitem(BugLeg) < 3*.@am) goto L_Trade_Missing; + if (countitem(MaggotSlime) < 5*.@am) goto L_Trade_Missing; + if (Zeny < 100*.@am) goto L_Trade_Missing; + + inventoryplace Lifestone, 1*.@am; + + delitem BugLeg, 3*.@am; + delitem MaggotSlime, 5*.@am; + set Zeny, Zeny - 100*.@am; + + getitem Lifestone, 1*.@am; + goto L_TradeOK; + +L_TradeOK: + mes ""; + + // Only grant Experience on first craft + if (getq(TulimsharQuest_Lifestone) == 0) { + setq TulimsharQuest_Lifestone, 1; + getexp 84, 10; + } + + // Second tier stuff + if (ST_TIER == 8) { + if (gettimetick(2) > QUEST_ELEVARTEMPO) { + mesc l(".:: Second Tier Quest - Timed Out ::."), 1; + } else { + getexp 44, 0; + ST_TIER=9; + mes l("##9.:: Second Tier Quest - Time Remaining: @@ ::.", FuzzyTime(QUEST_ELEVARTEMPO,2,2)); + } + } + + mesn; + mesq l("These will do just fine."); + next; + mesn; + mes l("Elanore casts a chant over the items, closes her hands, and vóila! A lifestone."); + next; + mesq l("I guess that you could use these lifestones to gain a regeneration effect, too, but I do not know how."); + next; + mes l("She smiles."); + close; + +L_Trade_Missing: + mesn; + mesq l("This is not what I asked for."); + close; + +OnInit: + .@npcId = getnpcid(.name$); + //setunitdata(.@npcId, UDT_HEADTOP, 2929); + setunitdata(.@npcId, UDT_HEADMIDDLE, SorcererRobe); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 8); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; + +} diff --git a/npc/004-1/sign.txt b/npc/004-1/sign.txt new file mode 100644 index 0000000..649b229 --- /dev/null +++ b/npc/004-1/sign.txt @@ -0,0 +1,19 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Sign + +004-1,70,74,0 script Sign#HalinarzoGoto NPC_SWORDS_SIGN,{ + mesc "↑ "+l("Tulimshar"); + mesc "→ "+l("Halinarzo Route"); + mesc "← "+l("Tulimshar Beach"); + mesc "↓ "+l("Tulimshar Mines"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + diff --git a/npc/004-2-1/_import.txt b/npc/004-2-1/_import.txt new file mode 100644 index 0000000..4c353fe --- /dev/null +++ b/npc/004-2-1/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-1: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-1/_mobs.txt", +"npc/004-2-1/_warps.txt", diff --git a/npc/004-2-1/_mobs.txt b/npc/004-2-1/_mobs.txt new file mode 100644 index 0000000..b24e70f --- /dev/null +++ b/npc/004-2-1/_mobs.txt @@ -0,0 +1,13 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-1: Canyon Cave mobs +004-2-1,0,0,0,0 monster Cave Maggot 1027,25,40000,200000 +004-2-1,67,48,29,23 monster Cave Snake 1035,12,35000,150000 +004-2-1,80,36,11,4 monster Snake 1122,1,35000,150000 +004-2-1,46,70,14,13 monster Snake 1122,3,35000,150000 +004-2-1,90,55,9,9 monster Snake 1122,3,35000,150000 +004-2-1,38,40,4,2 monster Maggot 1030,2,35000,150000 +004-2-1,57,24,4,2 monster Maggot 1030,2,35000,150000 +004-2-1,34,62,4,2 monster Maggot 1030,2,35000,150000 +004-2-1,88,68,4,2 monster Maggot 1030,2,35000,150000 +004-2-1,48,31,17,15 monster Scorpion 1071,5,35000,150000 +004-2-1,0,0,0,0 monster Small Topaz Bif 1101,2,35000,150000 diff --git a/npc/004-2-1/_warps.txt b/npc/004-2-1/_warps.txt new file mode 100644 index 0000000..9facc2a --- /dev/null +++ b/npc/004-2-1/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-1: Canyon Cave warps +004-2-1,38,43,0 warp #004-2-1_38_43 0,0,004-2,117,55 +004-2-1,57,21,0 warp #004-2-1_57_21 0,0,004-2,116,28 +004-2-1,87,70,0 warp #004-2-1_87_70 0,0,004-2,142,78 +004-2-1,33,64,0 warp #004-2-1_33_64 0,0,004-2,81,72 diff --git a/npc/004-2-10/_import.txt b/npc/004-2-10/_import.txt new file mode 100644 index 0000000..7a10799 --- /dev/null +++ b/npc/004-2-10/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-10: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-10/_mobs.txt", +"npc/004-2-10/_warps.txt", diff --git a/npc/004-2-10/_mobs.txt b/npc/004-2-10/_mobs.txt new file mode 100644 index 0000000..04c4072 --- /dev/null +++ b/npc/004-2-10/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-10: Canyon Cave mobs +004-2-10,0,0,0,0 monster Plushroom Field 1011,1,35000,150000 +004-2-10,0,0,0,0 monster Maggot 1030,1,35000,150000 diff --git a/npc/004-2-10/_warps.txt b/npc/004-2-10/_warps.txt new file mode 100644 index 0000000..a4d454e --- /dev/null +++ b/npc/004-2-10/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-10: Canyon Cave warps +004-2-10,33,29,0 warp #004-2-10_33_29 0,0,004-2-3,61,89 diff --git a/npc/004-2-11/_import.txt b/npc/004-2-11/_import.txt new file mode 100644 index 0000000..ce8f01e --- /dev/null +++ b/npc/004-2-11/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-11: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-11/_mobs.txt", +"npc/004-2-11/_warps.txt", diff --git a/npc/004-2-11/_mobs.txt b/npc/004-2-11/_mobs.txt new file mode 100644 index 0000000..1cd1d47 --- /dev/null +++ b/npc/004-2-11/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-11: Canyon Cave mobs +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-11: Canyon Cave mobs +004-2-11,33,39,4,3 monster Desert Maggot 1083,2,35000,150000 +004-2-11,0,0,0,0 monster Cave Maggot 1027,8,35000,150000 +004-2-11,42,33,12,10 monster Snake 1122,4,35000,150000 +004-2-11,55,39,14,11 monster Old Snake 1199,1,35000,150000 +004-2-11,46,33,16,11 monster Scorpion 1071,3,35000,150000 +004-2-11,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 diff --git a/npc/004-2-11/_warps.txt b/npc/004-2-11/_warps.txt new file mode 100644 index 0000000..ad3ee40 --- /dev/null +++ b/npc/004-2-11/_warps.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-11: Canyon Cave warps +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-11: Canyon Cave warps +004-2-11,33,43,0 warp #004-2-11_33_43 0,0,010-1,64,77 +004-2-11,61,49,0 warp #004-2-11_61_49 0,0,004-2-3,72,29 +004-2-11,52,20,0 warp #004-2-11_52_20 0,0,010-1-11,36,29 diff --git a/npc/004-2-12/_import.txt b/npc/004-2-12/_import.txt new file mode 100644 index 0000000..362871b --- /dev/null +++ b/npc/004-2-12/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-12: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-12/_mobs.txt", +"npc/004-2-12/_warps.txt", diff --git a/npc/004-2-12/_mobs.txt b/npc/004-2-12/_mobs.txt new file mode 100644 index 0000000..f9ea16e --- /dev/null +++ b/npc/004-2-12/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-12: Canyon Cave mobs +004-2-12,51,32,4,2 monster Maggot 1030,2,35000,150000 +004-2-12,39,33,9,13 monster Black Scorpion 1074,1,35000,150000 +004-2-12,40,34,12,12 monster Angry Scorpion 1131,3,35000,150000 +004-2-12,0,0,0,0 monster Cave Maggot 1027,3,35000,150000 +004-2-12,0,0,0,0 monster Small Ruby Bif 1098,1,35000,150000 diff --git a/npc/004-2-12/_warps.txt b/npc/004-2-12/_warps.txt new file mode 100644 index 0000000..c25da92 --- /dev/null +++ b/npc/004-2-12/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-12: Canyon Cave warps +004-2-12,52,35,0 warp #004-2-12_52_35 1,0,004-2,51,112 +004-2-12,36,52,0 warp #004-2-12_36_52 0,0,004-3-2,43,84 diff --git a/npc/004-2-2/_import.txt b/npc/004-2-2/_import.txt new file mode 100644 index 0000000..30d22a2 --- /dev/null +++ b/npc/004-2-2/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-2: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-2/_mobs.txt", +"npc/004-2-2/_warps.txt", diff --git a/npc/004-2-2/_mobs.txt b/npc/004-2-2/_mobs.txt new file mode 100644 index 0000000..7af7313 --- /dev/null +++ b/npc/004-2-2/_mobs.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-2: Canyon Cave mobs +004-2-2,50,84,14,5 monster Old Snake 1199,2,35000,150000 +004-2-2,0,0,0,0 monster Cave Maggot 1027,15,35000,150000 +004-2-2,56,55,4,26 monster Scorpion 1071,6,35000,150000 +004-2-2,36,79,4,2 monster Maggot 1030,2,35000,150000 +004-2-2,55,25,4,3 monster Desert Maggot 1083,2,35000,150000 +004-2-2,44,28,12,5 monster Old Snake 1199,2,35000,150000 +004-2-2,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 diff --git a/npc/004-2-2/_warps.txt b/npc/004-2-2/_warps.txt new file mode 100644 index 0000000..2ed7971 --- /dev/null +++ b/npc/004-2-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-2: Canyon Cave warps +004-2-2,36,76,0 warp #004-2-2_36_76 0,0,004-2,121,28 +004-2-2,55,21,0 warp #004-2-2_55_21 0,0,010-1,121,93 diff --git a/npc/004-2-3/_import.txt b/npc/004-2-3/_import.txt new file mode 100644 index 0000000..ae0ef44 --- /dev/null +++ b/npc/004-2-3/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-3: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-3/_mobs.txt", +"npc/004-2-3/_warps.txt", diff --git a/npc/004-2-3/_mobs.txt b/npc/004-2-3/_mobs.txt new file mode 100644 index 0000000..3b3d450 --- /dev/null +++ b/npc/004-2-3/_mobs.txt @@ -0,0 +1,23 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-3: Canyon Cave mobs +004-2-3,51,31,4,3 monster Desert Maggot 1083,2,35000,150000 +004-2-3,87,44,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,65,28,4,3 monster Desert Maggot 1083,2,35000,150000 +004-2-3,94,28,4,3 monster Desert Maggot 1083,2,35000,150000 +004-2-3,98,45,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,113,45,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,125,49,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,135,63,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,108,68,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,69,59,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,58,124,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,102,113,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,134,112,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,123,75,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,87,93,4,2 monster Maggot 1030,2,35000,150000 +004-2-3,0,0,0,0 monster Cave Maggot 1027,90,35000,150000 +004-2-3,40,115,6,9 monster Blub 1008,2,35000,150000 +004-2-3,95,85,38,32 monster Snake 1122,8,35000,150000 +004-2-3,104,76,32,49 monster Old Snake 1199,8,35000,150000 +004-2-3,0,0,0,0 monster Topaz Bif 1102,6,35000,150000 +004-2-3,55,102,22,25 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/004-2-3/_warps.txt b/npc/004-2-3/_warps.txt new file mode 100644 index 0000000..7ce29f0 --- /dev/null +++ b/npc/004-2-3/_warps.txt @@ -0,0 +1,22 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-3: Canyon Cave warps +004-2-3,58,127,0 warp #004-2-3_58_127 0,0,004-2,90,62 +004-2-3,123,78,0 warp #004-2-3_123_78 0,0,004-2,147,61 +004-2-3,87,96,0 warp #004-2-3_87_96 0,0,004-2,94,81 +004-2-3,135,115,0 warp #004-2-3_135_115 0,0,004-2,144,53 +004-2-3,102,116,0 warp #004-2-3_102_116 0,0,004-2,122,67 +004-2-3,69,62,0 warp #004-2-3_69_62 0,0,004-2,74,21 +004-2-3,108,66,0 warp #004-2-3_108_66 0,0,004-2,108,38 +004-2-3,135,66,0 warp #004-2-3_135_66 0,0,004-2,126,38 +004-2-3,97,48,0 warp #004-2-3_97_48 0,0,004-2,85,31 +004-2-3,124,47,0 warp #004-2-3_124_47 0,0,004-2,128,50 +004-2-3,87,42,0 warp #004-2-3_87_42 0,0,004-2,107,48 +004-2-3,49,32,0 warp #004-2-3_49_32 0,0,010-1,58,80 +004-2-3,95,31,0 warp #004-2-3_95_31 0,0,010-1,109,85 +004-2-3,65,25,0 warp #004-2-3_65_25 0,0,010-1,69,73 +004-2-3,135,57,0 warp #004-2-3_135_57 0,0,004-2-4,99,58 +004-2-3,72,28,0 warp #004-2-3_72_28 0,0,004-2-11,61,48 +004-2-3,74,97,0 warp #004-2-3_74_97 0,0,010-4-2,37,61 +004-2-3,114,42,0 warp #004-2-3_114_42 0,0,004-2,118,50 +004-2-3,102,61,0 warp #004-2-3_102_61 0,0,004-3-2,124,34 +004-2-3,61,88,0 warp #004-2-3_61_88 0,0,004-2-10,33,28 diff --git a/npc/004-2-4/_import.txt b/npc/004-2-4/_import.txt new file mode 100644 index 0000000..d3ca214 --- /dev/null +++ b/npc/004-2-4/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-4: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-4/_mobs.txt", +"npc/004-2-4/_warps.txt", diff --git a/npc/004-2-4/_mobs.txt b/npc/004-2-4/_mobs.txt new file mode 100644 index 0000000..5e048e9 --- /dev/null +++ b/npc/004-2-4/_mobs.txt @@ -0,0 +1,17 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-4: Canyon Cave mobs +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-4: Canyon Cave mobs +004-2-4,34,41,4,3 monster Desert Maggot 1083,2,35000,150000 +004-2-4,124,83,4,2 monster Maggot 1030,2,35000,150000 +004-2-4,0,0,0,0 monster Cave Maggot 1027,70,35000,150000 +004-2-4,135,71,4,2 monster Maggot 1030,2,35000,150000 +004-2-4,71,45,23,5 monster Scorpion 1071,8,35000,150000 +004-2-4,120,42,24,15 monster Snake 1122,8,35000,150000 +004-2-4,91,66,55,7 monster Black Scorpion 1074,7,35000,150000 +004-2-4,98,40,10,6 monster Bat 1039,1,35000,150000 +004-2-4,68,83,15,15 monster Angry Scorpion 1131,9,35000,150000 +004-2-4,49,55,13,5 monster Old Snake 1122,1,35000,150000 +004-2-4,135,82,1,7 monster Old Snake 1122,1,35000,150000 +004-2-4,125,92,11,2 monster Old Snake 1122,1,35000,150000 +004-2-4,0,0,0,0 monster Ruby Bif 1099,3,35000,150000 diff --git a/npc/004-2-4/_warps.txt b/npc/004-2-4/_warps.txt new file mode 100644 index 0000000..05dd3ae --- /dev/null +++ b/npc/004-2-4/_warps.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-4: Canyon Cave warps +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-4: Canyon Cave warps +004-2-4,34,45,0 warp #004-2-4_34_45 1,0,010-1,52,94 +004-2-4,124,86,0 warp #004-2-4_124_86 0,0,004-2,132,57 +004-2-4,134,24,0 warp #004-2-4_134_24 0,0,010-1-1,151,121 +004-2-4,135,69,0 warp #004-2-4_135_69 0,0,004-2,139,32 +004-2-4,103,21,0 warp #004-2-4_103_21 0,0,010-1-1,116,121 +004-2-4,99,59,0 warp #004-2-4_99_59 0,0,004-2-3,135,58 +004-2-4,118,42,0 warp #004-2-4_118_42 0,0,010-1-12,35,29 +004-2-4,121,20,0 warp #004-2-4_121_20 0,0,010-1-2,46,109 diff --git a/npc/004-2-5/_import.txt b/npc/004-2-5/_import.txt new file mode 100644 index 0000000..2ca65f7 --- /dev/null +++ b/npc/004-2-5/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-5: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-5/_mobs.txt", +"npc/004-2-5/_warps.txt", diff --git a/npc/004-2-5/_mobs.txt b/npc/004-2-5/_mobs.txt new file mode 100644 index 0000000..c6205b0 --- /dev/null +++ b/npc/004-2-5/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-5: Canyon Cave mobs +004-2-5,42,38,14,11 monster Old Snake 1199,1,35000,150000 +004-2-5,39,33,9,7 monster Bat 1039,2,35000,150000 +004-2-5,69,50,4,2 monster Little Blub 1007,1,35000,150000 +004-2-5,0,0,0,0 monster Cave Maggot 1027,8,35000,150000 +004-2-5,53,49,4,2 monster Maggot 1030,2,35000,150000 +004-2-5,65,54,9,6 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/004-2-5/_warps.txt b/npc/004-2-5/_warps.txt new file mode 100644 index 0000000..635af5e --- /dev/null +++ b/npc/004-2-5/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-5: Canyon Cave warps +004-2-5,53,46,0 warp #004-2-5_53_46 0,0,004-2,146,84 +004-2-5,55,34,0 warp #004-2-5_55_34 0,0,004-2-8,57,21 +004-2-5,35,53,0 warp #004-2-5_35_53 0,0,004-2-7,39,21 diff --git a/npc/004-2-6/_import.txt b/npc/004-2-6/_import.txt new file mode 100644 index 0000000..b96bb82 --- /dev/null +++ b/npc/004-2-6/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-6: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-6/_mobs.txt", +"npc/004-2-6/_warps.txt", diff --git a/npc/004-2-6/_mobs.txt b/npc/004-2-6/_mobs.txt new file mode 100644 index 0000000..10b1eea --- /dev/null +++ b/npc/004-2-6/_mobs.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-6: Canyon Cave mobs +004-2-6,0,0,0,0 monster Cave Maggot 1027,16,35000,150000 +004-2-6,71,33,12,11 monster Moutain Snake 1123,1,35000,150000 +004-2-6,56,33,25,8 monster Snake 1122,5,35000,150000 +004-2-6,34,31,3,3 monster Scorpion 1071,1,35000,150000 +004-2-6,34,24,4,2 monster Maggot 1030,2,35000,150000 +004-2-6,83,42,4,2 monster Maggot 1030,2,35000,150000 +004-2-6,0,0,0,0 monster Small Sapphire Bif 1113,1,35000,150000 diff --git a/npc/004-2-6/_warps.txt b/npc/004-2-6/_warps.txt new file mode 100644 index 0000000..d3d53d4 --- /dev/null +++ b/npc/004-2-6/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-6: Canyon Cave warps +004-2-6,34,21,0 warp #004-2-6_34_21 0,0,004-2,110,93 +004-2-6,84,44,0 warp #004-2-6_84_44 0,0,004-2,145,104 +004-2-6,77,21,0 warp #004-2-6_77_21 0,0,004-2-7,51,38 diff --git a/npc/004-2-7/_import.txt b/npc/004-2-7/_import.txt new file mode 100644 index 0000000..43ce948 --- /dev/null +++ b/npc/004-2-7/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-7: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-7/_mobs.txt", +"npc/004-2-7/_warps.txt", diff --git a/npc/004-2-7/_mobs.txt b/npc/004-2-7/_mobs.txt new file mode 100644 index 0000000..705fcc9 --- /dev/null +++ b/npc/004-2-7/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-7: Canyon Cave mobs +004-2-7,0,0,0,0 monster Cave Maggot 1027,6,35000,150000 +004-2-7,46,29,11,7 monster Snake 1122,1,35000,150000 +004-2-7,45,29,15,6 monster Cave Snake 1035,2,35000,150000 +004-2-7,45,31,15,6 monster Bat 1039,6,35000,150000 +004-2-7,0,0,0,0 monster Topaz Bif 1102,1,35000,150000 diff --git a/npc/004-2-7/_warps.txt b/npc/004-2-7/_warps.txt new file mode 100644 index 0000000..7d32f8c --- /dev/null +++ b/npc/004-2-7/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-7: Canyon Cave warps +004-2-7,51,39,0 warp #004-2-7_51_39 0,0,004-2-6,77,22 +004-2-7,39,20,0 warp #004-2-7_39_20 0,0,004-2-5,35,52 +004-2-7,56,21,0 warp #004-2-7_56_21 0,0,004-2-8,47,40 diff --git a/npc/004-2-8/_import.txt b/npc/004-2-8/_import.txt new file mode 100644 index 0000000..db0afcf --- /dev/null +++ b/npc/004-2-8/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-8: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-8/_mobs.txt", +"npc/004-2-8/_warps.txt", diff --git a/npc/004-2-8/_mobs.txt b/npc/004-2-8/_mobs.txt new file mode 100644 index 0000000..300108f --- /dev/null +++ b/npc/004-2-8/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-8: Canyon Cave mobs +004-2-8,0,0,0,0 monster Cave Maggot 1027,17,35000,150000 +004-2-8,40,72,4,2 monster Maggot 1030,2,35000,150000 +004-2-8,50,44,17,20 monster Angry Scorpion 1131,19,35000,150000 +004-2-8,36,45,5,20 monster Black Scorpion 1074,1,35000,150000 +004-2-8,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 diff --git a/npc/004-2-8/_warps.txt b/npc/004-2-8/_warps.txt new file mode 100644 index 0000000..2c528fe --- /dev/null +++ b/npc/004-2-8/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-8: Canyon Cave warps +004-2-8,39,75,0 warp #004-2-8_39_75 0,0,004-2,141,114 +004-2-8,57,20,0 warp #004-2-8_57_20 0,0,004-2-5,55,33 +004-2-8,47,41,0 warp #004-2-8_47_41 0,0,004-2-7,56,22 diff --git a/npc/004-2-9/_import.txt b/npc/004-2-9/_import.txt new file mode 100644 index 0000000..6ca7eff --- /dev/null +++ b/npc/004-2-9/_import.txt @@ -0,0 +1,4 @@ +// Map 004-2-9: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2-9/_mobs.txt", +"npc/004-2-9/_warps.txt", diff --git a/npc/004-2-9/_mobs.txt b/npc/004-2-9/_mobs.txt new file mode 100644 index 0000000..13c4696 --- /dev/null +++ b/npc/004-2-9/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-9: Canyon Cave mobs +004-2-9,61,25,4,3 monster Desert Maggot 1083,2,35000,150000 +004-2-9,45,41,14,3 monster Bat 1039,1,35000,150000 +004-2-9,0,0,0,0 monster Cave Maggot 1027,6,35000,150000 +004-2-9,47,39,17,11 monster Old Snake 1199,1,35000,150000 +004-2-9,45,39,15,12 monster Cave Snake 1035,4,35000,150000 +004-2-9,0,0,0,0 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/004-2-9/_warps.txt b/npc/004-2-9/_warps.txt new file mode 100644 index 0000000..f069f1c --- /dev/null +++ b/npc/004-2-9/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2-9: Canyon Cave warps +004-2-9,61,21,0 warp #004-2-9_61_21 0,0,010-1,92,70 +004-2-9,46,32,0 warp #004-2-9_46_32 0,0,010-2-4,58,48 +004-2-9,36,57,0 warp #004-2-9_36_57 0,0,004-3-5,34,21 diff --git a/npc/004-2/_import.txt b/npc/004-2/_import.txt new file mode 100644 index 0000000..7c8d017 --- /dev/null +++ b/npc/004-2/_import.txt @@ -0,0 +1,7 @@ +// Map 004-2: Desert Mountains +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-2/_mobs.txt", +"npc/004-2/_warps.txt", +"npc/004-2/mirio.txt", +"npc/004-2/pachua.txt", +"npc/004-2/sign.txt", diff --git a/npc/004-2/_mobs.txt b/npc/004-2/_mobs.txt new file mode 100644 index 0000000..b531f3e --- /dev/null +++ b/npc/004-2/_mobs.txt @@ -0,0 +1,18 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2: Desert Mountains mobs +004-2,0,0,0,0 monster Maggot 1030,100,35000,150000 +004-2,118,37,17,15 monster Scorpion 1071,14,35000,150000 +004-2,61,43,10,22 monster Scorpion 1071,6,35000,150000 +004-2,111,58,4,4 monster Mister Prickel 1436,1,35000,35000 +004-2,118,100,4,4 monster Snake 1122,3,35000,150000 +004-2,87,54,9,4 monster Snake 1199,1,35000,150000 +004-2,77,66,14,11 monster Scorpion 1071,8,35000,150000 +004-2,94,56,42,32 monster Desert Log Head 1127,30,35000,150000 +004-2,56,98,1,1 monster Duck 1029,1,35000,150000 +004-2,71,100,28,10 monster Red Scorpion 1072,10,35000,150000 +004-2,146,28,3,2 monster Yellow Slime 1091,1,35000,150000 +004-2,143,47,6,18 monster Scorpion 1071,9,35000,150000 +004-2,140,57,9,8 monster Snake 1122,2,35000,150000 +004-2,142,96,3,5 monster Mountain Snake 1123,1,35000,300000 +004-2,131,75,17,9 monster Snake 1122,3,35000,150000 +004-2,70,106,20,14 monster Mister Prickel 1436,5,35000,35000 diff --git a/npc/004-2/_warps.txt b/npc/004-2/_warps.txt new file mode 100644 index 0000000..31f2d88 --- /dev/null +++ b/npc/004-2/_warps.txt @@ -0,0 +1,43 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-2: Desert Mountains warps +004-2,117,54,0 warp #004-2_117_54 0,0,004-2-1,38,42 +004-2,116,27,0 warp #004-2_116_27 0,0,004-2-1,57,22 +004-2,30,51,0 warp #004-2_30_51 0,2,004-1,125,95 +004-2,121,27,0 warp #004-2_121_27 0,0,004-2-2,36,77 +004-2,94,80,0 warp #004-2_94_80 0,0,004-2-3,87,95 +004-2,147,60,0 warp #004-2_147_60 0,0,004-2-3,123,77 +004-2,132,56,0 warp #004-2_132_56 0,0,004-2-4,124,85 +004-2,144,25,0 warp #004-2_144_25 0,0,010-4-2,87,41 +004-2,144,52,0 warp #004-2_144_52 0,0,004-2-3,135,114 +004-2,90,61,0 warp #004-2_90_61 0,0,004-2-3,58,126 +004-2,113,120,0 warp #004-2_113_120 0,0,004-3-2,83,63 +004-2,86,116,0 warp #004-2_86_116 0,0,004-3-2,59,54 +004-2,60,126,0 warp #004-2_60_126 0,0,004-3-1,68,32 +004-2,53,103,0 warp #004-2_53_103 0,0,004-3-1,70,24 +004-2,53,88,0 warp #004-2_53_88 0,0,004-3,53,41 +004-2,63,88,0 warp #004-2_63_88 0,0,004-3,63,41 +004-2,81,93,0 warp #004-2_81_93 0,0,004-3-2,44,37 +004-2,73,89,0 warp #004-2_73_89 0,0,010-2-8,37,21 +004-2,68,124,0 warp #004-2_68_124 0,0,010-2-8,32,62 +004-2,51,111,0 warp #004-2_51_111 0,0,004-2-12,52,34 +004-2,48,59,0 warp #004-2_48_59 0,0,007-1-1,56,35 +004-2,110,92,0 warp #004-2_110_92 0,0,004-2-6,34,22 +004-2,119,107,0 warp #004-2_119_107 0,0,004-3-2,95,51 +004-2,145,103,0 warp #004-2_145_103 0,0,004-2-6,84,43 +004-2,122,66,0 warp #004-2_122_66 0,0,004-2-3,102,115 +004-2,85,30,0 warp #004-2_85_30 0,0,004-2-3,97,47 +004-2,74,20,0 warp #004-2_74_20 0,0,004-2-3,69,61 +004-2,108,37,0 warp #004-2_108_37 0,0,004-2-3,108,67 +004-2,126,37,0 warp #004-2_126_37 0,0,004-2-3,135,65 +004-2,118,49,0 warp #004-2_118_49 0,0,004-2-3,114,43 +004-2,107,47,0 warp #004-2_107_47 0,0,004-2-3,87,43 +004-2,128,49,0 warp #004-2_128_49 0,0,004-2-3,124,48 +004-2,142,77,0 warp #004-2_142_77 0,0,004-2-1,87,69 +004-2,146,85,0 warp #004-2_146_85 0,0,004-2-5,53,47 +004-2,141,113,0 warp #004-2_141_113 0,0,004-2-8,39,74 +004-2,81,71,0 warp #004-2_81_71 0,0,004-2-1,33,63 +004-2,85,49,0 warp #004-2_85_49 0,0,007-1-2,50,53 +004-2,92,33,0 warp #004-2_92_33 0,0,007-1-2,56,27 +004-2,139,31,0 warp #004-2_139_31 0,0,004-2-4,135,70 +004-2,45,98,0 warp #004-2_45_98 0,0,004-3-6,45,58 +004-2,104,93,0 warp #004-2_104_93 0,0,004-3-2,74,41 diff --git a/npc/004-2/mirio.txt b/npc/004-2/mirio.txt new file mode 100644 index 0000000..4c149eb --- /dev/null +++ b/npc/004-2/mirio.txt @@ -0,0 +1,230 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Part of Speed Run Quest +// Structure: +// HalinarzoQuest_Speed( state, time, record ) + +004-2,146,115,0 script Mirio NPC_INDIGENOUS_YOUNG,1,1,{ + function neutral; + function naked; + function disarmed; + function hungry; + function speed; + function start; + function slow; + function running; + function restricted; + function retry; + .@q = getq(HalinarzoQuest_Speed); + .@t = getq2(HalinarzoQuest_Speed); + .@r = getq3(HalinarzoQuest_Speed); + if (BaseLevel < 40) neutral(); + if (getequipid(EQI_HEAD_MID) < 1) naked(); + if (getequipid(EQI_HEAD_TOP) < 1) naked(); + if (getequipid(EQI_HAND_R) < 1) disarmed(); + if (getequipid(EQI_HAND_L) < 1) disarmed(); + if (@pachua) running(); + if (.@q <= 0) hungry(); + if (.@q == 1) speed(); + if (.@q < 10) retry(); + neutral(); + close; + +function neutral { + mesn; + mesq l("Howdy! Pachua and I scout this canyon to protect our clan. I acknowledge only those whom are fast like the wind! I would love to challenge you for a race... But not today."); + close; +} + +function naked { + mesn; + mesq l("...I know it is hot in this desert, but if you don't wear some armor and a hat, monsters will get to you. Really harsh, nowadays."); + close; +} + +function disarmed { + .@pn$ = lg("girl", "dude"); + mesn; + mesq l("...%s, as far as you should be concerned, this is an hostile land and I'm not your friend. You should use a weapon and a shield if possible. Never trust strangers, specially if they look like an assassin... exactly like me.", .@pn$); + close; +} + +function slow { + mesn; + mesq l("...Try allocating some points in agility before we continue. I think %d should be suffice.", getarg(0, 40)); + close; +} + +function restricted { + mesn; + mesq l("Sorry, but this quest cannot be done by staff."); + close; +} + +function hungry { + .@pn$ = lg("gal", "pal"); + mesn; + mesq l("Howdy, partner! Man, you have no idea how I'm hungry. Been scouting this canyon for weeks, under the scorching sun, and there's only snake meat to eat... I'm get tired of this repulsive stuff."); + next; + mesn; + mesq l("So let's strike a deal, bring me %d %s, %d %s, %d %s, and a %s. I'll reward you, promise.", + 10, getitemlink(LettuceLeaf), + 5, getitemlink(Cheese), + 2, getitemlink(TonoriDelight), + getitemlink(HalfCroconut)); + next; + select + l("Sure, I'll be back."), + l("I have everything you asked me."); + mes ""; + if (@menu == 1) closeclientdialog; + if (@menu == 2) { + if (countitem(LettuceLeaf) < 10 || + countitem(Cheese) < 5 || + countitem(TonoriDelight) < 2 || + countitem(HalfCroconut) < 1) { + mesc l("A dagger is stuck to your heart."), 1; + mesn; + mesq l("Told ya, %s, should know better than to mess with an... assassin!", .@pn$); + percentheal -100, 0; + close; + } + inventoryplace CaveSnakeSkin, 1, SnakeSkin, 1; + delitem LettuceLeaf, 10; + delitem Cheese, 5; + delitem TonoriDelight, 2; + delitem HalfCroconut, 1; + getitem SnakeSkin, 1; + getitem CaveSnakeSkin, 1; + setq HalinarzoQuest_Speed, 1; + mesn; + mesq l("Ah, good. You didn't even bother poisoning it? Hahah, that's great, %s! Here is a couple skins I had lying around, may be useful.", .@pn$); + next; + mesn; + mesq l("How about this, I can teach you how to be a tiny bit faster, so you can use heavier armor. I'll even give you a discount. Give it some thought, come back to me after you make up your mind."); + } + close; +} + +///////////////////////////////////////////////////////////////////////////////// +function speed { + if (is_staff() && !$@GM_OVERRIDE && !debug) restricted(); + if (readparam2(bAgi) < 40) slow(); + mesn; + mesq l("Have you thought on my proposal? I'll only charge you %d GP or a %s for each attempt made.", .price, getitemlink(.alt)); + next; + select + l("Not now, thanks."), + rif(Zeny >= .price, l("I'll pay the GP.")), + rif(countitem(.alt), l("I'll give you the item.")); + mes ""; + if (@menu == 1) close; + if (@menu == 2) Zeny -= .price; + if (@menu == 3) delitem .alt, 1; + start(); + return; +} + +function start { + mesn; + mesq l("Great! It is simple, really. Pachua and I are scouting the desert. Run as fast as you can to Pachua, and then run back here. Pachua will send a smoke signal when you talk to him, so I'll know you haven't cheated."); + next; + mesn; + mesq l("Try to make a good time, and... Good luck! On my signal, 3, 2, 1..."); + next; + setq2 HalinarzoQuest_Speed, gettimetick(2) + 1; // Grace time due @refresh + @pachua = 1; + slide 146, 115; + closeclientdialog; + // META: How they know your name? ...Via label, obviously. + npctalk3 l("GO! Run, %s, RUN!!!!", strcharinfo(0)); + atcommand("@refresh"); + close; +} + +function running { + if (@pachua < 2) { + npctalk3 l("What are you waiting for?! Get running, Pachua is waiting!"); + closeclientdialog; + close; + } + // Pachua == 2, completed the run + .@to = getq2(HalinarzoQuest_Speed); + .@q = getq(HalinarzoQuest_Speed); + .@t = gettimetick(2) - .@to; + .@r = getq3(HalinarzoQuest_Speed); + @pachua = 0; + mesn; + mesq l("...And stop! You took %s this time!", FuzzyTime(.@to)); + if (.@t < 15) { + mesc l("That's obviously impossible, so I'll voiding your result!"); + close; + } + if (.@t < .@r) { + mesc l("That's %d seconds better than your previous record!", .@r - .@t); + } + if (!.@r || .@t < .@r) + setq3 HalinarzoQuest_Speed, .@t; + setq2 HalinarzoQuest_Speed, 0; + next; + // Condition + .@g = 320 - (.@q * 20); + if (.@t > .@g) { + mesn; + mesq l("You took more than %s to do this, that is lame! Better luck next time.", FuzzyTime(.@g)); + // Consolation Prize + if (.@t < 450) { + next; + mesn; + mesq l("...Well, I'll refund you 10% because it was not utterly terrible."); + Zeny += .price / 10; + } + // MISSION FAILED + mesc l("RACE FAILED."), 1; + close; + } + // Ooh, you did it! + setq1 HalinarzoQuest_Speed, .@q + 1; + getexp 2500, 0; + mesn; + mesq l("I'm proud of you!"); + if (getq(HalinarzoQuest_Speed) < 10) + mesc l("Feel free to try again whenever."); + close; +} + +function retry { + if (is_staff() && !$@GM_OVERRIDE && !debug) restricted; + .@q = getq(HalinarzoQuest_Speed); + .@p = .price * .@q; + .@s = 40 + (.@q * 5); + if (readparam2(bAgi) < .@s) slow(.@s); + mesn; + mesq l("To retry, I will charge you %d GP or %d %s for each attempt made. You'll have less time to finish as well.", .@p, .@q * 2, getitemlink(.alt)); + next; + select + l("Not now, thanks."), + rif(Zeny >= .@p, l("I'll pay the GP.")), + rif(countitem(.alt) >= .@q * 2, l("I'll give you the item.")); + mes ""; + if (@menu == 1) close; + if (@menu == 2) Zeny -= .@p; + if (@menu == 3) delitem .alt, .@q * 2; + start(); + return; +} + +OnTouch: + if (@pachua) running(); + end; + +OnInit: + .distance = 4; + .sex = G_MALE; + .price = 320; + .alt = TonoriDelight; + end; +} + diff --git a/npc/004-2/pachua.txt b/npc/004-2/pachua.txt new file mode 100644 index 0000000..b196bfd --- /dev/null +++ b/npc/004-2/pachua.txt @@ -0,0 +1,49 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Part of Speed Run Quest, and an unplanned assassin quest. + +004-2,61,118,0 script Pachua NPC_INDIGENOUS_OLD,1,1,{ + if (@pachua == 1) goto OnTouch; + mesn; + mesq l("Howdy! Mirio and I scout this canyon to protect our clan. I am also a retired assassin, maybe someday I can exchange some knowledge with you! Hahaha!"); + if (getq(LoFQuest_EPISODE) == 9 && + countitem(Honey) >= 10 && + countitem(ElixirOfLife)) { + next; + select + l("Nice."), + l("[10x Honey, 1x Elixir of Life] The Shadow Tortuga won the race against the Panthom Lord."); + mes ""; + if (@menu == 2) { + mesc l("%s lifts an eyebrow to you.", .name$); + next; + inventoryplace ArcmageBoxset, 1; + mesn; + mesq l("So they need me again. Alright. Tell them the following:"); + mes b(l("The crow took off to his last flight.")); + next; + delitem Honey, 10; + delitem ElixirOfLife, 1; + getitem ArcmageBoxset, 1; + setq LoFQuest_EPISODE, 10; + mesn; + mesq l("And you, take this %s. It is a reward for doing the dirty job. You would think old age would let you retire, hahaha!", getitemlink(ArcmageBoxset)); + close; + } + } + close; + +OnTouch: + if (@pachua != 1) end; + message strcharinfo(0), "Pachua quickly inhales from his pipe and releases a ring of smoke towards the sky!"; + @pachua = 2; + end; + +OnInit: + .distance = 4; + .sex = G_MALE; + end; +} + diff --git a/npc/004-2/sign.txt b/npc/004-2/sign.txt new file mode 100644 index 0000000..46a2e75 --- /dev/null +++ b/npc/004-2/sign.txt @@ -0,0 +1,48 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Error Handler + +004-2,64,63,0 script Sign#HalinRoute NPC_SWORDS_SIGN,{ + mesc "← "+l("Tulimshar"); + mesc "→ "+l("Canyon - Safe Route"); + mesc "↓ "+l("Canyon - Settlement Route"); + mes ""; + mesc l("\"Follow the light.\" - Weary traveler"); + mesc l("\"Those whom believe the Pink Moouboo wear rock knifes at the entrances.\" - Aahna"); + mesc l("\"Those whom stray from the light shall met a quick death.\" - Saulc, from the 'Blame Saulc' famous book"), 1; + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +004-2,81,23,0 script Sign#TempBugfix NPC_SWORDS_SIGN,{ + if (countitem(DesertTablet)) goto L_Tablet; + if (MAGIC_LVL >= 7) goto L_RawPower; + mesc l("Impossible to read."); + close; + +L_Tablet: + mes l("The %s shines with a strange light...", getitemlink(DesertTablet)); + next; + closeclientdialog; + cwarp "001-3", 117, 135; + close; + +L_RawPower: + mes l("You can sense powerful cloaking magic emanating from this sign. With your superior magic, you dismiss the enchantment, and see what the cliffs have been hiding all along..."); + next; + closeclientdialog; + cwarp "001-3", 117, 135; + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + diff --git a/npc/004-3-1/_import.txt b/npc/004-3-1/_import.txt new file mode 100644 index 0000000..f5cd5a7 --- /dev/null +++ b/npc/004-3-1/_import.txt @@ -0,0 +1,5 @@ +// Map 004-3-1: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-3-1/_mobs.txt", +"npc/004-3-1/_warps.txt", +"npc/004-3-1/falkrun.txt", diff --git a/npc/004-3-1/_mobs.txt b/npc/004-3-1/_mobs.txt new file mode 100644 index 0000000..94df09c --- /dev/null +++ b/npc/004-3-1/_mobs.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-1: Canyon Cave mobs +004-3-1,69,31,4,2 monster Maggot 1030,2,35000,150000 +004-3-1,69,22,4,2 monster Maggot 1030,2,35000,150000 +004-3-1,70,36,14,7 monster Scorpion 1071,2,35000,150000 +004-3-1,0,0,0,0 monster Cave Maggot 1027,1,35000,150000 +004-3-1,38,34,8,9 monster Little Blub 1007,6,35000,150000 +004-3-1,60,23,11,3 monster Red Scorpion 1072,1,35000,150000 +004-3-1,45,32,15,12 monster Plushroom Field 1011,2,35000,150000 diff --git a/npc/004-3-1/_warps.txt b/npc/004-3-1/_warps.txt new file mode 100644 index 0000000..d7eb1bc --- /dev/null +++ b/npc/004-3-1/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-1: Canyon Cave warps +004-3-1,70,25,0 warp #004-3-1_70_25 0,0,004-2,53,104 +004-3-1,68,33,0 warp #004-3-1_68_33 0,0,004-2,60,127 diff --git a/npc/004-3-1/falkrun.txt b/npc/004-3-1/falkrun.txt new file mode 100644 index 0000000..75d16a9 --- /dev/null +++ b/npc/004-3-1/falkrun.txt @@ -0,0 +1,200 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Part of the Doctor's Quest. Lore Master. Saulc's Residence Gatekeeper. + +004-3-1,37,23,0 script Falkrun NPC_INDIGENOUS_ANY,{ + if (instance_id() >= 0) goto L_Episode; + mesn; + mesq l("Howdy! Lately I feel like a placeholder, just surviving on these harsh lands."); + next; +L_Main: + // He needs to explain the Civil War and the Great Fire for Doctor's Quest + mesn; + mesq l("Our clan has moved away from Tulimshar before the Civil War, almost was wiped off during the Great Fire, and when we thought we could finally life in peace, the Mana War and the Monster War broke out in rapid succession."); // Hence, TMW2 + next; + mesn; + mesq l("I don't know if our clan will survive for much longer. Do you have any questions before we all die and our knowledge gets forgotten?"); + next; + .@hist = 0; + do + { + select + l("No, none."), + l("Tell me about the Civil War."), + l("Tell me about the Great Fire."), + l("Tell me about the Mana War."), + l("Tell me about the Monster War."), + rif(.@hist == 15, l("What happened to the Wizard's Tower?")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("The Civil War is one of the stuff which nearly everyone forgot about."); + next; + mesn; + mesq l("Jarret, Tulimshar's Founder, was a great man, but his successors were not. Specially the Red Queen. Eventually, our clan had enough and moved away from Tulim."); + next; + mesn; + mesq l("She was a tyrant who wanted to rule over not only Tulimshar, but the whole mana world, with an iron fist. She even charged her own premier of treason and tried to execute him."); + next; + mesn; + mesq l("The council, however, supported the premier. He might have accepted the death sentence quietly, but the council would hear none of it. Benjamin took over and incited a civil war."); + next; + mesn; + mesq l("The Red Queen was murdered, but her supporters prolonged the Civil War for at least six years after her death. The grudges were strong back then..."); + next; + .@hist = .@hist | 1; + break; + case 3: + mesn; + mesq l("You see, Benjamin wanted to reshape the world. Very ambitious plans, which would have been great... If they had prospered."); + next; + mesn; + mesq l("Tulimshar spent trillions in research, and so did Hurnscald and Nivalis. Everything else was neglected. Eventually, some highly experimental tech experiment went haywire."); + next; + mesn; + mesq l("A fire broke out in Tulimshar's Academy, which quickly spread over the whole town... And you would think it would be all, but no. The fire was odd."); + next; + mesn; + mesq l("Instead of burning the town to a crisp, it moved to the canyon. Passed through the scarce vegetation until it arrived Hurnscald... And burned it down."); + next; + mesn; + mesq l("And by \"moved to the canyon\", I do mean it destroyed the little which kept our community alive back then. I heard the fire even moved to the icelands and burned everything on the path until it died at the frozen sea, but that would be too far for I know."); + next; + mesn; + mesq l("The great fire did very few causalities, but it destroyed a lot of infrastructure and ruined the economy. Famine and chaos ensued, we almost were wiped out back then. Maybe it would be best if we had indeed been wiped out..."); + next; + .@hist = .@hist | 2; + break; + case 4: + mesn; + mesq l("As you might know, the Great Fire ruined the whole world. Many people wanted to escape the wastelands which the continent had became, so they made convoys and left to the great sea."); + next; + mesn; + mesq l("Such convoys had been made since the Civil War, after all, many feared the Red Queen and moving to the sea was a sure way to stay out of her reach. None had ever heard again of them..."); + next; + mesn; + mesq l("...Until one day, past the sea, in Kolev's continent, they found a mana stone. Magic. This great breakthrough saved this continent, and saved our clan as well."); + next; + mesn; + mesq l("An academy was built north of Tulimshar, and later, the Land of Fire Village was also built, symbol of progress, development and research."); + next; + mesn; + mesq l("Which sound great in theory, but it was not. Greedy people found a way into power. They probably had good reasons, but they started confiscating Mana Stones, and storing them in a tower..."); + next; + mesn; + mesq l("...A tower, on this canyon. People disliked it, humans and all other races. The Mana War happened, bloodshed, and... The Monster War."); + next; + .@hist = .@hist | 4; + break; + case 5: + mesn; + mesq l("The Monster War broke out on the very last day of the Mana War. A high council of Tulimshar had set a fortress and storage towers in the canyon, and a few populists went to war."); + next; + mesn; + mesq l("At the last day, an earthquake happened. And then, monsters started showing up everywhere. Immortal monsters, which come back after killed, as if they had touched the Soul Menhir."); + next; + mesn; + mesq l("And thus, the longest war begun... And our clan was wiped out. Pachua, Mirio, I, Melune and Darug are the last ones. Oh, and Andra, I guess?"); + next; + mesn; + mesq l("The clan will end on this generation. And hopefully, the world will survive longer, things aren't looking well right now."); + next; + .@hist = .@hist | 8; + break; + case 6: + mesn; + mesq l("Mhm? I believe it was rebuilt, and the High Alliance took it over from the mage association for not paying taxes."); + next; + mesn; + mesq l("It is a magical tower, though, cloaked in the desert. You can't just walk there. The tower was north of here, you will find a sign near the gate... But you won't find the gate itself."); + next; + mesn; + mesq l("Maybe, if you had some sort of ancient artifact and touched the sign, it would allow you to see through the cloaking? Or if you were powerful enough. As if I would know!"); + next; + break; + } + } while (@menu != 1); + close; + +L_Episode: + if (getq(LoFQuest_EPISODE) != 11) + goto L_Main; + if (getq3(LoFQuest_EPISODE) && + mobcount(getmap(), "all") <= 0) + goto L_EpFinish; + if (getq3(LoFQuest_EPISODE) && + mobcount(getmap(), "all") > 0) { + npctalk l("Good luck, %s! There are more monsters nearby!", strcharinfo(0)); + end; + } + setq3 LoFQuest_EPISODE, 1; + mesn; + mesq l("Uh? What are you doing here?!"); + next; + select + l("I don't know?"), + l("How did I got here?"), + l("Who are you?"); + mes ""; + mesn; + mesq l("What have you done?! We're under attack!"); + next; + mesn; + mesq l("Quick, battle position! They'll strike! I'll help you a tiny bit!"); + mesc l("WARNING: You have a time limit!"), 1; + next; + changeplayermusic "let_the_battles_begin.ogg"; + instance_set_timeout(900, 900, getq2(LoFQuest_EPISODE)); + SC_Bonus(90, SC_INCHITRATE, 50); + SC_Bonus(90, SC_INCMHPRATE, 65); + SC_Bonus(90, SC_KAIZEL, 80); + percentheal 100, 100; + .@m$ = getmap(); + // FIXME TODO - TOO DIFFICULT, split them a bit apart + monster(.@m$, 47, 41, "Lost", BlueSlimeMother, 1); + monster(.@m$, 45, 31, "Lost", CopperSlimeMother, 1); + monster(.@m$, 43, 32, "Lost", YellowSlimeMother, 1); + monster(.@m$, 44, 32, "Lost", RedSlimeMother, 1); + monster(.@m$, 61, 21, "Lost", ChocolateSlimeMother, 1); + monster(.@m$, 45, 33, "Lost", WhiteSlimeMother, 1); + monster(.@m$, 43, 34, "Lost", AzulSlimeMother, 1); + monster(.@m$, 65, 40, "Lost", SeaSlimeMother, 1); + monster(.@m$, 45, 35, "Lost", GreenSlimeMother, 1); + //monster(.@m$, 47, 35, "Lost", LavaSlimeMother, 1); + monster(.@m$, 68, 30, "Lost", Thug, 1); + monster(.@m$, 70, 22, "Lost", Thug, 1); + monster(.@m$, 74, 42, "Lost", Thug, 1); + closeclientdialog; + close; + +L_EpFinish: + // Cleanup + instance_set_timeout(900, 900, getq2(LoFQuest_EPISODE)); + enablenpc instance_npcname("#004-3-1_70_25", getq2(LoFQuest_EPISODE)); + enablenpc instance_npcname("#004-3-1_68_33", getq2(LoFQuest_EPISODE)); + if (CHEST_MONEY) { + mesc l("You were refunded in %s GP!", fnum(CHEST_MONEY)); + Zeny+=CHEST_MONEY; + CHEST_MONEY=0; + } + setq LoFQuest_EPISODE, 12; + getitem SacredLifePotion, 10; + getitem SacredImmortalityPotion, 5; + getexp 60000, 0; + // Final dialog + mesn; + mesq l("Phew... We should be safe now. Thanks, %s!", strcharinfo(0)); + next; + mesn; + mesq l("...Even if this is your fault. Anyway, ##Breturn to whoever sent you here##b and begone."); + close; + +OnInit: + .distance = 4; + .sex = G_OTHER; + end; +} + diff --git a/npc/004-3-2/_import.txt b/npc/004-3-2/_import.txt new file mode 100644 index 0000000..60fe365 --- /dev/null +++ b/npc/004-3-2/_import.txt @@ -0,0 +1,4 @@ +// Map 004-3-2: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-3-2/_mobs.txt", +"npc/004-3-2/_warps.txt", diff --git a/npc/004-3-2/_mobs.txt b/npc/004-3-2/_mobs.txt new file mode 100644 index 0000000..99f6bb8 --- /dev/null +++ b/npc/004-3-2/_mobs.txt @@ -0,0 +1,19 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-2: Canyon Cave mobs +004-3-2,0,0,0,0 monster Cave Maggot 1027,70,35000,150000 +004-3-2,83,61,4,2 monster Maggot 1030,2,35000,150000 +004-3-2,44,36,4,2 monster Maggot 1030,2,35000,150000 +004-3-2,123,103,5,2 monster Lost Ocean Croc 1133,1,35000,150000 +004-3-2,119,69,8,7 monster Little Blub 1007,7,35000,150000 +004-3-2,96,86,10,8 monster Blub 1008,1,35000,150000 +004-3-2,126,101,17,4 monster Blub 1008,1,35000,150000 +004-3-2,81,58,38,30 monster Angry Scorpion 1131,30,35000,150000 +004-3-2,124,52,12,25 monster Black Scorpion 1074,1,35000,150000 +004-3-2,85,58,51,35 monster Nigth Scorpion 1077,1,35000,150000 +004-3-2,64,55,24,10 monster Red scorpion 1072,8,35000,150000 +004-3-2,69,59,6,22 monster Bat 1039,2,35000,150000 +004-3-2,59,52,4,2 monster Maggot 1030,2,35000,150000 +004-3-2,74,39,4,2 monster Maggot 1030,2,35000,150000 +004-3-2,95,52,4,2 monster Maggot 1030,2,35000,150000 +004-3-2,112,84,23,20 monster Plushroom Field 1011,3,35000,150000 +004-3-2,0,0,0,0 monster Ruby Bif 1099,4,35000,150000 diff --git a/npc/004-3-2/_warps.txt b/npc/004-3-2/_warps.txt new file mode 100644 index 0000000..1472cd0 --- /dev/null +++ b/npc/004-3-2/_warps.txt @@ -0,0 +1,13 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-2: Canyon Cave warps +004-3-2,126,95,0 warp #004-3-2_126_95 0,0,010-2-12,39,21 +004-3-2,95,50,0 warp #004-3-2_95_50 0,0,004-2,119,106 +004-3-2,44,38,0 warp #004-3-2_44_38 0,0,004-2,81,94 +004-3-2,59,55,0 warp #004-3-2_59_55 0,0,004-2,86,117 +004-3-2,83,64,0 warp #004-3-2_83_64 0,0,004-2,113,121 +004-3-2,62,34,0 warp #004-3-2_62_34 0,0,004-3-3,61,28 +004-3-2,124,35,0 warp #004-3-2_124_35 0,0,004-2-3,102,62 +004-3-2,129,63,0 warp #004-3-2_129_63 0,0,004-3-4,83,21 +004-3-2,80,79,0 warp #004-3-2_80_79 0,0,004-3-4,33,39 +004-3-2,43,83,0 warp #004-3-2_43_83 0,0,004-2-12,36,51 +004-3-2,74,42,0 warp #004-3-2_74_42 0,0,004-2,104,94 diff --git a/npc/004-3-3/_import.txt b/npc/004-3-3/_import.txt new file mode 100644 index 0000000..583e110 --- /dev/null +++ b/npc/004-3-3/_import.txt @@ -0,0 +1,4 @@ +// Map 004-3-3: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-3-3/_mobs.txt", +"npc/004-3-3/_warps.txt", diff --git a/npc/004-3-3/_mobs.txt b/npc/004-3-3/_mobs.txt new file mode 100644 index 0000000..fcfdc87 --- /dev/null +++ b/npc/004-3-3/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-3: Canyon Cave mobs +004-3-3,0,0,0,0 monster Cave Maggot 1027,7,35000,150000 +004-3-3,46,29,11,7 monster Scorpion 1071,14,35000,150000 +004-3-3,0,0,0,0 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/004-3-3/_warps.txt b/npc/004-3-3/_warps.txt new file mode 100644 index 0000000..eaa2a51 --- /dev/null +++ b/npc/004-3-3/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-3: Canyon Cave warps +004-3-3,33,32,0 warp #004-3-3_33_32 0,0,004-3-6,61,47 +004-3-3,61,29,0 warp #004-3-3_61_29 0,0,004-3-2,62,35 diff --git a/npc/004-3-4/_import.txt b/npc/004-3-4/_import.txt new file mode 100644 index 0000000..da511cd --- /dev/null +++ b/npc/004-3-4/_import.txt @@ -0,0 +1,4 @@ +// Map 004-3-4: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-3-4/_mobs.txt", +"npc/004-3-4/_warps.txt", diff --git a/npc/004-3-4/_mobs.txt b/npc/004-3-4/_mobs.txt new file mode 100644 index 0000000..c0368a1 --- /dev/null +++ b/npc/004-3-4/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-4: Canyon Cave mobs +004-3-4,59,29,21,10 monster Bat 1039,4,35000,150000 +004-3-4,60,29,31,10 monster Cave Snake 1035,2,35000,150000 +004-3-4,76,28,13,11 monster Black Scorpion 1074,1,35000,150000 +004-3-4,36,34,8,6 monster Angry Scorpion 1131,1,35000,150000 +004-3-4,0,0,0,0 monster Cave Maggot 1027,1,35000,150000 +004-3-4,0,0,0,0 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/004-3-4/_warps.txt b/npc/004-3-4/_warps.txt new file mode 100644 index 0000000..7c34841 --- /dev/null +++ b/npc/004-3-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-4: Canyon Cave warps +004-3-4,33,40,0 warp #004-3-4_33_40 0,0,004-3-2,80,80 +004-3-4,83,20,0 warp #004-3-4_83_20 0,0,004-3-2,129,64 diff --git a/npc/004-3-5/_import.txt b/npc/004-3-5/_import.txt new file mode 100644 index 0000000..933cc4c --- /dev/null +++ b/npc/004-3-5/_import.txt @@ -0,0 +1,4 @@ +// Map 004-3-5: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-3-5/_mobs.txt", +"npc/004-3-5/_warps.txt", diff --git a/npc/004-3-5/_mobs.txt b/npc/004-3-5/_mobs.txt new file mode 100644 index 0000000..580b6b3 --- /dev/null +++ b/npc/004-3-5/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-5: Canyon Cave mobs +004-3-5,34,31,4,10 monster Angry Red Scorpion 1130,14,35000,150000 +004-3-5,0,0,0,0 monster Cave Maggot 1027,3,35000,150000 +004-3-5,0,0,0,0 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/004-3-5/_warps.txt b/npc/004-3-5/_warps.txt new file mode 100644 index 0000000..20de5e4 --- /dev/null +++ b/npc/004-3-5/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-5: Canyon Cave warps +004-3-5,36,45,0 warp #004-3-5_36_45 0,0,007-1-2,48,21 +004-3-5,34,20,0 warp #004-3-5_34_20 0,0,004-2-9,36,56 diff --git a/npc/004-3-6/_import.txt b/npc/004-3-6/_import.txt new file mode 100644 index 0000000..1c892a6 --- /dev/null +++ b/npc/004-3-6/_import.txt @@ -0,0 +1,4 @@ +// Map 004-3-6: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-3-6/_mobs.txt", +"npc/004-3-6/_warps.txt", diff --git a/npc/004-3-6/_mobs.txt b/npc/004-3-6/_mobs.txt new file mode 100644 index 0000000..fc8b418 --- /dev/null +++ b/npc/004-3-6/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-6: Canyon Cave mobs +004-3-6,45,42,11,14 monster Bat 1039,6,35000,150000 +004-3-6,42,49,12,8 monster Black Scorpion 1074,3,35000,150000 +004-3-6,0,0,0,0 monster Cave Maggot 1027,7,35000,150000 +004-3-6,49,38,15,15 monster Angry Scorpion 1131,6,35000,150000 +004-3-6,0,0,0,0 monster Topaz Bif 1102,1,35000,150000 diff --git a/npc/004-3-6/_warps.txt b/npc/004-3-6/_warps.txt new file mode 100644 index 0000000..f0b1305 --- /dev/null +++ b/npc/004-3-6/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3-6: Canyon Cave warps +004-3-6,42,20,0 warp #004-3-6_42_20 0,0,007-1-1,45,47 +004-3-6,61,48,0 warp #004-3-6_61_48 0,0,004-3-3,33,33 diff --git a/npc/004-3/_import.txt b/npc/004-3/_import.txt new file mode 100644 index 0000000..7cabd31 --- /dev/null +++ b/npc/004-3/_import.txt @@ -0,0 +1,6 @@ +// Map 004-3: Pachua's Village +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/004-3/_mobs.txt", +"npc/004-3/_warps.txt", +"npc/004-3/darug.txt", +"npc/004-3/melune.txt", diff --git a/npc/004-3/_mobs.txt b/npc/004-3/_mobs.txt new file mode 100644 index 0000000..579ebad --- /dev/null +++ b/npc/004-3/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3: Pachua's Village mobs +004-3,57,39,8,2 monster Maggot 1030,2,35000,150000 +004-3,34,30,4,7 monster Ratto 1005,1,35000,150000 diff --git a/npc/004-3/_warps.txt b/npc/004-3/_warps.txt new file mode 100644 index 0000000..f293db7 --- /dev/null +++ b/npc/004-3/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 004-3: Pachua's Village warps +004-3,53,42,0 warp #004-3_53_42 0,0,004-2,53,89 +004-3,63,42,0 warp #004-3_63_42 0,0,004-2,63,89 diff --git a/npc/004-3/darug.txt b/npc/004-3/darug.txt new file mode 100644 index 0000000..badfd87 --- /dev/null +++ b/npc/004-3/darug.txt @@ -0,0 +1,26 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Part of Ancient Temple Quest + +004-3,81,22,0 script Darug NPC_INDIGENOUS_CHIEF,{ + mesn; + mesq l("Greetings, traveler. I am Darug, chief of this clan."); + next; + mesn; + mesq l("Since the war outbreak, we have to keep moving constantly. Without strong walls to protect ourselves, though, our clan is on the verge of extinction."); + next; + mesn; + mesq l("We know a lot, but all this knowledge will part with us. We should not the only nomads, though. There should be nomads in Argaes, Kaizei, besides the citadels of Oceania and Volcania. Unfortunately, all of them are incommunicable. But so are us."); + next; + mesn; + mesq l("So, please, make the most of your stay and our leather works before this knowledge disappear from the world!"); + close; + +OnInit: + .distance = 4; + .sex = G_MALE; + end; +} + diff --git a/npc/004-3/melune.txt b/npc/004-3/melune.txt new file mode 100644 index 0000000..0f498fc --- /dev/null +++ b/npc/004-3/melune.txt @@ -0,0 +1,20 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Leatherwork: Cowboy Hats and Jeans Chaps. Mother of Nowhere Family. + +004-3,85,22,0 script Melune NPC_INDIGENOUS_WIFE,{ + mesn; + mesq l("Greetings, traveler. My name is Melune, and I'm Darug's wife."); + next; + mesn; + mesq l("My children and grandchildren have all moved to the Land of Fire, so the amount of leatherwork I can offer you is limited."); + close; + +OnInit: + .distance = 4; + .sex = G_FEMALE; + end; +} + diff --git a/npc/005-1-1/_import.txt b/npc/005-1-1/_import.txt new file mode 100644 index 0000000..9430192 --- /dev/null +++ b/npc/005-1-1/_import.txt @@ -0,0 +1,3 @@ +// Map 005-1-1: Candor Island +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-1-1/main.txt", diff --git a/npc/005-1-1/main.txt b/npc/005-1-1/main.txt new file mode 100644 index 0000000..93db929 --- /dev/null +++ b/npc/005-1-1/main.txt @@ -0,0 +1,453 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Candor - Maggot Extermination Quest + +// Added for GonzoDark + +005-1-1,30,20,0 script Nylo#Marggo NPC_PLAYER,{ + // Returns the crop ID to disable + // getcropid(x,y) + function getcropid { + .@x=getarg(0); + .@y=getarg(1); + .@w=12; // Width of a row + .@offx=23; // First X + .@offy=22; // First Y + .@i=0; + // 0 is: 23,22 + // width: 35-23=12 + // height is not relevant + + // .@x / .@y + // .@w = 12 + // .@i → Real value + + // For each Y beyond .@offy we add .@w to i + .@i+=(.@y-.@offy)*.@w; + // Now we add any X beyond .@offx + .@i+=(.@x-.@offx); + // I don't know why this offset happens, but it happens + .@i+=(.@y-.@offy); + // Do not go out of scope if maggot wander outside the area + return(sprintf("%03d", max(0, min(.@i, 168)))); + } + + npctalk l("Go kill the maggots!"); + end; + +OnStart: + //initnpctimer; + if ($@GM_OVERRIDE) { + addtimer(400, "Nylo#Marggo::OnTimer55000"); + addtimer(5000, "Nylo#Marggo::OnTimer60000"); + addtimer(5300, "Nylo#Marggo::OnBegin"); + end; + } + addtimer(10000, "Nylo#Marggo::OnTimer10000"); + addtimer(20000, "Nylo#Marggo::OnTimer20000"); + addtimer(30000, "Nylo#Marggo::OnTimer30000"); + addtimer(40000, "Nylo#Marggo::OnTimer40000"); + addtimer(50000, "Nylo#Marggo::OnTimer50000"); + addtimer(55000, "Nylo#Marggo::OnTimer55000"); + addtimer(60000, "Nylo#Marggo::OnTimer60000"); + addtimer(60300, "Nylo#Marggo::OnBegin"); + mesn; + mesq l("The maggots will spawn in @@ at random.", b(l("60 seconds"))); + mes l("Read the instructions carefully."); + next; + mesn l("Time Limit"); + mes l("Campaign starts after 60 seconds. Eight maggots will spawn on the next 100 seconds."); + mes l("Four minutes after the warp, or when the last maggot die, it'll be over."); + next; + mesn l("Victory Conditions"); + mes l("If you save 80% from the crops or more, you'll get @@ GP.", 300); + mes l("If you save 90% from the crops or more, you'll also get a @@.", getitemlink(RoundLeatherShield)); + mes l("== If you decide to complete the quest you cannot repeat it."); + next; + mes l("Good luck!"); + close; + +OnTimer10000: + dispbottom "50 seconds!"; + end; + +OnTimer20000: + dispbottom "40 seconds!"; + end; + +OnTimer30000: + dispbottom "30 seconds!"; + end; + +OnTimer40000: + dispbottom "20 seconds!"; + end; + +OnTimer50000: + dispbottom "10 seconds!"; + end; + +OnTimer55000: + dispbottom "5 seconds!"; + end; + +OnTimer60000: + dispbottom l("Start!"); + //stopnpctimer; + end; + +OnBegin: + // @MARGGO → (MaggotID, IsAlive) + // IsAlive: 1 → yes + @MARGGO=htnew; + @TOTAL_MRG=0; + //@DESTROY=0; // Array of all lost crops + // 8 maggots + // Minimum 80 sec + // Maximum 100 sec + addtimer(10, "Nylo#Marggo::OnSpawn"); + addtimer(400, "Nylo#Marggo::OnInteract"); + addtimer(180000, "Nylo#Marggo::OnFinish"); + end; + +// Spawn a new maggot until eight maggots were spawn +OnSpawn: + .@ID=getcharid(0); + .@MAP$="MRGO@"+str(.@ID); + @TOTAL_MRG+=1; + if (@TOTAL_MRG < 8) + addtimer(rand(10000, 12500), "Nylo#Marggo::OnSpawn"); + if (mobcount(getmap(), "Nylo#Marggo::OnMurder") >= 4) + end; + + .@mobID=areamonster(.@MAP$, 23, 22, 35, 34, l("Maggot"), Maggot, 1, "Nylo#Marggo::OnMurder"); + htput @MARGGO, str(.@mobID), 1; + + // 50% chances to spawn two maggots at once + if (any(true, false) && @TOTAL_MRG < 8) { + @TOTAL_MRG+=1; + .@mobID=areamonster(.@MAP$, 23, 22, 35, 34, l("Maggot"), Maggot, 1, "Nylo#Marggo::OnMurder"); + htput @MARGGO, str(.@mobID), 1; + } + end; + +// Move all maggots +OnInteract: + .@hti = htiterator(@MARGGO); + for(.@key$ = htinextkey(.@hti); hticheck(.@hti); .@key$ = htinextkey(.@hti)) { + .@alive=htget(@MARGGO, .@key$); + /* + if (.@alive == 2) + continue; + */ + .@mobId=atoi(.@key$); + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, .@mobId); + //debugmes("[LOGIC] -------------------------- proccess %d", .@mobId); + + // Destroy the crop if needed + .@cropId$=getcropid(.@x, .@y); + if (array_find(@DESTROYED$, .@cropId$) == -1) { + array_push(@DESTROYED$, .@cropId$); + //debugmes("[LOGIC] -------------------------- crop destroyed"); + // hideonnpc + disablenpc instance_npcname("#Marggo"+.@cropId$, MARGGO_ID); + } + + /* + // Tell the Maggot to walk to a random coordinate + // (it may walk backwards due not preserving old data) + // This will cause maggot to abort a counter attack and to never stop + // If it hits an edge, it'll start moving to the center, may cause odd behavior + unitstop(.@mobId); + if (.@x == 22 || .@y == 22 || .@x == 36 || .@y == 34) + unitwalk(.@mobId, 30, 30); + else + unitwalk(.@mobId, .@x+any(1, -1), .@y+any(1, -1)); + */ + //debugmes("[LOGIC] ------------------------------------- ai advanced"); + } + htidelete(.@hti); + + // Continue the cycle + if (compare(getmap(), "MRGO") || compare(getmap(), "005-1-1")) + addtimer(400, "Nylo#Marggo::OnInteract"); + end; + +// A maggot was killed +OnMurder: + // I don't know which one was killed, so I cycle every one until I find the dead one + .@hti = htiterator(@MARGGO); + for(.@key$ = htinextkey(.@hti); hticheck(.@hti); .@key$ = htinextkey(.@hti)) { + .@alive=htget(@MARGGO, .@key$); + /* + if (.@alive == 2) + continue; + */ + .@mobId=atoi(.@key$); + if (getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, .@mobId) >= 0) + continue; + + // We found the dead monster, mark it as dead and stop this meaningless loop + htput @MARGGO, .@key$, 0; + //debugmes("[OK] Deleting Maggot"); + break; + } + htidelete(.@hti); + if (@TOTAL_MRG >= 8) { + //debugmes("Current MARGGO Size: %d", htsize(@MARGGO)); + if (htsize(@MARGGO) <= 1) + doevent("Nylo#Marggo::OnFinish"); + } + end; + +// You killed all maggots or ran out of time +OnFinish: + htdelete @MARGGO; + deltimer("Nylo#Marggo::OnInteract"); + deltimer("Nylo#Marggo::OnSpawn"); + deltimer("Nylo#Marggo::OnFinish"); + MARGGO_ID=0; + .@trueratio=100*getarraysize(@DESTROYED$)/169; + .@ratio=100-.@trueratio; + debugmes("Player lost %d crops", getarraysize(@DESTROYED$)); + deletearray(@DESTROYED$); + + mesn l("Nylo"); + mes l("You managed to save @@ % of my crops!", .@ratio); + next; + if (.@ratio >= 80) { + inventoryplace RoundLeatherShield, 1; + mesc l("Complete Quest?"); + mesc l("You won't be able to aim for a better record!"); + menuint + l("Yes, I'm done with it."), ASK_YES, + l("No, I want to aim for a higher score."), ASK_NO; + mes ""; + if (@menuret == ASK_YES) { + setq CandorQuest_Marggo, 1; + Zeny=Zeny+300; + getexp .@ratio*8/10, (.@ratio/3); + if (.@ratio >= 90) + getitem RoundLeatherShield, 1; + } + mes ""; + } + mesn l("Nylo"); + mes l("Thanks for the help!"); + warp "005-5", 25, 41; + close; + +// Cosmetic +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, CreasedShirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShorts); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 0); + + .sex = G_MALE; + .distance = 4; + end; +OnInstanceInit: + .@npcId = getnpcid(instance_npcname(.name$)); + setunitdata(.@npcId, UDT_HEADTOP, CreasedShirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShorts); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 0); + end; + +} + +/* python +i=0 +x=23 +y=22 +x2=35 +y2=34 +while y <= y2: + print("005-1-1,%d,%d,0 script #Marggo%03d NPC_CROPS,{ end; }"% (x, y, i)) + x+=1 + if (x > x2): + x=23 + y+=1 + i+=1 + +*/ + +005-1-1,23,22,0 script #Marggo000 NPC_CROPS,{ end; } +005-1-1,24,22,0 script #Marggo001 NPC_CROPS,{ end; } +005-1-1,25,22,0 script #Marggo002 NPC_CROPS,{ end; } +005-1-1,26,22,0 script #Marggo003 NPC_CROPS,{ end; } +005-1-1,27,22,0 script #Marggo004 NPC_CROPS,{ end; } +005-1-1,28,22,0 script #Marggo005 NPC_CROPS,{ end; } +005-1-1,29,22,0 script #Marggo006 NPC_CROPS,{ end; } +005-1-1,30,22,0 script #Marggo007 NPC_CROPS,{ end; } +005-1-1,31,22,0 script #Marggo008 NPC_CROPS,{ end; } +005-1-1,32,22,0 script #Marggo009 NPC_CROPS,{ end; } +005-1-1,33,22,0 script #Marggo010 NPC_CROPS,{ end; } +005-1-1,34,22,0 script #Marggo011 NPC_CROPS,{ end; } +005-1-1,35,22,0 script #Marggo012 NPC_CROPS,{ end; } +005-1-1,23,23,0 script #Marggo013 NPC_CROPS,{ end; } +005-1-1,24,23,0 script #Marggo014 NPC_CROPS,{ end; } +005-1-1,25,23,0 script #Marggo015 NPC_CROPS,{ end; } +005-1-1,26,23,0 script #Marggo016 NPC_CROPS,{ end; } +005-1-1,27,23,0 script #Marggo017 NPC_CROPS,{ end; } +005-1-1,28,23,0 script #Marggo018 NPC_CROPS,{ end; } +005-1-1,29,23,0 script #Marggo019 NPC_CROPS,{ end; } +005-1-1,30,23,0 script #Marggo020 NPC_CROPS,{ end; } +005-1-1,31,23,0 script #Marggo021 NPC_CROPS,{ end; } +005-1-1,32,23,0 script #Marggo022 NPC_CROPS,{ end; } +005-1-1,33,23,0 script #Marggo023 NPC_CROPS,{ end; } +005-1-1,34,23,0 script #Marggo024 NPC_CROPS,{ end; } +005-1-1,35,23,0 script #Marggo025 NPC_CROPS,{ end; } +005-1-1,23,24,0 script #Marggo026 NPC_CROPS,{ end; } +005-1-1,24,24,0 script #Marggo027 NPC_CROPS,{ end; } +005-1-1,25,24,0 script #Marggo028 NPC_CROPS,{ end; } +005-1-1,26,24,0 script #Marggo029 NPC_CROPS,{ end; } +005-1-1,27,24,0 script #Marggo030 NPC_CROPS,{ end; } +005-1-1,28,24,0 script #Marggo031 NPC_CROPS,{ end; } +005-1-1,29,24,0 script #Marggo032 NPC_CROPS,{ end; } +005-1-1,30,24,0 script #Marggo033 NPC_CROPS,{ end; } +005-1-1,31,24,0 script #Marggo034 NPC_CROPS,{ end; } +005-1-1,32,24,0 script #Marggo035 NPC_CROPS,{ end; } +005-1-1,33,24,0 script #Marggo036 NPC_CROPS,{ end; } +005-1-1,34,24,0 script #Marggo037 NPC_CROPS,{ end; } +005-1-1,35,24,0 script #Marggo038 NPC_CROPS,{ end; } +005-1-1,23,25,0 script #Marggo039 NPC_CROPS,{ end; } +005-1-1,24,25,0 script #Marggo040 NPC_CROPS,{ end; } +005-1-1,25,25,0 script #Marggo041 NPC_CROPS,{ end; } +005-1-1,26,25,0 script #Marggo042 NPC_CROPS,{ end; } +005-1-1,27,25,0 script #Marggo043 NPC_CROPS,{ end; } +005-1-1,28,25,0 script #Marggo044 NPC_CROPS,{ end; } +005-1-1,29,25,0 script #Marggo045 NPC_CROPS,{ end; } +005-1-1,30,25,0 script #Marggo046 NPC_CROPS,{ end; } +005-1-1,31,25,0 script #Marggo047 NPC_CROPS,{ end; } +005-1-1,32,25,0 script #Marggo048 NPC_CROPS,{ end; } +005-1-1,33,25,0 script #Marggo049 NPC_CROPS,{ end; } +005-1-1,34,25,0 script #Marggo050 NPC_CROPS,{ end; } +005-1-1,35,25,0 script #Marggo051 NPC_CROPS,{ end; } +005-1-1,23,26,0 script #Marggo052 NPC_CROPS,{ end; } +005-1-1,24,26,0 script #Marggo053 NPC_CROPS,{ end; } +005-1-1,25,26,0 script #Marggo054 NPC_CROPS,{ end; } +005-1-1,26,26,0 script #Marggo055 NPC_CROPS,{ end; } +005-1-1,27,26,0 script #Marggo056 NPC_CROPS,{ end; } +005-1-1,28,26,0 script #Marggo057 NPC_CROPS,{ end; } +005-1-1,29,26,0 script #Marggo058 NPC_CROPS,{ end; } +005-1-1,30,26,0 script #Marggo059 NPC_CROPS,{ end; } +005-1-1,31,26,0 script #Marggo060 NPC_CROPS,{ end; } +005-1-1,32,26,0 script #Marggo061 NPC_CROPS,{ end; } +005-1-1,33,26,0 script #Marggo062 NPC_CROPS,{ end; } +005-1-1,34,26,0 script #Marggo063 NPC_CROPS,{ end; } +005-1-1,35,26,0 script #Marggo064 NPC_CROPS,{ end; } +005-1-1,23,27,0 script #Marggo065 NPC_CROPS,{ end; } +005-1-1,24,27,0 script #Marggo066 NPC_CROPS,{ end; } +005-1-1,25,27,0 script #Marggo067 NPC_CROPS,{ end; } +005-1-1,26,27,0 script #Marggo068 NPC_CROPS,{ end; } +005-1-1,27,27,0 script #Marggo069 NPC_CROPS,{ end; } +005-1-1,28,27,0 script #Marggo070 NPC_CROPS,{ end; } +005-1-1,29,27,0 script #Marggo071 NPC_CROPS,{ end; } +005-1-1,30,27,0 script #Marggo072 NPC_CROPS,{ end; } +005-1-1,31,27,0 script #Marggo073 NPC_CROPS,{ end; } +005-1-1,32,27,0 script #Marggo074 NPC_CROPS,{ end; } +005-1-1,33,27,0 script #Marggo075 NPC_CROPS,{ end; } +005-1-1,34,27,0 script #Marggo076 NPC_CROPS,{ end; } +005-1-1,35,27,0 script #Marggo077 NPC_CROPS,{ end; } +005-1-1,23,28,0 script #Marggo078 NPC_CROPS,{ end; } +005-1-1,24,28,0 script #Marggo079 NPC_CROPS,{ end; } +005-1-1,25,28,0 script #Marggo080 NPC_CROPS,{ end; } +005-1-1,26,28,0 script #Marggo081 NPC_CROPS,{ end; } +005-1-1,27,28,0 script #Marggo082 NPC_CROPS,{ end; } +005-1-1,28,28,0 script #Marggo083 NPC_CROPS,{ end; } +005-1-1,29,28,0 script #Marggo084 NPC_CROPS,{ end; } +005-1-1,30,28,0 script #Marggo085 NPC_CROPS,{ end; } +005-1-1,31,28,0 script #Marggo086 NPC_CROPS,{ end; } +005-1-1,32,28,0 script #Marggo087 NPC_CROPS,{ end; } +005-1-1,33,28,0 script #Marggo088 NPC_CROPS,{ end; } +005-1-1,34,28,0 script #Marggo089 NPC_CROPS,{ end; } +005-1-1,35,28,0 script #Marggo090 NPC_CROPS,{ end; } +005-1-1,23,29,0 script #Marggo091 NPC_CROPS,{ end; } +005-1-1,24,29,0 script #Marggo092 NPC_CROPS,{ end; } +005-1-1,25,29,0 script #Marggo093 NPC_CROPS,{ end; } +005-1-1,26,29,0 script #Marggo094 NPC_CROPS,{ end; } +005-1-1,27,29,0 script #Marggo095 NPC_CROPS,{ end; } +005-1-1,28,29,0 script #Marggo096 NPC_CROPS,{ end; } +005-1-1,29,29,0 script #Marggo097 NPC_CROPS,{ end; } +005-1-1,30,29,0 script #Marggo098 NPC_CROPS,{ end; } +005-1-1,31,29,0 script #Marggo099 NPC_CROPS,{ end; } +005-1-1,32,29,0 script #Marggo100 NPC_CROPS,{ end; } +005-1-1,33,29,0 script #Marggo101 NPC_CROPS,{ end; } +005-1-1,34,29,0 script #Marggo102 NPC_CROPS,{ end; } +005-1-1,35,29,0 script #Marggo103 NPC_CROPS,{ end; } +005-1-1,23,30,0 script #Marggo104 NPC_CROPS,{ end; } +005-1-1,24,30,0 script #Marggo105 NPC_CROPS,{ end; } +005-1-1,25,30,0 script #Marggo106 NPC_CROPS,{ end; } +005-1-1,26,30,0 script #Marggo107 NPC_CROPS,{ end; } +005-1-1,27,30,0 script #Marggo108 NPC_CROPS,{ end; } +005-1-1,28,30,0 script #Marggo109 NPC_CROPS,{ end; } +005-1-1,29,30,0 script #Marggo110 NPC_CROPS,{ end; } +005-1-1,30,30,0 script #Marggo111 NPC_CROPS,{ end; } +005-1-1,31,30,0 script #Marggo112 NPC_CROPS,{ end; } +005-1-1,32,30,0 script #Marggo113 NPC_CROPS,{ end; } +005-1-1,33,30,0 script #Marggo114 NPC_CROPS,{ end; } +005-1-1,34,30,0 script #Marggo115 NPC_CROPS,{ end; } +005-1-1,35,30,0 script #Marggo116 NPC_CROPS,{ end; } +005-1-1,23,31,0 script #Marggo117 NPC_CROPS,{ end; } +005-1-1,24,31,0 script #Marggo118 NPC_CROPS,{ end; } +005-1-1,25,31,0 script #Marggo119 NPC_CROPS,{ end; } +005-1-1,26,31,0 script #Marggo120 NPC_CROPS,{ end; } +005-1-1,27,31,0 script #Marggo121 NPC_CROPS,{ end; } +005-1-1,28,31,0 script #Marggo122 NPC_CROPS,{ end; } +005-1-1,29,31,0 script #Marggo123 NPC_CROPS,{ end; } +005-1-1,30,31,0 script #Marggo124 NPC_CROPS,{ end; } +005-1-1,31,31,0 script #Marggo125 NPC_CROPS,{ end; } +005-1-1,32,31,0 script #Marggo126 NPC_CROPS,{ end; } +005-1-1,33,31,0 script #Marggo127 NPC_CROPS,{ end; } +005-1-1,34,31,0 script #Marggo128 NPC_CROPS,{ end; } +005-1-1,35,31,0 script #Marggo129 NPC_CROPS,{ end; } +005-1-1,23,32,0 script #Marggo130 NPC_CROPS,{ end; } +005-1-1,24,32,0 script #Marggo131 NPC_CROPS,{ end; } +005-1-1,25,32,0 script #Marggo132 NPC_CROPS,{ end; } +005-1-1,26,32,0 script #Marggo133 NPC_CROPS,{ end; } +005-1-1,27,32,0 script #Marggo134 NPC_CROPS,{ end; } +005-1-1,28,32,0 script #Marggo135 NPC_CROPS,{ end; } +005-1-1,29,32,0 script #Marggo136 NPC_CROPS,{ end; } +005-1-1,30,32,0 script #Marggo137 NPC_CROPS,{ end; } +005-1-1,31,32,0 script #Marggo138 NPC_CROPS,{ end; } +005-1-1,32,32,0 script #Marggo139 NPC_CROPS,{ end; } +005-1-1,33,32,0 script #Marggo140 NPC_CROPS,{ end; } +005-1-1,34,32,0 script #Marggo141 NPC_CROPS,{ end; } +005-1-1,35,32,0 script #Marggo142 NPC_CROPS,{ end; } +005-1-1,23,33,0 script #Marggo143 NPC_CROPS,{ end; } +005-1-1,24,33,0 script #Marggo144 NPC_CROPS,{ end; } +005-1-1,25,33,0 script #Marggo145 NPC_CROPS,{ end; } +005-1-1,26,33,0 script #Marggo146 NPC_CROPS,{ end; } +005-1-1,27,33,0 script #Marggo147 NPC_CROPS,{ end; } +005-1-1,28,33,0 script #Marggo148 NPC_CROPS,{ end; } +005-1-1,29,33,0 script #Marggo149 NPC_CROPS,{ end; } +005-1-1,30,33,0 script #Marggo150 NPC_CROPS,{ end; } +005-1-1,31,33,0 script #Marggo151 NPC_CROPS,{ end; } +005-1-1,32,33,0 script #Marggo152 NPC_CROPS,{ end; } +005-1-1,33,33,0 script #Marggo153 NPC_CROPS,{ end; } +005-1-1,34,33,0 script #Marggo154 NPC_CROPS,{ end; } +005-1-1,35,33,0 script #Marggo155 NPC_CROPS,{ end; } +005-1-1,23,34,0 script #Marggo156 NPC_CROPS,{ end; } +005-1-1,24,34,0 script #Marggo157 NPC_CROPS,{ end; } +005-1-1,25,34,0 script #Marggo158 NPC_CROPS,{ end; } +005-1-1,26,34,0 script #Marggo159 NPC_CROPS,{ end; } +005-1-1,27,34,0 script #Marggo160 NPC_CROPS,{ end; } +005-1-1,28,34,0 script #Marggo161 NPC_CROPS,{ end; } +005-1-1,29,34,0 script #Marggo162 NPC_CROPS,{ end; } +005-1-1,30,34,0 script #Marggo163 NPC_CROPS,{ end; } +005-1-1,31,34,0 script #Marggo164 NPC_CROPS,{ end; } +005-1-1,32,34,0 script #Marggo165 NPC_CROPS,{ end; } +005-1-1,33,34,0 script #Marggo166 NPC_CROPS,{ end; } +005-1-1,34,34,0 script #Marggo167 NPC_CROPS,{ end; } +005-1-1,35,34,0 script #Marggo168 NPC_CROPS,{ end; } + + diff --git a/npc/005-1/_import.txt b/npc/005-1/_import.txt new file mode 100644 index 0000000..d7d03ba --- /dev/null +++ b/npc/005-1/_import.txt @@ -0,0 +1,16 @@ +// Map 005-1: Candor Island +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-1/_mobs.txt", +"npc/005-1/_warps.txt", +"npc/005-1/ayasha.txt", +"npc/005-1/liana.txt", +"npc/005-1/mapflags.txt", +"npc/005-1/maya.txt", +"npc/005-1/sailors.txt", +"npc/005-1/saxso.txt", +"npc/005-1/ship.txt", +"npc/005-1/soul-menhir.txt", +"npc/005-1/underground.txt", +"npc/005-1/vincent.txt", +"npc/005-1/wateranimation.txt", +"npc/005-1/zegas.txt", diff --git a/npc/005-1/_mobs.txt b/npc/005-1/_mobs.txt new file mode 100644 index 0000000..ed13f97 --- /dev/null +++ b/npc/005-1/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-1: Candor Island mobs +005-1,43,50,2,1 monster Clover Field 1028,1,35000,300000 +005-1,30,66,1,0 monster Diamond Bif 1108,1,35000,90000,Rosen::OnKillMBif +005-1,70,94,7,6 monster Maggot 1030,9,35000,300000,Trainer::OnKillMaggot +005-1,89,97,8,9 monster Candor Scorpion 1073,7,35000,300000,Trainer::OnKillCandorScorpion +005-1,43,115,6,3 monster Piou 1002,1,35000,300000 +005-1,87,43,16,15 monster Candor Scorpion 1073,12,35000,300000,Trainer::OnKillCandorScorpion +005-1,78,49,28,10 monster Mana Bug 1075,5,35000,300000,Trainer::OnKillManaBug +005-1,37,49,12,14 monster Scorpion 1071,2,35000,300000,Trainer::OnKillScorpion diff --git a/npc/005-1/_warps.txt b/npc/005-1/_warps.txt new file mode 100644 index 0000000..4c19648 --- /dev/null +++ b/npc/005-1/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-1: Candor Island warps +005-1,38,89,0 warp #005-1_38_89 0,0,005-2,33,42 +005-1,41,84,0 warp #005-1_41_84 0,0,005-3,25,42 +005-1,55,80,0 warp #005-1_55_80 0,0,005-4,25,42 +005-1,46,78,0 warp #005-1_46_78 0,0,005-5,25,41 +005-1,34,99,0 warp #005-1_34_99 0,0,005-7,45,37 +005-1,50,85,0 warp #005-1_50_85 0,0,005-6,33,42 diff --git a/npc/005-1/ayasha.txt b/npc/005-1/ayasha.txt new file mode 100644 index 0000000..6789f42 --- /dev/null +++ b/npc/005-1/ayasha.txt @@ -0,0 +1,438 @@ +// Author: +// Crazyfefe +// Jesusalva +// Description: +// Ayasha takes care of the children of Candor Village, and is in charge of protecting them. +// The kids sometimes go play outside of city walls, and this makes her upset. +// Even if no major attack happened in Candor for a while. +// Variables: +// CandorQuest_HAS +// 0: Not met +// 1: Already met +// 2: Quest ongoing +// 3: Found all kids +// 4: Reward given + +005-1,59,91,0 script Ayasha NPC_HUMAN_FEMALE_NOOB,{ + showavatar NPC_HUMAN_FEMALE_NOOB; // this is handled by avatars.xml + if (strcharinfo(2) == "Monster King" && is_master()) goto L_MKControl; + + function quest_findAllKids { + setq CandorQuest_HAS, 2; + mesn; + mesq l("Good luck!"); + close; + } + + .@has = getq(CandorQuest_HAS); + if (.@has == 0) { + speech S_LAST_NEXT, + l("Ah, the kids are playing hide and seek, but I am afraid they went too far. A monster attack could start anytime, after all."), + l("I am currently very worried with them. They're just children! They don't know how to fight!"), + l("Could you perhaps help me to find all kids?"); + do + { + select + l("Yes!"), + l("I can't, sorry."); + + setq CandorQuest_HAS, 1; + switch (@menu) { + case 1: + quest_findAllKids; + break; + } + } while (@menu != 2); + } else if (.@has == 1) { + mesn; + mesq l("Even if the city has not been attacked on the last few years, I can't help but be concerned while the kids are playing hide and seek."); + next; + mesq l("Could you perhaps help me to find all kids?"); + next; + + do + { + select + l("Yes!"), + l("I can't, sorry."); + + switch (@menu) { + case 1: + quest_findAllKids; + break; + } + } while (@menu != 2); + } else if (.@has == 2) { + mesn; + mesq l("You still haven't found all of them yet."); + close; + } else if (.@has == 3) { + speech S_LAST_NEXT, + l("Thank you, here is your reward and... some pocket money."); + narrator("You receive 30 exp and 50 GP."); + getexp 32, 0; + Zeny = (Zeny + 50); + setq CandorQuest_HAS, 4; + close; + } else { + mesn; + mesq l("Thank you for your help."); + if (BaseLevel >= 24 && gettimetick(2) > .RENT_TIME) goto L_Sword; + close; + } + + //closedialog; + //goodbye; + close; + +L_Sword: + menu + l("The children are safe, could you rent me your sword?"), L_Rent, + l("You're welcome!"), -; + close; + +L_Rent: + // This code is equivalent to: speech S_FIRST_BLANK_LINE | S_LAST_NEXT + mes ""; + mesn; + mesq l("Ah, I guess you want to fight at the cave north of me..."); + next; + mesq l("The children are safe, aren't they...?"); + next; + mesq l("I can rent it to you for 300 GP, during 15 minutes. Deal?"); + next; + menu + rif(Zeny >= 300 && gettimetick(2) > .RENT_TIME, l("Deal!")), L_DoRent, + l("Maybe later."), -; + close; + +L_DoRent: + if (gettimetick(2) <= .RENT_TIME) mesq l("Sorry, you were taking too long to decide and I've rented my sword to somebody else."); + if (gettimetick(2) <= .RENT_TIME) close; + .RENT_TIME=gettimetick(2)+(15*60); + set Zeny, Zeny - 300; + rentitem Judgement, (15*60); + mes ""; + mesn; + mesq l("Here it is. Take care with it!"); + if ($RUNES_HOLDER$ == "" && $GAME_STORYLINE >= 4) { + next; + mesn; + mesq l("Actually, I heard that on the cave north of me a great wizard died from a curse, but his staff would be available for any other wizard who looks."); + next; + mesn; + mesq l("I don't know, I assume the staff was burried with him on the deepest parts of the cave. If it was easy, someone would already have stolen it."); + } + close; + + + + +L_MKControl: + mesn; + mes l("Oh noes! You've found the Candor control panel!"); + menu + l("Initiate small siege (lv. 5)"), L_MKSmall, + l("Abort"), -; + close; + +L_MKSmall: + addmapmask "005-1", MASK_MATTACK; + changemusic "005-1", "mythica.ogg"; + disablenpc("Mana Stone"); + pvpon("005-1"); + announce(l("##1WARNING! WARNING! Siege starting at Candor!!"), bc_all); + areamonster("005-1", 0, 0, 125, 125, l("Black Scorpion"), BlackScorpion, 1, "Ayasha::OnBlackScorpionDeath"); + areamonster("005-1", 0, 0, 125, 125, ("House Maggot"), HouseMaggot, 3, "Ayasha::OnHouseMaggotDeath"); + initnpctimer; + close; + + +OnBlackScorpionDeath: + dispbottom "BSD"; + areamonster("005-1", 0, 0, 125, 125, l("Black Scorpion"), BlackScorpion, 1, "Ayasha::OnBlackScorpionDeath"); + if (rand(10000) <= 900) + getitem StrangeCoin, 1; + debugmes "BSD OK"; + end; + +OnManaGhostDeath: + dispbottom "MGD"; + areamonster("005-1", 0, 0, 125, 125, l("Mana Ghost"), ManaGhost, 1, "Ayasha::OnManaGhostDeath"); + if (rand(10000) <= 500) + getitem StrangeCoin, 1; + debugmes "MGD OK"; + end; + +OnHouseMaggotDeath: + dispbottom "HMD"; + areamonster("005-1", 0, 0, 125, 125, l("House Maggot"), ManaGhost, 1, "Ayasha::OnHouseMaggotDeath"); + if (rand(10000) <= 400) + getitem StrangeCoin, 1; + debugmes "HMD OK"; + end; + +OnTimer5000: + areamonster("005-1", 0, 0, 125, 125, "Black Scorpion", BlackScorpion, 1, "Ayasha::OnBlackScorpionDeath"); + areamonster("005-1", 0, 0, 125, 125, ("House Maggot"), HouseMaggot, 10, "Ayasha::OnHouseMaggotDeath"); + mapannounce("003-1", "##2Message to all Candor NPCs: Take shelter!", bc_map); + disablenpc "Ayasha"; + disablenpc "Charda"; + disablenpc "Faris"; + disablenpc "Ghada"; + disablenpc "Latif"; + disablenpc "Rasin"; + disablenpc "Lilly"; + disablenpc "Zegas"; + disablenpc "Vincent"; + disablenpc "Liana"; + disablenpc "Maya"; + disablenpc "Sailors#005-1"; + disablenpc "Magic Barrier"; + disablenpc "Tolchi"; + disablenpc "Rosen"; + disablenpc "Nylo"; + disablenpc "Cynric"; + disablenpc "Morgan"; + disablenpc "Zitoni"; + disablenpc "Trainer"; + end; + +OnTimer60000: + areamonster("005-1", 0, 0, 125, 125, "Black Scorpion", BlackScorpion, 1, "Ayasha::OnBlackScorpionDeath"); + areamonster("005-1", 0, 0, 125, 125, ("Mana Ghost"), ManaGhost, 3, "Ayasha::OnManaGhostDeath"); + areamonster("005-1", 0, 0, 125, 125, ("House Maggot"), HouseMaggot, 5, "Ayasha::OnHouseMaggotDeath"); + end; + +OnTimer120000: +OnTimer180000: +OnTimer240000: +OnTimer300000: +OnTimer360000: +OnTimer420000: +OnTimer480000: + areamonster("005-1", 0, 0, 125, 125, ("Slime Blast"), 1090, 1); + areamonster("005-1", 0, 0, 125, 125, ("Mana Ghost"), ManaGhost, 1, "Ayasha::OnManaGhostDeath"); + areamonster("005-1", 0, 0, 125, 125, ("House Maggot"), HouseMaggot, 1, "Ayasha::OnHouseMaggotDeath"); + end; + +OnTimer540000: + mapannounce("005-1", "##1The Monster Army is getting tired of resistance!", bc_map); + areamonster("005-1", 0, 0, 125, 125, ("Slime Blast"), 1090, 5); + areamonster("005-1", 0, 0, 125, 125, ("Mana Ghost"), ManaGhost, 1, "Ayasha::OnManaGhostDeath"); + areamonster("005-1", 0, 0, 125, 125, ("House Maggot"), HouseMaggot, 1, "Ayasha::OnHouseMaggotDeath"); + end; + +OnTimer600000: + mapannounce("005-1", "##1The Monster King army is preparing to withdraw!", bc_map); + end; + +OnTimer630000: + removemapmask "005-1", MASK_MATTACK; + changemusic "005-1", "bartk_adventure.ogg"; + enablenpc("Mana Stone"); + killmonsterall("005-1", 0); + pvpoff("005-1"); + announce(("Candor siege is over!"), bc_all); + enablenpc "Ayasha"; + enablenpc "Charda"; + enablenpc "Faris"; + enablenpc "Ghada"; + enablenpc "Latif"; + enablenpc "Rasin"; + enablenpc "Lilly"; + enablenpc "Zegas"; + enablenpc "Vincent"; + enablenpc "Liana"; + enablenpc "Maya"; + enablenpc "Sailors#005-1"; + enablenpc "Magic Barrier"; + enablenpc "Tolchi"; + enablenpc "Rosen"; + enablenpc "Nylo"; + enablenpc "Cynric"; + enablenpc "Morgan"; + enablenpc "Zitoni"; + enablenpc "Trainer"; + stopnpctimer; + end; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + .RENT_TIME=0; // TODO: This could easily be a $GLOBAL_VARIABLE + end; +} + + +function script CheckEnfant { + @kids_count = 0; + @count_tmp = 0; + if ($@GM_OVERRIDE) + npctalk3 l("Hello, I am K-@@, of the @@ order.", @kids, $@KidsBits[@kids]); + if (STARTAREA & $@KidsBits[@kids]) + goto L_Already; + STARTAREA = STARTAREA | $@KidsBits[@kids]; + + goto L_Loop; + +L_KidsTally: + if (debug || $@GM_OVERRIDE) + npctalk3 l("You found @@ out of @@ kids.", @kids_count, @count_tmp); + if (@kids_count == 6) + { + message strcharinfo(0), "That must have been the last kid."; + setq CandorQuest_HAS, 3; + } + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Oh! You found me. Good job!"); + narrator("You receive 5 exp."); + getexp 5,0; + close; + return; + +L_Loop: + while (@count_tmp < 6) { + if (STARTAREA & $@KidsBits[@count_tmp]) + @kids_count = (@kids_count + 1); + @count_tmp = (@count_tmp + 1); + } + goto L_KidsTally; + +L_Already: + mesn; + mesq l("Thanks for playing with us! Can you find my friends?"); + close; + + return; +} +005-1,76,100,0 script Charda NPC_CHILD8,{ + @kids = 0; + if (getq(CandorQuest_HAS) == 2) + { + CheckEnfant(); + } else { + mes l("The kid is not paying attention to you."); + close; + } + end; +OnInit: + // This works at same var from KidsBits. We start counting from 10, then. + setarray $@KidsBits, (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15); + .sex = G_OTHER; + .distance = 1; + end; +} +005-1,28,100,0 script Faris NPC_CHILD7,{ + @kids = 1; + if (getq(CandorQuest_HAS) == 2) + { + CheckEnfant(); + } else { + mes l("The kid is not paying attention to you."); + close; + } + end; +OnInit: + .sex = G_MALE; + .distance = 1; + end; +} +005-1,91,31,0 script Ghada NPC_CHILD5,{ + @kids = 2; + if (getq(CandorQuest_HAS) == 2) + { + CheckEnfant(); + } else { + mes l("The kid is not paying attention to you."); + close; + } + end; +OnInit: + .sex = G_MALE; + .distance = 1; + end; +} +005-1,45,68,0 script Latif NPC_HUMAN_M_ARTIS,{ + @kids = 3; + if (getq(CandorQuest_HAS) == 2) + { + CheckEnfant(); + } else { + mes l("The kid is not paying attention to you."); + close; + } + end; +OnInit: + .sex = G_MALE; + .distance = 1; + end; +} +005-1,25,53,0 script Rasin NPC_CHILD3,{ + @kids = 4; + if (getq(CandorQuest_HAS) == 2) + { + CheckEnfant(); + } else { + mes l("The kid is not paying attention to you."); + close; + } + end; +OnInit: + .sex = G_MALE; + .distance = 1; + end; +} +005-1,44,74,0 script Lilly NPC_CHILD6,{ + @kids = 5; + if (getq(CandorQuest_HAS) == 2) + { + CheckEnfant(); + } else { + mes l("The kid is not paying attention to you."); + close; + } + end; +OnInit: + .sex = G_MALE; + .distance = 1; + end; +} + +005-1,60,91,0 script AyashaDebug NPC_MONA,{ + showavatar NPC_MONA; // this is handled by avatars.xml + mesn; + mesq l("Reset?"); + next; + menu + l("Yes."), L_Reset, + l("Test"), L_Charda, + + l("No."), L_Close; + +L_Reset: + setq CandorQuest_HAS, 0; + STARTAREA = STARTAREA &~ (1 << 10); + STARTAREA = STARTAREA &~ (1 << 11); + STARTAREA = STARTAREA &~ (1 << 12); + STARTAREA = STARTAREA &~ (1 << 13); + STARTAREA = STARTAREA &~ (1 << 14); + STARTAREA = STARTAREA &~ (1 << 15); + mes l("Reset!"); + goto L_Close; + +L_Charda: + STARTAREA = STARTAREA &~ (1 << 10); + mes l("Charda clean!"); + goto L_Close; + +L_Close: + //showavatar; // Use this to hide the showavatar + close; + +// Use @shownpc to enable AyashaDebug +OnInit: + if (!debug) + disablenpc .name$; + end; +} diff --git a/npc/005-1/liana.txt b/npc/005-1/liana.txt new file mode 100644 index 0000000..2916013 --- /dev/null +++ b/npc/005-1/liana.txt @@ -0,0 +1,110 @@ +// TMW2 Script +// Author: +// Jesusalva +// Saulc +// Description: +// Candor girl ask for maggot sliem every 6 hours +// Variable: +// CandorQuest_Liana +// PS. Liana could (should) explain too the small cave north of there. It can be +// a place to run, or maybe monsters there are natural and protect them from the +// Mana Monsters, etc. + +005-1,54,96,0 script Liana NPC_ELVEN_FEMALE,{ + + mesn; + //mesq l("Hurnscald is a large city. I'm sure glad I live in Candor because I know where everything's at."); + mesq l("Monsters do not aim small towns like Candor. This city also comes with the plus that I know where everything's at."); + if (BaseLevel >= 5) goto L_Menu; + tutmes l("This quest will unlock at level 5."), l("Protip"), false; + close; + +L_Menu: + mesn; + mesq l("Are you enjoying yourself in Candor? Do you have any questions?"); + mes ""; + menu + l("What can I do with Maggot Slime?"),L_Slime, + l("What can I do with Bug Leg?"),L_Bug, + l("No, thanks."),L_Close; + +L_Slime: + mes ""; + .@q=getq(CandorQuest_Liana); + mesq l("I collect them."); + tutmes l("Liana, like other NPCs, provide a repeatable quest. Once you finish it, you will be able to do it again, after waiting for a few hours."); + next; + if (.@q == 0) goto L_Quest; + if (gettimetick(2) >= LIANA_TIMER + 60 * 60 * 6) goto L_Repeat; + close; + +L_Quest: + mesq l("With this I make balls of slime for Candor's childs, they really like to play with them."); + next; + mesq l("Maybe you could bring me 5 @@? I will reward you for your effort.", getitemlink(MaggotSlime)); + mes ""; + menu + rif(countitem(MaggotSlime) >= 5, l("Here they are!")), L_Finish, + l("I'll get to it."), L_Close; + close; // double sure + +L_Repeat: + mesq l("I am searching again maggot slime to craft more balls."); + next; + mesq l("Maybe you could bring me 10 sticky @@?", getitemlink(MaggotSlime)); + mes ""; + menu + rif(countitem(MaggotSlime) >= 10, l("Here they are!")), L_Finish2, + l("I'll get to it."), L_Close; + close; + +L_Finish2: + delitem MaggotSlime, 10; + getexp 21, 0; + Zeny = (Zeny + 80); // 10*4 = 40 base + setq CandorQuest_Liana, 1; + set LIANA_TIMER, gettimetick(2); + mes ""; + mesn; + mesq l("Many, many thanks! I'm sure the children will love it!"); + close; + +L_Finish: + delitem MaggotSlime, 5; + getexp 58, 7; + Zeny = (Zeny + 30); // 5*4 = 20 base + setq CandorQuest_Liana, 1; + set LIANA_TIMER, gettimetick(2); + mes ""; + mesn; + mesq l("Many, many thanks! I'm sure the children will love it!"); + close; + +L_Bug: + mes ""; + mesq l("Ah, personally I don't use it?"); + next; + mes l("She shakes her head."); + next; + if (getq(CandorQuest_Vincent) < 2) { + mesq l("You should ask this question at Vincent."); + next; + mesq l("He is in the process of making a figurine made of bug leg."); + next; + } else { + mesq l("If you have a @@, perhaps you could use it as a bait.", getitemlink(FishingRod)); + next; + mesq l("I guess it can also be used at quests... Or sell to Juliet, on Nard's ship."); + next; + } + mesq l("I hope my answer help you in your adventure!"); + next; + mes l("she's smiling at you."); + goto L_Close; + +L_Close: + closedialog; + goodbye; + close; +} + diff --git a/npc/005-1/mapflags.txt b/npc/005-1/mapflags.txt new file mode 100644 index 0000000..e0cbd89 --- /dev/null +++ b/npc/005-1/mapflags.txt @@ -0,0 +1,8 @@ +005-1 mapflag town +005-1 mapflag nopenalty +//005-2 mapflag town // Saxso House +005-3 mapflag town +005-4 mapflag town +005-5 mapflag town +005-6 mapflag town +005-7 mapflag town diff --git a/npc/005-1/maya.txt b/npc/005-1/maya.txt new file mode 100644 index 0000000..d990abc --- /dev/null +++ b/npc/005-1/maya.txt @@ -0,0 +1,234 @@ +// TMW2 scripts. +// Author: +// Saulc +// GonzoDark +// Jesusalva +// Variables: +// 0 CandorQuest_Maya +// Values: +// 00 Default, no quest selected. +// 01 First quest accepted: Need 3 cotton cloth and 3 maggot slime +// 02 First quest completed: Reward Candor shirt +// 03 Second quest accepted: Need 3 ScorpionStinger and 10 Piou Feathers +// 04 Second quest completed: Reward 700 GP (precise calculation) +// 05 wolvern tooth +20k + 2000monster point ->claw pendant + +005-1,49,47,0 script Maya NPC_RAIJIN_FEMALE_LEGION_ARTIS,{ + function itemCombo; + .@maya = getq(CandorQuest_Maya); + + if (.@maya == 0) + goto L_QuestNotStarted; + if (.@maya == 1) + goto L_QuestAccepted; + if (.@maya == 2) + goto L_Quest2; + if (.@maya == 3) + goto L_Quest2Accepted; + if (.@maya == 4) + goto L_NextQuestPending; + +L_QuestNotStarted: + mesn; + mesq l("Hi there, I can always use a helping hand around here, are you the one for the job?"); + mes ""; + menu + l("Sure"),L_Next, + l("No, thanks."),L_Close; + +L_Next: + mes ""; + mesn; + mesq l("Good! First, let us test if you are resourceful. Bring me 3 @@ and 3 @@. That should be enough!", getitemlink(CottonCloth), getitemlink(MaggotSlime)); + setq CandorQuest_Maya, 1; + tutmes l("You can use \"%s <monster_name>\" to obtain specified monster drop list and stats.", b("@monsterinfo")), l("Protip"), false; + close; + +L_QuestAccepted: + mesn; + mesq l("I see you have brought @@/3 @@ and @@/3 @@ for me",countitem(CottonCloth),getitemlink(CottonCloth),countitem(MaggotSlime),getitemlink(MaggotSlime)); + mes ""; + menu + rif(countitem(CottonCloth) >= 3 && countitem(MaggotSlime) >= 3, l("Here they are!")), L_QuestCompleted, + rif(countitem(CottonCloth) < 3 || countitem(MaggotSlime) < 3, l("Oh, then I don't have enough! I'll bring more later!")), L_GetHelp1, + l("Can we get back to that later?"), -; + tutmes l("You can use \"%s <monster_name>\" to obtain specified monster drop list and stats.", b("@monsterinfo")), l("Protip"), false; + close; + +L_GetHelp1: + mes ""; + mesc l("Protip: You can get @@ from shops. Cotton is rumored to be magical, keep this is mind.", getitemlink(CottonCloth)); + mesc l("Protip 2: If you fell stuck, ask at #world, even if nobody is online. Who knows, someone on Discord or IRC might reply!"); + tutmes l("You can use \"%s <monster_name>\" to obtain specified monster drop list and stats.", b("@monsterinfo")), l("Protip"), false; + next; + goto L_Close; + +L_QuestCompleted: + delitem CottonCloth, 3; + delitem MaggotSlime, 3; + set Zeny, Zeny + 325; + getitem CandorShirt, 1; + getexp BaseLevel*8, 5; + setq CandorQuest_Maya, 2; + + mes ""; + mesn; + mesq l("Thanks for the help. Here, take this shirt and some money."); + close; + +L_Quest2: + mesn; + mesq l("Thanks again for the help. You have proven that you are resourceful."); + next; + if (BaseLevel < 7) mesn; + if (BaseLevel < 7) mesq l("But maybe you should help other people and get some levels before returning to me."); + if (BaseLevel < 7) close; + mesn; + mesq l("As always, I can use a helping hand around here. Interested?"); + mes ""; + menu + l("Sure"),-, + l("No, thanks."),L_Close; + + mes ""; + mesn; + mesq l("Good! I want 3 @@ and 10 @@. I have a contract to transform that in good money.", + getitemlink(ScorpionStinger), getitemlink(PiouFeathers)); + setq CandorQuest_Maya, 3; + close; + +L_Quest2Accepted: + mesn; + mesq l("I see you have brought @@/3 @@ and @@/10 @@ for me.", + countitem(ScorpionStinger),getitemlink(ScorpionStinger), + countitem(PiouFeathers),getitemlink(PiouFeathers)); + mes ""; + menu + rif(countitem(ScorpionStinger) >= 3 && countitem(PiouFeathers) >= 10, l("Here they are!")), L_Quest2Completed, + rif(countitem(ScorpionStinger) < 3 || countitem(PiouFeathers) < 10, l("Oh, then I don't have enough! I'll bring more later!")), L_Close, + l("Can we get back to that later?"), -; + close; + +L_Quest2Completed: + delitem ScorpionStinger, 3; + delitem PiouFeathers, 10; + set Zeny, Zeny + 725; + getexp BaseLevel*10, 10; + setq CandorQuest_Maya, 4; + + // Reward Calculation: Piou base is 3 and Stinger base is 25. + // Maya will pay in a 1.5x factor + 300 GP she owed you + a small bonus to round things + // (3*3*1.5)+(25*10*1.5) = roughly 389 GP + 300 + bonus(11) = 700 + + mes ""; + mesn; + mesq l("Thanks for the help! If you help people, they'll start trusting you. Once they trust you, they'll give you quests which are very important to them;"); + next; + mesn; + mesq l("And once they entrust you with what is important for them, they'll pay better. Here is 700 GP. Come back later."); + close; + + +L_NextQuestPending: + mesn; + mesq l("Thanks again for the help. You have proven that you are resourceful. Come back again later."); + switch (getequipid(EQI_HEAD_MID)) { + case CreasedShirt: + itemCombo(l("Creased"), l("All Stats +1"), + CreasedShirt, CreasedBoots, CreasedGloves, CreasedShorts); + break; + case CandorShirt: + itemCombo(l("Candor"), l("All Stats +1, Max HP +1%"), + CandorShirt, CandorBoots, CandorGloves, CandorShorts, CandorHeadBand); + break; + case CottonShirt: + itemCombo(l("Cotton"), l("All Stats +1, Max HP +1%, Max MP +5%"), + CottonShirt, CottonBoots, CottonGloves, 0, + CottonShorts, CottonTrousers, CottonSkirt, MiniSkirt); + break; + case MinerTankTop: + itemCombo(l("Miner"), l("All Stats +1, Vit +2"), + MinerTankTop, MinerGloves, MinerHat, 0, + MinerKnife, Pickaxe); + break; + case WarlordPlate: + itemCombo(l("Warlord"), l("All Stats +1, Block +2%"), + WarlordPlate, WarlordBoots, WarlordGloves, WarlordPants, WarlordHelmet); + break; + case GoldenWarlordPlate: + itemCombo(l("Golden Warlord"), l("All Stats +1, Block +2%"), + GoldenWarlordPlate, WarlordBoots, WarlordGloves, WarlordPants, WarlordHelmet); + break; + case Chainmail: + itemCombo(l("Chainmail"), l("Block +1%"), + Chainmail, ChainmailSkirt); + break; + case GoldenChainmail: + itemCombo(l("Golden Chainmail"), l("Block +1%"), + GoldenChainmail, ChainmailSkirt); + break; + case TerraniteArmor: + itemCombo(l("Terranite"), l("All Stats +1, MDEF +30%"), + TerraniteArmor, TerraniteBoots, + TerranitePants, TerraniteMask); + break; + case LeatherShirt: + itemCombo(l("Leather"), l("All Stats +2"), + LeatherShirt, LeatherBoots, LeatherGloves, LeatherTrousers); + break; + case AssassinChest: + itemCombo(l("Assassin"), l("All Stats +1, Agi +5"), + AssassinChest, AssassinBoots, AssassinGloves, AssassinPants); + break; + case SaviorArmor: + itemCombo(l("Savior"), l("All Stats +5"), + SaviorArmor, SaviorBoots, SaviorPants, SaviorHelmet, 0, + SaviorShield, Skypiercer); + break; + case SilkRobe: + itemCombo(l("Silk"), l("Passive MP Regen"), + SilkRobe, CottonBoots, 0, + SilkGloves, CottonGloves); + break; + case SorcererRobe: + itemCombo(l("Sorcerer"), l("Passive MP Regen"), + SorcererRobe, 0, + CottonBoots, WizardMoccasins, WitchBoots, 0, + CottonGloves, SilkGloves); + break; + } + close; + +function itemCombo { + next; + mesn; + mesq l("I see you're trying to use the %s set, nice!", b(getarg(0))); + mesq l("This set grants the following effect when complete:"); + mesc getarg(1), 3; + next; + mesn; + mesq l("This set is composed by the following items:"); + mes ""; + for (.@i = 2; .@i < getargcount(); .@i++) { + if (getarg(.@i) <= 0) + mesc l("--- AND ANY OF ---"); + else + mesc l("* %s", getitemlink(getarg(.@i))),(countitem(getarg(.@i)) ? 3 : 9); + } + mes ""; + next; + mesn; + mesq l("To get the combo, you must fill all slots if one of the listed items. Usually any of the listed ones will do, but in rare cases it won't!"); + return; +} + +L_Close: + closedialog; + goodbye; + close; + +OnInit: + .sex = G_FEMALE; + .distance = 4; + end; +} diff --git a/npc/005-1/sailors.txt b/npc/005-1/sailors.txt new file mode 100644 index 0000000..633e195 --- /dev/null +++ b/npc/005-1/sailors.txt @@ -0,0 +1,157 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Crazyfefe +// Description: +// A Sailor from Nard's crew. +// CandorQuest_Sailors +// 0: Not started +// 1: Accepted +// 2: Invite Elmo +// 3: Completed + +005-1,102,109,0 script Sailors#005-1 NPC_ELVEN_MAN_TRADER_SITTING,{ + .@q = getq(CandorQuest_Sailors); + if (.@q == 1) + goto L_Report; + if (.@q == 2) + goto L_Elmo; + if (.@q == 3) + goto L_Complete; + if ( BaseLevel < 8) { hello; end; } + + mesn; + mesq l("Ahoy matey!"); + next; + mesq l("Arr, it is always good to be on land after so much time in sea!"); + next; + mesq l("We want to celebrate this moment, but can you believe we ran out of beer?"); + mes ""; + menu + l("If I were you, I would drink water."),L_Water, + l("WHAT? How can you ever party without beer?!"),L_Accept; + +L_Water: + mes ""; + mesn; + mesq l("Ah, it is not the same. Not the same."); + next; + mesq l("When I am drunk I tell myself to stop drinking, but I won't listen the words of a drunkard."); + close; + +L_Accept: + mes ""; + mesn; + mesq l("We can't, don't you agree?!"); + next; + mesq l("However, if we waste the ship's money in beer, Nard will get mad."); + next; + mesq l("We won't be able to pay you in money, but we'll make you one of us if you bring us beer!"); + next; + mesq l("Please bring us 5 @@! That should be enough!", getitemlink("Beer")); + setq CandorQuest_Sailors, 1; + close; + +L_Report: + mesn; + mesq l("I see you brought @@/5 @@ for us!",countitem("Beer"),getitemlink("Beer")); + mes ""; + menu + rif(countitem("Beer") >= 5, l("Indeed, matey! Here they are!")), L_Give, + rif(countitem("Beer") >= 5, l("You're doing the math wrong, matey! I'll bring them later!")), L_Later, + rif(countitem("Beer") < 5, l("Arr, that's not enough! I'll bring more later!")), L_Later; + close; + +L_Later: + mes ""; + mesn; + mesq l("Arr, we will wait for you then! We still have tasks to complete!"); + close; + +L_Give: + inventoryplace Bandana, 1; + delitem "Beer", 5; + getitem Bandana, 1; + getexp 35, 5; + setq CandorQuest_Sailors, 2; + mes ""; + mesn; + mesq l("Arr, that's some fine ale! We can do the party when we're done with our work!"); + next; + mesn; + mesq l("Take this @@ to prove you're one of us! Could you also invite Elmo? Thanks, matey!", getitemlink("Bandana")); + close; + + +L_Elmo: + mesn; + mesq l("Please invite Elmo for the party, matey! We can't leave our positions!"); + close; + +L_Complete: + .@q = getq(CandorQuest_SailorCure); + mesn; + mesq l("Thanks for the help! Arr, that was some fine ale, indeed!"); + if (.@q == 1) + close; + next; + mesn; + mesq l("A pity a friend of ours drank too much. Juliet knows how to cure. We need to give her a @@ to do a hangover potion.", getitemlink(ScorpionStinger)); + +L_CureMaster: + if (countitem(ScorpionStinger) < 1) + close; + next; + mesn; + mesq l("...Dealing with scorpion stingers is a gamble, so we may need a few stingers before making a successful potion."); + next; + select + rif(countitem(ScorpionStinger) >= 1, l("I have a Stinger with me. Try it!")), + l("I see."); + + mes ""; + + if (@menu == 1) + goto L_CureLoop; + close; + +L_CureLoop: + inventoryplace CandorBoots, 1; + delitem ScorpionStinger,1; + setq2 CandorQuest_SailorCure, getq2(CandorQuest_SailorCure)+1; + if (rand(5) == 2) // Crazyfefe like this number :3 + goto L_questCure_success; + goto L_questCure_failure; + +L_questCure_success: + if (getq2(CandorQuest_SailorCure) * 30 < 240) + Zeny = Zeny + 240 - getq2(CandorQuest_SailorCure) * 30; + else + Zeny = Zeny + 30; + getitem CandorBoots, 1; + getexp 20, 2; + setq CandorQuest_SailorCure, 1; + mesn; + mesq l("That... It... It worked! This is just the right claw!"); + next; + mesn; + mesq lg("We'll bring this one to Juliet at once. Thanks for your help! Savior!"); + close; + +L_questCure_failure: + if (getq2(CandorQuest_SailorCure) * 30 < 240) + Zeny = Zeny + 60; + else + Zeny = Zeny + 30; + mesn; + mesq l("That... Didn't work. I'm sorry."); + next; + mesn; + mesq l("Here's some gold for your efforts."); + goto L_CureMaster; + +OnInit: + .sex = G_OTHER; + .distance = 7; + end; +} diff --git a/npc/005-1/saxso.txt b/npc/005-1/saxso.txt new file mode 100644 index 0000000..5bc728f --- /dev/null +++ b/npc/005-1/saxso.txt @@ -0,0 +1,23 @@ +// TMW2 Script +// Author: +// Jesusalva + +// Sign of great danger +005-1,37,90,0 script #SignSaxso NPC_NO_SPRITE,{ + mes ".:: "+l("DANGER")+" ::."; + mesc l("Saxso's Official Residence."); + mesc l("DO NOT ENTER."); + mes ""; + mesc l("-- Zegas, the Mayoress"); + tutmes l("In some dungeons etc., there might be a monster much, much stronger than all other monsters on the area. These are the optional miniboss."), l("Optional Miniboss"); + tutmes l("Defeating is entirely optional. They usually give more experience than the average, but are much stronger and dangerous."), l("Optional Miniboss"); + tutmes l("Usually they have a few rare drops. The miniboss of Candor may drop a rare key, which you can use to open a certain chest..."), l("Optional Miniboss"); + tutmes l("(Protip: Always use %s before engaging a different monster!)", b("@monsterinfo")), l("Optional Miniboss"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + diff --git a/npc/005-1/ship.txt b/npc/005-1/ship.txt new file mode 100644 index 0000000..3b333be --- /dev/null +++ b/npc/005-1/ship.txt @@ -0,0 +1,17 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// This script controls access to Nard's Ship, fixing variables. + +005-1,50,117,0 script CandorShip NPC_HIDDEN,0,0,{ + +OnTouch: + LOCATION$="Candor"; + goto L_Warp; + +L_Warp: + warp "002-3@"+LOCATION$, 31, 28; + closedialog; + close; +} diff --git a/npc/005-1/soul-menhir.txt b/npc/005-1/soul-menhir.txt new file mode 100644 index 0000000..6ea59bf --- /dev/null +++ b/npc/005-1/soul-menhir.txt @@ -0,0 +1,20 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Soul Menhir + +005-1,43,97,0 script Soul Menhir#candor NPC_SOUL_MOSS,{ + @map$ = "005-1"; + setarray @Xs, 42, 43, 44, 42, 44, 42, 43, 44; + setarray @Ys, 96, 96, 96, 97, 97, 98, 98, 98; + @x = 0; + @y = 0; + callfunc "SoulMenhir"; + @map$ = ""; + cleararray @Xs[0], 0, getarraysize(@Xs); + cleararray @Ys[0], 0, getarraysize(@Ys); + @x = 0; + @y = 0; + close; +} diff --git a/npc/005-1/underground.txt b/npc/005-1/underground.txt new file mode 100644 index 0000000..bd5b214 --- /dev/null +++ b/npc/005-1/underground.txt @@ -0,0 +1,41 @@ +// TMW2 Script +// Author: +// Crazyfefe +// Jesusalva + +005-1,65,63,0 script Sign#0051UG NPC_SWORDS_SIGN,{ + mes ".:: "+l("DANGER")+" ::."; + mesc l("Endless, cute, dangerous, deadly."); + mesc l("Only those whom overcome all Candor Challenges shall wield ultimate power."); + mes ""; + mesc l("-- The Expedition"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +// Effective Warp +005-1,66,62,0 script Magic Barrier#0051 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (TUTORIAL && !@candor_warn) { + mesc ".:: "+l("WARNING")+" ::.", 1; + mesc l("You're about to enter a dangerous zone!"); + mesc l("Outside towns, there's an EXP penalty if you die."); + mes ""; + mes l("This cave is considered outside the town!"); + next; + mesc l("Enter anyway?"); + mesc l("EXP Penalty: ON"), 1; + if (askyesno() == ASK_NO) close; + closeclientdialog; + @candor_warn = true; // Maybe record this in a more definitive way + } + warp "006-0", 49, 53; + end; +} + diff --git a/npc/005-1/vincent.txt b/npc/005-1/vincent.txt new file mode 100644 index 0000000..7b6e7e2 --- /dev/null +++ b/npc/005-1/vincent.txt @@ -0,0 +1,78 @@ +// TMW2 scripts. +// Authors: +// Saulc +// Variables: +// CandorQuest_Vincent - quest var + + +005-1,99,105,0 script Vincent NPC_PLAYER,{ + .@q = getq(CandorQuest_Vincent); + if (.@q == 1) goto L_CheckItems; + if (.@q == 2) goto L_QuestDone; + + speech S_LAST_BLANK_LINE, + l("I am making a figurine with Bug legs for the upcoming festival."), + lg("Be a friend and bring me @@ @@.", "Be a friend and bring me @@ @@.", .LegsCount, getitemlink(BugLeg)); + + switch (select(l("Ok, I'll be back in no time."), + rif(countitem(BugLeg) >= .LegsCount, l("I have them here already.")), + l("Sorry, I'm doing other things at the moment."))) + { + case 1: + setq CandorQuest_Vincent, 1; + speech S_FIRST_BLANK_LINE, + l("Thank you. I'll wait here."); + close; + case 2: + setq CandorQuest_Vincent, 1; + goto L_CheckItems; + case 3: + speech S_FIRST_BLANK_LINE, + l("But I'm almost out of @@...", getitemlink(BugLeg)); + close; + } + +L_CheckItems: + if (countitem(BugLeg) < .LegsCount) + { + speech + l("Sorry, but you don't have what I need."), + l("I need @@ @@.", .LegsCount, getitemlink(BugLeg)); + close; + } + + speech + l("I can't believe it! You've brought me @@ @@!", .LegsCount, getitemlink(BugLeg)), + l("That's exactly what I needed!"), + l("I will be forever grateful!"); + + delitem BugLeg, .LegsCount; + getexp 15, 5; + Zeny = Zeny + 850; + message strcharinfo(0), l("You receive @@ GP!", 850); + setq CandorQuest_Vincent, 2; + close; + +L_QuestDone: + speech + l("Hey, @@!", strcharinfo(0)), + l("My figurine is so nice!"); + tutmes l("Some quests and events, like the festival, are only available during specific times."); + tutmes l("Some will only repeat yearly, while other events happen weekly or monthly, or in some other time aspect."); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, CreasedShirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShorts); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 25); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .LegsCount = 7; + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/005-1/wateranimation.txt b/npc/005-1/wateranimation.txt new file mode 100644 index 0000000..cb8ae56 --- /dev/null +++ b/npc/005-1/wateranimation.txt @@ -0,0 +1,37 @@ +// TMW2 scripts. +// Author: +// Saulc +// Description: +// Water animations, splash, fishes, etc... + +005-1,81,62,0 script #water_animation_candor0 NPC_WATER_SPLASH,{ + + fishing(1, + Roach, + Tench); // begin or continue fishing + close; + +OnInit: + setarray .bait_ids, + SmallTentacles, 24, + PiouLegs, 1, + Bread, 8, + Aquada, 24, + Tentacles, 32, + BugLeg, 2; + + .sex = G_OTHER; + .distance = 4; + end; +} + +005-1,84,66,0 duplicate(#water_animation_candor0) #water_animation_candor1 NPC_WATER_SPLASH +005-1,74,66,0 duplicate(#water_animation_candor0) #water_animation_candor2 NPC_WATER_SPLASH +005-1,76,70,0 duplicate(#water_animation_candor0) #water_animation_candor3 NPC_WATER_SPLASH +005-1,79,71,0 duplicate(#water_animation_candor0) #water_animation_candor4 NPC_WATER_SPLASH +005-1,78,62,0 duplicate(#water_animation_candor0) #water_animation_candor5 NPC_WATER_SPLASH +005-1,55,61,0 duplicate(#water_animation_candor0) #water_animation_candor6 NPC_WATER_SPLASH +005-1,49,64,0 duplicate(#water_animation_candor0) #water_animation_candor7 NPC_WATER_SPLASH +005-1,56,65,0 duplicate(#water_animation_candor0) #water_animation_candor8 NPC_WATER_SPLASH +005-1,48,66,0 duplicate(#water_animation_candor0) #water_animation_candor9 NPC_WATER_SPLASH + diff --git a/npc/005-1/zegas.txt b/npc/005-1/zegas.txt new file mode 100644 index 0000000..a623f32 --- /dev/null +++ b/npc/005-1/zegas.txt @@ -0,0 +1,144 @@ +// TMW2 Script +// Author: +// Unknown +// Description: +// Saxso's widow. Asks new players to trigger a bug bomb in the storehouse. + +005-1,43,85,0 script Zegas NPC_MONA,{ + function lagTutorial; + .@q = getq(CandorQuest_Barrel); + if (BaseLevel < 5) + goto L_Weak; + if (.@q == 1) + goto L_Find; + if (.@q == 2) + goto L_Looking; + if (.@q == 3) + goto L_QuestEnd; + if (.@q == 4) + goto L_Thanks; + goto L_Meet; + +L_Weak: + mesn; + mesq l("Do not enter in this storehouse, the maggots there will kill you."); + close; + +L_Meet: + mesn; + mesq l("Hey do you have a second?"); + next; + mesq l("The storehouse here is overrun with house maggots."); + next; + mesq l("Wouldn't you know it, the bug bomb Eomie gave us is in one of the store room barrels."); + next; + mesq l("Can you search ##Bthe barrels##b for the bug bomb and set it off when you find it?"); + setq CandorQuest_Barrel, 1; + menu + l("Sure."),L_Start, + l("Maybe some other time."),L_Close; + +L_Find: + mesn; + mesq l("The storehouse is still over run with house maggots."); + next; + mesq l("Please help me find the bug bomb Eomie gave us is in one of the store room barrels?"); + menu + l("Sure."),L_Start, + l("Maybe some other time."),L_Close; + +L_Start: + mesn; + mesq l("Thanks, come back and see me once you found the bug bomb and set it off."); + setq CandorQuest_Barrel, 2; + lagTutorial(); + close; + +L_Looking: + mesn; + mesq l("Still haven't found it? Well, keep looking the barrels at this storehouse. I know it's in there, somewhere."); + if (TUTORIAL) { + mesc l("Read the tutorial again?"); + if (askyesno() == ASK_YES) + lagTutorial(); + } + close; + +L_QuestEnd: + mesn; + mesq l("From the smell I can see you found the bug bomb!"); + mesq l("Thanks once again, I know it's not much but here is @@ GP for your troubles.", 350); + getexp 63, 0; + getitem CandorShorts, 1; + Zeny = (Zeny + 350); + setq CandorQuest_Barrel, 4; + close; + +L_Thanks: + mesn; + mesq l("Thanks for Helping with clear out the store room!"); + if (!getq(CandorQuest_Chest)) { + next; + mesn; + mesq l("Do you see that fancy house over there? My husband, @@, used to live there.", b(l("Saxso"))); + next; + mesn; + mesq l("Strangely enough, when he died, he became a ghost. This shouldn't happen, but yet..."); + next; + mesn; + mesq l("Anyway, conspiratory theories aside, his ghost is a powerful boss. I would not attack him if I were you!"); + } + close; + +L_Close: + closedialog; + goodbye; + close; + +function lagTutorial { + // TUTORIAL block + tutmes l("The bugs there can be pretty vicious. Do you know what's worse though? Lag."); + tutmes l("By pressing @@, you can open the Shortcut menu. And by pressing @@, you open the skill menu!", b("F8"), b("F5")); + tutmes l("If you drag the \"Resync\" skill from the skill list to the shortcut list, you'll be able to rapidly solve some lag issues!"); + tutmes l("Of course, lag might always be a problem. Remember to drag healing items to the shortcut list too. %%2"); + return; +} + +OnInit: + .sex = G_FEMALE; + .distance = 4; + end; +} + +005-1,32,73,0 script ZegasDebug NPC_MONA,{ + showavatar NPC_MONA; // this is handled by avatars.xml + mesn; + mesq l("Reset?"); + next; + menu + l("Yes."), L_Reset, + l("No."), L_Close; + +L_Reset: + setq CandorQuest_Barrel, 0; + STARTAREA = STARTAREA &~ (1 << 2); + STARTAREA = STARTAREA &~ (1 << 3); + STARTAREA = STARTAREA &~ (1 << 4); + STARTAREA = STARTAREA &~ (1 << 5); + STARTAREA = STARTAREA &~ (1 << 6); + STARTAREA = STARTAREA &~ (1 << 7); + STARTAREA = STARTAREA &~ (1 << 8); + STARTAREA = STARTAREA &~ (1 << 9); + mes l("Reset!"); + close; + +L_Close: + //showavatar; // Use this to hide the showavatar + close; + +// Use @shownpc to enable ZegasDebug +OnInit: + if (!debug) + disablenpc "ZegasDebug"; + end; +} diff --git a/npc/005-2-1/_import.txt b/npc/005-2-1/_import.txt new file mode 100644 index 0000000..c9b4ade --- /dev/null +++ b/npc/005-2-1/_import.txt @@ -0,0 +1,4 @@ +// Map 005-2-1: Saxso's Basement +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-2-1/_mobs.txt", +"npc/005-2-1/_warps.txt", diff --git a/npc/005-2-1/_mobs.txt b/npc/005-2-1/_mobs.txt new file mode 100644 index 0000000..e697811 --- /dev/null +++ b/npc/005-2-1/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-2-1: Saxso's Basement mobs +005-2-1,34,38,10,3 monster Lava Slime 1097,7,36000,300000 diff --git a/npc/005-2-1/_warps.txt b/npc/005-2-1/_warps.txt new file mode 100644 index 0000000..31167fe --- /dev/null +++ b/npc/005-2-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-2-1: Saxso's Basement warps +005-2-1,28,34,0 warp #005-2-1_28_34 0,0,005-2,25,36 diff --git a/npc/005-2/_import.txt b/npc/005-2/_import.txt new file mode 100644 index 0000000..09564f7 --- /dev/null +++ b/npc/005-2/_import.txt @@ -0,0 +1,6 @@ +// Map 005-2: Saxso's House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-2/_mobs.txt", +"npc/005-2/_warps.txt", +"npc/005-2/saxsochest.txt", +"npc/005-2/trapdoor.txt", diff --git a/npc/005-2/_mobs.txt b/npc/005-2/_mobs.txt new file mode 100644 index 0000000..436e0f1 --- /dev/null +++ b/npc/005-2/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-2: Saxso's House mobs +005-2,43,40,0,0 monster Saxso Ghost 1076,1,36000,300000 diff --git a/npc/005-2/_warps.txt b/npc/005-2/_warps.txt new file mode 100644 index 0000000..35e4436 --- /dev/null +++ b/npc/005-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-2: Saxso's House warps +005-2,33,43,0 warp #005-2_33_43 0,0,005-1,38,90 diff --git a/npc/005-2/saxsochest.txt b/npc/005-2/saxsochest.txt new file mode 100644 index 0000000..591372e --- /dev/null +++ b/npc/005-2/saxsochest.txt @@ -0,0 +1,82 @@ +// TMW2 Script +// Author: +// Crazyfefe +// Description: +// Minor quest for a necklace + +005-2,44,41,0 script Saxso Chest NPC_NO_SPRITE,{ + + function quest_completed { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("you already opened the chest."); + close; + } + + function quest_open { + if (countitem(.key) > 0) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("You open the chest and found a @@.",getitemlink(.reward)); + delitem .key,1; + getitem .reward,1; + getexp 80, 15; + setq CandorQuest_Chest, 1; + close; + } else { + speech S_FIRST_BLANK_LINE, + l("You don't have the key."); + tutmes l("In the world, you may find several treasure boxes. Different treasure boxes need different keys."), l("Treasure!"); + tutmes l("The most common treasure box uses %s and can opened many times. However, this is a special treasure box. You'll need a %s to open it.", getitemlink(TreasureKey), getitemlink(.key)), l("Treasure!"); + tutmes l("This key is dropped by the Saxso Ghost. Did you knew you can obtain info about the monster drop rates and strength?"), l("Monster Information!"); + tutmes l("Use %s (name in english) to obtain this info. You don't need the full name either, so go ahead and try it!", b("@monsterinfo Saxso")), l("Monster Information!"); + close; + } + } + + function quest_started { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("It looks locked."); + narrator S_LAST_NEXT, + l("Perhaps you should use a key to open it."); + do + { + select + l("Use a key."), + menuaction(l("Quit")); + + switch (@menu) { + case 1: + quest_open; + break; + } + } while (@menu != 2); + } + + do + { + .@chest = getq(CandorQuest_Chest); + if (.@chest == 1) + goto quest_completed; + select + rif(.@chest == 0,l("There is a dusty chest.")), + menuaction(l("Quit")); + + switch (@menu) { + case 1: + quest_started; + break; + } + } while (@menu != 2); + + closedialog; + goodbye; + close; + +OnInit: + .key = SaxsoKey; + .reward = ToothNecklace; + + .sex = G_MALE; + .distance = 3; + end; +} + diff --git a/npc/005-2/trapdoor.txt b/npc/005-2/trapdoor.txt new file mode 100644 index 0000000..41c2317 --- /dev/null +++ b/npc/005-2/trapdoor.txt @@ -0,0 +1,13 @@ +// TMW 2 script +// Author: +// Saulc + +005-2,24,36,0 script Trap Door NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@chest = getq(CandorQuest_Chest); + if (.@chest == 1) + warp "005-2-1", 28, 35; + close; +} diff --git a/npc/005-3/_import.txt b/npc/005-3/_import.txt new file mode 100644 index 0000000..e47ad86 --- /dev/null +++ b/npc/005-3/_import.txt @@ -0,0 +1,5 @@ +// Map 005-3: Storage House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-3/_mobs.txt", +"npc/005-3/_warps.txt", +"npc/005-3/barrel.txt", diff --git a/npc/005-3/_mobs.txt b/npc/005-3/_mobs.txt new file mode 100644 index 0000000..4ab4a44 --- /dev/null +++ b/npc/005-3/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-3: Storage House mobs +005-3,34,37,10,3 monster House Maggot 1084,6,40000,300000,Trainer::OnKillHouseMaggot diff --git a/npc/005-3/_warps.txt b/npc/005-3/_warps.txt new file mode 100644 index 0000000..93f1503 --- /dev/null +++ b/npc/005-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-3: Storage House warps +005-3,25,43,0 warp #005-3_25_43 0,0,005-1,41,85 diff --git a/npc/005-3/barrel.txt b/npc/005-3/barrel.txt new file mode 100644 index 0000000..160e779 --- /dev/null +++ b/npc/005-3/barrel.txt @@ -0,0 +1,171 @@ +// TMW-2 script. +// Author: +// Crazyfefe +// Jesusalva +// Description: +// Barrels +// Variables: +// CandorQuest_Barrel +// Values: +// 0 Default. +// 1 Quest Found. +// 2 Quest Accepted. +// 3 Bomb Defused. +// 4 Quest Completed. + +function script CheckBarrel { + @barrel_count = 0; + @count_tmp = 0; + if ($@GM_OVERRIDE) + npctalk3 l("Hello, I am B-@@, of the @@ order.", @barrel, $@BarrelBits[@barrel]); + if (STARTAREA & $@BarrelBits[@barrel]) + goto L_Empty; + STARTAREA = STARTAREA | $@BarrelBits[@barrel]; + + goto L_Loop; + +L_BarrelTally: + areamonster "005-3", 24, 34, 45, 42, "House Maggot", 1084, 1, "Trainer::OnKillHouseMaggot"; + if (@barrel_count == 8) + { + message strcharinfo(0), "You found the bug bomb."; + specialeffect(14); + specialeffect(51); + killmonster("005-3", "Trainer::OnKillHouseMaggot"); + setq CandorQuest_Barrel, 3; + } + @rand = rand2(5); + if (@rand == 0) + getitem Coal,1; + else if (@rand == 1) + getitem BugLeg,1; + else if (@rand == 2) + getitem CommonCarp,1; + else if (@rand == 3) + getitem Croconut,1; + else if (@rand == 4) + getitem TolchiArrow,6; + return; + +L_Loop: + while (@count_tmp < 8) { + @count_tmp = (@count_tmp + 1); + if (STARTAREA & $@BarrelBits[@count_tmp]) + @barrel_count = (@barrel_count + 1); + } + goto L_BarrelTally; + +L_Empty: + message strcharinfo(0), "You've already searched this barrel."; + return; + +} + +005-3,24,36,0 script Barrel#1 NPC_NO_SPRITE,{ + @barrel = 1; + .@q = getq(CandorQuest_Barrel); + if (.@q == 2) + { + CheckBarrel(); + } + end; +OnInit: + // Remember: array start at zero, but barrels count start at 1. "Fixing" may break barrel 8! + setarray $@BarrelBits, (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9); + .sex = G_OTHER; + .distance = 1; + end; +} + +005-3,24,39,0 script Barrel#2 NPC_NO_SPRITE,{ + @barrel = 2; + .@q = getq(CandorQuest_Barrel); + if (.@q == 2) + { + CheckBarrel(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + +005-3,29,37,0 script Barrel#3 NPC_NO_SPRITE,{ + @barrel = 3; + .@q = getq(CandorQuest_Barrel); + if (.@q == 2) + { + CheckBarrel(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} +005-3,35,34,0 script Barrel#4 NPC_NO_SPRITE,{ + @barrel = 4; + .@q = getq(CandorQuest_Barrel); + if (.@q == 2) + { + CheckBarrel(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} +005-3,38,34,0 script Barrel#5 NPC_NO_SPRITE,{ + @barrel = 5; + .@q = getq(CandorQuest_Barrel); + if (.@q == 2) + { + CheckBarrel(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} +005-3,44,40,0 script Barrel#6 NPC_NO_SPRITE,{ + @barrel = 6; + .@q = getq(CandorQuest_Barrel); + if (.@q == 2) + { + CheckBarrel(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} +005-3,38,41,0 script Barrel#7 NPC_NO_SPRITE,{ + @barrel = 7; + .@q = getq(CandorQuest_Barrel); + if (.@q == 2) + { + CheckBarrel(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} +005-3,29,41,0 script Barrel#8 NPC_NO_SPRITE,{ + @barrel = 8; + .@q = getq(CandorQuest_Barrel); + if (.@q == 2) + { + CheckBarrel(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} diff --git a/npc/005-4/_import.txt b/npc/005-4/_import.txt new file mode 100644 index 0000000..e93dfbb --- /dev/null +++ b/npc/005-4/_import.txt @@ -0,0 +1,6 @@ +// Map 005-4: Armor Shop +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-4/_warps.txt", +"npc/005-4/rosen.txt", +"npc/005-4/shop.txt", +"npc/005-4/tolchi.txt", diff --git a/npc/005-4/_warps.txt b/npc/005-4/_warps.txt new file mode 100644 index 0000000..21addc1 --- /dev/null +++ b/npc/005-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-4: Armor Shop warps +005-4,25,43,0 warp #005-4_25_43 0,0,005-1,55,81 diff --git a/npc/005-4/rosen.txt b/npc/005-4/rosen.txt new file mode 100644 index 0000000..3c67021 --- /dev/null +++ b/npc/005-4/rosen.txt @@ -0,0 +1,221 @@ +// TMW2 scripts. +// Author: +// Saulc +// Description: +// Candor Armor&Weapon shop keeper. +// Variables: +// CandorQuest_Rosen +// Suggestion: Deliver a letter to Zegas, giving player background about +// Candor Island and Saxso. Requires level 5. Reward: 150 GP. +// Could have an additional step related to Bifs. Even a daily quest asking +// for (day % 8) ore, with suitable prices. +// +// 0 - Not assigned +// 1 - Quest Accepted +// 2 - Quest Complete +// 3 - Reward Taken +// 4 - Second Quest Accepted +// 5 - Second Quest Complete + +005-4,29,36,0 script Rosen NPC_GUARD1,{ + function explain_ironingot { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Did you see Jhedia the blacksmith in Tulimshar? She might know how you could get this."), + l("Nevertheless, you probably need some base materials from Bifs. Who knows what it will drop if you are lucky?"); + + return; + } + + speech S_LAST_NEXT, + l("Welcome to Tolchi and Rosen Shop."), + l("What would you like today?"); + + do + { + select + menuaction(l("Trade")), + l("How can I get iron ingot?"), + l("I want to improve my equipment."), + menuaction(l("Quit")); + + switch (@menu) + { + case 1: + if (!getq(General_Narrator) && TUTORIAL) { + mesn; + mesc l("Please spend responsibly. You still need to pay Nard for a ship travel and there's only so much money you can find on a small island like Candor."), 1; + mesc l("And some of the shop items can be found as drops or in quests!"); + next; + } + closedialog; + shop "Shop#Candor"; + close; + case 2: + explain_ironingot; + break; + case 3: + goto L_Gloves; + break; + case 4: + closedialog; + goodbye; + close; + } + } while (1); + + +L_Gloves: + .@q=getq(CandorQuest_Rosen); + .@b=getq(ShipQuests_ChefGado); + mes ""; + if (BaseLevel < 4) goto L_NoLevel; + if (.@b < 2) goto L_NoGloves; + if (.@q >= 3) goto L_Complete; + .@k=getq2(CandorQuest_Rosen); // Get number of kills (via getq2) + + mesn; + if (.@q == 0) { + mesq l("Ah, I see you have some used gloves. I'm not sure if you can even mine with it..."); + next; + mesn; + mesq l("Uhm, maybe I could teach you something, too. Go mine 5 @@. You should find some at northeast of the Island.", getmonsterlink(DiamondBif)); + tutmes l("Bif is a monster shaped like weird rocks. Diamond Bif is a Bif with higher chances to drop Diamonds."); + next; + mesn; + mesq l("These monsters are a great source of raw crafting materials."); + setq CandorQuest_Rosen, 1, 0; + } else if (.@q == 1) { + mesq l("You didn't mine enough @@. The perfect spot is at northeast of this island. It takes a while to them respawn, so don't hurry.", getmonsterlink(DiamondBif)); + } else if (.@q == 2) { + mesq l("Wow! Those pitiable gloves sure weren't made for mining. They're almost ruined!"); + mesq l("Here, take this @@. It will be better suited!", getitemlink(CandorGloves)); + inventoryplace CandorGloves, 1; + getexp 30, 5; + getitem CandorGloves, 1; + setq CandorQuest_Rosen, 3, 0; + } + close; + +L_NoLevel: + mesn; + mesq l("You aren't strong enough."); + next; + mesn; + mesq l("Go see someone else for now. Yes, you need level to take most tasks available on the world!"); + close; + +L_NoGloves: + mesn; + mesq l("You should have some decent gloves, dude. These offer defense, as it's easier to handle your weapon and parry attacks."); + next; + mesn; + mesq l("For sure the chef of Nard's ship could spare you a pair of gloves."); + close; + +L_Complete: + if (BaseLevel > 5 && .@q < 5 && countitem(TolchiArrow)) + goto L_Task; + mesn; + mesq l("Ah, uhm, I'm not sure. We at Candor don't need much."); + next; + mesn; + mesq l("You could try to get new equipment by doing more quests. You need level to use them, though."); + next; + mesn; + mesq l("Alternatively, I think someone at the Land Of Fire Village is able to refine some items. Why don't you try it sometime?"); + close; + +L_Task: + if (.@q != 4) { + mesn; + mesq l("Actually, I see you have some @@. Ever tried a bow before?", getitemlink(TolchiArrow)); + next; + mesn; + mesq l("Bows give you a good attack range, in exchange of all your evasion."); + mesq l("Meaning that once you equip a bow, you likely won't be able to dodge attacks."); + next; + mesn; + mesq l("Well, if you are good, you can just not get hit. If you're not so good, then bows will be a pain."); + setq CandorQuest_Rosen, 4; + } + mesq l("I was thinking, maybe I could make a @@ for you. But I want a few items:", getitemlink(TrainingBow)); + mesc l("@@/@@ @@", countitem(CactusDrink), 1, getitemlink(CactusDrink)); // Less than 1% drop + mesc l("@@/@@ @@", countitem(Piberries), 1, getitemlink(Piberries)); // Can be bought, or 6% drop from Mana Bug + next; + mesq l("Do you have that with you?"); + if (askyesno() == ASK_YES) { + mes ""; + if (!countitem(CactusDrink) || + !countitem(Piberries)) { + mesn; + mesq l("Now, listen closely. Jesusalva desgined most of the quests. And he hates cheaters and liars."); + next; + mesn; + mesq l("Actually, he's just too lazy to add proper checks everywhere. If you try to cheat, you'll suffer some penalty."); + next; + mesn; + mesq l("In this case, haven't I counted, I would have deleted only part of the items, then I would go silent. No refunds."); + mesq l("That's how this world inhabitants deal with cheaters... So don't be one, my friend. You have been warned!"); + close; + } + inventoryplace TrainingBow, 1; + delitem CactusDrink, 1; + delitem Piberries, 1; + setq CandorQuest_Rosen, 5; + getitem TrainingBow, 1; + mesn; + mesq l("Here you go, my friend. Uhm, good luck with archery."); + } + close; + + function rosen_add_kills + { + .@qp=getq(CandorQuest_Rosen); + .@kp=getq2(CandorQuest_Rosen); // Get number of kills (via getq2) + setq CandorQuest_Rosen, .@qp, .@kp+1; + //message strcharinfo(0), l("Set status @@ with @@ kills", .@qp, .@kp); + } + + function rosen_max_kills + { + .@qp=getq(CandorQuest_Rosen); + setq CandorQuest_Rosen, .@qp+1, 0; + //message strcharinfo(0), l("End status @@", .@qp); + } + +OnKillMBif: + .@q=getq(CandorQuest_Rosen); + .@k=getq2(CandorQuest_Rosen); // Get number of kills (via getq2) + if (.@q == 1) { + if (.@k+1 >= 5) { + rosen_max_kills(); + message strcharinfo(0), l("Go back to Rosen!"); + } else { + rosen_add_kills(); + message strcharinfo(0), l("@@/5 @@", .@k+1, getmonsterlink(DiamondBif)); + } + } + fix_mobkill(DiamondBif); + end; + + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 28, 36, + "dir", DOWN, 0, + "wait", 31, 0, + "move", 31, 36, + "dir", DOWN, 0, + "wait", 31, 0, + "move", 25, 35, + "dir", UP, 0, + "wait", 2, 0, + "move", 29, 36, + "dir", DOWN, 0, + "wait", 31, 0; + initialmove; + initnpctimer; + .distance = 5; +} diff --git a/npc/005-4/shop.txt b/npc/005-4/shop.txt new file mode 100644 index 0000000..67e1d5e --- /dev/null +++ b/npc/005-4/shop.txt @@ -0,0 +1,48 @@ +// TMW2 scripts. +// Author: +// Saulc +// Description: +// Rosen sells basic armor on Candor. Logic by Reid. + +005-4,30,37,0 trader Shop#Candor NPC_NO_SPRITE,{ + +OnInit: + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem RoundLeatherShield, -1, 2; + sellitem Knife, -1, 5; + sellitem TolchiArrow, -1, 30000; + sellitem TolchiAmmoBox, -1, 4; + sellitem LousyMoccasins, -1, 4; + sellitem TrainingBow, -1, 3; + + .sex = G_OTHER; + .distance = 3; + end; + +OnClock0602: +OnClock1206: +OnClock1811: +OnClock0004: + restoreshopitem RoundLeatherShield, -1, 2; + restoreshopitem Knife, -1, 5; + restoreshopitem TolchiArrow, -1, 30000; + restoreshopitem TolchiAmmoBox, -1, 4; + restoreshopitem LousyMoccasins, -1, 4; + restoreshopitem TrainingBow, -1, 3; + end; + + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} + diff --git a/npc/005-4/tolchi.txt b/npc/005-4/tolchi.txt new file mode 100644 index 0000000..05096b9 --- /dev/null +++ b/npc/005-4/tolchi.txt @@ -0,0 +1,407 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Variables: +// CandorQuest_Tolchi +// Description: +// Tolchi crafts weapons at Rosen & Tolchi shop +// Values: +// 0 BlackSmith quest ask for 1 iron oore +// 1 BlackSmith quest ask for 3 coal +// 2 BlackSmith quest ask for 1 iron ingot +// 3 Quest is Complete + + +005-4,42,37,0 script Tolchi NPC_RAIJIN_FEMALE_LEGION_ARTIS,{ + function quest_close; + function quest_giveitem; + function quest_first; + function quest_second; + function quest_third; + function tolchi_arrows; + function tolchi_tweaks; + + ///////////////////////////////////////////////////////////////////////////// + @q = getq(CandorQuest_Tolchi); + + if (@q == 3) { + mesn; + mesq l("Hey! How are you today? Thanks again for your help."); + } else if (BaseLevel < 5 || BaseLevel < 10 && @q == 1 || BaseLevel < 15 && @q == 2) { + mesn; + mesq l("I need help, but you aren't strong enough. Please come back later."); + next; + } else { + mesn; + mesq l("Hello! You seem strong enough, could take a request from me? Of course, not for free."); + next; + } + + do + { + select + l("I'm interested in your arrows, they're too expensive with Rosen."), + rif(@q == 0 && BaseLevel >= 5, l("Yes. What do you need help with?")), + rif(@q == 1 && BaseLevel >= 10, l("Yes. What do you need help with?")), + rif(@q == 2 && BaseLevel >= 15, l("Yes. What do you need help with?")), + l("Do you accept special requests?"), + l("I have other things to do at the moment."); + mes ""; + switch (@menu) + { + case 1: + tolchi_arrows(); + break; + case 2: + quest_first(); + break; + case 3: + quest_second(); + break; + case 4: + quest_third(); + break; + case 5: + tolchi_tweaks(); + break; + default: + quest_close(); + break; + } + } while (@menu != 6); + + closedialog; + goodbye; + close; + +// Close and talk about quest +function quest_close { + if (@q < 3) { + mesn; + mesq l("Maybe next time, then."); + next; + } + return; +} + +// Item delivery core +function quest_giveitem { +@q = getq(CandorQuest_Tolchi); + if (@q == 2) + { + if (countitem(IronIngot) == 0) + { + mesn; + mesq l("You don't have the Iron Ingot."); + next; + return; + } + delitem IronIngot, 1; + Zeny = Zeny + 8000; // Real worth: 2820 GP + 2400 (ship fee) = 5220 gp poll (the plus is net profit) + getexp 1575,0; + setq CandorQuest_Tolchi, 3; + speech S_FIRST_BLANK_LINE, + l("Thanks mate, that is everything I need! Have a good day!"); + } + if (@q == 1) + { + if (countitem(Coal) < 3) + { + mesn; + mesq l("You don't have the three Coal lumps."); + next; + return; + } + delitem Coal, 3; + Zeny = Zeny + 825; + getexp 105,0; + setq CandorQuest_Tolchi, 2; + } + if (@q == 0) + { + if (countitem("Iron Ore") == 0) + { + mesn; + mesq l("You don't have the Iron Ore."); + next; + return; + } + delitem IronOre, 1; + Zeny = Zeny + 225; + getexp 52,0; + setq CandorQuest_Tolchi, 1; + } + close; +} + +// First quest description +function quest_first { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Well, it is terrible! How can I make armours if I ran out of Iron? Shall the citizens of Candor Island perish in a monster attack?!"), // NOTE: I really prefer EN_US (eg. armor vs armour) + l("They shouldn't. Could you perhaps, kindly bring me 1 @@?", getitemlink(IronOre)); + do + { + //l("Do not worry, I'll seek and bring it to you."), + select + l("Do not worry, I have them right here."), + l("I am not a citizen of Candor."); + + switch (@menu) + { + case 1: + quest_giveitem(); + break; + case 2: + quest_close(); + break; + } + } while (@menu != 2); + return; +} + +// Second quest description +function quest_second { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Thanks for helping my shop earlier. I forge weapons, but unfortunately, I need more than just iron to forge them."), + l("Can you bring me 3 @@? Of course, you'll be rewarded.", getitemlink(Coal)); + do + { + // l("Hey, I like rewards. Wait me, I'll be back!"), + select + l("You better have it ready, because I have the Coal with me!"), + l("Eh, that seems too problematic. Sorry."); + + switch (@menu) + { + case 1: + quest_giveitem(); + break; + case 2: + quest_close(); + break; + } + } while (@menu != 2); + return; +} + +// Third quest description +function quest_third { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Ok, this will be my last request. The Trainer asked me for a fine weapon, to protect our village."), + l("Problem is, I do not have the knowledge to make it without @@. If you bring me one, I'll reward you with one quarter of my commission.", getitemlink(IronIngot)); + do + { + // l("Do not worry, I'll be back in a jiffy."), + select + l("No problem is too big for me. I have them right here!"), + l("Sorry, I am busy..."); + + switch (@menu) + { + case 1: + quest_giveitem(); + break; + case 2: + quest_close(); + break; + } + } while (@menu != 2); + return; +} + +// Make Tolchi Arrows (not cost-effective) +function tolchi_arrows { + // Price: 120~200, with 25 GP discount per task done (max. 3 tasks atm) + .@price=max(120, 200-(@q*25)); + mesn; + mesq l("Well, I can sell you a random box with almost 100 @@ for just @@ GP.", getitemlink(TolchiArrow), .@price); + next; + if (Zeny < .@price || askyesno() == ASK_NO) { + mesc l("Come back when you are willing to spend money."); + next; + } else { + mes ""; + inventoryplace TolchiArrow, 110; + Zeny=Zeny-.@price; + getitem TolchiArrow, rand2(102,106); + mesn; + mesq l("Here you go. Uhm, I really prefer if you buy with Rosen, though."); // I just don't want to add loops or inputs + next; + } + return; +} + +// Supreme Tweaking +function tolchi_tweaks { + // Price: the sky is the limit + .@price=max(120, 200-(@q*25)); + mesn; + mes l("Hmm, no, not really. However, I can try my hand at tweaking an item options :3"); + next; + mesn; + mesq l("Options are %s. Any craftsman can make them. Adventurers tend to be the best craftsman in the world.", b(l("item dependant bonuses"))); + next; + mesn; + mesq l("Usually you can only tweak if the item already have another option, but I am Tolchi. So, which item you want to tweak?"); + next; + + // Request and confirm + .@id=requestitemindex(); + if (!csys_Confirm(.@id)) + return false; + + // Find numeric ID + delinventorylist(); + getinventorylist(); + .@handle=@inventorylist_id[.@id]; + + // Invalid (absolutely should never happen) + if (.@handle < 1) { + mesn; + mesq l("...What? Which item? Sorry, too much smoke around here."); + next; + return; + } + // Multiple + if (countitem(.@handle) != 1) { + mesn; + mesq l("Sorry, but you have multiple %s.", getitemlink(.@handle)); + next; + return; + } + // Permission NG: (Not Granted) + if (array_find(.disallow_equip, .@handle) >= 0) { + mesn; + mesq l("Sorry, but I won't dare touch a %s.", getitemlink(.@handle)); + next; + mesn; + mesq l("Depending on the case, Nicholas, in Hurnscald, can do a better job than me."); + next; + return; + } + // Aleady slotted + if (getitemoptionidbyindex(.@id, 0) > 0) { + mesn; + mesq l("I'm not going to try to improve this masterpiece. Look its options!"); + next; + return; + } + + // Calculate price + .@lv=getiteminfo(.@handle, ITEMINFO_ELV); + .@price=.@lv**3; // Cubic function to determine price. + // Lv 1: 3 GP | Lv 10: 1kGP | Lv 20: 8kGP | Lv 40: 64kGP | Lv 60: 216kGP + // Lv 80: 512k GP | Lv 100: 1 Million GP! + if (isequipped(.@handle)) { + mesn; + mesq l("Please unequip your %s first.", getitemlink(.@handle)); + close; + } + mesn; + mesc l("Tweaking: %s", getitemlink(.@handle));//getinvindexlink(.@id)); + mesq l("That will be %s GP. Are you sure?", col(format_number(.@price), 1)); + next; + + if (Zeny < .@price || askyesno() == ASK_NO) { + mesc l("Come back when you are willing to spend money."); + next; + } else { + mes ""; + // Okay, time to force a tweaking + Zeny=Zeny-.@price; + + // Options which Tolchi knows + deletearray(@csys_attr); + setarray @csys_attr, 0, + IOPT_RICHNESS, 5, + IOPT_EXPGAIN, 9, + VAR_HPACCELERATION, 37, + VAR_MAXHPAMOUNT, 49; + + // Armor fix + if (getiteminfo(.@handle, ITEMINFO_TYPE) == IT_ARMOR) { + csys_ArmorFix(.@handle); + .@lv=max(1, .@lv/4); + } else { + csys_WeaponFix(.@handle); + } + + // Select a bonus and purge it + .@vartp=relative_array_random(@csys_attr); + + // Safety check + if (.@vartp < 1) + .@vartp=relative_array_random(@csys_attr); + + // First time player bonus + if (!#FIRST_TIME) { + .@vartp=IOPT_RICHNESS; + } + + // Apply the bonus + .@bonus=csys_Multiplier(.@lv); + .@bonus=limit(1, .@bonus, 25); + if (.@bonus > 5) + .@bonus=rand2(5, .@bonus); + if (.@vartp == VAR_MAXHPAMOUNT) + .@bonus+=rand2(5, 30); + setitemoptionbyindex(.@id, 0, .@vartp, .@bonus); + + // First time item bonus + if (!#FIRST_TIME) { + if (TOP3AVERAGELVL() > 50) { + .@vartp=IOPT_EXPGAIN; + //.@bonus=csys_Multiplier(.@lv); + .@bonus=limit(10, (TOP3AVERAGELVL()/2)-20, 30); + setitemoptionbyindex(.@id, 1, .@vartp, .@bonus); + } + #FIRST_TIME=true; + } + + .@mylevel=BaseLevel*(REBIRTH+1) + max(1, .@lv); + if (.@mylevel > 30) + setitemoptionbyindex(.@id, 3, IOPT_WALKSPEED, rand2(-10, -5)); + mesn; + mesc l("Well, here you are. I hope you enjoy ^^", 3); + next; + } + return; +} + +OnTimer1000: + domovestep; + +OnInit: + initpath "move", 44, 35, + "dir", UP, 0, + "wait", 30, 0, + "move", 43, 40, + "dir", UP, 0, + "wait", 30, 0, + "move", 40, 35, + "dir", UP, 0, + "wait", 30, 0, + "dir", LEFT, 0, + "wait", 30, 0, + "move", 42, 37, + "dir", DOWN, 0, + "dir", UP, 0, + "wait", 30, 0, + "move", 45, 37, + "dir", DOWN, 0, + "wait", 30, 0, + "move", 33, 37, + "dir", LEFT, 0, + "wait", 30, 0, + "move", 33, 37, + "dir", RIGHT, 0, + "wait", 30, 0; + + initialmove; + initnpctimer; + .distance = 5; + + setarray .disallow_equip, 0, SponsorNecklace, DiscordNecklace, Event1HSword, Event2HSword, EventBow, EventWand, Lightbringer, DemureAxe, Tyranny, Runestaff, AegisShield, BlacksmithAxe, Dustynator, SaviorShield, SaviorArmor, SaviorPants, SaviorBoots, Skypiercer, CreasedShirt, CreasedShorts; + end; +} diff --git a/npc/005-5/_import.txt b/npc/005-5/_import.txt new file mode 100644 index 0000000..6bdc135 --- /dev/null +++ b/npc/005-5/_import.txt @@ -0,0 +1,5 @@ +// Map 005-5: Candor Bank +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-5/_warps.txt", +"npc/005-5/cynric.txt", +"npc/005-5/nylo.txt", diff --git a/npc/005-5/_warps.txt b/npc/005-5/_warps.txt new file mode 100644 index 0000000..12df5de --- /dev/null +++ b/npc/005-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-5: Candor Bank warps +005-5,25,43,0 warp #005-5_25_43 0,0,005-1,46,79 diff --git a/npc/005-5/cynric.txt b/npc/005-5/cynric.txt new file mode 100644 index 0000000..af05aad --- /dev/null +++ b/npc/005-5/cynric.txt @@ -0,0 +1,15 @@ +// TMW-2 Script. +// Author: +// Saulc +// Jesusalva + +005-5,30,37,0 script Cynric NPC_LLOYD,{ + Banker(.name$, "Candor", 7700); + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + end; +} + diff --git a/npc/005-5/nylo.txt b/npc/005-5/nylo.txt new file mode 100644 index 0000000..18c3d31 --- /dev/null +++ b/npc/005-5/nylo.txt @@ -0,0 +1,221 @@ +// Author: +// Saulc +// Jesusalva + +005-5,27,38,0 script Nylo NPC_PLAYER,{ + function nylo_thanks; + function nylo_items; + function nylo_money; + function nylo_explain; + + + speech S_LAST_BLANK_LINE, + l("Visiting the bank too right? We're only a small village but Cynric is the best bank guy I know."); + + .@q=getq(CandorQuest_Marggo); + do + { + select + l("Yeah, you're right."), + l("You store a large collection of goods. Could you perhaps sell me some?"), + rif(!.@q,l("What do you for a living?")), + l("What is banking?"); + switch (@menu) + { + case 1: + break; + case 2: + mesn; + mesq l("Sure. I like to keep Alcoholic beverages, because they raise EXP gain when you drink @@.", b(l("with friends"))); + next; + mesn; + mesq l("Of course: better drinks, more EXP. Just be careful to don't get so drunk that you cannot fight anymore, will ya?"); + next; + if (TUTORIAL && !@beertuto) { + @beertuto=true; + tutmes l("Drinking with friends will give 1.5% extra XP boost for each person nearby."), l(".:: Alcohol Tutorial ::."); + tutmes l("Alcohol effects expire upon death. You need vitality to drink more beer."), l(".:: Alcohol Tutorial ::."); + tutmes l("Even if you drink alone, you'll still receive the EXP bonus marked on the item description."), l(".:: Alcohol Tutorial ::."); + } + if (!.@q) { + mesn; + mesc l("Unfortunately, I cannot sell them to you. Maggots are attacking my crops and giving me headache."), 1; + next; + mesn; + mesq l("Maybe if someone helped me to get rid of the maggots on my crops, I would be able to sell them to everyone again..."); + next; + mesn; + mesq l("...Otherwise, they're too precious to sell to wanna be adventurers. Beer EXP Bonuses expire on death, you know?!"); + next; + } else { + closeclientdialog; // Not needed? + openshop .name$; + close; + } + break; + case 3: + goto L_Main; + break; + case 4: + nylo_explain(); + .@mask=0; + do { + select + rif(!(.@mask & 1), l("How do I save items?")), + rif(!(.@mask & 2), l("How do I save money?")), + l("Thanks!"); + switch (@menu) { + case 1: + .@mask=.@mask|1; + nylo_items(); + break; + case 2: + .@mask=.@mask|2; + nylo_money(); + break; + case 3: + .@mask=.@mask|4; + nylo_thanks(); + break; + } + } while (true); + break; + } + } while (@menu != 1); + + closedialog; + goodbye; + close; + +L_Main: + mesn; + mesq l("Well, for a living, I usually tend to the crops."); + next; + mesn; + mes l("I raise them, water them, and then sell them."); + mes l("I also brew some of the crops, making Beer. And then I sell to adventurers!"); + if (BaseLevel < 6) + close; + next; + mesn; + mes l("Well, you're an adventurer, and I guess tending to crops isn't your cup of tea."); + mes l("But maybe you could help me killing a few maggots?"); + next; + mesn; + mes l("Eight Maggots will rise from the ground and will start destroying the crops."); + mes l("You must kill them before the crops are gone. I'll reward you, of course."); + next; + mesn; + mesq l("Interested?"); + if (askyesno() == ASK_NO) { + mes ""; + mesn; + mesq l("Heh. Fair enough."); + close; + } + + .@ID=getcharid(0); + .@MAP$="MRGO@"+str(.@ID); + + // Create the Marggo + .@INSTID = instance_create("MRGX@"+(.@ID), 0, IOT_NONE); + if (.@INSTID < 0) + .@instanceMapName$ = ""; + else + .@instanceMapName$ = instance_attachmap("005-1-1", .@INSTID, 0, .@MAP$); + // Instance already exists + if (.@instanceMapName$ == "") { + mesn; + mesq l("Wait. I remember you. You ruined my crops a few seconds ago!!"); + next; + mesn; + mesq l("Shooo, shooo! Give my crops some time to recover, your noob."); + close; + } + // It was just created, so begin Marggo Quest + instance_set_timeout(300, 300, .@INSTID); + instance_init(.@INSTID); + warp .@MAP$, 29, 20; + + // We'll need instance ID later and @vars are unreliable + MARGGO_ID=.@INSTID; + doevent("Nylo#Marggo::OnStart"); + closeclientdialog; + close; + + +// Functions +function nylo_thanks { + speech S_LAST_BLANK_LINE, + l("You are very welcome."); + close; +} + +function nylo_items { + speech S_LAST_BLANK_LINE, + l("Let Cynric open your storage. You might also want to open your inventory."), + l("Pick an item from either storage or inventory and choose what you want to do with it by clicking the right button."), + l("You can also pick and drag items from one window into the other but this will move all items of this kind."); + next; + speech S_LAST_BLANK_LINE, + l("Items in your storage are totally safe. Banking itself is totally safe."); + return; +} + +function nylo_money { + speech S_LAST_BLANK_LINE, + l("That's easy. Cynric will save your money when you ask him to deposit. Or you pick up your money by asking to withdraw."), + l("You only have to tell him how much you want to deposit or withdraw."), + l("Also you can ask how much he already save for you by checking your balance."); + next; + speech S_LAST_BLANK_LINE, + l("Money in your storage is totally safe. Banking itself is totally safe."); + return; +} + +function nylo_explain { + speech S_LAST_BLANK_LINE, + l("There are banks all over the world. Usually every important village or city has one."), + l("You can save both items and money at a bank."); + return; +} + +OnInit: + .@npcId = getnpcid(.name$); + //setunitdata(.@npcId, UDT_HEADMIDDLE, 1321); // Maybe Nylo could use the whole Candor set... + setunitdata(.@npcId, UDT_HEADTOP, CreasedShirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShorts); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 0); + + tradertype(NST_MARKET); + sellitem Beer, 320, 100; + + .sex = G_MALE; + .distance = 4; + end; + +// Restock +OnClock0600: +OnClock1200: +OnClock1800: +OnClock0004: + restoreshopitem Beer, 320, 100; + end; + + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} + diff --git a/npc/005-6/_import.txt b/npc/005-6/_import.txt new file mode 100644 index 0000000..365d443 --- /dev/null +++ b/npc/005-6/_import.txt @@ -0,0 +1,5 @@ +// Map 005-6: Candor Magic House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-6/_warps.txt", +"npc/005-6/morgan.txt", +"npc/005-6/zitoni.txt", diff --git a/npc/005-6/_warps.txt b/npc/005-6/_warps.txt new file mode 100644 index 0000000..545f754 --- /dev/null +++ b/npc/005-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-6: Candor Magic House warps +005-6,33,43,0 warp #005-6_33_43 0,0,005-1,50,86 diff --git a/npc/005-6/morgan.txt b/npc/005-6/morgan.txt new file mode 100644 index 0000000..727c334 --- /dev/null +++ b/npc/005-6/morgan.txt @@ -0,0 +1,156 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Morgan is the only mage in Candor. She was assigned by the Alliance to defend +// the small island, and is married with Zitoni. + +005-6,30,37,0 script Morgan NPC_RUMLY_FEMALE,{ + + mesn; + mesq lg("Hello there, darling."); + next; + menu + l("Who are you?"), L_Who, + l("Do you sell anything here?"), L_Shop, + l("Hi."), -; // Let's be honest, many conversations dies this way >.< + close; + +L_Who: + mes ""; + mesn; + mesq l("I am @@, the only mage in Candor.", .name$); + next; + mesq l("My husband and I have moved here under the Alliance orders."); + next; + mesq l("I really hope that nothing major happens, though. I would hate to see blood being spilled."); + next; + if (!MAGIC_LVL) + mesq l("If you train hard enough, and get access to a Mana Stone, I could teach you a trick or two. But right now, I don't sense magic compatibility in you."); + else + goto L_Magic; + close; + +L_Shop: + mes ""; + mesn; + mesq l("Why, I actually do sell a few things here! But they probably aren't what you're looking for."); + next; + openshop; + closedialog; + close; + +// Magic Subsystem +L_Magic: + mesn; + mesq l("I see you have magical powers. Have you been acknowledge by the Alliance as a mage? If not, you should regularize yourself as a @@º degree mage in Tulimshar Magic Council.", MAGIC_LVL); + +L_MagicCore: + select + l("Acknowledgment? What do you mean?"), + l("Magic Council? What's that?"), + rif(!getskilllv(TMW2_KALMURK), l("Can you teach me a spell?")), + rif(getskilllv(TMW2_KALMURK), l("I keep failing to summon maggots...")), + l("Thanks for the help!"); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("You won't learn any skill without a good professor to teach them to you."); + next; + mesn; + mesq l("And unless you have a mage permit from the Magic Council, you won't be going to CR1 - Academy Island either."); + break; + case 2: + mesn; + mesq l("You know the Tulimshar Magic Council? The big building on Tulimshar North?"); + next; + mesn; + mesq l("It rules Tulimshar, and is also one of the superior bodies of the Alliance. They have the last Mana Stone in the world on their meeting hall."); + next; + mesn; + mesq l("They restrict access to it to prevent new Monster Kings from emerging. They also keep record on every mage in the world."); + next; + mesn; + mesq l("In general, by touching the Mana Stone once, will make you a first degree mage. But these degrees goes up to seven and beyond, I think."); + next; + mesn; + mesq l("Of course, if you don't ask from the Mana Stone for more magic, you will stay on the first degree forever..."); + break; + case 3: + mesn; + mesq l("Sure! But that is Summon Magic. Just bring me a mug of beer, I'm thristy. And I'll teach you a basic skill."); + next; + if (askyesno() == ASK_YES) { + if (!countitem(Beer)) goto L_Cheat; + delitem Beer, 1; + skill(TMW2_KALMURK,1,0); + mesn; + mesq l("You can use @@ to summon some maggots. That depends on your magic level, of course.", b("Kalmurk")); + next; + mesn; + mesq l("That's a Summon Magic. Unlike regular magic, you can increase its levels directly from skill menu (F5)."); + next; + mesn; + mesq l("Keep in mind the more power you have, the less control you'll have as well. Until you get a %s to track progress, use %s to check your control. Less control may result in failure to summon!", getitemlink(JesusalvaGrimorium), b("@abizit")); + next; + mesn; + mesq l("By the way, you need a couple of @@ to try the skill. Fail rate is pretty high if you don't know how to control your magic.", getitemlink(MaggotSlime)); + } + break; + case 4: + mesn; + mesq l("Summoning is part of Mana Magic. Mana Magic is trickier, you must have control over magic power to use it properly."); + next; + mesn; + mesq l("This means everytime you get more power, you lose control. Pratice is the key."); + next; + mesn; + mesq l("Try praticing with different mana magic skills. Anyway, you can use this command to see your magic proeficiency: " + b("@abizit")); + break; + default: + close; + } + next; + goto L_MagicCore; + + +L_Cheat: + mesn; + mesq l("I offer an awesome skill for you, for a very slow price, and you try to cheat me... pitiful..."); + close; + +OnInit: + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem CottonCloth, -1, 7; + sellitem Fungus, -1, 10; + sellitem Bread, -1, 15; + + .sex = G_FEMALE; + .distance = 5; + end; + +OnClock0556: +OnClock1201: +OnClock1759: +OnClock0003: + restoreshopitem CottonCloth, 7; + restoreshopitem Fungus, 10; + restoreshopitem Bread, 15; + end; + + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} diff --git a/npc/005-6/zitoni.txt b/npc/005-6/zitoni.txt new file mode 100644 index 0000000..9d232f3 --- /dev/null +++ b/npc/005-6/zitoni.txt @@ -0,0 +1,222 @@ +// TMW2 scripts. +// Author: +// Vasily_Makarov (original from Evol) +// Jesusalva +// Description: +// Stat resetter. He is an alchemist, married with Morgan. +// Others: +// .@wasSP - free status points before reset + +005-6,43,39,0 script Zitoni NPC_RUMLY,{ + mesn; + mesq l("Ah, hello there! I am @@, a Redy alchemist.", .name$); + mes ""; + mesq l("I have developed a special formula, which resets your stats!"); + next; + mes ""; + +L_Menu: + .@q1=getq(TulimsharQuest_DarkInvocator); + if (BaseLevel < 10) + .@price = 1; + else + .@price = (BaseLevel*200-(9*200))/(BaseLevel/10); + // Lv 10: 1 GP + // Lv 90: 1.800 GP + + mesn strcharinfo(0); + select + l("Status reset? Sounds illegal!"), + l("Can you reset my stats please?"), + l("Do you make any other kind of potions?"), + rif(.@q1 == 1 || .@q1 == 2, l("Can you help me with Everburn Powder? I need 5.")), + rif(.@q1 == 3 && countitem(DarkDesertMushroom) >= 1,l("Zarkor sent you this gift. He needs Everburn Powder.")), + rif(.@q1 >= 4,l("I need some Everburn Powder.")), + lg("You are weird, I have to go sorry."); + + mes ""; + switch (@menu) { + case 1: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + lg("Wait, are you with the police? I didn't do anything wrong, I promise!"), + l("My formula is not a drug, nor magic. It is an ancient technology of our people!"), + l("You can use it to clear your stats, to start freshly if you know what I mean..."), + l("For only a small amount of Gold Pieces, I will show you how it works!"), + l("Although the more powerful you are, the more money you will need."), + l("I will let you test it for a peny until level 10!"); + + select + l("Sounds good!"), + rif(Zeny >= .@price, lg("I think I have enough gold with me.")), + l("We will talk about it later."), + l("My stats are too good, I won't need it."); + + switch (@menu) { + case 1: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Yes, it is a really sweet deal, believe me!"); + + goto L_Menu; + case 2: + goto L_ResetStats; + case 3: + goto L_Quit; + case 4: + goto L_Quit; + } + + case 2: + goto L_ResetStats; + case 3: + goto L_OtherPotion; + case 4: + goto L_DarkInv_Ever; + case 5: + goto L_DarkInv_Mush; + case 6: + goto L_DarkInv_Powder; + default: + goto L_Quit; + } + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + if (BaseLevel <= 10) + ConfirmStatusReset(0, false); + else + ConfirmStatusReset(-1, false); + goto L_Quit; + +L_OtherPotion: + mesn; + mesq l("I make both @@ and @@, if you give me the shrooms associated to them, and money.", getitemlink(HastePotion), getitemlink(StrengthPotion)); + next; + mesn; + mesq l("For you, it will be only 50 GP for potion! But I need the base ingredients, four @@ - or @@, depends on which one.", getitemlink(Plushroom), getitemlink(Chagashroom)); + next; + menu + l("Nothing at the moment."), L_Quit, + l("4 Plushrooms for a Haste Potion!"), L_HastePotion, + l("4 Chagashrooms for a Strength Potion!"), L_StrengthPotion; + +L_HastePotion: + mes ""; + mesn; + if (Zeny < 50) { + mesq l("You don't have enough money. Sorry."); + next; + goto L_Quit; + } + if (countitem(Plushroom) < 4) { + mesq l("I need @@ to work...", getitemlink(Plushroom)); + next; + goto L_Quit; + } + inventoryplace HastePotion, 1; + Zeny=Zeny-50; + delitem Plushroom, 4; + getitem HastePotion, 1; + mesq l("Here you go!"); + goto L_OtherPotion; + +L_StrengthPotion: + mes ""; + mesn; + if (Zeny < 50) { + mesq l("You don't have enough money. Sorry."); + next; + goto L_Quit; + } + if (countitem(Chagashroom) < 4) { + mesq l("I need @@ to work...", getitemlink(Chagashroom)); + next; + goto L_Quit; + } + inventoryplace StrengthPotion, 1; + Zeny=Zeny-50; + delitem Chagashroom, 4; + getitem StrengthPotion, 1; + mesq l("Here you go!"); + goto L_OtherPotion; + + +L_DarkInv_Ever: + mesn; + mesq l("...What? You want @@? Five of them?!", getitemlink(EverburnPowder)); + next; + mesn; + mesq l("You have no idea of how dangerous that item is! I can't simply do it!"); + next; + mesn; + mesq l("I don't know who sent you to me, but this is a flat and big NO!"); + next; + mesc l("Zitoni won't cooperate with you. Perhaps you should ask to Zarkor about that."); + setq TulimsharQuest_DarkInvocator, 2; + close; + +L_DarkInv_Mush: + mesn; + mesq l("Ah... A @@. The sturdiest from all mushroom, and very, very rare.", getitemlink(DarkDesertMushroom)); + next; + mesn; + mesc l("Zitoni seems to be lost on deep thought."); + next; + mesn; + mesq l("Ah... Well, ok. I'll do the powder for you, but you still need to bring me the material."); + next; + mesn; + mesq l("Just... Don't accidentaly invoke a Legendary Guardian or something, these thingies could destroy a whole city."); + delitem DarkDesertMushroom, 1; + setq TulimsharQuest_DarkInvocator, 4; + close; + +L_DarkInv_Powder: + mesn; + mesq l("@@ is an expensive, rare, and dangerous item. Do not shake it too much, or it will catch fire.", getitemlink(EverburnPowder)); + next; + mesn; + mesq l("...And trust me, it'll take way more than just water to put the fire down."); + next; + mesn; + mesq l("I will require 2500 GP, 1 @@, 1 @@ and 1 @@, for a small handful of it.", getitemlink(IronPowder), getitemlink(SulfurPowder), getitemlink(PileOfAsh)); + mes ""; + select + l("I still don't have everything, but don't worry, I'll be back."), + l("I have everything here with me."); + mes ""; + if (@menu == 1) + goto L_Quit; + if (countitem(IronPowder) < 1 || + countitem(SulfurPowder) < 1 || + countitem(PileOfAsh) < 1 || + Zeny < 2500) goto L_Liar; + Zeny=Zeny-2500; + delitem IronPowder, 1; + delitem SulfurPowder, 1; + delitem PileOfAsh, 1; + getitem EverburnPowder, 1; + getexp 100, 0; + mesn; + mesq l("Here you go. Handle it with caution."); + close; + +L_Liar: + mesn; + mesq l("Liar. You can't fool me. That's the price. Get that or get out!"); + next; + mesn; + mesq l("We're talking about @@. It's not something for kids or pranksters!", getitemlink(EverburnPowder)); + close; + +L_Quit: + closedialog; + goodbye; + close; // double sure + +OnInit: + .sex = G_MALE; + end; +} diff --git a/npc/005-7/_import.txt b/npc/005-7/_import.txt new file mode 100644 index 0000000..943f21d --- /dev/null +++ b/npc/005-7/_import.txt @@ -0,0 +1,5 @@ +// Map 005-7: Training House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/005-7/_warps.txt", +"npc/005-7/nurse.txt", +"npc/005-7/trainer.txt", diff --git a/npc/005-7/_warps.txt b/npc/005-7/_warps.txt new file mode 100644 index 0000000..0a105e5 --- /dev/null +++ b/npc/005-7/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 005-7: Training House warps +005-7,45,38,0 warp #005-7_45_38 0,0,005-1,34,100 diff --git a/npc/005-7/nurse.txt b/npc/005-7/nurse.txt new file mode 100644 index 0000000..4c27c5e --- /dev/null +++ b/npc/005-7/nurse.txt @@ -0,0 +1,95 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Crazyfefe + +005-7,41,30,0 script Candor's Nurse NPC_FEMALE,{ + + //if (sc_check(SC_POISON)) goto L_CurePoison; + if (Hp < (MaxHp/100*90)) goto L_Heal; + + mesn; + mesq lg("Hello, dear! I love @@, could you bring me some? They're sooooo sweet and delicious!","Hello, dear! I love @@, could you bring me some? They're sooooo sweet and delicious!", getitemlink(Candy) ); + mesq l("Ah, I can also cure you, if you need."); + + do + { + select + rif(Hp < MaxHp, l("Please heal me!")), + rif(countitem(Candy) > 0, l("Here, I have a candy!")), + l("I'm fine, no worries..."); + + switch (@menu) + { + case 1: + goto L_Heal; + break; + case 2: + goto L_Help; + break; + case 3: + goto L_Close; + break; + } + } while (@menu != 3); + +L_Help: + mes ""; + mesn; + + if (countitem(Candy) == 0) + { + mesq l("You don't seem to have any @@ with you!", getitemlink(.Item)); + close; + } + delitem .Item, 1; + mesq l("Thanks a lot!"); + if (getq(CandorQuest_Nurse) == 0) + { + getexp 21,0; + Zeny = Zeny + 200; + setq CandorQuest_Nurse, 1; + close; + } + if (getq(CandorQuest_Nurse) == 1) + { + getexp 11,0; + Zeny = Zeny + 100; + setq CandorQuest_Nurse, 2; + close; + } + if (getq(CandorQuest_Nurse) == 2) + { + getexp 6,0; + Zeny = Zeny + 50; + setq CandorQuest_Nurse, 3; + close; + } + getexp 5,0; + Zeny = Zeny + 25; + close; + +L_Heal: + Nurse(.name$, 10, 5); + close; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonShorts); + setunitdata(.@npcId, UDT_HEADMIDDLE, ShortTankTop); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 14); + + // quest setup + .Item = Candy; + + .sex = G_FEMALE; + .distance = 5; + end; + +} + diff --git a/npc/005-7/trainer.txt b/npc/005-7/trainer.txt new file mode 100644 index 0000000..2160918 --- /dev/null +++ b/npc/005-7/trainer.txt @@ -0,0 +1,660 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Originals: Ernando <ernando.quirino@hotmail.com> +// Original review: Lunovox <rui.gravata@gmail.com>; Ernando <ernando.quirino@hotmail.com>; Jesusalva <supremo@brasil.byethost22.com> +// Objective: Train the player and give some experience. +// License: GPL v3 +// +// CandorQuest_Trainer +// 0: Not started + +// 1: Took Maggot Quest +// 2: Completed Maggot Quest +// 3: Awaiting next quest + +// 4: Took House Maggot Quest +// 5: Completed House Maggot Quest +// 6: Awaiting next quest + +// 7: Took Candor Scorpion Quest +// 8: Completed Candor Scorpion Quest +// 9: Awaiting next quest + +// 10: Took Scorpion Quest +// 11: Completed Scorpion Quest +// 12: Finished all training + +005-7,25,29,0 script Trainer NPC_PLAYER,{ + goto L_Begin; + +OnRemoteHelp: +L_Begin: + .@q=getq(CandorQuest_Trainer); + .@b=getq(ShipQuests_Knife); // TODO: We should check if user is with weapon equipped instead + .@map$=getmap(); + mesn; + mesq lg("Hello my friend! Need my help?"); + next; + goto L_Menu; + +L_PreMenu: + mesn; + mesq l("Do you need my help?"); + next; + goto L_Menu; + +L_Menu: + mesn strcharinfo(0); + menu + l("Why do you want to help me?"), L_Backstory, + rif(.@map$ == "005-7" && getq(CandorQuest_Trainer) < 15, l("I want to be trained!")), L_Training, + l("I wanted info about how to play."), L_Manaplus_gap, + l("How do I make money?"), L_MakingMoney, + l("Monsters."), L_Monsters, + l("Quests."), L_Quests, + l("Status."), L_Status, + l("NPCs."), L_NPC, + l("Magic."), L_Magic, + l("Rules."), L_Rules, + l("Weather & Seasons."), L_Seasons, + l("Experience."), L_Experience, + l("No, thanks!"), L_Close; + + +L_Manaplus_gap: + mes ""; + goto L_Manaplus; + +L_Manaplus: + mesn; + mesq l("What do you want to learn more about?"); + next; + mesn strcharinfo(0); + menu + l("How do I see my items?"), L_ArmorItems, + l("How do I trade with other players?"), L_Trading, + l("How do I hunt monsters?"), L_HuntingMonsters, + l("How do I talk with someone?"), L_Talking, + l("Hairstyles."), L_Hairstyle, + l("Commands."), L_Commands, + l("I changed my mind."), L_Menu_gap; + +L_Training: + .@q=getq(CandorQuest_Trainer); + mes ""; + if (BaseLevel < 3) goto L_NoLevel; + if (.@b == 0) goto L_NoKnife; + .@k=getq2(CandorQuest_Trainer); // Get number of kills (via getq2) + + mesn; + if (.@q == 0) { + mesq l("It looks like you are ready. Let me see if there is an easy task for you..."); + next; + mesq l("Ah! I know. Kill 10 @@. They are usually found in the fields, feeding on the crops.", getmonsterlink(Maggot)); + setq CandorQuest_Trainer, 1, 0; + } else if (.@q == 1) { + mesq l("You are currently tasked with killing @@. As a reminder, you can usually find them where we grow our crops.", getmonsterlink(Maggot)); + } else if (.@q == 2) { + mesq l("Good job! Here is your reward!"); + getexp 30, 0; + set Zeny, Zeny + 25; + setq CandorQuest_Trainer, 3, 0; + } else if (.@q == 3) { + mesq l("Less maggots means a more reliable food supply. This kind of maggot will be a little more difficult to defeat, which is an excellent way to test your prowess in battle."); + next; + mesq l("In the storehouse, kill 4 @@. They keep coming back and contaminate our stored goods.", getmonsterlink(HouseMaggot)); + next; + mesq l("Our dear mayoress may also need help dealing with that maggot infestation."); + setq CandorQuest_Trainer, 4, 0; + } else if (.@q == 4) { + mesq l("Kill the @@ at the storehouse.", getmonsterlink(HouseMaggot)); + } else if (.@q == 5) { + mesq l("Perfect! Here is your reward!"); + getexp 42, 0; + set Zeny, Zeny + 25; + setq CandorQuest_Trainer, 6, 0; + } else if (.@q == 6) { + mesq l("As you learned, helping others is a good way to level up. You can also sell monster parts for some money."); + next; + mesq l("So, please kill 3 @@, which are usually found around our beaches. You can sell their stingers if you are short on money.", getmonsterlink(CandorScorpion)); + setq CandorQuest_Trainer, 7, 0; + } else if (.@q == 7) { + mesq l("The @@ I told you to kill are usually at the beach.", getmonsterlink(CandorScorpion)); + } else if (.@q == 8) { + inventoryplace SmallKnife, 1; + mesq l("Managed to get any nice drops? It requires more luck than skill. Here is your reward."); + getexp 53, 0; + set Zeny, Zeny + 25; + getitem SmallKnife, 1; + setq CandorQuest_Trainer, 9, 0; + } else if (.@q == 9) { + mesq l("This will be your last task. Besides the @@, the strongest monster on this island worth killing is the @@.", getmonsterlink(ManaBug), getmonsterlink(Scorpion)); + next; + mesq l("Kill 2 @@. This will prove your worth. They are northwest of the island.", getmonsterlink(Scorpion)); + setq CandorQuest_Trainer, 10, 0; + } else if (.@q == 10) { + mesq l("I want you to kill @@, which are located near the northwest coast of the island.", getmonsterlink(Scorpion)); + } else if (.@q == 11) { + inventoryplace Beer, 1; + mesq l("Congratulations! Here is your reward."); + mesq l("You completed your training, so you're getting 50 bonus experience points. If you level up, use your stat points wisely!"); + mesq l("Also, have a %s on the house!", getitemlink(Beer)); + getexp 70, 0; + set Zeny, Zeny + 25; + setq CandorQuest_Trainer, 12, 0; + getitem Beer, 1; + } else if (.@q == 12 && BaseLevel >= 9) { + mesq l("You are already fully trained. You should go to Tulimshar, but if you want a bonus task, please kill a @@.", getmonsterlink(ManaBug)); + mesq l("It should be almost a boss at your level, so I don't expect to see you again here so soon."); + setq CandorQuest_Trainer, 13, 0; + } else if (.@q == 12 && BaseLevel < 9) { + mesq l("You are already fully trained. You should go to Tulimshar. I may have another task for you later, but you are too weak now, get some levels."); + } else if (.@q == 13) { + mesq l("You are trying to kill a @@.", getmonsterlink(ManaBug)); + mesq l("It should be almost a boss at your level, so I don't expect to see you again here so soon."); + } else if (.@q == 14) { + mesq l("Wow! You did it! I do not think anyone else could have done that."); + mesq l("Here, take this @@ - you deserve it! And here is 200 GP to buy a better weapon.", getitemlink(CandorHeadBand)); + inventoryplace CandorHeadBand, 1; + getexp 80, 5; + getitem CandorHeadBand, 1; + set Zeny, Zeny + 200; + setq CandorQuest_Trainer, 15, 0; + } + next; + goto L_PreMenu; + +L_NoLevel: + mesn; + mesq l("Ah, yes... You see, there is just no task I can give to you right now. ##BYou are too weak to fight monsters.##b"); + next; + mesn; + mesq l("Try doing quests which doesn't involve monster hunting first. I'm sure ##B Ayasha ##b could use your help."); + next; + mesn; + mesq l("Otherwise, the monsters here usually won't attack you unless provoked."); + next; + mesn; + mesq l("Complete quests, gain some experience, allocate some status, and you'll be ready for my training."); + next; + goto L_PreMenu; + +L_NoKnife: + mesn; + mesq l("And with what you expect to fight monsters? You must arrange yourself a weapon!"); + next; + mesn; + mesq l("I don't know. Try getting a Rusty Knife or something. Maybe the chef of Nard's ship can spare you one."); + next; + mesn; + mesq l("There probably is a huge, flashing orange exclamation mark over a suitable knife you could take and nobody would mind."); + next; + goto L_PreMenu; + +// Anything below this line is copy-pasted from SG:ManaBird, a TMW-BR clone +// It was translated by Google and therefore may have bad terms about ManaPlus interface. +L_Backstory: + mes ""; + mesn; + mesq l("Saxso, the former mayor, commanded me to strengthen the youngsters, so that they have sufficient power to fight monsters."); + next; + mesn; + mesq lg("He died, but I plan in fulfilling his will. I can give you training for that, and teach you how to fight properly."); + next; + mesn; + mesq l("I see you have arms long enough to be an archer."); + next; + goto L_PreMenu; + +L_MakingMoney: + mes ""; + mesn; + mes l("Merchants like to buy body parts of killed monsters and animals because they can make items and equipment."); + next; + mesn; + mes l("Some others also like to buy them to keep as trophies. Either way, you can make some money with that."); + next; + mesn; + mes l("You must find someone willing to buy, they usually will buy almost anything you have, even items which cannot be replaced, so be careful."); + next; + mesn; + mes l("You must \"add\" the items you plan on selling, and then press \"sell\" to confirm. You'll have this time to review."); + mes l("Some, but not all, from the rare or non-replaceable items will have a warning when you try to sell them."); + next; + mesn; + mes l("You can also make money ##Bdoing quests##b. Elmo will tell you almost every quest which can be done in Candor."); + next; + goto L_PreMenu; + +L_ArmorItems: + mes ""; + mesn; + mes l("You can see all your equipment by pressing the F3 key."); + next; + mesn; + mes l("To equip or unequip an item, select it and press the 'Equip' or 'Unequip' button. You can not 'Equip' or 'Unequip' when talking to someone."); + next; + mesn; + mes l("Dress up! Do not walk without clothes! Always wear your items! They leave you less vulnerable to attacks and stronger to defeat your opponents."); + next; + mesn; + mes "##B"+l("Remember that some equipment sets will give you hidden stat bonuses! So dress yourself in a fashion way, if possible!")+"##b"; + next; + mesn; + mes l("To discard an item you no longer want, select it and press the 'Discard' button. Generic items can be discarded or sold."); + mes l("Some special items cannot be traded, discarded, nor sold. With a right click, you can also protect normal items as if they were special ones."); + next; + mesn; + mes l("There are three types of items."); + mes l("Those for consumption, equipment and generics."); + next; + mesn; + mes l("Items for consumption, like potions, can only be used once."); + mes l("Once used, they will disappear from your inventory."); + next; + mesn; + mes l("Equippable items are armour, weapons and accessories."); + mes l("They can be equipped to make your look more interesting or to improve some of its features."); + next; + mesn; + mesq l("Generic items are used for different purposes. In creating other items, to swap and sell, to collect, etc."); + next; + goto L_Manaplus; + +L_Trading: + mes ""; + mesn; + mes l("Press the 'R' key to ignore or accept business proposals. You and the other citizen who want to negotiate need to be in the configuration that accepts negotiations. if your configuration is 'Ignoring business proposals', then you will not receive the warning from any citizen wanting to negotiate with you, and you will not be able to initiate negotiations."); + next; + mesn; + mes l("To negotiate with other citizens, you should click the second mouse button on some other citizen who is accepting negotiations, and select the 'Negotiation' option from the menu that will appear."); + next; + mesn; + mes l("After you have confirmed the negotiation, a window with a vertical split will appear. The left side are the items you intend to offer in trading. The right side are the items that the other citizen intends to offer in trading."); + next; + mesn; + mes l("Open your inventory window (F3 key) next to the trading window. Select an item you want to offer, and then press the Add button. To add money to the negotiation, enter the amount you will offer and press the Change button."); + next; + mesn; + mes l("When you have added all the items and money you want, press the 'Propose Business' button. The other citizen must also press the 'Propose Business' button."); + next; + mesn; + mesq l("if the proposal is not convenient for you, just close the trading window to cancel the exchange of items and money. But if both press the 'Accept Negotiation' button, then the marketing will be finished."); + next; + mesn; + mes l("Remember! You're trading things, not lending/borrowing them. You are solely responsible for everything you own."); + next; + goto L_Manaplus; + +// TODO: We have over nine instructions here. You usually can only memorise from three to five at a time! +L_HuntingMonsters: + mes ""; + mesn; + mesq l("Note down. To hunt a target you must click the primary mouse button on it. Avoid fighting monsters or citizens much stronger than you. ##BYou will lose experience if you are defeated.##b"); + next; + mesn; + mes l("Within the cities is a place safe enough not to be attacked by another person (except during wars). But outside of them there are some places where the citizen can be attacked by enemies from other realms, or even by someone from the same realm."); + next; + mesn; + mes l("There are some stones scattered around the world that mark your point of return in case of defeats. Some ship chests may also serve as a return point. You can also select some beds in case of defeats."); + next; + mesn; + mes l("Almost all creatures drop useful items when defeated. To get the dropped item press the 'Z' key next to the item or click the primary button on the item."); + next; + mesn; + mes l("To focus on a creature, press the 'A' key. To focus on another citizen, press the 'Q' key. To attack the focused target press the 'X' key or click the primary button on the creature."); + next; + mesn; + mes l("To focus on an NPC, press the 'N' key. To talk to him press the 'T' key."); + next; + mesn; + mes l("To defocus or stop attacking, press Shift + A."); + next; + mesn; + mes l("You can, however, use ##BCtrl##b to auto-select a monster and attack them. This usually also collects drops, but press Z to be sure."); + next; + goto L_Manaplus; + +L_Talking: + mes ""; + mesn; + mes l("To display the dialog box with other citizens, press the F7 key."); + next; + mesn; + mes l("To speak in public select the 'General' tab. It serves to talk to people who are appearing on your screen."); + next; + mesn; + mes l("To speak privately with someone, click the second mouse button on the citizen and select the 'Whisper' option."); + next; + mesn; + mes l("In order to enter a message press the 'Enter' key, this will display the white box of typing. Type your message there and press 'Enter' again to send your speech."); + next; + mesn; + mes l("To speak privately to a friend who is not appearing on your screen, type the command '##B /q Citizen Name ##b' and press 'Enter'. This command will open a long-distance dialog that has the name of who you want to talk to. Select this new tab and send your message through it."); + next; + mesn; + mes l("And by last, to speak to everyone online, besides whoever might be idling on Discord, select the '#world' tab."); + next; + mesn; + mes l("But be careful: do not scream when using a lot of capital letters, and do not keep repeating the lines, and above all DO NOT SPAM, or you may be severely penalized."); + next; + goto L_Manaplus; + +L_Close: + close; + +L_Monsters: + mes ""; + mesn; + mesq l("Monsters are everywhere. They're a plague we're trying to get rid of."); + next; + mesn; + mesq l("There are three types of monsters: the aggressive, the neutral, and the collaborative."); + next; + mesn; + mes l("Aggressors always know when they are in danger! Therefore, they are always on standby, attacking anyone who appears ahead."); + next; + mesn; + mes l("Neutral monsters do not have such a sense of danger."); + mes l("They will not attack anyone unless they are attacked first."); + next; + mesn; + mes l("Normally, collaborative behave like neutral monsters. Unless some partner of the same species is in danger, at which point they all take an aggressive stance against the aggressor."); + mes l("It's always good to see if you have a lot of them around before you think about attacking one!"); + next; + mesn; + mes "\""+l("Also, most monsters get enraged and will attack whoever is closest to them, regardless of anything else."); + mes l("Not all monsters will do this, but most will. So if you see a monster running after a player and you stand in the way..."); + mes l("...It'll most likely attack you, instead.")+"\""; + next; + mesn; + mes l("One last thing to keep in mind... If you are surrounded, you'll suffer an agility and defense penalty."); + mes l("But if you and other players surrounds the monster instead, they'll suffer the same penalties!"); + mes l("Any boss which was previously unhittable, can be hit with appropriate number of attackers."); + next; + goto L_PreMenu; + +L_Hairstyle: + mes ""; + mesn; + mes l("NPC stylists can cut your hair and give you a new style, which means the hair style you woke up is something that can be changed."); + mes l("They are known to use a revolutionary hair growth formula, which can give you a wild thatch even if you are bald!"); + next; + goto L_Manaplus; + +L_Quests: + mes ""; + mesn; + mes l("There are people in the world who need help!"); + mes l("Most of these people will not think twice before giving a nice reward to anyone who helps them."); + mes l("So be nice and help people along the way!"); + next; + mesn; + mesq l("Seldomly, they'll have an exclamation mark over their heads. But some quests are hidden, so talk to people and have fun!"); + next; + goto L_PreMenu; + +L_NPC: + mes ""; + mesn; + mes l("NPCs(Non Playable Characters) or non-playable characters are characters that are always in the game, offering a wide variety of reactions, from a simple friendly conversation to a desperate request for help."); + next; + mes l("##BIMPORTANT:##b People usually doesn't shout, they talk. Because this, if you are too far, an NPC won't hear you."); + mes l("When this is the case, you should get closer to the NPC, until they hear you."); + mes l("If you are above the NPC and they still doesn't hear you, this mean they are deaf - you should report this!"); + goto L_PreMenu; + +L_Commands: + mes ""; + mesn; + mes l("/ clear clears the text box."); + mes l("/ whisper [name] allows you to send a private message to the player. if [name] contains spaces, it must be enclosed in quotation marks."); + //mes l("/who mostra o número de jogadores conectados no momento."); + mes l("/ present shows the number of people in the neighbourhood."); + mes l("/ where shows the name of the map you are in."); + mes l("/ help explains how to use all client commands."); + mes l("@commands lists even more advanced commands, but you can't use all of them."); + next; + mes l("@resync will help when the client starts lagging. If you see an attack but no monsters, that's the cause."); + mes l("@lang allows you to change game language, anytime, anywhere."); + mes l("@rules will tell you all the rules once again."); + mes l("@toevent will warp you to event island, if an event is happening, of course."); + mes l("@discord allows you to setup Discord integration settings."); + mes l("@ucp allows you to manage your account, eg. recover lost email."); + next; + mes l("@resyncall is the more powerful version of @resync. It'll reload everything, even the clouds if needed."); + mes l("@info and @tutorial will, using Jesusalva's powers, allow you to contact me anywhere for info."); + mes l("/ mi does the same as @monsterinfo. Takes the monster name as argument and reports monster stats and drops."); + next; + goto L_Manaplus; + +L_Status: + mes ""; + mesn; + mesq l("People vary greatly in the amount of strength, agility, dexterity, intelligence, vitality and luck."); + next; + mesn; + mes l("@@ helps you carry more items and also gives you a more forceful blow, but ends up not being very interesting if you focus on weapons that use projectiles, such as the bow.", b(l("Strength"))); + mes l("Greater @@ allows you to attack faster and has a greater chance of evading attacks.", b(l("agility"))); + mes l("Your @@ determines your ability to hit monsters and is valuable to players who prefer weapons that use projectiles.", b(l("dexterity"))); + next; + mesn; + mes l("@@ determines how many blows you can take before you die. It also affects status effects, like poison.", b(l("Vitality"))); + mes l("@@ is very useful for alchemy and magic, but nowadays there are few opportunities to use it.", b(l("Intelligence"))); + mes l("Your @@ determines several small things, including critical attacks and, limited to a certain extent, affect drop rates.", b(l("luck"))); + next; + mesn; + mes l("A critical hit deals added damage and disregards defense. A critical always hit, although it can be blocked just fine."); + mes l("On a side note, more defense is always good, but the damage won't decrease on the same rate that defense raises."); + mes l("Also note that if you are in overweight, your natural regen will halt. 90% in weight, and you won't be able to attack."); + next; + mesn; + mes l("I recommend that you train your agility a great deal, since most monsters out there aren't really amazing at hitting you."); + mes l("For now do not take too much time to work on your intelligence, after all, almost nobody have magic this day."); + next; + mesn; + mes l("You can allocate point on those attributes every time you level up."); + mes l("There's also a job level, which produces green sparkles when you level it."); + mes l("Job Level and certain equips can affect your status. You'll see the modifiers with a + sign."); + next; + goto L_PreMenu; + +L_Magic: + mes ""; + mesn; + mesq l("Magic is dead. Well, not yet, we still have some mana stones left - but only the strongest ones are allowed to use them and acquire magic."); + next; + mesn; + mesq l("I've heard about some adventurers whom obtained magic in another way, but you would still need a lot of levels for that."); + next; + goto L_PreMenu; + +L_Seasons: + mes ""; + mesn; + mes l("First of, there's a day/night cycle on the game."); + if (is_night()) + mes l("It's currently night, that's why Candor is dark."); + else + mes l("It's currently day, but when night falls, Candor will become darker."); + next; + mesn; + mes l("During night, the monsters usually respawn faster. That can be a problem with aggressive monsters."); + mes l("I also hear fisherman likes to fish at night. They say the catch is bigger, if you understand me."); + next; + mesn; + mes l("Not only that, but at night monsters are stronger! They also give more experience and drop more often to compensate, though."); + next; + mesn; + mes l("There's also weather, meaning it can rain, snow, or even happen a sandstorm. They are usually cosmetic, but..."); + mes l("...who knows if there isn't a secret in that?"); + next; + mesn; + mes l("Besides this, there is Seasons. You know, summer, autumn, winter and spring."); + mes l("Each season unlocks a set of quests and drops which can only be obtained on the season."); + next; + mesn; + mes l("Think on Season Quests as a yearly quest which you have three months to do."); + mes l("We follow north hemisphere seasons in case you're wondering."); + next; + goto L_PreMenu; + +L_Experience: + mes ""; + mesn; + mes l("Experience can be gained by completing quests and killing monsters. When you accumulate enough experience, you'll level up!"); + next; + mesn; + mes l("Each level up will buff your base stats, and give you stats points to allocate. However, there is Job Level."); + next; + mesn; + mes l("There's also a job level, which produces green sparkles when you level it."); + mes l("Job Level and certain equips can affect your status. You'll see the modifiers with a + sign."); + next; + mesn; + mes l("Also, you'll get more experience by killing monsters stronger than you, and less experience by killing monsters weaker than you."); + mes l("You can find out the monster strength by using \"@monsterinfo <English Monster Name>\". Check the level in it!"); + next; + goto L_PreMenu; + +L_Rules: + mes ""; + callfunc "GameRules"; + next; + mes ".:: "+l("Automatic Botchecks")+" ::."; + mes l("If enabled, automatic captchas may be sent to you every once in a while."); + mes l("To answer them, use: @captcha <num_value>"); + next; + CaptchExample(); + mes l("Example: Give the answer for the following: one+1"); + mes l("Reply: %s", b("@captcha 2")); + next; + goto L_PreMenu; + +L_Menu_gap: + mes ""; + goto L_PreMenu; + + function trainer_add_kills + { + .@qp=getq(CandorQuest_Trainer); + .@kp=getq2(CandorQuest_Trainer); // Get number of kills (via getq2) + setq CandorQuest_Trainer, .@qp, .@kp+1; + //message strcharinfo(0), l("Set status @@ with @@ kills", .@qp, .@kp); + return; + } + + function trainer_max_kills + { + .@qp=getq(CandorQuest_Trainer); + setq CandorQuest_Trainer, .@qp+1, 0; + //message strcharinfo(0), l("End status @@", .@qp); + return; + } + +OnKillMaggot: + .@q=getq(CandorQuest_Trainer); + .@k=getq2(CandorQuest_Trainer); // Get number of kills (via getq2) + if (.@q == 1) { + if (.@k+1 >= 10) { + trainer_max_kills(); + message strcharinfo(0), l("All maggots are dead!"); + } else { + trainer_add_kills(); + message strcharinfo(0), l("@@/10 Maggots", .@k+1); + } + } + // Additional Cactus Drink drop rate for newcomers: 4.9% additional + if (BaseLevel <= 22) { + if (rand2(10000) < 490-(BaseLevel**2)) { + getmapxy(.@m$, .@x, .@y, 0); + makeitem CactusDrink, 1, .@m$, .@x, .@y; + } + } + fix_mobkill(Maggot); + end; +OnKillHouseMaggot: + .@q=getq(CandorQuest_Trainer); + .@k=getq2(CandorQuest_Trainer); // Get number of kills (via getq2) + if (.@q == 4) { + if (.@k+1 >= 4) { + trainer_max_kills(); + message strcharinfo(0), l("All house maggots are dead!"); + } else { + trainer_add_kills(); + message strcharinfo(0), l("@@/4 House Maggots", .@k+1); + } + } else { + if (is_staff()) + dispbottom "It's working. (T:OKHM)"; + } + fix_mobkill(HouseMaggot); + end; +OnKillCandorScorpion: + if (!playerattached()) end; + .@q=getq(CandorQuest_Trainer); + .@k=getq2(CandorQuest_Trainer); // Get number of kills (via getq2) + if (.@q == 7) { + if (.@k+1 >= 3) { + trainer_max_kills(); + message strcharinfo(0), l("All candor scorpions are dead!"); + } else { + trainer_add_kills(); + message strcharinfo(0), l("@@/3 Candor Scorpions", .@k+1); + } + } + // Additional Bug Leg drop rate for newcomers: 5% additional + if (BaseLevel <= 22) { + if (rand2(10000) < 500-(BaseLevel**2)) { + getmapxy(.@m$, .@x, .@y, 0); + makeitem BugLeg, 1, .@m$, .@x, .@y; + } + } + fix_mobkill(CandorScorpion); + end; +OnKillScorpion: + .@q=getq(CandorQuest_Trainer); + .@k=getq2(CandorQuest_Trainer); // Get number of kills (via getq2) + if (.@q == 10) { + if (.@k+1 >= 2) { + trainer_max_kills(); + message strcharinfo(0), l("All scorpions are dead!"); + } else { + trainer_add_kills(); + message strcharinfo(0), l("@@/2 Scorpion", .@k+1); + } + } + fix_mobkill(Scorpion); + end; +OnKillManaBug: + .@q=getq(CandorQuest_Trainer); + .@k=getq2(CandorQuest_Trainer); // Get number of kills (via getq2) + if (.@q == 13) { + if (.@k+1 >= 1) { + trainer_max_kills(); + message strcharinfo(0), l("All mana bugs are dead!"); + } else { + trainer_add_kills(); + message strcharinfo(0), l("@@/1 Mana Bug", .@k+1); + } + } + fix_mobkill(ManaBug); + end; + +OnInit: + bindatcmd "info", "Trainer::OnRemoteHelp", 0, 60, 0; + bindatcmd "tutorial", "Trainer::OnRemoteHelp", 0, 60, 0; + + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, LeatherShirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, CottonTrousers); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 20); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/006-0/_import.txt b/npc/006-0/_import.txt new file mode 100644 index 0000000..8986616 --- /dev/null +++ b/npc/006-0/_import.txt @@ -0,0 +1,7 @@ +// Map 006-0: Candor's Underground, B1F +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-0/_mobs.txt", +"npc/006-0/_warps.txt", +"npc/006-0/core.txt", +"npc/006-0/piouisle.txt", +"npc/006-0/warpcandorbattle.txt", diff --git a/npc/006-0/_mobs.txt b/npc/006-0/_mobs.txt new file mode 100644 index 0000000..05bcb2c --- /dev/null +++ b/npc/006-0/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-0: Candor's Underground, B1F mobs +006-0,46,38,16,14 monster Little Yellow Slime 1233,6,120000,30000 +006-0,45,30,16,8 monster Little Red Slime 1234,4,120000,30000 +006-0,46,23,19,4 monster Mineral Bif 1058,1,300000,30000 +006-0,44,25,23,9 monster Big Diamond Bif 1109,1,240000,30000,Rosen::OnKillMBif diff --git a/npc/006-0/_warps.txt b/npc/006-0/_warps.txt new file mode 100644 index 0000000..68a575b --- /dev/null +++ b/npc/006-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-0: Candor's Underground, B1F warps +006-0,49,54,0 warp #006-0_49_54 0,0,005-1,66,63 diff --git a/npc/006-0/core.txt b/npc/006-0/core.txt new file mode 100644 index 0000000..4714a51 --- /dev/null +++ b/npc/006-0/core.txt @@ -0,0 +1,27 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-0,46,26,0 script Magic Barrier#0060 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (CRAZYPOINTS < 3) goto L_Reject; // FIXME + /* TODO: Piou Isles Arc */ + if (getq(General_Narrator) < 3) goto L_Reject; // Tulimshar Arc must be finished + + specialeffect(5000, SELF, getcharid(3)); + dispbottom l("The power which lies in Candor acknowledges your strength."); + sleep2(2500); + warp "006-3", 49, 52; + sleep2(500); + specialeffect(5001, SELF, getcharid(3)); + end; + +L_Reject: + specialeffect(5000, SELF, getcharid(3)); + dispbottom l("The power which lies in Candor rejects your strength."); + sleep2(3000); + specialeffect(5002, SELF, getcharid(3)); + end; +} diff --git a/npc/006-0/piouisle.txt b/npc/006-0/piouisle.txt new file mode 100644 index 0000000..7ad3afe --- /dev/null +++ b/npc/006-0/piouisle.txt @@ -0,0 +1,22 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-0,41,36,0 script Sign#0060PI NPC_SWORDS_SIGN,{ + mesc l("To the lovely cuteness; Unwavering helpfulness."); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +// Effective warp +006-0,40,35,0 script Magic Barrier#Pi NPC_HIDDEN,0,0,{ + end; + +OnTouch: + warp "006-2", 68, 117; + end; +} diff --git a/npc/006-0/warpcandorbattle.txt b/npc/006-0/warpcandorbattle.txt new file mode 100644 index 0000000..2a81960 --- /dev/null +++ b/npc/006-0/warpcandorbattle.txt @@ -0,0 +1,30 @@ +// TMW2 Script +// Author: +// Crazyfefe +// Jesusalva + + +006-0,53,36,0 script Sign#0060CF NPC_SWORDS_SIGN,{ + //mesc l("To the brave heart; An endless maze."); + mesc l("To the strong soul; Challenges to overthrow."); // challenges await thou? + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +// Effective warp +006-0,54,35,0 script Magic Barrier NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (BaseLevel >= 20 && !$@FEFE_CAVE_LEVEL) + warp "006-1", 49, 53; + else if (BaseLevel < 20) + npctalk3 l("You don't have the required level to pass this barrier."); + else + npctalk3 l("You can't pass this barrier while people are fighting inside!"); + end; +} diff --git a/npc/006-1/_import.txt b/npc/006-1/_import.txt new file mode 100644 index 0000000..c5c3938 --- /dev/null +++ b/npc/006-1/_import.txt @@ -0,0 +1,5 @@ +// Map 006-1: Crazyfefe's Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-1/barrier.txt", +"npc/006-1/crazyfefe.txt", +"npc/006-1/mapflags.txt", diff --git a/npc/006-1/barrier.txt b/npc/006-1/barrier.txt new file mode 100644 index 0000000..da4ca36 --- /dev/null +++ b/npc/006-1/barrier.txt @@ -0,0 +1,14 @@ +// TMW2 Script +// Author: +// Crazyfefe +// Jesusalva + +006-1,49,54,0 script #FightCaveBarrier NPC_NO_SPRITE,0,0,{ + if ($@FEFE_CAVE_LEVEL) goto L_Block; + warp "006-0", 54, 36; + end; + +L_Block: + dispbottom l("Your coward outer self fails to convince you to leave."); + end; +} diff --git a/npc/006-1/crazyfefe.txt b/npc/006-1/crazyfefe.txt new file mode 100644 index 0000000..8647207 --- /dev/null +++ b/npc/006-1/crazyfefe.txt @@ -0,0 +1,425 @@ +// TMW2 Script +// Author: +// TMW Org. (original code) +// Jesusalva +// Description: +// Crazyfefe will wake up when he smells battle +// Variables: +// $@FEFE_CAVE_LEVEL +// Tracks selected difficulty level. +// 0 - Not started +// 1 - Easy +// 2 - Normal +// 3 - Hard +// 4 - Out of Mind (Exclusive for top 3) +// 5 - Cronqvist Mode (Exclusive for Candor Battle Season) +// $@FEFE_DELAY +// Tracks the delay between challenges. Also used to start the combat. Set it to zero so you can start fight at once. +// $@FEFE_CAVE_HERO$ +// Whoever started a fight. Also used to know if a fight is about to begin. +// $@FEFE_CAVE_PLAYER_COUNT +// How many players are there. +// $@FEFE_WAVE +// Current wave +// $@GM_OVERRIDE +// Only possible with @set command, overrides the co-op requeriment. Affects other scripts. + +006-1,47,22,0 script Crazyfefe NPC_STATUE_EVILMAN,{ + function pleaseWait; + function Scoreboard; + function Challenge; + function aboutCandor; + function StartFight; + function CleanUp; + function CaveLogic; + function NextRound; + + // Battle already begun + if ($@FEFE_WAVE) + { + npctalk3 l("Pay attention to the fight, @@!", strcharinfo(0)); + dispbottom l("Round @@", $@FEFE_WAVE); + end; + } + if ($@FEFE_CAVE_HERO$ != "") { + pleaseWait(); + close; + } + + // Main Loop + mesn; + mesq l("Who dares to disturb my slumber?"); + next; + do + { + select + l("Sorry, sorry! Please rest, great Crazyfefe!"), + l("I do. I want to challenge the Candor Cave!"), + l("What are current leaderboard?"), + l("What is this place anyway?"); + mes ""; + switch (@menu) { + case 1: close; break; + case 2: Challenge(); break; + case 3: Scoreboard(); break; + case 4: aboutCandor(); break; + } + } while (true); + close; + +/// Functions +function pleaseWait { + // Battle in delay (either to begin or to allow new challenges) + if ($@FEFE_DELAY > gettimetick(2)) + { + .@time$=FuzzyTime($@FEFE_DELAY,2,2); + // Less than 30 seconds left, do not open a message window. + if ($@FEFE_DELAY-gettimetick(2) <= 30) { + npctalk3 l("@@!", .@time$); + end; + } + mesn; + mesq l("Be Patient... You still need to wait @@.", .@time$); + close; + } + return; +} + +// Are you a Candor Top 3? +// ISCANDORTOP3( - ) +function ISCANDORTOP3 { + return (strcharinfo(0) == $@cfefe_name$[0] || + strcharinfo(0) == $@cfefe_name$[1] || + strcharinfo(0) == $@cfefe_name$[2]); +} + +function Scoreboard { + mesc l("All leaderboards are refreshed hourly."), 1; + mesc l("Your current score: @@", CRAZYPOINTS), 3; + HallOfCandor(); + next; + return; +} + +function aboutCandor { + mesn; + mesq l("I am @@, guardian of Candor Cave. Below this cave, lies the legendary Fefe, arch-wizard from the Great War.", .name$); + next; + mesn; + mesq l("During the war, the Monster King cursed the place where he died, but he fought back, and cursed the curse."); + next; + mesn; + mesq l("For short, they had a fight with curses, and now there's a talking statue over his grave which can spawn monsters here."); + next; + mesn; + mesq l("If you pay the fee, I'll spawn them, for no longer than 20 minutes. You should not fight alone, instead, you need a group of at least three, because, you know, the curse. Or something."); + next; + return; +} + +function Challenge { + pleaseWait(); + if ($EVENT$ != "Candor") { + mesn; + mesq l("Very well, but for a fee of @@ GP. There's no free lunch, after all!", .price); + //mesc l("A minimum of %d players at the time of start is required!", 2), 1; + mesc l("If at any moment in the fight there's less than %d players in the room, a severe penalty will be applied, so watch out!", 3), 1; + + // Not enough money? We end here. + if (Zeny < .price) + close; + next; + } else { + mesn; + mesq l("Very well, let's see what you are made of!"); + mesc l("A minimum of %d players at the time of start is required!", 2), 1; + next; + } + + // Difficulty Selection + mesc l("Select difficulty."), 1; + mesc l("Out of Mind mode is only available for Top 3."); + mesc l("Cronqvist mode is only available during Candor Battle."); + mesc l("Only Cronqvist Mode yields more points per wave!"); + menuint + l("Easy"), 1, + l("Hard"), 3, + rif(ISCANDORTOP3() || is_master(), l("Out of Mind")), 5, + rif($EVENT$ == "Candor" || is_master(), l("Cronqvist Mode")), 12, + rif($EVENT$ == "Candor" && ISCANDORTOP3() || is_master(), l("Crazyfefe Mode")), 25, + rif($EVENT$ == "Candor" && $GAME_STORYLINE >= 4 || is_master(), l("Monster King Mode")), 100, + rif($EVENT$ == "Candor" && ISCANDORTOP3() && $GAME_STORYLINE >= 4 || is_master(), l("Mooubootaur Mode")), 250; + .@cdif=@menuret; + next; + + // Already started? + if ($@FEFE_WAVE || $@FEFE_CAVE_HERO$ != "") + { + mesn; + mesq l("Sorry, @@ already started the fight.", $@FEFE_CAVE_HERO$); + close; + } + + menuint + l("Sorry, I misclicked the first button. Have a nice nap, great Crazyfefe!"), -1, + rif(is_gm() || $EVENT$ == "Candor", l("WHAT ARE WE WAITING FOR?! START ALREADY!")), 0, + l("Yeah, I have the money. Give me FIVE minutes, and Bring it on!"), 5, + l("Yeah, I have the money. Give me TEN minutes, and Bring it on!"), 10, + l("Yeah, I have the money. Give me FIFTEEN minutes, and Bring it on!"), 15, + rif(is_gm(), l("I'm GM and got the money. Gimme HALF HOUR to invite everybody!")), 30; + + // Cancel? + if (@menuret < 0) + close; + + // Already started? + if ($@FEFE_WAVE || $@FEFE_CAVE_HERO$ != "") + { + mesn; + mesq l("Sorry, @@ already started the fight.", $@FEFE_CAVE_HERO$); + close; + } + + // Use the money + if ($EVENT$ != "Candor") + Zeny -= .price; + // Time delay + $@FEFE_DELAY = gettimetick(2)+60*@menuret; + $@FEFE_CAVE_HERO$ = strcharinfo(0); + .DIFFICULTY = .@cdif; + + // We must begin at once, so ignore normal procedure + if (!@menuret) { + logmes "candor fight - start without announce", LOGMES_ATCOMMAND; + closeclientdialog; + StartFight(); + } + + // Give you a bonus for starting the fight, and announce the fight. + @crazypoints=1; + initnpctimer; + .@msg$=$@FEFE_CAVE_HERO$+" invites everyone to a challenge against Candor Cave. It'll start in "+FuzzyTime($@FEFE_DELAY,2,2)+". Prepare yourselves!"; + kamibroadcast(.@msg$); + mesn; + mesq l("Please wait, the fight will start in @@, as requested.", FuzzyTime($@FEFE_DELAY,2,2)); + close; + return; +} + +function StartFight { + if (getmapusers("006-1") < 1 && !$@GM_OVERRIDE) { + mapannounce("006-1", "Oh noes! There's not enough players. Fight aborted, no refunds!", bc_map); + CleanUp(); + } + + $@FEFE_CAVE_LEVEL = .DIFFICULTY; + .DIFFICULTY = 0; + $@FEFE_WAVE = 0; + $@FEFE_DELAY = gettimetick(2)+.delay; + $@FEFE_CAVE_PLAYER_COUNT = getmapusers("006-1"); + mapannounce("006-1", "The battle is about to begin!", bc_map); + killmonsterall("006-1"); // Saulc likes to spawn stuff to heat stuff up + // Recalculate difficulty + $@FEFE_DIFFICULTY=0; + areatimer "006-1", 20, 20, 70, 60, 10, "Crazyfefe::OnSumLevel"; + initnpctimer; + end; +} + + + + +// Recalc difficulty +OnSumLevel: + if (ispcdead()) end; + $@FEFE_DIFFICULTY+=BaseLevel; + end; + +// Check if we're ready for next wave. Otherwise, do this check again after 5 seconds. +function CaveLogic { + .wtime+=5; // This is looped every 5 s + $@FEFE_CAVE_PLAYER_COUNT = getmapusers("006-1"); + + // Victory conditions: Too few players are alive OR twenty minutes went on. + // WARNING, dead players still in cave are counted, we might never finish the fight. + // And a GM might be called to ban everyone inside. + if ($@FEFE_CAVE_PLAYER_COUNT < 1 && !$@GM_OVERRIDE || gettimetick(2) >= $@FEFE_DELAY - 300) { + CleanUp(); + } + + // Recalculate difficulty + $@FEFE_DIFFICULTY=0; + areatimer "006-1", 20, 20, 70, 60, 10, "Crazyfefe::OnSumLevel"; + sleep(15); + + // Penalty for being below the player count thresholds + // It merely assumes you *are* within the threshold + if ($@FEFE_CAVE_PLAYER_COUNT < 2) + $@FEFE_DIFFICULTY *= 3; + else if ($@FEFE_CAVE_PLAYER_COUNT < 3) + $@FEFE_DIFFICULTY *= 2; + + // New wave condition: All mobs dead, or 90 seconds have passed + if (mobcount("006-1", "Crazyfefe::OnPetDeath") <= 0 || .wtime >= 90) + NextRound(); + + // reset timer + initnpctimer; + end; +} + +function NextRound { + .wtime=0; + $@FEFE_WAVE = $@FEFE_WAVE + 1; + // Prepare next round, and reward survivors + $@FEFE_DIFFICULTY+=$@FEFE_CAVE_PLAYER_COUNT+$@FEFE_WAVE*8; + $@FEFE_DIFFICULTY+=(($@FEFE_WAVE/5)*10); + .@mult=10+(3*$@FEFE_CAVE_LEVEL); // +30% per difficulty setting + $@FEFE_DIFFICULTY=$@FEFE_DIFFICULTY*.@mult/10; + areatimer "006-1", 20, 20, 70, 60, 10, "Crazyfefe::OnReward"; + + mapannounce "006-1", "The wave nº "+$@FEFE_WAVE+" is starting with " + $@FEFE_CAVE_PLAYER_COUNT + " player(s) left alive." , 0; + + .@amount=$@FEFE_WAVE+$@FEFE_CAVE_PLAYER_COUNT+rand2(1,2); + freeloop(true); + for (.@i = 0; .@i < .@amount; ++.@i) { + + .@monsterId=Piou; + .@lv=max(1, $@FEFE_DIFFICULTY/10); + + // Luck shall interfer with .@lv (up to +20 random levels) + // This prevents from excess of House Maggots, for example + .@lv+=any(0, 0, 0, 1, 1, 2); + //debugmes("[Candor] Difficulty is %d, pool is %d", .@lv, $@FEFE_DIFFICULTY); + .@lv=rand(0, .@lv); + + switch (.@lv) { + case 0: + case 1: + case 2: + //.@monsterId = any(HouseMaggot, HouseMaggot, SlimeBlast, HouseMaggot) ; break; + case 3: + .@monsterId = any(AngryScorpion, CaveMaggot, MagicGoblin, ViciousSquirrel, AngryBat, HouseMaggot, SlimeBlast) ; break; + case 4: + case 5: + case 6: + .@monsterId = any(AngryBat, RedSlime, AngryRedScorpion, DesertBandit, Bandit, Sarracenus, VampireBat, Skeleton, GreenSlime) ; break; + case 7: + case 8: + .@monsterId = any(GreenSlime, LavaSlime, Bluepar, RedMushroom, CandiedSlime, RobinBandit, AngryYellowSlime, OldSnake, GrassSnake, BlueSlime) ; break; + case 9: + case 10: + .@monsterId = any(BlueSlime, SaxsoGhost, Snake, BlackSlime, Wolvern, FireSkull, DarkLizard, BlackScorpion) ; break; + case 11: + case 12: + .@monsterId = any(BlackScorpion, DustRifle, DustGatling, DustRevolver, ArmoredSkeleton, Grenadier) ; break; + case 13: + case 14: + .@monsterId = any(DustRifle, DustGatling, DustRevolver, MountainSnake, Yeti, HoodedNinja, FallenGuard1, FallenGuard2) ; break; + case 15: + case 16: + .@monsterId = any(FallenGuard1, FallenGuard2, BanditLord, Yeti, WickedMushroom, Thug, Grenadier) ; break; + case 17: + case 18: + .@monsterId = any(Forain, WickedMushroom, BlackMamba, Michel, Terranite) ; break; + case 19: + case 20: + case 21: + .@monsterId = any(Forain, Archant, BlackMamba, JackO, TerraniteProtector, EliteDuck) ; break; + default: + .@monsterId = any(GoboBear, GiantMutatedBat, TerraniteProtector, Reaper) ; break; + } + .@m=areamonster("006-1", 20, 20, 70, 60, strmobinfo(1, .@monsterId), .@monsterId, 1, "Crazyfefe::OnPetDeath"); + set_aggro(.@m); + // We must lower difficulty according to summoned monster. Only 60% is lowered. + // If total level exceeds 250, it will cap at that to prevent excessive "slooping" + .@lower=(limit(1, .@lv, 25)*6); + .@lower=.@lower*7/10; // 70% + $@FEFE_DIFFICULTY=$@FEFE_DIFFICULTY-.@lower; + } + freeloop(false); + return; +} + +function CleanUp { + mapannounce "006-1", "Game over! Who will be the next to fall on Crazyfefe's Cave?", 0; + + areatimer "006-1", 20, 20, 70, 60, 10, "Crazyfefe::OnReward2"; + $@FEFE_OLD = $@FEFE_CAVE_LEVEL; + $@FEFE_CAVE_LEVEL = 0; + $@FEFE_WAVE = 0; + $@FEFE_CAVE_HERO$ = ""; + $@FEFE_DELAY=gettimetick(2)+180; + .wtime=0; + killmonster "006-1", "Crazyfefe::OnPetDeath"; + stopnpctimer; + initnpctimer; + stopnpctimer; + end; +} + + + + + +// Special case for survivors. +OnReward2: + if (@crazypoints > CRAZYPOINTS) { + CRAZYPOINTS=@crazypoints; + dispbottom l("Crazyfefe Cave: New Highscore: @@ points", CRAZYPOINTS); + @crazypoints=0; + } + if (!ispcdead() && $@FEFE_OLD >= .BONUS_LVL) + getitem StrangeCoin, limit(1, @crazypoints/25, 5); +// Rewards surviving players between rounds, according to performance, and get rid of dead PCs. +OnReward: + if (ispcdead()) { + recovery(getcharid(3)); + warp "Save", 0, 0; + // If your current points is more than your top, update + if (@crazypoints > CRAZYPOINTS) { + CRAZYPOINTS=@crazypoints; + dispbottom l("Crazyfefe Cave: New Highscore: @@ points", CRAZYPOINTS); + @crazypoints=0; + } + end; + } + // Player reward is a random number based on current wave + .@prize=rand($@FEFE_WAVE/2,$@FEFE_WAVE)*2; + // $@FEFE_DIFFICULTY might not be available anymore + if ($@FEFE_WAVE % 5 == 0) + Zeny+=.@prize; + getexp .@prize, rand(1,3); + @crazypoints+=($@FEFE_CAVE_LEVEL >= .BONUS_LVL && $EVENT$ == "Candor" ? 2 : 1); + end; + +// Every 5 seconds, handle cave, if fighting. Does nothing when waiting. +OnTimer5000: + if ($@FEFE_CAVE_LEVEL) + CaveLogic(); + end; + +// Announces and attempts to start the fight once time run out (assuming there are enough players) +OnTimer300000: + if ($@FEFE_CAVE_LEVEL > 0) end; + if ($@FEFE_DELAY <= gettimetick(2)) StartFight(); + .@msg$=$@FEFE_CAVE_HERO$+" invites everyone to a challenge against Candor Cave. It'll start in "+FuzzyTime($@FEFE_DELAY)+". Prepare yourselves!"; + announce .@msg$, bc_all|bc_npc; + //channelmes("#world", .@msg$); + //kamibroadcast(.@msg$); + initnpctimer; + end; + +OnPetDeath: + end; + +OnInit: + .sex=G_OTHER; + .distance=5; + .price=400; + .delay=(60*25); + .wtime=0; + .DIFFICULTY=0; + .BONUS_LVL=5; + end; +} diff --git a/npc/006-1/mapflags.txt b/npc/006-1/mapflags.txt new file mode 100644 index 0000000..58aeccf --- /dev/null +++ b/npc/006-1/mapflags.txt @@ -0,0 +1,2 @@ +006-1 mapflag zone MMO No Revive +006-1 mapflag bexp 115 diff --git a/npc/006-10/_config.txt b/npc/006-10/_config.txt new file mode 100644 index 0000000..bdc6b78 --- /dev/null +++ b/npc/006-10/_config.txt @@ -0,0 +1,20 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-10: Fefe's Crypt conf + +006-10,42,29,0 script #006-10_42_29 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=2; + end; +} + +006-10,44,29,0 script #006-10_44_29 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=2; + end; +} diff --git a/npc/006-10/_import.txt b/npc/006-10/_import.txt new file mode 100644 index 0000000..69724d9 --- /dev/null +++ b/npc/006-10/_import.txt @@ -0,0 +1,7 @@ +// Map 006-10: Fefe's Crypt +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-10/006-10_blackbox.txt", +"npc/006-10/_config.txt", +"npc/006-10/_mobs.txt", +"npc/006-10/_warps.txt", +"npc/006-10/logic.txt", diff --git a/npc/006-10/_mobs.txt b/npc/006-10/_mobs.txt new file mode 100644 index 0000000..734e9c1 --- /dev/null +++ b/npc/006-10/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-10: Fefe's Crypt mobs +006-10,44,34,15,15 monster Vampire Bat 1063,12,300000,30000 diff --git a/npc/006-10/_warps.txt b/npc/006-10/_warps.txt new file mode 100644 index 0000000..b39cc23 --- /dev/null +++ b/npc/006-10/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-10: Fefe's Crypt warps +006-10,43,49,0 warp #006-10_43_49 1,0,006-6,46,27 diff --git a/npc/006-10/logic.txt b/npc/006-10/logic.txt new file mode 100644 index 0000000..931a387 --- /dev/null +++ b/npc/006-10/logic.txt @@ -0,0 +1,196 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Crazyfefe's Shrine (lore bits) + +006-10 mapflag zone MMO + +006-10,46,45,0 script #00610WA NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 53, 46; end; +} + +006-10,58,45,0 script #00610WB NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 54, 38; end; +} + +006-10,49,38,0 script #00610WC NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 42, 40; end; +} + +006-10,55,37,0 script #00610WD NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 45, 46; end; +} + +006-10,44,39,0 script #00610WE NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 50, 38; end; +} + +006-10,38,39,0 script #00610WF NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 35, 35; end; +} + +006-10,30,34,0 script #00610WG NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 33, 25; end; +} + +006-10,38,24,0 script #00610WH NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 45, 22; end; +} + +006-10,50,21,0 script #00610WI NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 57, 29; end; +} + +006-10,52,28,0 script #00610WJ NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 45, 31; end; +} + +006-10,40,30,0 script #00610WK NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 37, 25; end; +} + +006-10,32,24,0 script #00610WL NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 31, 46; end; +} + +006-10,36,45,0 script #00610WM NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 41, 46; end; +} + +006-10,30,45,0 script #00610WN NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 57, 46; end; +} +// -------------------------------------------------- +006-10,52,45,0 script #00610WO NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 45, 46; end; +} + +006-10,40,45,0 script #00610WP NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 35, 35; end; +} + +006-10,36,34,0 script #00610WQ NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 40, 40; end; +} + +006-10,58,28,0 script #00610WR NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 49, 22; end; +} + +006-10,46,30,0 script #00610WS NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 53, 29; end; +} + +006-10,44,21,0 script #00610WT NPC_HIDDEN,0,3,{ + end; +OnTouch: + slide 37, 25; end; +} +// -------------------------------------------------- +006-10,43,28,0 script Crypt#Fefe NPC_NO_SPRITE,{ + mesn l("Crypt"); + mesc l("Herein lies Fefe, for his great bravure and heroic deeds, savior of our realm, valiantly defeated in combat against the Monster King, may the mana shine upon him forever."); + next; + mesn l("Crypt"); + mesc l("Herein also lies Raid Yahoo, enemy of manakind and nemesis of Saul, the one hated by many and loved by few, greedy mana stone collector, but who still opposed to the Monster King and lost her life for it."); + next; + mesn l("Crypt"); + mesc l("May their memory be remembered, but may their struggle be forgotten, their bodies never profaned, and in the æthyr, may they find eternal rest and comfort for their souls."); + close; + +OnInit: + .distance=2; + end; +} + + +006-10,33,44,0 script Diary#Fefe NPC_NO_SPRITE,{ + setpcblock(PCBLOCK_HARD, true); + mesc l("STORY MODE ENABLED. Monsters won't attack you, so you can read without worries."), 1; + mesc l("Here lies a diary, presumably from Fefe."); + next; + clear; + mesn b(l("Tulimshar, Tonori - 2nd March 300 AT")); + mesc l("Tomorrow is the day, when we shall march against Raid, in order to free our people from the oppression of her mages."); + dnext; + mesc l("Saul will lead the frontal assault, Mr. Saves shall provide distraction, and I'll infiltrate within the Mage Tower."); + dnext; + mesc l("I can't sleep, so I'm writing this entry. It'll take a considerable amount of time to decrypt the shield frequency Raid is using, and when Saul's army is marching towards their deaths, time is of essence. Every second I waste, is a comrade who will die."); + dnext; + mesc l("I'm a pro, this will be GG EZ. But Raid is not a foe to be underestimated, and I have no hopes for reinforcements. I refuse to leave last words, I shall either triumph, or be forgotten in shame."); + next; + mes ""; + mesn b(l("Wizards Tower, Tonori - 6th March 300 AT")); + mesc l("...I knew bandits were not be trusted, but Saul insisted in doing so. I hate him so much right now. Even if he is my brother."); + dnext; + mesc l("Raid and me have been in hot pursuit for a while, but I fear it is already too late. Yesterday there was another earthquake, and we're receiving summons to return to Tulimshar at once. Apparently, it is under attack."); + dnext; + mesc l("Saul will deal with it. His mess, he will clean up. Raid and me will keep the pursuit. And this time, we shall triumph."); + next; + mes ""; + mesn b(l("Wizards Tower, Tonori - 8th April 300 AT")); + mesc l("The Monster King once again eluded us. How long has it been? His minions are laying waste to all towns, Saul has been killed, Raid has been wounded."); + dnext; + mesc l("I'm fine, but I can't keep carrying dead weights with me. Candor is a special place, I shall make a base there... Because I know the Monster King will eventually head there."); + dnext; + mesc l("This time, I'll be prepared. His reign of terror has its days counted."); + next; + mes ""; + mesn b(l("Saxso's Basement, Candor - 27th July 300 AT")); + mesc l("This is my chance - the biggest Mana Quake ever, it feels like a whole world crushing in ours. I can feel the drain from Candor's Mana Reserves all the way up here."); + dnext; + mesc l("Now to head inside the caves, until the last floor, and have my showdown and revenge against the Monster King. I do not plan in failing."); + dnext; + mesc l("But if I do, I'll not return. Neither me, nor Raid. Which is why I'm taking precautions: This diary. In the event of my death, please bury my body and contact Halinarzo."); + dnext; + mesc l("If I can't beat him, no one else can, and if our species are to survive, we'll need to return to the Edge. Even if it means... Fulfilling the prophecy. %s. Whatever the cost.", b("Per Asper Ad Salvos")); + next; + 00610_Init_BlackBox(); + setpcblock(PCBLOCK_HARD, false); + closeclientdialog; + close; + +OnInit: + .distance=2; + end; +} + diff --git a/npc/006-2-1/_import.txt b/npc/006-2-1/_import.txt new file mode 100644 index 0000000..d5dc075 --- /dev/null +++ b/npc/006-2-1/_import.txt @@ -0,0 +1,9 @@ +// Map 006-2-1: Piou Houses +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-2-1/_mobs.txt", +"npc/006-2-1/_warps.txt", +"npc/006-2-1/chirp.txt", +"npc/006-2-1/lune.txt", +"npc/006-2-1/miltaco.txt", +"npc/006-2-1/plum.txt", +"npc/006-2-1/server.txt", diff --git a/npc/006-2-1/_mobs.txt b/npc/006-2-1/_mobs.txt new file mode 100644 index 0000000..02ce451 --- /dev/null +++ b/npc/006-2-1/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-1: Piou Houses mobs +006-2-1,28,29,1,1 monster Piousse 1003,4,30000,30000 +006-2-1,76,69,2,2 monster Piou 1002,6,30000,30000 +006-2-1,79,27,7,3 monster Piou 1002,4,30000,30000 +006-2-1,37,77,5,3 monster Piou 1002,3,30000,30000 +006-2-1,37,69,5,3 monster Piousse 1003,4,30000,30000 diff --git a/npc/006-2-1/_warps.txt b/npc/006-2-1/_warps.txt new file mode 100644 index 0000000..84ef7f8 --- /dev/null +++ b/npc/006-2-1/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-1: Piou Houses warps +006-2-1,24,23,0 warp #006-2-1_24_23 0,0,006-2,68,100 +006-2-1,38,65,0 warp #006-2-1_38_65 0,0,006-2,87,96 +006-2-1,79,23,0 warp #006-2-1_79_23 0,0,006-2,92,96 +006-2-1,83,65,0 warp #006-2-1_83_65 0,0,006-2,87,101 diff --git a/npc/006-2-1/chirp.txt b/npc/006-2-1/chirp.txt new file mode 100644 index 0000000..05de00c --- /dev/null +++ b/npc/006-2-1/chirp.txt @@ -0,0 +1,15 @@ +// TMW-2 Script. +// Author: +// +Seeds +// Jesusalva + +006-2-1,41,66,0 script Chirp NPC_PIOU_BANKER,{ + Banker(.name$, "Piou Isles", 8000); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + diff --git a/npc/006-2-1/lune.txt b/npc/006-2-1/lune.txt new file mode 100644 index 0000000..486ac01 --- /dev/null +++ b/npc/006-2-1/lune.txt @@ -0,0 +1,201 @@ +// TMW2 Script +// Author: +// Saulc +// Vasily_Makarov (original from Evol) +// Jesusalva +// seeds +// Description: +// Status Reset and Agility Potions + +006-2-1,34,66,0 script Lune NPC_PIOU_ALCHEMIST,{ + + speech S_LAST_NEXT, + l("I am %s, a piou alchemist specializing in reset and agility potions.", .name$); + +L_Menu: + .@plush_count = BaseLevel*210-(9*210); + // Lv 10: 210 GP + // Lv 90: 1.890 GP + if (BaseLevel > 10) + .@plush_count = .@plush_count/(BaseLevel/10); + + select + l("Can you reset my stats please?"), + l("Can you mix me an agility potion?"), + lg("I'm allergic to piou potions, goodbye."); + mes ""; + + switch (@menu) + { + case 1: + goto L_ResetStats; + case 2: + goto L_PotionList; + default: + goto L_Quit; + break; + } + close; + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + ConfirmStatusReset(); + goto L_Quit; + +L_PotionList: + mes ""; + mesn; + mesq l("I can make three types of Agility Potions, which would you like?"); + next; + select + rif(BaseLevel > 20, l("I want an Agi Potion.")), + rif(BaseLevel > 30, l("I want an Agi+ Potion.")), + rif(BaseLevel > 40, l("I want an Agi++ Potion.")), + l("I changed my mind, goodbye."); + mes ""; + switch (@menu) { + case 1: + goto L_AgiPotionA; + break; + case 2: + goto L_AgiPotionB; + break; + case 3: + goto L_AgiPotionC; + break; + default: + goto L_Quit; + break; + } + close; + +L_AgiPotionA: + .@price=POL_AdjustPrice(1000); + mes ""; + mesn; + mesq l("To make an %s I need a %s, an %s, and %s GP for commission.", getitemlink(AgiPotionA), getitemlink(TopazPowder), getitemlink(HerbalTea), fnum(.@price)); + next; + select + l("I have the ingredients here."), + l("I'll come back later."), + l("I don't need anything after all, goodbye!"); + if (@menu == 2) + goto L_Menu; + + if (@menu == 3) + goto L_Quit; + + if ( + countitem(TopazPowder) && + countitem(HerbalTea) && + Zeny >= .@price) { + + inventoryplace AgiPotionA, 3; + delitem TopazPowder, 1; + delitem HerbalTea, 1; + POL_PlayerMoney(.@price); + getitem AgiPotionA, any(2,3); + getexp rand2(6,18), rand2(6,18); + + mesn; + mesq l("Here you go. Perhaps you need another one?"); + next; + goto L_PotionList; + } else { + mesn; + mesq l("Sorry, but I need the ingredients and %d GP.", .@price); + next; + } + goto L_Menu; + +L_AgiPotionB: + .@price=POL_AdjustPrice(1250); + mes ""; + mesn; + mesq l("To make an %s I need a %s, two %s, and %s GP for commission.", getitemlink(AgiPotionB), getitemlink(Topaz), getitemlink(HerbalTea), fnum(.@price)); + next; + select + l("I have the ingredients here."), + l("I'll come back later."), + l("I don't need anything after all, goodbye!"); + if (@menu == 2) + goto L_Menu; + + if (@menu == 3) + goto L_Quit; + + if ( + countitem(Topaz) && + countitem(HerbalTea) >= 2 && + Zeny >= .@price) { + + inventoryplace AgiPotionB, 3; + delitem Topaz, 1; + delitem HerbalTea, 2; + POL_PlayerMoney(.@price); + getitem AgiPotionB, any(2,3); + getexp rand2(6,18), rand2(6,18); + + mesn; + mesq l("Here you go. Perhaps you need another one?"); + next; + goto L_PotionList; + } else { + mesn; + mesq l("Sorry, but I need the ingredients and %d GP.", .@price); + next; + } + goto L_Menu; + +L_AgiPotionC: + .@price=POL_AdjustPrice(1500); + mes ""; + mesn; + mesq l("To make an %s I need a %s, three %s, and %s GP for commission.", getitemlink(AgiPotionC), getitemlink(PolishedTopaz), getitemlink(HerbalTea), fnum(.@price)); + next; + select + l("I have the ingredients here."), + l("I'll come back later."), + l("I don't need anything after all, goodbye!"); + if (@menu == 2) + goto L_Menu; + + if (@menu == 3) + goto L_Quit; + + if ( + countitem(PolishedTopaz) && + countitem(HerbalTea) >= 3 && + Zeny >= .@price) { + + inventoryplace AgiPotionC, 3; + delitem PolishedTopaz, 1; + delitem HerbalTea, 3; + POL_PlayerMoney(.@price); + getitem AgiPotionC, any(2,3); + getexp rand2(6,18), rand2(6,18); + + mesn; + mesq l("Here you go. Perhaps you need another one?"); + next; + goto L_PotionList; + } else { + mesn; + mesq l("Sorry, but I need the ingredients and %d GP.", .@price); + next; + } + goto L_Menu; + +L_Quit: + closeclientdialog; + goodbye; + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} diff --git a/npc/006-2-1/miltaco.txt b/npc/006-2-1/miltaco.txt new file mode 100644 index 0000000..427b9a4 --- /dev/null +++ b/npc/006-2-1/miltaco.txt @@ -0,0 +1,55 @@ +// TMW2 scripts. +// Authors: +// Saulc +// Jesusalva +// Reid +// Travolta +// Modified by: +// +seeds +// Description: +// Miltaco is the barber. + +006-2-1,85,67,0 script Miltaco NPC_PIOU_BARBER,{ + mesn; + mesq l("Hi! I'm Miltaco, a piou specialist in humanoid hairstyles. Do you want a hair cut?"); + + do + { + select + l("What is my current hairstyle and hair color?"), + l("I'd like to get a different style."), + l("Can you do something with my color?"), + l("I'm allergic to pious, goodbye."); + + switch (@menu) + { + case 1: + BarberSayStyle 2; + break; + case 2: + BarberChangeStyle; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Enjoy your new style."); + l("Anything else?"); + break; + case 3: + BarberChangeColor; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("I hope you like this color."); + l("Anything else?"); + break; + case 4: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Come and visit again!"); + + goodbye; + } + } while (1); + close; + + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} diff --git a/npc/006-2-1/plum.txt b/npc/006-2-1/plum.txt new file mode 100644 index 0000000..cc987b4 --- /dev/null +++ b/npc/006-2-1/plum.txt @@ -0,0 +1,17 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// +seeds +// Description: +// Nurse. + +006-2-1,85,71,0 script Plum NPC_PIOU_NURSE,{ + Nurse(.name$, 10, 5); + close; + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} + diff --git a/npc/006-2-1/server.txt b/npc/006-2-1/server.txt new file mode 100644 index 0000000..8f0d04c --- /dev/null +++ b/npc/006-2-1/server.txt @@ -0,0 +1,54 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Modified by: +// +seeds + +006-2-1,41,74,0 script Server NPC_PIOU_SERVER,{ + + mesn; + mesq l("Hello! Would you like to try some piou delicacies?"); + next; + closedialog; + npcshopattach(.name$); + shop .name$; + close; + +OnInit: + + .sex = G_OTHER; + .distance = 5; + + tradertype(NST_MARKET); + sellitem Piberries, 20, 20; + sellitem Aquada, 120, 5; + sellitem LettuceLeaf, 30, 15; + sellitem Bread, 46, 10; + sellitem Cheese, 55, 15; + + end; + +OnClock0556: +OnClock1201: +OnClock1759: +OnClock0003: + restoreshopitem Piberries, 20, 20; + restoreshopitem Aquada, 120, 5; + restoreshopitem LettuceLeaf, 30, 15; + restoreshopitem Bread, 46, 10; + restoreshopitem Cheese, 55, 15; + end; + + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} diff --git a/npc/006-2-2/_import.txt b/npc/006-2-2/_import.txt new file mode 100644 index 0000000..87a6367 --- /dev/null +++ b/npc/006-2-2/_import.txt @@ -0,0 +1,4 @@ +// Map 006-2-2: Secret Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-2-2/_warps.txt", +"npc/006-2-2/ctrl.txt", diff --git a/npc/006-2-2/_warps.txt b/npc/006-2-2/_warps.txt new file mode 100644 index 0000000..4c7ce13 --- /dev/null +++ b/npc/006-2-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-2: Secret Cave warps +006-2-2,35,48,0 warp #006-2-2_35_48 1,0,006-2,71,87 diff --git a/npc/006-2-2/ctrl.txt b/npc/006-2-2/ctrl.txt new file mode 100644 index 0000000..4288cdc --- /dev/null +++ b/npc/006-2-2/ctrl.txt @@ -0,0 +1,13 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-2-2,35,24,0 script #PiouGate NPC_HIDDEN,1,0,{ + end; + +OnTouch: + // TODO Checks + dispbottom l("This gate is still sealed."); + end; +} + diff --git a/npc/006-2-3/_import.txt b/npc/006-2-3/_import.txt new file mode 100644 index 0000000..d6b7fb2 --- /dev/null +++ b/npc/006-2-3/_import.txt @@ -0,0 +1,4 @@ +// Map 006-2-3: Mushroom Cavern +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-2-3/_mobs.txt", +"npc/006-2-3/_warps.txt", diff --git a/npc/006-2-3/_mobs.txt b/npc/006-2-3/_mobs.txt new file mode 100644 index 0000000..29db028 --- /dev/null +++ b/npc/006-2-3/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-3: Mushroom Cavern mobs +006-2-3,44,34,14,13 monster Piou 1002,6,30000,30000 +006-2-3,43,32,8,7 monster Silk Worm 1034,6,30000,30000 +006-2-3,49,34,6,10 monster Red Butterfly 1025,2,30000,30000 +006-2-3,39,34,6,10 monster Cyan Butterfly 1172,2,30000,30000 diff --git a/npc/006-2-3/_warps.txt b/npc/006-2-3/_warps.txt new file mode 100644 index 0000000..e0052f7 --- /dev/null +++ b/npc/006-2-3/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-3: Mushroom Cavern warps +006-2-3,44,20,0 warp #006-2-3_44_20 0,0,006-2,62,109 +006-2-3,44,48,0 warp #006-2-3_44_48 0,0,006-2,68,114 diff --git a/npc/006-2-4/_import.txt b/npc/006-2-4/_import.txt new file mode 100644 index 0000000..516cd26 --- /dev/null +++ b/npc/006-2-4/_import.txt @@ -0,0 +1,4 @@ +// Map 006-2-4: Training Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-2-4/_mobs.txt", +"npc/006-2-4/_warps.txt", diff --git a/npc/006-2-4/_mobs.txt b/npc/006-2-4/_mobs.txt new file mode 100644 index 0000000..760f2ad --- /dev/null +++ b/npc/006-2-4/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-4: Training Room mobs +006-2-4,35,27,3,2 monster Piou Knight 1434,2,30000,30000 diff --git a/npc/006-2-4/_warps.txt b/npc/006-2-4/_warps.txt new file mode 100644 index 0000000..dac54f4 --- /dev/null +++ b/npc/006-2-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-4: Training Room warps +006-2-4,39,22,0 warp #006-2-4_39_22 0,0,006-2-5,43,24 diff --git a/npc/006-2-5/_import.txt b/npc/006-2-5/_import.txt new file mode 100644 index 0000000..7a07b21 --- /dev/null +++ b/npc/006-2-5/_import.txt @@ -0,0 +1,4 @@ +// Map 006-2-5: Throne Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-2-5/_mobs.txt", +"npc/006-2-5/_warps.txt", diff --git a/npc/006-2-5/_mobs.txt b/npc/006-2-5/_mobs.txt new file mode 100644 index 0000000..86c6e35 --- /dev/null +++ b/npc/006-2-5/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-5: Throne Room mobs +006-2-5,34,28,1,1 monster Forest Piou 1202,2,30000,30000 +006-2-5,42,28,1,1 monster Mana Piou 1155,2,30000,30000 diff --git a/npc/006-2-5/_warps.txt b/npc/006-2-5/_warps.txt new file mode 100644 index 0000000..aca0234 --- /dev/null +++ b/npc/006-2-5/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-5: Throne Room warps +006-2-5,38,35,0 warp #006-2-5_38_35 0,0,006-2,68,75 +006-2-5,34,21,0 warp #006-2-5_34_21 0,0,006-2-6,33,23 +006-2-5,43,23,0 warp #006-2-5_43_23 0,0,006-2-4,39,23 diff --git a/npc/006-2-6/_import.txt b/npc/006-2-6/_import.txt new file mode 100644 index 0000000..2b82b5a --- /dev/null +++ b/npc/006-2-6/_import.txt @@ -0,0 +1,4 @@ +// Map 006-2-6: Upper Level +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-2-6/_mobs.txt", +"npc/006-2-6/_warps.txt", diff --git a/npc/006-2-6/_mobs.txt b/npc/006-2-6/_mobs.txt new file mode 100644 index 0000000..6eeca13 --- /dev/null +++ b/npc/006-2-6/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-6: Upper Level mobs +006-2-6,35,31,1,1 monster Piou 1002,3,30000,30000 +006-2-6,41,31,1,1 monster Piousse 1003,3,30000,30000 diff --git a/npc/006-2-6/_warps.txt b/npc/006-2-6/_warps.txt new file mode 100644 index 0000000..8dd18ae --- /dev/null +++ b/npc/006-2-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2-6: Upper Level warps +006-2-6,34,23,0 warp #006-2-6_34_23 0,0,006-2-5,34,22 diff --git a/npc/006-2/_import.txt b/npc/006-2/_import.txt new file mode 100644 index 0000000..e459cf2 --- /dev/null +++ b/npc/006-2/_import.txt @@ -0,0 +1,4 @@ +// Map 006-2: The Piou Islands +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-2/_mobs.txt", +"npc/006-2/_warps.txt", diff --git a/npc/006-2/_mobs.txt b/npc/006-2/_mobs.txt new file mode 100644 index 0000000..ea4136d --- /dev/null +++ b/npc/006-2/_mobs.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2: The Piou Islands mobs +006-2,68,72,9,6 monster Forest Piou 1202,5,30000,30000 +006-2,92,100,9,6 monster Piousse 1003,12,30000,30000 +006-2,68,101,9,6 monster Mana Piou 1155,8,30000,30000 +006-2,44,100,9,6 monster Piou 1002,18,30000,30000 +006-2,68,117,2,3 monster Forest Piou 1202,1,20000,15000 +006-2,68,117,2,3 monster Mana Piou 1155,1,20000,15000 +006-2,68,72,6,5 monster Shrewboo 1435,4,30000,30000 diff --git a/npc/006-2/_warps.txt b/npc/006-2/_warps.txt new file mode 100644 index 0000000..24bc598 --- /dev/null +++ b/npc/006-2/_warps.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-2: The Piou Islands warps +006-2,68,101,0 warp #006-2_68_101 0,0,006-2-1,24,24 +006-2,87,97,0 warp #006-2_87_97 0,0,006-2-1,38,66 +006-2,92,97,0 warp #006-2_92_97 0,0,006-2-1,79,24 +006-2,87,102,0 warp #006-2_87_102 0,0,006-2-1,83,66 +006-2,61,109,0 warp #006-2_61_109 0,0,006-2-3,44,21 +006-2,68,113,0 warp #006-2_68_113 0,0,006-2-3,44,47 +006-2,68,118,0 warp #006-2_68_118 0,0,006-0,40,36 +006-2,68,74,0 warp #006-2_68_74 0,0,006-2-5,38,34 diff --git a/npc/006-3/_import.txt b/npc/006-3/_import.txt new file mode 100644 index 0000000..450a0a8 --- /dev/null +++ b/npc/006-3/_import.txt @@ -0,0 +1,7 @@ +// Map 006-3: Candor's Underground, B2F +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-3/_mobs.txt", +"npc/006-3/_warps.txt", +"npc/006-3/core.txt", +"npc/006-3/grotto.txt", +"npc/006-3/jaklab.txt", diff --git a/npc/006-3/_mobs.txt b/npc/006-3/_mobs.txt new file mode 100644 index 0000000..60f8634 --- /dev/null +++ b/npc/006-3/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-3: Candor's Underground, B2F mobs +006-3,46,38,16,14 monster Yellow Slime 1091,6,120000,30000 +006-3,44,36,15,14 monster Red Slime 1092,4,120000,30000 +006-3,45,33,18,14 monster Mineral Bif 1058,1,300000,30000 +006-3,44,32,23,12 monster Big Diamond Bif 1109,1,240000,30000 diff --git a/npc/006-3/_warps.txt b/npc/006-3/_warps.txt new file mode 100644 index 0000000..8bbe831 --- /dev/null +++ b/npc/006-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-3: Candor's Underground, B2F warps +006-3,49,49,0 warp #006-3_49_49 0,0,006-0,46,27 diff --git a/npc/006-3/core.txt b/npc/006-3/core.txt new file mode 100644 index 0000000..9311a10 --- /dev/null +++ b/npc/006-3/core.txt @@ -0,0 +1,27 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-3,46,26,0 script Magic Barrier#0063 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!getq(CandorQuest_Groata)) goto L_Reject; + if (!getq(CandorQuest_Jak1)) goto L_Reject; + if (getq(General_Narrator) < 10) goto L_Reject; // Halinarzo Arc must be finished + + specialeffect(5000, SELF, getcharid(3)); + dispbottom l("The power which lies in Candor acknowledges your strength."); + sleep2(2500); + warp "006-6", 49, 52; + sleep2(500); + specialeffect(5001, SELF, getcharid(3)); + end; + +L_Reject: + specialeffect(5000, SELF, getcharid(3)); + dispbottom l("The power which lies in Candor rejects your strength."); + sleep2(3000); + specialeffect(5002, SELF, getcharid(3)); + end; +} diff --git a/npc/006-3/grotto.txt b/npc/006-3/grotto.txt new file mode 100644 index 0000000..d7ff87b --- /dev/null +++ b/npc/006-3/grotto.txt @@ -0,0 +1,23 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-3,41,36,0 script Sign#0063PI NPC_SWORDS_SIGN,{ + mesc l("To the brave, shan't be your grave."); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +// Effective warp +006-3,40,35,0 script Magic Barrier#Bug63 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + warp "006-5", 49, 53; + //dispbottom l("This area is not yet ready. Please use the portal to the right instead."); + end; +} diff --git a/npc/006-3/jaklab.txt b/npc/006-3/jaklab.txt new file mode 100644 index 0000000..f2d1190 --- /dev/null +++ b/npc/006-3/jaklab.txt @@ -0,0 +1,27 @@ +// TMW2 Script +// Author: +// Crazyfefe +// Jesusalva + + +006-3,53,36,0 script Sign#0063CF NPC_SWORDS_SIGN,{ + mesc l("To the courageous, thy foe is rancorous."); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +// Effective warp +006-3,54,35,0 script Magic Barrier#0064 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (BaseLevel > 40) + warp "006-4", 32, 258; + else + dispbottom l("Your strength is not enough to power on this portal."); + end; +} diff --git a/npc/006-4-1/_config.txt b/npc/006-4-1/_config.txt new file mode 100644 index 0000000..363a2fb --- /dev/null +++ b/npc/006-4-1/_config.txt @@ -0,0 +1,16 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-4-1: Abandoned Building conf + +006-4-1,58,60,0 script #006-4-1_58_60 NPC_HIDDEN,6,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnCurse"; + end; +} + +006-4-1,44,31,0 script #006-4-1_44_31 NPC_HIDDEN,0,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnCurse"; + end; +} diff --git a/npc/006-4-1/_import.txt b/npc/006-4-1/_import.txt new file mode 100644 index 0000000..1fea850 --- /dev/null +++ b/npc/006-4-1/_import.txt @@ -0,0 +1,6 @@ +// Map 006-4-1: Abandoned Building +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-4-1/_config.txt", +"npc/006-4-1/_mobs.txt", +"npc/006-4-1/_warps.txt", +"npc/006-4-1/painting.txt", diff --git a/npc/006-4-1/_mobs.txt b/npc/006-4-1/_mobs.txt new file mode 100644 index 0000000..76eaeba --- /dev/null +++ b/npc/006-4-1/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-4-1: Abandoned Building mobs +006-4-1,55,103,19,9 monster Mana Ghost 1068,6,75000,250000 +006-4-1,55,46,31,11 monster Green Slime Mother 1236,6,75000,25000 +006-4-1,56,101,28,9 monster Yellow Slime Mother 1239,4,75000,25000 +006-4-1,54,151,14,10 monster Red Slime Mother 1240,3,75000,25000 diff --git a/npc/006-4-1/_warps.txt b/npc/006-4-1/_warps.txt new file mode 100644 index 0000000..0ce91aa --- /dev/null +++ b/npc/006-4-1/_warps.txt @@ -0,0 +1,24 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-4-1: Abandoned Building warps +006-4-1,27,108,0 script #006-4-1_27_108 NPC_HIDDEN,0,1,{ + end; +OnTouch: + slide 87,53; end; +} +006-4-1,86,103,0 script #006-4-1_86_103 NPC_HIDDEN,0,1,{ + end; +OnTouch: + slide 40,160; end; +} +006-4-1,39,160,0 script #006-4-1_39_160 NPC_HIDDEN,0,1,{ + end; +OnTouch: + slide 85,103; end; +} +006-4-1,58,61,0 warp #006-4-1_58_61 6,0,006-4,95,100 +006-4-1,88,53,0 script #006-4-1_88_53 NPC_HIDDEN,0,1,{ + end; +OnTouch: + slide 28,108; end; +} +006-4-1,44,30,0 warp #006-4-1_44_30 0,0,006-4,70,82 diff --git a/npc/006-4-1/painting.txt b/npc/006-4-1/painting.txt new file mode 100644 index 0000000..bb3cc1b --- /dev/null +++ b/npc/006-4-1/painting.txt @@ -0,0 +1,49 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-4-1,24,53,0 script #jakWestWind NPC_HIDDEN,0,0,{ + end; + +OnTouch: + dispbottom l("This passage is still sealed."); + end; +} + +006-4-1,69,158,0 script Abandoned Diary#jak1 NPC_NO_SPRITE,{ + setpcblock(PCBLOCK_HARD, true); + mesc l("STORY MODE ENABLED. Monsters won't attack you, so you can read without worries."), 1; + next; + clear; + mesc l("This diary had several pages torn off, but the last page is clearly legible."); + mes ""; + mesn; + mes l("It has been a total disaster. They killed %s. And everyone on the village.", b(l("her"))); + next; + mes l("Why did we had to decide to research slimes of all creatures?! But alas, now is too late. Not even a %s could restore her in this state...", getitemlink(ElixirOfLife)); + next; + mes l("So I did what I could - What I always do. Turned my beloved sister in a slime. Turned her into what killed her."); + next; + mes l("There is someone who might know how to turn a slime back in a human. But alas, that old fart doesn't live here."); + next; + mes l("Therefore, I shall head to the %s, and look for %s. If anyone knows how to fix it, might as well be that rat-faced... being.", b(l("Mirror Lake")), b(l("Wushin"))); + next; + mes l("If you're reading this - Feel free to use my laboratory in any way you deem fit. Or what's left of it, at least. Also, please kill any slimes you find. I shall be back in six leaps time, according to the Imperial Time, but if I'm not back by then, assume something went wrong and both wushin and me are off the table."); + next; + mes l("JAK1, THE SLIME RESEARCHER"); + mes l("Oranye Isles, 257 AT"); + + next; + setpcblock(PCBLOCK_HARD, false); + if (getvaultid()) + ##02_MLQUEST = ##02_MLQUEST | MLP_ML_JAK1; + compareandsetq CandorQuest_Jak1, 0, 1; + closeclientdialog; + close; + +OnInit: + .distance=2; + end; +} + + diff --git a/npc/006-4/_config.txt b/npc/006-4/_config.txt new file mode 100644 index 0000000..0b9ea89 --- /dev/null +++ b/npc/006-4/_config.txt @@ -0,0 +1,23 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-4: Abandoned Island conf + +006-4,71,82,0 script #006-4_71_82 NPC_HIDDEN,1,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnCurse"; + end; +} + +006-4,95,100,0 script #006-4_95_100 NPC_HIDDEN,1,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnCurse"; + end; +} + +006-4,32,258,0 script #006-4_32_258 NPC_HIDDEN,0,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnCurse"; + end; +} diff --git a/npc/006-4/_import.txt b/npc/006-4/_import.txt new file mode 100644 index 0000000..78369b5 --- /dev/null +++ b/npc/006-4/_import.txt @@ -0,0 +1,6 @@ +// Map 006-4: Abandoned Island +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-4/_config.txt", +"npc/006-4/_mobs.txt", +"npc/006-4/_warps.txt", +"npc/006-4/soulstone.txt", diff --git a/npc/006-4/_mobs.txt b/npc/006-4/_mobs.txt new file mode 100644 index 0000000..793827f --- /dev/null +++ b/npc/006-4/_mobs.txt @@ -0,0 +1,18 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-4: Abandoned Island mobs +006-4,57,82,10,24 monster Mana Ghost 1068,5,75000,25000 +006-4,59,192,16,19 monster Mana Ghost 1068,9,75000,250000 +006-4,94,179,17,15 monster Mana Ghost 1068,9,75000,250000 +006-4,128,183,14,19 monster Mana Ghost 1068,9,75000,250000 +006-4,102,202,20,8 monster Mana Ghost 1068,9,75000,250000 +006-4,101,153,21,10 monster Mana Ghost 1068,9,75000,250000 +006-4,95,66,39,11 monster Green Dragon 1195,6,75000,105000 +006-4,31,251,17,12 monster Green Slime Mother 1236,1,75000,25000 +006-4,66,203,15,11 monster Green Slime Mother 1236,3,75000,25000 +006-4,139,154,14,9 monster Green Slime Mother 1236,1,75000,25000 +006-4,149,181,7,19 monster Green Slime Mother 1236,1,75000,25000 +006-4,60,149,11,12 monster Green Slime Mother 1236,1,75000,25000 +006-4,142,77,10,22 monster Blue Slime Mother 1237,1,75000,25000 +006-4,94,91,34,11 monster Copper Slime Mother 1238,1,75000,25000 +006-4,94,85,35,6 monster Green Dragon 1195,6,75000,105000 +006-4,55,97,12,9 monster White Slime Mother 1242,1,120000,25000 diff --git a/npc/006-4/_warps.txt b/npc/006-4/_warps.txt new file mode 100644 index 0000000..cc25e7c --- /dev/null +++ b/npc/006-4/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-4: Abandoned Island warps +006-4,95,99,0 warp #006-4_95_99 1,0,006-4-1,58,60 +006-4,32,257,0 warp #006-4_32_257 0,0,006-3,54,36 +006-4,71,83,0 warp #006-4_71_83 1,0,006-4-1,44,31 diff --git a/npc/006-4/soulstone.txt b/npc/006-4/soulstone.txt new file mode 100644 index 0000000..c9e6c92 --- /dev/null +++ b/npc/006-4/soulstone.txt @@ -0,0 +1,16 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-4,21,243,0 script #WaterfallPass NPC_HIDDEN,1,0,{ + end; + +OnTouch: + slide 62, 163; + end; +} + +006-4,62,164,0 script Soul Stone#0064 NPC_NO_SPRITE,{ + slide 22, 244; + end; +} diff --git a/npc/006-5/_import.txt b/npc/006-5/_import.txt new file mode 100644 index 0000000..94dce7e --- /dev/null +++ b/npc/006-5/_import.txt @@ -0,0 +1,4 @@ +// Map 006-5: Groata Grotto +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-5/_warps.txt", +"npc/006-5/groata.txt", diff --git a/npc/006-5/_warps.txt b/npc/006-5/_warps.txt new file mode 100644 index 0000000..b9d0bcf --- /dev/null +++ b/npc/006-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-5: Groata Grotto warps +006-5,49,54,0 warp #006-5_49_54 0,0,006-3,40,36 diff --git a/npc/006-5/groata.txt b/npc/006-5/groata.txt new file mode 100644 index 0000000..ca149f7 --- /dev/null +++ b/npc/006-5/groata.txt @@ -0,0 +1,477 @@ +// TMW2 Script +// Author: +// Jesusalva +// Note: +// Groata Grotto is NOT instanced, NOT timed, and meant to be weird. + +function script GroataGrotto { + .@lv = getarg(0, $@GROATA); + + .@mb[0] = MagicGoblin; + .@mb[1] = IceMaggot; + + // Artillery + if (.@lv > 40) + array_push(.@mb, RobinBandit); + if (.@lv > 50) + array_push(.@mb, DustGatling); + if (.@lv > 60) + array_push(.@mb, DustRifle); + if (.@lv > 70) + array_push(.@mb, DustRevolver); + if (.@lv > 80) + array_push(.@mb, DustBoss); + if (.@lv > 90) + array_push(.@mb, GreatMoubooSlime); + if (.@lv > 100) + array_push(.@mb, Jhon); + + // Monsters + if (is_between2(0, .@lv, 30)) { + array_push(.@mb, Piou); + array_push(.@mb, Piousse); + array_push(.@mb, Squirrel); + array_push(.@mb, ManaPiou); + array_push(.@mb, ForestPiou); + array_push(.@mb, RedButterfly); + array_push(.@mb, Maggot); + array_push(.@mb, CandorScorpion); + array_push(.@mb, HouseMaggot); + array_push(.@mb, LittleYellowSlime); + array_push(.@mb, Ratto); + array_push(.@mb, RudolphSlime); + array_push(.@mb, MoubooSlime); + array_push(.@mb, Croc); + array_push(.@mb, Scorpion); + array_push(.@mb, SmallFrog); + } + if (is_between2(10, .@lv, 40)) { + array_push(.@mb, BigFrog); + array_push(.@mb, Lavern); + array_push(.@mb, LittleRedSlime); + array_push(.@mb, ChocolateSlime); + array_push(.@mb, Blub); + array_push(.@mb, Duck); + array_push(.@mb, Bat); + array_push(.@mb, CaveMaggot); + array_push(.@mb, ManaGhost); + array_push(.@mb, ManaBug); + array_push(.@mb, Fluffy); + array_push(.@mb, FireGoblin); + array_push(.@mb, ViciousSquirrel); + array_push(.@mb, RedScorpion); + array_push(.@mb, WhiteSlime); + array_push(.@mb, AzulSlime); + array_push(.@mb, DesertLogHead); + } + if (is_between2(20, .@lv, 50)) { + array_push(.@mb, RedSlime); + array_push(.@mb, PoisonSpikyMushroom); + array_push(.@mb, DesertBandit); + array_push(.@mb, OceanCroc); + array_push(.@mb, ToppyBlub); + array_push(.@mb, Sarracenus); + array_push(.@mb, IceMaggot); + array_push(.@mb, VampireBat); + array_push(.@mb, Bandit); + array_push(.@mb, Pinkie); + array_push(.@mb, LivingPotato); + array_push(.@mb, Assassin); + array_push(.@mb, Skeleton); + } + if (is_between2(30, .@lv, 60)) { + array_push(.@mb, CaveSnake); + array_push(.@mb, GreenSlime); + array_push(.@mb, CopperSlime); + array_push(.@mb, YellowSlime); + array_push(.@mb, SantaSlime); + array_push(.@mb, LavaSlime); + array_push(.@mb, Bluepar); + array_push(.@mb, DeathCat); + array_push(.@mb, Moggun); + array_push(.@mb, SeaSlime); + array_push(.@mb, RedMushroom); + array_push(.@mb, Mouboo); + array_push(.@mb, LogHead); + array_push(.@mb, CandiedSlime); + array_push(.@mb, OldSnake); + array_push(.@mb, GrassSnake); + } + if (is_between2(40, .@lv, 70)) { + array_push(.@mb, GiantMaggot); + array_push(.@mb, IcedFluffy); + array_push(.@mb, Snake); + array_push(.@mb, BlackSlime); + array_push(.@mb, Tipiou); + array_push(.@mb, AlphaMouboo); + array_push(.@mb, Pollet); + array_push(.@mb, PiouKnight); + array_push(.@mb, Shrewboo); + } + if (is_between2(40, .@lv, 80)) { + array_push(.@mb, Wolvern); + array_push(.@mb, FireSkull); + array_push(.@mb, DarkLizard); + } + if (is_between2(50, .@lv, 90)) { + array_push(.@mb, ArmoredSkeleton); + array_push(.@mb, BlackScorpion); + array_push(.@mb, ElectroWorm); + array_push(.@mb, EarthFairy); + array_push(.@mb, FireFairy); + array_push(.@mb, WaterFairy); + array_push(.@mb, WindFairy); + array_push(.@mb, PoisonFairy); + array_push(.@mb, MountainSnake); + array_push(.@mb, HoodedNinja); + array_push(.@mb, ForestMushroom); + array_push(.@mb, GoldenScorpion); + } + if (is_between2(60, .@lv, 100)) { + array_push(.@mb, Yeti); + array_push(.@mb, FallenGuard1); + array_push(.@mb, GreenSlimeMother); + array_push(.@mb, SnowFlower); + array_push(.@mb, BlueSlimeMother); + array_push(.@mb, WickedMushroom); + array_push(.@mb, CopperSlimeMother); + array_push(.@mb, YellowSlimeMother); + array_push(.@mb, RedSlimeMother); + array_push(.@mb, ChocolateSlimeMother); + array_push(.@mb, WhiteSlimeMother); + array_push(.@mb, Archant); + array_push(.@mb, Scar); + } + if (is_between2(70, .@lv, 110)) { + array_push(.@mb, AzulSlimeMother); + array_push(.@mb, SeaSlimeMother); + array_push(.@mb, LavaSlimeMother); + array_push(.@mb, BlackSlimeMother); + array_push(.@mb, Crafty); + array_push(.@mb, Forain); + array_push(.@mb, GreenDragon); + array_push(.@mb, Michel); + array_push(.@mb, Troll); + } + if (is_between2(80, .@lv, 120)) { + array_push(.@mb, EliteDuck); + array_push(.@mb, AzulSkullSlime); + array_push(.@mb, Moonshroom); + array_push(.@mb, RedSkullSlime); + array_push(.@mb, Terranite); + array_push(.@mb, JackO); + array_push(.@mb, BlackMamba); + array_push(.@mb, GreenSkullSlime); + array_push(.@mb, BloodyMouboo); + array_push(.@mb, Centaur); + array_push(.@mb, GoboBear); + } + if (is_between2(90, .@lv, 130)) { + array_push(.@mb, CopperSkullSlime); + array_push(.@mb, LavaSkullSlime); + array_push(.@mb, BlackSkullSlime); + array_push(.@mb, GiantCaveMaggot); + array_push(.@mb, TerraniteProtector); + array_push(.@mb, VanityPixie); + array_push(.@mb, HolyPixie); + } + if (is_between2(100, .@lv, 140)) { + array_push(.@mb, ShadowPixie); + array_push(.@mb, NulityPixie); + array_push(.@mb, Reaper); + array_push(.@mb, NightmareDragon); + array_push(.@mb, Snail); + array_push(.@mb, WhirlyBird); + } + if (is_between2(110, .@lv, 150)) { + array_push(.@mb, PinkieSuseran); + array_push(.@mb, Mandragora); + array_push(.@mb, PinkieMaximus); + } + if (.@lv > 120) { + array_push(.@mb, Junglefowl); + array_push(.@mb, Tengu); + array_push(.@mb, Moubi); + } + if (.@lv > 130) { + array_push(.@mb, SuperiorShroom); + array_push(.@mb, Nutcracker); + array_push(.@mb, Golem); + } + if (.@lv > 140) { + array_push(.@mb, SiegeTower); + array_push(.@mb, GreenhornAbomination); + array_push(.@mb, ShadowTortuga); + array_push(.@mb, FireElement); + array_push(.@mb, WaterElement); + array_push(.@mb, EarthElement); + array_push(.@mb, WindElement); + } + if (.@lv > 150) { + array_push(.@mb, SacredWisp); + array_push(.@mb, EvilWisp); + array_push(.@mb, PanthomWisp); + array_push(.@mb, EpiphanyWisp); + } + if (.@lv > 175) + array_push(.@mb, Tortuga); + + /* Spawn them and make hostile */ + freeloop(true); + for (.@i = 0; .@i < 1+($@GROTTO*2/3); .@i++) { + .@mid = any_of(.@mb); + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), strmobinfo(1, .@mid), .@mid, 1); + set_aggro(.@m); + } + freeloop(false); + + // TODO: Boss Logic if needed + // One boss each 10 grottos or something? + if (is_between2(10, $@GROTTO, 15)) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", MonsterSergeant, 1); + set_aggro(.@m); + } + if ($@GROTTO > 15) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", SaxsoGhost, 1); + set_aggro(.@m); + } + if ($@GROTTO > 20) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", BlueSlime, 1); + set_aggro(.@m); + } + if ($@GROTTO > 25) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", MurdererScorpion, 1); + set_aggro(.@m); + } + if ($@GROTTO > 30) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", NightScorpion, 1); + set_aggro(.@m); + } + if ($@GROTTO > 35) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", SpiderQueen, 1); + set_aggro(.@m); + } + if ($@GROTTO > 40) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", HoodedAssassin, 1); + set_aggro(.@m); + } + if ($@GROTTO > 45) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", BanditLord, 1); + set_aggro(.@m); + } + if ($@GROTTO > 50) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", Tipiu, 1); + set_aggro(.@m); + maptimer2("006-5", 10, "Torch#0065::OnLv50"); + } + if ($@GROTTO > 55) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", FafiDragon, 1); + set_aggro(.@m); + } + if ($@GROTTO > 60) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", NightDragon, 1); + set_aggro(.@m); + } + if ($@GROTTO > 65) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", GiantMutatedBat, 1); + set_aggro(.@m); + } + if ($@GROTTO > 70) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", EvilScythe, 1); + set_aggro(.@m); + } + if ($@GROTTO > 75) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", AndreiSakar, 1); + set_aggro(.@m); + } + if ($@GROTTO > 80) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", Yetifly, 1); + set_aggro(.@m); + } + if ($@GROTTO > 85) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", DemureFirstForm, 1); + set_aggro(.@m); + } + if ($@GROTTO > 90) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", YetiKing, 1); + set_aggro(.@m); + } + if ($@GROTTO > 95) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", FallenKing1, 1); + set_aggro(.@m); + } + if ($@GROTTO > 100) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", PsiConscience, 1); + set_aggro(.@m); + maptimer2("006-5", 10, "Torch#0065::OnLv100"); + } + if ($@GROTTO > 105) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", Isbamuth, 1); + set_aggro(.@m); + } + if ($@GROTTO > 110) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", TerraniteKing, 1); + set_aggro(.@m); + } + if ($@GROTTO > 120) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", PinkieEmperor, 1); + set_aggro(.@m); + } + if ($@GROTTO > 130) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", PanthomLord, 1); + set_aggro(.@m); + } + if ($@GROTTO > 140) { + .@m=areamonster("006-5", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-5"), getmapinfo(MAPINFO_SIZE_Y,"006-5"), "Groata?", MonsterKing, 1); + set_aggro(.@m); + } + if ($@GROTTO > 150) + maptimer2("006-5", 10, "Torch#0065::OnLv150"); + return; +} + +006-5,49,36,0 script Torch#0065 NPC_NO_SPRITE,{ + if (!mobcount("006-5", "all")) { + if ($@GROTTO) { + mesc l("The torch is currently lit."); + mesc l("Do you want to put it out, or to make it brighter?"); + next; + select + l("I have to think."), + l("Put it out"), + l("Make it brighter"); + mes ""; + // Put it out + if (@menu == 2) { + $@GROTTO = 0; + $@GROATA = 0; + enablenpc "#006-5_49_54"; + } + // I do not want to continue + if (@menu != 3) + close; + } else { + mesc l("Lit the torch?"); + if (askyesno() == ASK_NO) close; + disablenpc "#006-5_49_54"; + } + if (!getq(CandorQuest_Groata)) { + getitem any(Coal, SulfurPowder, FluoPowder, PileOfAsh, Pearl), 2; + setq CandorQuest_Groata, 1; + } + $@GROTTO += 1; + // Create monsters based on GROTTO + $@GROATA = max(BaseLevel, $@GROATA+1); + GroataGrotto(); + close; + } + mesc l("The torch is lit."); + close; + +// Rewards +OnLv50: + if (getq(CandorQuest_Groata) == 1) { + dispbottom l("This is ridiculous. Groata is not here, I am wasting my time!"); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Uh? Hey, look! A platinum card! This should be worth something with the Guild Merchant Guild, right?"); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("...I'll take it."); + Zeny+=7500; + Mobpt+=2500; + getexp 0, 10000; + setq CandorQuest_Groata, 2; + } + end; + +OnLv100: + if (getq(CandorQuest_Groata) == 2) { + dispbottom l("Groata : Uh. What's happening here?"); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Groata : Hey. I live here. Why are you spawning monsters in my home??"); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Groata : This is rude. Seriously, stop doing this."); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Groata : Let's make a deal. Take this, clear my house, and get out."); + getitem Mustache, 1; + setq CandorQuest_Groata, 3; + } + end; + +OnLv150: + if (getq(CandorQuest_Groata) == 3) { + dispbottom l("Groata : You must be idiotic in keeping spawning."); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Groata : I wonder for how long you can survive here..."); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Groata : ...Will you seriously just keep running around? Like a headless chicken?"); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Groata : C'mon, that is boring. What I need to do to get you out of my house?"); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Groata : ...Meh. I'll give you this... stuff, and for the last time, don't bother me again..."); + sleep2(5000); + if (getmap() != "006-5") end; + dispbottom l("Groata : ...Because I am out of freebies and need to sleep. So shoo."); + getitembound MylarinDust, 1, 1; + setq CandorQuest_Groata, 4; + } + end; + +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + +006-5,52,36,0 script Crystal#0065 NPC_NO_SPRITE,{ + if (!mobcount("006-5", "all")) { + if ($@GROTTO) { + mesc l("The crystal looks damaged."); + mesc l("Do you want to restore it, or to keep breaking it?"); + next; + select + l("I have to think."), + l("Restore it"), + l("Keep breaking"); + mes ""; + // Restore it + if (@menu == 2) { + $@GROTTO = 0; + $@GROATA = 0; + enablenpc "#006-5_49_54"; + } + // I do not want to continue + if (@menu != 3) + close; + } else { + mesc l("Break the crystal?"); + if (askyesno() == ASK_NO) close; + disablenpc "#006-5_49_54"; + } + if (!getq(CandorQuest_Groata)) { + getitem any(WurtziteOre, DarkCrystal, CandorWarpCrystal), 1; + setq CandorQuest_Groata, 1; + } + $@GROTTO += 5; + // Create monsters based on GROTTO + $@GROATA = max(BaseLevel, $@GROATA+1); + GroataGrotto(); + close; + } + mesc l("The crystal has a shard missing."); + close; + +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + diff --git a/npc/006-6/_import.txt b/npc/006-6/_import.txt new file mode 100644 index 0000000..824eb53 --- /dev/null +++ b/npc/006-6/_import.txt @@ -0,0 +1,6 @@ +// Map 006-6: Candor's Underground, B3F +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-6/_mobs.txt", +"npc/006-6/_warps.txt", +"npc/006-6/all.txt", +"npc/006-6/core.txt", diff --git a/npc/006-6/_mobs.txt b/npc/006-6/_mobs.txt new file mode 100644 index 0000000..161c5d1 --- /dev/null +++ b/npc/006-6/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-6: Candor's Underground, B3F mobs +006-6,46,38,16,14 monster Yellow Skull Slime 1403,3,120000,30000 +006-6,44,36,15,14 monster Red Skull Slime 1404,3,120000,30000 +006-6,45,33,18,14 monster Mineral Bif 1058,4,300000,30000 +006-6,44,32,23,12 monster Troll 1171,3,240000,30000 +006-6,43,37,18,14 monster Vampire Bat 1063,12,300000,30000 +006-6,39,46,16,7 monster Crafty 1018,1,300000,30000 diff --git a/npc/006-6/_warps.txt b/npc/006-6/_warps.txt new file mode 100644 index 0000000..f9f897b --- /dev/null +++ b/npc/006-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-6: Candor's Underground, B3F warps +006-6,49,49,0 warp #006-6_49_49 0,0,006-3,46,27 diff --git a/npc/006-6/all.txt b/npc/006-6/all.txt new file mode 100644 index 0000000..67edcd9 --- /dev/null +++ b/npc/006-6/all.txt @@ -0,0 +1,71 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-6,53,36,0 script Sign#0066WR NPC_SWORDS_SIGN,{ + mesc l("In a blood bath, survival is the epitaph."); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +006-6,46,34,0 script Sign#0066CR NPC_SWORDS_SIGN,{ + mesc l("*this sign is too blurred to read*"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +006-6,39,36,0 script Sign#0066ER NPC_SWORDS_SIGN,{ + mesc l("With Magic and Blades, The Icicle shall break."); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +// Effective warps +006-6,40,35,0 script Magic Barrier#0067W NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!$@ICICLE_CHALLENGE || is_admin()) + warp "006-7", 44, 49; + else + dispbottom l("Your strength is not enough to power on this portal."); + end; +} + +// Effective warps +006-6,54,35,0 script Magic Barrier#0067E NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!$@SURVIVAL_CANDOR || is_admin()) + warp "006-9", 37, 22; + else + dispbottom l("Your strength is not enough to power on this portal."); + end; +} + + +// Effective warps +006-6,47,33,0 script Magic Barrier#0067C NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!$@CAPTURE_FLAG || is_admin()) + warp "006-8", 44, 67; + else + dispbottom l("Your strength is not enough to power on this portal."); + end; +} + diff --git a/npc/006-6/core.txt b/npc/006-6/core.txt new file mode 100644 index 0000000..bb391c3 --- /dev/null +++ b/npc/006-6/core.txt @@ -0,0 +1,30 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-6,46,26,0 script Magic Barrier#0066 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!(getq(General_EasterEggs) & EE_SNOWMAN)) goto L_Reject; + if (SCANDORPTS < 300) goto L_Reject; + if (CAPTURE_FLAG < 1) goto L_Reject; + if (getq(General_Narrator) < 17) goto L_Reject; // Frostia Arc must be finished + /* Some of previous sections come back to bite you, now */ + if (CRAZYPOINTS < 10) goto L_Reject; + + specialeffect(5000, SELF, getcharid(3)); + dispbottom l("The power which lies in Candor acknowledges your strength."); + sleep2(2500); + warp "006-10", 42, 48; + sleep2(500); + specialeffect(5001, SELF, getcharid(3)); + end; + +L_Reject: + specialeffect(5000, SELF, getcharid(3)); + dispbottom l("The power which lies in Candor rejects your strength."); + sleep2(3000); + specialeffect(5002, SELF, getcharid(3)); + end; +} diff --git a/npc/006-7/_import.txt b/npc/006-7/_import.txt new file mode 100644 index 0000000..87e9595 --- /dev/null +++ b/npc/006-7/_import.txt @@ -0,0 +1,3 @@ +// Map 006-7: Aethyr Chamber +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-7/ctrl.txt", diff --git a/npc/006-7/ctrl.txt b/npc/006-7/ctrl.txt new file mode 100644 index 0000000..17243b5 --- /dev/null +++ b/npc/006-7/ctrl.txt @@ -0,0 +1,354 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-7 mapflag zone MMO + +006-7,44,50,0 script #OutOf0067 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!$@ICICLE_CHALLENGE) + warp "006-6", 40, 36; + end; + +OnRw: + getexp 1000000, 0; + Mobpt += 10000; + dispbottom l("Der Schneemann has been defeated: %s Monster Points gained.", fnum(10000)); + RegEasterEgg(EE_SNOWMAN, 25); + specialeffect(FX_FANFARE, SELF, getcharid(3)); + end; +} + +006-7,44,35,0 script Ice Lord NPC_YETIFLY,{ + if ($@ICICLE_CHALLENGE) end; + if (.ticks > gettimetick(2)) { + mesn; + mesq l("This challenge will be available in %s. Please wait until then.", FuzzyTime(.ticks)); + close; + } + mesn; + mesq l("With Magic and Blades, The Icicle shall break. Do you undertake the challenge?"); + mesc l("Advised: 6+ players"), 1; + mesc l("Advised: 1+ mage, 1+ tanker, 2+ healers"), 1; + mesc l("Time Limit: 90 minutes"), 1; + mesc l("Enter/Leave after start: %s", b(l("NO"))), 1; + next; + select + l("Not yet."), + l("Bring it on!"), + rif(REBIRTH, l("Bring me, my worst nightmare.")); + mes ""; + if (@menu == 1) { + closeclientdialog; + close; + } + if (@menu == 2) + .hard = false; + else + .hard = true; + + // Der Schneemann + .ticks = gettimetick(2) + 5400; // 90 minutes + .BOSS = monster("006-7", 44, 36, "The Icicle", SnowmanBoss, 1); + .@mlt = (.hard ? 15 : 10); + + // Basic attributes + setunitdata(.BOSS, UDT_MAXHP, 750000 * .@mlt / 10); // 750k ~ 1250k + setunitdata(.BOSS, UDT_HP, 750000 * .@mlt / 10); + setunitdata(.BOSS, UDT_ATKRANGE, (.hard ? 7 : 6)); + + // Reconfigure the AI + .@opt=getunitdata(.BOSS, UDT_MODE); + // Disable looting + if (.@opt & MD_LOOTER) + .@opt=.@opt^MD_LOOTER; + // Add knockback immunity + .@opt=.@opt|MD_NOKNOCKBACK; + // Mark as boss + .@opt=.@opt|MD_BOSS; + // Mark as aggressive + .@opt=.@opt|MD_AGGRESSIVE; + .@opt=.@opt|MD_ANGRY; + // Make it more op + .@opt=.@opt|MD_DETECTOR; + .@opt=.@opt|MD_CASTSENSOR_CHASE; + .@opt=.@opt|MD_CASTSENSOR_IDLE; + .@opt=.@opt|MD_CHANGECHASE; + .@opt=.@opt|MD_CHANGETARGET_MELEE; + .@opt=.@opt|MD_CHANGETARGET_CHASE; + setunitdata(.BOSS, UDT_MODE, .@opt); + + // Nerf the damage, but never miss a hit + setunitdata(.BOSS, UDT_ATKMIN, 60 * .@mlt / 10); // 60~90 dmg + setunitdata(.BOSS, UDT_ATKMAX, 60 * .@mlt / 10); + setunitdata(.BOSS, UDT_ADELAY, 2220 / .@mlt * 10); // 2220 or 1480ms + setunitdata(.BOSS, UDT_HIT, 2400); + + // Boosting the defense is not necessary + // It nerfs weapons to 40% (bows to 20%) + // Then it resists 50% of Neutral element. + // Note it is strong against Water (25% dmg) + // And weak against Fire (snow) and Wind (100% dmg) + // Otherwise, behave as Ghost element + + $@ICICLE_CHALLENGE = true; + disablenpc .name$; + initnpctimer; + closeclientdialog; + close; + +// Fail-safe Mechanism (will never happen) +OnTimer60000: + consolebug("Warning! final fail-safe mechanism triggered to Icicle."); + initnpctimer; + end; +OnTimer25000: +OnTimer15000: + consolewarn("Warning, fail-safe mechanism triggered to Icicle."); +// This is the boss' core +OnTimer5000: + /* Maybe the fight is over */ + if (!mobcount("006-7", "all")) + maptimer2("006-7", 10, "#OutOf0067::OnRw"); + if (.ticks < gettimetick(2) || !mobcount("006-7", "all") || !getmapusers("006-7")) { + killmonsterall("006-7"); + enablenpc .name$; + npctalk "The battle is over!"; + .ticks = min(.ticks, gettimetick(2) + 1800); // Min. Cooldown: 30 min + .beats = 0; + .warn = 0; + $@ICICLE_CHALLENGE = false; + stopnpctimer; + end; + } + + /* Warn players when time is about to end */ + if (.ticks - 1800 < gettimetick(2) && .warn < 1) { + mapannounce("006-7", "Warning : ##1##BTime left: 30 minutes##b", 0); + .warn = 1; + } + else if (.ticks - 900 < gettimetick(2) && .warn < 2) { + mapannounce("006-7", "Warning : ##1##BTime left: 15 minutes##b", 0); + .warn = 2; + } + else if (.ticks - 600 < gettimetick(2) && .warn < 3) { + mapannounce("006-7", "Warning : ##1##BTime left: 10 minutes##b", 0); + .warn = 3; + } + else if (.ticks - 300 < gettimetick(2) && .warn < 4) { + mapannounce("006-7", "Warning : ##1##BTime left: 5 minutes##b", 0); + .warn = 4; + } + + /* Prepare some data */ + .@hp = getunitdata(.BOSS, UDT_HP) * 10 / getunitdata(.BOSS, UDT_MAXHP); + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, .BOSS); + .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, .@m$); + .@mvp=0;.@rnd=0;.@def=-1; + for (.@i = 0; .@i < .@c; .@i++) { + if (!.@rnd || !rand2(.@c)) + .@rnd=.@pcs[.@i]; + if (readbattleparam(.@pcs[.@i], UDT_DEF) > .@def) { + if (readparam(Hp, .@pcs[.@i]) < 1) continue; + .@mvp=.@pcs[.@i]; + .@def=readbattleparam(.@pcs[.@i], UDT_DEF); + } + } + .beats += 1; + + /* Everyone is dead, get rid of them */ + if (!.@mvp || !.@rnd) { + mapwarp("006-7", "006-6", 40, 36); + initnpctimer; + end; + } + + //debugmes "----------- Skill Loop, beat is %d", .beats % 18; + /* Decide the skill to use based on ~5s beats over 3 minutes */ + switch (.beats % 18) { + // (1/6) Summon Reinforcements (every 60s) + case 0: + case 6: + case 12: + unittalk(.BOSS, "Come forth, ##Bsnow army##b, for the Icicle shall live forever!"); + specialeffect(65, AREA, .BOSS); // Actually 64, but won't do well here + sleep(1000); + monster("006-7", 44, 22, strmobinfo(1, Snowman), Snowman, max(1, (11 - .@hp) / 10)); + break; + // (2/6) Tanker (~30s) + case 1: + case 4: + case 7: + case 10: + case 13: + case 16: + specialeffect(702, AREA, .BOSS); + sleep(1000); + if (.@hp < 3) { + // Third Attack Pattern: Judgment + unittalk(.BOSS, sprintf("%s cannot stop me! ##BJudgment##b!", strcharinfo(0, "cursed player", .@mvp))); + .@PW=240; .@SPW=60; .@RG=3; + } else if (.@hp < 7) { + // Second Attack Pattern: Holy Light + unittalk(.BOSS, sprintf("%s, I'll show you no mercy! ##BHoly Light##b!", strcharinfo(0, "cursed player", .@mvp))); + .@PW=125; .@SPW=25; .@RG=1; + } else { + // First Attack Pattern: Napalm Beat + unittalk(.BOSS, sprintf("This battle is over, %s! ##BNapalm Beat##b!", strcharinfo(0, "cursed player", .@mvp))); + .@PW=35; .@SPW=5; .@RG=2; + } + .@mtk = calcdmg(.BOSS, .@mvp, HARM_MAGI); + .@dmg = .@mtk * .@PW / 100; + .@dsb = .@mtk * .@SPW / 100; + sleep(1000); + areaharm(.@mvp, .@RG, .@dsb, HARM_MAGI, Ele_Holy, "filter_always", BL_PC|BL_MER|BL_HOM); + harm(.@mvp, .@dmg, HARM_MAGI, Ele_Holy); + break; + // (3/6) Random Target (~60s) + case 2: + case 8: + case 14: + specialeffect(50, AREA, .BOSS); + sleep(1000); + .@time=rand2(18000, 36000) + 10000 - (.@hp * 1000); + // Switch between curse and disable + if (any(true,false)) { + unittalk(.BOSS, sprintf("I hereby ##Bcurse##b you, %s!", strcharinfo(0, "cursed player", .@rnd))); + sc_start(SC_CURSE, .@time, 1, 10000, SCFLAG_FIXEDRATE, .@rnd); + } else { + unittalk(.BOSS, sprintf("I shall ##Bdisable##b you, %s!", strcharinfo(0, "cursed player", .@rnd))); + sc_start(SC_BLIND, .@time / 2, 1, 10000, SCFLAG_FIXEDRATE, .@rnd); + sc_start(SC_SILENCE, .@time / 2, 1, 10000, SCFLAG_FIXEDRATE, .@rnd); + } + // Second pattern: Bleeding ON + if (.@hp < 7) { + sc_start(SC_BLOODING, 10000, 1, 9000-(.@hp*1000), SCFLAG_FIXEDRATE, .@rnd); + } + break; + // (4/6) Traps (~60s) + case 3: + case 9: + case 15: + .@x1=rand2(31, 58); .@x2=rand2(31, 58); .@x3=rand2(31, 58); + .@y1=rand2(23, 49); .@y2=rand2(23, 49); .@y3=rand2(23, 49); + .@t1=monster("006-7", .@x1, .@y1, "", Dummy, 1); + .@t2=monster("006-7", .@x2, .@y2, "", Dummy, 1); + .@t3=monster("006-7", .@x3, .@y3, "", Dummy, 1); + specialeffect(67, AREA, .@t1); + specialeffect(67, AREA, .@t2); + specialeffect(67, AREA, .@t3); + immortal(.@t1); immortal(.@t2); immortal(.@t3); + if (.@hp < 5 || .hard) { + .@x4=rand2(31, 58); .@x5=rand2(31, 58); .@x6=rand2(31, 58); + .@y4=rand2(23, 49); .@y5=rand2(23, 49); .@y6=rand2(23, 49); + .@t4=monster("006-7", .@x1, .@y1, "", Dummy, 1); + .@t5=monster("006-7", .@x2, .@y2, "", Dummy, 1); + .@t6=monster("006-7", .@x3, .@y3, "", Dummy, 1); + specialeffect(67, AREA, .@t4); + specialeffect(67, AREA, .@t5); + specialeffect(67, AREA, .@t6); + immortal(.@t4); immortal(.@t5); immortal(.@t6); + } + sleep(2000); + specialeffect(11, AREA, .@t1); + specialeffect(11, AREA, .@t2); + specialeffect(11, AREA, .@t3); + if (.@hp < 5 || .hard) { + specialeffect(11, AREA, .@t4); + specialeffect(11, AREA, .@t5); + specialeffect(11, AREA, .@t6); + } + areaharm(.@t1, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); + areaharm(.@t2, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); + areaharm(.@t3, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); + if (.@hp < 5 || .hard) { + areaharm(.@t4, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); + areaharm(.@t5, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); + areaharm(.@t6, 2, 450, HARM_MISC, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); + } + sleep(1000); + // FIXME: M+ fails to remove them, need @refresh + unitkill(.@t1); unitkill(.@t2); unitkill(.@t3); + if (.@hp < 5 || .hard) { + unitkill(.@t4); unitkill(.@t5); unitkill(.@t6); + } + break; + // (5/6) Weak AOE (~60s) + case 5: + case 11: + case 17: + specialeffect(60, AREA, .BOSS); + sleep(500); + switch (rand2(3)) { + case 1: + unittalk(.BOSS, "I shall ##Bpoison##b you all!"); + .@sc = (.@hp < 1 ? SC_DPOISON : SC_POISON); + break; + case 2: + unittalk(.BOSS, "I shall ##Bhurt##b you all!"); + .@sc = SC_BLOODING; + break; + case 3: + unittalk(.BOSS, "I shall ##Bsilence##b you all!"); + .@sc = SC_SILENCE; + break; + default: + unittalk(.BOSS, "I shall ##Bcripple##b you all!"); + .@sc = SC_BLIND; + break; + } + areasc((.hard ? 7 : 6), 45000, .@sc, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .BOSS, 95000); + areaharm(.BOSS, (.hard ? 7 : 6), 100, HARM_MAGI, Ele_Water, "filter_always", BL_PC|BL_MER|BL_HOM); + break; + } + // Strong AOE: Every 3 minutes (case = 0) + // Stalls all other skills for a while + if (.beats % 18 == 0) { + specialeffect(66, AREA, .BOSS); + unittalk(.BOSS, "I am the Icicle, the immortal."); + sleep((.hard ? 1000 : 1500)); + specialeffect(700, AREA, .BOSS); + unittalk(.BOSS, "You're no match for me, so..."); + sleep((.hard ? 1000 : 1500)); + specialeffect(700, AREA, .BOSS); + if (.@hp < 3) { + unittalk(.BOSS, "Perish! ##BSnowstorm##b!"); + .@dmg=rand2(900, 1100); + areasc(3, 5000, SC_FREEZE, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .BOSS, 2000); + areasc(6, 10000, SC_BLIND, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .BOSS, 2000); + } else if (.@hp < 7) { + unittalk(.BOSS, "Perish! ##BBlizzard##b!"); + .@dmg=rand2(650, 900); + areasc(3, 5000, SC_FREEZE, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .BOSS, 1500); + } else { + unittalk(.BOSS, "Perish! ##BShattering Winds##b!"); + .@dmg=rand2(400, 650); + } + /* Three blocks */ + specialeffect(66, AREA, .BOSS); + areaharm(.BOSS, (.hard ? 7 : 6), .@dmg, HARM_MAGI, Ele_Neutral, "filter_always", BL_PC|BL_MER|BL_HOM); + sleep(500); + specialeffect(66, AREA, .BOSS); + areaharm(.BOSS, (.hard ? 18 : 12), .@dmg, HARM_MAGI, Ele_Water, "filter_always", BL_PC|BL_MER|BL_HOM); + sleep(500); + specialeffect(66, AREA, .BOSS); + specialeffect(312, AREA, .BOSS); + areaharm(.BOSS, (.hard ? 24 : 18), .@dmg, HARM_MAGI, Ele_Holy, "filter_always", BL_PC|BL_MER|BL_HOM); + areasc((.hard ? 24 : 18), .@dmg*rand2(50, 100), SC_FROSTMISTY, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .BOSS, 10001 - (.@hp * 1000)); // FIXME + // SC_COLD freezes you, so trying SC_FROSTMISTY + } + + initnpctimer; + end; + +OnInit: + .distance = 4; + .ticks = gettimetick(2); + .beats = 0; + .hard = 0; + .warn = 0; + end; +} + diff --git a/npc/006-8/_import.txt b/npc/006-8/_import.txt new file mode 100644 index 0000000..2587b75 --- /dev/null +++ b/npc/006-8/_import.txt @@ -0,0 +1,3 @@ +// Map 006-8: Capture the Flag +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-8/kage.txt", diff --git a/npc/006-8/kage.txt b/npc/006-8/kage.txt new file mode 100644 index 0000000..894c6e4 --- /dev/null +++ b/npc/006-8/kage.txt @@ -0,0 +1,331 @@ +// TMW2 Script +// Author: +// Jesusalva + +// Mapflags (FIXME: Disable splash... Or weapons and magic in general) +006-8 mapflag zone MMO No Revive +006-8 mapflag battleground + +006-8,44,68,0 script #OutOf0068 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!$@CAPTURE_FLAG) + warp "006-6", 47, 34; + end; +} + +006-8,38,58,0 script #Abort0068A NPC_HIDDEN,0,1,{ + end; + +OnTouch: + if (!$@CAPTURE_FLAG) { + slide 40, 64; + bg_leave(); + } + end; +} + +006-8,51,58,0 script #Abort0068B NPC_HIDDEN,0,1,{ + end; + +OnTouch: + if (!$@CAPTURE_FLAG) { + slide 49, 64; + bg_leave(); + } + end; +} + +006-8,40,62,0 script #Signin0068A NPC_HIDDEN,1,0,{ + end; + +OnTouch: + if ($@CAPTURE_INSC && CAPTURE_FLAG >= 0) + bgjoin($@CAPTURE_T1, "006-8", 37, 58); + end; +} + +006-8,48,62,0 script #Signin0068B NPC_HIDDEN,1,0,{ + end; + +OnTouch: + if ($@CAPTURE_INSC && CAPTURE_FLAG >= 0) + bgjoin($@CAPTURE_T2, "006-8", 52, 58); + end; +} + +006-8,45,65,0 script Kage NPC_CRUSADER,{ + function getmobid; + if ($@CAPTURE_FLAG) { + npctalk l("A game is currently going on, please wait for it to finish."); + end; + } + mesn; + mesq l("This a simple game; Choose one of the shrines - west or east. The one at west is %s, the one at east is %s.", b(l("Magenta")), b(l("Yellow"))); + next; + mesn; + mesq l("You can enter when clock ticks :15, and the game begin when the clock ticks :25 if anyone is around. This is to match with Call of Dusty event."); + next; + mesn; + mesq l("Once it begins (you can fight against an AI), kill the most of enemy sparks you can, while preventing your own sparks of being killed. Wins whoever has more sparks after %s minutes.", l(b("five"))); + next; + mesn; + mesq l("Keep in mind this is a PvP area - So try to avoid being butchered, because revival spells are disabled here! Hahaha!"); + next; + mesn; + mesq l("Oh, and if I perchance catch you using %s or %s... You'll be banned from my game, understood?", b(l("splash weapons")), b(l("splash magic"))); + mesc l("-- In such event, Candor Underground B4F will be permanently unreachable for this char."), 1; + if (is_master() && !$@CAPTURE_FLAG) { + next; + select + l("Ok"), + l("Toggle Inscription"), + l("Start"); + mes ""; + if (@menu == 2) + $@CAPTURE_INSC = !$@CAPTURE_INSC; + if (@menu == 3) { + initnpctimer; + closeclientdialog; + goto OnMinute25; + } + } + close; + +OnForfeit1: + .@s=true; +OnForfeit2: + bg_leave(); + mapannounce "006-8", sprintf("%s (%s side) is down!", strcharinfo(0), (.@s ? b("Magenta") : b("Yellow"))), 0; + end; + +OnSpark1: + .sp1 += 1; end; +OnSpark2: + .sp2 += 1; end; + end; +OnSkip: + end; +OnFinish: + if (getcharid(4) == .win) { + Zeny+=670; + Mobpt+=500; + dispbottom l("You earned %s Monster Points for winning.", fnum(500)); + CAPTURE_FLAG += 1; + switch (CAPTURE_FLAG % 11) { + case 1: getitem MercBoxA, 1; break; + case 2: getitem any(Aquada, Piberries, Cheese, Bread), 1; break; + case 3: getitem any(Coffee, ChamomileTea, MysteriousBottle), 1; break; + case 4: getitem any(Aquada, Piberries, Cheese, Bread), 1; break; + case 5: getitem any(ReturnPotion, TreasureMap, DungeonMap), 1; break; + case 6: getitem any(Aquada, Piberries, Cheese, Bread), 1; break; + case 7: getitem any(SmokeGrenade, DodgePotion), 1; break; + case 8: getitem any(Aquada, Piberries, Cheese, Bread), 1; break; + case 9: getitem any(ScrollSCave, InsuranceContract, Insurance), 1; break; + case 10: getitem any(Aquada, Piberries, Cheese, Bread), 1; break; + default: getitem any(IcedBottle, PurificationPotion, ScrollSMaggot), 1; + } + } + bg_leave(); + warp "006-8", 44, 66; + end; + +OnInit: + .distance=4; + .sp1 = 0; // Sparks killed by Team 1 + .sp2 = 0; // Sparks killed by Team 2 + .win = -2; // Last match winner + $@CAPTURE_FLAG = false; + $@CAPTURE_INSC = false; + $@CAPTURE_T1 = bgnew("006-8", 34, 28, "Kage::OnForfeit1", "Kage::OnForfeit1"); + $@CAPTURE_T2 = bgnew("006-8", 55, 28, "Kage::OnForfeit2", "Kage::OnForfeit2"); + end; + +function getmobid { + .@lv = getarg(0); + if (.@lv <= 5) + return HouseMaggot; + else if (.@lv <= 11) + return AngryScorpion; + else if (.@lv <= 15) + return CaveMaggot; + else if (.@lv <= 20) + return AngryBat; + else if (.@lv <= 26) + return Bandit; + else if (.@lv <= 30) + return Bluepar; + else if (.@lv <= 35) + return RobinBandit; + else if (.@lv <= 40) + return Snake; + else if (.@lv <= 46) + return Wolvern; + else if (.@lv <= 52) + return HoodedNinja; + else if (.@lv <= 60) + return FallenGuard1; + else if (.@lv <= 65) + return Scar; + else if (.@lv <= 75) + return Forain; + else if (.@lv <= 80) + return Terranite; + else if (.@lv <= 85) + return GoboBear; + else if (.@lv <= 90) + return TerraniteProtector; + else if (.@lv <= 100) + return Reaper; + else if (.@lv <= 107) + return Snail; + else if (.@lv <= 115) + return Mandragora; + else if (.@lv <= 120) + return PinkieMaximus; + else if (.@lv <= 125) + return Junglefowl; + else if (.@lv <= 130) + return Tengu; + else if (.@lv <= 135) + return SuperiorShroom; + else if (.@lv <= 140) + return Nutcracker; + else if (.@lv <= 150) + return Golem; + else if (.@lv <= 160) + return EarthElement; + else if (.@lv <= 175) + return EpiphanyWisp; + // If all else fails, pick the strongest aggro mob + return EpiphanyWisp; +} + +OnMinute15: + $@CAPTURE_INSC = true; + mapannounce "006-8", "Kage : Inscriptions are open! The event will begin in ##B10 minutes!##b", 0; + end; + +OnMinute24: + mapannounce "006-8", "Kage : Inscriptions are about to close! The event will begin in ##B1 minute!##b", 0; + end; + +OnMinute25: + $@CAPTURE_INSC = false; + $@CAPTURE_FLAG = true; + /* Prepare the teams */ + setbgteam $@CAPTURE_T1, 1; + setbgteam $@CAPTURE_T2, 2; + /* Validate difficulty */ + .@t1p = bg_get_data($@CAPTURE_T1, 0); + .@t2p = bg_get_data($@CAPTURE_T2, 0); + .@t1lv = 0; .@t2lv = 0; + /* Optional Reinforcements when unbalanced */ + freeloop(true); + .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, "006-8", 30, 20, 60, 60); + for (.@i = 0; .@i < .@c; .@i++) { + .@id = .@pcs[.@i]; + .@n$ = strcharinfo(0, "", .@id); + .@bg = getcharid(4, .@n$); + if (.@bg < 1) { consolewarn("Invalid bg for %s (%d): %d",.@m$, .@id, .@bg); continue; } // TODO: Get rid of them + if (.@bg == $@CAPTURE_T1) + .@t1lv += readparam(BaseLevel, .@id); + else if (.@bg == $@CAPTURE_T2) + .@t2lv += readparam(BaseLevel, .@id); + else + consolebug("BG %d is not valid (%d, %d)", .@bg, $@CAPTURE_T1, $@CAPTURE_T2); + } + // Calc. averages + if (.@t1p) + .@t1a = .@t1lv / .@t1p; + if (.@t2p) + .@t2a = .@t2lv / .@t2p; + // If average is zero...?! + if (.@t1a && !.@t2a) + .@t2a = .@t1a; + if (.@t2a && !.@t1a) + .@t1a = .@t2a; + /* Spawn guards as needed... */ + // Normalize player count + // TODO: Maybe use the other side average, mobs are weak? + while (.@t1p < .@t2p) { + .@t1p += 1; + bg_monster($@CAPTURE_T1, "006-8", 32, rand2(24, 32), "Magenta Ally", getmobid(max(.@t1a, .@t2a)), "Kage::OnSkip"); + } + while (.@t2p < .@t1p) { + .@t2p += 1; + bg_monster($@CAPTURE_T2, "006-8", 53, rand2(24, 32), "Yellow Ally", getmobid(max(.@t1a, .@t2a)), "Kage::OnSkip"); + } + // TODO: Maybe buff their HP. Also, they didn't attack sparks? + // Apply a buff on the teams, based on level difference + .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, "006-8", 30, 20, 60, 60); + .@t1b = .@t2a - .@t1a; // Team 1 Boost + .@t2b = .@t1a - .@t2a; // Team 2 Boost + for (.@i = 0; .@i < .@c; .@i++) { + .@id = .@pcs[.@i]; + .@n$ = strcharinfo(0, "", .@id); + .@bg = getcharid(4, .@n$); + if (.@bg == $@CAPTURE_T1 && .@t1b > 0) { + sc_start SC_INCMHP, 300000, .@t1b * 1000, 10000, SCFLAG_NOAVOID, .@id; + sc_start SC_INCATKRATE, 300000, .@t1b, 10000, SCFLAG_NOAVOID, .@id; + sc_start SC_INCHITRATE, 300000, .@t1b, 10000, SCFLAG_NOAVOID, .@id; + sc_start SC_INCFLEERATE, 300000, .@t1b, 10000, SCFLAG_NOAVOID, .@id; + } else if (.@bg == $@CAPTURE_T2 && .@t2b > 0) { + sc_start SC_INCMHP, 300000, .@t2b * 1000, 10000, SCFLAG_NOAVOID, .@id; + sc_start SC_INCATKRATE, 300000, .@t2b, 10000, SCFLAG_NOAVOID, .@id; + sc_start SC_INCHITRATE, 300000, .@t2b, 10000, SCFLAG_NOAVOID, .@id; + sc_start SC_INCFLEERATE, 300000, .@t2b, 10000, SCFLAG_NOAVOID, .@id; + } + } + freeloop(false); + // FALLTHROUGH +OnTimer60000: +OnTimer120000: +OnTimer180000: +OnTimer240000: +OnMinute26: +OnMinute27: +OnMinute28: +OnMinute29: + /* New wave of sparks */ + .@am = 7 + getmapusers("006-8"); + freeloop(true); + for (.@i=0; .@i < .@am; .@i++) { + bg_monster($@CAPTURE_T1, "006-8", rand2(31, 44), rand2(21, 35), "Magenta Spark", MagentaSpark, "Kage::OnSpark1"); + bg_monster($@CAPTURE_T2, "006-8", rand2(45, 58), rand2(21, 35), "Yellow Spark", YellowSpark, "Kage::OnSpark2"); + + } + freeloop(false); + end; + +OnTimer300000: +OnMinute30: + /* Finish the duel! */ + killmonsterall("006-8"); + mapannounce "006-8", sprintf("Kage : Final Score: Magenta %d - %d Yellow", .sp2, .sp1), 0; + if (.sp2 > .sp1) { + // More spark 2 killed than spark 1 + mapannounce "006-8", "Kage : ##BMagenta Team##b is the victor!", 0; + .win = $@CAPTURE_T1; + } else if (.sp1 > .sp2) { + mapannounce "006-8", "Kage : ##BYellow Team##b is the victor!", 0; + .win = $@CAPTURE_T2; + } else { + mapannounce "006-8", "Kage : ##BDraw##b! No one has won!", 0; + .win = -2; + } + // Assign rewards + maptimer("006-8", 10, "Kage::OnFinish"); + /* Cleanup */ + $@CAPTURE_FLAG = false; + .sp1 = 0; + .sp2 = 0; + stopnpctimer; + end; + +} + + + + diff --git a/npc/006-9/_config.txt b/npc/006-9/_config.txt new file mode 100644 index 0000000..d24aa8b --- /dev/null +++ b/npc/006-9/_config.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 006-9: The Abyss conf + +006-9,37,22,0 script #006-9_37_22 NPC_HIDDEN,0,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnBleed"; + end; +} diff --git a/npc/006-9/_import.txt b/npc/006-9/_import.txt new file mode 100644 index 0000000..a268948 --- /dev/null +++ b/npc/006-9/_import.txt @@ -0,0 +1,4 @@ +// Map 006-9: The Abyss +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/006-9/_config.txt", +"npc/006-9/ctrl.txt", diff --git a/npc/006-9/ctrl.txt b/npc/006-9/ctrl.txt new file mode 100644 index 0000000..46b1fe3 --- /dev/null +++ b/npc/006-9/ctrl.txt @@ -0,0 +1,314 @@ +// TMW2 Script +// Author: +// Jesusalva + +006-9 mapflag zone MMO No Tricks + +006-9,37,21,0 script #OutOf0069 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!$@SURVIVAL_CANDOR) + warp "006-6", 54, 36; + end; +} + +006-9,55,32,0 script Bloodbath NPC_NO_SPRITE,{ + function spawn; + if ($@SURVIVAL_CANDOR) { + npctalk3 l("Score: %s / %s", FuzzyTime($@SURVIVAL_CANDOR), FuzzyTime(gettimetick(2)+SCANDORPTS)); + end; + } + mesn; + mes l("Goal: Survive the longest possible."); + mes l("Monsters will spawn in the south fountain continuously."); + mes l("They'll also start spawning on whole map, so be careful."); + mesc l("The fee to use this room is %s GP.", fnum(.price)), 1; + mes ""; + mes l("Current score: %s", FuzzyTime(gettimetick(2)+SCANDORPTS)); + + if (Zeny < .price) close; + next; + mesc l("Begin?"); + menuint + l("Yes"), ASK_YES, + l("No"), ASK_NO, + l("Scoreboards"), -1; + mes ""; + if (@menuret == ASK_NO || Zeny < .price) { + closeclientdialog; + close; + } else if (@menuret < 0) { + HallOfBloodbath(); + close; + } + + Zeny-=.price; + $@SURVIVAL_CANDOR = gettimetick(2); + initnpctimer; + close; + +// Timer logic +OnTimer4000: + .diff += 1; + if (.diff == 75) + mapannounce "006-9","Five minutes have passed, changing spawn pattern!",0; + if (.diff == 450) + mapannounce "006-9", "Half hour has passed, changing spawn pattern!", 0; + if (.diff == 900) + mapannounce "006-9", "One hour has passed, changing spawn pattern!", 0; + initnpctimer; +OnTimer2000: + /* Verify for game over */ + if (getmapusers("006-9") < 1) { + sleep(25); + stopnpctimer; + .diff = 0; + $@SURVIVAL_CANDOR = 0; + killmonsterall("006-9"); + end; + } + spawn(.diff); + end; + +// Spawn logic +function spawn { + .@lv = getarg(0); + if (.@lv < 450) { + .@mb[0] = MagicGoblin; + .@mb[1] = IceMaggot; + } + + // Artillery + if (.@lv > 40) + array_push(.@mb, RobinBandit); + if (.@lv > 50) + array_push(.@mb, DustGatling); + if (.@lv > 60) + array_push(.@mb, DustRifle); + if (.@lv > 70) + array_push(.@mb, DustRevolver); + if (.@lv > 80) + array_push(.@mb, DustBoss); + if (.@lv > 90) + array_push(.@mb, GreatMoubooSlime); + if (.@lv > 100) + array_push(.@mb, Jhon); + + // Monsters + if (is_between2(0, .@lv, 60)) { + array_push(.@mb, Piou); + array_push(.@mb, Piousse); + array_push(.@mb, Squirrel); + array_push(.@mb, ManaPiou); + array_push(.@mb, ForestPiou); + array_push(.@mb, RedButterfly); + array_push(.@mb, Maggot); + array_push(.@mb, CandorScorpion); + array_push(.@mb, HouseMaggot); + array_push(.@mb, LittleYellowSlime); + array_push(.@mb, Ratto); + array_push(.@mb, RudolphSlime); + array_push(.@mb, MoubooSlime); + array_push(.@mb, Croc); + array_push(.@mb, Scorpion); + array_push(.@mb, SmallFrog); + } + if (is_between2(20, .@lv, 80)) { + array_push(.@mb, BigFrog); + array_push(.@mb, Lavern); + array_push(.@mb, LittleRedSlime); + array_push(.@mb, ChocolateSlime); + array_push(.@mb, Blub); + array_push(.@mb, Duck); + array_push(.@mb, Bat); + array_push(.@mb, CaveMaggot); + array_push(.@mb, ManaGhost); + array_push(.@mb, ManaBug); + array_push(.@mb, Fluffy); + array_push(.@mb, FireGoblin); + array_push(.@mb, ViciousSquirrel); + array_push(.@mb, RedScorpion); + array_push(.@mb, WhiteSlime); + array_push(.@mb, AzulSlime); + array_push(.@mb, DesertLogHead); + } + if (is_between2(30, .@lv, 100)) { + array_push(.@mb, RedSlime); + array_push(.@mb, PoisonSpikyMushroom); + array_push(.@mb, DesertBandit); + array_push(.@mb, OceanCroc); + array_push(.@mb, ToppyBlub); + array_push(.@mb, Sarracenus); + array_push(.@mb, IceMaggot); + array_push(.@mb, VampireBat); + array_push(.@mb, Bandit); + array_push(.@mb, Pinkie); + array_push(.@mb, LivingPotato); + array_push(.@mb, Assassin); + array_push(.@mb, Skeleton); + } + if (is_between2(50, .@lv, 120)) { + array_push(.@mb, CaveSnake); + array_push(.@mb, GreenSlime); + array_push(.@mb, CopperSlime); + array_push(.@mb, YellowSlime); + array_push(.@mb, SantaSlime); + array_push(.@mb, LavaSlime); + array_push(.@mb, Bluepar); + array_push(.@mb, DeathCat); + array_push(.@mb, Moggun); + array_push(.@mb, SeaSlime); + array_push(.@mb, RedMushroom); + array_push(.@mb, Mouboo); + array_push(.@mb, LogHead); + array_push(.@mb, CandiedSlime); + array_push(.@mb, OldSnake); + array_push(.@mb, GrassSnake); + } + if (is_between2(60, .@lv, 140)) { + array_push(.@mb, GiantMaggot); + array_push(.@mb, IcedFluffy); + array_push(.@mb, Snake); + array_push(.@mb, BlackSlime); + array_push(.@mb, Tipiou); + array_push(.@mb, AlphaMouboo); + array_push(.@mb, Pollet); + array_push(.@mb, Snowman); + array_push(.@mb, PiouKnight); + array_push(.@mb, Shrewboo); + } + if (is_between2(70, .@lv, 160)) { + array_push(.@mb, Wolvern); + array_push(.@mb, FireSkull); + array_push(.@mb, DarkLizard); + } + if (is_between2(90, .@lv, 180)) { + array_push(.@mb, ArmoredSkeleton); + array_push(.@mb, BlackScorpion); + array_push(.@mb, ElectroWorm); + array_push(.@mb, EarthFairy); + array_push(.@mb, FireFairy); + array_push(.@mb, WaterFairy); + array_push(.@mb, WindFairy); + array_push(.@mb, PoisonFairy); + array_push(.@mb, MountainSnake); + array_push(.@mb, HoodedNinja); + array_push(.@mb, ForestMushroom); + array_push(.@mb, GoldenScorpion); + } + if (is_between2(110, .@lv, 200)) { + array_push(.@mb, Yeti); + array_push(.@mb, FallenGuard1); + array_push(.@mb, GreenSlimeMother); + array_push(.@mb, SnowFlower); + array_push(.@mb, BlueSlimeMother); + array_push(.@mb, WickedMushroom); + array_push(.@mb, CopperSlimeMother); + array_push(.@mb, YellowSlimeMother); + array_push(.@mb, RedSlimeMother); + array_push(.@mb, ChocolateSlimeMother); + array_push(.@mb, WhiteSlimeMother); + array_push(.@mb, Archant); + array_push(.@mb, Scar); + } + if (is_between2(140, .@lv, 220)) { + array_push(.@mb, AzulSlimeMother); + array_push(.@mb, SeaSlimeMother); + array_push(.@mb, LavaSlimeMother); + array_push(.@mb, BlackSlimeMother); + array_push(.@mb, Crafty); + array_push(.@mb, Forain); + array_push(.@mb, GreenDragon); + array_push(.@mb, Michel); + array_push(.@mb, Troll); + } + if (is_between2(160, .@lv, 240)) { + array_push(.@mb, EliteDuck); + array_push(.@mb, AzulSkullSlime); + array_push(.@mb, Moonshroom); + array_push(.@mb, RedSkullSlime); + array_push(.@mb, Terranite); + array_push(.@mb, JackO); + array_push(.@mb, BlackMamba); + array_push(.@mb, GreenSkullSlime); + array_push(.@mb, BloodyMouboo); + array_push(.@mb, Centaur); + array_push(.@mb, GoboBear); + } + if (is_between2(180, .@lv, 260)) { + array_push(.@mb, CopperSkullSlime); + array_push(.@mb, LavaSkullSlime); + array_push(.@mb, BlackSkullSlime); + array_push(.@mb, GiantCaveMaggot); + array_push(.@mb, TerraniteProtector); + array_push(.@mb, VanityPixie); + array_push(.@mb, HolyPixie); + } + if (is_between2(200, .@lv, 280)) { + array_push(.@mb, ShadowPixie); + array_push(.@mb, NulityPixie); + array_push(.@mb, Reaper); + array_push(.@mb, NightmareDragon); + array_push(.@mb, Snail); + array_push(.@mb, WhirlyBird); + } + if (is_between2(250, .@lv, 350)) { + array_push(.@mb, PinkieSuseran); + array_push(.@mb, Mandragora); + array_push(.@mb, PinkieMaximus); + } + if (.@lv > 240) { + array_push(.@mb, Junglefowl); + array_push(.@mb, Tengu); + array_push(.@mb, Moubi); + } + if (.@lv > 260) { + array_push(.@mb, SuperiorShroom); + array_push(.@mb, Nutcracker); + array_push(.@mb, Golem); + } + if (.@lv > 280) { + array_push(.@mb, SiegeTower); + array_push(.@mb, GreenhornAbomination); + array_push(.@mb, ShadowTortuga); + array_push(.@mb, FireElement); + array_push(.@mb, WaterElement); + array_push(.@mb, EarthElement); + array_push(.@mb, WindElement); + } + if (.@lv > 320) { + array_push(.@mb, SacredWisp); + array_push(.@mb, EvilWisp); + array_push(.@mb, PanthomWisp); + array_push(.@mb, EpiphanyWisp); + } + if (.@lv >= 350) + array_push(.@mb, Tortuga); + + /* The final attack pattern is aimed at nukes */ + if (.@lv >= 900) + .@lv *= 3; // 12 -> 36 mobs + + /* Spawn them and make hostile */ + .@mid = any_of(.@mb); + .@m=areamonster("006-9", 35, 63, 36, 65, strmobinfo(1, .@mid), .@mid, 1); + set_aggro(.@m); + /* After five minutes, spawn some globally */ + freeloop(true); + for (.@i = 0; .@i < 1+(.@lv / 75); .@i++) { + .@mid = any_of(.@mb); + .@m=areamonster("006-9", 20, 20, getmapinfo(MAPINFO_SIZE_X,"006-9"), getmapinfo(MAPINFO_SIZE_Y,"006-9"), strmobinfo(1, .@mid), .@mid, 1); + set_aggro(.@m); + } + freeloop(false); + return; +} + +OnInit: + .distance=3; + .price=2400; + .diff = 0; + end; +} + diff --git a/npc/007-1-1/_import.txt b/npc/007-1-1/_import.txt new file mode 100644 index 0000000..0746a76 --- /dev/null +++ b/npc/007-1-1/_import.txt @@ -0,0 +1,4 @@ +// Map 007-1-1: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/007-1-1/_mobs.txt", +"npc/007-1-1/_warps.txt", diff --git a/npc/007-1-1/_mobs.txt b/npc/007-1-1/_mobs.txt new file mode 100644 index 0000000..b975904 --- /dev/null +++ b/npc/007-1-1/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-1-1: Canyon Cave mobs +007-1-1,35,35,4,2 monster Maggot 1030,2,35000,150000 +007-1-1,56,34,4,2 monster Maggot 1030,2,35000,150000 +007-1-1,45,37,6,9 monster Little Blub 1007,4,35000,150000 +007-1-1,0,0,0,0 monster Cave Maggot 1027,6,35000,150000 +007-1-1,40,30,10,8 monster Scorpion 1071,5,35000,150000 +007-1-1,44,34,6,11 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/007-1-1/_warps.txt b/npc/007-1-1/_warps.txt new file mode 100644 index 0000000..dc19e3c --- /dev/null +++ b/npc/007-1-1/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-1-1: Canyon Cave warps +007-1-1,35,38,0 warp #007-1-1_35_38 0,0,004-1,119,108 +007-1-1,42,20,0 warp #007-1-1_42_20 0,0,010-2-15,35,41 +007-1-1,45,49,0 warp #007-1-1_45_49 0,0,004-3-6,42,21 +007-1-1,56,36,0 warp #007-1-1_56_36 0,0,004-2,48,60 diff --git a/npc/007-1-2/_import.txt b/npc/007-1-2/_import.txt new file mode 100644 index 0000000..ed81198 --- /dev/null +++ b/npc/007-1-2/_import.txt @@ -0,0 +1,4 @@ +// Map 007-1-2: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/007-1-2/_mobs.txt", +"npc/007-1-2/_warps.txt", diff --git a/npc/007-1-2/_mobs.txt b/npc/007-1-2/_mobs.txt new file mode 100644 index 0000000..172ef66 --- /dev/null +++ b/npc/007-1-2/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-1-2: Canyon Cave mobs +007-1-2,50,51,4,2 monster Maggot 1030,2,35000,150000 +007-1-2,56,29,4,2 monster Maggot 1030,2,35000,150000 +007-1-2,0,0,0,0 monster Desert Maggot 1083,4,35000,150000 +007-1-2,44,36,7,7 monster Scorpion 1071,5,35000,150000 +007-1-2,0,0,0,0 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/007-1-2/_warps.txt b/npc/007-1-2/_warps.txt new file mode 100644 index 0000000..379d05b --- /dev/null +++ b/npc/007-1-2/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-1-2: Canyon Cave warps +007-1-2,50,54,0 warp #007-1-2_50_54 0,0,004-2,85,50 +007-1-2,56,26,0 warp #007-1-2_56_26 0,0,004-2,92,34 +007-1-2,48,20,0 warp #007-1-2_48_20 0,0,004-3-5,36,44 +007-1-2,32,32,0 warp #007-1-2_32_32 0,0,010-2-15,60,36 diff --git a/npc/007-1/_import.txt b/npc/007-1/_import.txt new file mode 100644 index 0000000..bbbfd86 --- /dev/null +++ b/npc/007-1/_import.txt @@ -0,0 +1,12 @@ +// Map 007-1: Tulimshar Mining Camp +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/007-1/_mobs.txt", +"npc/007-1/_warps.txt", +"npc/007-1/caelum.txt", +"npc/007-1/dracoula.txt", +"npc/007-1/naem.txt", +"npc/007-1/pylon.txt", +"npc/007-1/torches.txt", +"npc/007-1/treasure.txt", +"npc/007-1/tycoon.txt", +"npc/007-1/zarkor.txt", diff --git a/npc/007-1/_mobs.txt b/npc/007-1/_mobs.txt new file mode 100644 index 0000000..c0fe019 --- /dev/null +++ b/npc/007-1/_mobs.txt @@ -0,0 +1,14 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-1: Tulimshar Mining Camp mobs +007-1,140,101,43,90 monster Cave Maggot 1027,45,35000,300000,Tycoon::OnKillCaveMaggot +007-1,129,50,14,14 monster Black Scorpion 1074,5,32000,210000,Tycoon::OnKillBlackScorpion +007-1,71,61,20,18 monster Ratto 1005,5,35000,220000,Tycoon::OnKillRatto +007-1,64,50,19,23 monster Piou 1002,3,35000,300000 +007-1,118,140,57,52 monster Cave Snake 1035,25,35000,300000,Tycoon::OnKillCaveSnake +007-1,57,156,37,29 monster Red Scorpion 1072,11,35000,120000,Tycoon::OnKillRedScorpion +007-1,39,125,7,6 monster Cave Maggot 1027,5,35000,270000,Tycoon::OnKillCaveMaggot +007-1,89,72,75,57 monster Cave Bat 1039,9,35000,240000 +007-1,117,94,10,7 monster Black Scorpion 1074,1,35000,220000,Tycoon::OnKillBlackScorpion +007-1,93,110,9,9 monster Cave Maggot 1027,8,35000,300000,Tycoon::OnKillCaveMaggot +007-1,101,141,76,47 monster Night Scorpion 1077,1,3600000,1800000 +007-1,0,0,0,0 monster Ruby Bif 1099,9,35000,300000 diff --git a/npc/007-1/_warps.txt b/npc/007-1/_warps.txt new file mode 100644 index 0000000..2aebed2 --- /dev/null +++ b/npc/007-1/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-1: Tulimshar Mining Camp warps +007-1,56,37,0 warp #007-1_56_37 0,0,004-1,39,110 +007-1,90,52,0 warp #007-1_90_52 0,0,004-1,98,115 +007-1,99,190,0 warp #007-1_99_190 1,0,007-2,64,66 diff --git a/npc/007-1/caelum.txt b/npc/007-1/caelum.txt new file mode 100644 index 0000000..4cc3dda --- /dev/null +++ b/npc/007-1/caelum.txt @@ -0,0 +1,171 @@ +// TMW2 Script +// Author: +// SkyDragon +// Saulc +// Reviewer: +// Jesusalva +// Description: +// Caelum Miner, main author Skydragon Caelum mean Sky in Latin. +// id:193 MineQuests_Caelum + +007-1,143,49,0 script Caelum NPC_PLAYER,{ + // User is behind the wall, do nothing + if (isin("007-1", 142, 43, 3)) + end; + + .@Miner = getq(MineQuest_Caelum); + if (BaseLevel < 28) goto L_TooWeak; + if (.@Miner == 1) goto L_Check; + if (.@Miner == 2) goto L_Complete; + +L_GiveTask: + mesn; + mesq lg("Hello, wanderer!"); + next; + mesq l("How did you end up here?"); + next; + + menu + l("To be honest, I don't know. I was only walking, seeking for adventure!"), L_Quest, + l("It's none of your business."), L_Quit; + + +L_Quest: + mes ""; + mesn; + mesq l("You perchance said in adventure? Good, because I just hurt my arm while mining!"); + next; + mesn; + mesq l("If you bring me some items to I do a bandage and heal myself, I'll give you my gloves!"); + next; + + menu + l("Really? What do you need?"), L_Start, + l("Better do this some other time..."), L_Quit; + + +L_Start: + setq MineQuest_Caelum, 1; + mes ""; + mesn; + mesq l("Ok, what I need is:"); + goto L_List; + +L_Quit: + mes ""; + mesn; + mesq l("Alright."); + close; + +L_List: + mes ""; + mesn; + mes l("Here's what I need:"); + mes l("@@/1 @@", countitem(Lifestone), getitemlink(Lifestone)); + mes l("@@/1 @@", countitem(CottonCloth), getitemlink(CottonCloth)); + mes l("@@/2 @@", countitem(RedScorpionStinger), getitemlink(RedScorpionStinger)); + mes l("@@/5 @@", countitem(ScorpionClaw), getitemlink(ScorpionClaw)); + mes l("@@/10 @@", countitem(BatTeeth), getitemlink(BatTeeth)); + mes l("@@/10 @@", countitem(CactusDrink), getitemlink(CactusDrink)); + mes l("@@/20 @@", countitem(MaggotSlime), getitemlink(MaggotSlime)); + mes l("@@/8 @@, just because I'm hungry as a bear.", countitem(BugLeg), getitemlink(BugLeg)); + close; + +L_Check: + mesn; + mesq l("Did you brought me everything I asked for?"); + next; + menu + l("Yes!"), L_Give, + l("I forgot what you need!"), L_List, + l("No!"), L_Quit; + +L_Give: + if ( + countitem(Lifestone) < 1 || + countitem(CottonCloth) < 1 || + countitem(RedScorpionStinger) < 2 || + countitem(BugLeg) < 8 || + countitem(BatTeeth) < 10 || + countitem(CactusDrink) < 10 || + countitem(MaggotSlime) < 20 || + countitem(ScorpionClaw) < 5 + ) goto L_Lying; + + inventoryplace MinerGloves, 1; + + delitem(Lifestone, 1); + delitem(CottonCloth, 1); + delitem(RedScorpionStinger, 2); + delitem(BugLeg, 8); + delitem(BatTeeth, 10); + delitem(CactusDrink, 10); + delitem(MaggotSlime, 20); + delitem(ScorpionClaw, 5); + + getitem(MinerGloves, 1); + getexp(2855, 43); + setq(MineQuest_Caelum, 2); + + mes ""; + mesn; + mesq l("Here, all yours. I can't use them like I am now. Thank you."); + close; + +L_Complete: + mesn; + mesq l("Wandering too much? Take care to don't get lost."); + if (getq(MineQuest_Pickaxe) < 2) { + next; + select + l("Okay, bye."), + l("Can I become a miner?"); + mes ""; + if (@menu == 2) { + compareandsetq MineQuest_Pickaxe, 0, 1; + mesn; + mesq l("Uh, you should ask %s, he is the miners leader.", b(l("Tycoon"))); + next; + } + } + close; + +// Funnier to write than to read, but the player lied. :angel: +L_Lying: + mesn; + mesq l("No no no, that's wrong."); + next; + mesc l("The miner goes to count your stuff again."); + next; + mesc l("And again."); + next; + mesc l("And again, and again."); + next; + mesc l("And again, and again, again."); + next; + mesc l("You wonder, maybe he entered on an infinite loop? Hellooo, anybody home?"); + next; + mesn; + mesq l("No no no, you don't have everything I've asked for!"); + next; + goto L_List; + +L_TooWeak: + mesn; + mesq l("Wanderer, here is dangerous! Go back!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, MinerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 4); + setunitdata(.@npcId, UDT_HAIRCOLOR, 3); + + .sex = G_MALE; + .distance = 4; + + end; +} diff --git a/npc/007-1/dracoula.txt b/npc/007-1/dracoula.txt new file mode 100644 index 0000000..323fd78 --- /dev/null +++ b/npc/007-1/dracoula.txt @@ -0,0 +1,160 @@ +// TMW2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Dracoula is daily npc, ask for bat teeth every 23 h +// Variable: +// MineQuest_Dracoula + +007-1,170,99,0 script Dracoula NPC_UKAR_F,{ + mesn; + mesq lg("Hello adventurer! Are you lost?"); + next; + mesq l("This is not a place for non-experimented people!"); + next; + mesq l("Mine exit is top left!"); + mes ""; + if (BaseLevel >= 15) goto L_Menu; + close; + +L_Menu: + mesn; + mesq l("But as you're here now, could you do me a favour?"); + mes ""; + menu + l("Hum, Which type of favor?"),L_Quest, + l("Can I find a mana source here?"),L_Mana, + l("Can I become a miner?"),L_Miner, + l("No, thanks. I gonna leave this place."),L_Close; + +L_Quest: + mes ""; + .@q=getq(MineQuest_Dracoula); + if (!.@q) { + mesn; + mesq l("Nice! First let me introduce myself. I am Dracoula, a miner!"); + next; + mesn; + mesq l("I mine here since a while. My favorite activity is to scare other miners!"); + next; + } + if (.@q == 0) goto L_Continue; + .@q2=getq2(MineQuest_Dracoula) + 60 * 60 * 23; + if (santime() >= .@q2) goto L_Repeat; + mesn; + mesq l("But come back in a few hours, I didn't lost all @@!", getitemlink(BatTeeth)); + close; + +L_Continue: + mesq l("I love seeing their terrorize face."); + mes ""; + menu + l("Ok. Cool life!"), L_Close, + l("Haha, Nice, but how do you do that?"), L_Next; + +L_Next: + mes ""; + mesq l("I disguise myself into a giant mutated bat, but every time I break or lose my fake teeth."); + next; + mesq l("I usually ask adventurers for 11 @@, but new miners should be arriving soon. I need to make them cry!", getitemlink(BatTeeth)); + next; + mesq l("So it could be nice, if you could bring me 20 @@,", getitemlink(BatTeeth)); + mes ""; + menu + rif(countitem(BatTeeth) >= 20, l("Hey! I already got them!")), L_Finish, + l("I'll get to it."), L_Close; + close; // double sure + +L_Repeat: + mesn; + if (getequipid(EQI_HEAD_TOP) > 0) + mesq l("Oh it's you @@, I did not recognize you with your hat!", strcharinfo(0)); + else + mesq l("Oh it's you @@, I did not recognize you without a hat!", strcharinfo(0)); + next; + mesq l("Do you have an extra of 11 @@ for me?", getitemlink(BatTeeth)); + mes ""; + menu + rif(countitem(BatTeeth) >= 11, l("Yep, I bring them for you!")), L_Finish2, + l("Actually not."), L_Close; + close; + +// First Time Only +L_Finish: + delitem BatTeeth, 20; + getexp 666, 5; // 20 / 18% = 111 kills * 15 xp = 1665 xp gained from killing. (40% bonus) + Zeny = (Zeny + 600); // 10*20 = 200 base (300% bonus) + setq MineQuest_Dracoula, 1, santime(); + mes ""; + mesn; + mesq l("WAW thank you! Come back later to bring me extra @@!", getitemlink(BatTeeth)); + close; + +// Repeat +L_Finish2: + delitem BatTeeth, 11; + getexp 275, 1; // 11 / 18% = 61 kills * 15 xp = 915 xp gained from killing. (30% bonus) + Zeny = (Zeny + 220); // 10*11 = 110 base (200% bonus) + setq MineQuest_Dracoula, 1, santime(); + mes ""; + mesn; + mesq l("So COOL, thanks! Come back later to bring me extra @@!", getitemlink(BatTeeth)); + close; + +L_Mana: + mes ""; + mesn; + mesq l("Ah! Actually nobody found one."); + next; + mesn; + mesq l("But it's ultimate goal of miners there."); + next; + mesn; + mesq l("If one of us found a Mana stone. They would become rich!"); + next; + mesn; + mesq l("Twelve times more if it is an elusive Mana Fragment no one knows where they are!"); + next; + mesn; + mesq l("That is."); + next; + goto L_Menu; + +L_Miner: + mes ""; + mesn; + mesq l("You should ask %s.", b(l("Tycoon"))); + next; + mesn; + mesq l("He is the Miners leader."); + if (getq(MineQuest_Dracoula)) { + compareandsetq MineQuest_Pickaxe, 0, 1; + } else { + next; + mesn; + mesq l("I can vouch for you, but only if you help me first!"); + } + next; + goto L_Menu; + +L_Close: + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, MinerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, ArtisTankTop); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 8); + + .sex = G_FEMALE; + .distance = 4; + + end; +} + diff --git a/npc/007-1/naem.txt b/npc/007-1/naem.txt new file mode 100644 index 0000000..4a7414b --- /dev/null +++ b/npc/007-1/naem.txt @@ -0,0 +1,66 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Naem is... I'm not sure. + +007-1,180,46,2 script Naem NPC_PLAYER,0,0,{ + .@q = getq(MineQuest_Naem); + mesn; + mesq l("My name is Naem and I guard the tunnels in this direction. You shall not pass. Now leave."); + if (BaseLevel < 28) + close; + if (.@q == 2) { + if (!checkbound(IcedBottle)) close; + next; + inventoryplace MinerTankTop, 1; + delitem IcedBottle, 1; + getitem MinerTankTop, 1; + getexp 440, 0; + setq MineQuest_Naem, 3; + mesn; + mesq l("Oh, %s, thanks! The tunnels behind me are very hot, this will help if I have to go inside them.", getitemlink(IcedBottle)); + next; + mesn; + mesq l("You can have this %s as a token of gratitude. But no, you cannot pass, so keep going!", getitemlink(MinerTankTop)); + close; + } + if (.@q >= 3) + close; + next; + mesn; + mesq l("Actually, you look strong...ish. Still, maybe you can help me."); + next; + mesn; + mesq l("I need groceries. %d %s, %d %s, %d %s, %d %s and %d %s. Give it to my wife, Silvia.", + 7, getitemlink(Aquada), + 6, getitemlink(PiouLegs), + 3, getitemlink(Cheese), + 3, getitemlink(HalfCroconut), + 1, getitemlink(PurpleBlobime)); + next; + mesn; + mesq l("You can collect croconuts by killing any tree with it. Then you can break it in half from the inventory screen."); + compareandsetq MineQuest_Naem, 0, 1; + close; + +// TODO: If you walk past him having the requisite, warp +OnTouch: + npctalk3 l("Hey, I said NO ENTRY! Get moving!"); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, MinerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, MinerTankTop); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 4); + setunitdata(.@npcId, UDT_HAIRCOLOR, 3); + + .sex = G_MALE; + .distance = 4; + end; + +} + diff --git a/npc/007-1/pylon.txt b/npc/007-1/pylon.txt new file mode 100644 index 0000000..ed2f2c0 --- /dev/null +++ b/npc/007-1/pylon.txt @@ -0,0 +1,174 @@ +// TMW2 scripts. +// TMW2 Authors: +// Saulc +// Jesusalva +// Description: +// Miner Pylon transforme gems into gem powder, he ask for 100gp + 1 gem and give 100 EXp (each time) + 1 to 3 gem powder's. +// Variable: +// dont need variable +// Reference: +// https://fr.wikipedia.org/wiki/Pilon#Objets not Epiphysis ! + +007-1,47,44,0 script Pylon NPC_PLAYER,{ + + mesn; + mesq l("Hello, I'm Pylon!"); + next; + mesq l("I work in this mine since 3 years, I'm an expert on gems: Ruby, Topaz, Sapphire, You know!"); + next; + mesq l("If you have some gems, I can transform them into powder."); + next; + mesq l("I only take a tax of 100 gp per gem."); + if (Zeny < 100) + close; + next; + mesq lg("Then, would you like me to transform one of your gems?"); + next; + goto L_Menu; + +L_Menu: + mesn strcharinfo(0); + // We could add a drag zone, and allow players to drag their gems, but... Meh. + select + l("Hum, how many gem powders can I get from one gem?"), + rif(countitem(Diamond) >= 1 && Zeny >= 100, l("Yeah sure, take my Diamond!")), + rif(countitem(Ruby) >= 1 && Zeny >= 100, l("Yeah sure, take my Ruby!")), + rif(countitem(Emerald) >= 1 && Zeny >= 100, l("Yeah sure, take my Emerald!")), + rif(countitem(Sapphire) >= 1 && Zeny >= 100, l("Yeah sure, take my Sapphire!")), + rif(countitem(Topaz) >= 1 && Zeny >= 100, l("Yeah sure, take my Topaz!")), + rif(countitem(Amethyst) >= 1 && Zeny >= 100, l("Yeah sure, take my Amethyst!")), + rif(countitem(IronOre) >= 1 && Zeny >= 100, l("And what's about Iron Ore?")), + rif(countitem(SunnyCrystal) >= 1 && Zeny >= 1000, l("And what's about Sunny Crystal?")), + l("No, thanks, I will keep my gems."); + + mes ""; + + switch (@menu) { + case 1: + goto L_Question; + break; + case 8: + goto L_Ore; + break; + case 9: + goto L_Savior; + break; + case 10: + close; + break; + default: + goto L_Powder; + break; + } + +L_Question: + mesn; + mesq lg("That depends on your luck!"); + next; + mesq l("With one gem you can expect to get 1 to 3 powders!"); + next; + mesc l("We must blame Saulc!"); + next; + mesq lg("By the way would you like to transform your gem?"); + next; + goto L_Menu; + +L_Ore: + mesn; + mesq l("Hum... I'm not really good at it."); + next; + mesn; + mesq l("But I can try my best for you!"); + next; + mesn; + mesq l("If you want it?"); + next; + goto L_MenuOre; + +L_MenuOre: + menu + rif(countitem(IronOre) >= 1 && Zeny >= 100, l("Make me an Iron Powder.")), L_OreOk, + l("Nah, thank you."), L_Close; + +L_OreOk: + // Amount iron ore + .@amo=rand2(1,2); + + delitem IronOre, 1; + Zeny = Zeny - 100; + getexp 10, 0; + inventoryplace IronPowder, .@amo; + getitem IronPowder, .@amo; + mes ""; + mesn; + mesq l("Here you go, I tried my best! Do you want another?"); + next; + goto L_MenuOre; + + +// Must rework IDs +L_Powder: + // Magic + .@id=Diamond+@menu-2; + .@pw=DiamondPowder+@menu-2; + + // Amount + .@am=rand2(1,3); + + delitem .@id, 1; + Zeny = Zeny - 100; + getexp 60, 0; + + + inventoryplace .@pw, .@am; + getitem .@pw, .@am; + + mes ""; + mesn; + mesq l("Here is your powder! I hope it will be useful."); + next; + mesq l("Would you like to transform one more?"); + next; + goto L_Menu; + + +L_Savior: + // FIXME This is a dirt hack. TODO There should be no such checks + if (checkbound(SunnyCrystal)) { + mesn; + mesc l("Not every @@ with you belongs to you.", getitemlink(SunnyCrystal)), 1; + close; + } + mesn; + mesq l("I can make @@ from @@, but this costs @@ GP.", getitemlink(MylarinDust), getitemlink(SunnyCrystal), 1000); + next; + mesn; + mesc l("WARNING, THIS IS A RARE ITEM AND THIS OPERATION CANNOT BE REVERTED!!"); + next; + mesn; + mesc l("Are you sure you want to smash it?"), 1; + next; + if (askyesno() == ASK_YES) { + Zeny-=1000; + delitem SunnyCrystal, 1; + getitem MylarinDust, 3; + mesc l("SMASH! And it is now only dust..."), 3; + next; + } + goto L_Menu; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, MinerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, ArtisTankTop); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 15); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/007-1/torches.txt b/npc/007-1/torches.txt new file mode 100644 index 0000000..5dd82e2 --- /dev/null +++ b/npc/007-1/torches.txt @@ -0,0 +1,153 @@ +// TMW-2 script. +// Author: +// Crazyfefe +// Jesusalva +// Description: +// Torches +// Variables: +// TulimsharQuest_DarkInvocator +// Values: +// 0 Default. +// 1 Quest Found. +// 2 Quest Accepted. +// 3 Bomb Defused. +// 4 Quest Completed. + + +function script CheckTorch { + @torch_count = 0; + @count_tmp = 0; + if ($@GM_OVERRIDE) + npctalk3 l("Hello, I am T-@@, of the @@ order.", @torch, $@TorchBits[@torch]); + if (getq2(TulimsharQuest_DarkInvocator) & $@TorchBits[@torch]) + goto L_Empty; + if (countitem(EverburnPowder) <= 0) { + dispbottom(l("I do not have Everburn Powder to lit the torch.")); + end; + } + setq2 TulimsharQuest_DarkInvocator, getq2(TulimsharQuest_DarkInvocator) | $@TorchBits[@torch]; + + goto L_Loop; + +L_TorchTally: + specialeffect(62); + specialeffect(54); + getmapxy(@m$, .@x, .@y, 0); + + switch (@torch_count) { + case 1: + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Maggot", CaveMaggot, 4, "Zarkor::OnMonster"; + break; + case 2: + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Maggot", CaveMaggot, 5, "Zarkor::OnMonster"; + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Goblin", MagicGoblin, 1, "Zarkor::OnMonster"; + break; + case 3: + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Maggot", CaveMaggot, 6, "Zarkor::OnMonster"; + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Goblin", MagicGoblin, 5, "Zarkor::OnMonster"; + break; + case 4: + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Black Scorpion", BlackScorpion, 1, "Zarkor::OnMonster"; + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Maggot", CaveMaggot, 6, "Zarkor::OnMonster"; + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Goblin", MagicGoblin, 7, "Zarkor::OnMonster"; + break; + case 5: + areamonster @m$, .@x-3, .@y-3, .@x+3, .@y+3, "Black Scorpion", BlackScorpion, 3, "Zarkor::OnMonster"; + areamonster @m$, .@x-3, .@y-3, .@x+3, .@y+3, "Magic Maggot", CaveMaggot, 8, "Zarkor::OnMonster"; + areamonster @m$, .@x-3, .@y-3, .@x+3, .@y+3, "Magic Goblin", MagicGoblin, 10, "Zarkor::OnMonster"; + break; + default: + dispbottom l("BUG, Please report: TorchTally, err_val @@", @torch_count); + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Black Scorpion", BlackScorpion, rand(1,2), "Zarkor::OnMonster"; + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Maggot", CaveMaggot, rand(1,3), "Zarkor::OnMonster"; + areamonster @m$, .@x-2, .@y-2, .@x+2, .@y+2, "Magic Goblin", MagicGoblin, rand(1,5), "Zarkor::OnMonster"; + } + + delitem EverburnPowder, 1; + if (@torch_count == 5) { + message strcharinfo(0), l("All torches are lit!"); + //killmonsterall("007-1"); + setq1 TulimsharQuest_DarkInvocator, 6; + } + + return; + +L_Loop: + while (@count_tmp < 5) { + @count_tmp = (@count_tmp + 1); + if (getq2(TulimsharQuest_DarkInvocator) & $@TorchBits[@count_tmp]) + @torch_count = (@torch_count + 1); + } + goto L_TorchTally; + +L_Empty: + message strcharinfo(0), l("This torch is already lit."); + return; + +} + +007-1,171,145,0 script Torch#1 NPC_NO_SPRITE,{ + @torch = 1; + .@q = getq(TulimsharQuest_DarkInvocator); + if (.@q == 5) { + CheckTorch(); + npctalk(l("What is @@ doing?", strcharinfo(0)), "Miner#SkyDG"); + } + end; +OnInit: + // Remember: array start at zero, but barrels count start at 1. "Fixing" may break torch 5! + setarray $@TorchBits, (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6); + .sex = G_OTHER; + .distance = 1; + end; +} + +007-1,70,116,0 script Torch#2 NPC_NO_SPRITE,{ + @torch = 2; + .@q = getq(TulimsharQuest_DarkInvocator); + if (.@q == 5) { + CheckTorch(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + +007-1,67,133,0 script Torch#3 NPC_NO_SPRITE,{ + @torch = 3; + .@q = getq(TulimsharQuest_DarkInvocator); + if (.@q == 5) { + CheckTorch(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} +007-1,40,125,0 script Torch#4 NPC_NO_SPRITE,{ + @torch = 4; + .@q = getq(TulimsharQuest_DarkInvocator); + if (.@q == 5) { + CheckTorch(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} +007-1,144,24,0 script Torch#5 NPC_NO_SPRITE,{ + @torch = 5; + .@q = getq(TulimsharQuest_DarkInvocator); + if (.@q == 5) { + CheckTorch(); + } + end; +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} diff --git a/npc/007-1/treasure.txt b/npc/007-1/treasure.txt new file mode 100644 index 0000000..9c887ed --- /dev/null +++ b/npc/007-1/treasure.txt @@ -0,0 +1,86 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Fishing and Treasure Box + +007-1,155,163,0 script #fishing_00710 NPC_WATER_SPLASH,{ + + .@regen_time=200; + fishing(2, CommonCarp, RustyKnife, + ScorpionStinger, FatesPotion, GrassCarp); // begin or continue fishing (AlchemyBlueprintA, EquipmentBlueprintA) TODO + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + .cooldown = 200; + .bp_chance = 2; + end; +} + +/* +007-1,x,y,0 duplicate(#fishing_00710) #fishing_00711 NPC_WATER_SPLASH +007-1,x,y,0 duplicate(#fishing_00710) #fishing_00712 NPC_WATER_SPLASH +*/ + +// Animation code by Evol Team +// 4144, gumi, Hal9000, Reid +// (Random) Treasure Chest +// Authored by Jesusalva +// Regenerates every 6 hours +007-1,0,0,0 script #chest_00710 NPC_CHEST,{ + + if (!.busy && !.empty) { + TreasureBox(); + + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + .dir = .dir == 0 ? 2 : 6; // closed ? opening : closing + .busy = true; // lock until available again + initnpctimer; + } else if (!.busy) { + mesc l("Someone looted this treasure box already..."); + } else { + end; + } + close; + +OnTimer160: + .dir = .dir == 6 ? 0 : 4; // closing ? closed : open + end; + +OnTimer500: + .busy = false; // unlock + if (.dir == 0 || .dir == 4) + stopnpctimer; // stop here if the chest is closed + end; + +OnInit: + .busy = false; + .distance = 2; + .empty = false; + +OnClock0156: +OnClock0756: +OnClock1356: +OnClock1956: + // Try to warp randomly to a walkable spot, up to 20 attempts + // Otherwise, it'll stay where it already is (but will close and refill). + .@e=0; .@x=0; .@y=0; + while (!checkcell(.map$, .@x, .@y, cell_chkpass)) + { + if (.@e == 20) { + .@x=.x; + .@y=.y; + break; + } + // Remember the +20 -20 margin adjustment + .@x = rand(20, 185); + .@y = rand(20, 180); + ++.@e; + } + .busy=false; + .empty=false; + movenpc .name$, .@x, .@y, 0; + end; +} diff --git a/npc/007-1/tycoon.txt b/npc/007-1/tycoon.txt new file mode 100644 index 0000000..f5a3249 --- /dev/null +++ b/npc/007-1/tycoon.txt @@ -0,0 +1,382 @@ +// TMW2 scripts. +// TMW2 Authors: +// Saulc +// Jesusalva +// Description: +// Miner leader asks you to purge mine from monsters. +// Variable: +// MineQuest_Tycoon +// Reference: +// npc name from https://www.idleminertycoon.com/ +// Steps: +// 0: Not started +// +// Experience Table +// 15 16 17 18 19 20 +// 830,970,1120,1260,1420,1620, +// 1860,1990,2240,2504,2950,3426,3934,4474,6889,7995, +// 21 22 23 24 25 26 27 28 29 30 +// +// +// 1: Took ratto Quest 10 kills (lvl 15) +// 2: Completed ratto Quest - 50 EXP 50 GP +// 3: Awaiting next quest +// +// 4: Took Cave Maggot Quest 25 kills (lvl 19) +// 5: Completed Cave Maggot Quest - 200 EXP 150 GP + Miner Knife +// 6: Awaiting next quest +// +// 7: Took Cave Snake Quest 45 kills (lvl 24) +// 8: Completed Cave Snake Quest - 1000 EXP 350 GP +// 9: Awaiting next quest +// +// 10: Took Red Scorpion Quest 100 kills (lvl 29) +// 11: Completed Red Scorpion Quest - Miner Hat +// 12: Awaiting next quest +// +// 13: Took Black Scorpion Quest 200 kills (lvl 34) +// 14: Completed Black Scorpion Quest - 12 000 EXP 5 000 GP +// 15: Finished all trainment + +007-1,71,65,0 script Tycoon NPC_PLAYER,{ + .@q=getq(MineQuest_Tycoon); + + mesn; + mesq lg("Hello my friend!"); + next; + mesq lg("Some of my miners friends died in this mine, because there are plenty of dangerous monsters."); + next; + mesq lg("Would you like to help us? Maybe you just need some information."); + next; + goto L_Menu; + +L_PreMenu: + mesn; + mesq l("I need your help dude!"); + next; + goto L_Menu; + +L_Menu: + mesn strcharinfo(0); + menu + rif(getq(MineQuest_Tycoon) < 15, l("I am a brave adventurer! How can I help you?")), L_Purge, + rif(getq(MineQuest_Pickaxe) < 2, l("Can I become a miner?")), L_Miner, + l("What does miners do in cave?"), L_CaveMiner, + l("Can you give me a tip?"), L_Tip, + l("Are you a terranite?"), L_Terranite, + l("I don't care about dirty miners problem."), L_Close, + l("No, thanks, it's not my job!"), L_Close; + +L_Purge: + .@q=getq(MineQuest_Tycoon); + mes ""; + if (BaseLevel < 15) goto L_NoLevel; + .@k=getq2(MineQuest_Tycoon); // Get number of kills (via getq2) + + mesn; + if (.@q == 0) { + mesq l("Ok, you look strong enough. Now, let me think on an easy task for you..."); + next; + mesq l("Ah! I know. Kill 10 @@. They usually are near the mine's inn.", getmonsterlink(Ratto)); + setq MineQuest_Tycoon, 1, 0; + } else if (.@q == 1) { + mesq l("You are killing @@/10 @@. They usually are near the mine's Inn.", .@k, getmonsterlink(Ratto)); + } else if (.@q == 2) { + mesq l("Good job! Here is your reward!"); + getexp 300, 0; // 10% of level requeriment (r7.4) (53->83->300) + set Zeny, Zeny + 50; + setq MineQuest_Tycoon, 3, 0; + } else if (.@q == 3 && BaseLevel >= 19) { + mesq l("Less rattos means more food. So, this time the monsters will be a little stronger."); + next; + mesq l("Ok, now I want you to purge the cave from maggots. Kill 25 @@. They keep respawning and annoy miners team.", getmonsterlink(CaveMaggot)); + setq MineQuest_Tycoon, 4, 0; + } else if (.@q == 3 && BaseLevel < 19) { + mesq l("You are too weak for my task, please come back later with some levels."); + } else if (.@q == 4) { + mesq l("You are killing @@/25 @@ at the cave. Good progress!", getmonsterlink(CaveMaggot)); + } else if (.@q == 5) { + mesq l("Perfect! Here is your reward!"); + getexp 800, 0; // (r7.5 158→800) + set Zeny, Zeny + 150; + getitem MinerKnife, 1; + setq MineQuest_Tycoon, 6, 0; + } else if (.@q == 6 && BaseLevel >= 24) { + mesq l("That was fast. Do not forget to sell their... parts... for some extra cash."); + next; + mesq l("Now, please kill 50 @@, Try to collect some lamps in the same time, they could be useful later.", getmonsterlink(CaveSnake)); + setq MineQuest_Tycoon, 7, 0; + } else if (.@q == 6 && BaseLevel < 24) { + mesq l("You are too weak for my task, please come back later with some levels."); + } else if (.@q == 7) { + mesq l("You are killing @@/50 @@ at the cave.", .@k, getmonsterlink(CaveSnake)); + } else if (.@q == 8) { + mesq l("I hope you got a Jean Shorts, but that depends on luck. Anyway, here is your reward."); + getexp 1150, 0; // (r7.5 1050 → 1150) + set Zeny, Zeny + 350; + setq MineQuest_Tycoon, 9, 0; + } else if (.@q == 9 && BaseLevel >= 29) { + mesq l("If you want to be a real miner friend, do this hard task for me. I will reward you with a @@.", getitemlink(MinerHat)); + next; + mesq l("I'll ask you to kill 100 @@. This will prove your worth. Good luck.", getmonsterlink(RedScorpion)); + setq MineQuest_Tycoon, 10, 0; + } else if (.@q == 9 && BaseLevel < 29) { + mesq l("You are too weak for my task, please come back later with some levels."); + } else if (.@q == 10) { + mesq l("You are killing @@/100 @@. It's still in progress.", .@k, getmonsterlink(RedScorpion)); + } else if (.@q == 11) { + mesq l("Congratulations! Here is your reward, a @@!", getitemlink(MinerHat)); + mesq l("Mining opearations advance steadly day by day with your help, thanks! Maybe one day, we find a Mana Stone here!"); + inventoryplace MinerHat, 1; + getitem MinerHat, 1; + getexp 3800, 0; // r7.5 1365→3800 + setq MineQuest_Tycoon, 12, 0; + } else if (.@q == 12 && BaseLevel >= 40) { + mesq l("The last task I could give you is to exterminate 200 @@. They actually stop miners progress on the mine.", getmonsterlink(BlackScorpion)); + if (BaseLevel < 45) + mesc l("WARNING: This is a level 45 quest!"), 1; + setq MineQuest_Tycoon, 13, 0; + } else if (.@q == 12 && BaseLevel < 40) { + mesq l("You are too weak for my last task, please come back later with some levels."); + } else if (.@q == 13) { + mesq l("You are killing @@/200 @@ at the cave.", .@k, getmonsterlink(BlackScorpion)); + if (BaseLevel < 45) + mesc l("WARNING: This is a level 45 quest!"), 1; + } else if (.@q == 14) { + mesq l("Wow! You did it! I do not think anyone else could have done that."); + mesq l("Here, take this @@ - you deserve it! And here is some gold and experience.", getitemlink(PolishedRuby)); + inventoryplace PolishedRuby, 1; + getitem PolishedRuby, 1; + getexp 55000, 0; // About 94% from needed EXP to level up (you won't be level 45 when you finish) + set Zeny, Zeny + 5000; + setq MineQuest_Tycoon, 15, 0; + } + close; + +L_NoLevel: + mesn; + mesq l("Ah, yes... You see, there is just no task I can give to you right now. You are too weak to fight these monsters."); + next; + mesn; + mesq l("come back stronger, and I will give you a task."); + next; + goto L_Close; + + + +L_Miner: + if (!getq(MineQuest_Pickaxe)) { + mesn; + mesq l("Sorry pal, but only if another miner vouches for you."); + next; + if (getq(MineQuest_Tycoon) >= 9) { + mesn; + mesq l("I'm not neutral, so my vouch have no effect."); + next; + } + tutmes l("You should do the other miners quest first! Some, however, are harder than others. This will unlock the %s, an important item to advance as a crafter.", getitemlink(Pickaxe)); + goto L_Menu; + } + + // Configure Price + .@price=800; + if (getq(MineQuest_Tycoon) >= 9) + .@price-=200; + if (getq(MineQuest_Tycoon) >= 12) + .@price-=100; + if (getq(MineQuest_Tycoon) == 15) + .@price-=300; + if (getq(MineQuest_Caelum) >= 2) + .@price-=150; + + mes ""; + mesn; + mesq l("Well, I'm the miner leader, indeed. A miner need to have the full set."); + next; + mesn; + mesq l("The uniform would be: @@, @@, @@ and @@", getitemlink(LeatherShirt), getitemlink(MinerHat), getitemlink(MinerGloves), getitemlink(Pickaxe)); + mesc l("But you can also wear a %s instead, no problem.", getitemlink(MinerTankTop)); + next; + mesn; + mesq l("I can sell you the Pickaxe for @@ GP if you have and @@ the remaining equipment.", .@price, b(l("equip"))); + if (Zeny < .@price) + close; + + // Allow you to change + enable_items(); + next; + disable_items(); + // If you're in proper uniform, DO NOT ASK FOR CONFIRMATION + if ( + getequipid(EQI_HEAD_TOP) == MinerHat && + (getequipid(EQI_HEAD_MID) == LeatherShirt || + getequipid(EQI_HEAD_MID) == MinerTankTop) && + getequipid(EQI_GARMENT) == MinerGloves && + Zeny >= .@price) { + + inventoryplace Pickaxe, 1; + Zeny-=.@price; + getitem Pickaxe, 1; + setq MineQuest_Pickaxe, 2; + mesn; + mesq l("Good. You look like a real miner! Take this @@ and go mine bifs!", getitemlink(Pickaxe)); + if (TUTORIAL) { + next; + mesc l("@@ is a two-handed weapon with very low damage and attack speed.", getitemlink(Pickaxe)); + mesc l("While it may not be suitable for fighting, by using it against Bifs and other mineral formations, you'll be able to get more ore."); + next; + mesc l("You may even obtain ores which you wouldn't otherwise obtain normally, and the drop rate is fixed."); + mesc l("A Miner is a must-have for a craftsman/craftswoman, as they are in constant need of ores and coal."); + } + next; + } + goto L_Menu; + + +L_CaveMiner: + mes ""; + mesn; + mesq l("Tulimshar miners mainly try to obtain gems."); + next; + mesq l("In this cave we can easily find Coal, Iron ore, and sometimes @@.", getitemlink(Ruby)); // maybe add link for coal and iron ? + next; + goto L_Menu; + +L_Tip: + mes ""; + mesn; + mesq l("If you fight Black scorpion or others dangerous monster you should use the 'hit and run' tactic."); + next; + mesn; + mesq l("To do this you need to disable auto move to target in 'yellow bar'."); + next; + mesn; + mesq l("To open yellow Bar you need to right click on your health bar and active status bar, also called yellow bar. Or you can press '8' on numeric keypad."); + next; + mesn; + mesq l("Now you need to fight monster with the maximum range, you can to do this do these steps in order: lock enemy, Attack, hit, move backwards of one tile, hit, ETC."); + next; + mesn; + mesq l("With this tip you will be able to fight strong monsters. It's on Saul's Strategy Guide Against Archwizards."); + next; + goto L_Menu; + +L_Terranite: + mes ""; + mesn; + mesq l("Haha, no, I'm not! But we found traces of them."); + next; + mesn; + mesq l("Terranite probably mined part of this mine long time ago."); + next; + mesn; + mesq l("That can explain why we have difficulties to find rare gems here. They probably were already extracted."); + next; + goto L_Menu; + + +L_Close: + close; + + + function tycoon_add_kills + { + .@qp=getq(MineQuest_Tycoon); + .@kp=getq2(MineQuest_Tycoon); // Get number of kills (via getq2) + setq MineQuest_Tycoon, .@qp, .@kp+1; + //message strcharinfo(0), l("Set status @@ with @@ kills", .@qp, .@kp); + } + + function tycoon_max_kills + { + .@qp=getq(MineQuest_Tycoon); + setq MineQuest_Tycoon, .@qp+1, 0; + //message strcharinfo(0), l("End status @@", .@qp); + } + +OnKillRatto: + .@q=getq(MineQuest_Tycoon); + .@k=getq2(MineQuest_Tycoon); // Get number of kills (via getq2) + if (.@q == 1) { + if (.@k+1 >= 10) { + tycoon_max_kills(); + message strcharinfo(0), l("All rattos are dead! Go back to Tycoon."); + } else { + tycoon_add_kills(); + message strcharinfo(0), l("@@/10 Rattos", .@k+1); + } + } + fix_mobkill(Ratto); + end; + +OnKillCaveMaggot: + .@q=getq(MineQuest_Tycoon); + .@k=getq2(MineQuest_Tycoon); // Get number of kills (via getq2) + if (.@q == 4) { + if (.@k+1 >= 25) { + tycoon_max_kills(); + message strcharinfo(0), l("All cave maggots are dead! Go back to Tycoon."); + } else { + tycoon_add_kills(); + message strcharinfo(0), l("@@/25 Cave Maggots", .@k+1); + } + } + fix_mobkill(CaveMaggot); + end; +OnKillCaveSnake: + .@q=getq(MineQuest_Tycoon); + .@k=getq2(MineQuest_Tycoon); // Get number of kills (via getq2) + if (.@q == 7) { + if (.@k+1 >= 50) { + tycoon_max_kills(); + message strcharinfo(0), l("All cave snakes are dead! Go back to Tycoon."); + } else { + tycoon_add_kills(); + message strcharinfo(0), l("@@/50 Cave Snakes", .@k+1); + } + } + fix_mobkill(CaveSnake); + end; +OnKillRedScorpion: + .@q=getq(MineQuest_Tycoon); + .@k=getq2(MineQuest_Tycoon); // Get number of kills (via getq2) + if (.@q == 10) { + if (.@k+1 >= 100) { + tycoon_max_kills(); + message strcharinfo(0), l("All red scorpions are dead! Go back to Tycoon."); + } else { + tycoon_add_kills(); + message strcharinfo(0), l("@@/100 Red Scorpions", .@k+1); + } + } + fix_mobkill(RedScorpion); + end; +OnKillBlackScorpion: + .@q=getq(MineQuest_Tycoon); + .@k=getq2(MineQuest_Tycoon); // Get number of kills (via getq2) + if (.@q == 13) { + if (.@k+1 >= 200) { + tycoon_max_kills(); + message strcharinfo(0), l("All black scorpions are dead! Go back to Tycoon."); + } else { + tycoon_add_kills(); + message strcharinfo(0), l("@@/200 Black Scorpions", .@k+1); + } + } + fix_mobkill(BlackScorpion); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, MinerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, ArtisTankTop); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 19); + setunitdata(.@npcId, UDT_HAIRCOLOR, 8); + + .sex = G_MALE; + .distance = 4; + + end; +} diff --git a/npc/007-1/zarkor.txt b/npc/007-1/zarkor.txt new file mode 100644 index 0000000..2eba368 --- /dev/null +++ b/npc/007-1/zarkor.txt @@ -0,0 +1,228 @@ +// TMW2 Script +// Author: +// Crazyfefe +// Jesusalva +// Description: +// Dark Invocator Quest +// Notes: +// You'll be tempted to sell the Dark Desert Mushroom for 8'000 GP. There's a but. +// You won't be able to finish this quest. Ok, no biggie, it was not that rewarding. +// But you won't be able to do more Everburn Powder. And it can be required on another +// quest. And there won't be other way to get the Powder. So DO NOT SELL IT. Think ahead. + +007-1,157,155,0 script Zarkor NPC_MIRAJ,{ + .@q=getq(TulimsharQuest_DarkInvocator); + if (BaseLevel <= 30) + goto L_Weak; + switch (.@q) { + case 0: + goto L_Start; + break; + case 1: + mesn "Zarkor, the Dark Summoner"; + mesq l("Talk to Zitoni, the Alchemist of Candor. He may look humble, but he is the greatest alchemist of this world. I need five @@.", getitemlink(EverburnPowder)); + close; + break; + case 2: + goto L_Price; + break; + case 3: + mesn "Zarkor, the Dark Summoner"; + mesq l("Talk to Zitoni, and deliver the @@. I can't give you another one, they're super rare.", getitemlink(DarkDesertMushroom)); + close; + break; + case 4: + goto L_Quest; + break; + case 5: + mesn "Saulc, the Bug Master"; + mesq col("What, are we really letting you do something so insane as summoning the Monster King?! Blame Saulc!!"); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("You didn't lit every torch yet! Hurry up!!"); + close; + break; + case 6: + if (getq2(TulimsharQuest_DarkInvocator) == 0) { + setq TulimsharQuest_DarkInvocator, 4; + goto L_Quest; + } + goto L_Ritual; + break; + + } + mesn; + mesq l("One day... One day! ONE DAY I'LL GET REVENGE ON YOU, MONSTER KING!"); + close; + +L_Weak: + mesn; + mesq l("Stay out of harm way. Nothing to see here. Stay away, pathetically weak %s.", get_race()); + close; + +L_Start: + mesn; + mesq l("Hey... You... Come here."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("I am Zarkor, the Dark Summoner. I want to do a dark summoning ritual, but there are strings attached."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("I need to buy your silence. I'll reward you greatly when I'm done. But first, I need to know if you'll be loyal to me."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("To prove me your faith, bring me 1 @@ or 1 @@.",getitemlink(Ruby),getitemlink(Topaz)); + mesq l("Such precious gem, is not something you would give for free, unless you are interested on my... rituals."); + next; + mesn strcharinfo(0); + select + l("Indeed. Sorry. I'm not giving you anything."), + rif(countitem(Ruby) >= 1, l("I swear silence, over this bloody ruby.")), + rif(countitem(Topaz) >= 1, l("I swear silence, over this shining topaz.")); + + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("Very well. This conversation never happened."); + next; + mesn; + mesq l("But if you regret this choice later... You know where you can find me."); + close; + break; + case 2: + delitem Ruby, 1; // Acc: 712 GP + setq TulimsharQuest_DarkInvocator, 1; + break; + case 3: + delitem Topaz, 1; // Acc: 825 GP + setq TulimsharQuest_DarkInvocator, 1; + break; + } + mesn "Zarkor, the Dark Summoner"; + mesq l("Very well. I shall now share with you the details of this rite."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("I will summon the Monster King. Don't ask me my reasons. I know this will put Tulimshar in danger. But I will still do it."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("For that, I need to light five torches. The pentagram, the magic, and all the else are with me. Just light the torches."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("Talk to Zitoni, the Alchemist of Candor. He may look humble, but he is the greatest alchemist of this world. I need five @@.", getitemlink(EverburnPowder)); + close; + +L_Price: + mesn strcharinfo(0); + mesq l("Zitoni refused to make the Powder."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("I thought he would. After all, Everburn Powder... is not a mere powder."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("Well, you gave me the gems, so I trust you. Here, take this @@.", getitemlink(DarkDesertMushroom)); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("Take much care with it. It is a very, very rare drop from a rare monster from a difficult, high level area."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("Deliver it to Zitoni on my name. He'll trust it. The relation between me and Zitoni... goes way back."); + getitem DarkDesertMushroom, 1; + setq TulimsharQuest_DarkInvocator, 3; + close; + +L_Quest: + if (countitem(EverburnPowder) < 5) { + mesn "Zarkor, the Dark Summoner"; + mesq l("This is not enough, I need 5 @@ to perform the rite. Go talk with Zitoni.", getitemlink(EverburnPowder)); + close; + } + mesn "Zarkor, the Dark Summoner"; + mesq l("Good, good, you have enough powder. Now I need you to lit all five torches with them."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("Once you lit one of them, monsters will pour out, so take care to don't die."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("If you die, you'll have to begin again. And there is the experience penalty, too."); + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("Good luck, because you'll need it. Talk to me once all of them are lit, and I'll SUMMON DAH MONSTER KING."); + setq TulimsharQuest_DarkInvocator, 5; + close; + +L_Ritual: + percentheal 100, 0; + specialeffect(56); + mesc l("Zarkor spills many fluids on the lake, with much concentration."); + next; + specialeffect(56); + mesn "Zarkor, the Dark Summoner"; + mesc l("Zarkor starts a weird chant."); + next; + specialeffect(52); + killmonster("007-1", "Zarkor::OnMonster"); + mesn "Zarkor, the Dark Summoner"; + mesq l("MONSTER KING, APPEAR TO ME!"); + next; + mesn "MONSTER KING"; + mesc l("...Foolish human... Do you really think I will attend your summon?"), 1; + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("But... But! Where are you?! That summon ritual is equivalent to GM magic!"); + next; + mesn "MONSTER KING"; + mesc l("And who do you think that created GM Magic in first place, inexperienced kid?"), 1; + next; + mesn "Zarkor, the Dark Summoner"; + mesq l("You... You killed my father during Nivalis siege! I'll make you pay for that!!"); + next; + mesn "MONSTER KING"; + mesc l("You humans are so greedy... Just like me. I have more important things to do, other than waste time with you."), 1; + next; + mesn "MONSTER KING"; + mesc l("Zarkor, fell a small taste of my power. That is nothing. If you can't handle that, you're not even near my lowest lieutenant."), 1; + mesc l("And you got help from a kid, Zarkor? Lucky is that kid, that I don't have time to deal with them. Farewell."), 1; + next; + specialeffect(312); + mesn "Zarkor, the Dark Summoner"; + mes l("GAAAAAAAAAAAAAAAAHHHHHHH!"); + next; + goto L_Finish; + + + +L_Finish: + setq TulimsharQuest_DarkInvocator, 7; + // These values were taken from Experience table, and are magic numbers + getexp 7995, 412; // 100% for levelset (30,11) + // Pay roughly what you spent + // Powder + Trips + Gems + Sulf + Iron + Ash + Profit = Money + // (3000*5 + 4*500G + 1000 + 5*84 + 5*66 + 5*80 + 850) = 20.000 + Zeny=Zeny+20000; + getitem ZarkorScroll, 1; + + mesn; + mesq l("*Ugh* Thanks for your help. Here... Take this, as promised."); + next; + mesn strcharinfo(0); + mesq l("Are you feeling well? That was a pretty powerful attack!"); + next; + mesn; + mesq lg("Do not worry with me, youngling... I'll be fine."); + close; + +OnMonster: + end; + +OnPCDieEvent: + if (getq(TulimsharQuest_DarkInvocator) == 5 || getq(TulimsharQuest_DarkInvocator) == 6) + setq2 TulimsharQuest_DarkInvocator, 0; + end; + +OnInit: + .sex = G_MALE; + .distance = 6; + end; + +} diff --git a/npc/007-2/_config.txt b/npc/007-2/_config.txt new file mode 100644 index 0000000..bd7f183 --- /dev/null +++ b/npc/007-2/_config.txt @@ -0,0 +1,25 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-2: Tulimshar Volcano Underground conf + +007-2,48,32,0 script #007-2_48_32 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=2; + end; +} + +007-2,65,66,0 script #007-2_65_66 NPC_HIDDEN,3,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnHeat"; + end; +} + +007-2,54,26,0 script #007-2_54_26 NPC_HIDDEN,0,1,{ + end; +OnTouch: + doevent "#DungeonCore::OnHeat"; + end; +} diff --git a/npc/007-2/_import.txt b/npc/007-2/_import.txt new file mode 100644 index 0000000..33fc5a2 --- /dev/null +++ b/npc/007-2/_import.txt @@ -0,0 +1,5 @@ +// Map 007-2: Tulimshar Volcano Underground +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/007-2/_config.txt", +"npc/007-2/_mobs.txt", +"npc/007-2/_warps.txt", diff --git a/npc/007-2/_mobs.txt b/npc/007-2/_mobs.txt new file mode 100644 index 0000000..ef8ee33 --- /dev/null +++ b/npc/007-2/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-2: Tulimshar Volcano Underground mobs +007-2,49,78,48,19 monster Red Scorpion 1072,12,25000,25000,Tycoon::OnKillRedScorpion +007-2,47,76,48,19 monster Fire Goblin 1067,12,25000,25000 +007-2,51,75,48,19 monster Red Slime 1092,20,25000,25000 +007-2,45,73,48,19 monster Angry Red Scorpion 1130,12,25000,25000 +007-2,45,37,48,19 monster Lava Slime 1097,16,25000,25000 +007-2,40,33,46,19 monster Red Mushroom 1042,6,25000,25000 +007-2,36,31,44,15 monster Fire Fairy 1183,12,25000,25000 +007-2,49,23,31,10 monster Red Slime Mother 1240,3,35000,25000 diff --git a/npc/007-2/_warps.txt b/npc/007-2/_warps.txt new file mode 100644 index 0000000..3817f27 --- /dev/null +++ b/npc/007-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 007-2: Tulimshar Volcano Underground warps +007-2,65,65,0 warp #007-2_65_65 1,0,007-1,98,189 diff --git a/npc/008-0/_import.txt b/npc/008-0/_import.txt new file mode 100644 index 0000000..1ff3fcc --- /dev/null +++ b/npc/008-0/_import.txt @@ -0,0 +1,6 @@ +// Map 008-0: Party Dungeon Waiting Hall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/008-0/_mobs.txt", +"npc/008-0/_warps.txt", +"npc/008-0/mapflags.txt", +"npc/008-0/master.txt", diff --git a/npc/008-0/_mobs.txt b/npc/008-0/_mobs.txt new file mode 100644 index 0000000..a0d03ce --- /dev/null +++ b/npc/008-0/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 008-0: Party Dungeon Waiting Hall mobs +008-0,49,49,28,28 monster Ratto 1005,6,35000,300000 +008-0,50,47,28,28 monster Cave Bat 1039,4,35000,300000 +008-0,48,48,28,28 monster Silk Worm 1034,6,35000,300000 +008-0,49,63,29,16 monster Red Scorpion 1072,3,35000,300000 +008-0,49,32,29,14 monster Scorpion 1071,3,35000,300000 diff --git a/npc/008-0/_warps.txt b/npc/008-0/_warps.txt new file mode 100644 index 0000000..b14f6a7 --- /dev/null +++ b/npc/008-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 008-0: Party Dungeon Waiting Hall warps +008-0,41,22,0 warp #008-0_41_22 0,0,003-1-1,44,142 diff --git a/npc/008-0/mapflags.txt b/npc/008-0/mapflags.txt new file mode 100644 index 0000000..e292f7e --- /dev/null +++ b/npc/008-0/mapflags.txt @@ -0,0 +1,3 @@ +008-1 mapflag zone MMO +008-2 mapflag zone MMO +008-2 mapflag pvp diff --git a/npc/008-0/master.txt b/npc/008-0/master.txt new file mode 100644 index 0000000..2158e53 --- /dev/null +++ b/npc/008-0/master.txt @@ -0,0 +1,183 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// This NPC is a work on progress. It measures all players equal. +// Controls the whole dungeon feature. + +008-0,47,63,0 script Party Master NPC_BRGUARD_SWORD,{ + if (!is_staff()) + goto L_TODO; // TODO + + mesn; + if (getcharid(1) > 0) { + mesq l("You're in the \"@@\" party, very good!", getpartyname(getcharid(1))); + next; + } else { + mesq l("I protect a dungeon for PARTIES. You're not on a party, get moving."); + close; + } + + mesn; + mesq l("I protect a very dangerous dungeon, and it is so dangerous, that only parties can go in."); + next; + if (strcharinfo(0) != getpartyleader(getcharid(1))) goto L_NotYou; + mesn; + mesq l("There are five floors, and they're all very dangerous. But there are riches to be found."); + next; + mesn; + mesq l("You also can't stay there forever! You will have about 20 minutes to entirely clear it out and defeat the last boss."); + if (!party_expon(getcharid(1))) + mesc l("Note: Your party is currently not sharing experience, and will suffer a time penalty. Your time will be halved."), 6; + next; + // Get info about your party, and backup it + getpartymember(getcharid(1)); + .@count = $@partymembercount; + copyarray(.@name$[0], $@partymembername$[0], $@partymembercount); + copyarray(.@aid[0], $@partymemberaid[0], $@partymembercount); + copyarray(.@cid[0], $@partymembercid[0], $@partymembercount); + .@count_online=0; + + if (.@count < 3 && !$@GM_OVERRIDE) goto L_TooSmall; + mesn; + mesc l("Are you and your party ready?"), 1; + if (askyesno() != ASK_YES) + close; + + // Loop though party to see if you can start + for (.@i = 0; .@i < .@count; ++.@i) { + // Online? + if (isloggedin(.@aid[.@i], .@cid[.@i])) { + getmapxy(.@m$, .@x, .@y, 0, .@name$[.@i]); + // Here? + if (.@m$ == .map$) + .@count_online++; + } + } + + // How many are logged in and here? + if (.@count_online < 3 && !$@GM_OVERRIDE) goto L_TooSmall; + + // TODO: Query if exp sharing is enabled + // TODO: Instance for party + + // Warp everyone and add timers + partytimer("008-0", 1000, "Party Master::OnStart", getcharid(1)); + warpparty("008-1", 176, 20, getcharid(1), "008-0", true); + + // One bonus time + monster("008-1", 90, 69, "Time Bonus", Mouboo, 1, "Party Master::OnMobTime"); + + // Four Chests + monster("008-1", 38, 104, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + monster("008-1", 41, 104, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + monster("008-1", 44, 104, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + monster("008-1", 47, 104, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + + // We still need the SummonBoss NPC and etc. + close; + + + + + + + + + +L_TODO: + mesn; + mesq l("Jesusalva still have this closed because the traps are broken... %%n But we shall open soon. %%G"); + next; + mesn; + mesq l("It's not hard to repair, but nobody is bothering Jesusalva on #world as of late. %%n"); + mesq l("Because this, the national budget is going to other silly, under-rewarding, minor things..."); + mesc l("If you think this should be a priority, please ask Jesusalva."), 1; + close; + +// Only the party leader can start this. But you can rejoin, as long map leader is on 008-x +L_NotYou: + getmapxy(.@m$, .@x, .@y, 0, getpartyleader(getcharid(1))); + if (.@m$ ~= "008-*" && + .@m$ != "008-0" && + BaseLevel > 0 && + @pmloop) { + mesn; + mesq l("@@, your party leader, is inside, I'm not sure where.", getpartyleader(getcharid(1))); + mesc l("Enter dungeons? You won't be able to join anymore when he dies."), 1; + if (askyesno() != ASK_YES) + close; + // Double check + getmapxy(.@m$, .@x, .@y, 0, getpartyleader(getcharid(1))); + if (.@m$ ~= "008-*" && .@m$ != "008-0" && getcharid(1) > 0) + warp "008-1", 176, 20; + } else { + mesn; + mesq l("If you bring me @@, your party leader, I can let you in.", getpartyleader(getcharid(1))); + } + close; + +// Minimum 3 players +L_TooSmall: + mesn; + mesq l("However, I need to see at least three volunteers here, to allow you in."); + close; + + + +// Main Loop +OnStart: + @pmloop=0; +OnLoop: + @pmloop+=1; + .@lost=""; + + // Anti-Crazyfefe™ Alpha System + if (getcharid(1) <= 0) { + // Left the party! + .@lost=l("You are not a member of a party anymore."); + } + if (!party_expon(getcharid(1))) { + // Party exp sharing disabled means time penalty. + @pmloop+=1; + } + + // Check if party master still alive and in caves. + if (getmapxy(.@m$, .@x, .@y, 0, getpartyleader(getcharid(1))) <= 0) + .@lost=l("Party leader is gone."); + else if (!(.@m$ ~= "008-*")) + .@lost=l("Party leader is not on dungeons."); + + // Add new cycle or finish. + if (@pmloop < 1200 && .@lost == "") + addtimer(1000, "Party Master::OnLoop"); + else + .@lost=l("You ran out of time..."); + + // See if it is time to finish + if (.@lost != "") { + @pmloop=0; + warp "008-0", 47, 64; + dispbottom str(.@lost); + } + end; + +OnMobTime: + getmapxy(.@m$, .@x, .@y, 0); + areatimer(.@m$, .@x-4, .@y-4, .@x+4, .@y+4, 10, "Party Master::OnAddTime"); + end; + +OnAddTime: + @pmloop=60; + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + + // Won't be finished until later later later™ + if (!debug) + disablenpc .name$; + end; +} + diff --git a/npc/008-1/_import.txt b/npc/008-1/_import.txt new file mode 100644 index 0000000..ecc43a5 --- /dev/null +++ b/npc/008-1/_import.txt @@ -0,0 +1,5 @@ +// Map 008-1: 1st Floor - Party Dungeon +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/008-1/_mobs.txt", +"npc/008-1/_warps.txt", +"npc/008-1/master.txt", diff --git a/npc/008-1/_mobs.txt b/npc/008-1/_mobs.txt new file mode 100644 index 0000000..e65aade --- /dev/null +++ b/npc/008-1/_mobs.txt @@ -0,0 +1,23 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 008-1: 1st Floor - Party Dungeon mobs +008-1,155,138,15,15 monster Ratto 1005,2,15000,15000 +008-1,34,113,28,16 monster Ratto 1005,2,15000,15000 +008-1,52,147,42,15 monster Angry Scorpion 1131,4,15000,25000 +008-1,118,144,22,17 monster Pinkie 1132,3,15000,25000 +008-1,72,57,49,34 monster House Maggot 1084,14,15000,25000 +008-1,165,75,16,13 monster Slime Blast 1090,2,15000,25000 +008-1,37,54,29,26 monster Red Scorpion 1072,2,15000,25000 +008-1,132,108,42,15 monster Fire Goblin 1067,4,15000,25000 +008-1,157,158,17,15 monster Mana Ghost 1068,1,15000,25000 +008-1,154,46,29,15 monster Mineral Bif 1058,2,15000,45000 +008-1,147,143,29,27 monster Squirrel 1032,4,15000,25000 +008-1,146,44,37,30 monster Maggot 1030,8,15000,25000 +008-1,134,96,41,18 monster Candor Scorpion 1073,6,15000,25000 +008-1,66,41,42,15 monster Bat 1039,5,15000,25000 +008-1,60,78,35,22 monster Duck 1029,4,15000,25000 +008-1,52,133,37,31 monster Cave Maggot 1027,10,15000,25000 +008-1,103,144,12,15 monster Little Blub 1007,3,15000,25000 +008-1,99,54,79,33 monster Piou 1002,8,15000,25000 +008-1,100,108,79,21 monster Piousse 1003,6,15000,25000 +008-1,59,149,39,19 monster Plushroom Field 1011,2,15000,25000 +008-1,139,150,39,19 monster Chagashroom Field 1128,2,15000,25000 diff --git a/npc/008-1/_warps.txt b/npc/008-1/_warps.txt new file mode 100644 index 0000000..9ed2049 --- /dev/null +++ b/npc/008-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 008-1: 1st Floor - Party Dungeon warps +008-1,176,19,0 warp #008-1_176_19 0,0,008-0,47,64 diff --git a/npc/008-1/master.txt b/npc/008-1/master.txt new file mode 100644 index 0000000..51a1994 --- /dev/null +++ b/npc/008-1/master.txt @@ -0,0 +1,150 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// This NPC is a work on progress. It measures all players equal. +// Controls the first floor, but not many things to control. + +008-1,48,117,0 script #FPDM12 NPC_HIDDEN,0,0,{ + end; +OnTouch: + // IMPORTANT: Prevent party master from returning here + getmapxy(.@m$, .@x, .@y, 0, getpartyleader(getcharid(1))); + if (.@m$ ~= "008-*" && + .@m$ != "008-0" && + .@m$ != "008-1" && + BaseLevel > 20 && + mobcount("008-1", "First Dungeon Master::OnBossDeath") == 0 && + @pmloop) { + dispbottom l("Go and follow @@, your party leader!", getpartyleader(getcharid(1))); + warp "008-2", 135, 20; + } else if (BaseLevel > 20) { + dispbottom l("If I bring @@, my party leader, and the boss is defeated, I can go you in.", getpartyleader(getcharid(1))); + } else { + mesc l("I cannot pass, because I am only level @@.", BaseLevel); + mesc l("Newbies can only get past their limits once, with their party leader level help! If they die, they can't rejoin!"); + // I thought it would be better to allow first passage based on leader level. + // Parties are not meant to be permanent, and this helps a lot. + } + end; +} + +008-1,48,117,0 script First Dungeon Master NPC_BRGUARD_SWORD,{ + // Double-check, you should have been flung out long ago. + if (getcharid(1) <= 0 || @pmloop <= 0) { + mesn; + mesq l("Something seems wrong."); + warp "SavePoint",0,0; + close; + } + if (mobcount("008-1", "First Dungeon Master::OnBossDeath") > 0) { + npctalk3 l("What are you doing? Fight!"); + end; + } + + mesn; + mesq l("Compliments reaching this far, @@ from the @@ party!", strcharinfo(0), getpartyname(getcharid(1))); + next; + + mesn; + mesq l("I can summon the BOSS for the level 0~20 area."); + next; + if (strcharinfo(0) != getpartyleader(getcharid(1))) goto L_NotYou; + if (BaseLevel < 20) goto L_TooWeak; + mesn; + mesq l("He is strong though, so keep your party together!"); + next; + // Get info about your party, and backup it + getpartymember(getcharid(1)); + .@count = $@partymembercount; + copyarray(.@name$[0], $@partymembername$[0], $@partymembercount); + copyarray(.@aid[0], $@partymemberaid[0], $@partymembercount); + copyarray(.@cid[0], $@partymembercid[0], $@partymembercount); + .@count_online=0; + + if (.@count < 3 && !$@GM_OVERRIDE) goto L_TooSmall; + mesn; + mesc l("Are you and your party ready?"), 1; + if (askyesno() != ASK_YES) + close; + + // Loop though party to see if you can start + for (.@i = 0; .@i < .@count; ++.@i) { + // Online? + if (isloggedin(.@aid[.@i], .@cid[.@i])) { + getmapxy(.@m$, .@x, .@y, 0, .@name$[.@i]); + // Here? + if (.@m$ == .map$) + .@count_online++; + } + } + + // How many are logged in and here? + if (.@count_online < 3 && !$@GM_OVERRIDE) goto L_TooSmall; + + // TODO: Query if exp sharing is enabled + // TODO: Instance for party + // TODO: Second Floor + npctalk l("@@: Fight!", getpartyname(getcharid(1))); + monster "008-1", rand(37,54), rand(109,122), "First Dungeon Boss", Sarracenus, 1, "First Dungeon Master::OnBossDeath"; + close; + + + + + + + + + + +// Only the party leader can start this. +L_NotYou: + mesn; + dispbottom l("Go and fetch @@, the party leader!", getpartyleader(getcharid(1))); + close; + +// Minimum 3 players +L_TooSmall: + mesn; + mesq l("However, I need to see at least three volunteers here, to allow you in."); + close; + +// Must have level to face boss +L_TooWeak: + mesn; + mesq l("This is the end for your party, as the leader doesn't have sufficient level."); + close; + +// Second Floor special monsters +OnReward: + Zeny=Zeny+200; + getexp 200, 20; + dispbottom l("Reward: 200 GP, 200 XP"); + +// Boss death causes 008-2 to be set +OnBossDeath: + // Give every party member in map a reward + partytimer("008-1", 200, "First Dungeon Master::OnReward", getcharid(1)); + + // Warp everyone and add timers + warpparty("008-2", 135, 20, getcharid(1), "008-1", true); + + // Bonus Monsters + monster("008-1", 104, 238, "Time Bonus", NightScorpion, 1, "Party Master::OnMobTime"); + monster("008-1", 85, 117, "Time Bonus", BlackScorpion, 1, "Party Master::OnMobTime"); + + // Five Chests + monster("008-2", 70, 239, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + monster("008-2", 70, 241, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + monster("008-2", 70, 243, "Mysterious Chest", any(BronzeChest, BronzeMimic, SilverChest, SilverMimic), 1); + monster("008-2", 70, 245, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + monster("008-2", 70, 247, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/008-2/_config.txt b/npc/008-2/_config.txt new file mode 100644 index 0000000..01efa14 --- /dev/null +++ b/npc/008-2/_config.txt @@ -0,0 +1,127 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 008-2: 2nd Floor - Party Dungeon conf + +008-2,80,208,0 script #008-2_80_208 NPC_HIDDEN,{ + end; +OnDisable: + delcells "008-2_80_208"; end; +OnEnable: +OnInit: + setcells "008-2", 80, 208, 88, 208, 1, "008-2_80_208"; +} + +008-2,106,237,0 script #008-2_106_237 NPC_SWITCH_ONLINE,{ + if (getnpcclass() == NPC_SWITCH_OFFLINE) + end; + callfunc "massprovoke", 12; + setnpcdisplay "#008-2_106_237", NPC_SWITCH_OFFLINE; + end; +OnInit: + .distance=2; +} + +008-2,66,143,0 script #008-2_66_143 NPC_SWITCH_OFFLINE,{ + if (getnpcclass() == NPC_SWITCH_ONLINE) + end; + callfunc "grenade", 12, 10000; + setnpcdisplay "#008-2_66_143", NPC_SWITCH_ONLINE; + end; +OnInit: + .distance=2; +} + +008-2,111,166,0 script #008-2_111_166 NPC_SWITCH_ONLINE,{ + if (getnpcclass() == NPC_SWITCH_OFFLINE) + end; + callfunc "ALCReset"; + setnpcdisplay "#008-2_111_166", NPC_SWITCH_OFFLINE; + end; +OnInit: + .distance=2; +} + +008-2,135,21,0 script #008-2_135_21 NPC_HIDDEN,2,1,{ + end; +OnTouch: + callfunc "massprovoke", 12; + end; +} + +008-2,54,237,0 script #008-2_54_237 NPC_HIDDEN,2,1,{ + end; +OnTouch: + doevent "#008-2_80_208::OnDisable"; + end; +} + +008-2,72,247,0 script #008-2_72_247 NPC_TRAP_ONLINE,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; +OnTouch: +OnTouchNPC: + IronTrap(200, 0, 1); + end; +} + +008-2,87,240,0 script #008-2_87_240 NPC_TRAP_ONLINE,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; +OnTouch: +OnTouchNPC: + IronTrap(200, 0, 1); + end; +} + +008-2,46,211,0 script #008-2_46_211 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; +OnTouch: +OnTouchNPC: + IronTrap(200, 10, 2); + end; +OnTimer10000: + stopnpctimer; setnpctimer 0; setnpcdisplay "#008-2_46_211", NPC_TRAP; end; +} + +008-2,85,117,0 script #008-2_85_117 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; +OnTouch: +OnTouchNPC: + IronTrap(400, 15, 3); + end; +OnTimer15000: + stopnpctimer; setnpctimer 0; setnpcdisplay "#008-2_85_117", NPC_TRAP; end; +} + +008-2,59,222,0 script #008-2_59_222 NPC_HIDDEN,{ + end; +OnDisable: + delcells "008-2_59_222"; end; +OnEnable: +OnInit: + setcells "008-2", 59, 222, 67, 222, 1, "008-2_59_222"; +} + +008-2,32,99,0 script #008-2_32_99 NPC_SWITCH_ONLINE,{ + if (getnpcclass() == NPC_SWITCH_OFFLINE) + end; + doevent "#008-2_59_222::OnDisable"; + setnpcdisplay "#008-2_32_99", NPC_SWITCH_OFFLINE; + end; +OnInit: + .distance=2; +} + +008-2,87,221,0 script #008-2_87_221 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=3; + end; +} diff --git a/npc/008-2/_import.txt b/npc/008-2/_import.txt new file mode 100644 index 0000000..12a0692 --- /dev/null +++ b/npc/008-2/_import.txt @@ -0,0 +1,6 @@ +// Map 008-2: 2nd Floor - Party Dungeon +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/008-2/_config.txt", +"npc/008-2/_mobs.txt", +"npc/008-2/_warps.txt", +"npc/008-2/master.txt", diff --git a/npc/008-2/_mobs.txt b/npc/008-2/_mobs.txt new file mode 100644 index 0000000..db96f19 --- /dev/null +++ b/npc/008-2/_mobs.txt @@ -0,0 +1,28 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 008-2: 2nd Floor - Party Dungeon mobs +008-2,79,244,15,12 monster Slime Blast 1090,2,15000,15000 +008-2,47,200,21,39 monster Bandit 1024,4,25000,25000 +008-2,106,203,33,26 monster Mouboo 1023,4,25000,25000 +008-2,84,35,61,19 monster Cave Snake 1035,7,25000,25000 +008-2,60,74,35,18 monster Giant Maggot 1031,4,25000,25000 +008-2,86,107,8,14 monster Moggun 1070,2,25000,25000 +008-2,48,126,28,33 monster Mana Bug 1075,6,25000,25000 +008-2,119,244,23,13 monster Copper Slime 1088,1,25000,25000 +008-2,117,84,21,27 monster Red Slime 1092,6,25000,25000 +008-2,122,157,21,17 monster Yellow Slime 1091,3,25000,25000 +008-2,110,125,21,14 monster Lava Slime 1097,2,25000,25000 +008-2,89,158,9,17 monster Snake 1122,2,25000,25000 +008-2,37,63,21,39 monster Bandit 1024,4,25000,25000 +008-2,119,93,21,77 monster Desert Log Head 1127,6,25000,25000 +008-2,117,213,21,39 monster Desert Bandit 1124,3,25000,25000 +008-2,30,197,17,24 monster Sarracenus 1125,2,25000,25000 +008-2,88,70,10,48 monster Angry Red Scorpion 1130,6,25000,25000 +008-2,67,87,10,60 monster Sea Slime 1093,7,25000,25000 +008-2,33,138,21,32 monster Robin Bandit 1153,1,25000,25000 +008-2,59,204,11,34 monster Candied Slime 1089,4,25000,25000 +008-2,86,172,10,54 monster Green Slime 1085,6,25000,25000 +008-2,81,61,73,39 monster Plushroom Field 1011,4,25000,45000 +008-2,81,242,21,15 monster Piou 1002,3,25000,15000 +008-2,81,117,73,39 monster Chagashroom Field 1128,4,25000,45000 +008-2,40,200,32,39 monster Big Ruby Bif 1100,1,35000,45000 +008-2,103,200,29,39 monster Sapphire Bif 1114,2,25000,45000 diff --git a/npc/008-2/_warps.txt b/npc/008-2/_warps.txt new file mode 100644 index 0000000..adea286 --- /dev/null +++ b/npc/008-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 008-2: 2nd Floor - Party Dungeon warps +008-2,80,255,0 warp #008-2_80_255 0,0,008-2,54,237 diff --git a/npc/008-2/master.txt b/npc/008-2/master.txt new file mode 100644 index 0000000..5577e40 --- /dev/null +++ b/npc/008-2/master.txt @@ -0,0 +1,172 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// This NPC is a work on progress. It measures all players equal. +// Controls the second floor + +// 21: From 2 to 1 +008-2,135,19,0 script #FPDM21 NPC_HIDDEN,0,0,{ + end; +OnTouch: + // Party leader cannot retreat + if (strcharinfo(0) == getpartyleader(getcharid(1))) + dispbottom l("As the leader of your party, you can only go forward."); + else + warp "008-1", 43, 104; + end; +} + +008-2,135,19,0 script #FPDM23 NPC_HIDDEN,0,0,{ + end; +OnTouch: + end; + // IMPORTANT: Prevent party master from returning here + getmapxy(.@m$, .@x, .@y, 0, getpartyleader(getcharid(1))); + if (.@m$ ~= "008-*" && + .@m$ != "008-0" && + .@m$ != "008-1" && + .@m$ != "008-2" && + BaseLevel > 40 && + mobcount("008-2", "Second Dungeon Master::OnBossDeath") == 0 && + @pmloop) { + dispbottom l("Go and follow @@, your party leader!", getpartyleader(getcharid(1))); + warp "008-1", 176, 20; // 008-2 + } else if (BaseLevel > 40) { + dispbottom l("If I bring @@, my party leader, and the boss is defeated, I can go you in.", getpartyleader(getcharid(1))); + } else { + mesc l("I cannot pass, because I am only level @@.", BaseLevel); + mesc l("Newbies can only get past their limits once, with their party leader level help! If they die, they can't rejoin!"); + // I thought it would be better this way. There was at least three different ways to prevent that. + } + end; +} + +008-2,75,240,0 script Second Dungeon Master NPC_BRGUARD_SWORD,{ + // Double-check, you should have been flung out long ago. + if (getcharid(1) <= 0 || @pmloop <= 0) { + mesn; + mesq l("Something seems wrong."); + warp "SavePoint",0,0; + close; + } + if (mobcount("008-2", "Second Dungeon Master::OnBossDeath") > 0) { + npctalk3 l("What are you doing? Fight!"); + end; + } + + mesn; + mesq l("Compliments reaching this far, @@ from the @@ party!", strcharinfo(0), getpartyname(getcharid(1))); + next; + + mesn; + mesq l("I can summon the BOSS for the level 21~40 area."); + next; + if (strcharinfo(0) != getpartyleader(getcharid(1))) goto L_NotYou; + if (BaseLevel < 40) goto L_TooWeak; + mesn; + mesq l("He is strong though, so keep your party together!"); + next; + // Get info about your party, and backup it + getpartymember(getcharid(1)); + .@count = $@partymembercount; + copyarray(.@name$[0], $@partymembername$[0], $@partymembercount); + copyarray(.@aid[0], $@partymemberaid[0], $@partymembercount); + copyarray(.@cid[0], $@partymembercid[0], $@partymembercount); + .@count_online=0; + + if (.@count < 3 && !$@GM_OVERRIDE) goto L_TooSmall; + mesn; + mesc l("Are you and your party ready?"), 1; + if (askyesno() != ASK_YES) + close; + + // Loop though party to see if you can start + for (.@i = 0; .@i < .@count; ++.@i) { + // Online? + if (isloggedin(.@aid[.@i], .@cid[.@i])) { + getmapxy(.@m$, .@x, .@y, 0, .@name$[.@i]); + // Here? + if (.@m$ == .map$) + .@count_online++; + } + } + + // How many are logged in and here? + if (.@count_online < 3 && !$@GM_OVERRIDE) goto L_TooSmall; + + // TODO: Query if exp sharing is enabled + // TODO: Instance for party + // TODO: Second Floor + npctalk l("@@: Fight!", getpartyname(getcharid(1))); + monster "008-2", rand(74,84), rand(241,250), "Second Dungeon Boss", BanditLord, 1, "Second Dungeon Master::OnBossDeath"; + close; + + + + + + + + + + +// Only the party leader can start this. +L_NotYou: + mesn; + dispbottom l("Go and fetch @@, the party leader!", getpartyleader(getcharid(1))); + close; + +// Minimum 3 players +L_TooSmall: + mesn; + mesq l("However, I need to see at least three volunteers here, to allow you in."); + close; + +// Must have level to face boss +L_TooWeak: + mesn; + mesq l("This is the end for your party, as the leader doesn't have sufficient level."); + close; + +// Second Floor special monsters +OnReward: + // 40% odds of a Black Scorpion coming to avenge fallen monster leader + if (rand(1,5) % 2) { + getmapxy(.@m$, .@x, .@y, 0); + monster .@m$, .@x, .@y, "Semi Boss", BlackScorpion, 1; + } + Zeny=Zeny+700; + getexp 400, 60; + getitem CelestiaTea, 1; + dispbottom l("Reward: 700 GP, 400 XP, 1 @@", getitemlink(CelestiaTea)); + + +OnBossDeath: + // Give every party member in map a reward + partytimer("008-2", 200, "Second Dungeon Master::OnReward", getcharid(1)); + + // Warp everyone and add timers + warpparty("008-1", 176, 20, getcharid(1), "008-2", true); // TODO 008-3 + // While we can't advance, we allow you to redo the dungeons + + /* + // Bonus Monsters + monster("008-3", 104, 238, "Time Bonus", RobinBandit, 1, "Party Master::OnMobTime"); + monster("008-3", 85, 117, "Time Bonus", RobinBandit, 1, "Party Master::OnMobTime"); + + // Five Chests + monster("008-3", 38, 104, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + monster("008-3", 41, 104, "Mysterious Chest", any(BronzeChest, BronzeMimic, EvilChest), 1); + monster("008-3", 44, 104, "Mysterious Chest", any(SilverChest, SilverMimic), 1); + monster("008-3", 47, 104, "Mysterious Chest", any(BronzeChest, BronzeMimic, EvilChest), 1); + monster("008-3", 50, 104, "Mysterious Chest", any(BronzeChest, BronzeMimic), 1); + */ + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/009-1/_import.txt b/npc/009-1/_import.txt new file mode 100644 index 0000000..36577f2 --- /dev/null +++ b/npc/009-1/_import.txt @@ -0,0 +1,14 @@ +// Map 009-1: Halinarzo +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/009-1/_mobs.txt", +"npc/009-1/_warps.txt", +"npc/009-1/charles.txt", +"npc/009-1/estate.txt", +"npc/009-1/fisherman.txt", +"npc/009-1/foxhound.txt", +"npc/009-1/guards.txt", +"npc/009-1/lynnthetraveler.txt", +"npc/009-1/mapflags.txt", +"npc/009-1/soul-menhir.txt", +"npc/009-1/teleporter.txt", +"npc/009-1/town.txt", diff --git a/npc/009-1/_mobs.txt b/npc/009-1/_mobs.txt new file mode 100644 index 0000000..7c0859b --- /dev/null +++ b/npc/009-1/_mobs.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 009-1: Halinarzo mobs +009-1,76,35,49,10 monster Giant Maggot 1031,6,30000,90000 +009-1,27,29,7,8 monster Piou 1002,1,60000,60000 +009-1,73,51,49,14 monster Fire Goblin 1067,12,45000,45000,Fisherman::OnKillFireGoblin +009-1,70,98,47,6 monster Scorpion 1071,3,30000,20000 +009-1,70,83,47,6 monster Red Scorpion 1072,1,30000,20000 +009-1,27,96,7,9 monster Duck 1029,2,60000,60000 +009-1,0,0,0,0 monster Desert Maggot 1083,45,35000,150000 +009-1,76,105,1,0 monster Chagashroom Field 1128,1,70000,60000 +009-1,113,88,0,0 monster Chagashroom Field 1128,1,60000,60000 diff --git a/npc/009-1/_warps.txt b/npc/009-1/_warps.txt new file mode 100644 index 0000000..82d16fb --- /dev/null +++ b/npc/009-1/_warps.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 009-1: Halinarzo warps +009-1,56,109,0 warp #009-1_56_109 2,0,010-2,169,20 +009-1,119,40,0 warp #009-1_119_40 0,0,011-1,177,177 +009-1,84,63,0 warp #009-1_84_63 0,0,009-2,62,66 +009-1,73,63,0 warp #009-1_73_63 0,0,009-2,28,65 +009-1,86,34,0 warp #009-1_86_34 0,0,009-3,44,56 +009-1,33,62,0 warp #009-1_33_62 0,0,009-4,48,53 +009-1,64,38,0 warp #009-1_64_38 1,0,009-5,32,41 diff --git a/npc/009-1/charles.txt b/npc/009-1/charles.txt new file mode 100644 index 0000000..d3cdf3e --- /dev/null +++ b/npc/009-1/charles.txt @@ -0,0 +1,199 @@ +// TMW-2 Script +// Original Creator: Adson Renato <adson25@gmail.com> +// Date: 2010-11-19 2:31 AM BRT +// Review: +// * 2013-09-03: ernando2000 <ernando.quirino@hotmail.com> +// * 2013-09-04: Lunovox Heavenfinder <rui.gravata@gmail.com> +// * 2016-06-14: Jesusalva <tmwbr@jesussalva.com> +// * 2017-06-17: Jesusalva <cpntb1@ymail.com> +// * 2018-05-24: Jesusalva <admin@tmw2.org> +// * Adapted from TMW-BR to TMW2 +// Minimum Req.: LVL 30 +// Consumables: Bandits drops: CoinBag, Dagger, Leather Shield, Empty Bottle +// Prize: 1 BurglarMask +// Description: +// A contrabandist which feeds Halinarzo's black market. +// Will later teach the Steal skill. (maybe) +// Notes: +// Blame Saulc. + + + + +009-1,24,105,0 script Charles, Trader King NPC_ORC,{ + .@q=getq(HalinarzoQuest_TraderKing); + if (BaseLevel < 30) goto L_Weak; + if (.@q == 1) goto L_Return; + if (.@q == 2) goto L_End; + goto L_Start; + +L_Weak: + mesn; + mesq lg("Get out of here, weakling. You'll be killed by bandits."); + close; + +L_Start: + mesn; + mes lg(":> \"Hello, youngling...\""); + next; + mesn strcharinfo(0); + menu + l("This character person is strange I better get away..."), L_Distrust, + l("Hmm, who are you?") , L_WhoAmI; + +L_WhoAmI: + mes ""; + mesn; + mesq l("I am the Trader King, because I supply a market!"); // the Black Market + close; + +L_Distrust: + mes ""; + mesn strcharinfo(0); + mes l("¬.¬ \"Hmm, to me, you look like a thief or bandit...\""); + next; + mesn; + mes l(":D \"HAHAHAHAH! Me?! a thief? only during free time...\""); + next; + mesn strcharinfo(0); + mes l("¬.¬ \"I knew it! I'll report you at once.\""); + next; + mesn; + mes l("0.0 \"No no, please no! I can propose you a great deal for your silence!\""); + next; + menu + l("Which deal?"), L_Deal, + l("No, justice must be done.") , L_Die; + +L_Deal: + mes ""; + mesn; + mesq l(":/ \"Hmm... let me see... Aha, I know! I can make you a great bandit with a simple burglar mask!\""); + next; + mesn strcharinfo(0); + mes l(":o \"What? I'm not a bandit! But it looks SO COOL! Do you need anything while you're here?\""); + next; + mesn; + mes l(":D \"Ah! Yes, that would help a lot! I don't need anything a bandit couldn't give you: 25 @@, 5 @@, 2 @@ and a @@. And 6000 GP.\"", getitemlink(CoinBag), getitemlink(EmptyBottle), getitemlink(Dagger), getitemlink(LeatherShield)); + mesq l("What do you say?"); + next; + mesn strcharinfo(0); + menu + l("No way! That's absurd!"), L_Close, + l("That's a lot, but we have a deal!"), L_Accept; + +L_Accept: + mes ""; + mesn; + mes l(">:| \"Go take the items and remember: Not even a single word about me!\""); + next; + mesn strcharinfo(0); + mes l(";-) \"Don't worry! Leave to me!\""); + next; + mesn; + mes l("Here's what I need:"); + mes l("@@/25 @@", countitem(CoinBag), getitemlink(CoinBag)); + mes l("@@/5 @@", countitem(EmptyBottle), getitemlink(EmptyBottle)); + mes l("@@/2 @@", countitem(Dagger), getitemlink(Dagger)); + mes l("@@/1 @@", countitem(LeatherShield), getitemlink(LeatherShield)); + mes l("@@/6000 GP", Zeny); + + setq HalinarzoQuest_TraderKing, 1; + close; + +L_Return: + mesn; + mes "\"Do you have what I asked for?\""; + next; + menu + l("Yes, of course."), L_Finish, + l("I forgot what you need."), L_Review, + l("Not yet, I'll be right back."), L_Wait; + +L_Wait: + mes ""; + mesn; + mesq l("I'll be waiting for you! And trading some items of questionable origins..."); + close; + +L_Finish: + if (countitem(CoinBag) < 25 || + countitem(EmptyBottle) < 5 || + countitem(Dagger) < 2 || + countitem(LeatherShield) < 1) + goto L_Missing; + if (Zeny < 6000) goto L_Poor; + + inventoryplace BurglarMask, 1; + delitem CoinBag, 25; + delitem EmptyBottle, 5; + delitem Dagger, 2; + delitem LeatherShield, 1; + Zeny = Zeny - 6000; + getitem BurglarMask, 1; // Bragging Rights + getexp 5840, 25; + setq HalinarzoQuest_TraderKing, 2; + mes ""; + mesn; + mes l(":D \"Excellent! You've kept your end on the bargain!\""); + next; + mesn; + mesq l("I always keep my end on the bargain, so here you go."); + next; + mesn; + mes l(":) \"A pleasure to trade if you. Don't get caught if you're doing something wrong!\""); + close; + +L_Poor: + mes ""; + mesn; + mes l(":< \"Hey hey! Where's the money?\""); + close; + +L_Missing: + percentheal -15, 0; + mes ""; + mesn; + mes l(":< \"Never lie to me. Keep your end on the bargain! Give me everything I asked for!\""); + close; + +L_End: + mesn; + mes l(":P \"I am busy, leave me alone.\""); + close; + +L_Review: + mes ""; + mesn; + mes l("Here's what I need:"); + mes l("@@/30 @@", countitem(CoinBag), getitemlink(CoinBag)); + mes l("@@/5 @@", countitem(EmptyBottle), getitemlink(EmptyBottle)); + mes l("@@/2 @@", countitem(Dagger), getitemlink(Dagger)); + mes l("@@/1 @@", countitem(LeatherShield), getitemlink(LeatherShield)); + mes l("@@/6000 GP", Zeny); + close; + +L_Die: + mesn; + mesq l("Then I'll be sure you can't talk again."); + die(); + close; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, BurglarMask); + setunitdata(.@npcId, UDT_HEADMIDDLE, Chainmail); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LeatherBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 1); + + .sex = G_MALE; + .distance = 5; + end; + + +} diff --git a/npc/009-1/estate.txt b/npc/009-1/estate.txt new file mode 100644 index 0000000..f530de1 --- /dev/null +++ b/npc/009-1/estate.txt @@ -0,0 +1,171 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System + +// ID: 4 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +009-1,42,48,0 script Sign#RES_0096 NPC_SWORDS_SIGN,{ + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + if (is_admin() && $@GM_OVERRIDE) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + mesc l("Press the doorbell?"); + next; + if (askyesno() == ASK_YES) + doevent "Doorbell#RES_0096::OnDoorbell"; + close; + +L_RentAvailable: + realestate_rent(.id, .price); + close; + +L_Manage: + realestate_manage(.id, (.price*7/10)); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=4; // Estate ID + .price=20000; // Monthly rent price + end; + +} + +// Door entrance +009-1,43,47,0 script #RES_0096 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3) || $ESTATE_PASSWORD$[.id] == "") + goto L_Warp; + + mesc l("The door is locked"); + next; + mesc l("However, it can be unlocked if you know the password:"); + if (is_gm()) mesc l("You can use super password \"mouboo\" to unlock the door."), 1; + input .@password$; + // GMs can use super password "mouboo" + if (.@password$ == $ESTATE_PASSWORD$[.id] || (is_gm() && .@password$ == "mouboo")) + goto L_Warp; + close; + +L_Warp: + warp "009-6", 33, 33; + closeclientdialog; + close; + +L_RentAvailable: + dispbottom l("This estate is available for rent, talk to the sign to rent it."); + close; + +OnInit: + // Estate Settings + .id=4; // Estate ID + end; + +} + +////////////////////////////////////////////////////////////////////////////// +// ID: 5 + +// The sign is the main controller +009-1,108,46,0 script Sign#RES_0097 NPC_SWORDS_SIGN,{ + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + if (is_admin() && $@GM_OVERRIDE) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + mesc l("Press the doorbell?"); + next; + if (askyesno() == ASK_YES) + doevent "Doorbell#RES_0097::OnDoorbell"; + close; + +L_RentAvailable: + realestate_rent(.id, .price); + close; + +L_Manage: + realestate_manage(.id, (.price*7/10)); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=5; // Estate ID + .price=20000; // Monthly rent price + end; + +} + +// Door entrance +009-1,109,45,0 script #RES_0097 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3) || $ESTATE_PASSWORD$[.id] == "") + goto L_Warp; + + mesc l("The door is locked"); + next; + mesc l("However, it can be unlocked if you know the password:"); + if (is_gm()) mesc l("You can use super password \"mouboo\" to unlock the door."), 1; + input .@password$; + // GMs can use super password "mouboo" + if (.@password$ == $ESTATE_PASSWORD$[.id] || (is_gm() && .@password$ == "mouboo")) + goto L_Warp; + close; + +L_Warp: + warp "009-7", 33, 33; + closeclientdialog; + close; + +L_RentAvailable: + dispbottom l("This estate is available for rent, talk to the sign to rent it."); + close; + +OnInit: + // Estate Settings + .id=5; // Estate ID + end; + +} + diff --git a/npc/009-1/fisherman.txt b/npc/009-1/fisherman.txt new file mode 100644 index 0000000..4caa86b --- /dev/null +++ b/npc/009-1/fisherman.txt @@ -0,0 +1,153 @@ +// TMW-2 Script +// Originals: TMW BR +// +// Author: +// Jesusalva +// Description: +// Forest Bow Quest. DO NOT RENAME THE NPC. + +009-1,116,106,0 script Fisherman NPC_FISHERMAN_HALI,{ + .@q=getq(HurnscaldQuest_ForestBow); + if (BaseLevel < 30) goto L_Weak; + mesn; + mesq l("Hello. Ah, bad luck. I am three days without fishing a single carp."); + next; + mesn strcharinfo(0); + menu + l("Any fishing tips for me?"),-, + rif(.@q == 1 && getq3(HurnscaldQuest_ForestBow) < 99, l("I need a string, but not just any string, a really good string.")), L_String; + + mes ""; + mesn; + mesq l("Well, yes. Look this river. The water is not moving."); + next; + mesn; + mesq l("If the water doesn't moves, you will never fish anything. Ah, fat luck..."); + close; + +L_Weak: + mesn; + mesq l("..."); + next; + mesn; + mesq l("I am trying to fish here without success. You're weak, do not bother me."); + close; + +L_String: + .@k=getq3(HurnscaldQuest_ForestBow); + mes ""; + switch (.@k) { + case 0: + mesn; + mesq l("Well, that's easy to do. For starters, I need 1.000 GP as payment, and 10 @@ to make the string.", getitemlink(Root)); + next; + mesn; + mesq l("That's not everything, though. I am having a bad time fishing. Bring me a @@. I know, it is rare. Good luck with it.", getitemlink(GrassCarp)); + next; + mesn; + mesq l("And one last thing I'll want. See these @@? I don't like them, so I'll have you to kill 40 of them for me.", getmonsterlink(FireGoblin)); + next; + mesn strcharinfo(0); + mes l("o.o \"You're being unreasonable. That's way too much!\""); + next; + mesn; + mesq l("To make the string you want, I need a special oil, which I make myself. It's hard to do, so I'll spend time I could be using to fish or help the city guard."); + next; + mesn; + mesq l("I'll make your life easier, I'm also accepting 20 @@ or @@ instead the Grass Carp. Go now, pal.", getitemlink(CommonCarp), getitemlink(Roach)); + setq3 HurnscaldQuest_ForestBow, 10; + close; + break; + case 50: + mesn; + mesq l("Yo, good joob killing the Fire Goblins. Do you have everything I've asked for?"); + next; + goto L_Finish; + default: + mesn; + mesq l("You have:"); + if (countitem(GrassCarp) > 0) + mes l("@@/1 @@", countitem(GrassCarp), getitemlink(GrassCarp)); + else if (countitem(Roach) > countitem(CommonCarp)) + mes l("@@/20 @@ (or @@)", countitem(Roach), getitemlink(Roach), getitemlink(CommonCarp)); + else + mes l("@@/20 @@ (or @@)", countitem(CommonCarp), getitemlink(CommonCarp), getitemlink(Roach)); + + mes l("@@/10 @@", countitem(Root), getitemlink(Root)); + mes l("@@/40 slayed @@", .@k-10, getmonsterlink(FireGoblin)); + mes l("@@/1000 GP", Zeny); + close; + } + close; + +L_Finish: + mesq l("You need:"); + if (countitem(GrassCarp) > 0) + mes l("@@/1 @@", countitem(GrassCarp), getitemlink(GrassCarp)); + else if (countitem(Roach) > countitem(CommonCarp)) + mes l("@@/20 @@ (or @@)", countitem(Roach), getitemlink(Roach), getitemlink(CommonCarp)); + else + mes l("@@/20 @@ (or @@)", countitem(CommonCarp), getitemlink(CommonCarp), getitemlink(Roach)); + + mes l("@@/10 @@", countitem(Root), getitemlink(Root)); + mes l("40/40 slayed @@", getmonsterlink(FireGoblin)); + mes l("@@/1000 GP", Zeny); + mes ""; + select + l("Not yet."), + rif(countitem(GrassCarp) >= 1, l("Yes, I'll pay with Grass Carp.")), + rif(countitem(CommonCarp) >= 20, l("Yes, I'll pay with Common Carp.")), + rif(countitem(Roach) >= 20, l("Yes, I'll pay with Roach.")); + if (@menu == 1) + close; + + if (countitem(Root) < 10 || Zeny < 1000) { + mesn; + mesq l("Well, you forgot the most important: The material I need to do the string."); + next; + mesn; + mesq l("I am not a half-word man, so I won't take your fish. Come back with the money and the Roots, and I'll make the string for you."); + close; + } + + switch (@menu) { + case 2: + delitem GrassCarp, 1; break; + case 3: + delitem CommonCarp, 20; break; + case 4: + delitem Roach, 20; break; + default: + dispbottom l("Script Error: \"Tux didn't found his fish! Blame Saulc at once!\""); close; end; + } + delitem Root, 10; + Zeny=Zeny-1000; + setq3 HurnscaldQuest_ForestBow, 99; + getexp 800, 0; // 10% of max exp + mes ""; + mesn; + mesc l("With a knife, the fisherman makes the roots as thin as he can. You wonder if he knows what he is doing."); + next; + mesn; + mesc l("Once the roots are of a desired thickness, he ties them to make a string, and applies an oil on it."); + next; + mesn; + mesc l("After waiting for the oil to dry, he hands you the string."); + mesq l("Here, thanks to the oil, this string is very sturdy. You better not keep it on your inventory. Good luck!"); + close; + +OnKillFireGoblin: + if (getq(HurnscaldQuest_ForestBow) == 1 && getq3(HurnscaldQuest_ForestBow) >= 10 && getq3(HurnscaldQuest_ForestBow) < 50) { + setq3 HurnscaldQuest_ForestBow, getq3(HurnscaldQuest_ForestBow)+1; + dispbottom l("@@/40 Fire Goblins killed", getq3(HurnscaldQuest_ForestBow)-10); + } + fix_mobkill(FireGoblin); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; + +} + diff --git a/npc/009-1/foxhound.txt b/npc/009-1/foxhound.txt new file mode 100644 index 0000000..fa6040d --- /dev/null +++ b/npc/009-1/foxhound.txt @@ -0,0 +1,197 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Begs for food. Lots of it. And in the end, an armor piece awaits you. +// Enliven Reva Foxhound is an anagram of Lunovox Heavenfinder + +009-1,89,57,0 script Enliven Reva Foxhound NPC_IVAN,{ + .@q=getq(HalinarzoQuest_Foxhound); + if (BaseLevel < .@q+30) { + mesn; + mesq l("Ah, living in Halinarzo is so difficult..."); + next; + mesn; + mesq l("If there were strong people to give us food, ah, how that would be good..."); + next; + mesn; + mesq l("Sorry for making you listen my rambles. I need someone strong, you see..."); + close; + } + mesn; + mesq l("Please traveler, help my starving family!"); + next; + mesn; + mesq l("We from Halinarzo are very poor, and in constant need of food. You have to help us!"); + mes ""; + menu + l("I'll help you."), -, + l("Sorry, I am so hungry as you."), L_Close; + + switch (.@q) { + + case 0: + mesn; + mesq l("There's a long time we don't eat anything. Bring us @@! Five should do it.", getitemlink(Bread)); + select + rif(countitem(Bread) >= 5, l("I have it right here. You can take it!")), + l("I'm sorry... I can't help you either..."); + if (@menu == 1) { + delitem Bread, 5; + getexp 20, 0; + Zeny=Zeny+25*5; + mes ""; + mesn; + mesq lg("Thank you! Thank you! Savior!"); + setq(HalinarzoQuest_Foxhound, 1); + next; + } + break; + + + case 1: + mesn; + mesq l("Bread is fine, but it would taste better with @@. Could you bring us three?", getitemlink(Cheese)); + select + rif(countitem(Cheese) >= 3, l("I have it right here. You can take it!")), + l("I'm sorry... I can't help you either..."); + if (@menu == 1) { + delitem Cheese, 3; + getexp 25, 0; + Zeny=Zeny+25*3; + mes ""; + mesn; + mesq lg("Thank you! Thank you! Savior!"); + setq(HalinarzoQuest_Foxhound, .@q+1); + next; + } + break; + + case 2: + mesn; + mesq l("Do you know @@? It's a poisonous food you can't find around here. It can be made edible with special prepare.", getitemlink(PinkBlobime)); + select + rif(countitem(PinkBlobime) >= 1, l("I have it right here. You can take it!")), + l("I'm sorry... I can't help you either..."); + if (@menu == 1) { + delitem PinkBlobime, 1; + getexp 30, 0; + Zeny=Zeny+10*1; + mes ""; + mesn; + mesq lg("Thank you! Thank you! Savior!"); + setq(HalinarzoQuest_Foxhound, .@q+1); + next; + } + break; + + case 3: + mesn; + mesq l("I saw Purple the other day... Ah, now I want to eat @@... A dozen should do...", getitemlink(Plushroom)); + select + rif(countitem(Plushroom) >= 12, l("I have it right here. You can take it!")), + l("I'm sorry... I can't help you either..."); + if (@menu == 1) { + delitem Plushroom, 12; + getexp 35, 0; + Zeny=Zeny+15*12; + mes ""; + mesn; + mesq lg("Thank you! Thank you! Savior!"); + setq(HalinarzoQuest_Foxhound, .@q+1); + next; + } + break; + + case 4: + mesn; + mesq l("There exist more food than you know. But this time I don't want anything extravagant, just 6 @@ should be fine.", getitemlink(CaveSnakeEgg)); + select + rif(countitem(CaveSnakeEgg) >= 6, l("I have it right here. You can take it!")), + l("I'm sorry... I can't help you either..."); + if (@menu == 1) { + delitem CaveSnakeEgg, 6; + getexp 40, 0; + Zeny=Zeny+27*6; + mes ""; + mesn; + mesq lg("Thank you! Thank you! Savior!"); + setq(HalinarzoQuest_Foxhound, .@q+1); + next; + } + break; + + case 5: + mesn; + mes l("Thanks for feeding us earlier. I wanted to give a great dinner to my family, even if just once."); + mes l("Could you please give us a full meal, with 5 @@, 5 @@, 5@@, 5 @@ and 5 @@?", getitemlink(SnakeEgg), getitemlink(Bread), getitemlink(Cheese), getitemlink(Aquada), getitemlink(Manana)); + select + rif(countitem(SnakeEgg) >= 5 && countitem(Bread) >= 5 && countitem(Cheese) >= 5 && countitem(Aquada) >= 5 && countitem(Manana) >= 5, l("I have it right here. You can take it!")), + l("I'm sorry... I can't help you either..."); + if (@menu == 1) { + mes ""; + mesn; + mesq l("Thanks for attending even this selfish request of mine."); + next; + delitem SnakeEgg, 5; + delitem Bread, 5; + delitem Cheese, 5; + delitem Aquada, 5; + delitem Manana, 5; + getexp 11500, 15; + getitem Bucket, 1; + mesn; + mesq l("This is just a bucket, but I did some cuts on it so you can see though it. It protects your whole face."); + next; + mesn; + mesq l("This is the least I could do for you. Many thanks. And if you have more food, you can keep feeding my family."); + setq(HalinarzoQuest_Foxhound, .@q+1); + setq2(HalinarzoQuest_Foxhound, .@q+1); + next; + } + break; + + default: + .@q=getq2(HalinarzoQuest_Foxhound); + // Manana (Death Penalty) or Aquadas (Hard to get item)? + mesn; + mesq l("I only need @@ @@. Please! You have to help me!", .@q, getitemlink(Manana)); + select + rif(countitem(Manana) >= .@q, l("I have it right here. You can take it!")), + l("I'm sorry... I can't help you either..."); + if (@menu == 1) { + delitem Manana, .@q; + Zeny=Zeny+(60*.@q); + getexp 0, .@q; + mes ""; + mesn; + mesq lg("Thank you! Thank you! Savior!"); + setq(HalinarzoQuest_Foxhound, .@q+1); + next; + } + + } + + goto L_Close; + +L_Close: + closedialog; + goodbye; + close; + +OnInit: + //.@npcId = getnpcid(.name$); + //setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + //setunitdata(.@npcId, UDT_HEADMIDDLE, VneckJumper); + //setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + //setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + //setunitdata(.@npcId, UDT_HAIRSTYLE, 6); + //setunitdata(.@npcId, UDT_HAIRCOLOR, 3); + + .sex = G_MALE; + .distance = 5; + end; + + + +} diff --git a/npc/009-1/guards.txt b/npc/009-1/guards.txt new file mode 100644 index 0000000..43bbcbd --- /dev/null +++ b/npc/009-1/guards.txt @@ -0,0 +1,202 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Protect Halinarzo + +009-1,71,24,0 script Lieutenant Jacob NPC_PLAYER,{ + // The Monster King guild have a special menu + if (strcharinfo(2) == "Monster King") goto L_MKControl; + mesn; + mesq l("Halt! Beyond this gate, is the Great River and the Eternal Swamps."); + next; + mesn; + mesq l("Was not it only flooded constantly, the graveyard is not too far. If you stray away from the path, you'll get lost."); + next; + .@fd=!(getmapmask("011-3")&1024); // .@fd - is flooded? + if (!.@fd && $GAME_STORYLINE >= 1) { + mesn; + // There's a limit on how much info I can send with @@ + mesq l("Various people already went missing, including GMs. This is why if you plan to cross, ") + b(l("You won't be allowed to walk sideways, except to avoid a monster or two.")); + mesc l("If you try to walk west or east too much, you'll hit an \"invisible wall\" to prevent you from getting lost."); + next; + select + l("I'm fine, thanks."), + l("My equipment is good, let me through!"); + mes ""; + if (@menu == 2 && $HURNS_LIBDATE) { + .@palgal$=lg("gal", "pal"); + if (BaseLevel < 20) { + mesn; + mesq l("It might be, but your level isn't. Sorry @@. No going to Hurnscald before level 20.", .@palgal$); + close; + } + if (reputation("Halin") < 1) { + mesn; + mesq l("It might be, but I never heard of you before. Sorry @@, I can't let just anyone go through. It would be a disservice to see you get stuck and die.", .@palgal$); + close; + } + warp "011-3", 37, 219; + closedialog; + close; + } else if (!$HURNS_LIBDATE) { + mesn; + mesq l("No. Hurnscald is besieged and it would be too dangerous to let you go alone. You should talk to %s, instead.", b("Saulc GM")); + } + } else { + mesn; + mesq l("In fact, it is flooded at the moment. Come back later."); + next; + } + + if (GHQUEST) + GHQ_Assign(Snake, "Halinarzo"); + close; + +L_MKControl: + mesn; + mes l("Oh noes! You've found the Halinarzo control panel!"); + next; + select + l("Abort"), + l("Initiate a siege"); + mes ""; + if (@menu == 2) { + doevent "Lieutenant Jacob::OnStartSiege"; + closedialog; + } + close; + +OnMKSiege: + $@SIEGE_ABORTED = false; +OnStartSiege: + kamibroadcast(col("WARNING! WARNING! Monster Army is moving towards Halinarzo!!",1)); + do_siege("009-1", "010-2", "HALIN", TP_TULIM, .name$, .siegetime); + initnpctimer; + end; + +// Timers +OnTimer5000: + .siegetime+=5; + do_siege("009-1", "010-2", "HALIN", TP_TULIM, .name$, .siegetime); + switch (.siegetime) { + case 0: + disablenpc "Sawis"; + break; + // Monster Army arrives in town + case 60: + disablenpc "Fisherman"; + disablenpc "Charles, Trader King"; + disablenpc "Lynn The Traveler"; + disablenpc "Enliven Reva Foxhound"; + disablenpc "Ryan"; + disablenpc "Bella, the Scholar"; + disablenpc "Book Keeper"; + disablenpc "Yumi"; + disablenpc "Halinarzo's Nurse"; + disablenpc "Joaquim"; + disablenpc "Alvasus"; + disablenpc "Luanna"; + break; + // Monster Army deployed in town + case 90: + disablenpc "Dang Rostra"; + disablenpc "Kevin"; + disablenpc "Barzil"; + break; + // Monster army have withdrawn completly + case MK_SIEGE_DURATION: + .siegetime=0; + announce(("Halinarzo siege is over!"), bc_all); + enablenpc "Sawis"; + enablenpc "Fisherman"; + enablenpc "Charles, Trader King"; + enablenpc "Lynn The Traveler"; + enablenpc "Enliven Reva Foxhound"; + enablenpc "Ryan"; + enablenpc "Bella, the Scholar"; + enablenpc "Book Keeper"; + enablenpc "Yumi"; + enablenpc "Halinarzo's Nurse"; + enablenpc "Joaquim"; + enablenpc "Alvasus"; + enablenpc "Luanna"; + enablenpc "Dang Rostra"; + enablenpc "Kevin"; + enablenpc "Barzil"; + stopnpctimer; + end; + break; + } + + // Loop again + initnpctimer; + end; + +OnInit: + .siegetime=0; + .sex = G_MALE; + .distance = 4; + + // Check items.xml for info about this. + .@npcId = getnpcid(); + setunitdata(.@npcId, UDT_HEADTOP, BullHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, LieutenantArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_SHIELD, LousyMoccasins); // TODO FIXME: Display Boots + setunitdata(.@npcId, UDT_WEAPON, Backsword); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 15); + + end; +} + +// Handle Guard's logic +function script HaliGuardHandler { + legiontalk; + + return; +} + +009-1,100,30,0 script Guard Corina NPC_GUARD2,{ + HaliGuardHandler(); + end; + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} + + +009-1,111,48,0 script Guard Jhon NPC_GUARD1,{ + HaliGuardHandler(); + end; + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} + +009-1,41,56,0 script Guard Laurie NPC_GUARD2,{ + HaliGuardHandler(); + end; + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} + + +009-1,62,74,0 script Guard Amy NPC_GUARD2,{ + HaliGuardHandler(); + end; + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} + diff --git a/npc/009-1/lynnthetraveler.txt b/npc/009-1/lynnthetraveler.txt new file mode 100644 index 0000000..58ab3f3 --- /dev/null +++ b/npc/009-1/lynnthetraveler.txt @@ -0,0 +1,93 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// The Travelers travel around the world telling stories. + +009-1,30,26,0 script Lynn The Traveler NPC_F_COINKEEPER,{ + + mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Wow! Are you @@? Everyone, in every city, talks about you!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + + mesq l("Hello. I am @@, and I am from a family of travellers. We travel though the whole world, looking for exotic goods.", .name$); + next; + mesq l("You can buy rare items with me, or I can tell you about different cities in our world."); + +L_Menu: + mes ""; + menu + l("I want to trade with you."), L_Trade, + l("Tell me about Halinarzo."), L_Halin, + l("Tell me about Tulimshar."), L_Tulim, + l("Tell me about Hurnscald."), L_Hurns, + l("Sorry, I'll pass."), L_Close; + +L_Tulim: + mes ""; + mesn; + mesq l("Tulimshar is the oldest human city, and its foundation is the year zero of our calendar."); + next; + mesq l("The city only flourished because Janett Platinum had the idea to build city walls surrounding this city."); + next; + mesq l("The desert climate means you'll find mostly maggots and scorpions. Their drops include cactus drinks, cake, knifes, black pearls, gold, and other common things."); + next; + mesq l("You can find for a good price desert equipment and some kind of dyes. You find all sort of crafters, artisans and warriors here."); + next; + goto L_Menu; + +L_Hurns: + mes ""; + mesn; + mesq l("Hurnscald was founded after Tulimshar, in more fertile lands. Their walls are not so sturdy as the ones of Tulimshar."); + next; + mesq l("Under the leadership of King Wusher, they were the first to accept immigrants from other races. You will find humans and non-humans there."); + next; + mesq l("The fertile climate is ideal for mushrooms. You can also find lots of wood."); + next; + mesq l("Their economy provide many edible items and potions."); + next; + goto L_Menu; + +L_Halin: + mes ""; + mesn; + mesq l("Halinarzo was founded to explore Mana Stones."); + next; + mesq l("You can find both huge swamps, as huge desertic areas near and on it."); + next; + mesq l("Lizards are the main monster found, and they steal gold from innocent bypassers."); + next; + mesq l("Without any mana stone left, and because the walls were not very strong, most of the city was destroyed."); + next; + mesq l("Unlike many other cities, if you want people in eternal need of items, there is a good place to look."); + next; + goto L_Menu; + + +L_Trade: + mesn; + mesq l("Use your @@ as currency!", getitemlink(StrangeCoin)); + next; + tutmes l("%s is obtained during events, daily logins, heroic deeds, gifts, etc. But cannot be bought with real money.", getitemlink(StrangeCoin)); + openshop "Aeros Trader"; + closedialog; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, UglyChristmasSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 18); + setunitdata(.@npcId, UDT_HAIRCOLOR, 8); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/009-1/mapflags.txt b/npc/009-1/mapflags.txt new file mode 100644 index 0000000..e6436cf --- /dev/null +++ b/npc/009-1/mapflags.txt @@ -0,0 +1,2 @@ +009-1 mapflag town +009-1 mapflag nopenalty diff --git a/npc/009-1/soul-menhir.txt b/npc/009-1/soul-menhir.txt new file mode 100644 index 0000000..172f933 --- /dev/null +++ b/npc/009-1/soul-menhir.txt @@ -0,0 +1,20 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Soul Menhir + +009-1,28,29,0 script Soul Menhir#hali NPC_SOUL_MOSS,{ + @map$ = "009-1"; + setarray @Xs, 26, 27, 28, 28, 29, 30; + setarray @Ys, 30, 31, 30, 31, 30, 30; + @x = 0; + @y = 0; + callfunc "SoulMenhir"; + @map$ = ""; + cleararray @Xs[0], 0, getarraysize(@Xs); + cleararray @Ys[0], 0, getarraysize(@Ys); + @x = 0; + @y = 0; + close; +} diff --git a/npc/009-1/teleporter.txt b/npc/009-1/teleporter.txt new file mode 100644 index 0000000..85d85ef --- /dev/null +++ b/npc/009-1/teleporter.txt @@ -0,0 +1,23 @@ +// TMW2 Script +// Authors: +// Jesusalva +// Description: +// Link portals to soul menhirs like the teleporters from old +// The price is temporary. This feature got in because no ship in Nivalis Port +// PS. Anise => “Aisen” Anagram + + +009-1,113,89,0 script #WarpGateHalin NPC_NO_SPRITE,1,0,{ + end; + +OnTouch: + TeleporterGate(TP_HALIN); + close; + + +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + diff --git a/npc/009-1/town.txt b/npc/009-1/town.txt new file mode 100644 index 0000000..bef1fa1 --- /dev/null +++ b/npc/009-1/town.txt @@ -0,0 +1,11 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Reset LOCATION$ when entering a town + +009-1,71,24,0 script #LocHalin NPC_HIDDEN,4,1,{ +OnTouch: + EnterTown("Halin"); end; +} +009-1,55,72,0 duplicate(#LocHalin) #LocHalinB NPC_HIDDEN,3,1 diff --git a/npc/009-2/_import.txt b/npc/009-2/_import.txt new file mode 100644 index 0000000..2d0238f --- /dev/null +++ b/npc/009-2/_import.txt @@ -0,0 +1,7 @@ +// Map 009-2: Halinarzo Library & Bar +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/009-2/_warps.txt", +"npc/009-2/librarian.txt", +"npc/009-2/politics.txt", +"npc/009-2/ryan.txt", +"npc/009-2/scholar.txt", diff --git a/npc/009-2/_warps.txt b/npc/009-2/_warps.txt new file mode 100644 index 0000000..df08e8b --- /dev/null +++ b/npc/009-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 009-2: Halinarzo Library & Bar warps +009-2,28,66,0 warp #009-2_28_66 0,0,009-1,73,64 +009-2,62,67,0 warp #009-2_62_67 0,0,009-1,84,64 diff --git a/npc/009-2/librarian.txt b/npc/009-2/librarian.txt new file mode 100644 index 0000000..b08a71e --- /dev/null +++ b/npc/009-2/librarian.txt @@ -0,0 +1,200 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Main Storyline +// NPC_SAMANTHA + +009-2,67,60,2 script Book Keeper NPC_FEMALE,{ + .@n = getq(General_Narrator); + if (.@n == 9) mesn; + if (.@n == 9) mesc l("Anyway, I think you should go to Hurnscald Townhall again, and speak with the mayor."); + if (.@n == 9) next; + if (.@n >= 9) goto L_Complete; + if (.@n == 8) goto L_Report; + if (.@n == 6 || .@n == 7) goto L_Advance; + if (.@n < 6 && .@n >= 2) goto L_Horses; + mesn; + mesq l("Hello. I am the book keeper, responsible for keeping Halinarzo History."); + // TODO: Import data from BR-002-5 (Royal Library) and make available story about TMW-BR + close; + +L_Horses: + mesn; + mesq l("Hello. I am the book keeper, responsible for keeping Halinarzo History."); + next; + select + l("Cool, thank you."), + l("I was born here but I had amnesia."); + mes ""; + if (@menu == 1) + close; + mesn; + mesq l("Please prove your statement."); + next; + mesc l("You realize that you have no means to prove your statement."); + mesc l("Saying that you were sent here would prove nothing."); + mesc l("How do you even prove amnesia, anyway?"); + next; + mesn; + mesq l("You are just a random stranger trying to steal adventurer data, aren't you?? %%e"); + next; + mesn; + mesq l("I bet you'll now ask for %s's password, and scam all their money!!", strcharinfo(0)); + next; + mesn; + mesq l("Get out of here! Before I call the city guard!!"); + close; + +L_Advance: + .@nt = getq2(General_Narrator); + // Initial Value is 0. + // .@nt tracks future value + if (!.@nt || .@n == 6) + goto L_Prologue; + + // If you wait 7 - instead of 2 - hours, you are abusing. + // So now, you must wait an extra hour. + if (.@nt > santime()+(60*60*7)) { + setq2 General_Narrator, santime()+(60*60); + .@nt=santime()+(60*60); + } + + // You got magic, so, why waiting so long. Chop half hour. + // Should be plenty to instantly finish. + if (MAGIC_LVL && !@halinskip) { + .@nt-=1800; + setq2 General_Narrator, .@nt; + @halinskip=true; + } + + // Wait time is over! + if (.@nt <= santime()) + goto L_Complete; + + mesn; + mesq l("Have you found out magic already? It should not be possible, but the girl reading the book says it is. I don't know in what to believe."); + next; + mesn; + mesq l("Anyway, I still need @@. You have plenty of time to do things at your own pace.", FuzzyTime(.@nt)); + tutmes l("Maybe you should go to the depleted mines in the small cave inside the town. Who knows what you may find inside."); + // Rule #99999999: Never tell players to grind for 3 hours you lazy dev + //mesq l("Please help us with the bandits on the Canyon, and meanwhile, I'll seek the records for you. Just @@ more.", FuzzyTime(.@nt)); + close; + +L_Prologue: + mesn strcharinfo(0); + mesq l("Hi! Apparently, I came from here and moved at the age of 4, but I had amnesia and can't remember!"); + next; + mesn strcharinfo(0); + mesq l("It would be very kind of you, if you could seek who I am!"); + next; + mesn; + mesq l("What are you talking about? You are who you are right now. There's no other you."); + mesc l("Unless, of course, if Saulc cloned you. But then we must blame Saulc!"); + next; + mesn; + mesq l("Ah... I see. You are a lost soul, without parents, lost on the world with only some basic stuff."); + next; + mesn; + mesq l("Alright, I'll look in the archives. I'll have an answer for you in @@. Meanwhile, why don't you suppress the bandits on the cliff?", l("45 minutes")); + // Please wait 45 minutes + setq General_Narrator, 7, santime()+(60*45); + next; + mesn; + mesq l("Also, Halinarzo is famous for the depleted mana mines in the town. You probably won't find a mana stone there, but it might be cool to look."); + next; + mesn; + mesq l("Thinking a bit... Yes, you definitely should go to the mines. They have been infested by slimes and people sometimes worry they'll leave and attack the town, but you should be capable of suppressing them."); + close; + +L_Complete: + mesn; + mesq l("So, I looked the records. There was nothing really useful."); + mesq lg("Like, I know you are female.", "Like, I know you are male."); + next; + mesn; + mesq l("But, I found something really interesting! Ah... I hope you like history?"); + next; + mesn l("HISTORY BOOK"); + mesc l("In these dark times, countless souls moved away from Halinarzo. All hope seemed lost."); + mesc l("But then, Andrei Sakar appeared. Hope was not lost. We could still defend it."); + next; + mesn l("HISTORY BOOK"); + mesc l("Many sacrifices were done, but it was lost."); + mesc l("Even the mighty Andrei Sakar could only barely escape alive. The Monster King was too strong."); + mesc l("All hope was lost. We failed to protect it. We lost everyone who challenged the Monster King. Killed without mercy."); + next; + mesn l("HISTORY BOOK"); + mesc l("The Monster King was so powerful! It is impossible to recover the artifact now, and everyone will die!"); + mesc l("A few families, also known as the Ancient Families of the Soul Menhir (for whatever reasons that may be), departed."); + next; + mesn l("HISTORY BOOK"); + mesc l("They promised to travel to the ##BWorld Edge##b to fetch a Mana Fragment. The wisest of them said it was the only chance."); + mesc l("They were never again seen. None of them. We burnt all records about everyone here, and blamed the Monster King."); + mesc l("They must never know we had it. Their sacrifices must be forgotten. For the sake of all."); + compareandsetq General_Narrator, 7, 8; + close; + +L_Report: + mesn; + mesq l("Do you want to read the story again? Or should we get to the most obvious point?"); + next; + select + l("I want to know what you found out again"), + l("I want to know why you shown me that."); + if (@menu == 1) + goto L_Complete; + mesn; + mesq l("We have three vanishing sort of people on the story."); + next; + mesn; + mes l("1- The coward families, who ran away when outlook was bad."); + mes l("2- The defenders of the artifact, who lost their lives and left people behind."); + mes l("3- The ancient families."); + next; + mesn; + mesq l("You probably was only 4 when all that happened. And opposed to official story, the Monster King never enter this building."); + next; + mesn; + mesq l("In other words: If Lua couldn't find your record, your parents were part of this story! But, on which of these three groups were them?"); + next; + mesn; + mes l("Well, if you were from the defenders, you would have been adopted by a family which left Halinarzo."); + mesq l("And if your family, real or adoptive, was coward, there would be no lead at all."); + next; + mesn; + mesq l("So, let's work with the last proposal, that you're from an ancient family. We can backtrace what they did."); + next; + mesn; + mesq l("Their first stop was Hurnscald, so please go to Hurnscald Townhall, and speak with the mayor, he can help you getting there. You can't reach there normally, after all."); + next; + inventoryplace ScholarshipBadge, 1; + mesn; + mesq l("Good luck, @@!", strcharinfo(0)); + mesc l("@@ hands you an @@.", .name$, getitemlink(ScholarshipBadge)); + next; + getexp BaseLevel*750, JobLevel*50;// Reference Levels: (40, 15) + getitem ScholarshipBadge, 1; + mesc b(l(".:: Main Quest 4-1 ::.")), 3; + mesc l("* Talk to Librarian in Halinarzo"), 2; + mesc l("* Return to Hurnscald Townhall"), 9; + setq General_Narrator, 9; + getvaultexp(10); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShirt); + //setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, JeansShorts); + setunitdata(.@npcId, UDT_HAIRSTYLE, any(8,11,20)); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + npcsit; + + .sex=G_FEMALE; + .distance=5; + end; +} + diff --git a/npc/009-2/politics.txt b/npc/009-2/politics.txt new file mode 100644 index 0000000..490de70 --- /dev/null +++ b/npc/009-2/politics.txt @@ -0,0 +1,58 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Town Administrator file, see npc/functions/politics.txt +// User variables: +// #POL_APPLYWEEK = Week of last application +// #POL_VOTEDAY = Day of last vote + +009-2,66,63,0 script Halinarzo Office NPC_POLITICS,{ +do +{ + mesc ".:: "+l("Halinarzo Townhall")+" ::.", 2; + mesc l("Current Town Administrator: ")+$HALIN_MAYOR$, 3; + POL_TownInfo("HALIN"); + mesc l("Application fee: @@ GP", .applytax); + next; + select + l("Information"), + rif(strcharinfo(0) == $HALIN_MAYOR$, l("Manage Town")), + rif(#POL_APPLYWEEK != gettimeparam(GETTIME_WEEKDAY), l("Apply for the office!")), + l("View Candidate List and cast a vote"), + l("[Quit]"); + + switch (@menu) { + case 1: + POL_Information(); + break; + case 2: + POL_Manage("HALIN"); + break; + case 3: + // array_push might be too sensible for getd/setd + if (Zeny < .applytax) + break; + Zeny-=.applytax; + $HALIN_MONEY+=.applytax; + #POL_APPLYWEEK=gettimeparam(GETTIME_WEEKDAY); + array_push($HALIN_CANDIDATE$, strcharinfo(0)); + array_push($HALIN_VOTES, 0); + mesc l("Application successful!"), 3; + next; + break; + case 4: + POL_Candidate("HALIN"); + break; + default: + close; + } +} while (true); +end; + +OnInit: + .applytax=100; + .distance=4; + end; +} + diff --git a/npc/009-2/ryan.txt b/npc/009-2/ryan.txt new file mode 100644 index 0000000..db5c196 --- /dev/null +++ b/npc/009-2/ryan.txt @@ -0,0 +1,172 @@ +// TMW-2 Script. +// Author: +// Saulc +// Jesusalva +// Notes: +// Buys Tonori Delight + +009-2,29,57,0 script Ryan NPC_PLAYER,{ + if (BaseLevel < 17) goto L_Weak; + if (!getq(HalinarzoQuest_LifeDelight)) goto L_Prologue; + .@q=getq2(HalinarzoQuest_LifeDelight); + if (.@q < 25) + .@a=(.@q**2)+18; + else if (.@q < 40) + .@a=625+18*(.@q-24); + else if (.@q < 50) + .@a=895+10*(.@q-39); + else + .@a=1000+(.@q*(.@q < 75 ? 1 : 2)); + + if (.@q > 100) goto L_Complete; + mesn; + mesq l("Ah, I just wanted @@... Yummy, lovely @@...", getitemlink(TonoriDelight), getitemlink(TonoriDelight)); + next; + mesn; + mesq l("If you bring me @@ @@, I'll give you a reward!", .@a, getitemlink(TonoriDelight)); + next; + select + rif(countitem(TonoriDelight) >= .@a, l("I have everything!")), + l("Ah, not now..."); + mes ""; + if (@menu == 2) + close; + + inventoryplace ChefHat, 1; + inventoryplace PlatinumIngot, 1, IridiumIngot, 2; + + // Generic rewards: XP Boost (.@l) and GP boost (.p) + .@l=BaseLevel+JobLevel; + + // Main reward + delitem TonoriDelight, .@a; + getexp rand(.@a, .@a*2)+.@q*rand(10,30), .@l; + Zeny=Zeny+.@a*.p; + setq2 HalinarzoQuest_LifeDelight, .@q+1; + mesn; + mesq l("Hmmm... Yummy!"); + + // Landmark rewards + // Chef hat last stage: 120 Delights - 583 in total + .@r=getq(HalinarzoQuest_LifeDelight); + .@q=getq2(HalinarzoQuest_LifeDelight); + if (.@r < 2 && .@q >= 11) { + setq1 HalinarzoQuest_LifeDelight, 2; + getitem ChefHat, 1; + getexp 13500, 10; + next; + mesn; + mesq l("Ah, you really deserve this @@. Good job!", getitemlink(ChefHat)); + } + + // Bonus stage + if (.@q == 20) + getitem Coffee, 1; + + if (.@q >= 21) { + Zeny+=rand2(1,.@a)*.p; + getexp rand2(1,.@q)*.@l, rand2(1, .@q); + Mobpt+=.@q; // From now on, Monster Points will be generated + } + + // Bonus stage + if (.@q == 30) + getitem Manapple, 1; + + // Bonus stage + if (.@q == 40) + getitem LoFWarpCrystal, 1; + + // Elixir Of Life last stage: ~995 Delights - 26,385 in total + if (.@r < 3 && .@q >= 51) { + setq1 HalinarzoQuest_LifeDelight, 3; + getitem ElixirOfLife, 1; + getitem MylarinDust, 1; + getexp 30000, 5000; + next; + mesn; + mesq l("Hey, take this @@. It heals fully but who cares, I have Tonori Delight!", getitemlink(ElixirOfLife)); + } + + if (.@q >= 51) { + Zeny+=rand2(.@q,.@a)*.p; + getexp rand2(1,.@a)*.@l, rand2(1, .@q); + } + + // Bonus stage + if (.@q == 60) + getitem SacredLifePotion, 1; + + // Bonus stage + if (.@q == 70) + getitem MercBoxDD, 1; + + // Bonus stage + if (.@q == 80) + getitem DivineApple, 1; + + // Bonus stage + if (.@q == 90) + getitem MysteriousFruit, 1; + + if (.@q >= 91) { + Zeny+=rand2(.@a, .@a+.@q)*.p; + getexp rand2(.@q,.@a)*.@l, rand2(1, .@q); + } + // Platinum + Iridium last stage: ~1290 Delights - 86,885 in total + if (.@r < 4 && .@q == 100) { + setq1 HalinarzoQuest_LifeDelight, 4; + getitem SunnyCrystal, 1; + getitem PlatinumIngot, 1; + getitem IridiumIngot, 2; + getitem EquipmentBlueprintE, 2; + getitem AlchemyBlueprintE, 2; + Mobpt+=1000000; // 1 Million Monster Points + getexp 500000, 50000; // Reference: Lv 74 / Lv 38 + next; + mesn; + mesq l("Ah, I have enough @@ for a life! Take this EXTREMELY RARE @@. Good job!", getitemlink(TonoriDelight), getitemlink(SunnyCrystal)); + mesc l("Got Master Blueprints!"); + mesc l("Got 1,000,000 Monster Points!"); + } + + close; + +L_Complete: + mesn; + mesq l("Thanks for the help! Yummy!"); + close; + +L_Weak: + mesn; + mesq l("How did you even got here? There are dangerous swamps north, and dangerous deserts south..."); + close; + +L_Prologue: + setq HalinarzoQuest_LifeDelight, 1, 0; + mesn; + mesq l("Ah, I just wanted @@... Yummy, lovely @@...", getitemlink(TonoriDelight), getitemlink(TonoriDelight)); + next; + mesn; + mesq l("There's a big prize in the end... Could you bring me some later?"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, ChefHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_WEAPON, Boots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 2); + + .sex = G_MALE; + .distance = 4; + .p=97; // Price in GP of each Tonori Delight. + // real price is 556 in ingredients (this gets a x2 sell price) + // + 120 GP of taxes + // Total: 676 GP per batch (5~9 units). We consider the average batch (7). + // NO EXTRA PROFIT IS REQUIRED/ALLOWED. + end; +} + diff --git a/npc/009-2/scholar.txt b/npc/009-2/scholar.txt new file mode 100644 index 0000000..c1d9a82 --- /dev/null +++ b/npc/009-2/scholar.txt @@ -0,0 +1,89 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Encourages players to get magic and keep the game interesting +// HalinarzoQuest_MageEquipment +// 0 - Haven't started +// 1 - Listened to intro about mage equipments. +// 2 - NYI: A quest to obtain more powerful magic equipment. + +009-2,51,54,0 script Bella, the Scholar NPC_BELLA,{ + .@q=getq(HalinarzoQuest_MageEquipment); + mesn; + mesq l("I am Bella, the Scholar."); + if (!mstone(MAGIC_LVL)) goto L_NotEnough; + mesq l("Out of my experience, you are ready to learn magic."); + next; + mesn; + mesq l("A pity that without being born with magic, you need a Mana Stone... And there are none left..."); + mesc l("*sigh*"); + if (.@q < 1) + goto L_Intro; + if (.@q && getq(General_Narrator) == 7) + goto L_NeedHelp; + close; + +L_NotEnough: + if (BaseLevel < $MANA_BLVL+MAGIC_LVL*rand(15,17)) { + mesq l("Out of my experience, your level may not be enough."); + } + if (JobLevel < $MANA_JLVL+MAGIC_LVL*rand(10,12)) { + mesq l("Out of my experience, your job may not be enough."); + } + if (readparam2(bInt) < $MANA_BINT+MAGIC_LVL*rand(10,13)) { + mesq l("Out of my experience, your base intelligence may not be enough."); + if (!.@q) + mesc l("You should reset your stats and allocate some points at intelligence."); + } + if (.@q < 1) + goto L_Intro; + close; + +L_Intro: + next; + mesn; + mesq l("I'll only say you this once. Mages can, of course, go to battlefield in heavy armor and stuff."); + next; + mesn; + mesq l("But if a mage wants to be a warrior, they won't be able to use their full magic skills."); + next; + mesn; + mesq l("Mages should use equipment designed for them. Those may be lacking on defense, but will raise MP and Magic Attack."); + mesq l("I must, however, remember that the most powerful magical equipment ##B may require intelligence ##b to use."); + next; + mesn; + mesq l("Without magic attack, using a fireball at an enemy will be like throwing a stone."); + mesq l("And without proper MP-raising equipment, you'll be exausthed after your first fireball."); + next; + mesn; + mesq l("Mage equipment also raises MP recovery. So yes, a mage can use a sword and heavy armor, but..."); + mesq l("...If they decide to do so, they'll have to deal with weak magical attacks, and with quick mana depletion."); + next; + inventoryplace TrainingWand, 1; + mesn; + mesq l("Thanks for listening to my rambles. I'll gift you a @@. Of course, having a wand won't give you magic...", getitemlink(TrainingWand)); + mesq l("...You need to learn magic before this equipment being useful to you in any way."); + setq HalinarzoQuest_MageEquipment, 1; + getitem TrainingWand, 1; + close; + +L_NeedHelp: + next; + mesn; + mesq l("Did you knew some crazy adventurers insists there is a Mana Stone on the dangerous caves inside the town?"); + next; + mesn; + mesq l("I mean, that's absurd! If there was one, it would have been taken already. Unless if grew some sentience or something."); + next; + mesn; + mesq l("Anyway, if you ever feel bored and wanna see what I mean by yourself, just leave the building and head west, then north. There's a small entrance there."); + tutmes col(l("WARNING: "),1)+l("Magic is necessary for warriors and archers to learn skills. Skills can be learned in Tulimshar."), l("Magic, Warriors and Archers"); + close; + +OnInit: + .sex=G_FEMALE; + .distance=5; + end; +} diff --git a/npc/009-3/_import.txt b/npc/009-3/_import.txt new file mode 100644 index 0000000..632f4c5 --- /dev/null +++ b/npc/009-3/_import.txt @@ -0,0 +1,6 @@ +// Map 009-3: Halinarzo General Store +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/009-3/_warps.txt", +"npc/009-3/barzil.txt", +"npc/009-3/dangrostra.txt", +"npc/009-3/kevin.txt", diff --git a/npc/009-3/_warps.txt b/npc/009-3/_warps.txt new file mode 100644 index 0000000..1577a7a --- /dev/null +++ b/npc/009-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 009-3: Halinarzo General Store warps +009-3,44,57,0 warp #009-3_44_57 0,0,009-1,86,35 diff --git a/npc/009-3/barzil.txt b/npc/009-3/barzil.txt new file mode 100644 index 0000000..d4462bd --- /dev/null +++ b/npc/009-3/barzil.txt @@ -0,0 +1,28 @@ +// TMW-2 Script. +// Author: +// Saulc +// Jesusalva +// Description: +// Banker. Also used on Tier 2 Quest. + +009-3,29,38,0 script Barzil NPC_LLOYD,{ + if (ST_TIER == 2 && gettimetick(2) < QUEST_ELEVARTEMPO) goto L_Tier2; + Banker(.name$, "Halinarzo", 8900); + close; + +L_Tier2: + mesn strcharinfo(0); + mesq l("I need for the Magic Academy a @@, fast!", getitemlink(SunnyCrystal)); + next; + mesn; + mesq l("Of course! Quick, take it!!"); + inventoryplace SunnyCrystal, 1; + ST_TIER=3; + getitem SunnyCrystal, 1; + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/009-3/dangrostra.txt b/npc/009-3/dangrostra.txt new file mode 100644 index 0000000..665cf63 --- /dev/null +++ b/npc/009-3/dangrostra.txt @@ -0,0 +1,85 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Dang Rostra is the seller of Halinarzo (and usually sells overpriced stuff) +// Dang Rostra is an anagram of DragonStar, winner of Easter 2018 + +009-3,26,47,0 script Dang Rostra NPC_PLAYER,{ + + mesn; + mesq l("Halinarzo people are very poor, but we have our resources..."); + mes ""; + menu + l("Trade"), -, + l("Leave"), L_Close; + + closedialog; + npcshopattach(.name$); + shop .name$; + close; + +L_Close: + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, VneckJumper); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 6); + setunitdata(.@npcId, UDT_HAIRCOLOR, 3); + + .sex = G_MALE; + .distance = 5; + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + // Bows (rare) + sellitem ShortBow, 10200, 1; + sellitem WoodenBow, 6800, 1; + // Misc (uncommon) + sellitem SharpKnife, -1, 1; + sellitem EmptyBottle, -1, 1; // You can buy some empty bottles here, but they're scarse + // Arrows (common) + sellitem Arrow, -1, 200; + sellitem IronArrow, -1, 90; + sellitem ArrowAmmoBox, -1, 2; + sellitem IronAmmoBox, -1, 1; + + // Speciality Drinks (common) + sellitem Coffee, -1, 3; + end; + +OnClock0001: + restoreshopitem ShortBow, 10200, 1; +OnClock1200: + restoreshopitem WoodenBow, 6800, 1; +OnClock0400: +OnClock1600: + restoreshopitem SharpKnife, 1; + restoreshopitem EmptyBottle, 1; +OnClock0800: +OnClock2000: + restoreshopitem Arrow, 200; + restoreshopitem IronArrow, 90; + restoreshopitem ArrowAmmoBox, 2; + restoreshopitem IronAmmoBox, 1; + restoreshopitem Coffee, 3; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} diff --git a/npc/009-3/kevin.txt b/npc/009-3/kevin.txt new file mode 100644 index 0000000..923ac1b --- /dev/null +++ b/npc/009-3/kevin.txt @@ -0,0 +1,169 @@ +// TMW2 Script +// Author: +// Saulc +// Vasily_Makarov (original from Evol) +// Jesusalva +// Dye Quest added by: +// Povo +// Description: +// Status Reset, mixed gem powder, dye + +009-3,39,48,0 script Kevin NPC_PLAYER,{ + + speech S_LAST_NEXT, + l("I am @@, an alchemist specialized in reset potions.", .name$); + +L_Menu: + .@plush_count = BaseLevel*210-(9*210); + // Lv 10: 210 GP + // Lv 90: 1.890 GP + if (BaseLevel > 10) + .@plush_count = .@plush_count/(BaseLevel/10); + + select + l("Can you reset my stats please?"), + l("Can you mix Gem Powder?"), + l("Do you make anything else?"), + lg("You are weird, I have to go sorry."); + + switch (@menu) + { + case 1: + goto L_ResetStats; + case 2: + goto L_Powder; + case 3: + goto L_Dye; + case 4: + goto L_Quit; + } + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + ConfirmStatusReset(); + goto L_Quit; + +L_Powder: + .@price=POL_AdjustPrice(800); + mes ""; + mesn; + mesq l("To make @@ I need one from each gem powders, and @@ GP for commission.", getitemlink(GemPowder), .@price); + next; + select + l("Yeah, I need one."), + l("Thanks for the help, but no."), + l("Actually, nevermind. Good bye!"); + if (@menu == 2) + goto L_Menu; + + if (@menu == 3) + goto L_Quit; + + if ( + countitem(DiamondPowder) && + countitem(RubyPowder) && + countitem(EmeraldPowder) && + countitem(SapphirePowder) && + countitem(TopazPowder) && + countitem(AmethystPowder) && + Zeny >= .@price) { + + inventoryplace GemPowder, 1; + delitem DiamondPowder, 1; + delitem RubyPowder, 1; + delitem EmeraldPowder, 1; + delitem SapphirePowder, 1; + delitem TopazPowder, 1; + delitem AmethystPowder, 1; + POL_PlayerMoney(.@price); + getitem GemPowder, 1; + getexp rand2(6,18), rand2(6,18); + + mesn; + mesq l("Thanks! Here you go. Perhaps you need another one?"); + next; + goto L_Powder; + } else { + mesn; + mesq l("Sorry, but I need one of each gem powder, and %d GP.", .@price); + next; + } + goto L_Menu; + +L_Dye: + mesn; + mesq l("Actually, using a byproduct of my other works and a few additional ingredients, I could make a %s", + getitemlink(PinkDye)); + if (BaseLevel < 25) { + mesq l("Hmm...actually, nevermind."); + next; + mesn; + mesq l("I cannot in good faith ask someone with so little experience to risk their life for something so trivial."); + next; + mesn; + mesq l("Come back when you are stronger."); + next; + goto L_Menu; + } + next; + mesq l("Bring me %d %s, %d %s, and %d %s and I can make it for you... oh and it will be %d GP for my work.", + 1, getitemlink(BottleOfTonoriWater), 20, getitemlink(PinkBlobime), 30, getitemlink(Plushroom), 500); + if (getq(HalinarzoQuest_Kevin) < 1) + setq HalinarzoQuest_Kevin, 1; + next; + select + l("Yeah, I need one."), + l("Thanks for the help, but no."), + l("Actually, nevermind. Good bye!"); + mes ""; + if (@menu == 2) + goto L_Menu; + + if (@menu == 3) + goto L_Quit; + + if (countitem(BottleOfTonoriWater) >= 1 && + countitem(PinkBlobime) >= 20 && + countitem(Plushroom) >= 30 && Zeny >= 500) { + inventoryplace PinkDye, 1, EmptyBottle, 1; + delitem BottleOfTonoriWater, 1; + delitem PinkBlobime, 20; + delitem Plushroom, 30; + Zeny=Zeny-500; + getitem PinkDye, 1; + getitem EmptyBottle, 1; + if (getq(HalinarzoQuest_Kevin) == 1) { + setq HalinarzoQuest_Kevin, 2; + getexp 300, 0; + } + mesn; + mesq l("Thanks! Here you go. Perhaps you want another one?"); + next; + goto L_Dye; + } else { + mesn; + mesq l("Sorry, you don't seem to have everything I need."); + } + close; + +L_Quit: + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/009-4/_import.txt b/npc/009-4/_import.txt new file mode 100644 index 0000000..25d6ae1 --- /dev/null +++ b/npc/009-4/_import.txt @@ -0,0 +1,5 @@ +// Map 009-4: Halinarzo Hospital +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/009-4/_warps.txt", +"npc/009-4/nurse.txt", +"npc/009-4/yumi.txt", diff --git a/npc/009-4/_warps.txt b/npc/009-4/_warps.txt new file mode 100644 index 0000000..247d1db --- /dev/null +++ b/npc/009-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 009-4: Halinarzo Hospital warps +009-4,48,54,0 warp #009-4_48_54 0,0,009-1,33,63 diff --git a/npc/009-4/nurse.txt b/npc/009-4/nurse.txt new file mode 100644 index 0000000..3f6e1eb --- /dev/null +++ b/npc/009-4/nurse.txt @@ -0,0 +1,23 @@ +// TMW-2 Script +// Author: +// Jesusalva + +009-4,39,46,0 script Halinarzo's Nurse NPC_FEMALE,{ + Nurse(.name$, 10, 5); + close; + +OnInit: + .@npcId = getnpcid(.name$); + // I am too lazy to dress every NPC I add >.< + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, MiniSkirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, ShortTankTop); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 9); + + .sex = G_FEMALE; + .distance = 5; + end; + +} + diff --git a/npc/009-4/yumi.txt b/npc/009-4/yumi.txt new file mode 100644 index 0000000..151e26c --- /dev/null +++ b/npc/009-4/yumi.txt @@ -0,0 +1,60 @@ +// TMW2 Script +// Author: Jesusalva +// Description: +// Joaquim's wife. +// TODO sprite on the bed + +009-4,36,33,0 script Yumi NPC_FEMALE,{ + .@q=getq(HalinarzoQuest_SickWife); + if (.@q > 3) goto L_PostAid; + if (.@q == 3) goto L_DoIt; + if (BaseLevel > 36) goto L_PreAid; + + mesn; + mesq l("Hello. Take care with the Mountain Snakes, they're highly poisonous!"); + close; + +L_PreAid: + mesn; + mesq l("Please... Speak low... I am dying........"); + next; + mesn; + mesq l("The nurse... The nurse is... Doing all she can... To help me, though."); + close; + +L_PostAid: + mesn; + mesq l("Ah, I am lively again. I wish I could just raise from this bed and do some exercise, but the Nurse refuses to let me go."); + // There could be another quest stage her + close; + +L_DoIt: + if (countitem(ElixirOfLife) < 1) + goto L_PreAid; + + inventoryplace ElixirOfLife, 1; + delitem ElixirOfLife, 1; + getexp 1000, 0; + setq HalinarzoQuest_SickWife, 4; + mesc l("*gulp* *gulp* *gulp*"); + next; + mesn; + mesq l("AAAAAAAHHHHH, Thanks, I am lively again!"); + next; + mesn; + mesq l("In fact, I am cured! Hooray!!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, VneckJumper); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, CottonGloves); // Do not use boots when lying on the bed + setunitdata(.@npcId, UDT_HAIRSTYLE, 10); + setunitdata(.@npcId, UDT_HAIRCOLOR, 15); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/009-5/_import.txt b/npc/009-5/_import.txt new file mode 100644 index 0000000..b875296 --- /dev/null +++ b/npc/009-5/_import.txt @@ -0,0 +1,6 @@ +// Map 009-5: Halinarzo Church +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/009-5/_warps.txt", +"npc/009-5/alvasus.txt", +"npc/009-5/joaquim.txt", +"npc/009-5/luanna.txt", diff --git a/npc/009-5/_warps.txt b/npc/009-5/_warps.txt new file mode 100644 index 0000000..ee7350c --- /dev/null +++ b/npc/009-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 009-5: Halinarzo Church warps +009-5,33,42,0 warp #009-5_33_42 1,0,009-1,63,39 diff --git a/npc/009-5/alvasus.txt b/npc/009-5/alvasus.txt new file mode 100644 index 0000000..e2f60b6 --- /dev/null +++ b/npc/009-5/alvasus.txt @@ -0,0 +1,193 @@ +// TMW2 Script +// Author: +// Pookie +// Saulc +// Jesusalva +// Description: +// Alvasus is a believer, he is from Halinarzo and he collects Roasted Maggot +// for Church Party. +// Quest for level 19 - 5 roasted maggot for Alvasus Pendant +// Notes: +// There is no church on Tulimshar. If he wanted to spread faith or whatever +// he would fit, but as he wants a party, I had to move him to Halinarzo. +// I turned him on a weekly quest. Sorry, I got excited u.u +// +// First time: Alvasus Pendant +// Later times: GP and EXP +// +// Q1: Current State +// Q2: Current Reward +// Q3: Current Week Number (for repeat) + +009-5,29,39,0 script Alvasus NPC_PLAYER,{ + if (!getq(HalinarzoQuest_Alvasus)) { + npctalk3 l("Uhm? Oh, a new believer! Welcome, child of the mana, to the only church in this world."); + setq HalinarzoQuest_Alvasus, 1, 0, atoi(gettimestr("%U", 2)); + } + + .@q3 = getq3(HalinarzoQuest_Alvasus); + .@q2 = getq2(HalinarzoQuest_Alvasus); + if (.@q3 != atoi(gettimestr("%U", 2))) + setq HalinarzoQuest_Alvasus, 1, .@q2, atoi(gettimestr("%U", 2)); + + if (gettime(GETTIME_WEEKDAY) == SUNDAY) goto L_Sunday; + .@q = getq(HalinarzoQuest_Alvasus); + if (BaseLevel < 19) goto L_TooWeak; + if (.@q == 1) goto L_GiveTask; + if (.@q == 2) goto L_Check; + if (.@q == 3) goto L_Complete; + + closedialog; + goodbye; + close; + +L_TooWeak: + mesn; + mesq l("Go away, kid! Let me pray in peace!"); + close; + +L_Sunday: + mesn; + mesq l("Ah, Sunday. What better time to go to church?"); + next; + if (!@alvasus && .@q == 2) goto L_Party; + mesn; + mesq l("We will have a party today, but only believers are allowed in!"); + close; + +L_Quit: + mes ""; + mesn; + mesq l("Alright."); + close; + +L_Party: + mesn; + mesq l("Please pray a bit with us!"); + if (askyesno() != ASK_YES) + close; + mes ""; + mesc "..."; + next; + mesc "..."; + next; + mesc "..."; + next; + mesc "..."; + next; + mesc "..."; + next; + mesn; + mesq l("Alright, PARTY TIME!"); + @alvasus=any(1,1,1,1,2,3,4,5); + @min = 65*@alvasus; + @max = 175*@alvasus; + @delay = 3*@alvasus; + @type = 1; + doevent "rand_sc_heal::OnUse"; + close; + + +L_GiveTask: + mesn; + mesq lg("Hello Adventurer!"); + next; + mesn; + mesq l("Would you like to help me with organizing a Church Party?"); + next; + + menu + l("Sure, why not?"), L_Quest, + l("I don't have time for that."), L_Quit; + + +L_Quest: + mes ""; + mesn; + mesq l("Sunday I am having a Church Party but I forgot to prepare food!"); + next; + mesn; + mesq l("Can you help me find some @@?", getitemlink(RoastedMaggot)); + next; + + menu + l("Okay!"), L_Start, + l("I don't have the time!"), L_Quit; + + +L_Start: + setq1 HalinarzoQuest_Alvasus, 2; + mes ""; + mesn; + mesq l("Ok, let's see how many you have and how many I need:"); + goto L_List; + +L_List: + mes ""; + mesn; + mes l("I will need:"); + mes l("@@/5 @@", countitem(RoastedMaggot), getitemlink(RoastedMaggot)); + next; + goto L_Check; + +L_Check: + mesn; + mesq l("Do you have my @@?", getitemlink(RoastedMaggot)); + next; + menu + l("Yes!"), L_Give, + l("How many did you need again?"), L_List, + l("No!"), L_Quit; + +L_Give: + if ( + countitem(RoastedMaggot) < 5 + ) goto L_NoItem; + + + // First time you get the Pendant + .@q2 = getq2(HalinarzoQuest_Alvasus); + if (.@q2 == 0) { + inventoryplace AlvasusPendant, 1; + getitem(AlvasusPendant, 1); + } + + delitem(RoastedMaggot, 5); + + getexp(700+.@q2, 1+.@q2); + Zeny=Zeny+(.@q2*5); + + setq1(HalinarzoQuest_Alvasus, 3); + setq2 HalinarzoQuest_Alvasus, .@q2+1; + setq3 HalinarzoQuest_Alvasus, atoi(gettimestr("%U", 2)); + + mes ""; + mesn; + mesq l("Thanks for all of your help!"); + close; + +L_Complete: + mesn; + mesq l("Now, to wait for Sunday is the most boring part... Alas, I wonder if I'll remember next week, too."); + //mesq l("Do you know where the party is? It's at Halin, to get there you need to pass through the Desert Canyon."); + close; + +L_NoItem: + mesn; + mesq l("Thanks for the kindness, but you don't have enough. I need 5!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, DesertHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + setunitdata(.@npcId, UDT_WEAPON, CreasedBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 6); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .sex = G_MALE; + .distance = 4; + + end; +} + diff --git a/npc/009-5/joaquim.txt b/npc/009-5/joaquim.txt new file mode 100644 index 0000000..4028fd5 --- /dev/null +++ b/npc/009-5/joaquim.txt @@ -0,0 +1,187 @@ +// TMW2 Script, ported from TMW-BR +// TMW2 Author: Jesusalva +// +// Creator: Cardinalli +// Review: Lunovox <rui.gravata@gmail.com> +// +// Description: +// A rather easy quest to give players what to kill. +// Reward: +// Mouboo pendant + 20% exp of level 55 + +009-5,36,35,4 script Joaquim NPC_PLAYER,{ + .@q=getq(HalinarzoQuest_SickWife); + if (BaseLevel < 36) goto L_TooWeak; + if (BaseLevel < 55) goto L_Weak; + if (.@q == 5) goto L_Complete; + if (.@q == 4) goto L_Finish; + if (.@q == 3) goto L_DoIt; + if (.@q == 2) goto L_Return; + if (.@q == 1) goto L_Found; + goto L_Start; + +L_TooWeak: + mesn; + mesq l("Here is a safe haven for we who don't fight. There are no monsters, and the Mouboo watches over us."); + close; + +L_Weak: + mesn; + .@r=rand2(1,5); + switch (.@r) { + case 1: + mesq l("Watch out! My wife was gravely wounded the other day. Don't forget your shield when leaving this holy place!"); + break; + case 2: + mesq l("Ah, I hate mushrooms. Perhaps in future, I could use their spikes and mushies."); + break; + case 3: + mesq l("Ah, I hate snakes. Perhaps in future, I could use their tongues."); + break; + case 4: + mesq l("Ah, I love mouboos. But their steaks, hmm. Ah, no, I shouldn't eat that..."); + break; + case 5: + mesq l("You should never sell your Cactus Drinks. They have many uses."); + break; + } + close; + +L_Complete: + mesn; + mesq l("Thanks for helping my wife, I'll be forever grateful."); + close; + +L_Finish: + inventoryplace ElixirOfLife, 1, MoubooPendant, 1; + getitem ElixirOfLife, 1; + getitem MoubooPendant, 1; + getexp 39260, 75; + setq HalinarzoQuest_SickWife, 5; + mesn; + mesq l("Take this spare @@ I did. It heals fully and instantly, so don't hesit to use it if you're about to die.", getitemlink(ElixirOfLife)); + next; + mesn; + mesq l("Thanks for helping my wife! Here is, an @@. May the Mouboo watch over you! o.o", getitemlink(MoubooPendant)); + close; + +L_DoIt: + mesn; + mesq l("Please help my wife Yumi, on the Hospital!"); + close; + +// Quest Core +L_Start: + mesn; + mesq l("Ohhhhh..... Please, help me!!! My wife is gravely wounded!!"); + next; + mesn strcharinfo(0); + mesq l("Calm down! How can I help you?"); + next; + mesn; + mesq l("My grandmother gave me a recipe of the @@, it can cure anything but death.", getitemlink(ElixirOfLife)); + next; + mesn; + mesq l("I don't remember what I need now, but if you give me a moment, I'll get the list."); + setq HalinarzoQuest_SickWife, 1; + close; + +L_Found: + mesn; + mesq l("I can make an @@, I still have a bottle of fairy blood, a few mana pearls, and some other rare ingredients.", getitemlink(ElixirOfLife)); + mesq l("It is the non-rare ingredients I actually need help with!"); + next; + mesn l("@@ Recipe", getitemlink(ElixirOfLife)); + mesc l("@@/100 @@", countitem(CactusDrink), getitemlink(CactusDrink)); + mesc l("@@/60 @@", countitem(HardSpike), getitemlink(HardSpike)); + mesc l("@@/45 @@", countitem(SmallMushroom), getitemlink(SmallMushroom)); + mesc l("@@/40 @@", countitem(SnakeTongue), getitemlink(SnakeTongue)); + mesc l("@@/30 @@", countitem(BottleOfTonoriWater), getitemlink(BottleOfTonoriWater)); + mesc l("@@/20 @@", countitem(CaveSnakeTongue), getitemlink(CaveSnakeTongue)); + mesc l("@@/15 @@", countitem(MoubooSteak), getitemlink(MoubooSteak)); + next; + select + l("I will do it, don't worry."), + rif(countitem(ElixirOfLife), l("I have one here...")), + l("Ahh, too many items. Sorry."); + mes ""; + mesn; + if (@menu == 1) { + mes lg("Thanks! Thanks! Savior! Hurry up!"); + setq HalinarzoQuest_SickWife, 2; + } else if (@menu == 2) { + mes l("Uhm, sorry, I don't trust stuff you get at market. You know."); + mes l("Full of agrotoxins, transgenics and whatever. Not safe."); + } else { + mes l("Oh noes, who nows can help my wife? Please reconsider!"); + } + close; + +L_Return: + mesn l("@@ Recipe", getitemlink(ElixirOfLife)); + mesc l("@@/100 @@", countitem(CactusDrink), getitemlink(CactusDrink)); + mesc l("@@/60 @@", countitem(HardSpike), getitemlink(HardSpike)); + mesc l("@@/45 @@", countitem(SmallMushroom), getitemlink(SmallMushroom)); + mesc l("@@/40 @@", countitem(SnakeTongue), getitemlink(SnakeTongue)); + mesc l("@@/30 @@", countitem(BottleOfTonoriWater), getitemlink(BottleOfTonoriWater)); + mesc l("@@/20 @@", countitem(CaveSnakeTongue), getitemlink(CaveSnakeTongue)); + mesc l("@@/15 @@", countitem(MoubooSteak), getitemlink(MoubooSteak)); + next; + select + l("I'll be back later with all ingredients."), + l("They're with me."); + mes ""; + if (@menu == 1) + close; + + if (countitem(CactusDrink) < 100 || + countitem(HardSpike) < 60 || + countitem(SmallMushroom) < 45 || + countitem(SnakeTongue) < 40 || + countitem(BottleOfTonoriWater) < 30 || + countitem(CaveSnakeTongue) < 20 || + countitem(MoubooSteak) < 15) + goto L_Missing; + + inventoryplace ElixirOfLife, 1; + + delitem CactusDrink, 100; + delitem HardSpike, 60; + delitem SmallMushroom, 45; + delitem SnakeTongue, 40; + delitem BottleOfTonoriWater, 30; + delitem CaveSnakeTongue, 20; + delitem MoubooSteak, 15; + getitem ElixirOfLife, 1; + setq HalinarzoQuest_SickWife, 3; + mesn; + mesq l("Thanks, I'll just bake the Elixir right away...!"); + next; + mesc l("@@ goes away for a while and returns briefly.", .name$); + next; + mesn; + mesq l("Here, take the Elixir. Please, bring it to my wife! I am counting on you!!"); + close; + +L_Missing: + mesn strcharinfo(0); + mesq l("Except they're not. I'll be back later."); + next; + mesn; + mesq l("Please, @@! Hurry up!", strcharinfo(0)); + close; + +OnInit: + .@npcId = getnpcid(.name$); + //setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, VneckJumper); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 3); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + npcsit; + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/009-5/luanna.txt b/npc/009-5/luanna.txt new file mode 100644 index 0000000..c2b570b --- /dev/null +++ b/npc/009-5/luanna.txt @@ -0,0 +1,30 @@ +// TMW2 Script +// Author: +// Jesusalva, 4144 +// Description: +// Luanna is responsible for marriage. Subject to change stuff later. + +009-5,32,29,0 script Luanna NPC_FEMALE,{ + marriagemain(); + close; + +OnTimer30000: + marriagecheck(); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, VneckJumper); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 10); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + + .sex = G_FEMALE; + .distance = 5; + initnpctimer; + + end; + +} diff --git a/npc/009-6/_import.txt b/npc/009-6/_import.txt new file mode 100644 index 0000000..ceb575e --- /dev/null +++ b/npc/009-6/_import.txt @@ -0,0 +1,5 @@ +// Map 009-6: Real Estate +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/009-6/_warps.txt", +"npc/009-6/doorbell.txt", +"npc/009-6/utils.txt", diff --git a/npc/009-6/_warps.txt b/npc/009-6/_warps.txt new file mode 100644 index 0000000..b489496 --- /dev/null +++ b/npc/009-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 009-6: Real Estate warps +009-6,34,34,0 warp #009-6_34_34 1,0,009-1,43,48 diff --git a/npc/009-6/doorbell.txt b/npc/009-6/doorbell.txt new file mode 100644 index 0000000..2f67968 --- /dev/null +++ b/npc/009-6/doorbell.txt @@ -0,0 +1,329 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Doorbell allows you to purchase mobilia, besides loading it when server starts +// Each layer can have 32 different furniture pieces because bitmask limit. +// This file is custom to every room + +// ID: 4 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +009-6,32,34,0 script Doorbell#RES_0096 NPC_NO_SPRITE,{ + // Name, Layer, Price, ID, x1, y1, x2, y2, + function create_object { + array_push(.nams$, getarg(0)); + array_push(.layer, getarg(1)); + array_push(.price, getarg(2)); + array_push(.objid, getarg(3)); + array_push(.x1, getarg(4)); + array_push(.y1, getarg(5)); + array_push(.x2, getarg(6)); + array_push(.y2, getarg(7)); + return; + } + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + close; + +// When using setcells() a player could get trapped! +// This label will slide the player back to entrance, which should be a safe spot +OnSlide: + slide 33, 33; + end; + +// If someone press the doorbell from outside and doorbell is enabled +OnDoorbell: + if ($ESTATE_DOORBELL[.id]) + end; + + if (.dpost < gettimetick(2)) { + npctalk (strcharinfo(0)+" is pressing the doorbell."); // We actually don't want l() + } + .dpost=gettimetick(2)+.delay; + end; + +// Managment Menu +L_Manage: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Managment Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Rent time available: @@", FuzzyTime($ESTATE_RENTTIME[.id])); + mesc l("Total Credits and GP: @@", format_number(.@gp)); + mes ""; + mesc l("Room password: @@", $ESTATE_PASSWORD$[.id]); + if ($ESTATE_DOORBELL[.id]) + mesc l("Doorbell is disabled"), 1; + + next; + select + l("Leave"), + l("Enable/disable doorbell"), + l("Manage Furniture"), + l("Set room password"); + + switch (@menu) { + case 1: + close; + break; + case 2: + $ESTATE_DOORBELL[.id]=!$ESTATE_DOORBELL[.id]; + break; + case 3: + goto L_Furniture; + break; + case 4: + mesc l("(Leave the password blank to disable)"); + mesc l("Current Room password: @@", $ESTATE_PASSWORD$[.id]); + mesc l("Input new password: "); + input .@password$; + mesc l("Repeat new password: "); + input .@passwordc$; + if (.@password$ == .@passwordc$) { + $ESTATE_PASSWORD$[.id]=.@password$; + mesc l("Password changed with success!"), 3; + } else { + mesc l("The passwords doesn't match."), 1; + } + break; + } + goto L_Manage; + +L_Furniture: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Furniture Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Total Credits and GP: @@", format_number(.@gp)); + + next; + select + l("Finish"), + l("Manage Beds"), + l("Manage Utilities"), + l("Manage Luxury furniture"), + l("Manage Decoration"), + l("Manage Chairs"), + l("Manage Paintings"); + mes ""; + + switch (@menu) { + case 1: + goto L_Manage; + break; + case 2: + mesc ".:: "+ l("Beds") + " ::.", 3; + @re_col=RES_OBJECTS; + break; + case 3: + mesc ".:: "+ l("Utilities") + " ::.", 3; + @re_col=RES_UTILITIES; + break; + case 4: + mesc ".:: "+ l("Luxury furniture") + " ::.", 3; + @re_col=RES_LUXURY; + break; + case 5: + mesc ".:: "+ l("Decoration") + " ::.", 3; + @re_col=RES_DECORATION; + break; + case 6: + mesc ".:: "+ l("Chairs") + " ::.", 3; + @re_col=RES_SITTABLE; + break; + case 7: + mesc ".:: "+ l("Paintings") + " ::.", 3; + @re_col=RES_WALLDECORATION; + break; + } + +// L_ContinuousLoop +// Requires the following variables: +// @re_col +// Target Collision ID +L_ContinuousLoop: + deletearray @valid_ids; + + // Create a second array (@valid_ids) with the ID of objects within @re_col group + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + //debugmes "Found object ID %d named %s on layer %s coords (%d,%d) - Looking for layer %d", .@i, .nams$[.@i], .layer[.@i], .x1[.@i], .y1[.@i], @re_col; + if (.layer[.@i] == @re_col) + array_push(@valid_ids, .@i); + } + //debugmes "Found %d valid objects", getarraysize(@valid_ids); + + // Create the menu with @valid_ids - Check if you already have the item to decide if you're buying or selling + @menuentries$="Finish:"; + for (.@j=0; .@j < getarraysize(@valid_ids); .@j++) { + .@i=@valid_ids[.@j]; + if (realestate_hasmobilia(.id, .layer[.@i], .objid[.@i])) + @menuentries$+=l("Sell ")+.nams$[.@i]+l(" for ") + format_number( realestate_sellprice(.id,.price[.@i]) ) +":"; + else + @menuentries$+=l("Purchase ")+.nams$[.@i]+(" for ") + format_number( .price[.@i] )+":"; + } + select (@menuentries$); + mes ""; + + // First option to return to previous menu + if (@menu == 1) + goto L_Furniture; + + // Otherwise, we know then that (@menu-2) is the ID in @valid_ids + // So we save .@id with the correct ID in object arrays. + // We also calculate how much aggregated money you have. + .@id=@valid_ids[@menu-2]; + .@gp=REAL_ESTATE_CREDITS+Zeny; + + if (realestate_hasmobilia(.id, .layer[.@id], .objid[.@id])) { + // If you have the mobilia, you're selling it for Mobiliary Credits + delcells realestate_cellname(.id, .@id); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0096"); + REAL_ESTATE_CREDITS+=realestate_sellprice(.id,.price[.@i]); + mesc l("Sale successful!"); + next; + } else { + // Else, you're buying it, so we must check if you have the moolah first + .@price=.price[.@id]; + if (.@gp > .@price) { + realestate_payment(.@price); + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + areatimer(.mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], 10, "::OnSlide"); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0096"); + mesc l("Purchase successful!"); + next; + } else { + mesc l("Not enough funds!"); + next; + } + } + + // This loops forever + goto L_ContinuousLoop; + + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=4; // Estate ID + .delay=15; // Forced wait between rings + .dpost=0; // Last doorbell ring + .mapa$="009-6"; + + // Arrays + // We go element by element on the array building the menu + .nams$=""; + .layer=0; + .price=0; + .objid=0; + .x1=0; + .y1=0; + .x2=0; + .y2=0; + + // Furniture Settings + // Name, Collision Layer, Price, ID, x1, y1, x2, y2 + // For Collision Layer, see constants.conf ("Real Estate Collisions") + create_object("Placeholder" ,99,999999,99999, 99, 99, 99, 99); + + create_object("Bed 01" , 5, 5000, 1, 26, 27, 27, 30); + create_object("Bed 02" , 5, 5000, 2, 28, 27, 29, 30); + + create_object("Wardrobe" , 1, 7000, 1, 21, 23, 22, 23); + create_object("Stovetop" , 1, 10000, 2, 28, 23, 29, 24); + create_object("Shelf 01" , 1, 2000, 4, 25, 23, 25, 23); + create_object("Shelf 02" , 1, 2000, 8, 26, 23, 26, 23); + create_object("Shelf 03" , 1, 2000, 16, 27, 23, 27, 23); + create_object("Shelf 04" , 1, 2000, 32, 30, 23, 30, 23); + create_object("Shelf 05" , 1, 2000, 64, 31, 23, 31, 23); + create_object("Shelf 06" , 1, 2000, 128, 32, 23, 32, 23); + create_object("Shelf 07" , 1, 2000, 256, 33, 23, 33, 23); + create_object("Shelf 08" , 1, 2000, 512, 34, 23, 34, 23); + create_object("Shelf 09" , 1, 2000, 1024, 35, 23, 35, 23); + create_object("Shelf 10" , 1, 2000, 2048, 36, 23, 36, 23); + create_object("Shelf 11" , 1, 2000, 4096, 37, 23, 37, 23); + create_object("Shelf 12" , 1, 2000, 8192, 38, 23, 38, 23); + + create_object("Piano" , 3, 10000, 1, 33, 25, 35, 25); + + create_object("Left Desk" , 2, 5000, 1, 20, 25, 22, 27); + create_object("Right Desk" , 2, 5000, 2, 36, 30, 38, 32); + + create_object("Left Chair" , 4, 2000, 1, 21, 28, 21, 28); + create_object("Right Chair" , 4, 2000, 2, 37, 29, 37, 29); + + create_object("Painting 01" , 6, 3000, 1, 21, 20, 21, 20); + create_object("Painting 02" , 6, 3000, 2, 23, 21, 23, 21); + create_object("Painting 03" , 6, 3000, 4, 25, 20, 25, 20); + create_object("Painting 04" , 6, 3000, 8, 28, 21, 28, 21); + create_object("Painting 05" , 6, 3000, 16, 31, 20, 31, 20); + create_object("Painting 06" , 6, 3000, 32, 36, 20, 36, 20); + + // Load Mobilia already existing + //debugmes "[REAL ESTATE] Now loading mobilia"; + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + switch (.layer[.@i]) { + case 1: + if ($ESTATE_MOBILIA_64[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 2: + if ($ESTATE_MOBILIA_4[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 3: + if ($ESTATE_MOBILIA_8[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 4: + if ($ESTATE_MOBILIA_32[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 5: + if ($ESTATE_MOBILIA_128[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 6: + if ($ESTATE_MOBILIA_2[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + default: + // We do nothing by default + //debugmes("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", .@i, .layer[.@i]); + break; + } + } + //debugmes "Found %d valid objects", getarraysize(.valid_ids); + for (.@j=0; .@j < getarraysize(.valid_ids); .@j++) { + .@id=.valid_ids[.@j]; + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + //debugmes "Creating %s in %s", realestate_cellname(.id, .@id), .mapa$; + } + deletearray .valid_ids; + // Load NPCs + donpcevent "NPCs#RES_0096::OnReload"; + end; + +} + + diff --git a/npc/009-6/utils.txt b/npc/009-6/utils.txt new file mode 100644 index 0000000..65ffbb2 --- /dev/null +++ b/npc/009-6/utils.txt @@ -0,0 +1,74 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Utils take care of NPCs - Their code, and enable/disable using check_cell +// This file is custom to every room + +// ID: 4 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller for rent system +// Doorbell is the main controller for indoor +// This is the NPC script controller +009-6,0,0,0 script NPCs#RES_0096 NPC_HIDDEN,{ + // load_npc ( name , map, x , y{, cell} ) + function load_npc { + if (checknpccell(getarg(1), getarg(2), getarg(3), getarg(4, cell_chknopass))) { + enablenpc getarg(0); + //debugmes "ENABLING NPC %s", getarg(0); + } else { + disablenpc getarg(0); + //debugmes "Disabling NPC %s", getarg(0); + } + + /* + debugmes "----- %s (%d,%d) cell report", getarg(1), getarg(2), getarg(3); + debugmes "cell_chknopass: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknopass); + debugmes "cell_chknoreach: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknoreach); + debugmes "cell_chkbasilica: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkbasilica); + debugmes ""; + debugmes "cell_chkwater: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwater); + debugmes "cell_chkwall: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwall); + debugmes "cell_chkcliff: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkcliff); + debugmes "----- Npc Id: %s", getarg(0); + */ + return; + } + end; + +OnInit: + // Estate Settings + .id=4; // Estate ID + .mapa$="009-6"; // Map name + + // NPC Settings + .sex = G_OTHER; + .distance = 3; + end; + +// Load or unload accordingly +OnReload: + //debugmes "[REAL ESTATE] NPC ONRELOAD"; + // load_npc ( name , map, x , y{, cell} ) + load_npc("Wardrobe#RES_0096", .mapa$, 21, 23); + load_npc("Piano#RES_0096" , .mapa$, 34, 25); + end; + +} + + diff --git a/npc/009-7/_import.txt b/npc/009-7/_import.txt new file mode 100644 index 0000000..bd2aad1 --- /dev/null +++ b/npc/009-7/_import.txt @@ -0,0 +1,5 @@ +// Map 009-7: Real Estate +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/009-7/_warps.txt", +"npc/009-7/doorbell.txt", +"npc/009-7/utils.txt", diff --git a/npc/009-7/_warps.txt b/npc/009-7/_warps.txt new file mode 100644 index 0000000..51b82ba --- /dev/null +++ b/npc/009-7/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 009-7: Real Estate warps +009-7,34,34,0 warp #009-7_34_34 1,0,009-1,109,46 diff --git a/npc/009-7/doorbell.txt b/npc/009-7/doorbell.txt new file mode 100644 index 0000000..0898dc3 --- /dev/null +++ b/npc/009-7/doorbell.txt @@ -0,0 +1,329 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Doorbell allows you to purchase mobilia, besides loading it when server starts +// Each layer can have 32 different furniture pieces because bitmask limit. +// This file is custom to every room + +// ID: 5 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +009-7,32,34,0 script Doorbell#RES_0097 NPC_NO_SPRITE,{ + // Name, Layer, Price, ID, x1, y1, x2, y2, + function create_object { + array_push(.nams$, getarg(0)); + array_push(.layer, getarg(1)); + array_push(.price, getarg(2)); + array_push(.objid, getarg(3)); + array_push(.x1, getarg(4)); + array_push(.y1, getarg(5)); + array_push(.x2, getarg(6)); + array_push(.y2, getarg(7)); + return; + } + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + close; + +// When using setcells() a player could get trapped! +// This label will slide the player back to entrance, which should be a safe spot +OnSlide: + slide 33, 33; + end; + +// If someone press the doorbell from outside and doorbell is enabled +OnDoorbell: + if ($ESTATE_DOORBELL[.id]) + end; + + if (.dpost < gettimetick(2)) { + npctalk (strcharinfo(0)+" is pressing the doorbell."); // We actually don't want l() + } + .dpost=gettimetick(2)+.delay; + end; + +// Managment Menu +L_Manage: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Managment Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Rent time available: @@", FuzzyTime($ESTATE_RENTTIME[.id])); + mesc l("Total Credits and GP: @@", format_number(.@gp)); + mes ""; + mesc l("Room password: @@", $ESTATE_PASSWORD$[.id]); + if ($ESTATE_DOORBELL[.id]) + mesc l("Doorbell is disabled"), 1; + + next; + select + l("Leave"), + l("Enable/disable doorbell"), + l("Manage Furniture"), + l("Set room password"); + + switch (@menu) { + case 1: + close; + break; + case 2: + $ESTATE_DOORBELL[.id]=!$ESTATE_DOORBELL[.id]; + break; + case 3: + goto L_Furniture; + break; + case 4: + mesc l("(Leave the password blank to disable)"); + mesc l("Current Room password: @@", $ESTATE_PASSWORD$[.id]); + mesc l("Input new password: "); + input .@password$; + mesc l("Repeat new password: "); + input .@passwordc$; + if (.@password$ == .@passwordc$) { + $ESTATE_PASSWORD$[.id]=.@password$; + mesc l("Password changed with success!"), 3; + } else { + mesc l("The passwords doesn't match."), 1; + } + break; + } + goto L_Manage; + +L_Furniture: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Furniture Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Total Credits and GP: @@", format_number(.@gp)); + + next; + select + l("Finish"), + l("Manage Beds"), + l("Manage Utilities"), + l("Manage Luxury furniture"), + l("Manage Decoration"), + l("Manage Chairs"), + l("Manage Paintings"); + mes ""; + + switch (@menu) { + case 1: + goto L_Manage; + break; + case 2: + mesc ".:: "+ l("Beds") + " ::.", 3; + @re_col=RES_OBJECTS; + break; + case 3: + mesc ".:: "+ l("Utilities") + " ::.", 3; + @re_col=RES_UTILITIES; + break; + case 4: + mesc ".:: "+ l("Luxury furniture") + " ::.", 3; + @re_col=RES_LUXURY; + break; + case 5: + mesc ".:: "+ l("Decoration") + " ::.", 3; + @re_col=RES_DECORATION; + break; + case 6: + mesc ".:: "+ l("Chairs") + " ::.", 3; + @re_col=RES_SITTABLE; + break; + case 7: + mesc ".:: "+ l("Paintings") + " ::.", 3; + @re_col=RES_WALLDECORATION; + break; + } + +// L_ContinuousLoop +// Requires the following variables: +// @re_col +// Target Collision ID +L_ContinuousLoop: + deletearray @valid_ids; + + // Create a second array (@valid_ids) with the ID of objects within @re_col group + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + //debugmes "Found object ID %d named %s on layer %s coords (%d,%d) - Looking for layer %d", .@i, .nams$[.@i], .layer[.@i], .x1[.@i], .y1[.@i], @re_col; + if (.layer[.@i] == @re_col) + array_push(@valid_ids, .@i); + } + //debugmes "Found %d valid objects", getarraysize(@valid_ids); + + // Create the menu with @valid_ids - Check if you already have the item to decide if you're buying or selling + @menuentries$="Finish:"; + for (.@j=0; .@j < getarraysize(@valid_ids); .@j++) { + .@i=@valid_ids[.@j]; + if (realestate_hasmobilia(.id, .layer[.@i], .objid[.@i])) + @menuentries$+=l("Sell ")+.nams$[.@i]+l(" for ") + format_number( realestate_sellprice(.id,.price[.@i]) ) +":"; + else + @menuentries$+=l("Purchase ")+.nams$[.@i]+(" for ") + format_number( .price[.@i] )+":"; + } + select (@menuentries$); + mes ""; + + // First option to return to previous menu + if (@menu == 1) + goto L_Furniture; + + // Otherwise, we know then that (@menu-2) is the ID in @valid_ids + // So we save .@id with the correct ID in object arrays. + // We also calculate how much aggregated money you have. + .@id=@valid_ids[@menu-2]; + .@gp=REAL_ESTATE_CREDITS+Zeny; + + if (realestate_hasmobilia(.id, .layer[.@id], .objid[.@id])) { + // If you have the mobilia, you're selling it for Mobiliary Credits + delcells realestate_cellname(.id, .@id); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0097"); + REAL_ESTATE_CREDITS+=realestate_sellprice(.id,.price[.@i]); + mesc l("Sale successful!"); + next; + } else { + // Else, you're buying it, so we must check if you have the moolah first + .@price=.price[.@id]; + if (.@gp > .@price) { + realestate_payment(.@price); + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + areatimer(.mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], 10, "::OnSlide"); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0097"); + mesc l("Purchase successful!"); + next; + } else { + mesc l("Not enough funds!"); + next; + } + } + + // This loops forever + goto L_ContinuousLoop; + + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=5; // Estate ID + .delay=15; // Forced wait between rings + .dpost=0; // Last doorbell ring + .mapa$="009-7"; + + // Arrays + // We go element by element on the array building the menu + .nams$=""; + .layer=0; + .price=0; + .objid=0; + .x1=0; + .y1=0; + .x2=0; + .y2=0; + + // Furniture Settings + // Name, Collision Layer, Price, ID, x1, y1, x2, y2 + // For Collision Layer, see constants.conf ("Real Estate Collisions") + create_object("Placeholder" ,99,999999,99999, 99, 99, 99, 99); + + create_object("Bed 01" , 5, 5000, 1, 26, 27, 27, 30); + create_object("Bed 02" , 5, 5000, 2, 28, 27, 29, 30); + + create_object("Wardrobe" , 1, 7000, 1, 21, 23, 22, 23); + create_object("Stovetop" , 1, 10000, 2, 28, 23, 29, 24); + create_object("Shelf 01" , 1, 2000, 4, 25, 23, 25, 23); + create_object("Shelf 02" , 1, 2000, 8, 26, 23, 26, 23); + create_object("Shelf 03" , 1, 2000, 16, 27, 23, 27, 23); + create_object("Shelf 04" , 1, 2000, 32, 30, 23, 30, 23); + create_object("Shelf 05" , 1, 2000, 64, 31, 23, 31, 23); + create_object("Shelf 06" , 1, 2000, 128, 32, 23, 32, 23); + create_object("Shelf 07" , 1, 2000, 256, 33, 23, 33, 23); + create_object("Shelf 08" , 1, 2000, 512, 34, 23, 34, 23); + create_object("Shelf 09" , 1, 2000, 1024, 35, 23, 35, 23); + create_object("Shelf 10" , 1, 2000, 2048, 36, 23, 36, 23); + create_object("Shelf 11" , 1, 2000, 4096, 37, 23, 37, 23); + create_object("Shelf 12" , 1, 2000, 8192, 38, 23, 38, 23); + + create_object("Piano" , 3, 10000, 1, 33, 25, 35, 25); + + create_object("Left Desk" , 2, 5000, 1, 20, 25, 22, 27); + create_object("Right Desk" , 2, 5000, 2, 36, 30, 38, 32); + + create_object("Left Chair" , 4, 2000, 1, 21, 28, 21, 28); + create_object("Right Chair" , 4, 2000, 2, 37, 29, 37, 29); + + create_object("Painting 01" , 6, 3000, 1, 21, 20, 21, 20); + create_object("Painting 02" , 6, 3000, 2, 23, 21, 23, 21); + create_object("Painting 03" , 6, 3000, 4, 25, 20, 25, 20); + create_object("Painting 04" , 6, 3000, 8, 28, 21, 28, 21); + create_object("Painting 05" , 6, 3000, 16, 31, 20, 31, 20); + create_object("Painting 06" , 6, 3000, 32, 36, 20, 36, 20); + + // Load Mobilia already existing + //debugmes "[REAL ESTATE] Now loading mobilia"; + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + switch (.layer[.@i]) { + case 1: + if ($ESTATE_MOBILIA_64[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 2: + if ($ESTATE_MOBILIA_4[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 3: + if ($ESTATE_MOBILIA_8[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 4: + if ($ESTATE_MOBILIA_32[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 5: + if ($ESTATE_MOBILIA_128[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 6: + if ($ESTATE_MOBILIA_2[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + default: + // We do nothing by default + //debugmes("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", .@i, .layer[.@i]); + break; + } + } + //debugmes "Found %d valid objects", getarraysize(.valid_ids); + for (.@j=0; .@j < getarraysize(.valid_ids); .@j++) { + .@id=.valid_ids[.@j]; + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + //debugmes "Creating %s in %s", realestate_cellname(.id, .@id), .mapa$; + } + deletearray .valid_ids; + // Load NPCs + donpcevent "NPCs#RES_0097::OnReload"; + end; + +} + + diff --git a/npc/009-7/utils.txt b/npc/009-7/utils.txt new file mode 100644 index 0000000..424a1f7 --- /dev/null +++ b/npc/009-7/utils.txt @@ -0,0 +1,73 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Utils take care of NPCs - Their code, and enable/disable using check_cell +// This file is custom to every room + +// ID: 5 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller for rent system +// Doorbell is the main controller for indoor +// This is the NPC script controller +009-7,0,0,0 script NPCs#RES_0097 NPC_HIDDEN,{ + // load_npc ( name , map, x , y{, cell} ) + function load_npc { + if (checknpccell(getarg(1), getarg(2), getarg(3), getarg(4, cell_chknopass))) { + enablenpc getarg(0); + //debugmes "ENABLING NPC %s", getarg(0); + } else { + disablenpc getarg(0); + //debugmes "Disabling NPC %s", getarg(0); + } + + /* + debugmes "----- %s (%d,%d) cell report", getarg(1), getarg(2), getarg(3); + debugmes "cell_chknopass: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknopass); + debugmes "cell_chknoreach: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknoreach); + debugmes "cell_chkbasilica: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkbasilica); + debugmes ""; + debugmes "cell_chkwater: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwater); + debugmes "cell_chkwall: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwall); + debugmes "cell_chkcliff: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkcliff); + debugmes "----- Npc Id: %s", getarg(0); + */ + return; + } + end; + +OnInit: + // Estate Settings + .id=5; // Estate ID + .mapa$="009-7"; // Map name + + // NPC Settings + .sex = G_OTHER; + .distance = 3; + end; + +// Load or unload accordingly +OnReload: + //debugmes "[REAL ESTATE] NPC ONRELOAD"; + // load_npc ( name , map, x , y{, cell} ) + load_npc("Wardrobe#RES_0097", .mapa$, 21, 23); + load_npc("Piano#RES_0097" , .mapa$, 34, 25); + end; + +} + diff --git a/npc/010-1-1/_import.txt b/npc/010-1-1/_import.txt new file mode 100644 index 0000000..0cf7810 --- /dev/null +++ b/npc/010-1-1/_import.txt @@ -0,0 +1,6 @@ +// Map 010-1-1: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-1/_mobs.txt", +"npc/010-1-1/_warps.txt", +"npc/010-1-1/boss.txt", +"npc/010-1-1/treasure.txt", diff --git a/npc/010-1-1/_mobs.txt b/npc/010-1-1/_mobs.txt new file mode 100644 index 0000000..e4b28e7 --- /dev/null +++ b/npc/010-1-1/_mobs.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-1: Canyon Cave mobs +010-1-1,0,0,0,0 monster Cave Maggot 1027,130,35000,150000 +010-1-1,124,52,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-1,83,118,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-1,109,95,26,24 monster Scorpion 1071,8,35000,150000 +010-1-1,135,88,20,20 monster Bat 1039,7,35000,150000 +010-1-1,165,49,17,19 monster Snake 1122,12,35000,150000 +010-1-1,0,0,0,0 monster Big Topaz Bif 1103,5,35000,150000 +010-1-1,109,38,78,14 monster Small Topaz Bif 1101,3,35000,150000 +010-1-1,64,41,12,23 monster Plushroom Field 1011,4,35000,150000 diff --git a/npc/010-1-1/_warps.txt b/npc/010-1-1/_warps.txt new file mode 100644 index 0000000..5972511 --- /dev/null +++ b/npc/010-1-1/_warps.txt @@ -0,0 +1,24 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-1: Canyon Cave warps +010-1-1,124,56,0 warp #010-1-1_124_56 0,0,010-2,86,112 +010-1-1,111,84,0 warp #010-1-1_111_84 0,0,010-1,86,40 +010-1-1,183,51,0 warp #010-1-1_183_51 0,0,010-2,131,113 +010-1-1,188,53,0 warp #010-1-1_188_53 0,0,010-2,134,114 +010-1-1,181,25,0 warp #010-1-1_181_25 0,0,010-2,121,98 +010-1-1,47,32,0 warp #010-1-1_47_32 0,0,010-2,37,97 +010-1-1,40,75,0 warp #010-1-1_40_75 0,0,010-1,46,52 +010-1-1,60,84,0 warp #010-1-1_60_84 0,0,010-4-1,41,56 +010-1-1,71,116,0 warp #010-1-1_71_116 0,0,010-1,77,84 +010-1-1,43,42,0 warp #010-1-1_43_42 0,0,010-2,34,102 +010-1-1,43,26,0 warp #010-1-1_43_26 0,0,010-1,45,30 +010-1-1,102,70,0 warp #010-1-1_102_70 0,0,010-1,104,67 +010-1-1,166,87,0 warp #010-1-1_166_87 0,0,010-1,124,63 +010-1-1,107,31,0 warp #010-1-1_107_31 0,0,010-1,95,34 +010-1-1,50,20,0 warp #010-1-1_50_20 0,0,010-2-14,34,21 +010-1-1,167,43,0 warp #010-1-1_167_43 0,0,010-2-6,60,58 +010-1-1,190,103,0 warp #010-1-1_190_103 0,0,010-2-10,36,50 +010-1-1,146,74,0 warp #010-1-1_146_74 0,0,010-1-6,86,40 +010-1-1,50,117,0 warp #010-1-1_50_117 0,0,010-1-5,62,80 +010-1-1,116,122,0 warp #010-1-1_116_122 0,0,004-2-4,103,22 +010-1-1,151,122,0 warp #010-1-1_151_122 0,0,004-2-4,134,25 +010-1-1,125,47,0 warp #010-1-1_125_47 0,0,010-1,107,42 diff --git a/npc/010-1-1/boss.txt b/npc/010-1-1/boss.txt new file mode 100644 index 0000000..3ee49d7 --- /dev/null +++ b/npc/010-1-1/boss.txt @@ -0,0 +1,32 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Giant Cave Maggot Boss + +010-1-1,0,0,0 script #BossCtrl_010-1-1 NPC_HIDDEN,{ + end; + +// Respawn every hour +OnTimer3600000: + stopnpctimer; +OnInit: + // WARNING: (51,19) INVALID COORDINATES + setarray .xp, 49, 51, 44, 101, 124, 164, 132, 154, 116; + setarray .yp, 59, 19, 100, 45, 26, 35, 61, 108, 94; + .@tg=rand(getarraysize(.xp)-1); + monster "010-1-1", .xp[.@tg], .yp[.@tg], strmobinfo(1, GiantCaveMaggot), GiantCaveMaggot, 1, "#BossCtrl_010-1-1::OnBossDeath"; + end; + +OnBossDeath: + initnpctimer; + .@party=getcharid(1); + if (.@party > 0) { + mapannounce getmap(), "Boss deafeated by Party: " + getpartyname(.@party), bc_all; + } else { + mapannounce getmap(), "Boss deafeated by: " + strcharinfo(0), bc_all; + } + fix_mobkill(GiantCaveMaggot); + end; + +} diff --git a/npc/010-1-1/treasure.txt b/npc/010-1-1/treasure.txt new file mode 100644 index 0000000..5f2c1fb --- /dev/null +++ b/npc/010-1-1/treasure.txt @@ -0,0 +1,61 @@ +// TMW2 Script + +// (Random) Treasure Chest +// Authored by Jesusalva +// Regenerates every 6 hours + +010-1-1,0,0,0 script #chest_010110 NPC_CHEST,{ + + if (!.busy && !.empty) { + TreasureBox(); + + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + .dir = .dir == 0 ? 2 : 6; // closed ? opening : closing + .busy = true; // lock until available again + initnpctimer; + } else if (!.busy) { + mesc l("Someone looted this treasure box already..."); + } else { + end; + } + close; + +OnTimer160: + .dir = .dir == 6 ? 0 : 4; // closing ? closed : open + end; + +OnTimer500: + .busy = false; // unlock + if (.dir == 0 || .dir == 4) + stopnpctimer; // stop here if the chest is closed + end; + +OnInit: + .busy = false; + .distance = 2; + .empty = false; + +OnClock0156: +OnClock0756: +OnClock1356: +OnClock1956: + // Try to warp randomly to a walkable spot, up to 20 attempts + // Otherwise, it'll stay where it already is (but will close and refill). + .@e=0; .@x=0; .@y=0; + while (!checkcell(.map$, .@x, .@y, cell_chkpass)) + { + if (.@e == 20) { + .@x=.x; + .@y=.y; + break; + } + // Remember the +20 -20 margin adjustment + .@x = rand(20, 180); + .@y = rand(20, 120); + ++.@e; + } + .busy=false; + .empty=false; + movenpc .name$, .@x, .@y, 0; + end; +} diff --git a/npc/010-1-10/_import.txt b/npc/010-1-10/_import.txt new file mode 100644 index 0000000..b88e251 --- /dev/null +++ b/npc/010-1-10/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1-10: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-10/_mobs.txt", +"npc/010-1-10/_warps.txt", diff --git a/npc/010-1-10/_mobs.txt b/npc/010-1-10/_mobs.txt new file mode 100644 index 0000000..966dd8c --- /dev/null +++ b/npc/010-1-10/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-10: Canyon Cave mobs +010-1-10,0,0,0,0 monster Cave Maggot 1027,12,35000,150000 +010-1-10,55,31,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-10,45,25,14,4 monster Mountain Snake 1123,3,35000,150000 diff --git a/npc/010-1-10/_warps.txt b/npc/010-1-10/_warps.txt new file mode 100644 index 0000000..c83dda9 --- /dev/null +++ b/npc/010-1-10/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-10: Canyon Cave warps +010-1-10,33,35,0 warp #010-1-10_33_35 0,0,010-2-4,53,21 +010-1-10,55,38,0 warp #010-1-10_55_38 0,0,010-1,91,49 diff --git a/npc/010-1-11/_import.txt b/npc/010-1-11/_import.txt new file mode 100644 index 0000000..3c69cdc --- /dev/null +++ b/npc/010-1-11/_import.txt @@ -0,0 +1,3 @@ +// Map 010-1-11: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-11/_warps.txt", diff --git a/npc/010-1-11/_warps.txt b/npc/010-1-11/_warps.txt new file mode 100644 index 0000000..f44c897 --- /dev/null +++ b/npc/010-1-11/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-11: Canyon Cave warps +010-1-11,36,30,0 warp #010-1-11_36_30 0,0,004-2-11,52,21 diff --git a/npc/010-1-12/_import.txt b/npc/010-1-12/_import.txt new file mode 100644 index 0000000..e317f7b --- /dev/null +++ b/npc/010-1-12/_import.txt @@ -0,0 +1,3 @@ +// Map 010-1-12: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-12/_warps.txt", diff --git a/npc/010-1-12/_warps.txt b/npc/010-1-12/_warps.txt new file mode 100644 index 0000000..3c5a961 --- /dev/null +++ b/npc/010-1-12/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-12: Canyon Cave warps +010-1-12,35,30,0 warp #010-1-12_35_30 0,0,004-2-4,118,43 diff --git a/npc/010-1-2/_import.txt b/npc/010-1-2/_import.txt new file mode 100644 index 0000000..803275e --- /dev/null +++ b/npc/010-1-2/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1-2: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-2/_mobs.txt", +"npc/010-1-2/_warps.txt", diff --git a/npc/010-1-2/_mobs.txt b/npc/010-1-2/_mobs.txt new file mode 100644 index 0000000..22f0eb9 --- /dev/null +++ b/npc/010-1-2/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-2: Canyon Cave mobs +010-1-2,0,0,0,0 monster Cave Maggot 1027,50,35000,150000 diff --git a/npc/010-1-2/_warps.txt b/npc/010-1-2/_warps.txt new file mode 100644 index 0000000..16e6258 --- /dev/null +++ b/npc/010-1-2/_warps.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-2: Canyon Cave warps +010-1-2,121,113,0 warp #010-1-2_121_113 0,0,010-2,190,109 +010-1-2,107,86,0 warp #010-1-2_107_86 0,0,010-2,149,120 +010-1-2,52,91,0 warp #010-1-2_52_91 0,0,010-1,124,54 +010-1-2,62,68,0 warp #010-1-2_62_68 0,0,010-2,106,126 +010-1-2,87,22,0 warp #010-1-2_87_22 0,0,010-2,160,67 +010-1-2,118,27,0 warp #010-1-2_118_27 0,0,010-2,173,79 +010-1-2,77,55,0 warp #010-1-2_77_55 0,0,010-2,126,120 +010-1-2,64,47,0 warp #010-1-2_64_47 0,0,010-2,112,102 +010-1-2,46,110,0 warp #010-1-2_46_110 0,0,004-2-4,121,21 diff --git a/npc/010-1-3/_import.txt b/npc/010-1-3/_import.txt new file mode 100644 index 0000000..f4becbd --- /dev/null +++ b/npc/010-1-3/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1-3: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-3/_mobs.txt", +"npc/010-1-3/_warps.txt", diff --git a/npc/010-1-3/_mobs.txt b/npc/010-1-3/_mobs.txt new file mode 100644 index 0000000..3863478 --- /dev/null +++ b/npc/010-1-3/_mobs.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-3: Canyon Cave mobs +010-1-3,0,0,0,0 monster Cave Maggot 1027,12,35000,150000 +010-1-3,43,37,10,11 monster Scorpion 1071,3,35000,150000 +010-1-3,34,34,7,15 monster Snale 1098,2,35000,150000 +010-1-3,32,46,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-3,53,51,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-3,51,28,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-3,0,0,0,0 monster Cave Maggot 1027,4,35000,150000 +010-1-3,47,38,11,3 monster Cave Snale 1035,1,35000,150000 +010-1-3,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 diff --git a/npc/010-1-3/_warps.txt b/npc/010-1-3/_warps.txt new file mode 100644 index 0000000..0a6524f --- /dev/null +++ b/npc/010-1-3/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-3: Canyon Cave warps +010-1-3,51,31,0 warp #010-1-3_51_31 0,0,010-1,70,42 +010-1-3,32,50,0 warp #010-1-3_32_50 0,0,010-1,51,54 +010-1-3,53,55,0 warp #010-1-3_53_55 0,0,010-1,74,56 diff --git a/npc/010-1-4/_import.txt b/npc/010-1-4/_import.txt new file mode 100644 index 0000000..0e07f8a --- /dev/null +++ b/npc/010-1-4/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1-4: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-4/_mobs.txt", +"npc/010-1-4/_warps.txt", diff --git a/npc/010-1-4/_mobs.txt b/npc/010-1-4/_mobs.txt new file mode 100644 index 0000000..7dc5689 --- /dev/null +++ b/npc/010-1-4/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-4: Canyon Cave mobs +010-1-4,0,0,0,0 monster Cave Maggot 1027,7,35000,150000 +010-1-4,49,35,9,13 monster Scorpion 1071,6,35000,150000 +010-1-4,39,42,9,14 monster Cave Snake 1035,2,35000,150000 +010-1-4,56,26,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-4,35,52,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-4,0,0,0,0 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/010-1-4/_warps.txt b/npc/010-1-4/_warps.txt new file mode 100644 index 0000000..51367da --- /dev/null +++ b/npc/010-1-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-4: Canyon Cave warps +010-1-4,35,55,0 warp #010-1-4_35_55 0,0,010-1,67,54 +010-1-4,56,30,0 warp #010-1-4_56_30 0,0,010-1,87,52 diff --git a/npc/010-1-5/_import.txt b/npc/010-1-5/_import.txt new file mode 100644 index 0000000..b41161a --- /dev/null +++ b/npc/010-1-5/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1-5: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-5/_mobs.txt", +"npc/010-1-5/_warps.txt", diff --git a/npc/010-1-5/_mobs.txt b/npc/010-1-5/_mobs.txt new file mode 100644 index 0000000..2a83487 --- /dev/null +++ b/npc/010-1-5/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-5: Canyon Cave mobs +010-1-5,0,0,0,0 monster Cave Maggot 1027,30,35000,150000 +010-1-5,58,69,18,18 monster Desert Bandit 1124,3,35000,150000 +010-1-5,46,42,5,19 monster Bat 1039,4,35000,150000 +010-1-5,69,39,15,17 monster Snake 1122,4,35000,150000 +010-1-5,41,40,10,15 monster Scorpion 1071,8,35000,150000 +010-1-5,58,27,28,4 monster Black Scorpion 1074,4,35000,150000 +010-1-5,44,71,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-5,55,39,4,3 monster Desert Maggot 1083,2,35000,150000 +010-1-5,0,0,0,0 monster Topaz Bif 1102,2,35000,150000 +010-1-5,54,35,24,11 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/010-1-5/_warps.txt b/npc/010-1-5/_warps.txt new file mode 100644 index 0000000..a6b1131 --- /dev/null +++ b/npc/010-1-5/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-5: Canyon Cave warps +010-1-5,44,75,0 warp #010-1-5_44_75 0,0,010-1,44,74 +010-1-5,56,39,0 warp #010-1-5_56_39 0,0,010-1,49,67 +010-1-5,36,20,0 warp #010-1-5_36_20 0,0,010-1-8,36,29 +010-1-5,83,36,0 warp #010-1-5_83_36 1,0,010-1-6,32,58 +010-1-5,62,79,0 warp #010-1-5_62_79 0,0,010-1-1,50,116 +010-1-5,56,50,0 warp #010-1-5_56_50 0,0,010-1,59,43 diff --git a/npc/010-1-6/_import.txt b/npc/010-1-6/_import.txt new file mode 100644 index 0000000..e30a47d --- /dev/null +++ b/npc/010-1-6/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1-6: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-6/_mobs.txt", +"npc/010-1-6/_warps.txt", diff --git a/npc/010-1-6/_mobs.txt b/npc/010-1-6/_mobs.txt new file mode 100644 index 0000000..4a60551 --- /dev/null +++ b/npc/010-1-6/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-6: Canyon Cave mobs +010-1-6,86,25,5,3 monster Snake 1122,1,35000,150000 +010-1-6,78,31,11,4 monster Black Scorpion 1074,1,35000,150000 +010-1-6,0,0,0,0 monster Cave Maggot 1027,50,35000,150000 +010-1-6,51,72,5,4 monster Blub 1008,1,35000,150000 +010-1-6,0,0,0,0 monster Plushroom Field 1011,2,35000,150000 diff --git a/npc/010-1-6/_warps.txt b/npc/010-1-6/_warps.txt new file mode 100644 index 0000000..b57cdc3 --- /dev/null +++ b/npc/010-1-6/_warps.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-6: Canyon Cave warps +010-1-6,34,72,0 warp #010-1-6_34_72 0,0,010-1,75,70 +010-1-6,46,46,0 warp #010-1-6_46_46 0,0,010-1,84,34 +010-1-6,70,65,0 warp #010-1-6_70_65 0,0,010-1,104,48 +010-1-6,86,39,0 warp #010-1-6_86_39 0,0,010-1-1,146,73 +010-1-6,64,36,0 warp #010-1-6_64_36 0,0,010-1,104,31 +010-1-6,87,20,0 warp #010-1-6_87_20 0,0,010-1-7,37,39 +010-1-6,32,57,0 warp #010-1-6_32_57 0,0,010-1-5,82,35 +010-1-6,52,23,0 warp #010-1-6_52_23 0,0,010-2-5,52,55 diff --git a/npc/010-1-7/_import.txt b/npc/010-1-7/_import.txt new file mode 100644 index 0000000..b563c0d --- /dev/null +++ b/npc/010-1-7/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1-7: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-7/_mobs.txt", +"npc/010-1-7/_warps.txt", diff --git a/npc/010-1-7/_mobs.txt b/npc/010-1-7/_mobs.txt new file mode 100644 index 0000000..b407712 --- /dev/null +++ b/npc/010-1-7/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-7: Canyon Cave mobs +010-1-7,56,33,3,9 monster Bat 1039,5,35000,150000 +010-1-7,33,31,3,6 monster Bat 1039,5,35000,150000 +010-1-7,42,24,12,2 monster Bat 1039,5,35000,150000 +010-1-7,52,41,5,2 monster Bat 1039,2,35000,150000 +010-1-7,38,37,7,2 monster Bat 1039,3,35000,150000 +010-1-7,44,30,8,4 monster Black Scorpion 1074,15,35000,150000 +010-1-7,0,0,0,0 monster Cave Maggot 1027,7,35000,150000 +010-1-7,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 diff --git a/npc/010-1-7/_warps.txt b/npc/010-1-7/_warps.txt new file mode 100644 index 0000000..ef824d9 --- /dev/null +++ b/npc/010-1-7/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-7: Canyon Cave warps +010-1-7,47,20,0 warp #010-1-7_47_20 0,0,010-2-7,53,48 +010-1-7,37,40,0 warp #010-1-7_37_40 0,0,010-1-6,87,21 diff --git a/npc/010-1-8/_import.txt b/npc/010-1-8/_import.txt new file mode 100644 index 0000000..2af915b --- /dev/null +++ b/npc/010-1-8/_import.txt @@ -0,0 +1,3 @@ +// Map 010-1-8: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-8/_warps.txt", diff --git a/npc/010-1-8/_warps.txt b/npc/010-1-8/_warps.txt new file mode 100644 index 0000000..432ab3e --- /dev/null +++ b/npc/010-1-8/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-8: Canyon Cave warps +010-1-8,36,30,0 warp #010-1-8_36_30 0,0,010-1-5,36,21 diff --git a/npc/010-1-9/_import.txt b/npc/010-1-9/_import.txt new file mode 100644 index 0000000..29cc688 --- /dev/null +++ b/npc/010-1-9/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1-9: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1-9/_mobs.txt", +"npc/010-1-9/_warps.txt", diff --git a/npc/010-1-9/_mobs.txt b/npc/010-1-9/_mobs.txt new file mode 100644 index 0000000..088c591 --- /dev/null +++ b/npc/010-1-9/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-9: Canyon Cave mobs +010-1-9,44,41,14,21 monster Sarracenus 1125,2,35000,300000 +010-1-9,65,41,12,21 monster Sarracenus 1125,2,35000,300000 diff --git a/npc/010-1-9/_warps.txt b/npc/010-1-9/_warps.txt new file mode 100644 index 0000000..31e71cf --- /dev/null +++ b/npc/010-1-9/_warps.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1-9: Canyon Cave warps +010-1-9,60,33,0 warp #010-1-9_60_33 0,0,010-2,194,56 +010-1-9,73,20,0 warp #010-1-9_73_20 0,0,010-2,207,38 +010-1-9,60,63,0 warp #010-1-9_60_63 0,0,010-2-2,39,64 +010-1-9,49,47,0 warp #010-1-9_49_47 0,0,010-2,185,65 +010-1-9,69,60,0 warp #010-1-9_69_60 0,0,010-2,185,65 diff --git a/npc/010-1/_import.txt b/npc/010-1/_import.txt new file mode 100644 index 0000000..3d09583 --- /dev/null +++ b/npc/010-1/_import.txt @@ -0,0 +1,4 @@ +// Map 010-1: Desert Mountains +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-1/_mobs.txt", +"npc/010-1/_warps.txt", diff --git a/npc/010-1/_mobs.txt b/npc/010-1/_mobs.txt new file mode 100644 index 0000000..6917b22 --- /dev/null +++ b/npc/010-1/_mobs.txt @@ -0,0 +1,26 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1: Desert Mountains mobs +010-1,104,90,9,4 monster Desert Maggot 1083,12,35000,150000 +010-1,64,98,4,4 monster Scorpion 1071,8,35000,150000 +010-1,46,83,4,4 monster Scorpion 1071,6,35000,150000 +010-1,59,58,9,4 monster Sarracenus 1125,2,35000,300000 +010-1,87,77,3,8 monster Mountain Snake 1123,3,35000,300000 +010-1,102,30,3,8 monster Mountain Snake 1123,3,35000,300000 +010-1,119,33,3,2 monster Desert Bandit 1124,1,35000,300000 +010-1,114,48,3,2 monster Desert Bandit 1124,2,35000,300000 +010-1,52,88,3,2 monster Desert Bandit 1124,2,35000,300000 +010-1,92,80,6,5 monster Sarracenus 1125,2,35000,300000 +010-1,77,98,6,5 monster Sarracenus 1125,1,35000,300000 +010-1,74,46,6,5 monster Sarracenus 1125,1,35000,300000 +010-1,99,70,8,2 monster Snake 1122,12,35000,300000 +010-1,58,96,8,2 monster Snake 1122,2,35000,300000 +010-1,73,46,17,3 monster Snake 1122,4,35000,300000 +010-1,109,37,8,2 monster Desert Maggot 1083,10,35000,300000 +010-1,55,101,9,4 monster Desert Maggot 1083,12,35000,150000 +010-1,0,0,0,0 monster Desert Maggot 1083,70,35000,150000 +010-1,121,78,4,25 monster Desert Maggot 1083,14,35000,150000 +010-1,122,63,1,4 monster Lost Piou 1002,1,35000,300000 +010-1,111,73,13,9 monster Scorpion 1071,8,35000,150000 +010-1,88,57,4,4 monster Mister Prickel 1436,1,35000,35000 +010-1,73,46,35,15 monster Mister Prickel 1436,5,35000,35000 +010-1,70,71,4,4 monster Mister Prickel 1436,2,35000,35000 diff --git a/npc/010-1/_warps.txt b/npc/010-1/_warps.txt new file mode 100644 index 0000000..8cee1c7 --- /dev/null +++ b/npc/010-1/_warps.txt @@ -0,0 +1,33 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-1: Desert Mountains warps +010-1,121,92,0 warp #010-1_121_92 0,0,004-2-2,55,22 +010-1,124,53,0 warp #010-1_124_53 0,0,010-1-2,52,90 +010-1,86,39,0 warp #010-1_86_39 0,0,010-1-1,111,83 +010-1,70,41,0 warp #010-1_70_41 0,0,010-1-3,51,30 +010-1,51,53,0 warp #010-1_51_53 0,0,010-1-3,32,49 +010-1,74,55,0 warp #010-1_74_55 0,0,010-1-3,53,54 +010-1,87,51,0 warp #010-1_87_51 0,0,010-1-4,56,29 +010-1,67,63,0 warp #010-1_67_63 0,0,010-1-4,35,54 +010-1,49,66,0 warp #010-1_49_66 0,0,010-1-5,56,38 +010-1,44,73,0 warp #010-1_44_73 0,0,010-1-5,44,74 +010-1,104,47,0 warp #010-1_104_47 0,0,010-1-6,70,64 +010-1,92,69,0 warp #010-1_92_69 0,0,004-2-9,61,22 +010-1,46,51,0 warp #010-1_46_51 0,0,010-1-1,40,74 +010-1,52,93,0 warp #010-1_52_93 0,0,004-2-4,33,44 +010-1,109,84,0 warp #010-1_109_84 0,0,004-2-3,95,30 +010-1,77,83,0 warp #010-1_77_83 0,0,010-1-1,71,115 +010-1,45,29,0 warp #010-1_45_29 0,0,010-1-1,43,27 +010-1,84,33,0 warp #010-1_84_33 0,0,010-1-6,46,45 +010-1,75,69,0 warp #010-1_75_69 0,0,010-1-6,33,73 +010-1,91,48,0 warp #010-1_91_48 0,0,010-1-10,55,37 +010-1,41,35,0 warp #010-1_41_35 0,0,010-1-1,42,40 +010-1,125,63,0 warp #010-1_125_63 0,0,010-1-1,166,88 +010-1,104,66,0 warp #010-1_104_66 0,0,010-1-1,102,69 +010-1,59,42,0 warp #010-1_59_42 0,0,010-1-5,56,51 +010-1,58,79,0 warp #010-1_58_79 0,0,004-2-3,49,31 +010-1,69,72,0 warp #010-1_69_72 0,0,004-2-3,65,26 +010-1,123,27,0 warp #010-1_123_27 0,0,010-3,32,28 +010-1,64,76,0 warp #010-1_64_76 0,0,004-2-11,33,42 +010-1,104,30,0 warp #010-1_104_30 0,0,010-1-6,64,37 +010-1,95,33,0 warp #010-1_95_33 0,0,010-1-1,107,30 +010-1,107,41,0 warp #010-1_107_41 0,0,010-1-1,125,48 diff --git a/npc/010-2-10/_import.txt b/npc/010-2-10/_import.txt new file mode 100644 index 0000000..a9aa248 --- /dev/null +++ b/npc/010-2-10/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-10: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-10/_mobs.txt", +"npc/010-2-10/_warps.txt", diff --git a/npc/010-2-10/_mobs.txt b/npc/010-2-10/_mobs.txt new file mode 100644 index 0000000..42d4da8 --- /dev/null +++ b/npc/010-2-10/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-10: Canyon Cave mobs +010-2-10,39,34,9,12 monster Bat 1039,6,35000,150000 +010-2-10,0,0,0,0 monster Cave Maggot 1027,8,35000,150000 +010-2-10,40,35,10,5 monster Snake 1199,12,35000,150000 +010-2-10,42,27,11,5 monster Old Snake 1122,3,35000,150000 +010-2-10,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 diff --git a/npc/010-2-10/_warps.txt b/npc/010-2-10/_warps.txt new file mode 100644 index 0000000..ca9cec1 --- /dev/null +++ b/npc/010-2-10/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-10: Canyon Cave warps +010-2-10,36,51,0 warp #010-2-10_36_51 0,0,010-1-1,190,104 +010-2-10,53,24,0 warp #010-2-10_53_24 0,0,010-2-3,35,66 diff --git a/npc/010-2-11/_import.txt b/npc/010-2-11/_import.txt new file mode 100644 index 0000000..7b77c0d --- /dev/null +++ b/npc/010-2-11/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-11: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-11/_mobs.txt", +"npc/010-2-11/_warps.txt", diff --git a/npc/010-2-11/_mobs.txt b/npc/010-2-11/_mobs.txt new file mode 100644 index 0000000..ca7ce1a --- /dev/null +++ b/npc/010-2-11/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-11: Canyon Cave mobs +010-2-11,47,32,17,10 monster Scorpion 1071,5,35000,150000 +010-2-11,46,44,16,5 monster Snake 1199,2,35000,150000 +010-2-11,0,0,0,0 monster Cave Maggot 1027,7,35000,150000 +010-2-11,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 diff --git a/npc/010-2-11/_warps.txt b/npc/010-2-11/_warps.txt new file mode 100644 index 0000000..c601c2f --- /dev/null +++ b/npc/010-2-11/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-11: Canyon Cave warps +010-2-11,51,31,0 warp #010-2-11_51_31 0,0,010-2-2,84,73 +010-2-11,45,54,0 warp #010-2-11_45_54 0,0,010-2,230,116 diff --git a/npc/010-2-12/_import.txt b/npc/010-2-12/_import.txt new file mode 100644 index 0000000..be089a5 --- /dev/null +++ b/npc/010-2-12/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-12: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-12/_mobs.txt", +"npc/010-2-12/_warps.txt", diff --git a/npc/010-2-12/_mobs.txt b/npc/010-2-12/_mobs.txt new file mode 100644 index 0000000..172b15d --- /dev/null +++ b/npc/010-2-12/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-12: Canyon Cave mobs +010-2-12,44,40,12,11 monster Golden Scorpion 1078,1,40000,200000 +010-2-12,0,0,0,0 monster Cave Maggot 1027,9,35000,150000 diff --git a/npc/010-2-12/_warps.txt b/npc/010-2-12/_warps.txt new file mode 100644 index 0000000..b0f1c57 --- /dev/null +++ b/npc/010-2-12/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-12: Canyon Cave warps +010-2-12,39,20,0 warp #010-2-12_39_20 0,0,004-3-2,126,96 diff --git a/npc/010-2-13/_import.txt b/npc/010-2-13/_import.txt new file mode 100644 index 0000000..0dd4000 --- /dev/null +++ b/npc/010-2-13/_import.txt @@ -0,0 +1,3 @@ +// Map 010-2-13: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-13/_warps.txt", diff --git a/npc/010-2-13/_warps.txt b/npc/010-2-13/_warps.txt new file mode 100644 index 0000000..30273c3 --- /dev/null +++ b/npc/010-2-13/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-13: Canyon Cave warps +010-2-13,49,25,0 warp #010-2-13_49_25 0,0,010-2,198,102 +010-2-13,34,37,0 warp #010-2-13_34_37 0,0,010-2,192,108 diff --git a/npc/010-2-14/_import.txt b/npc/010-2-14/_import.txt new file mode 100644 index 0000000..9320e60 --- /dev/null +++ b/npc/010-2-14/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-14: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-14/_mobs.txt", +"npc/010-2-14/_warps.txt", diff --git a/npc/010-2-14/_mobs.txt b/npc/010-2-14/_mobs.txt new file mode 100644 index 0000000..46bc9e1 --- /dev/null +++ b/npc/010-2-14/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-14: Canyon Cave mobs +010-2-14,0,0,0,0 monster Bat 1039,3,35000,150000 +010-2-14,35,37,5,12 monster Bat 1039,3,35000,150000 +010-2-14,35,43,7,9 monster Snake 1122,4,35000,150000 +010-2-14,0,0,0,0 monster Plushroom Field 1011,2,35000,150000 diff --git a/npc/010-2-14/_warps.txt b/npc/010-2-14/_warps.txt new file mode 100644 index 0000000..d32d145 --- /dev/null +++ b/npc/010-2-14/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-14: Canyon Cave warps +010-2-14,34,20,0 warp #010-2-14_34_20 0,0,010-1-1,50,21 +010-2-14,39,56,0 warp #010-2-14_39_56 0,0,010-2-9,36,21 diff --git a/npc/010-2-15/_import.txt b/npc/010-2-15/_import.txt new file mode 100644 index 0000000..d9c52d1 --- /dev/null +++ b/npc/010-2-15/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-15: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-15/_mobs.txt", +"npc/010-2-15/_warps.txt", diff --git a/npc/010-2-15/_mobs.txt b/npc/010-2-15/_mobs.txt new file mode 100644 index 0000000..39ad9da --- /dev/null +++ b/npc/010-2-15/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-15: Canyon Cave mobs +010-2-15,50,27,13,5 monster Scorpion 1071,3,35000,150000 +010-2-15,0,0,0,0 monster Cave Maggot 1027,2,35000,150000 +010-2-15,45,26,12,7 monster Cave Snake 1035,1,35000,150000 diff --git a/npc/010-2-15/_warps.txt b/npc/010-2-15/_warps.txt new file mode 100644 index 0000000..650ae31 --- /dev/null +++ b/npc/010-2-15/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-15: Canyon Cave warps +010-2-15,35,42,0 warp #010-2-15_35_42 0,0,007-1-1,42,21 +010-2-15,60,37,0 warp #010-2-15_60_37 0,0,007-1-2,32,33 diff --git a/npc/010-2-16/_import.txt b/npc/010-2-16/_import.txt new file mode 100644 index 0000000..167d9d8 --- /dev/null +++ b/npc/010-2-16/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-16: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-16/_mobs.txt", +"npc/010-2-16/_warps.txt", diff --git a/npc/010-2-16/_mobs.txt b/npc/010-2-16/_mobs.txt new file mode 100644 index 0000000..043b77b --- /dev/null +++ b/npc/010-2-16/_mobs.txt @@ -0,0 +1,14 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-16: Canyon Cave mobs +010-2-16,37,24,4,3 monster Desert Maggot 1083,2,35000,150000 +010-2-16,57,25,4,3 monster Desert Maggot 1083,2,35000,150000 +010-2-16,105,40,4,3 monster Desert Maggot 1083,2,35000,150000 +010-2-16,48,44,17,3 monster Snake 1122,4,35000,300000 +010-2-16,73,45,15,10 monster Black Scorpion 1074,3,35000,150000 +010-2-16,56,52,6,4 monster Blub 1008,2,35000,150000 +010-2-16,0,0,0,0 monster Cave Maggot 1027,30,35000,150000 +010-2-16,48,37,18,9 monster Angry Scorpion 1131,9,35000,150000 +010-2-16,46,29,10,1 monster Black Scorpion 1074,1,35000,150000 +010-2-16,80,39,21,13 monster Mountain Snake 1123,2,35000,150000 +010-2-16,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 +010-2-16,66,48,5,2 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/010-2-16/_warps.txt b/npc/010-2-16/_warps.txt new file mode 100644 index 0000000..faa3c38 --- /dev/null +++ b/npc/010-2-16/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-16: Canyon Cave warps +010-2-16,37,20,0 warp #010-2-16_37_20 0,0,010-2,219,37 +010-2-16,57,21,0 warp #010-2-16_57_21 0,0,010-2,232,36 +010-2-16,105,45,0 warp #010-2-16_105_45 0,0,010-2,242,39 diff --git a/npc/010-2-2/_import.txt b/npc/010-2-2/_import.txt new file mode 100644 index 0000000..597193e --- /dev/null +++ b/npc/010-2-2/_import.txt @@ -0,0 +1,3 @@ +// Map 010-2-2: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-2/_warps.txt", diff --git a/npc/010-2-2/_warps.txt b/npc/010-2-2/_warps.txt new file mode 100644 index 0000000..d9e228d --- /dev/null +++ b/npc/010-2-2/_warps.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-2: Canyon Cave warps +010-2-2,94,20,0 warp #010-2-2_94_20 0,0,010-2,249,46 +010-2-2,84,72,0 warp #010-2-2_84_72 0,0,010-2-11,51,30 +010-2-2,64,48,0 warp #010-2-2_64_48 0,0,010-2,210,69 +010-2-2,33,82,0 warp #010-2-2_33_82 0,0,010-2,181,99 +010-2-2,39,63,0 warp #010-2-2_39_63 0,0,010-1-9,60,62 +010-2-2,80,29,0 warp #010-2-2_80_29 0,0,010-2,232,46 +010-2-2,77,52,0 warp #010-2-2_77_52 0,0,010-2,224,64 +010-2-2,70,56,0 warp #010-2-2_70_56 0,0,010-2,223,82 diff --git a/npc/010-2-3/_import.txt b/npc/010-2-3/_import.txt new file mode 100644 index 0000000..ead458c --- /dev/null +++ b/npc/010-2-3/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-3: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-3/_mobs.txt", +"npc/010-2-3/_warps.txt", diff --git a/npc/010-2-3/_mobs.txt b/npc/010-2-3/_mobs.txt new file mode 100644 index 0000000..3893c5c --- /dev/null +++ b/npc/010-2-3/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-3: Canyon Cave mobs +010-2-3,78,25,4,3 monster Desert Maggot 1083,2,35000,150000 +010-2-3,55,44,4,3 monster Desert Maggot 1083,2,35000,150000 +010-2-3,49,51,4,3 monster Desert Maggot 1083,2,35000,150000 +010-2-3,0,0,0,0 monster Cave Maggot 1027,35,35000,150000 +010-2-3,69,63,4,3 monster Little Blub 1008,3,35000,150000 +010-2-3,71,47,20,26 monster Snake 1122,8,35000,150000 +010-2-3,53,50,23,23 monster Mountain Snake 1123,6,35000,150000 +010-2-3,54,27,4,3 monster Desert Maggot 1083,2,35000,150000 +010-2-3,0,0,0,0 monster Topaz Bif 1102,2,35000,150000 +010-2-3,69,67,4,7 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/010-2-3/_warps.txt b/npc/010-2-3/_warps.txt new file mode 100644 index 0000000..21c0565 --- /dev/null +++ b/npc/010-2-3/_warps.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-3: Canyon Cave warps +010-2-3,78,29,0 warp #010-2-3_78_29 0,0,010-2,204,96 +010-2-3,55,48,0 warp #010-2-3_55_48 0,0,010-2,175,112 +010-2-3,49,55,0 warp #010-2-3_49_55 0,0,010-2,168,120 +010-2-3,35,67,0 warp #010-2-3_35_67 0,0,010-2-10,53,25 +010-2-3,54,23,0 warp #010-2-3_54_23 0,0,010-2,171,91 diff --git a/npc/010-2-4/_import.txt b/npc/010-2-4/_import.txt new file mode 100644 index 0000000..2faee43 --- /dev/null +++ b/npc/010-2-4/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-4: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-4/_mobs.txt", +"npc/010-2-4/_warps.txt", diff --git a/npc/010-2-4/_mobs.txt b/npc/010-2-4/_mobs.txt new file mode 100644 index 0000000..adf0970 --- /dev/null +++ b/npc/010-2-4/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-4: Canyon Cave mobs +010-2-4,0,0,0,0 monster Cave Maggot 1027,23,35000,150000 +010-2-4,48,38,7,3 monster Bat 1039,1,35000,150000 +010-2-4,45,38,13,5 monster Cave Snake 1035,4,35000,150000 +010-2-4,45,35,14,14 monster Angry Scorpion 1131,20,35000,150000 +010-2-4,38,39,8,8 monster Old Snake 1199,1,35000,150000 +010-2-4,0,0,0,0 monster Small Topaz Bif 1101,1,35000,150000 diff --git a/npc/010-2-4/_warps.txt b/npc/010-2-4/_warps.txt new file mode 100644 index 0000000..ca4834a --- /dev/null +++ b/npc/010-2-4/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-4: Canyon Cave warps +010-2-4,41,27,0 warp #010-2-4_41_27 0,0,010-2-9,41,42 +010-2-4,58,49,0 warp #010-2-4_58_49 0,0,004-2-9,46,33 +010-2-4,53,20,0 warp #010-2-4_53_20 0,0,010-1-10,33,34 diff --git a/npc/010-2-5/_import.txt b/npc/010-2-5/_import.txt new file mode 100644 index 0000000..dba92ff --- /dev/null +++ b/npc/010-2-5/_import.txt @@ -0,0 +1,3 @@ +// Map 010-2-5: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-5/_warps.txt", diff --git a/npc/010-2-5/_warps.txt b/npc/010-2-5/_warps.txt new file mode 100644 index 0000000..dd9110c --- /dev/null +++ b/npc/010-2-5/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-5: Canyon Cave warps +010-2-5,33,28,0 warp #010-2-5_33_28 0,0,010-2,58,87 +010-2-5,52,56,0 warp #010-2-5_52_56 0,0,010-1-6,52,24 +010-2-5,47,20,0 warp #010-2-5_47_20 0,0,010-2,62,74 diff --git a/npc/010-2-6/_import.txt b/npc/010-2-6/_import.txt new file mode 100644 index 0000000..8750b55 --- /dev/null +++ b/npc/010-2-6/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-6: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-6/_mobs.txt", +"npc/010-2-6/_warps.txt", diff --git a/npc/010-2-6/_mobs.txt b/npc/010-2-6/_mobs.txt new file mode 100644 index 0000000..e48aae1 --- /dev/null +++ b/npc/010-2-6/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-6: Canyon Cave mobs +010-2-6,0,0,0,0 monster Cave Maggot 1027,40,35000,150000 +010-2-6,43,38,12,14 monster Mountain Snake 1123,20,35000,150000 +010-2-6,48,46,15,6 monster Snake 1122,7,35000,150000 +010-2-6,0,0,0,0 monster Big Topaz Bif 1103,1,35000,150000 diff --git a/npc/010-2-6/_warps.txt b/npc/010-2-6/_warps.txt new file mode 100644 index 0000000..88b827c --- /dev/null +++ b/npc/010-2-6/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-6: Canyon Cave warps +010-2-6,41,20,0 warp #010-2-6_41_20 0,0,010-2,97,64 +010-2-6,60,59,0 warp #010-2-6_60_59 0,0,010-1-1,167,44 +010-2-6,41,29,0 warp #010-2-6_41_29 0,0,010-2-7,35,27 diff --git a/npc/010-2-7/_import.txt b/npc/010-2-7/_import.txt new file mode 100644 index 0000000..a52c6f7 --- /dev/null +++ b/npc/010-2-7/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-7: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-7/_mobs.txt", +"npc/010-2-7/_warps.txt", diff --git a/npc/010-2-7/_mobs.txt b/npc/010-2-7/_mobs.txt new file mode 100644 index 0000000..9d98ffd --- /dev/null +++ b/npc/010-2-7/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-7: Canyon Cave mobs +010-2-7,41,26,11,3 monster Mountain Snake 1123,3,35000,150000 +010-2-7,0,0,0,0 monster Cave Maggot 1027,22,35000,150000 +010-2-7,41,32,11,4 monster Snake 1122,3,35000,150000 +010-2-7,47,38,9,7 monster Cave Snake 1035,11,35000,150000 +010-2-7,37,33,7,2 monster Black Scorpion 1074,2,35000,150000 +010-2-7,0,0,0,0 monster Plushroom Field 1011,2,35000,150000 diff --git a/npc/010-2-7/_warps.txt b/npc/010-2-7/_warps.txt new file mode 100644 index 0000000..f4af0ad --- /dev/null +++ b/npc/010-2-7/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-7: Canyon Cave warps +010-2-7,53,49,0 warp #010-2-7_53_49 0,0,010-1-7,47,21 +010-2-7,35,28,0 warp #010-2-7_35_28 0,0,010-2-6,41,30 diff --git a/npc/010-2-8/_import.txt b/npc/010-2-8/_import.txt new file mode 100644 index 0000000..754c02d --- /dev/null +++ b/npc/010-2-8/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-8: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-8/_mobs.txt", +"npc/010-2-8/_warps.txt", diff --git a/npc/010-2-8/_mobs.txt b/npc/010-2-8/_mobs.txt new file mode 100644 index 0000000..b994ae7 --- /dev/null +++ b/npc/010-2-8/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-8: Canyon Cave mobs +010-2-8,0,0,0,0 monster Cave Maggot 1027,4,35000,150000 +010-2-8,37,23,4,2 monster Maggot 1030,2,35000,150000 +010-2-8,32,60,4,2 monster Maggot 1030,2,35000,150000 diff --git a/npc/010-2-8/_warps.txt b/npc/010-2-8/_warps.txt new file mode 100644 index 0000000..0399432 --- /dev/null +++ b/npc/010-2-8/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-8: Canyon Cave warps +010-2-8,32,63,0 warp #010-2-8_32_63 0,0,004-2,68,125 +010-2-8,37,20,0 warp #010-2-8_37_20 0,0,004-2,73,88 diff --git a/npc/010-2-9/_import.txt b/npc/010-2-9/_import.txt new file mode 100644 index 0000000..2310e8f --- /dev/null +++ b/npc/010-2-9/_import.txt @@ -0,0 +1,4 @@ +// Map 010-2-9: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2-9/_mobs.txt", +"npc/010-2-9/_warps.txt", diff --git a/npc/010-2-9/_mobs.txt b/npc/010-2-9/_mobs.txt new file mode 100644 index 0000000..6f4826b --- /dev/null +++ b/npc/010-2-9/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-9: Canyon Cave mobs +010-2-9,39,30,4,3 monster Little Blub 1007,7,35000,150000 +010-2-9,39,31,8,7 monster Bat 1039,7,35000,150000 +010-2-9,39,31,9,5 monster Giant Maggot 1031,9,35000,150000 +010-2-9,0,0,0,0 monster Cave Maggot 1027,20,35000,150000 +010-2-9,46,29,1,5 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/010-2-9/_warps.txt b/npc/010-2-9/_warps.txt new file mode 100644 index 0000000..15ceb7d --- /dev/null +++ b/npc/010-2-9/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2-9: Canyon Cave warps +010-2-9,41,43,0 warp #010-2-9_41_43 0,0,010-2-4,41,28 +010-2-9,36,20,0 warp #010-2-9_36_20 0,0,010-2-14,39,55 diff --git a/npc/010-2/_config.txt b/npc/010-2/_config.txt new file mode 100644 index 0000000..f105339 --- /dev/null +++ b/npc/010-2/_config.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2: Desert Mountains conf + +010-2,209,70,0 script #010-2_209_70 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=2; + end; +} diff --git a/npc/010-2/_import.txt b/npc/010-2/_import.txt new file mode 100644 index 0000000..dcd05d4 --- /dev/null +++ b/npc/010-2/_import.txt @@ -0,0 +1,6 @@ +// Map 010-2: Desert Mountains +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-2/_config.txt", +"npc/010-2/_mobs.txt", +"npc/010-2/_warps.txt", +"npc/010-2/sawis.txt", diff --git a/npc/010-2/_mobs.txt b/npc/010-2/_mobs.txt new file mode 100644 index 0000000..75679a8 --- /dev/null +++ b/npc/010-2/_mobs.txt @@ -0,0 +1,20 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2: Desert Mountains mobs +010-2,119,100,7,2 monster Snake 1122,2,35000,300000 +010-2,161,76,3,9 monster Mountain Snake 1123,7,35000,300000 +010-2,96,120,3,2 monster Desert Bandit 1124,1,35000,300000 +010-2,107,111,3,2 monster Desert Bandit 1124,1,35000,300000 +010-2,123,108,3,2 monster Desert Bandit 1124,2,35000,300000 +010-2,139,100,6,5 monster Desert Bandit 1124,3,35000,300000 +010-2,157,55,7,5 monster Desert Bandit 1124,3,35000,300000 +010-2,148,93,7,5 monster Sarracenus 1125,2,35000,300000 +010-2,175,30,46,9 monster Desert Maggot 1083,12,35000,150000 +010-2,157,60,9,20 monster Desert Maggot 1083,8,35000,150000 +010-2,147,82,3,9 monster Mountain Snake 1123,6,35000,300000 +010-2,47,105,3,9 monster Mountain Snake 1123,7,35000,300000 +010-2,76,118,3,8 monster Mountain Snake 1123,2,35000,300000 +010-2,176,87,3,9 monster Mountain Snake 1123,3,35000,300000 +010-2,169,91,3,9 monster Snake 1122,4,35000,300000 +010-2,151,78,28,45 monster Desert Maggot 1083,35,35000,150000 +010-2,85,113,30,17 monster Desert Maggot 1083,20,35000,150000 +010-2,220,74,39,56 monster Desert Maggot 1083,24,35000,150000 diff --git a/npc/010-2/_warps.txt b/npc/010-2/_warps.txt new file mode 100644 index 0000000..2b6f864 --- /dev/null +++ b/npc/010-2/_warps.txt @@ -0,0 +1,45 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-2: Desert Mountains warps +010-2,190,108,0 warp #010-2_190_108 0,0,010-1-2,120,109 +010-2,149,119,0 warp #010-2_149_119 0,0,010-1-2,94,96 +010-2,125,120,0 warp #010-2_125_120 0,0,010-1-2,77,56 +010-2,112,101,0 warp #010-2_112_101 0,0,010-1-2,64,46 +010-2,185,64,0 warp #010-2_185_64 0,0,010-1-9,69,59 +010-2,194,55,0 warp #010-2_194_55 0,0,010-1-9,60,32 +010-2,249,47,0 warp #010-2_249_47 0,0,010-2-2,94,21 +010-2,210,68,0 warp #010-2_210_68 0,0,010-2-2,64,49 +010-2,224,63,0 warp #010-2_224_63 0,0,010-2-2,59,49 +010-2,223,81,0 warp #010-2_223_81 0,0,010-2-2,70,57 +010-2,230,115,0 warp #010-2_230_115 0,0,010-2-11,45,53 +010-2,175,111,0 warp #010-2_175_111 0,0,010-2-3,55,47 +010-2,168,119,0 warp #010-2_168_119 0,0,010-2-3,49,54 +010-2,232,45,0 warp #010-2_232_45 0,0,010-2-2,80,28 +010-2,236,52,0 warp #010-2_236_52 0,0,010-2-3,77,28 +010-2,204,95,0 warp #010-2_204_95 0,0,010-2-3,78,28 +010-2,142,109,0 warp #010-2_142_109 0,0,010-1-9,32,31 +010-2,160,66,0 warp #010-2_160_66 0,0,010-1-2,87,23 +010-2,173,78,0 warp #010-2_173_78 0,0,010-1-2,118,28 +010-2,192,107,0 warp #010-2_192_107 0,0,010-2-13,33,36 +010-2,218,118,0 warp #010-2_218_118 0,0,010-1-9,33,59 +010-2,237,61,0 warp #010-2_237_61 0,0,010-1-9,72,21 +010-2,106,127,0 warp #010-2_106_127 0,0,010-1-2,62,69 +010-2,86,111,0 warp #010-2_86_111 0,0,010-1-1,124,55 +010-2,241,39,0 warp #010-2_241_39 0,0,010-2-16,105,44 +010-2,232,37,0 warp #010-2_232_37 0,0,010-2-16,57,22 +010-2,219,38,0 warp #010-2_219_38 0,0,010-2-16,37,21 +010-2,168,100,0 warp #010-2_168_100 0,0,010-1-1,180,25 +010-2,131,112,0 warp #010-2_131_112 0,0,010-1-1,183,52 +010-2,134,113,0 warp #010-2_134_113 0,0,010-1-1,188,52 +010-2,121,97,0 warp #010-2_121_97 0,0,010-1-1,181,26 +010-2,97,63,0 warp #010-2_97_63 0,0,010-2-6,41,21 +010-2,62,73,0 warp #010-2_62_73 0,0,010-2-5,47,21 +010-2,48,89,0 warp #010-2_48_89 0,0,010-2-5,33,29 +010-2,37,96,0 warp #010-2_37_96 0,0,010-1-1,47,33 +010-2,34,101,0 warp #010-2_34_101 0,0,010-1-1,43,41 +010-2,169,19,0 warp #010-2_169_19 2,0,009-1,56,108 +010-2,155,100,0 warp #010-2_155_100 0,0,004-1,33,110 +010-2,93,123,0 warp #010-2_93_123 0,0,010-3,34,21 +010-2,40,78,0 warp #010-2_40_78 0,0,010-1-1,54,41 +010-2,171,90,0 warp #010-2_171_90 0,0,010-2-3,54,24 +010-2,181,98,0 warp #010-2_181_98 0,0,010-2-2,33,81 +010-2,207,39,0 warp #010-2_207_39 0,0,010-1-9,73,21 diff --git a/npc/010-2/sawis.txt b/npc/010-2/sawis.txt new file mode 100644 index 0000000..dc3c9db --- /dev/null +++ b/npc/010-2/sawis.txt @@ -0,0 +1,134 @@ +// TMW2 Script +// Author: +// Saulc +// Reviewer: +// Jesusalva +// Description: +// craft desert shirt. Reference to player msawis +// id:193 HalinarzoQuest_Sawis + +010-2,138,122,0 script Sawis NPC_PLAYER,{ + .@q = getq(HalinarzoQuest_Sawis); + if (BaseLevel < 37) goto L_TooWeak; + if (.@q == 1) goto L_Check; + if (.@q == 2) goto L_Complete; + +L_GiveTask: + mesn; + mesq lg("Hello, wanderer!"); + next; + mesq l("No one visit me, I'm in peace there."); + next; + + menu + l("What are you doing alone in this desert?"), L_Quest, + l("Don't speak to me crazy guy!"), L_Quit; + +L_Quest: + mes ""; + mesn; + mesq l("I do some research about evolution of snake skin subject to hard sunlight."); + next; + mesn; + mesq l("Then I craft some new type of strong desert shirt, to protect users from snakes and sunlight."); + next; + mesn; + mesq l("If you bring me some items, I can craft one for you. You will be my test subject."); + next; + + menu + l("Yeah, sure? What do you need?"), L_Start, + l("Nah, sorry, everything is good with me."), L_Quit; + +L_Start: + setq HalinarzoQuest_Sawis, 1; + mes ""; + mesn; + mesq l("Ok, what I need is:"); + goto L_List; + +L_Quit: + mes ""; + mesn; + mesq l("Alright."); + close; + +L_List: + mes ""; + mesn; + mes l("Here's what I need:"); + mes l("@@/1 @@", countitem(LeatherShirt), getitemlink(LeatherShirt)); + mes l("@@/1 @@", countitem(CottonCloth), getitemlink(CottonCloth)); + mes l("@@/1 @@", countitem(CaveSnakeSkin), getitemlink(CaveSnakeSkin)); + mes l("@@/3 @@", countitem(SnakeSkin), getitemlink(SnakeSkin)); + close; + +L_Check: + mesn; + mesq l("Did you brought me everything I asked for?"); + next; + menu + l("Yes!"), L_Give, + l("Sorry, I forgot what you need!"), L_List, + l("No!"), L_Quit; + +L_Give: + if ( + countitem(LeatherShirt) < 1 || + countitem(CottonCloth) < 1 || + countitem(CaveSnakeSkin) < 1 || + countitem(SnakeSkin) < 3 + ) goto L_Lying; + + inventoryplace DesertShirt, 1; + + delitem(LeatherShirt, 1); + delitem(CottonCloth, 1); + delitem(CaveSnakeSkin, 1); + delitem(SnakeSkin, 3); + + getitem(DesertShirt, 1); + getexp(5000, 30); + setq(HalinarzoQuest_Sawis, 2); + + mes ""; + mesn; + mesq l("I hope this is your size."); + close; + +L_Complete: + mesn; + mesq l("As I see, you stay alive!"); + next; + mesn; + mesq l("I must keep notes of this."); + close; + +// Funnier to write than to read, but the player lied. :angel: +L_Lying: + mesn; + mesq l("No no no, that's wrong."); + next; + mesc l("Actually, you could be really useful testing Snake Poison. What do you think about that?"); + next; + goto L_List; + +L_TooWeak: + mesn; + mesq l("What are you doing here? This place is too dangerous for you!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADMIDDLE, DesertShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .sex = G_MALE; + .distance = 5; + npcsit; + + end; +} diff --git a/npc/010-3/_import.txt b/npc/010-3/_import.txt new file mode 100644 index 0000000..53c9f46 --- /dev/null +++ b/npc/010-3/_import.txt @@ -0,0 +1,4 @@ +// Map 010-3: Canyon Stone +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-3/_mobs.txt", +"npc/010-3/_warps.txt", diff --git a/npc/010-3/_mobs.txt b/npc/010-3/_mobs.txt new file mode 100644 index 0000000..371bab0 --- /dev/null +++ b/npc/010-3/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-3: Canyon Stone mobs +010-3,33,24,2,2 monster Plushroom Field 1011,1,35000,150000 diff --git a/npc/010-3/_warps.txt b/npc/010-3/_warps.txt new file mode 100644 index 0000000..e70381e --- /dev/null +++ b/npc/010-3/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-3: Canyon Stone warps +010-3,33,29,0 warp #010-3_33_29 0,0,010-1,123,28 +010-3,34,20,0 warp #010-3_34_20 0,0,010-2,94,123 diff --git a/npc/010-4-1/_import.txt b/npc/010-4-1/_import.txt new file mode 100644 index 0000000..c822ad4 --- /dev/null +++ b/npc/010-4-1/_import.txt @@ -0,0 +1,4 @@ +// Map 010-4-1: Katze Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-4-1/_warps.txt", +"npc/010-4-1/katze.txt", diff --git a/npc/010-4-1/_warps.txt b/npc/010-4-1/_warps.txt new file mode 100644 index 0000000..a819a03 --- /dev/null +++ b/npc/010-4-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-4-1: Katze Cave warps +010-4-1,41,57,0 warp #010-4-1_41_57 0,0,010-1-1,60,85 diff --git a/npc/010-4-1/katze.txt b/npc/010-4-1/katze.txt new file mode 100644 index 0000000..3976110 --- /dev/null +++ b/npc/010-4-1/katze.txt @@ -0,0 +1,15 @@ +// TMW2/LOF Script. +// Author: +// Jesusalva +// Description: +// THIS IS A PLACEHOLDER + +010-4-1,44,30,0 script Katze NPC_KATZE,{ + npctalk3 l("Meow."); + end; +OnInit: + .distance=5; + .sex = G_OTHER; + end; +} + diff --git a/npc/010-4-2/_import.txt b/npc/010-4-2/_import.txt new file mode 100644 index 0000000..72f4774 --- /dev/null +++ b/npc/010-4-2/_import.txt @@ -0,0 +1,4 @@ +// Map 010-4-2: Canyon Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/010-4-2/_mobs.txt", +"npc/010-4-2/_warps.txt", diff --git a/npc/010-4-2/_mobs.txt b/npc/010-4-2/_mobs.txt new file mode 100644 index 0000000..a42124b --- /dev/null +++ b/npc/010-4-2/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-4-2: Canyon Cave mobs +010-4-2,0,0,0,0 monster Lava Slime 1097,30,35000,150000 +010-4-2,87,39,4,2 monster Maggot 1030,2,35000,150000 diff --git a/npc/010-4-2/_warps.txt b/npc/010-4-2/_warps.txt new file mode 100644 index 0000000..c455441 --- /dev/null +++ b/npc/010-4-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 010-4-2: Canyon Cave warps +010-4-2,37,62,0 warp #010-4-2_37_62 0,0,004-2-3,74,98 +010-4-2,87,42,0 warp #010-4-2_87_42 0,0,004-2,144,26 diff --git a/npc/011-1/_import.txt b/npc/011-1/_import.txt new file mode 100644 index 0000000..258abb6 --- /dev/null +++ b/npc/011-1/_import.txt @@ -0,0 +1,6 @@ +// Map 011-1: Depleted Mana Stones Mine +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/011-1/_mobs.txt", +"npc/011-1/_warps.txt", +"npc/011-1/manastone.txt", +"npc/011-1/treasure.txt", diff --git a/npc/011-1/_mobs.txt b/npc/011-1/_mobs.txt new file mode 100644 index 0000000..6ab25f2 --- /dev/null +++ b/npc/011-1/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 011-1: Depleted Mana Stones Mine mobs +011-1,99,100,79,79 monster Mana Ghost 1068,40,20000,20000 +011-1,98,101,79,79 monster Fire Goblin 1067,90,60000,60000 +011-1,100,99,79,79 monster Lava Slime 1097,30,60000,60000 +011-1,101,98,79,79 monster Red Slime 1092,100,60000,60000 +011-1,97,102,79,79 monster Mineral Bif 1058,20,60000,60000 diff --git a/npc/011-1/_warps.txt b/npc/011-1/_warps.txt new file mode 100644 index 0000000..bba2aa0 --- /dev/null +++ b/npc/011-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 011-1: Depleted Mana Stones Mine warps +011-1,177,178,0 warp #011-1_177_178 0,0,009-1,119,41 diff --git a/npc/011-1/manastone.txt b/npc/011-1/manastone.txt new file mode 100644 index 0000000..3d8df2b --- /dev/null +++ b/npc/011-1/manastone.txt @@ -0,0 +1,136 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// The last Mana Stone in the whole world, which is not owned by the Magic Council +// or the Monster King. +// It actually hates (or rather, fears) everybody, and is not always willing to +// talk with people. May hide itself within the walls during these occasions. +// It may not hate some very specific things which are lore-related. +// +// Notes: During sieges, Monster King and Human Council apparitions, it may hide +// itself. +// +// Variables: +// $MANA_BINT => Base Intelligence for Mana Stone +// $MANA_BLVL => Base Level for Mana Stone +// $MANA_JLVL => Base Job Level for Mana Stone +// .int => Int Increment +// .lvl => Lvl Increment +// .jlvl => Jlv Increment + +011-1,0,0,0 script Mana Stone NPC_MANA_STONE,{ + function dearLord; + + if (BaseLevel < $MANA_BLVL) goto L_NotWorthy; + mesn; + mes l("The mighty Mana Stone does not reacts against you."); + mes l("Although this particular one seems to hate everyone and everything, it recognizes your strength."); + mes l("If you fell ready, perhaps you should touch it?"); + mes ""; + menu + l("Touch it!"), L_Level, + l("Take it!"), L_NotWorthy2, + l("Break it!"), L_NotWorthy2, + l("Leave it alone!"), -; + close; + + + +L_NotWorthy2: + if (is_gm()) movenpc .name$, 20+rand(150), 20+rand(150); + if (is_gm()) close; + npctalk3 l("You are not worthy!"); + percentheal -20, -50; + if (!MAGIC_LVL) + dispbottom l("I should train my intelligence, have full MP, and don't neglect even Job Level."); + end; + +L_Level: + // See functions/util.txt for *mstone() details + if (mstone(0)) goto L_LevelUp; + if (mstone(1)) goto L_LevelUp; + if (mstone(2)) goto L_LevelUp; + if (mstone(3)) goto L_LevelUp; + if (mstone(4)) goto L_LevelUp; + if (mstone(5)) goto L_LevelUp; + if (mstone(6)) goto L_LevelUp; + if (MAGIC_LVL >= 7) dearLord(); + if (MAGIC_LVL >= 7) npctalk3 l("You already got all power I could grant you!"); + if (is_gm()) percentheal -20, -50; + if (MAGIC_LVL >= 7 || is_gm()) close; + +L_NotWorthy: + if (readparam(Sp) != readparam(MaxSp)) + dispbottom l("I must have full MP to touch it... Which I don't."); + else + dispbottom l("I should train my intelligence, and level up, both my base as my Job Level."); + npctalk3 l("You are not worthy!"); + percentheal min(-10, -70+BaseLevel), min(-10, -100+BaseLevel); + end; + +L_LevelUp: + mes ""; + mes l("A great rush of mana flows though you."); + if (!MAGIC_LVL) mes l("Magic Power is granted to you, but you die from it."); + if (MAGIC_LVL) mes l("More Magic Power is granted to you, but you die from it."); + MAGIC_LVL = MAGIC_LVL+1; + sk_lvup(AL_DP); + // No penalty for death provoked by Mana Stone, see npc/001-8/hub.txt for more info + sc_start SC_CASH_DEATHPENALTY, 1000, 1; + /* + @deathpenalty_realvalue=readparam(BaseExp); + @deathpenalty_realvaljob=readparam(JobExp); + @deathpenalty_override=1; + */ + die(); + close; + +function dearLord { + if ($MANASTONE_WINNER$ == "") { + $MANASTONE_WINNER$=strcharinfo(0); + channelmes("#world", $MANASTONE_WINNER$+" is the first player to reach "+get_race()+"'s magic limit since the Monster King! %%N"); + announce "All hail ##B"+$MANASTONE_WINNER$+"##b, first to reach "+get_race()+"'s magic limit since the Monster King! %%N", bc_all|bc_npc; + getexp 0, 2000; + getitem SupremeGift, 1; + mesc l("CONGRATULATIONS! You are the first player to harness all power the Mana Stone would willingly give!"), 2; + mesc l("You just gained a Supreme Gift, and 2000 Job Exp for your bravery!"), 2; + next; + } + return; +} + +OnInit: + movenpc .name$, 20+rand(150), 20+rand(150); + .sex = G_OTHER; + .distance = 6; + end; + +OnClock0030: +OnClock0120: +OnClock0210: +OnClock0300: +OnClock0450: +OnClock0540: +OnClock0630: +OnClock0720: +OnClock0810: +OnClock0900: +OnClock1050: +OnClock1140: +OnClock1230: +OnClock1320: +OnClock1410: +OnClock1500: +OnClock1650: +OnClock1740: +OnClock1830: +OnClock1920: +OnClock2010: +OnClock2100: +OnClock2250: +OnClock2340: + npctalk "Those who are worthy, may get my magic. For the others, death awaits!"; + movenpc .name$, 20+rand(150), 20+rand(150); + end; +} diff --git a/npc/011-1/treasure.txt b/npc/011-1/treasure.txt new file mode 100644 index 0000000..8b2eb8e --- /dev/null +++ b/npc/011-1/treasure.txt @@ -0,0 +1,9 @@ +// TMW2 Script + +// (Random) Treasure Chest +// Authored by Jesusalva +// Regenerates every 6 hours + +011-1,0,0,0 duplicate(#chest_00710) #chest_01110 NPC_CHEST +011-1,0,0,0 duplicate(#chest_00710) #chest_01111 NPC_CHEST + diff --git a/npc/011-2/_import.txt b/npc/011-2/_import.txt new file mode 100644 index 0000000..e6b4c1d --- /dev/null +++ b/npc/011-2/_import.txt @@ -0,0 +1,4 @@ +// Map 011-2: Supreme Mana Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/011-2/_mobs.txt", +"npc/011-2/_warps.txt", diff --git a/npc/011-2/_mobs.txt b/npc/011-2/_mobs.txt new file mode 100644 index 0000000..81fe460 --- /dev/null +++ b/npc/011-2/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 011-2: Supreme Mana Cave mobs +011-2,55,58,29,22 monster Cave Snake 1035,12,35000,150000 +011-2,70,40,3,1 monster Small Topaz Bif 1101,1,35000,150000 +011-2,54,54,54,54 monster Cave Maggot 1027,20,40000,200000 +011-2,43,66,9,6 monster Snake 1122,3,35000,150000 +011-2,46,40,4,4 monster Snake 1122,3,35000,150000 +011-2,84,56,9,9 monster Snake 1122,3,35000,150000 +011-2,60,46,29,22 monster Cave Maggot 1027,6,35000,150000 +011-2,55,48,29,22 monster Small Topaz Bif 1101,3,35000,150000 diff --git a/npc/011-2/_warps.txt b/npc/011-2/_warps.txt new file mode 100644 index 0000000..1d0b4e7 --- /dev/null +++ b/npc/011-2/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 011-2: Supreme Mana Cave warps +011-2,78,73,0 warp #011-2_78_73 0,0,004-2,100,90 +011-2,26,86,0 warp #011-2_26_86 0,0,004-2,84,78 +011-2,25,47,0 warp #011-2_25_47 0,0,010-1,93,67 +011-2,51,26,0 warp #011-2_51_26 0,0,004-2,116,35 diff --git a/npc/011-3/_import.txt b/npc/011-3/_import.txt new file mode 100644 index 0000000..b931fb1 --- /dev/null +++ b/npc/011-3/_import.txt @@ -0,0 +1,5 @@ +// Map 011-3: Eternal Swamps - Landbridge +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/011-3/_mobs.txt", +"npc/011-3/_warps.txt", +"npc/011-3/flood.txt", diff --git a/npc/011-3/_mobs.txt b/npc/011-3/_mobs.txt new file mode 100644 index 0000000..42dde23 --- /dev/null +++ b/npc/011-3/_mobs.txt @@ -0,0 +1,22 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 011-3: Eternal Swamps - Landbridge mobs +011-3,39,205,19,14 monster Angry Red Scorpion 1130,10,100000,30000 +011-3,40,206,19,14 monster Fire Goblin 1067,10,100000,30000 +011-3,38,207,19,14 monster Old Snake 1199,5,100000,30000 +011-3,38,174,18,13 monster Sarracenus 1125,7,100000,30000 +011-3,37,176,18,13 monster Black Scorpion 1074,4,100000,30000 +011-3,39,175,18,13 monster Snake 1122,6,100000,30000 +011-3,40,145,19,11 monster Snake 1122,3,100000,30000 +011-3,39,144,19,11 monster Sarracenus 1125,4,100000,30000 +011-3,38,146,19,11 monster Desert Bandit 1124,5,100000,30000 +011-3,40,101,36,26 monster Fire Fairy 1183,15,100000,30000 +011-3,39,99,36,26 monster Bandit 1124,13,100000,30000 +011-3,37,103,36,26 monster Robin Bandit 1153,7,100000,30000 +011-3,37,56,24,14 monster Sea Slime 1093,5,100000,30000 +011-3,39,55,24,14 monster Red Mushroom 1042,5,100000,30000 +011-3,38,54,24,14 monster Grass Snake 1169,4,100000,30000 +011-3,37,32,19,7 monster Sea Slime 1093,5,100000,30000 +011-3,39,32,19,7 monster Tipiou 1016,3,900000,300000 +011-3,38,31,19,7 monster Vicious Squirrel 1187,5,100000,30000 +011-3,51,113,3,4 monster Fire Fairy 1183,1,100000,30000 +011-3,30,89,3,4 monster Fire Fairy 1183,1,100000,30000 diff --git a/npc/011-3/_warps.txt b/npc/011-3/_warps.txt new file mode 100644 index 0000000..22caef9 --- /dev/null +++ b/npc/011-3/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 011-3: Eternal Swamps - Landbridge warps +011-3,37,20,0 warp #011-3_37_20 2,0,014-2,56,102 +011-3,37,221,0 warp #011-3_37_221 4,0,009-1,71,25 diff --git a/npc/011-3/flood.txt b/npc/011-3/flood.txt new file mode 100644 index 0000000..9353322 --- /dev/null +++ b/npc/011-3/flood.txt @@ -0,0 +1,117 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Controls Eternal Swamps. +// Contains functions to flood the map (hide the bridges - mask 1024 + mask 2) +// Flood happens at random and causes BluePar to be spawn. Runs every 6/4 minutes. +// At the 6th minute of an hour (6, 16, 26, 36, 46, 56) +// At the 2nd minute of an hour (2, 14 ... 44, 54) + +011-3,0,0,0 script #EternalSwampCore NPC_HIDDEN,{ + end; + +OnSwampMob: + end; + +OnInit: + addmapmask "011-3", MASK_SPECIAL; + bindatcmd "es_flood", "#EternalSwampCore::OnSkipFlood", 99, 99, 1; + end; + +OnMinute02: +OnMinute06: +OnMinute12: +OnMinute16: +OnMinute22: +OnMinute26: +OnMinute32: +OnMinute36: +OnMinute42: +OnMinute46: +OnMinute52: +OnMinute56: + // Check if auto-restart was scheduled + SchedRestart(); +OnSkipFlood: + if ($@GM_OVERRIDE) debugmes "[Flood] Cycle begin"; + .@fd=!(getmapmask("011-3")&MASK_SPECIAL); // .@fd - is flooded? + + if (.@fd) { + if ($@GM_OVERRIDE) debugmes "[Flood] UF - Unflooding"; + // If it is flooded, unflood it to prevent players getting struck for too long + killmonster("011-3", "#EternalSwampCore::OnSwampMob"); + addmapmask "011-3", MASK_SPECIAL; + //debugmes "[Flood] UF - Del Cells"; + delcells "ESwpBridge1"; + delcells "ESwpBridge2"; + delcells "ESwpBridge3"; + delcells "ESwpBridge4"; + delcells "ESwpBridge5"; + delcells "ESwpBridge6"; + delcells "ESwpBridge7"; + mapannounce "011-3", "Eternal Swamps: The flood ceases!",bc_all|bc_npc; + } else { + // 40% chances to flood, 75% during night + .@odds=40; + if (is_night()) + .@odds+=35; + + /* + // Bugfix + if ($@GM_OVERRIDE) + .@odds=100; + else + .@odds=0; + debugmes "[Flood] Analysis with %d odds to flood", .@odds; + */ + + // Maybe we should flood it + if (rand(0,100) < .@odds) { + if ($@GM_OVERRIDE) debugmes "[Flood] F - Flooding"; + removemapmask "011-3", MASK_SPECIAL; + + //debugmes "[Flood] F - Adding Cells"; + setcells "011-3", 37, 22, 39, 24, 1, "ESwpBridge1"; + setcells "011-3", 37, 34, 39, 41, 1, "ESwpBridge2"; + setcells "011-3", 31, 67, 33, 73, 1, "ESwpBridge3"; + setcells "011-3", 40, 96, 42, 102, 1, "ESwpBridge4"; + setcells "011-3", 38, 130, 40, 136, 1, "ESwpBridge5"; + setcells "011-3", 41, 157, 43, 163, 1, "ESwpBridge6"; + setcells "011-3", 36, 187, 38, 193, 1, "ESwpBridge7"; + + //debugmes "[Flood] F - Spawn and Announce"; + areamonster "011-3", 20, 20, 60, 220, "Bluepar", Bluepar, rand(8,26), "#EternalSwampCore::OnSwampMob"; + mapannounce "011-3", "Eternal Swamps: A flood starts!",bc_all|bc_npc; + + // TODO: Handle players in bridges + //debugmes "[Flood] F - Map Timer OK"; + maptimer("011-3", 100, "#EternalSwampCore::OnBridgeDown"); + } + + } + if ($@GM_OVERRIDE) debugmes "[Flood] Cycle finished"; + end; + +// Fix players struck by setcells +OnBridgeDown: + //debugmes "[Flood] [OnBD] Bridge is Down"; + if (isin("011-3", 37, 22, 39, 24)) + slide 38, 21; + else if (isin("011-3", 37, 34, 39, 41)) + slide 38, 33; + else if (isin("011-3", 31, 67, 33, 73)) + slide 32, 65; + else if (isin("011-3", 40, 96, 42, 102)) + slide 41, 95; + else if (isin("011-3", 38, 130, 40, 136)) + slide 39, 128; + else if (isin("011-3", 41, 157, 43, 163)) + slide 42, 155; + else if (isin("011-3", 36, 187, 38, 193)) + slide 37, 185; + //debugmes "[Flood] [OnBD] Finished"; + + end; +} + diff --git a/npc/012-1/_import.txt b/npc/012-1/_import.txt new file mode 100644 index 0000000..57e4e73 --- /dev/null +++ b/npc/012-1/_import.txt @@ -0,0 +1,26 @@ +// Map 012-1: Hurnscald +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/012-1/_mobs.txt", +"npc/012-1/_warps.txt", +"npc/012-1/andrei.txt", +"npc/012-1/bot.txt", +"npc/012-1/dyrinthetraveler.txt", +"npc/012-1/estate.txt", +"npc/012-1/guards.txt", +"npc/012-1/gwendolyn.txt", +"npc/012-1/hinnak.txt", +"npc/012-1/igor.txt", +"npc/012-1/jack.txt", +"npc/012-1/locamit.txt", +"npc/012-1/luffyx.txt", +"npc/012-1/mahad.txt", +"npc/012-1/mapflags.txt", +"npc/012-1/milly.txt", +"npc/012-1/richard.txt", +"npc/012-1/ship.txt", +"npc/012-1/shoppakep.txt", +"npc/012-1/soul-menhir.txt", +"npc/012-1/statue.txt", +"npc/012-1/terezin.txt", +"npc/012-1/town.txt", +"npc/012-1/wateranimation.txt", diff --git a/npc/012-1/_mobs.txt b/npc/012-1/_mobs.txt new file mode 100644 index 0000000..12bd68b --- /dev/null +++ b/npc/012-1/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-1: Hurnscald mobs +012-1,90,63,35,14 monster Red Butterfly 1025,4,30000,90000 +012-1,90,62,36,15 monster Mana Bug 1075,5,30000,90000 +012-1,85,33,35,15 monster Pinkie 1132,8,30000,20000,Hinnak::OnKillPinkie +012-1,44,56,21,41 monster Clover Patch 1028,3,60000,90000 +012-1,135,58,7,29 monster Piousse 1003,6,40000,60000 +012-1,85,86,52,15 monster Silk Worm 1034,5,30000,25000 +012-1,115,69,1,1 monster Training Dummy 1021,1,10000,10000 +012-1,81,59,54,35 monster Squirrel 1032,12,30000,45000 diff --git a/npc/012-1/_warps.txt b/npc/012-1/_warps.txt new file mode 100644 index 0000000..2798abb --- /dev/null +++ b/npc/012-1/_warps.txt @@ -0,0 +1,13 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-1: Hurnscald warps +012-1,22,62,0 warp #012-1_22_62 0,3,014-3,205,92 +012-1,79,17,0 warp #012-1_79_17 3,0,014-1,78,97 +012-1,79,102,0 warp #012-1_79_102 3,0,014-2,178,22 +012-1,132,101,0 warp #012-1_132_101 2,0,014-2,232,21 +012-1,65,55,0 warp #012-1_65_55 0,0,012-2,36,46 +012-1,89,58,0 warp #012-1_89_58 0,0,012-3,41,45 +012-1,101,55,0 warp #012-1_101_55 0,0,012-4,35,30 +012-1,121,71,0 warp #012-1_121_71 0,0,012-5,34,36 +012-1,116,66,0 warp #012-1_116_66 0,0,012-5,23,26 +012-1,110,56,0 warp #012-1_110_56 0,0,012-6,58,68 +012-1,102,69,0 warp #012-1_102_69 0,0,012-7,35,64 diff --git a/npc/012-1/andrei.txt b/npc/012-1/andrei.txt new file mode 100644 index 0000000..0f984f7 --- /dev/null +++ b/npc/012-1/andrei.txt @@ -0,0 +1,181 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Legendary hero + +// I started with 'Greetings, citzen' and then I thought on 4144. +// He would never do such formal, hero-ish introduction. So I decided for something +// more realistic, which sounded like a human hero (ie. a normal player). + +012-1,75,58,0 script Andrei Sakar NPC_ANDREI,{ + // NLib_question ( ) + function NLib_question { + //freeloop(true); + do { + @qid=rand(0,7); + } while (! (2**@qid) & @sortable); + debugmes "Question "+@qid; + @sortable-=(2**@qid); + setarray @ans$, $@NLIBa1$[@qid], $@NLIBa2$[@qid], $@NLIBa3$[@qid], $@NLIBa4$[@qid]; + setarray @val, $@NLIBmr1[@qid], $@NLIBmr2[@qid], $@NLIBmr3[@qid], $@NLIBmr4[@qid]; + /* + @nm=15; + @i=0; + debugmes "BG WHILE S00002"; + while (@nm > 0) { + do { + @nai=rand(0,3); + } while ((2**@nai) & @nm); + debugmes "BG WS2 NODE"; + @nm-=@nai; + @ans$[@i]=@as$[@nai]; + @val[@i]=@nai; + } + debugmes "BG NO FREE LOOP"; + //freeloop(false); + */ + // Final: @ans$ ; @val ; @qid + // Question, menuret, QID + + mes ""; + addtimer(30000, "Andrei Sakar::OnTooLong"); + .@q=getq3(Q_NivalisLibday); + setq3 Q_NivalisLibday, .@q-7; + mesn; + mesq $@NLIBqs$[@qid]; + menuint + @ans$[0], @val[0], + @ans$[1], @val[1], + @ans$[2], @val[2], + @ans$[3], @val[3]; + mes ""; + deltimer("Andrei Sakar::OnTooLong"); + if (@menuret == 1) { + .@q=getq3(Q_NivalisLibday); + setq3 Q_NivalisLibday, .@q+7; + mesn; + mesq l("That's right."); + next; + } else { + mesn; + mesq l("Sorry, but that's wrong."); + close; + } + debugmes "BG END"; + // end + } + + mesn l("Andrei Sakar, Legendary Hero"); + if ($NLIB_DAY || ($@GM_OVERRIDE && is_admin())) goto L_Quizz; + +L_Default: + if (strcharinfo(0) == $MOST_HEROIC$) + mesq l("Hi, @@.", strcharinfo(0)); + else + mesq l("Hi."); + if ($GAME_STORYLINE >= 3 && $LIGHT_HOLDER$ == "") { + next; + mesn l("Andrei Sakar, Legendary Hero"); + mesq l("If %s meet the true form of the %s and prove to be a real hero, the sword will give itself for them. But if they fail to prove real heroism, the sword will leave after a while.", $WORLD_HERO$, getitemlink(Lightbringer)); + next; + mesn l("Andrei Sakar, Legendary Hero"); + mesq l("Not a single deed, but continued heroism. A real hero must be strong. Must help others. Must show up above others. Should have magic even if they don't use, and of course, the world should be in need of a hero. No one is born as hero, for heroes are forged, not born."); + next; + mesn l("Andrei Sakar, Legendary Hero"); + mesq l("And remember: If it was easy, I would be wielding it right now. Instead, I use a %s. So take courage.", getitemlink(ImmortalSword)); + } + close; + +L_Quizz: + .@q=getq3(Q_NivalisLibday); + if (.@q == 0) { + setq3 Q_NivalisLibday, 100; + .@q=100; + } else if (.@q > 100) { + goto L_Default; + } + + mesq l("We are assembling forces to take Nivalis back."); + mesq l("I'll make you five questions about lore and general knowledge. You'll have 30 seconds to read and answer each."); + mesq l("If you finish everything, and answer everything right, you may get a reward."); + mesq l("If you take too long you'll be penalized."); + mesq l("Do you want to start?"); + next; + if (askyesno() == ASK_NO) + close; + setarray @sortable, 255; + + NLib_question(); + NLib_question(); + NLib_question(); + NLib_question(); + NLib_question(); + + + mesn; + .@q=getq3(Q_NivalisLibday); + // You cannot go above 100 points. + if (.@q > 100) { + mesq l("...More bugs."); + mesc l("A bug was found. Aborting script."), 1; + close; + } else if (.@q == 100) { + mesq l("Congratulations. You really know about the world lore."); + getitem HastePotion, 2; + getitem StrengthPotion, 2; + getitem Bread, 5; + } else if (.@q > 90) { + mesq l("Outstanding. Congratulations."); + getitem HastePotion, 2; + getitem StrengthPotion, 2; + getitem Bread, 4; + } else if (.@q > 75) { + mesq l("Good, knowing the world lore is important."); + getitem HastePotion, 1; + getitem StrengthPotion, 1; + getitem Bread, 4; + } else if (.@q > 50) { + mesq l("Good job."); + getitem HastePotion, 1; + getitem StrengthPotion, 1; + getitem Bread, 2; + } else if (.@q > 25) { + mesq l("Well, that was bad, but at least you know a bit from story."); + getitem Bread, 2; + } else if (.@q > 0) { + mesq l("Terrible. You know almost nothing from world lore..."); + getitem Bread, 1; + } else { + mesq l("You really should read the dialogs."); + } + // If you got a negative value, this will default to 1. + getexp .@q*BaseLevel, .@q*JobLevel; + setq3 Q_NivalisLibday, 9999; + close; + +OnTooLong: + .@q=getq3(Q_NivalisLibday); + setq3 Q_NivalisLibday, .@q-21; + npctalk3 l("You took too long to answer."); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + /* Because I was lazy to calculate array size, it is hard-coded to eight questions numbered from 0 to 7. + setarray $@NLIBqs$; + setarray $@NLIBa1$; + setarray $@NLIBa2$; + setarray $@NLIBa3$; + setarray $@NLIBa4$; + setarray $@NLIBmr1; + setarray $@NLIBmr2; + setarray $@NLIBmr3; + setarray $@NLIBmr4; + */ + if ($FIRESOFSTEAM >= 10) + disablenpc .name$; + end; + +} diff --git a/npc/012-1/bot.txt b/npc/012-1/bot.txt new file mode 100644 index 0000000..3a6422b --- /dev/null +++ b/npc/012-1/bot.txt @@ -0,0 +1,137 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Introduces the new area ingame +// Quest: +// TODO: Quest, requires 1× LOFCoin (obtain for free with Pyndragon) +// Reward: Mouboo Figurine + +012-1,82,59,0 script LOFBot NPC_PLAYER,{ + .@q=getq(HurnscaldQuest_LOFPass); + if (rand2(1,5) % 2 == 1) { + mesn; + mesq l("Ah, @@ is sooo amazing!", $MOST_HEROIC$); + next; + } + mesn; + mesq l("Have you ever heard of the [@@http://landoffire.org/|Land Of Fire@@]? It is a really cool game which is being developed by Pyndragon and Pihro!"); + next; + mesn; + mesq l("They had a TMWA server, but the Monster King went hyperactive and... Well, it crashed here."); + if (.@q == 0) goto L_Quest; + if (.@q == 1) goto L_Report; + if (.@q == 2) goto L_Report; // To accomodate Pyn's node. Not required. + +L_Menu: + mes ""; + menu + l("How can I reach the Land Of Fire?"), L_Where, + l("Tell me about the Land Of Fire."), L_Fire, + l("What are the Transcendence Gates?"), L_Gates, + l("Thanks for the help."), L_Close; + +// TODO: Perhaps it is worth mentioning on JSaves Castle is a Mana Source and thus, magic&skills work better, and weapons work worse? +L_Where: + mes ""; + mesn; + mesq l("If you head west, you'll eventually reach the magical Kamelot Castle."); + next; + mesn; + mesq l("Inside it, there'll be a Transcendence Gate."); + next; + mesn; + mesq l("It is a really cool place. You must visit it someday!"); + next; + goto L_Menu; + +L_Fire: + mes ""; + mesn; + mesq l("The Land Of Fire Village was the result from the collapse of two worlds. It is a huge village."); + next; + mesq l("It have lots of lava caves, and some cool stuff, like the @@, can only be craft there.", getitemlink(SaviorArmor)); + next; + mesq l("Unique monsters can be found there, and people say about staffs which shoot raw death and doom from it!"); + next; + mesq l("It's self sufficient economically, and many fairies enjoy travelling there. In fact, rumors says the Fairy Kingdom is near it!"); + next; + goto L_Menu; + +L_Gates: + mes ""; + mesn; + mesq l("Ah, you'll find on the Land Of Fire Village four transcendence gates."); + next; + mesn; + mesq l("People from here always look dumbfolded when they walk past them and find themselves somewhere else."); + next; + mesn; + mesq l("There's usually not a lot of things to do past these gates, but some monsters which only lives on the Land of Fire can only be found there!"); + next; + goto L_Menu; + +L_Close: + closedialog; + npctalkonce l("Ah, @@ is so amazing!", $MOST_HEROIC$); + close; + +L_Quest: + mesn; + mesq l("Actually, could I ask you a favor?"); + next; + mesn; + mesq l("I want a %s so badly... Pyndragon, in the forge, the master blacksmith which makes powerful weapons for high levels, can give you one.", getitemlink(LOFCoin)); + next; + mesn; + mesq l("Just tell him that I want to reconsider, and that I want the souvenir he offered me when I moved to Hurnscald."); + next; + setq HurnscaldQuest_LOFPass, 1; + goto L_Menu; + +L_Report: + mes l("Did you brought me a %s?", getitemlink(LOFCoin)); + next; + if (askyesno() == ASK_YES) goto L_Check; + goto L_Menu; + +L_Check: + if (!countitem(LOFCoin)) goto L_NoItem; + inventoryplace MoubooFigurine, 1; + delitem LOFCoin, 1; + getitem MoubooFigurine, 1; + getexp 400, 20; + setq HurnscaldQuest_LOFPass, 3; + mesn; + mesq lg("Thanks, my friend."); + next; + mesn; + mesq l("Here, you can have this souvenir."); + mesc l("* Item obtained: %s", getitemlink(MoubooFigurine)); + next; + goto L_Menu; + +L_NoItem: + mesn; + mesq l("I want a %s so badly... Pyndragon, in the forge, the master blacksmith which makes powerful weapons for high levels, can give you one.", getitemlink(LOFCoin)); + next; + mesn; + mesq l("Just tell him that I want to reconsider, and that I want the souvenir he offered me when I moved to Hurnscald."); + next; + goto L_Menu; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, CenturionHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, SaviorArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, TulimsharGuardBoots); + setunitdata(.@npcId, UDT_WEAPON, BromenalPants); + setunitdata(.@npcId, UDT_HAIRSTYLE, 1); + setunitdata(.@npcId, UDT_HAIRCOLOR, 1); + npcsit; + + .sex = G_OTHER; + .distance = 5; + end; +} + diff --git a/npc/012-1/dyrinthetraveler.txt b/npc/012-1/dyrinthetraveler.txt new file mode 100644 index 0000000..318ad6a --- /dev/null +++ b/npc/012-1/dyrinthetraveler.txt @@ -0,0 +1,110 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// The Travelers travel around the world telling stories. + +012-1,119,52,0 script Dyrin The Traveler NPC_M_COINKEEPER,{ + + mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Wow! Are you @@? Everyone, in every city, talks about you!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + + mesq l("Hello. I am @@, and I am from a family of travellers. We travel though the whole world, looking for exotic goods.", .name$); + next; + mesq l("You can buy rare items with me, or I can tell you about different cities in our world."); + +L_Menu: + mes ""; + menu + l("I want to trade with you."), L_Trade, + l("Tell me about Halinarzo."), L_Halin, + l("Tell me about Hurnscald."), L_Hurns, + l("Tell me about Nivalis."), L_Nival, + l("Tell me about Artis."), L_Artis, + l("Sorry, I'll pass."), L_Close; + +L_Hurns: + mes ""; + mesn; + mesq l("Hurnscald was founded after Tulimshar, in more fertile lands. Their walls are not so sturdy as the ones of Tulimshar."); + next; + mesq l("Under the leadership of King Wusher, they were the first to accept immigrants from other races. You will find humans and non-humans there."); + next; + mesq l("The fertile climate is ideal for mushrooms. You can also find lots of wood."); + next; + mesq l("Their economy provide many edible items and potions."); + next; + goto L_Menu; + +L_Halin: + mes ""; + mesn; + mesq l("Halinarzo was founded to explore Mana Stones."); + next; + mesq l("You can find both huge swamps, as huge desertic areas near and on it."); + next; + mesq l("Lizards are the main monster found, and they steal gold from innocent bypassers."); + next; + mesq l("Without any mana stone left, and because the walls were not very strong, most of the city was destroyed."); + next; + mesq l("Unlike many other cities, if you want people in eternal need of items, there is a good place to look."); + next; + goto L_Menu; + +L_Nival: + mes ""; + mesn; + mesq l("Nivalis was the last human settlement built during the First Era."); + next; + mesq l("It's cold, harsh climate makes difficult to live there. It was founded by people thrown away from Tulimshar and Hunrscald for political reasons."); + next; + mesq l("The cold climate is ideal for slimes, penguins, and other icy creatures. You can find lots of... ice, of course!"); + next; + mesq l("Some items are only produced in Nivalis. After all, it is hard to work properly with ice in a desert!"); + next; + goto L_Menu; + +L_Artis: + mes ""; + mesn; + mesq l("Artis is a city port founded after the Great Fire on the other continent."); + next; + mesq l("People say it is the second biggest city from the world."); + next; + mesq l("Different kind of monsters live near the city. For example, blubs. I have no idea of what are those."); + next; + mesq l("People usually dock there when travelling to the second continent. Nothing exceptional about economy."); + next; + if ($FIRESOFSTEAM) { + mesq l("They used to export food and other things but there has been radio silence recentely; Which is why Andrei Sakar and a group of adventurers borrowed Nard's ship and went to investigate."); + next; + } + goto L_Menu; + +L_Trade: + mesn; + mesq l("Use your @@ as currency!", getitemlink(StrangeCoin)); + tutmes l("%s is obtained during events, daily logins, heroic deeds, gifts, etc. But cannot be bought with real money.", getitemlink(StrangeCoin)); + next; + openshop "Aeros Trader"; + closedialog; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, UglyChristmasSweater); // All travelers wear this stuff + setunitdata(.@npcId, UDT_HEADBOTTOM, TulimsharGuardBoots); + setunitdata(.@npcId, UDT_WEAPON, SilkPants); + setunitdata(.@npcId, UDT_HAIRSTYLE, 13); + setunitdata(.@npcId, UDT_HAIRCOLOR, 12); + npcsit; + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/012-1/estate.txt b/npc/012-1/estate.txt new file mode 100644 index 0000000..96912a8 --- /dev/null +++ b/npc/012-1/estate.txt @@ -0,0 +1,95 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System + +// ID: 1 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +012-1,94,70,0 script Sign#RES_0128 NPC_SWORDS_SIGN,{ + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + if (is_admin() && $@GM_OVERRIDE) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + mesc l("Press the doorbell?"); + next; + if (askyesno() == ASK_YES) + doevent "Doorbell#RES_0128::OnDoorbell"; + close; + +L_RentAvailable: + realestate_rent(.id, .price); + close; + +L_Manage: + realestate_manage(.id, (.price*7/10)); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=1; // Estate ID + .price=60000; // Monthly rent price. Renew is only 70% from this value. + end; + +} + +// Door entrance +012-1,95,69,0 script #RES_0128 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3) || $ESTATE_PASSWORD$[.id] == "") + goto L_Warp; + + mesc l("The door is locked"); + next; + mesc l("However, it can be unlocked if you know the password:"); + if (is_gm()) mesc l("You can use super password \"mouboo\" to unlock the door."), 1; + input .@password$; + // GMs can use super password "mouboo" + if (.@password$ == $ESTATE_PASSWORD$[.id] || (is_gm() && .@password$ == "mouboo")) + goto L_Warp; + close; + +L_Warp: + warp "012-8", 33, 33; + closeclientdialog; + close; + +L_RentAvailable: + dispbottom l("This estate is available for rent, talk to the sign to rent it."); + close; + +OnInit: + // Estate Settings + .id=1; // Estate ID + end; + +} + diff --git a/npc/012-1/guards.txt b/npc/012-1/guards.txt new file mode 100644 index 0000000..d36cc5b --- /dev/null +++ b/npc/012-1/guards.txt @@ -0,0 +1,734 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Protect Hurnscald + +012-1,71,24,0 script Lieutenant Paul NPC_PLAYER,{ + .@q=getq(HurnscaldQuest_Lieutenant); + .@k=getq2(HurnscaldQuest_Lieutenant); + // The Monster King guild have a special menu + if (!$HURNS_LIBDATE && is_master()) goto L_Admus; + if (strcharinfo(2) == "Monster King") goto L_MKControl; + mesn; + mesq l("It is %s since the last great attack from the Monster King.", FuzzyTime($HURNS_LIBDATE,1,2)); + next; + mesn; + mesq l("Many citzens are still cowering in fear. Paths are closed, economy is a disaster, things are not as they should be."); + next; + mesn; + mesq l("But we are working day and night. We hope that soon, more people come out and this place gets lively again."); + if (BaseLevel >= 20 && MPQUEST && .@q <= 15) goto L_Quest; + close; + +// Paul's quest +L_Quest: + next; + // Quest State, Legacy value + @fc=0; + if (.@q == 0 && BaseLevel >= 20) @fc=1; + if (.@q == 1 && BaseLevel >= 22) @fc=2; + if (.@q == 2 && BaseLevel >= 24) @fc=3; + if (.@q == 3 && BaseLevel >= 26) @fc=4; + if (.@q == 4 && BaseLevel >= 28) @fc=5; + if (.@q == 5 && BaseLevel >= 30) @fc=6; + if (.@q == 6 && BaseLevel >= 32) @fc=7; + if (.@q == 7 && BaseLevel >= 34) @fc=8; + if (.@q == 8 && BaseLevel >= 36) @fc=9; + if (.@q == 9 && BaseLevel >= 38) @fc=10; + if (.@q == 10 && BaseLevel >= 40) @fc=11; + if (.@q == 11 && BaseLevel >= 44) @fc=12; + if (.@q == 12 && BaseLevel >= 48) @fc=13; + if (.@q == 13 && BaseLevel >= 52) @fc=14; + if (.@q == 14 && BaseLevel >= 56) @fc=15; + if (.@q == 15 && BaseLevel >= 60) @fc=16; + if (@fc) + goto L_Assign; + mesn; + mesq l("I see you are a Monster Hunting Quest Participant, right? I don't need your help right now, but maybe later, who knows?"); + close; + +L_Assign: + // 2 ** 0 = 1 * 100 = 100 mob points + // 2 ** 1 = 2 * 99 = 198 mob points + // 2 ** 2 = 4 * 98 = 392 mob points + // ... + // 2 ** 9 = 512 * 91 = 46592 mob points (when fc = 10) + // Then comes the stupdly high table + // 2 ** 10 = 1024 * 90 = 92,160 mpt (when fc = 11) + // 2 ** 11 = 2048 * 89 = 182k + // 2 ** 12 = 4096 * 88 = 360k + // 2 ** 13 = 8192 * 87 = 712k + // ... 2 ^ 15 * 85 = 2.8 million + @rq=(2**(@fc-1))*(101-@fc); + if (@fc == 16) @rq=1500000; + + // Check if you already met objective + if (.@k > 0 && Mobpt >= .@k+@rq) { + setq HurnscaldQuest_Lieutenant, @fc, 0; + // Do not deduct mob points anymore because gathering them without spending is a challenge on its own. + // Mobpt-=@rq; + mesn; + mesq l("Good job collecting the monster points for me. Here's your reward."); + @xp=1000; + // reward is 30% from reference level exp to level up + switch (@fc) { + case 1: @xp=486; break; + case 2: @xp=597; break; + case 3: @xp=751; break; + case 4: @xp=1028; break; + case 5: @xp=1342; break; + case 6: @xp=2399; break; + case 7: @xp=3128; break; + case 8: @xp=4190; break; + case 9: @xp=5303; break; + case 10: @xp=6532; break; + /* After this it demands so much XP, I raised the rate from 30 to 100% + case 11: @xp=10234; break; + case 12: @xp=12630; break; + case 13: @xp=15908; break; + case 14: @xp=19212; break; + case 15: @xp=22792; break; + case 16: @xp=34577; break; + */ + case 11: @xp=34212; break; + case 12: @xp=53026; break; + case 13: @xp=75973; break; + case 14: @xp=142784; break; + case 15: @xp=215198; break; + case 16: @xp=365914; break; + } + getexp @xp, @fc*3; + Zeny=Zeny+@fc*15+@fc; + mesc l("* Gained @@ EXP and @@ Job Exp", @xp, @fc*3); + mesc l("* Gained @@ GP", @fc*15+@fc); + // Welcome to the stupid table + if (@fc >= 10) { + switch (@fc) { + case 10: + .@tea=Coffee; .@gift=BronzeGift; .@rare=ScrollSTerranite; + break; + case 11: + .@tea=ChamomileTea; .@gift=SilverGift; .@rare=WhiskeyAle; + break; + case 12: + .@tea=SpearmintTea; .@gift=SilverGift; .@rare=PrecisionPotion; + break; + case 13: + .@tea=OolongTea; .@gift=GoldenGift; .@rare=SacredLifePotion; + break; + case 14: + .@tea=JasmineTea; .@gift=GoldenGift; .@rare=MercBoxEE; + break; + case 15: + .@tea=YerbaMate; .@gift=PrismGift; .@rare=MysteriousFruit; + break; + default: // 16+ + .@tea=ElixirOfLife; .@gift=PrismGift; .@rare=MylarinDust; + break; + } + mesc l("For your incredibly hard work, you got:"); + mesf("* %d %s", 1, getitemlink(.@tea)); + mesf("* %d %s", 1, getitemlink(.@gift)); + mesf("##B* %d %s##b", 1, getitemlink(.@rare)); + getitem .@rare, 1; + getitem .@gift, 1; + getitem .@tea, 1; + } + close; + } + // Assign quest + else if (.@k == 0) { + mesn; + mesq l("Maybe you can help our city. I see you are a Monster Hunting Quest Participant, right?"); + next; + mesn; + mesq l("So, I'm placing a special bounty for @@ Mob Points.", @rq); + mesq l("You currently have @@, so if you accept and come back later with @@ Mob Points, I'll mark the bounty as complete.", Mobpt, Mobpt+@rq); + next; + mesc l("Accept quest?"); + if (askyesno() == ASK_YES) { + setq HurnscaldQuest_Lieutenant, @fc-1, Mobpt; + } + } + // Resume quest + else { + // Reset if needed + if (Mobpt-.@k < 0) { + setq2 HurnscaldQuest_Lieutenant, Mobpt; + .@k=0; + } + mesn; + mesq l("You have collected @@/@@ Mob Points.", Mobpt-.@k, @rq); + } + close; + +/////////////////////////// +// Liberation Day facility +/////////////////////////// +L_Admus: + mes ".:: " + col("HURNSCALD LIBERATION DAY - ADMUS PANEL", 2) + " ::."; + mesc l("For HLib, a GM must take the role of the Monster King"); + mesc("Protip: Use @hide to not interfere."); + mes l("Determine Team Size (If everyone is ready and stdby at Tulimshar, use: @@. Minimum 2 players.)", getusers(1)-1); + input $@BG1_SIZE; + if ($@BG1_SIZE < 2 && !$@GM_OVERRIDE) close; + $@BG1_SIZE=$@BG1_SIZE+1; + + kickwaitingroomall("Hurnsguard"); + setmapflagnosave("012-1", "000-1", 22, 22); + //setmapflag("012-1", mf_nocommand); // This is just too powerful! + setmapflag("012-1",mf_zone,"MMO"); // MMO Zone: Overrides GM Commands + setmapflag("012-1", mf_battleground); + setmapflag("012-1", mf_nopenalty); + setmapflag("012-1",mf_bexp,126); + enablenpc "Hurnsguard"; + donpcevent "Hurnsguard::OnSet"; + donpcevent "Lieutenant Paul::OnSet"; + addmapmask "012-1", MASK_MATTACK; + addmapmask "012-1", MASK_EVILSANCTUM; + pvpon("012-1"); + disablenpc "#012-1_22_62"; + disablenpc "#012-1_79_17"; + disablenpc "#012-1_79_102"; + disablenpc "#012-1_132_101"; + disablenpc "#012-1_65_55"; + disablenpc "#012-1_89_58"; + disablenpc "#012-1_101_55"; + disablenpc "#012-1_121_71"; + disablenpc "#012-1_116_66"; + disablenpc "#012-1_110_56"; + disablenpc "#012-1_102_69"; + disablenpc "Andrei Sakar"; + disablenpc "Dyrin The Traveler"; + disablenpc "Gwendolyn"; + disablenpc "Jack"; + disablenpc "Locamit"; + disablenpc "Richard"; + disablenpc "Soul Menhir#hurns"; + disablenpc "HurnsShip#M"; + disablenpc "Hinnak"; + disablenpc "Tezzerin"; + disablenpc "Luffyx"; + disablenpc "Shoppa Kep"; + announce "##1Hear ya, hear ya! There are "+str($@BG1_SIZE-1)+" vacant slots at Hurnsguard to LIBERATE HURNSCALD!", bc_all | bc_npc; + mesc "You are assigned to Monster Forces. Do not leave Hurnscald.", 1; + close; + +// Monster King Siege (deprecated) +L_MKControl: + mesn; + mes l("Oh noes! You've found the Hurnscald control panel!"); + next; + select + l("Abort"), + l("Initiate a siege"); + mes ""; + if (@menu == 2) { + doevent "#HurnscaldSiege::OnStartSiege"; + closedialog; + } + close; + +// Hurnscald Liberation Day Core Logic +OnSet: + .CYCLES=0; + waitingroom("Monster Army", 2, "start#hurns_lib::OnReadyCheck", 1); + end; + +OnEnterBG: + debugmes "Paul::OnEnterBG"; + $@FK_Team2 = waitingroom2bg("012-1",141, 65,"start#hurns_lib::OnPlayer2Quit","start#hurns_lib::OnPlayer2Death"); + setbgteam $@FK_Team2, 2; + end; + +OnInit: + .sex = G_MALE; + .distance = 4; + + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, BullHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, LieutenantArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_SHIELD, LousyMoccasins); // TODO FIXME: Display Boots + setunitdata(.@npcId, UDT_WEAPON, Backsword); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 15); + + end; +} + +// Liberation Day Script +012-1,122,25,0 script Hurnsguard NPC_PLAYER,{ + //query_sql("SELECT online FROM `char` WHERE name='Saulc GM'", .@online); + // TODO + .@online=$@BG1_SIZE; + if (!.@online) { + mesn; + mesq l("We are planning to take over Hurnscald from the Monster King, and we will need everybody's help."); + next; + mesn; + mesq l("As soon that Saulc GM get back, we can start."); + close; + } + mesc l("Hurnscald Liberation Day special event"), 3; + mes ""; + mesc l("The objective of this event is to ##Bslay the Monster Admiral##b."); + mesc l("If you die, you ##Bwon't##b be able to rejoin, but you won't suffer the penalty."); + next; + mesc l("If all players there die, Hurnscald WON'T BE LIBERATED."); + mesc l("This basically means that it'll be another day without it."); + mesc l("Also, the more players survive, the better rewards will be given."); + next; + mesc l("Right click on this NPC to join the Hurnscald Alliance."), 3; + mesc l("The number of players must be precise, meaning if someone doesn't joins,"); + mesc l("the event won't start and HURNSCALD WON'T BE LIBERATED."); + next; + mesc l("Once you join the Alliance, you won't be able to talk with people outside it."); + mesc l("Additionaly, all your movement will be restricted until either you're warped or log out."); + close; + +OnSet: + waitingroom("Hurnscald Alliance", $@BG1_SIZE, "start#hurns_lib::OnReadyCheck", $@BG1_SIZE-1); + end; + +OnInit: + .CYCLES=0; + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, GoldenWarlordPlate); // Light armor + setunitdata(.@npcId, UDT_HEADMIDDLE, JeansChaps); // Pants + setunitdata(.@npcId, UDT_HEADBOTTOM, DeepBlackBoots); // Shoes + setunitdata(.@npcId, UDT_WEAPON, BugSlayer); + setunitdata(.@npcId, UDT_HAIRSTYLE, 13); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + if ($HURNS_LIBDATE) + disablenpc(.name$); + end; + +OnEnterBG: + debugmes "Alliance::OnEnterBG"; + $@FK_Team1 = waitingroom2bg("012-1",141, 65,"start#hurns_lib::OnPlayerQuit","start#hurns_lib::OnPlayerDeath"); + setbgteam $@FK_Team1, 1; + end; + +OnDoEvent: + debugmes "OnDoEvent"; + mapannounce("012-1", "Find and slay the Monster Admiral! DO NOT ATTACK GUARDS. Don't let everyone die!", bc_map); + .@Pl=getmapusers("012-1"); + .CYCLES=0; + + // Guards won't attack the Monster Staff + areamonster("012-1", 52, 45, 127, 80, "Monster Sergeant", Forain, .@Pl/3+1, "Hurnsguard::OnXtreem"); + areamonster("012-1", 52, 45, 127, 80, "Monster Sergeant", Moonshroom, .@Pl/2+1, "Hurnsguard::OnXtreem"); + areamonster("012-1", 52, 45, 127, 80, "Monster Soldier", AngryScorpion, .@Pl, "Hurnsguard::OnSkip"); + for (.@i = 0; .@i < .@Pl; .@i++) + bg_monster($@FK_Team1, "012-1", rand(131, 136), rand(64, 80), "Friendly Guard", FallenGuard1, "Cassia::OnSkip"); + initnpctimer(); + end; + +OnSkip: + // Here, coins drop rate are based on REMAINING PLAYERS and DEFEATED MONSTER LEVEL. + // A level 100 monster can have a 5% drop bonus against a level 0 monster. + // You have 1.2% of base chance, plus 0.5% each player + 0.05% each monster level. + if (rand(10000) <= 120 + (getmapusers("012-1")*50) + (strmobinfo(3,killedrid)*5)) + getmapxy(.@m$, .@x, .@y, 0); + makeitem(any(Plushroom, Chagashroom, Bread, HalfCroconut, Aquada, CasinoCoins, CactusCocktail, AppleCocktail, CherryCocktail), any(1,1,2), "012-1", .@x, .@y); + end; + +OnTimer300000: + .@Pl=getmapusers("012-1")+mobcount("012-1", "Cassia::OnSkip")-1; + .@Pla=getmapusers("012-1"); + .@Mb=mobcount("012-1", "Hurnsguard::OnSkip")+mobcount("012-1", "Hurnsguard::OnXtreem")+2; // Saulc GM is an enemy for server code + for (.@i = 0; .@i < .@Pla; .@i++) + bg_monster($@FK_Team1, "012-1", rand(131, 136), rand(64, 80), "Friendly Guard", FallenGuard1, "Cassia::OnSkip"); + + // See if we need extra guards, depending on how outnumbered allied forces are. + if ((.@Mb / .@Pl) > 7) { + bg_monster($@FK_Team1, "012-1", rand(131, 136), rand(64, 80), "Additional Guard", FallenGuard2, "Cassia::OnSkip"); + } + if ((.@Mb / .@Pl) > 6) { + bg_monster($@FK_Team1, "012-1", rand(131, 136), rand(64, 80), "Additional Guard", FallenGuard2, "Cassia::OnSkip"); + } + if ((.@Mb / .@Pl) > 5) { + bg_monster($@FK_Team1, "012-1", rand(131, 136), rand(64, 80), "Additional Guard", FallenGuard2, "Cassia::OnSkip"); + } + if ((.@Mb / .@Pl) > 4) { + bg_monster($@FK_Team1, "012-1", rand(131, 136), rand(64, 80), "Additional Guard", FallenGuard2, "Cassia::OnSkip"); + } + if ((.@Mb / .@Pl) > 3) { + bg_monster($@FK_Team1, "012-1", rand(131, 136), rand(64, 80), "Additional Guard", FallenGuard2, "Cassia::OnSkip"); + } + .@Pl=getmapusers("012-1")+mobcount("012-1", "Cassia::OnSkip")-1; + .@Pla=getmapusers("012-1")-1; + + mapannounce("012-1", "Survivors: "+.@Pla+" Hurnscald Alliance: "+.@Pl+" Monsters: "+.@Mb, bc_map); + initnpctimer; + end; + +OnTimer10000: +OnTimer120000: +OnTimer240000: + .CYCLES=.CYCLES+1; + + // Scripted monsters + if (.CYCLES == 2) { + mapannounce("012-1", "Monster Lieutenant appears! Watch out!", bc_map); + bg_monster($@FK_Team2, "012-1", 65, 71, "Monster Lieutenant", NightScorpion, "Hurnsguard::OnXtreem"); + } else if (.CYCLES == 3) { + mapannounce("012-1", "Monster Admiral appears! Kill it to liberate Hurns!", bc_map); + .@boss=bg_monster($@FK_Team2, "012-1", 65, 71, "Monster Admiral", GiantMutatedBat, "Hurnsguard::OnVictory"); + // Give huge amounts of health to the boss (Default: 43310) + setunitdata(.@boss, UDT_MAXHP, 100000); + setunitdata(.@boss, UDT_HP, 100000); + } + + // One monster per user alive + .@Pl=getmapusers("012-1"); + for (.@i = 0; .@i < .@Pl; .@i++) { + .@monsterId=any(CaveMaggot, RedSlime, LavaSlime, BlackScorpion, + AngryRedScorpion, Sarracenus, Snake, MountainSnake, GreenSlime, + Bandit, BlackScorpion, HouseMaggot, AngryScorpion, MagicGoblin, + Pinkie, Mouboo, Scorpion, Yeti); + + bg_monster($@FK_Team2, "012-1", rand(69, 136), rand(83, 74), "Monster Soldier", .@monsterId, "Hurnsguard::OnSkip"); + // 40% odds of having an extra monster spawned (player loop) + if (rand(1,5) % 2 == 0) + bg_monster($@FK_Team2, "012-1", rand(69, 136), rand(83, 74), "Monster Soldier", .@monsterId, "Hurnsguard::OnSkip"); + } + // One slime blast per general + .@Mgg=mobcount("012-1", "Hurnsguard::OnXtreem"); + for (.@i = 0; .@i < .@Mgg; .@i++) { + bg_monster($@FK_Team2, "012-1", rand(69, 136), rand(83, 74), "Monster Soldier", SlimeBlast, "Cassia::OnSkip"); + } + // One Magic Goblin or Pinkie per cycle + for (.@i = 0; .@i < .CYCLES; .@i++) { + if (rand(1,2) == 1) + bg_monster($@FK_Team2, "012-1", rand(69, 136), rand(83, 74), "Monster Soldier", Pinkie, "Hurnsguard::OnSkip"); + else + bg_monster($@FK_Team2, "012-1", rand(69, 136), rand(83, 74), "Monster Soldier", MagicGoblin, "Hurnsguard::OnSkip"); + } + // Spawn Angry Scorpions on the whole map. These monsters are neutral! + areamonster("012-1", 33, 16, 137, 100, "Monster Soldier", AngryScorpion, .CYCLES, "Hurnsguard::OnSkip"); + + // One extra guard on every summon + bg_monster($@FK_Team1, "012-1", rand(131, 136), rand(64, 80), "Additional Guard", FallenGuard2, "Cassia::OnSkip"); + end; + +OnXtreem: + if (rand2(0,100) > 50) + getitem StrangeCoin, 2; + // Here, the drop is a Bronze Gift, based on living players and players nearby. + // You have 0.1% of base chance, plus 0.2% each fighting player + 1% each near player. + getmapxy(.@m$, .@x, .@y, 0); + if (rand(10000) <= 10 + (getmapusers("012-1")*20) + (getareausers("012-1", .@x-8, .@x+8, .@y-8, .@y+8)*100)) + makeitem(BronzeGift, 1, "012-1", .@x, .@y); + end; + +OnVictory: + $HURNS_LIBDATE=gettimetick(2); + removemapflag("012-1", mf_nosave); + disablenpc "Hurnsguard"; + pvpoff("012-1"); + removemapmask "012-1", MASK_MATTACK; + removemapmask "012-1", MASK_EVILSANCTUM; + //removemapflag("012-1", mf_nocommand); + setmapflag("012-1",mf_zone,"Normal2"); // Revert MMO Zone + removemapflag("012-1", mf_battleground); + removemapflag("012-1", mf_bexp); + setmapflag("012-1",mf_bexp,100); + enablenpc "#012-1_22_62"; + enablenpc "#012-1_79_17"; + enablenpc "#012-1_79_102"; + enablenpc "#012-1_132_101"; + enablenpc "#012-1_65_55"; + enablenpc "#012-1_89_58"; + enablenpc "#012-1_101_55"; + enablenpc "#012-1_121_71"; + enablenpc "#012-1_116_66"; + enablenpc "#012-1_110_56"; + enablenpc "#012-1_102_69"; + enablenpc "Andrei Sakar"; + enablenpc "Dyrin The Traveler"; + enablenpc "Gwendolyn"; + enablenpc "Jack"; + enablenpc "Locamit"; + enablenpc "Richard"; + enablenpc "Soul Menhir#hurns"; + enablenpc "HurnsShip#M"; + enablenpc "Hinnak"; + enablenpc "Tezzerin"; + enablenpc "Luffyx"; + enablenpc "Shoppa Kep"; + announce "##2Hurnscald was recovered!", bc_all | bc_npc; + killmonster("012-1", "All"); + changemusic "012-1", "tws_birds_in_the_sunrise.ogg"; // Play a more peaceful tune. + stopnpctimer; + maptimer("012-1", 10, "Hurnsguard::OnReward"); + // Here, coins drop rate are based on REMAINING PLAYERS and DEFEATED MONSTER LEVEL. + // You have $coinsrate of base chance, plus 2% for each living player. + // $coinsrate is set on Aeros Event Master, and defaults to 6% + if (rand(10000) <= $coinsrate + (getmapusers("012-1")*200)) + getmapxy(.@m$, .@x, .@y, 0); + makeitem(StrangeCoin, 1, "012-1", .@x, .@y); + //Karma=Karma+1; + $MOST_HEROIC$=strcharinfo(0); + bg_destroy(1); + bg_destroy(2); + // Auto-schedule Liberation Day + // a month from now, to happen on a saturday + .@offset = gettime(GETTIME_WEEKDAY); // How long since sunday? + .@offset += 1; // Move it to Saturday + .@offset += 30; // Add a month + .@offset += gettimeparam(GETTIME_DAYOFMONTH); // Apply offset to the all days + $NLIB_SEQDAY = .@offset; // Schedule the event + + end; + +OnReward: + bg_leave(); + // Let's say we have 5 players for reference. Subtract a Game Master, he doesn't counts. + .@Pl=getmapusers("012-1")-1; + getexp .@Pl*600, .@Pl*30; // 3,000 exp, 150 jxp + Zeny=Zeny+.@Pl*300; // 1,500 gp + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +} + +//////////////////////////////////////////////// +//== Battleground Engine ====================== +//////////////////////////////////////////////// +012-1,0,0,0 script start#hurns_lib NPC_HIDDEN,{ +OnInit: + end; + +OnEnable: + end; + +OnPlayerQuit: +OnPlayerDeath: +OnPlayer2Quit: +OnPlayer2Death: + bg_leave(); + // Check if we are done for. + warp "000-1", 22, 22; + .@Pl=getmapusers("012-1"); + if (.@Pl <= 1 && !$@GM_OVERRIDE) { + announce "##1Hurnscald is lost!", bc_all | bc_npc; + disablenpc "Hurnsguard"; + pvpoff("012-1"); + removemapmask "012-1", 10; + removemapflag("012-1", mf_nocommand); + removemapflag("012-1", mf_battleground); + killmonster("012-1", "all"); + bg_destroy(1); + bg_destroy(2); + } + end; + +OnReadyCheck: + .@Alliance = getwaitingroomstate(0,"Hurnsguard"); + debugmes "Check:" +str(.@Alliance)+"<"+ str($@BG1_SIZE-1); + if ( .@Alliance < $@BG1_SIZE-1 ) + end; + donpcevent "Hurnsguard::OnEnterBG"; + donpcevent "Lieutenant Paul::OnEnterBG"; + debugmes "Prepare Warp"; + bg_warp $@FK_Team1,"012-1",141, 65; + bg_warp $@FK_Team2,"012-1",69, 71; + changemusic "012-1", "misuse.ogg"; + donpcevent "Hurnsguard::OnDoEvent"; + //initnpctimer; + end; +} + +//////////////////////// +// Handle Guard's logic +//////////////////////// +function script CheckpointGuard { + mesn; + mesq l("I am stationed here to protect Hurnscald from monsters."); + next; + mesn; + mesq l("If the Monster King attacks, I will try to control inbound monsters here."); + close; + return; +} + +012-1,81,18,0 script Checkpoint Guard#1 NPC_GUARD2,{ + CheckpointGuard(); + end; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + + +012-1,26,60,0 script Checkpoint Guard#2 NPC_GUARD1,{ + CheckpointGuard(); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +012-1,80,98,0 script Checkpoint Guard#3 NPC_GUARD2,{ + CheckpointGuard(); + end; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + + +012-1,134,98,0 script Checkpoint Guard#4 NPC_GUARD1,{ + CheckpointGuard(); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +012-1,94,56,0 script Guard#012-1.1 NPC_GUARD1,{ + .@q=getq(HurnscaldQuest_HungryGuard); + if (.@q == 0) { + mesn l("Guard"); + mesq l("Hey, you! Could you bring me an @@?", getitemlink(RedApple)); + next; + mesn l("Guard"); + mesq l("I'm hungry like a bear :b"); + if (!countitem(RedApple)) + close; + select + l("Here, you can have this one!"), + l("Nope, sorry!"); + mes ""; + if (@menu == 1) { + delitem RedApple, 1; + setq HurnscaldQuest_HungryGuard, 1; + getexp 500, 0; + mesn l("Guard"); + mesq l("Oooh, many thanks! ^.^"); + } + close; + } + legiontalk; + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + + +012-1,112,65,0 script Guard#012-1.2 NPC_GUARD2,{ + legiontalk; + end; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + + + +012-1,0,0,0 script #HurnscaldSiege NPC_HIDDEN,{ + end; + +// Begin Siege +OnMKSiege: + $@SIEGE_ABORTED = false; +OnStartSiege: + kamibroadcast(col("WARNING! WARNING! Monster Army is moving towards Hurnscald!!",1)); + do_siege("012-1", "014-3", "HURNS", TP_HURNS, .name$, .siegetime); + initnpctimer; + end; + +// Timers +OnTimer5000: + .siegetime+=5; + do_siege("012-1", "014-3", "HURNS", TP_HURNS, .name$, .siegetime); + switch (.siegetime) { + // Monster Army arrives in town + case 60: + disablenpc "Oscar"; + disablenpc "Gwendolyn"; + disablenpc "Milly"; + disablenpc "LOFBot"; + disablenpc "Locamit"; + disablenpc "Andrei Sakar"; + disablenpc "Luffyx"; + disablenpc "Tezzerin"; + disablenpc "Hinnak"; + disablenpc "Igor"; + disablenpc "Dyrin The Traveler"; + disablenpc "Mahad"; + disablenpc "Jack"; + break; + // Monster Army deployed in town + case 90: + disablenpc "Shoppa Kep"; + disablenpc "Richard"; + + disablenpc "Celestia"; + disablenpc "Airlia"; + disablenpc "Nicholas"; + disablenpc "Wyara"; + disablenpc "Alan"; + disablenpc "Khafar"; + disablenpc "Melina"; + disablenpc "Helena"; + disablenpc "Rakinorf, Mayor"; + break; + // Monster army have withdrawn completly + case MK_SIEGE_DURATION: + .siegetime=0; + announce(("Hurnscald siege is over!"), bc_all); + enablenpc "Gwendolyn"; + enablenpc "Milly"; + enablenpc "LOFBot"; + enablenpc "Locamit"; + enablenpc "Andrei Sakar"; + enablenpc "Luffyx"; + enablenpc "Tezzerin"; + enablenpc "Hinnak"; + enablenpc "Igor"; + enablenpc "Dyrin The Traveler"; + enablenpc "Mahad"; + enablenpc "Jack"; + enablenpc "Shoppa Kep"; + enablenpc "Richard"; + enablenpc "Celestia"; + enablenpc "Airlia"; + enablenpc "Nicholas"; + enablenpc "Wyara"; + enablenpc "Alan"; + enablenpc "Khafar"; + enablenpc "Melina"; + enablenpc "Helena"; + enablenpc "Rakinorf, Mayor"; + enablenpc "Oscar"; + stopnpctimer; + end; + break; + } + + // Loop again + initnpctimer; + end; + +OnInit: + .siegetime=0; + end; +} + diff --git a/npc/012-1/gwendolyn.txt b/npc/012-1/gwendolyn.txt new file mode 100644 index 0000000..42953dd --- /dev/null +++ b/npc/012-1/gwendolyn.txt @@ -0,0 +1,590 @@ +// TMW2 Script +// Author: +// The Mana World Team +// Edited by: +// Jesusalva +// Description: +// Archery Trainer + +012-1,88,52,0 script Gwendolyn NPC_BOWMASTER,{ + .@q=getq(HurnscaldQuest_HarkEye); + .@fbow=getq(HurnscaldQuest_ForestBow); + + if (.@q == 6) goto L_State6; + if (.@q == 5) goto L_State5; + if (.@q == 4) goto L_State4; + if (.@q == 3) goto L_State3; + if (.@q == 2) goto L_State2; + if (.@q == 1) goto L_State1; + + mesn l("Gwendolyn Bowmaker"); + mesq l("Hey there. My name is Gwendolyn Bowmaker; I'm the granddaughter of the famous Glinda Bowmaker."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("She and her husband Simon Bowmaker, vowed to teach the ways of archery to those with willpower, and allow everyone to use a bow."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("I intend to continue with my family's tradition, by teaching the Way of Archery."); + setq HurnscaldQuest_HarkEye, 1; + next; + +L_State1: + mesn l("Gwendolyn Bowmaker"); + mesq l("Are you interested in becoming a student of Archery?"); + next; + menu + l("I want to become an archer!"),L_Fee, + l("I'm not interested right now."),L_Abort; + +L_Fee: + mes ""; + mesn l("Gwendolyn Bowmaker"); + mesq l("Wonderful! My fee is @@. This fee is the first step in separating those with willpower, from those without.", .SCHOOLFEE); + next; + menu + l("That's a lot, but I think it will be worth it."),L_Next, + l("I'm not sure I want to spend that much."),L_Abort; + +L_Next: + mes ""; + if (Zeny < .SCHOOLFEE) + goto L_No_Money; + + Zeny = Zeny - .SCHOOLFEE; + setq HurnscaldQuest_HarkEye, 2; + + mesn l("Gwendolyn Bowmaker"); + mesq l("Great! The first thing you need is, obviously, a bow. You should not use any you come across, but a high quality one."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("Inside this house is my brother Alan. He knows how to make Forest Bows of exquisite quality."); + next; + + if (countitem("ForestBow") < 1) { + goto L_Close; + } + select + l("I already have a Forest Bow."); + mes ""; + +L_State2: + mesn l("Gwendolyn Bowmaker"); + mesq l("Let me see your bow."); + next; + + if (countitem("ForestBow") < 1) + goto L_NoBow; + if (.@fbow < 2) + goto L_FakeBow; + + setq HurnscaldQuest_HarkEye, 3; + mesn l("Gwendolyn Bowmaker"); + mesq l("Very well, this looks fine. It is time for your first lesson."); + next; + goto L_Lesson; + +L_Lesson: + mesn l("Gwendolyn Bowmaker"); + mesq l("The advantage of fighting with a ranged weapon is that you can stay out of the enemy's range. So there is no need to spend training on your resistance."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("But remember: This makes you much more vulnerable, and you usually cannot hit from too close, so watch your step and don't stumble into your enemy's attack."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("Also your strength doesn't matter much. It helps you to carry more arrows with you, but nothing a few trips to the store won't do."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("The most important thing to improve is your dexterity. When you use a bow, it is your dexterity that determines if you are able to hit your enemy where it hurts most."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("Also worth mentioning is how to improve your ability to shoot fast. You need to be agile to grab a new arrow from your quiver and aim for the next shot before your enemy has recovered from your last."); + next; + +L_State3: + mesn l("Gwendolyn Bowmaker"); + mesq l("To demonstrate that you understood what I'm trying to teach you, you should go and fight against some snakes. I know they're strong, but that's not important to an archer."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("To prove me your results, bring me @@ @@, @@ @@ and @@ @@.", + .CAVE_EGGS_AMOUNT, getitemlink(CaveSnakeEgg), + .SNAKE_EGGS_AMOUNT, getitemlink(SnakeEgg), + .MOUNTAIN_EGGS_AMOUNT, getitemlink(MountainSnakeEgg)); + next; + menu + l("Can you please repeat your lesson?"),L_Lesson, + l("I will go and hunt some snakes."),L_Close, + l("I have what you want."),L_Next1; + +L_Next1: + mes ""; + if (countitem("CaveSnakeEgg") < .CAVE_EGGS_AMOUNT + ||countitem("SnakeEgg") < .SNAKE_EGGS_AMOUNT + ||countitem("MountainSnakeEgg") < .MOUNTAIN_EGGS_AMOUNT) + goto L_Insufficient; + + delitem "CaveSnakeEgg", .CAVE_EGGS_AMOUNT; + delitem "SnakeEgg", .SNAKE_EGGS_AMOUNT; + delitem "MountainSnakeEgg", .MOUNTAIN_EGGS_AMOUNT; + + getexp .QUEST_EGG_EXP, 100; + + setq HurnscaldQuest_HarkEye, 4; + + mesn l("Gwendolyn Bowmaker"); + mesq l("Very well. That will make a great meal. You pass!"); + next; + +L_State4: + if (BaseLevel < .BASELEVEL_GAME) + goto L_NotReady; + if (!MAGIC_LVL) + goto L_NotReady; + mesc l("Gwendolyn takes an analyzing look at you. Then she nods."); + next; + setq HurnscaldQuest_HarkEye, 5; + mesn l("Gwendolyn Bowmaker"); + mesq l("Ok. You have made great progress. I think you are ready for further tasks."); + next; + goto L_Explain; + +L_Explain: + mes ""; + mesn l("Gwendolyn Bowmaker"); + mesq l("As you might have noticed, it depends on many circumstances if your arrow finds its target. Your shot may be too powerful or too weak, so the arrow goes far beyond your target or hits the ground before it reaches its destination."); + next; + mesq l("The greater the distance to your target is, the more important is the strength and direction of the wind. Even if you are exploring a cave, there might be air flow caused by the corners in the cave."); + next; + mesq l("Ok, you can see that target over there? I will tell you how strong the wind blows and from which direction. Then you will try to hit the target by moving right or left and decide how powerful you want to shoot."); + next; + goto L_State5; + +L_State5: + // needed for the minigame: + .@wdX = 0; // wind direction x-coordinate, rand + .@wdY = 0; // wind direction y-coordinate, rand + // set .@wp, 0; // windpower + .@spX = 0; // start position x-coordinate, player decision + // set .@spY, 0; // start position y-coordinate, const + .@sp = 0; // shotpower, player decision + //set .@targetX, 0; // target position, const + //set .@targetY, 20; // target position, const + .@pointX = 0; // point the player hits, x-coordinate + .@pointY = 0; // point the player hits, y-coordinate + .@wdIndex = 0; + // + mesn l("Gwendolyn Bowmaker"); + mesq l("Show me what you can!"); + next; + menu + l("Can you please explain the task again?"),L_Explain, + l("I'm ready, let's start!"),L_Game, + l("I'm sorry, I will come back later."),L_Close; + +L_Game: + mes ""; + if (countitem(ForestBow) < 1) + goto L_NoBow; + if (countitem(TrainingArrow) < 1) + goto L_Arrows; + delitem TrainingArrow, 1; + mesc l("You go to the training field and stand exactly on the same level with the target. The target is located about 13 meters north of you. You take one arrow and look at Gwendolyn."); + next; + + // possibility for wdX and wdY to become zero is reduced, so windless status is less often + .@wdX = rand(9) - 4; + if (.@wdX != 2 && .@wdX !=-2) .@wdX = rand(9) - 4; + if (.@wdX == 0) .@wdX = rand(9) - 4; + + .@wdY = rand(9) - 4; + if (.@wdY != 2 && .@wdY !=-2) .@wdY = rand(9) - 4; + if (.@wdY == 0) .@wdY = rand(9) - 4; + + // wind direction array, where the wind blows to: + // 123 + // 456 + // 789 + //here is where the wind comes from, that's why it is the opposite. + setarray .@wd$,"error1, please report", l("southeast"), l("south"), l("southwest"), l("east"), + "error2, please report", l("west"), l("northeast"), l("north"), l("northwest"); + @paramX = .@wdX; + @paramY = .@wdY; + callsub S_getDirection; + .@wdIndex = @returnIndex; + + // Cheat + if (is_admin() && debug) + dispbottom l("Wind: @@, @@",.@wdX,.@wdY); + + .@wp_sq = (.@wdX * .@wdX) + (.@wdY * .@wdY); + + if (.@wp_sq == 0) + goto L_wp0; + if (.@wp_sq < 3) + goto L_wp1; + if (.@wp_sq < 9) + goto L_wp2; + if (.@wp_sq < 19) + goto L_wp3; + goto L_wp4; + + +L_wp0: + mesn l("Gwendolyn Bowmaker"); + mesq lg("You're lucky. It is windless now."); + next; + goto L_Start; + +L_wp1: + mesn l("Gwendolyn Bowmaker"); + mesq l("There is a slight breeze coming from @@.", .@wd$[.@wdIndex]); + next; + goto L_Start; + +L_wp2: + mesn l("Gwendolyn Bowmaker"); + mesq l("The wind is blowing from @@.", .@wd$[.@wdIndex]); + next; + goto L_Start; + +L_wp3: + mesn l("Gwendolyn Bowmaker"); + mesq l("Right now, there is a strong wind blowing from @@.", .@wd$[.@wdIndex]); + next; + goto L_Start; + +L_wp4: + mesn l("Gwendolyn Bowmaker"); + mesq l("Here we have a squall from @@.", .@wd$[.@wdIndex]); + next; + goto L_Start; + +L_Start: + mesc l("Do you want to go to the left, the right or stay where you are?"); + next; + menu + l("I want to move left."),L_Left, + l("I go right."),L_Right, + l("I stay."),L_Stay, + l("I will try it later."),L_Close; + +L_Stay: + .@spX = 0; + mesc l("You stay where you are.."); + next; + goto L_Power; + +L_Left: + mesc l("How many steps?"); + next; + menu + "1",L_Left1, + "2",L_Left2, + "3",L_Left3, + "4",L_Left4; + +L_Left1: + .@spX = -1; + mesc l("You take one step to the left."); + next; + goto L_Power; + +L_Left2: + .@spX = -2; + mesc l("You take two steps to the left."); + next; + goto L_Power; + +L_Left3: + .@spX = -3; + mesc l("You take three steps to the left."); + next; + goto L_Power; + +L_Left4: + .@spX = -4; + mesc l("You take four steps to the left."); + next; + goto L_Power; + +L_Right: + mesc l("How many steps?"); + next; + menu + "1",L_Right1, + "2",L_Right2, + "3",L_Right3, + "4",L_Right4; + +L_Right1: + .@spX = 1; + mesc l("You take one step to the right."); + next; + goto L_Power; + +L_Right2: + .@spX = 2; + mesc l("You take two steps to the right."); + next; + goto L_Power; + +L_Right3: + .@spX = 3; + mesc l("You take three steps to the right."); + next; + goto L_Power; + +L_Right4: + .@spX = 4; + mesc l("You take four steps to the right."); + next; + goto L_Power; + +L_Power: + mes ""; + mesc l("How powerfully do you want to shoot?"); + next; + select + l("very weak shot"), + l("rather weak shot"), + l("weak shot"), + l("somewhat weak shot"), + l("medium shot"), + l("somewhat powerful shot"), + l("powerful shot"), + l("rather powerful shot"), + l("very powerful shot"); + + // we have 9 menu entries, so scale @menu, which is in range 1-9, to a scale in -4 to +4 + .@sp = @menu - 5; + // calculate where you hit. You range is in [-8, 8] in X and Y as well + .@pointX = .@wdX + .@spX; + .@pointY = .@wdY + .@sp; + + // Debug information. If you move wind info to above, you CHEAT. + if (is_admin()) { + dispbottom l("Wind: @@, @@",.@wdX,.@wdY); + dispbottom l("You: @@, @@",.@spX,.@sp); + dispbottom l("Final: @@, @@",.@pointX,.@pointY); + + } + + if( .@pointX==0 && .@pointY==0 ) + goto L_Success; + + //set .@targethit to these numbers: + //1,2,3 for left above, above, right above, + //4,5,6 for left, hit, right + //7,8,9 for lower left, below, lower right; + setarray .@target_direction$, "error3, please report", l("left above"), l("above"), l("right above"), l("left"), + "error4, please report", l("right"), l("lower left"), l("below"), l("lower right"); + + @paramX = .@pointX; + @paramY = .@pointY; + callsub S_getDirection; + .@targethit = @returnIndex; + + //now check how close + //get the distance squared: + // so it is in range 0 - 64: + // 0 is hit + // < 5 is very close + // <17 is close + // rest is a quite bad shot + .@target_dist_sq = (.@pointX * .@pointX) + (.@pointY*.@pointY); + + if (.@target_dist_sq < 5) + goto L_Quite; + if (.@target_dist_sq < 17) + goto L_Almost; + + //here are bad shots: + mesn l("Gwendolyn Bowmaker"); + mesq l("D'oh, what a terrible shot!"); + next; + mesq l("Do you even know what a bow is?"); + next; + mesq l("That was far @@.", .@target_direction$[.@targethit]); + next; + goto L_Game; + +L_Almost: + mesn l("Gwendolyn Bowmaker"); + mesq l("That was @@.", .@target_direction$[.@targethit]); + next; + mesq l("I suggest some training."); + next; + goto L_Game; + +L_Quite: + mesn l("Gwendolyn Bowmaker"); + mesq l("That was quite close. Impressive!"); + next; + mesq l("It was a little @@.", .@target_direction$[.@targethit]); + next; + mesq l("But you did not hit... have another try."); + next; + // You might get unlucky and the wind may change + if(rand(5) == 2) goto L_Game; + mesq l("Hurry up, before the wind changes."); + next; + goto L_Start; + +L_Success: + mesn l("Gwendolyn Bowmaker"); + mesq l("Wonderful! That was great! Directly hit the bull's eye!"); // TRANSLATORS: manner of speak, "to hit exactly on the target" + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("Maybe you did it, after all!"); + next; + mesq l("Let me inspect it..."); + mesc l("She walks to the target."); + next; + if( rand2(400) > (2*readparam2(bDex) + readparam2(bAgi))) + goto L_Miss; + + setq HurnscaldQuest_HarkEye, 6; + + skill TMW2_CHARGEDARROW, 1, 0; + getexp .QUEST_HAWK_EXP, 0; + mesn l("Gwendolyn Bowmaker"); + mesq l("Perfect indeed! Congratulations!"); + next; + + mesq l("Remember, there is always something new to learn. And you should not let your training down in order to keep and improve your abilities."); + next; + goto L_Close; + +L_Miss: + mesn l("Gwendolyn Bowmaker"); + mesq l("Ohh! A fingernail aside."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("That is quite impressive, but you can perform better with more training."); + next; + goto L_Game; + +L_State6: + mesn l("Gwendolyn Bowmaker"); + mesq l("You really have a reason to be proud."); + next; + goto L_Close; + +L_Abort: + mes ""; + mesn l("Gwendolyn Bowmaker"); + mesq l("That's a pity. Well, if you change your mind, come back any time."); + next; + goto L_Close; + +L_No_Money: + mes ""; + mesn l("Gwendolyn Bowmaker"); + mesq l("I am sorry, but it seems that you don't have enough money. Come back when you have reorganized your finances."); + next; + goto L_Close; + +L_NoBow: + mesq l("As an archer, you should always carry your bow with you. Doesn't warriors carry those heavy swords without complaining? Go and get it."); + next; + mesq l("Also, I don't care if you don't like the @@. That's the weapon a true archer should use!", getitemlink(ForestBow)); + goto L_Close; + +L_FakeBow: + mesn l("Gwendolyn Bowmaker"); + mesq l("You just bought it? You won't learn anything from that."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("To understand the nature of this bow, you have to collect the needed material by yourself. Only this way will you get a feeling of what it means to use such a weapon."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("The best thing would be to let you make the bow by yourself. But I guess, that would go to far. And it would take ages to get a high quality bow."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("Do as I told you, and talk to Alan for the bow!"); + next; + goto L_Close; + +L_Insufficient: + mesn l("Gwendolyn Bowmaker"); + mesq l("Maybe I should also give you a lesson in how to count? You don't have enough."); + next; + goto L_Close; + +L_NotReady: + mesn l("Gwendolyn Bowmaker"); + mesq l("I am pleased about your progress, but you are not ready for the next step yet. Go and do some more training, before you come back."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("Remember also: It is wise to speak with people you meet on your journey, there is always something new to learn."); + next; + goto L_Close; + +L_Arrows: + mesc l("Gwendolyn sighs and shake her head."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("Another lecture: Take always enough arrows with you. Go and come back when you are equipped adequately."); + next; + mesn l("Gwendolyn Bowmaker"); + mesq l("For this training, I need @@, not the usual arrow you use.", getitemlink(TrainingArrow)); + next; + goto L_Close; + +L_Close: + @paramX=0; + @paramY=0; + @returnIndex=0; + closedialog; + goodbye; + close; + +S_getDirection: + // get @paramX @paramY as parameters + // returns an index where these parameters are in relation to origin + // 123 + // 456 whereas 5 is the origin. (3,6,9 there is paramX>0); (1,2,3 there is paramY>0) + // 789 + //first check the quadrants and after check more precisely + if(@paramX>0 && @paramY>0) @returnIndex = 3; + if(@paramX>0 && @paramY<0) @returnIndex = 9; + if(@paramX<0 && @paramY>0) @returnIndex = 1; + if(@paramX<0 && @paramY<0) @returnIndex = 7; + + //straight left + if ( (@paramX < 0) && (@paramY >= @paramX/2) && (-@paramY >= @paramX/2) ) @returnIndex = 4; + + //straight right + if ( (@paramX > 0) && (@paramY <= @paramX/2) && (-@paramY <= @paramX/2) ) @returnIndex = 6; + + //straight above + if ( (@paramY > 0) && (@paramX <= @paramY/2) && (-@paramX <= @paramY/2) ) @returnIndex = 2; + + //straight below + if ( (@paramY < 0) && (@paramX >= @paramY/2) && (-@paramX >= @paramY/2) ) @returnIndex = 8; + + //check the origin, because the origin is found by the "straight" lines as well. (should not, but is.. maybe a bug?) + if( @paramX==0 && @paramY==0 ) @returnIndex = 5; + return; + +OnInit: + // schoolfee in gp + .SCHOOLFEE = 2500; + + // how many eggs do you need of each kind? + .CAVE_EGGS_AMOUNT = 21; + .SNAKE_EGGS_AMOUNT = 15; + .MOUNTAIN_EGGS_AMOUNT = 7; + + // experience for eggs + .QUEST_EGG_EXP = 7500; + + // The needed level for the minigame: + .BASELEVEL_GAME = 45; + + // experience gained for hitting the bullseye: + .QUEST_HAWK_EXP = 12000; + + .sex = G_FEMALE; + .distance = 7; + end; +} diff --git a/npc/012-1/hinnak.txt b/npc/012-1/hinnak.txt new file mode 100644 index 0000000..47b019c --- /dev/null +++ b/npc/012-1/hinnak.txt @@ -0,0 +1,162 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Hurnscald farmer. + +012-1,105,30,0 script Hinnak NPC_ORC,{ + .@q1=getq(HurnscaldQuest_Farmers); + .@q2=getq2(HurnscaldQuest_Farmers); + mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Ah, if it isn't @@? You're the talk of the town!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + if (.@q1 == 0) goto L_Start; + if (.@q1 == 1) goto L_Check; + if (.@q1 == 4) goto L_Letter; + if (.@q1 == 5) goto L_Thanks; + mesq l("Maybe you could now help my friend Oscar. His farm is west of here."); + close; + +L_Start: + mesq l("I hate you Pinkies, and I hate the Monster King too, for bringing forth these plagues!!"); + next; + mesn strcharinfo(0); + mesc l("The farmer seems mad and in need of help. Will you help him?"); + if (askyesno() != ASK_YES) { + mesq l("Sorry, I have to go."); + close; + } + next; + mesn; + mesq l("Ah, so you're willing to help? Great! Because I HATE THEM ALL!"); + next; + if (readparam2(bAgi) < 20) goto L_Slow; + if (BaseLevel < 20) goto L_Weak; + mesn; + mesq l("They jump left and right and left and right again, and I can't catch them."); + next; + mesn; + mesq l("Please kill some of them, and bring me 10 @@!", getitemlink(PinkAntenna)); + setq HurnscaldQuest_Farmers, 1, 0; + close; + +OnKillPinkie: + .@q1=getq(HurnscaldQuest_Farmers); + .@q2=getq2(HurnscaldQuest_Farmers); + if (.@q1 == 1) { + setq2 HurnscaldQuest_Farmers, .@q2+1; + if (! (.@q2+1) % 10) + dispbottom l("@@ pinkies killed on @@'s field.", .@q2+1, l("Oscar")); + } + fix_mobkill(Pinkie); + end; + +L_Slow: + mesn; + mesq l("But you're too slow to catch any of them. Sorry."); + next; + mesc l("Raise agility to do this quest."); + close; + +L_Weak: + mesn; + mesq l("But you're too weak to beat any of them. Sorry."); + close; + +L_Check: + mesq l("Hey, how is the monster extermination going?"); + next; + mesn; + // You in average need to kill 244 Pinkies. You get this bonus for not leaving the fields. + if (.@q2 >= 210) + mesq l("You killed so many Pinkies, I don't care with the Antennas anymore! They are almost gone!"); + else if (.@q2 >= 150) + mesq l("I see you are doing good. Keep slaying them, hahaah!"); + else if (.@q2 >= 100) + mesq l("Good job, you already killed over 100! Hahah, that sure teach them a lesson!"); + else if (.@q2 >= 50) + mesq l("Yeah, teach them a lesson! Keep going!"); + else + mesq l("Go kill them!!"); + next; + menu + rif (.@q2 >= 210 && countitem(PinkAntenna) >= 10, l("No, I insist, keep the Antennas as a trophy.")), L_RealHelp, + rif (.@q2 >= 210 && countitem(PinkAntenna) < 10, l("Thanks, they don't like to drop their Antennas.")), L_TrueHelp, + rif (.@q2 >= 100 && .@q2 < 210 && countitem(PinkAntenna) >= 10, l("Here are the Antennas.")), L_Help, + rif (.@q2 < 100 && countitem(PinkAntenna) >= 10, l("Here are the Antennas.")), L_NoKill, + l("I'm not done yet. I'll be back."), -; + close; + +L_RealHelp: + inventoryplace Scythe, 1; + delitem PinkAntenna, 10; + getexp 1600, 60; + Zeny=Zeny+2500; + setq HurnscaldQuest_Farmers, 2, 0; + getitem Scythe, 1; + mes ""; + mesn; + mesq l("Waw! You really did it, you're the savior of my farm!! You have my eternal gratitute. Here is 2500 GP for your troubles!"); + close; + +L_TrueHelp: + mes ""; + mesn; + mesq l("I know how hard it is! I hate them with all my heart, only seeing you killing them left and right was GREAT!"); + next; + inventoryplace Scythe, 1; + getitem Scythe, 1; + getexp 1000, 0; + Zeny=Zeny+500; + setq HurnscaldQuest_Farmers, 2, 0; + mesn; + mesq l("You have my eternal gratitute. Here is 500 GP for your troubles."); + close; + +L_Help: + inventoryplace Scythe, 1; + delitem PinkAntenna, 10; + getexp 1000, 0; + Zeny=Zeny+1000; + setq HurnscaldQuest_Farmers, 2, 0; + getitem Scythe, 1; + mes ""; + mesn; + mesq l("Many thanks. You have my eternal gratitute. Here is 1000 GP for your troubles."); + close; + +L_NoKill: + mesn; + mesq l("That won't do it. I want to see PINK BLOOD! No... not so cluttered. Just kill the Pinkies on my farm."); + next; + mesn; + mesq l("The antennas by themselves mean nothing, I want to see you killing my ENEMIES, the pinkies!"); + close; + +L_Letter: + mesq l("You're the savior of Hurnscald crops. Half from the world would die from famine, weren't for you."); + next; + mesn; + mesq l("@@ and I signed this letter. Deliver it to Airlia on the Town Hall, and she'll reward you correctly.", l("Oscar")); + close; + +L_Thanks: + mesq l("Many thanks for all the help!"); + next; + GHQ_Assign(Pinkie, "Hurnscald", getitemlink(PinkHelmet) + col(l(" (Hinnak's and Oscar's fields Pinkies may not count)"), 1)); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FarmerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, TulimsharGuardBoots); + setunitdata(.@npcId, UDT_WEAPON, CottonTrousers); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 0); + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/012-1/igor.txt b/npc/012-1/igor.txt new file mode 100644 index 0000000..dfd9b9b --- /dev/null +++ b/npc/012-1/igor.txt @@ -0,0 +1,14 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Igor gives the player latest news on the world + +012-1,126,59,0 script Igor NPC_JOURNALMAN,{ + Journalman(.name$); + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/012-1/jack.txt b/npc/012-1/jack.txt new file mode 100644 index 0000000..b36c09f --- /dev/null +++ b/npc/012-1/jack.txt @@ -0,0 +1,180 @@ +// TMW-2 Script +// Originals: TMW Org. +// +// Author: +// Jesusalva +// Description: +// Jack is Hurnscald Lumberjack + +012-1,69,93,0 script Jack NPC_LUMBERJACK,{ + .@q1=getq(HurnscaldQuest_ForestBow); + //.@q2=getq(HurnscaldQuest_WoodShield); + + mesn "Jack, the Lumberjack"; + mesq l("Hello there! I'm Jack, the Lumberjack, and enemy of all trees. If you need some firewood, just let me know."); + mes ""; + select + l("Nothing at the moment, thanks."), + l("I want you to transform my raw log in a wooden log"), + rif(.@q1 == 1, l("I need a sturdy wood piece for a Forest Bow.")); + + mes ""; + + switch (@menu) { + case 1: + close; + break; + case 2: + goto L_Polish; + break; + case 3: + goto L_ForestBow; + break; + } + + close; + +L_Polish: + mesn; + mes l("Yes, unless you're doing something VERY special, a Wooden Log is what you need."); + mes l("I can make a @@ for just one @@ and @@ GP for my work.", getitemlink(WoodenLog), getitemlink(RawLog), .cost); + input .@count; + mes ""; + + if (.@count == 0) + close; + + .@Cost = .@count * .cost; + .@empty = countitem(RawLog); + + if (.@empty < .@count || Zeny < .@Cost) { + mesn; + mesq l("You can't afford my services for that amount. Sorry."); + } + + inventoryplace WoodenLog, .@count; + + Zeny = Zeny - .@Cost; + delitem RawLog, .@count; + getitem WoodenLog, .@count; + mesc l("There you go, %s. If you don't know anything about crafting I heard someone on Tulimshar's Terranite Forge can help?", lg("gal", "pal")); + close; + +L_ForestBow: + .@k=getq2(HurnscaldQuest_ForestBow); + + switch (.@k) { + case 0: + mesn; + mesq l("Ah, don't bother me with that. That wood must be special, it must bend and cannot break. Too difficult to find!"); + next; + mesn; + mesq l("I also do not keep any stock of them. If you really want it, you can bring me some Raw Logs. I'll show you which one is the good one. Deal?"); + next; + if (BaseLevel < 25) { + mesn; + mesq l("Actually, nevermind. I'll wait you grind level 25 first, then we can do this."); // Note: Fisherman part is level 30! + close; + } + mesn strcharinfo(0); + mesq l("I'm railroaded here, I cannot say no. I'll bring you some @@. Break them until one of them fit.", getitemlink(RawLog)); + setq2 HurnscaldQuest_ForestBow, 1; + close; + break; + case 99: + mesn; + mesq l("Good luck..."); + break; + default: + goto L_Loop; + } + goto L_Close; + +L_Loop: + .@k=getq2(HurnscaldQuest_ForestBow); + mesn; + mesq l("Bring me your wood, and I'll show you which one is sturdy, yet flexible enough to make a good Forest Bow."); + mes ""; + select + rif(countitem(RawLog) > 0, l("Try this one.")), + l("Don't break my stuff."); + + if (@menu == 2) + close; + + mesc l("Jack bends the log over his knee."); + delitem RawLog, 1; + .@success=rand(6,26)-.@k; // You will never need more than 20 logs nor less than 5 logs + + if (.@success <= 1) { + mesc l("The wood bends a little, but doesn't breaks."); + getexp 295, 100; // 10% of max exp + setq2 HurnscaldQuest_ForestBow, 99; + next; + mesn; + mesq lg("That's the right wood, my son. Here, I'll wrap it so you don't accidentaly lose it. Good luck with your quest!"); + close; + } else { + mesc l("However, the log breaks with a loud crack."); + Zeny=Zeny+10; + getexp 5, 1; + setq2 HurnscaldQuest_ForestBow, .@k+1; + next; + mesn; + mesq lg("Sorry, son. I know how hard this is. But this is teaching you a valuable lesson, your eye will be more sharp for quality wood from now on."); + next; + } + + goto L_Loop; + +/* Copy-paste from TMW Org. + I'll uncomment and rewrite when time comes. + +L_NohMask_Ask: + menu + "I'll keep that in mind.", L_Close, + "I heard you aren't delivering any more living wood. Why not?", L_Next, + "Have you seen anything that might be connected to the recent robberies in town?", L_NohMask_Answer; + +L_NohMask_Answer: + mesn "Jack, the Lumberjack"; + mes "\"Sorry, no.\""; + goto L_Close; + +L_Made_Shield: + mesn "Jack, the Lumberjack"; + mes "\"Speaking of that shield...\""; + mes "\"It has put me behind on all these new Nivalis orders, now that the ship is running there.\""; + next; + mes "\"Care to help out? I'll pay you for your time.\""; + goto L_Daily; + +L_Daily: + @dq_level = 30; + @dq_cost = 25; + @dq_count = 30; + @dq_name$ = "RawLog"; + @dq_friendly_name$ = "raw logs"; + @dq_money = 4000; + @dq_exp = 4000; + + callfunc "DailyQuest"; + + next; + + mesn "Jack, the Lumberjack"; + mes "\"Check back with me tomorrow, and I'll let you know if I need any help with that shipment.\""; + + goto L_Close; +*/ + +L_Close: + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + .cost=60; + end; + +} diff --git a/npc/012-1/locamit.txt b/npc/012-1/locamit.txt new file mode 100644 index 0000000..c4acd9d --- /dev/null +++ b/npc/012-1/locamit.txt @@ -0,0 +1,72 @@ +// TMW2 scripts. +// Authors: +// Saulc +// Jesusalva +// Reid +// Travolta +// Description: +// Tamiloc is the barber. +// Locamit is an anagram. + +012-1,60,73,0 script Locamit NPC_ELVEN_FEMALE_ARMOR_SHOP,{ + function setRace { + clear; + setnpcdialogtitle l("Debug - Modify Race"); + mes l("Race") + ": " + $@allraces$[Class]; + next; + mes l("Please select the desired race."); + switch (select("Human:Ukar:Redy:Elf:Orc:Raijin:Tritan")) + { + default: + jobchange max(0, @menu-1); + } + return; + } + + + mesn; + mesq l("Hi! Do you want a hair cut?"); + + do + { + select + l("What is my current hairstyle and hair color?"), + l("I'd like to get a different style."), + l("Can you do something with my color?"), + rif(is_staff(), l("I am a GM, and I want to change my Race!")), + l("I'm fine for now, thank you."); + + switch (@menu) { + case 1: + BarberSayStyle 3; + break; + case 2: + BarberChangeStyle; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Enjoy your new style."); + l("Anything else?"); + break; + case 3: + BarberChangeColor; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("I hope you like this color."); + l("Anything else?"); + break; + case 4: + setRace; + break; + case 5: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Feel free to come visit me another time."); + + goodbye; + } + } while (1); + close; + + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/012-1/luffyx.txt b/npc/012-1/luffyx.txt new file mode 100644 index 0000000..d2cb239 --- /dev/null +++ b/npc/012-1/luffyx.txt @@ -0,0 +1,87 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Hunt mouboos, Summer Quest + +012-1,68,74,0 script Luffyx NPC_RAIJIN,{ + .@q=getq(SQuest_Summer); + mesn; + if (season() == SUMMER) { + mesq l("Yay it's summer! I love June 21st, and the summer vacations!"); + if (.@q < 1) + goto L_SummerQuest; + } else { + mesq l("Aww it's not summer. I love June 21st, and the summer vacations!"); + } + +L_Main: + if (GHQUEST) + GHQ_Assign(ForestMushroom, "Hurnscald"); + close; + +L_SummerQuest: + next; + mesn; + mesq l("Hey, do you know what is good on summer? @@!", getitemlink(LuffyxSummerShorts)); + next; + mesn; + mes l("What about you bring me:"); + mes l("@@/80 @@", countitem(CactusCocktail), getitemlink(CactusCocktail)); + mes l("@@/40 @@", countitem(AppleCocktail), getitemlink(AppleCocktail)); + mes l("@@/25 @@", countitem(CherryCocktail), getitemlink(CherryCocktail)); + mes l("@@/1 @@", countitem(JeansShorts), getitemlink(JeansShorts)); + next; + select + l("Not now, thanks"), + l("To be honest, I have that with me!"); + + mes ""; + if (@menu == 1) + goto L_Main; + if (@menu == 2) { + if ( + countitem(CactusCocktail) < 80 || + countitem(AppleCocktail) < 40 || + countitem(CherryCocktail) < 25 || + !countitem(JeansShorts) + ) goto L_Lying; + + inventoryplace LuffyxSummerShorts, 1; + delitem CherryCocktail, 25; + delitem AppleCocktail, 40; + delitem CactusCocktail, 80; + delitem JeansShorts, 1; + getitem LuffyxSummerShorts, 1; + getexp 6500, 120; + setq SQuest_Summer, 1; + mesn; + mesq l("Yay yay! Many thanks! Here, take the reward as promised!"); + next; + mesn; + mesq l("We can do this again on next summer!"); + goto L_Main; + } + + close; + +L_Lying: + mesn; + mesq l("Please don't lie to me..."); + goto L_Main; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, CommunityShirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, JeansChaps); + setunitdata(.@npcId, UDT_HEADBOTTOM, DeepBlackBoots); + setunitdata(.@npcId, UDT_WEAPON, BugSlayer); + setunitdata(.@npcId, UDT_HAIRSTYLE, 13); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .distance=4; + .sex=G_MALE; + end; + +} + diff --git a/npc/012-1/mahad.txt b/npc/012-1/mahad.txt new file mode 100644 index 0000000..e37cc69 --- /dev/null +++ b/npc/012-1/mahad.txt @@ -0,0 +1,68 @@ +// TMW-2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Mahad is the Hurnscald Well Master +// ...Well, he takes the water from the pond, but you shouldn't mind this. +// It's still safe to drink, 100% warranted! +// Note: You may get a Grass Carp (worth 100 GP) instead. Bottle is lost? O.o + +012-1,117,54,0 script Mahad NPC_PLAYER,{ + mesn; + mes l("I can fill your bottle with water for only @@ gp the bottle.", .price); + mes l("After all, I am the Well Master!"); + input .@count; + mes ""; + + if (!.@count) + close; + + .@gp = .@count * .price; + + // Not enough cash + if (Zeny < .@gp) { + mesn; + mes l("You don't have enough gold! You need @@ gp.", .@gp); + close; + } + + // Not enough bottles + if (countitem(EmptyBottle) < .@count) { + mesn; + mes l("You don't have that many empty bottles!"); + close; + } + + inventoryplace GrassCarp, .@count, BottleOfWoodlandWater, .@count; + delitem EmptyBottle, .@count; + + // Calculate how many grass carp you'll get + .@iced=0; + for (.@i=0; .@i < .@count; .@i++) { + if (rand(1,1000) < 11) + .@iced++; + } + + // Apply the results and have a happy day! + Zeny-=.@gp; + if (.@iced) + getitem GrassCarp, .@iced; + getitem BottleOfWoodlandWater, .@count-.@iced; + close; + +OnInit: + .price = 60; + .sex = G_MALE; + .distance = 7; + + .@npcId = getnpcid(.name$); + // Check items.xml for info about this + setunitdata(.@npcId, UDT_HEADTOP, InfantryHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + end; +} diff --git a/npc/012-1/mapflags.txt b/npc/012-1/mapflags.txt new file mode 100644 index 0000000..19c4352 --- /dev/null +++ b/npc/012-1/mapflags.txt @@ -0,0 +1,9 @@ +012-1 mapflag town +012-1 mapflag nopenalty +012-2 mapflag town +012-3 mapflag town +012-4 mapflag town +012-5 mapflag town +012-6 mapflag town +012-7 mapflag town +012-8 mapflag town diff --git a/npc/012-1/milly.txt b/npc/012-1/milly.txt new file mode 100644 index 0000000..1935858 --- /dev/null +++ b/npc/012-1/milly.txt @@ -0,0 +1,155 @@ +// TMW2 script. +// Authors: +// Jesusalva +// Description: +// Milly here is mixed with Crastur's almanach quest. +// She controls "World's Hero quest", which is now her MAIN QUEST; +// But maybe later she'll get nodes about Khafar etc but not like in TMW +// +// Her reward is a Plush Mouboo. Along about 1000 Mouboo Figurines, and whatever +// grand reward for completing Main Storyline, and a mega blaster level at magic, +// and probably within another general quest, YOU CAN SUMMON THE MOUBOOTAUR +// Eh... I think you need all seven Mana Fragments to control it, though. +// Without these fragments, you'll just face the baddest boss fight ever and die +// miserably, because the moubootaur is... well... Simply put... THE MOUBOOTAUR. + +012-1,111,66,0 script Milly NPC_TEDDYGIRL,{ + .@q=getq(General_Milly); + mesn; + if (.@q >= 1) mesq lg("Hello my hero!"); + else mesq l("Hello."); + if (.@q == 0) goto L_Assign; + else if (.@q == 1) goto L_Feat; + close; + +L_Assign: + next; + mesn; + mesq lg("hey, hey, are you a hero, are you a hero?"); + next; + mesn; + mesq l("I want to meet a hero!"); + next; + mesc b(l(".:: World's Hero Quest ::.")), 3; + msObjective(reputation("Candor") >= 100, l("* become @@ Hero", l("Candor"))); + msObjective(reputation("Tulim") >= 100, l("* become @@ Hero", l("Tulimshar"))); + msObjective(reputation("Halin") >= 100, l("* become @@ Hero", l("Halinarzo"))); + msObjective(reputation("Hurns") >= 100, l("* become @@ Hero", l("Hurnscald"))); + msObjective(reputation("LoF") >= 100, l("* become @@ Hero", l("Land Of Fire"))); + msObjective(reputation("Nival") >= 100, l("* become @@ Hero", l("Nivalis"))); + msObjective(reputation("Frostia") >= 100, l("* become @@ Hero", l("Frostia"))); + next; + if (reputation("Candor") >= 100 && + reputation("Tulim") >= 100 && + reputation("Halin") >= 100 && + reputation("Hurns") >= 100 && + reputation("LoF") >= 100 && + reputation("Nival") >= 100 && + reputation("Frostia") >= 100) goto L_Finish; + mesc l("You can become a hero by completing every quest in a certain location."); + close; + +L_Finish: + mesn; + mesq lg("Wo-wo-wow! You really did it! You're the best!"); + next; + mesn; + mesq l("But the game still is in development, so not every quest was added yet."); + next; + mesn; + mesq l("Do you want to get a reward nonetheless? You will lose @@.", b(l("any planned experience and gold reward."))); + mesc l("Also note that, under team's discretion, all rewards from this quest might be erasen along quest state."); + mesc l("This would mean you might need to complete this quest again in the future."); + next; + // Debug + percentheal 100, 100; + sc_start SC_ATTHASTE_POTION1, 1800000, 30; + /* + mesn; + mesc l("Saulc was here"), 1; + close; + */ + // / Debug + if (askyesno() == ASK_YES) { + mes ""; + inventoryplace PlushMouboo, 1; + setq General_Milly, 1; + getitem PlushMouboo, 1; + getexp 100, 100; // No, not really. + mesn; + mesq lg("Here you go, miss! Thanks for being my hero! <3", "Here you go, mister! Thanks for being my hero! <3"); + } + close; + +L_Feat: + next; + mesn; + mesq l("Hey hey, can you impress me? Can you impress me?"); + next; + mesn; + mesq l("I want you to impress me, so I can brag about you to all my friends!"); + next; + mesc b(l(".:: Impressive Hero Quest ::.")), 3; + msObjective(YETIKING_WINNER, l("* Impress %s", l("the Yeti King"))); + msObjective(HEROESHOLD_WINNER, l("* Impress %s", l("Colonel Dustman"))); + msObjective(REBIRTH_WINNER, l("* Impress %s", l("Sir Phoenix"))); + msObjective(QUIRINO_WINNER, l("* Impress %s", l("Quirin Voraz"))); + msObjective(GEMINI_WINNER, l("* Impress %s", l("Valia Gemini"))); + msObjective(GHQ_WINNER, l("* Impress %s", l("Aidan and Pet Detective"))); + msObjective(EPISODE_WINNER, l("* Impress %s", l("The Doctor"))); + msObjective(FORT_1ST_VISIT, l("* Impress %s", l("The Alliance"))); + msObjective(MOUBOOTAUR_WINNER, l("* Impress %s", l("Andrei Sakar"))); + mes ""; + mesc l("You need to finish at least %d/%d tasks.", 7, 9); + .@t = 0; + if (YETIKING_WINNER) .@t += 1; + if (HEROESHOLD_WINNER) .@t += 1; + if (REBIRTH_WINNER) .@t += 1; + if (QUIRINO_WINNER) .@t += 1; + if (GEMINI_WINNER) .@t += 1; + if (GHQ_WINNER) .@t += 1; + if (EPISODE_WINNER) .@t += 1; + if (FORT_1ST_VISIT) .@t += 1; + if (MOUBOOTAUR_WINNER) .@t += 1; + if (.@t >= 7) goto L_Epic; + close; + +L_Epic: + next; + mesn; + mesq lg("Wo-wo-wow! You really did it! You're the best!"); + next; + mesn; + mesq l("But the game still is in development, so not every great feat was added yet."); + next; + mesn; + mesq l("Do you want to get a reward nonetheless? You will lose @@.", b(l("any planned experience and gold reward."))); + mesc l("Also note that, under team's discretion, all rewards from this quest might be erasen along quest state."); + mesc l("This would mean you might need to complete this quest again in the future."); + next; + // Debug + percentheal 100, 100; + sc_start SC_ATTHASTE_POTION1, 1800000, 30; + /* + mesn; + mesc l("Saulc was here"), 1; + close; + */ + // / Debug + if (askyesno() == ASK_YES) { + mes ""; + inventoryplace SunnyCrystal, 1; + setq General_Milly, 2; + getitem SunnyCrystal, 1; + getexp 100, 100; // No, not really. + mesn; + mesq lg("Here you go, miss! Thanks for being my hero! <3", "Here you go, mister! Thanks for being my hero! <3"); + } + close; + +OnInit: + .sex=G_FEMALE; + .distance=5; + end; +} + diff --git a/npc/012-1/richard.txt b/npc/012-1/richard.txt new file mode 100644 index 0000000..5dc2c31 --- /dev/null +++ b/npc/012-1/richard.txt @@ -0,0 +1,14 @@ +// TMW-2 Script. +// Author: +// Saulc +// Jesusalva + +012-1,65,64,0 script Richard NPC_LLOYD,{ + Banker(.name$, "Hurnscald", 10000); + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/012-1/ship.txt b/npc/012-1/ship.txt new file mode 100644 index 0000000..b478e35 --- /dev/null +++ b/npc/012-1/ship.txt @@ -0,0 +1,23 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// This script controls access to Ships, fixing variables. + +012-1,157,65,0 script HurnsShip#M NPC_HIDDEN,0,0,{ + +OnTouch: + EnterTown("Hurns"); + goto L_Warp; + +L_Warp: + /* Is Hurnscald already liberated? Precendence. */ + if (!$HURNS_LIBDATE) { + warp "Save", 0, 0; + end; + } + + warp "016-1@"+LOCATION$, 21, 26; + closedialog; + close; +} diff --git a/npc/012-1/shoppakep.txt b/npc/012-1/shoppakep.txt new file mode 100644 index 0000000..13fb8f3 --- /dev/null +++ b/npc/012-1/shoppakep.txt @@ -0,0 +1,81 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Random Hurnscald Shopper with random overpriced stuff and gifts + +012-1,58,64,0 script Shoppa Kep NPC_SHOPPAKEP,{ + mesn; + mesq lg("Welcome! Only the finest wares!"); + next; + closedialog; + npcshopattach(.name$); + shop .name$; + close; + + function ShoppaKepItem { + return any(Aquada, Manana, HalfEggshell, SpearmintTea); + } + +OnInit: + .CurrentItem=ShoppaKepItem(); + .ThisRebootAmou=rand2(30,70); + + .sex = G_FEMALE; + .distance = 5; + + + sleep(SHOPWAIT); + tradertype(NST_MARKET); + sellitem CottonTrousers, 80000, 1; + sellitem CottonSkirt, 40000, 1; + sellitem MiniSkirt, 35000, 1; + sellitem ShortTankTop, 20000, 1; + sellitem CottonGloves, 20000, 1; + sellitem CottonBoots, 10000, 1; + + sellitem PinkieLeg, 500, 1; + sellitem Bread, -1, 50; + sellitem Piberries, -1, 30; + sellitem Plushroom, -1, 3; + sellitem Chagashroom, -1, 3; + + sellitem PiouFeathers, -1, .ThisRebootAmou; + sellitem .CurrentItem, getiteminfo(.CurrentItem, ITEMINFO_BUYPRICE)*15/10, rand2(2,6); + + end; + +OnClock1759: + restoreshopitem PinkieLeg, 500, 1; +OnClock0546: + restoreshopitem CottonTrousers, 80000, 1; + restoreshopitem CottonSkirt, 40000, 1; + restoreshopitem MiniSkirt, 35000, 1; + restoreshopitem ShortTankTop, 20000, 1; + restoreshopitem CottonGloves, 20000, 1; + restoreshopitem CottonBoots, 10000, 1; + + restoreshopitem Bread, -1, 50; + restoreshopitem Piberries, -1, 30; + restoreshopitem Plushroom, -1, 3; + restoreshopitem Chagashroom, -1, 3; + + restoreshopitem PiouFeathers, -1, .ThisRebootAmou; + + stopselling(.CurrentItem); + .CurrentItem=ShoppaKepItem(); + sellitem .CurrentItem, getiteminfo(.CurrentItem, ITEMINFO_BUYPRICE)*15/10, rand2(2,6); + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} + diff --git a/npc/012-1/soul-menhir.txt b/npc/012-1/soul-menhir.txt new file mode 100644 index 0000000..419541f --- /dev/null +++ b/npc/012-1/soul-menhir.txt @@ -0,0 +1,20 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Soul Menhir + +012-1,88,69,0 script Soul Menhir#hurns NPC_SOUL_NORMAL,{ + @map$ = "012-1"; + setarray @Xs, 86, 87, 86, 87; + setarray @Ys, 69, 70, 69, 70; + @x = 0; + @y = 0; + callfunc "SoulMenhir"; + @map$ = ""; + cleararray @Xs[0], 0, getarraysize(@Xs); + cleararray @Ys[0], 0, getarraysize(@Ys); + @x = 0; + @y = 0; + close; +} diff --git a/npc/012-1/statue.txt b/npc/012-1/statue.txt new file mode 100644 index 0000000..9ff9044 --- /dev/null +++ b/npc/012-1/statue.txt @@ -0,0 +1,31 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// These statues are of great honor to whoever have their name written in them! + +012-1,84,63,0 script Hero Statue#012-1 NPC_STATUE_ANDREI,{ + + mes l("This statue was built for memory of Andrei Sakar, the greatest hero this world has even seen."); + mes l("For defending Hurnscald alone and saving all its inhabitants."); + mes l("For fighting against the Monster King once and getting out alive to tell the story."); + mes l("For all his great deeds, and thousands of lives he saved, this statue is in his honor."); + if ($MOST_HEROIC$ == "") + goto L_Fame; + next; + mes l("Also in honor of @@, who did a great act of bravery recently. May they keep protecting our world!", $MOST_HEROIC$); + +L_Fame: + next; + mesq l("All hail the ones who proven their worth before the whole Alliance!"); + + HallOfHonor(); + HallOfGuild(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + diff --git a/npc/012-1/terezin.txt b/npc/012-1/terezin.txt new file mode 100644 index 0000000..82a5d09 --- /dev/null +++ b/npc/012-1/terezin.txt @@ -0,0 +1,61 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Hinnak's Wife, Terezin (renamed to make an Easter Egg) +// NPC_MONA? + +012-1,110,41,0 script Tezzerin NPC_ELVEN_FEMALE_READING,{ + .@q1=getq(HurnscaldQuest_Farmers); + .@q2=getq2(HurnscaldQuest_Farmers); + if (strcharinfo(0) == $MOST_HEROIC$) mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Ah, @@, welcome!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + if (.@q1 == 5 && .@q2 == 0) goto L_PinkieHat; + if (.@q1 == 4) goto L_Almost; + if (.@q1 >= 2) goto L_Thanks; + mesn; + mesq l("Ah, my husband Hinnak is so hard working..."); + close; + +L_Almost: + mesn; + mesq l("You deserve a reward for helping my husband Hinnak, but please, get Oscar's reward first."); + close; + +L_Thanks: + mesn; + mesq l("Thanks for helping my husband. I'll think if there's something I can do for you, why don't you help Oscar on the meanwhile...?"); + close; + +L_PinkieHat: + mesn; + mesq l("You deserve a reward for helping my husband. I can make you a nice gift."); + next; + mesn; + mesq l("Please bring me a single @@ and @@ GP, and I'll give you a memeto.", getitemlink(PinkAntenna), 6500); + if (countitem(PinkAntenna) < 1 || Zeny < 6500) + close; + next; + mesn strcharinfo(0); + mesc l("Give the Antenna and the GP to her?"); + if (askyesno() != ASK_YES) { + mesq l("I'll seek one for you."); + close; + } + delitem PinkAntenna, 1; + if (Zeny < 6500) + close; + Zeny=Zeny-6500; + getitem PinkieHat, 1; + mesn; + mesq l("Here, I did this @@ for you! I'm pretty sure this is also a drop, but that's literally everything I could do for helping my husband.", getitemlink(PinkieHat)); + setq2 HurnscaldQuest_Farmers, 1; + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/012-1/town.txt b/npc/012-1/town.txt new file mode 100644 index 0000000..fdffb17 --- /dev/null +++ b/npc/012-1/town.txt @@ -0,0 +1,13 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Reset LOCATION$ when entering a town + +012-1,78,101,0 script #LocHurns NPC_HIDDEN,3,1,{ +OnTouch: + EnterTown("Hurns"); end; +} +012-1,132,100,0 duplicate(#LocHurns) #LocHurnsS NPC_HIDDEN,2,1 +012-1,23,61,0 duplicate(#LocHurns) #LocHurnsW NPC_HIDDEN,1,2 +012-1,78,18,0 duplicate(#LocHurns) #LocHurnsE NPC_HIDDEN,3,1 diff --git a/npc/012-1/wateranimation.txt b/npc/012-1/wateranimation.txt new file mode 100644 index 0000000..4621afb --- /dev/null +++ b/npc/012-1/wateranimation.txt @@ -0,0 +1,29 @@ +// TMW2 scripts. +// Author: +// gumi +// Reid +// Saulc +// Jesusalva +// Description: +// Water animations, splash, fishes, etc... + +012-1,119,54,0 script #Hurn_WAM0 NPC_WATER_SPLASH,{ + + fishing(1, + CommonCarp, + GrassCarp, + BottleOfWoodlandWater); + + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +012-1,137,58,0 duplicate(#Hurn_WAM0) #Hurn_WAM1 NPC_WATER_SPLASH +012-1,137,88,0 duplicate(#Hurn_WAM0) #Hurn_WAM2 NPC_WATER_SPLASH +012-1,59,12,0 duplicate(#Hurn_WAM0) #Hurn_WAM3 NPC_WATER_SPLASH +012-1,138,31,0 duplicate(#Hurn_WAM0) #Hurn_WAM4 NPC_WATER_SPLASH + diff --git a/npc/012-2/GonzoDark.txt b/npc/012-2/GonzoDark.txt new file mode 100644 index 0000000..38a74ae --- /dev/null +++ b/npc/012-2/GonzoDark.txt @@ -0,0 +1,25 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Monthly Sponsor Quest +// Variable: +// SQuest_Sponsor +// Quest ID: 2 + +012-2,50,81,0 script GonzoDark Sponsor NPC_LUCAS,{ + mesn; + mesq lg("Yo, girl.", "Yo, man."); + next; + mesn; + mesq l("I sponsor the alliance and all I got was a NPC and access to this ugly room."); + next; + mesn; + mesq l("Well, I don't want to be an snob."); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/012-2/Saulc.txt b/npc/012-2/Saulc.txt new file mode 100644 index 0000000..3b5cadc --- /dev/null +++ b/npc/012-2/Saulc.txt @@ -0,0 +1,25 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Monthly Sponsor Quest +// Variable: +// SQuest_Sponsor +// Quest ID: 1 + +012-2,46,81,0 script Saulc Sponsor NPC_HUMAN_MALE_CHIEF,{ + mesn; + mesq lg("Yo, girl.", "Yo, man."); + next; + mesn; + mesq l("I sponsor the alliance and all I got was a NPC and access to this ugly room."); + next; + mesn; + mesq l("Well, I don't want to be an snob."); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/012-2/_import.txt b/npc/012-2/_import.txt new file mode 100644 index 0000000..23212c6 --- /dev/null +++ b/npc/012-2/_import.txt @@ -0,0 +1,11 @@ +// Map 012-2: Hurnscald Inn +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/012-2/GonzoDark.txt", +"npc/012-2/Saulc.txt", +"npc/012-2/_warps.txt", +"npc/012-2/door.txt", +"npc/012-2/helena.txt", +"npc/012-2/khafar.txt", +"npc/012-2/melina.txt", +"npc/012-2/note.txt", +"npc/012-2/rakinorf.txt", diff --git a/npc/012-2/_warps.txt b/npc/012-2/_warps.txt new file mode 100644 index 0000000..44ed24f --- /dev/null +++ b/npc/012-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-2: Hurnscald Inn warps +012-2,36,47,0 warp #012-2_36_47 0,0,012-1,65,56 +012-2,59,83,0 warp #012-2_59_83 0,0,012-2,59,39 diff --git a/npc/012-2/door.txt b/npc/012-2/door.txt new file mode 100644 index 0000000..113a225 --- /dev/null +++ b/npc/012-2/door.txt @@ -0,0 +1,59 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Notes: +// Sponsor Area Only + +012-2,59,38,0 script Sponsors Inn NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (!getgmlevel()) + goto L_Unauthorized; + if (SP_WARN) + SP_WARN=0; + slide 59, 82; + end; + +L_Unauthorized: + // Per r8.0 Apane Invasion, there's a moment on main storyline you'll be allowed in + .@n=getq(General_Narrator); + if (.@n == 5 || .@n == 6) { + slide 59, 82; + end; + } + + if (@sp_notrespass > 0) + goto L_Warning; + dispbottom col(l("Only Sponsors are allowed past this area."),1); + slide 59,39; + @sp_notrespass=1; + end; + +L_Warning: + if (@sp_kick > 0) + goto L_Kick; + dispbottom col(l("Only Sponsors are allowed past this area."),1); + dispbottom col(l("Do not attempt to go there again."),1); + slide 60,40; + @sp_kick=1; + end; + +L_Kick: + if (SP_WARN > 5) + goto L_Ban; + dispbottom col(l("You cannot visit the Sponsor's Inn!"),1); + slide 36,46; + // Force user to disconnect + atcommand "@kick "+strcharinfo(0); + SP_WARN=SP_WARN+1; + end; + +L_Ban: + dispbottom col(l("You cannot visit the Sponsor's Inn!"),1); + slide 36,46; + // Prevent abuse and possible vulnerabilities. + atcommand "@ban 20mn "+strcharinfo(0); + SP_WARN=0; + end; +} diff --git a/npc/012-2/helena.txt b/npc/012-2/helena.txt new file mode 100644 index 0000000..cb3fdb2 --- /dev/null +++ b/npc/012-2/helena.txt @@ -0,0 +1,284 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Lena +// Variables: +// $HELENA_ST +// Tracks how many set of Treasure Keys were given by all players thus far. +// Affects beer prices on Hurnscald. +// HurnscaldQuest_Bandits +// q1 -> Current status +// q2 -> Treasure Key Timer +// q3 -> Bandit Hood Timer + +012-2,47,40,0 script Helena NPC_FEMALE,{ + .@q=getq(HurnscaldQuest_Bandits); + + // Stage 1: Level 30, collect Treasure Key + if (BaseLevel < 30) goto L_TooWeak; + if (.@q == 0) goto L_Start; + if (.@q == 1) goto L_Return; + + // Stage 2: Level 40, collect Bandit Hood + if (BaseLevel < 40) goto L_Weak; + if (.@q == 2) goto L_Quest; + if (.@q == 3) goto L_Hood; + + // Stage 3: Level 45, defeat Bandit Lord + if (BaseLevel < 45) goto L_Busy; + if (.@q == 4) goto L_BanditLord; + if (.@q == 5) goto L_Busy; + if (.@q == 6) goto L_Busy; + if (.@q == 7) goto L_Finish; + + goto L_Daily; + +L_Daily: + .@k=getq2(HurnscaldQuest_Bandits); + .@h=getq3(HurnscaldQuest_Bandits); + mesn; + mesq l("Ah, @@, my friend! Are you here to help us with 5 @@? Or perhaps you have 5 more @@ to show that Hurnscald is getting safer?", strcharinfo(0), getitemlink(TreasureKey), getitemlink(BanditHood)); + mes ""; + select + rif((.@k < santime()) && countitem(TreasureKey) >= 5, l("I have 5 Treasure keys with me.")), + rif((.@h < santime()) && countitem(BanditHood) >= 5, l("I have 5 Bandit Hoods with me.")), + l("Nothing at the moment."); + mes ""; + switch (@menu) { + case 1: + setq2 HurnscaldQuest_Bandits, santime()+(60*60*24); + delitem TreasureKey, 5; + Zeny=Zeny+600; // 600/550 = 9.09% bonus + //Zeny=Zeny+800; // 800/550 = 45.45% bonus + getexp 2000, 0; + $HELENA_ST=$HELENA_ST+2; + mesn; + mesq l("Many thanks! %%s"); + next; + goto L_Daily; + case 2: + setq3 HurnscaldQuest_Bandits, santime()+(60*60*24); + delitem BanditHood, 5; + Zeny=Zeny+250; // 250/155 = 61.29% bonus + getexp 3000, 5; + $HELENA_ST=$HELENA_ST+1; + mesn; + mesq l("Many thanks! %%s"); + next; + goto L_Daily; + } + close; + +L_TooWeak: + mesn; + mesq lg("Hello, madam!", "Hello, sir!"); + next; + mesn; + mesq l("Ah, we have serious problems of robbery. I need someone really strong to help me, and you don't qualify."); + close; + +L_Weak: + mesn; + mesq l("You still need to grow a few levels more before being able to help me out again."); + close; + +L_Busy: + mesn; + mesq l("Ah, @@! I'm busy now, can we talk again later?", strcharinfo(0)); + close; + +L_DoIt: + mesn; + mesq l("@@, we are counting on you! We, the whole Hurnscald town!", strcharinfo(0)); + close; + +///////////////////////// Stage 1 +L_Start: + mesn; + mesq l("Ah, hello."); + next; + mesn; + mesq l("We actually have a problem. Bandits ransacked this tavern, and took a huge loot."); + next; + mesn; + mesq l("We pursued them until the mines, slayed them, and took the chests where they locked our stuff into."); + next; + mesn; + mesq l("The problem is... The slimes ate the keys for the chests. This is not the first time such thing happens."); + next; + // 5 ÷ 4.5% = 112 Copper Slimes. You can kill Yellow Slimes too + mesn; + mesq l("If you bring us 5 @@, we'll be forever grateful.", getitemlink(TreasureKey)); + select + l("Don't worry ma'm, I'll recover the Treasure Keys at once."), + l("Ah... Slimes... Sorry, not my cup of tea..."); + mes ""; + if (@menu == 2) + close; + setq HurnscaldQuest_Bandits, 1; + mesn; + mesq l("Wonderful! I'll be expecting you back."); + close; + +L_Return: + mesn; + mesq lg("Adventurer, did you brought me what I asked? I see you have @@/5 @@.","Adventurer, did you brought me what I asked? I see you have @@/5 @@.", countitem(TreasureKey), getitemlink(TreasureKey)); + mes ""; + select + rif(countitem(TreasureKey) >= 5, l("Yes, take it.")), + rif(countitem(TreasureKey) < 5, l("No, I'll be back with them.")), + l("Hm, can we talk again later?"); + mes ""; + if (@menu != 1) + close; + delitem TreasureKey, 5; + Zeny=Zeny+1000; + getexp 1599,0; // 20% from needed exp + setq HurnscaldQuest_Bandits, 2; + mesn; + mesq l("Hey hey... Good job! We can now use again the stuff we recovered from the bandits."); + next; + mesn; + mesq l("Here is 1000 GP for your efforts. Thanks for making Hurnscald a better place to live."); + next; + mesn; + mesq l("This happens quite often, too. My friends and I are always collecting keys to reduce beer price on Hurnscald. %%2"); + close; + + +///////////////////////// Stage 2 +L_Quest: + mesn; + mesq l("Ah, @@, good thing you are here.", strcharinfo(0)); + next; + mesn; + mesq l("Bandits are a huge threat to Hurnscald. We're just a small farming town, and they're countless."); + next; + mesn; + mesq l("I did a travel to their cave, I wondered why their faces are never seen. Reason is that they're monsters."); + next; + mesn; + mesq l("Well, perhaps a few of them are rebels, I mean, people like us, but many of them are monsters."); + next; + mesn; + mesq l("Unfortunately, I was cursed to never enter their cave again. They used a @@ to do that!", getitemlink(BlueManaPearl)); + next; + if (countitem(BlueManaPearl) >= 1) { + mesn; + mesq l("Ah... You have one too. Be careful with it, please. Many people lost their lives because they didn't handled that correctly."); + next; + } + mesn; + mesq l("Thankfully that item is too rare. I want somebody to slay the bandit leader, but if you want to do it, you must prove yourself."); + next; + mesn; + mesq l("I will reward whoever kills the current bandit leader, of course."); + select + l("I would gladly aid you to get rid of that scourge."), + l("Sorry, I forgot my courage on my other set of pants."); + mes ""; + if (@menu == 2) + close; + setq HurnscaldQuest_Bandits, 3; + mesn; + mesq l("Wonderful! So, how about a warm up?"); + next; + // 8% drop. 10 / 8% = avg. 125 bandits to kill + // And for once, I won't require these bandits to be from Hurnscald Bandit Cave. + mesn; + mesq l("Bring me 10 @@. I'll pay you some money for that, of course.", getitemlink(BanditHood)); + close; + +L_Hood: + mesn; + mesq lg("Adventurer, did you brought me what I asked? I see you have @@/10 @@.","Adventurer, did you brought me what I asked? I see you have @@/10 @@.", countitem(BanditHood), getitemlink(BanditHood)); + mes ""; + select + rif(countitem(BanditHood) >= 10, l("Yes, take it.")), + rif(countitem(BanditHood) < 10, l("No, I'll be back with them.")), + l("Hm, can we talk again later?"); + mes ""; + if (@menu != 1) + close; + inventoryplace LeatherPatch, 1; + delitem BanditHood, 10; + Zeny=Zeny+2000; + getexp 6842,0; // 20% from needed exp + getitem LeatherPatch, 1; + setq HurnscaldQuest_Bandits, 4; + mesn; + mesq l("Hey hey... Good job! I was worried you would ruin their hoods before being able to take them."); + next; + mesn; + mesq l("Here is 2000 GP for your efforts. Thanks for making Hurnscald a better place to live."); + next; + mesc l("You also gained a @@. Bows are very slow, so you should talk to the Blacksmith to make a Quiver.", getitemlink(LeatherPatch)); + close; + + +///////////////////////// Stage 3 +L_BanditLord: + mesn; + mesq l("So... I won't say you can't do it, @@. I will just say killing the Bandit Lord is no easy task.", strcharinfo(0)); + next; + mesn; + mesq l("The @@ is not only a fearsome and ruthless leader. He is strong, and he have tricks on his sleeve.", getmonsterlink(BanditLord)); + next; + mesn; + mesq l("He will summon allies if he think you have the upper hand. So take care if you are going ranged."); + next; + mesn; + mesq l("I know another Bandit Lord will take up his place, but the loss of their leader will make bandits scatter long enough."); + next; + mesn; + mesq l("Will you help me- no, I mean, will you help us, the whole town of Hurnscald?"); + next; + select + l("Sorry, I need to better prepare myself."), + l("I would gladly give my life for such noble goal."); + mes ""; + if (@menu == 1) + close; + setq HurnscaldQuest_Bandits, 5; + mesn; + mesq l("...You have courage. Many people tried and failed."); + next; + mesn; + mesq l("I know where the Bandit Lord room is, and I have a guard stationed not far from there. Ask him for the key."); + next; + mesn; + mesq l("One last thing... Good luck. This is a long shot, so don't hesit in running away."); + close; + +L_Finish: + mesn; + mesq l("Ah... You did it!"); + next; + mesn; + mesq l("That's easy to know, because the bandits are less coordinated. Perhaps we will be able to sleep in peace this night!"); + next; + inventoryplace ForestArmor, 1; + getitem ForestArmor, 1; + getexp 5842,93; // 10% from needed exp + JExp level 6 + setq HurnscaldQuest_Bandits, 8; + mesn; + mesq l("Here is the @@, like my armor, and one of the best for rangers.", getitemlink(ForestArmor)); + next; + mesn; + mesq l("Any friend of Hurnscald is my friend too. Come to me again, if you want to do daily quests!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FairyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, ForestArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonSkirt); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 17); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/012-2/khafar.txt b/npc/012-2/khafar.txt new file mode 100644 index 0000000..084ac29 --- /dev/null +++ b/npc/012-2/khafar.txt @@ -0,0 +1,137 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Kfahr - Placeholder +// Quests: +// HurnscaldQuest_Khafar +// .@q1 = when even, must buy a beer +// when odd, can ask another story +// .@q2 = Temporary variable, holds last story +// Secrets Unlocked: +// NivalisQuest_Baktar (status 1→2) +// Win arm wrestling to unlock Braknar Shield Secrets + +012-2,54,46,2 script Khafar NPC_ORC,{ + function MustDrink; + mesc l("You stand before a battle-scarred, darkly tanned warrior, brimming with muscles."); + mesc l("Just looking at him you smell danger, adventure, excitement..."); + next; + mesc l("On second thought, he really could use a bath."); + next; + mesc l("The warrior turns towards you, grinning broadly."); + next; + mesn; + mesq l("Why, hello there! Come to visit me to hear Kfahr the Warrior or Andrei Sakar's exploits, have you?"); + mesc l("He laughs heartily and gives you a slap on the back."); + next; + mesn; + mesq l("Can't blame you, can't blame you at all! Here, take a seat!"); + next; + goto L_Menu; + +L_Menu: + do + { + .@q=getq(HurnscaldQuest_Khafar); + if (.@q % 2 == 0) + MustDrink(); + select + l("Don't you have any exploit of your own?"), + l("Who is Kfahr the Warrior?"), + l("Who is Andrei Sakar?"), + l("Nah, see you later."); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("I once killed a Giant Maggot alone!"); + next; + break; + case 2: + mesn; + mesq l("He is my grand-grand-grandfather! The might Kfahr, with his friend Braknar, and heroes from Tulimshar legends."); + next; + mesn; + mesq l("Together, they wrote legends. They slayed Toby Rick the Desert Worm, the mightiest desert beast."); + next; + mesn; + mesq l("Braknar also used a pseudonym - Arvek. If you hear that name in some tale, it is the same person."); + next; + mesn; + mesq l("Well, that was a good fight. My grand-grand-grandfeather gave his shield to Arvek -- err, Braknar I mean."); + next; + mesn; + mesq l("The last time they were seen... They went to explore the Desert Temple. None returned alive."); + next; + select + l("Cool story, bro!"), + l("What about the Desert Temple?"), + rif(getq(NivalisQuest_Baktar), l("What about the Shield?")), + l("What about the worm?"); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("Lost forever on the Desert Canyon, I guess."); + next; + break; + case 3: + mesn; + mesq l("Oh, the @@ might be a heirloom or something from Braknar family. I dunno who could have it.", getitemlink(BraknarShield)); + if (getq(NivalisQuest_Baktar) == 1) + setq1 NivalisQuest_Baktar, 2; + next; + break; + case 4: + mesn; + mesq l("It is a boss. I don't know how to summon one, and to be honest - You won't want to see one either."); + next; + break; + } + break; + default: + mesn; + mesq lg("See you later, my friend! Thanks for the drink!"); + close; + break; + } + setq1 HurnscaldQuest_Khafar, .@q+1; + getexp 50, 0; // Beer is now lost - get 50xp + } while (true); + close; + +function MustDrink { + mesn; + mesq l("But first... My throat is dry. Can you bring me a beer?"); + if (!countitem(Beer)) + close; + next; + select + l("Here, you can have a glass."), + l("Sorry, I have nothing."); + mes ""; + if (@menu == 1) { + delitem Beer, 1; + .@q=getq(HurnscaldQuest_Khafar); + setq1 HurnscaldQuest_Khafar, .@q+1; + return; + } + close; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FairyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, ForestArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonShorts); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 24); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .sex = G_MALE; + .distance = 5; + npcsit; + end; +} + diff --git a/npc/012-2/melina.txt b/npc/012-2/melina.txt new file mode 100644 index 0000000..ac183a9 --- /dev/null +++ b/npc/012-2/melina.txt @@ -0,0 +1,73 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Sells beer +// Variables: +// $HELENA_ST +// Controls stock and beer prices + +012-2,54,43,0 script Melina NPC_FEMALE,{ + +hello; +npcshopattach(.name$); +shop .name$; +close; + +OnTimer1000: + domovestep; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, CottonGloves); + setunitdata(.@npcId, UDT_HEADMIDDLE, CottonShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, AssassinBoots); + setunitdata(.@npcId, UDT_WEAPON, MiniSkirt); + setunitdata(.@npcId, UDT_HAIRSTYLE, 20); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + tradertype(NST_MARKET); + if ($HELENA_ST < 700) + sellitem Beer, 500-($HELENA_ST/2), ($HELENA_ST/3)+1; + else + sellitem Beer, 150, ($HELENA_ST/3)+1; + + initpath "move", 54, 43, + "dir", DOWN, 0, + "wait", 27, 0, + "move", 43, 43, + "dir", DOWN, 0, + "wait", 27, 0, + "move", 48, 45, + "dir", DOWN, 0, + "wait", 27, 0, + "move", 58, 42, + "dir", DOWN, 0, + "wait", 27, 0, + "move", 35, 43, + "dir", DOWN, 0, + "wait", 20, 0; + initialmove; + initnpctimer; + + .sex = G_FEMALE; + .distance = 5; + end; + +OnClock2358: + if ($HELENA_ST < 700) + restoreshopitem Beer, 500-($HELENA_ST/2), ($HELENA_ST/3)+1; + else + restoreshopitem Beer, 150, ($HELENA_ST/3)+1; + end; +// Pay your taxes! +OnBuyItem: + PurchaseTaxes("Hurns"); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes("Hurns"); + end; + +} diff --git a/npc/012-2/note.txt b/npc/012-2/note.txt new file mode 100644 index 0000000..721fe6c --- /dev/null +++ b/npc/012-2/note.txt @@ -0,0 +1,29 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Lena +// Variables: +// $HELENA_ST +// Tracks how many set of Treasure Keys were given by all players thus far. +// Affects beer prices on Hurnscald. +// HurnscaldQuest_Bandits +// q1 -> Current status +// q2 -> Treasure Key Timer +// q3 -> Bandit Hood Timer + +012-2,40,41,0 script Note#012-2 NPC_NO_SPRITE,{ + HallOfSponsor(); + next; + if (.t > gettimetick(2)) + close; + .t=gettimetick(2)+2; + HallOfGMLog(); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + .t=gettimetick(2); + end; +} diff --git a/npc/012-2/rakinorf.txt b/npc/012-2/rakinorf.txt new file mode 100644 index 0000000..8e0554a --- /dev/null +++ b/npc/012-2/rakinorf.txt @@ -0,0 +1,335 @@ +// TMW2/LoF scripts. +// Authors: +// Jesusalva +// Description: +// Current Hurnscald Mayor +// Variable: +// SQuest_Sponsor +// Quest ID: 4 + +012-2,34,79,0 script Rakinorf, Mayor NPC_ANSELMO_BR,{ + function resetSQS { + setq SQuest_Sponsor, 0, gettime(GETTIME_MONTH); + } + // Check monthly quest + .@d=getq2(SQuest_Sponsor); + if (.@d != gettime(GETTIME_MONTH)) resetSQS(); + + .@n=getq(General_Narrator); + .@q=getq(SQuest_Sponsor); + mesn; + mesq l("Ah... I need more beer to keep going... This is so awful..."); + // Handle main storyline + if (.@n >= 5 && .@n <= 6) + goto L_MainStory; + // Handle main storyline + + // Core + .@q=getq(SQuest_Sponsor); + if (!(.@q & .questID) && getgmlevel()) goto L_Menu; + close; + +L_Menu: + if (!(.@q & .questID)) + close; + + mesq l("I will reward you for 7 @@.", getitemlink(.itemID)); + mes ""; + menu + rif(countitem(.itemID) >= 7, l("Here they are!")), L_Finish, + l("Where can I find them?"),L_Where, + l("No, thanks."),L_Close; + +L_Finish: + .@q=getq(SQuest_Sponsor); + if (!(.@q & .questID)) + close; + + delitem .itemID, 7; + getitem StrangeCoin, rand2(2,4); + setq1 SQuest_Sponsor, .@q | .questID; + mes ""; + mesn; + mesq l("Many thanks! Come back later to bring me extra @@!", getitemlink(.itemID)); + close; + +L_Where: + mes ""; + mesq l("Ah, there are lots with Melina, downstairs."); + next; + +L_Close: + closedialog; + goodbye; + close; + +// Main Storyline +L_MainStory: + next; + // 5 - First time speaking + // setq2 + // 0 - Not Assigned + // 1 - Need to bring Beer + // 2 - Need to find out how Arkim is doing + // 3 - Complete, Reluctancy node + // 4 - Told about Hurnscald Liberation Day, but didn't told where previous mayor is + // 5 - Told you to tell Airlia about. + // 6 - Airlia told you what she knew, must talk to Rakinorf + // 7 - Need to get levels (standard quest) + // 6 - Must visit Halinarzo Library + // Note: Expires on logout: @returnpotion_expiredate + + if (.@n == 5) + .@n2=getq2(General_Narrator); + else + .@n2=99; + + if (.@n2 == 5) { + mesn; + mesq l("You should talk to Airlia again to understand what Lua wants me to do."); + mes ""; + } + + .@rk=(!(getq(SQuest_Sponsor) & .questID)); + + select + rif(.@rk && getgmlevel(), l("Maybe I could give you more beer?")), + rif(.@n2 == 0, l("I know it may sound silly, but I need your help to find out who I am.")), + rif(.@n2 == 1, l("I have brought you the beer.")), + rif(.@n2 == 2, l("I know how many bat teeth and wings Arkim collected.")), + rif(.@n2 == 3, l("I know it may sound silly, but I need your help to find out who I am.")), + rif(.@n2 == 4, l("Where can I find the previous mayor?")), + rif(.@n2 == 6, l("Airlia told me you should lend me a Return Potion.")), + rif(.@n2 == 7, l("I think I'm ready.")), + rif(.@n == 6 && countitem(BrokenWarpCrystal), l("The warp crystal broke.")), + //rif(.@n == 6 && (!countitem(ReturnPotion)||@returnpotion_expiredate<gettimetick(2)), l("I need more return potions.")), + l("Ok, thanks."); + + mes ""; + switch (@menu) { + case 1: + goto L_Menu; break; + case 2: + mesn; + mesq l("Not now... *hic* I need @@... *hic* Bring me @@ if you *hic* can...", getitemlink(Beer), l("four")); + setq2 General_Narrator, 1; + break; + case 3: + if (countitem(Beer) < 4) { + mesn; + mesq l("That's not *hic*... That's not what I asked you for..."); + next; + mesn; + mesq l("Please bring me *hic*... Bring me @@ @@!", 4, getitemlink(Beer)); + close; + } + delitem Beer, 4; + getexp BaseLevel*10, JobLevel*3; + setq2 General_Narrator, 2; + mesn; + mesq l("Aaaaaaaahhhhh.... Much better now *hic*."); + next; + mesn; + mesq l("Could you *hic* do me a favor? Arkim, on a cave southeast of *hic* here, is collecting some stuff."); + next; + mesn; + mesq l("He's an *hic* hermit, and collects *hic* Bat teeth and wings... Tell me how many he collected!"); + break; + case 4: + mesn; + mesq l("So? How many *hic* he collect?"); + mesc l("Protip: Arkim is in a cave southeast of here and is an hermit."); + input .@am; + mes ""; + // If players give up to 10 wings/teeths since you check, I don't mind + if (.@am < ($ARKIM_ST-10) || .@am > $ARKIM_ST) + goto L_Fail; + mesn; + mesq l("Thanks. You can go, now."); + getexp BaseLevel*2, JobLevel; + setq2 General_Narrator, 3; + break; + case 5: + mesn; + mesq lg("Aaaaaaah, gal... Just let me drink in peace.", "Aaaaaaah, boy... Just let me drink in peace."); + next; + mesn strcharinfo(0); + mes l("T.T \"Why I think you are withdrawing information all along?\""); + next; + mesn; + mesq l("Sure, sure... Why are you even *hic* asking me this, anyway? Do I look like your... *hic* your father or something?!"); + next; + select + l("Maybe you do, your jerk! Stop drinking! Lua told me that you could help me! DO YOUR JOB!!"), + l("Sorry, Mister Rakinorf, but it was Lua that said you could help me."); + next; + /* + if (@menu == 1) + Karma=Karma+1; + else + Karma=Karma-1; + */ + setq2 General_Narrator, 4; + mesn strcharinfo(0); + mesq l("All she told me was that I needed to visit Halinarzo, but was too weak, and should look for you instead."); + next; + // Rakinorf gets somber after that + mesn; + mesq l("Ah, Halinarzo... Dangerous place. Tulimshar route is plagued with snakes, Hurnscald route is more often than not flooded."); + next; + mesn; + mes l(">.> \"Sorry pal, I have no idea what she meant by that.\""); + next; + mesn; + mesq l("You see, just @@ ago, Hurnscald was liberated from a massive monster attack.", FuzzyTime($HURNS_LIBDATE)); + mesq l("Dragonstar and Aisen did their best along many others, and managed to liberate Hurnscald, though!"); + next; + mesn; + mesq l("Well, problem is, after that, there was demand for an election. The previous mayor lost. I won."); + next; + mesn; + mesq l("That's basically how democracy works. You are warranted to stay on the office until something goes gravely wrong."); + mesq l("Now I just drink down my worries and hope for the best."); + next; + mesn; + mesq l("The previous mayor was an amazing mayor, but even so, he lost the office after the Monster King attacked."); + next; + mesn; + mesq l("I'm afraid I can't be of any help to you."); + next; + mesn; + mesq l("And now... Somber time is over! Time to get back to drinking! Yaaaay!!"); + break; + case 6: + setq2 General_Narrator, 5; + mesn; + mesq l("His daughter, Airlia, is not air-headed as her mother Lia. Try asking her instead."); + break; + case 7: + mesn; + mesq l("Whaaaaaaat *hic* is she *hic* have she gotten crazy? *hic*"); + next; + mesn; + mesq l("That's a *hic* precious item, and you're *hic* not nearly *hic* strong or worth enough to use it!"); + next; + mesn; + mesq l("You will need to *hic* prove yourself to *hic* me and the town first..."); + next; + mesc b(l(".:: Main Quest 3-2 ::.")), 3; + msObjective(BaseLevel >= 35, l("* @@/@@ Base Level", BaseLevel, 35)); + + if (JobLevel >= 15) + mesc l("* @@/@@ Job Level", JobLevel, 15), 2; + else + mesc l("* @@/@@ Job Level", JobLevel, 15), 9; + + if (countitem(Scythe)) + mesc l("* @@/@@ @@", countitem(Scythe), 1, getitemlink(Scythe)), 2; + else + mesc l("* @@/@@ @@", countitem(Scythe), 1, getitemlink(Scythe)), 9; + + if (getq(HurnscaldQuest_BloodDonor)) + mesc l("* Donate blood at least once."), 2; + else + mesc l("* Donate blood at least once."), 9; + + setq2 General_Narrator, 7; + next; + //mesn; + if (!countitem(Scythe)) + mes l("- Help the farmers. We rely a lot on agriculture."); + if (!getq(HurnscaldQuest_BloodDonor)) + mes l("- To donate blood, go to the hospital and ask about it."); + if (JobLevel < 15) + mes l("- Boss give more Job experience, but as long that you keep killing, you'll keep gaining."); + if (BaseLevel < 35) + mes l("I think Lieutenant Paul had a bounty for extra experience."); + break; + + case 8: + mesc b(l(".:: Main Quest 3-2 ::.")), 3; + msObjective(BaseLevel >= 35, l("* @@/@@ Base Level", BaseLevel, 35)); + + msObjective(JobLevel >= 15, l("* @@/@@ Job Level", JobLevel, 15)); + + msObjective(countitem(Scythe), l("* @@/@@ @@", countitem(Scythe), 1, getitemlink(Scythe))); + + msObjective(getq(HurnscaldQuest_BloodDonor), l("* Donate blood at least once.")); + + next; + if (BaseLevel >= 35 && + JobLevel >= 15 && + countitem(Scythe) && + getq(HurnscaldQuest_BloodDonor)) + goto L_Complete; + mesn; + if (!countitem(Scythe)) + mes l("- Help the farmers. We rely a lot on agriculture."); + if (!getq(HurnscaldQuest_BloodDonor)) + mes l("- To donate blood, go to the hospital and ask about it."); + if (JobLevel < 15) + mes l("- Boss give more Job experience, but as long that you keep killing, you'll keep gaining."); + break; + + case 9: + mesn; + mesq l("I don't have a spare. They're *hic* done by ANISE INC., their headquarters are in Frostia."); + next; + mesn; + mesq l("Frostia is a *hic* town way way north of here. They *hic* hate humans, so... Good luck?"); + break; + /* + case 10: + if (countitem(ReturnPotion)) + delitem ReturnPotion, countitem(ReturnPotion); + inventoryplace ReturnPotion, 5; + getitem ReturnPotion, rand(2,5); + @returnpotion_expiredate=gettimetick(2)+1200+rand(300,1200); // 20 minutes + 5~20 minutes == last 25~40 minutes + mesn; + mesq l("There... *hic* They will expire *hic* in just @@... Or on logout.", FuzzyTime(@returnpotion_expiredate)); + break; + */ + } + close; + +L_Fail: + mesn; + mes l("Eh? You sure?"); + if (.@am > $ARKIM_ST) + mes l("I don't think he could have collected that many!"); + else + mes l("Last time they told me he collected more than that..."); + next; + mesn; + mesq l("Could you try again, please?"); + // No penalty? Really?? + close; + +L_Complete: + inventoryplace ReturnPotion, 5, HalinWarpCrystal, 1, HurnsWarpCrystal, 1; + mesn; + mesq l("Good, you *hic* proved your *hic* worth. I'll give you them."); + next; + mesn; + mesq l("Here are EXTREMELY VALUABLE warp crystals, and the *hic* fabled return potions."); + next; + mesn; + mesq l("Halinarzo is a *hic* level 50 area. So please *hic* be careful!"); + mesc l("WARNING: Warp Crystals can break after use and have a cooldown."); + mesc l("Return Potions works instantly. Talk to Wyara to get more."); + setq General_Narrator, 6, 0; + getitem ReturnPotion, 10; + getitem TulimWarpCrystal, 1; + getitem HurnsWarpCrystal, 1; + getexp BaseLevel*400, JobLevel*50;// Reference Levels: (35, 15) + getvaultexp(10); + close; + +OnInit: + .questID=4; + .itemID=Beer; + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/012-3/_import.txt b/npc/012-3/_import.txt new file mode 100644 index 0000000..1f94b49 --- /dev/null +++ b/npc/012-3/_import.txt @@ -0,0 +1,4 @@ +// Map 012-3: Archery Shop +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/012-3/_warps.txt", +"npc/012-3/alan.txt", diff --git a/npc/012-3/_warps.txt b/npc/012-3/_warps.txt new file mode 100644 index 0000000..0b81722 --- /dev/null +++ b/npc/012-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-3: Archery Shop warps +012-3,41,46,0 warp #012-3_41_46 0,0,012-1,89,59 diff --git a/npc/012-3/alan.txt b/npc/012-3/alan.txt new file mode 100644 index 0000000..92cfc45 --- /dev/null +++ b/npc/012-3/alan.txt @@ -0,0 +1,142 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Alan is Hurnscald's bowmaster. He may craft the Forest Bow, and sell ammo. + +012-3,44,40,0 script Alan NPC_BOWMASTER,{ + .@q=getq(HurnscaldQuest_ForestBow); + .@q2=getq2(HurnscaldQuest_ForestBow); + .@q3=getq3(HurnscaldQuest_ForestBow); + switch (.@q) { + case 1: + npctalk3 l("Ah, if Jack and that fisherman from Halinarzo still gave me materials..."); + break; + case 2: + npctalk3 l("The bow I gave you was a masterpiece."); + break; + } + + mesn; + mesq l("Only the finest bows and arrows, in the land where wood is abundant!"); + mes ""; + menu + l("Ok, thanks."), L_Close, + l("I want to trade."), L_Shop, + rif(.@q == 0, l("Do you accept special requests? Could you make me a really good bow?")), L_ForestBow, + rif(.@q == 1 && .@q2 == .@q3 && .@q2 == 99, l("I have the sturdy wood and the string.")), L_Craft; + +L_Shop: + npcshopattach(.name$); + openshop; + closedialog; + close; + +L_ForestBow: + mesn; + mesq l("Well, I know how to do a @@, a sturdy bow, but I am not making those anymore, sorry.", getitemlink(ForestBow)); + next; + menu + l("Oh, too bad."), L_Close, + l("What? Why not?"), L_Next; + +L_Next: + mesn; + mesq l("You see, I cannot use any raw material. It must be sturdier than the usual, and I don't have the materials for it."); + next; + mesn; + mesq l("The wood, Jack the Lumberjack used to deliver me, but he isn't delivering anymore. And the string was imported from Halinarzo."); + next; + mesn; + mesq l("Perhaps, you could convince Jack to give me the wood, and seek for a fisherman on Halinarzo to give you the string? I won't charge anything."); + next; + menu + l("Not really, sorry."), L_Close, + l("Right'o, I'll arrange the material!"), L_Start; + +L_Start: + // q2 → Wood Part + // q3 → String Part + setq HurnscaldQuest_ForestBow, 1, 0, 0; + goto L_Close; + +L_Craft: + inventoryplace ForestBow, 1; + getitem ForestBow, 1; + getexp 900, 0; + setq HurnscaldQuest_ForestBow, 2, 0, 0; + mesn; + mesq l("Here you go - have fun with it."); + next; + goto L_Close; + + +L_Close: + closedialog; + goodbye; + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem ShortBow, 8800, 1; + sellitem WoodenBow, 4000, 2; + + sellitem TrainingArrow, -1, 2000; + sellitem Arrow, -1, 1000; + sellitem IronArrow, -1, 500; + + if ($ARKIM_ST > 1400) + sellitem CursedArrow, -1, $ARKIM_ST-1400; + if ($ARKIM_ST > 2800) + sellitem PoisonArrow, -1, $ARKIM_ST-2800; + + sellitem TrainingAmmoBox, -1, 10; + sellitem ArrowAmmoBox, -1, 5; + sellitem IronAmmoBox, -1, 2; + + if ($ARKIM_ST > 2800) + sellitem CursedAmmoBox, -1, (($ARKIM_ST-1400)/200); + if ($ARKIM_ST > 5600) + sellitem PoisonAmmoBox, -1, (($ARKIM_ST-2800)/200); + + end; + + +OnClock2357: +OnClock1151: + restoreshopitem ShortBow, 8800, 1; + restoreshopitem WoodenBow, 4000, 2; + restoreshopitem TrainingAmmoBox, -1, 10; + restoreshopitem ArrowAmmoBox, -1, 5; + restoreshopitem IronAmmoBox, -1, 2; + + if ($ARKIM_ST > 2800) + restoreshopitem CursedAmmoBox, -1, (($ARKIM_ST-1400)/200); + if ($ARKIM_ST > 5600) + restoreshopitem PoisonAmmoBox, -1, (($ARKIM_ST-2800)/200); +OnClock0611: +OnClock1800: + restoreshopitem TrainingArrow, -1, 2000; + restoreshopitem Arrow, -1, 1000; + restoreshopitem IronArrow, -1, 500; + if ($ARKIM_ST > 2800) + restoreshopitem CursedArrow, -1, (($ARKIM_ST-1400)/200); + if ($ARKIM_ST > 5600) + restoreshopitem PoisonArrow, -1, (($ARKIM_ST-2800)/200); + end; + +// Pay your taxes! +OnBuyItem: + PurchaseTaxes("Hurns"); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes("Hurns"); + end; + +} diff --git a/npc/012-4/_import.txt b/npc/012-4/_import.txt new file mode 100644 index 0000000..cb639cb --- /dev/null +++ b/npc/012-4/_import.txt @@ -0,0 +1,5 @@ +// Map 012-4: Alchemy Shop +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/012-4/_warps.txt", +"npc/012-4/craft.txt", +"npc/012-4/wyara.txt", diff --git a/npc/012-4/_warps.txt b/npc/012-4/_warps.txt new file mode 100644 index 0000000..fb88327 --- /dev/null +++ b/npc/012-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-4: Alchemy Shop warps +012-4,35,31,0 warp #012-4_35_31 0,0,012-1,101,56 diff --git a/npc/012-4/craft.txt b/npc/012-4/craft.txt new file mode 100644 index 0000000..be788e9 --- /dev/null +++ b/npc/012-4/craft.txt @@ -0,0 +1,54 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Rentable Alchemy Bench + +012-4,27,31,0 script Alchemy Cauldron NPC_NO_SPRITE,{ + mesc l("What will you brew today?"); + mesc l("It costs %d GP to use.", .price), 1; + mesc l("This tax must be paid now, and you can brew as much as you wish after payment."), 1; + if (Zeny < .price) + close; + next; + select + l("Pay"), + l("Don't pay."); + mes ""; + if (@menu == 2) { closeclientdialog; close; } + clear; + if (Zeny < .price) { + mesc l("WARNING, you have been detected cheating and thus, violating Tulimshar Anti-Theft Policy."), 1; + mesc l("You were jailed and now need a GM to get you out of there."), 1; + logmes "WARNING, "+strcharinfo(0)+" found out cheating, only had "+Zeny+"/300 GP for alchemy table. Jailed.", LOGMES_ATCOMMAND; + consoleinfo("%s jailed - no money when brewing.", strcharinfo(0)); + atcommand("@jail "+strcharinfo(0)); + // Apply a more realistic penalty + Zeny=0; + CRAFTSYS_CURRENT=0; + close; + } + Zeny-=.price; + + do { + mesc l("What will you brew today?"); + mes ""; + + if (AlchemySystem(CRAFT_PLAYER)) { + mesc l("Success!"), 3; + } else { + mesc l("That didn't work!"), 1; + } + next; + mesc l("Try again?"); + } while (askyesno() == ASK_YES); + + close; + +OnInit: + .distance=3; + .price=1500; + end; +} + + diff --git a/npc/012-4/wyara.txt b/npc/012-4/wyara.txt new file mode 100644 index 0000000..cac765c --- /dev/null +++ b/npc/012-4/wyara.txt @@ -0,0 +1,310 @@ +// TMW2 Script +// Author: +// Saulc +// Vasily_Makarov (original from Evol) +// Jesusalva +// Description: +// Hurnscald Potion Shopkeeper +// Notes: +// Reset must be turned in function + +012-4,29,28,0 script Wyara NPC_FEMALE,{ + + speech S_LAST_NEXT, + l("I am @@, an alchemist specialized in reset potions.", .name$); + +L_Menu: + .@n=getq(General_Narrator); + .@s=getq(HurnscaldQuest_Sagratha); + .@s3=getq3(HurnscaldQuest_Sagratha); + + select + l("Can you reset my stats please?"), + rif($ARKIM_ST >= 1200,l("I want Piberries Infusion!")), + rif(getq(HurnscaldQuest_InjuriedMouboo) == 2,l("Do you know how to break curses?")), + rif(.@s == 1 && .@s3,l("Sagratha was not home.")), + rif(.@s == 2,l("About Sagratha...")), + rif(SAGRATHA_SCORE < 0 && .@s >= 6, l("Sagratha hates my guts.")), + rif(.@n >= 6,l("I am in dire need of Return Potions!")), + lg("You are weird, I have to go sorry."); + + mes ""; + switch (@menu) { + case 1: + goto L_ResetStats; + case 2: + goto L_Piberries; + case 3: + goto L_Uncurse; + case 4: + goto L_SaggyHome; + case 5: + goto L_SaggyMain; + case 6: + goto L_SaggyHelp; + case 7: + goto L_ReturnPot; + case 8: + goto L_Quit; + } + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + ConfirmStatusReset(); + goto L_Quit; + +L_Piberries: + mesn; + mesq l("For (another) one @@, I'll need 3~4 @@ and 50 GP.", getitemlink(PiberriesInfusion), getitemlink(Piberries)); + next; + select + rif(Zeny >= 50 && countitem(Piberries) >= 4, l("Do it!")), + l("Not now, sorry."); + if (@menu == 2) + goto L_Quit; + + mes ""; + inventoryplace PiberriesInfusion, 1; + + Zeny=Zeny-50; + delitem Piberries, rand2(3,4); + getitem PiberriesInfusion, 1; + getexp 10, 0; + goto L_Piberries; + +L_Uncurse: + mesn; + mesq l("Well, it depends on the curse. Some are easy to break, and others are... well..."); + next; + select + l("It's a simple curse."), + l("It's a complex curse."), + l("It's a cursed mouboo."); + mes ""; + mesn; + switch (@menu) { + case 1: + mesq l("Then you should look in buying Caffeinne. Curse is a status ailment which reduces your attack, nullifies your luck and makes you a snail. Simple Curses can be cured with time, too."); + break; + case 2: + mesq l("Oh, then you should seek the help of an specialist. These curses have an specific condition to break, like leveling up or being killed. Force-breaking them can be difficult."); + break; + case 3: + mesq l("A... Mouboo? Well, I know who can handle curses on cute Mouboos."); + next; + mesn; + mesq l("Go talk to Sagratha, she is usually in a hut in northen forest. The door have a magic barrier, so you'll need to have minimal magic skills to get close enough to open it."); + next; + mesn; + mesq l("She doesn't likes @@s, only cute animals. She doesn't likes Ghosts, Undeads, and Shadow monsters either.", get_race()); + next; + mesn; + mesq l("So, when you get on the door, knock it, and say this: \"@@\". She will open the door for you.", b(l("Mouboos are cute"))); + compareandsetq HurnscaldQuest_Sagratha, 0, 1; + break; + } + close; + +L_ReturnPot: + .@price=7000-(reputation("Hurns")*20); + .@craft=2001-(reputation("Hurns")*20); + .@ammon=5+(reputation("Hurns")/30); + mesn; + mesq l("I understand. Rakinorf told me to stuff you with them if needed."); + next; + mesn; + mesq l("Be aware I can only bake batches of @@ potions.", .@ammon); + next; + mesn; + mesq l("So, it is @@ GP each one up-front. Or I can brew with your materials:", .@price); + mes ""; + mesn l("Craft Recipe"); + mesc l("- @@/@@ @@", countitem(MauveHerb), 80, getitemlink(MauveHerb)); + mesc l("- @@/@@ @@", countitem(MushroomSpores), 15, getitemlink(MushroomSpores)); + mesc l("- @@/@@ @@", countitem(Potatoz), 7, getitemlink(Potatoz)); + mesc l("- @@/@@ @@", countitem(Coral), 5, getitemlink(Coral)); + mesc l("- @@/@@ @@", countitem(EverburnPowder), 1, getitemlink(EverburnPowder)); + mesc l("- @@/@@ GP", format_number(.@craft), format_number(Zeny)); + next; + select + l("Too expensive %%n"), + rif(Zeny >= .@price, l("I want to pay the full price.")), + l("I want you to brew some for me."); + mes ""; + switch (@menu) { + case 1: + close; + case 2: + mesc l("How many? Max. @@", (Zeny/.@price)); + input .@c, 0, (Zeny/.@price); + .@payment=.@price*.@c; + if (Zeny >= .@payment) { + inventoryplace ReturnPotion, .@c*.@ammon; + Zeny-=.@payment; + getitem ReturnPotion, .@c*.@ammon; + mesn; + mesq l("There you go!"); + } else { + Exception("Illegal input.", RB_DEFAULT | RB_SPEECH); + } + break; + case 3: + mesc l("How many?"); + input .@c, 0, 100; + inventoryplace ReturnPotion, .@c*.@ammon; + if ( + countitem(MauveHerb) < 80*.@c || + countitem(MushroomSpores) < 15*.@c || + countitem(Potatoz) < 7*.@c || + countitem(Coral) < 5*.@c || + countitem(EverburnPowder) < 1*.@c || + Zeny < .@craft*.@c + ) Exception("You don't have that much material.", RB_ISFATAL|RB_SPEECH); + delitem MauveHerb, 80*.@c; + delitem MushroomSpores, 15*.@c; + delitem Potatoz, 7*.@c; + delitem Coral, 5*.@c; + delitem EverburnPowder, 1*.@c; + Zeny-=.@craft*.@c; + getitem ReturnPotion, .@c*.@ammon; + mesn; + mesq l("There you go!"); + break; + } + close; + +L_SaggyHome: + mesn; + mesq l("She probably just went out for a walk. Nothing to worry, I hope."); + next; + if (.@n < 11) { + mesn; + mesq l("It's impossible to know when she'll be back, so you should prioritize something else for now."); + close; + } + select + l("Yeah, she might be back soon."), + l("I don't think so. There have been... Incidents."); + if (@menu == 1) + close; + mes ""; + mesn; + mesq l("...Incidents?"); + next; + mesn strcharinfo(0); + mesc l("You tell her about the incident at the Blue Sage's residence and how they were aiming at sages."); + next; + mesn; + mesq l("Well, this is very disturbing, indeed. I hope my good friend Sagratha is fine."); + next; + mesn; + mesq l("Can you, perhaps, do me a favor? Please... Check her house for any signs of battle."); + next; + mesn; + mesq l("She should be safe as she is a strong woman, but... You never know."); + setq1 HurnscaldQuest_Sagratha, 2; + setq3 HurnscaldQuest_Sagratha, 0; + close; + +L_SaggyMain: + mesn; + mesq l("Yes? Have you looked her house for signs of battle?"); + next; + select + l("Yes, and there was no signs of a fight."), + l("...Not yet."); + if (@menu == 2) + close; + mes ""; + mesn; + mesq l("Are you sure? Like, really really sure?"); + next; + if (!.@s3) { + mesn strcharinfo(0); + mesq l("Hmm... Thinking well..."); + next; + mesn; + mesq l("THEN DON'T WASTE MY TIME! My friend could be in danger!"); + next; + mesn; + mesq l("Go do what I told you to do and examine the house thoroughly!"); + close; + } + mesn strcharinfo(0); + mesq l("Yes, I did. There was only an unlocked secret window."); + next; + mesn; + mesq l("Good, this must mean that Sagratha managed to flee in time."); + next; + mesn; + mesq l("Eh, I don't think you'll be brave enough to go after her. If even she decided to flee, I doubt you wouldn't do the same."); + next; + select + l("Yeah you're right, I'm a noob anyway, and Sagratha is a skilled mage. She should be fine on her own."), + l("Two still fight better than one. I have the courage of a dustman in me!"); + mes ""; + if (@menu == 1) + close; + mesn; + mesq l("Heh. Foolish. That's what adventurers are, I guess..."); + next; + mesn; + mesq l("If you leave by the secret window, you'll notice a small cave entrance. Enter it."); + next; + mesn; + mesq l("It seems to be the ruins of some sort of Mouboo Temple or whatever, from a millenia ago. There may be traps, so be careful."); + next; + inventoryplace MercCard_Jesusalva, 1; + mesn; + mesq l("I'll give you a @@. It should aid you out there.", getitemlink(MercCard_Jesusalva)); + next; + mesn; + mesq l("...Good luck, @@. And be careful. If Sagratha decided to flee... It might be too strong for you.", strcharinfo(0)); + setq1 HurnscaldQuest_Sagratha, 3; + setq3 HurnscaldQuest_Sagratha, 0; + getitem MercCard_Jesusalva, 1; + close; + +L_SaggyHelp: + mesn; + mesq l("Well, you probably deserve it."); + next; + mesn; + mesq l("Have you been harming the forest? Specially Mouboos. Are you killing them?"); + next; + mesn; + mesq l("If yes, of course she will hate you. With reason! You're murdering her family and friends!"); + next; + mesn; + mesq l("Listen, she decided to live away from civilization. She choose the forest as her home and the animals as her family."); + next; + mesn; + mesq l("You have no right to take that away from her!"); + next; + mesn; + mesq l("You could try to get on her good side by killing what destroys the forest, or by planting trees."); + next; + mesn; + mesq l("But if you keep harming the forest, this will be for naught. Was I clear?"); + close; + +L_Quit: + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 8); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + + .sex = G_FEMALE; + .distance = 4; + end; +} diff --git a/npc/012-5/_import.txt b/npc/012-5/_import.txt new file mode 100644 index 0000000..a6b89d2 --- /dev/null +++ b/npc/012-5/_import.txt @@ -0,0 +1,4 @@ +// Map 012-5: Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/012-5/_warps.txt", +"npc/012-5/nicholas.txt", diff --git a/npc/012-5/_warps.txt b/npc/012-5/_warps.txt new file mode 100644 index 0000000..2816bf3 --- /dev/null +++ b/npc/012-5/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-5: Indoors warps +012-5,34,37,0 warp #012-5_34_37 0,0,012-1,121,72 +012-5,23,27,0 warp #012-5_23_27 0,0,012-1,116,67 diff --git a/npc/012-5/nicholas.txt b/npc/012-5/nicholas.txt new file mode 100644 index 0000000..706a84a --- /dev/null +++ b/npc/012-5/nicholas.txt @@ -0,0 +1,283 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Nicholas is Hurnscald's blacksmith. He forges some stuff, and sell other stuff. +// Perhaps he should not forge armor? Remember he cannot forge EVERYTHING... +// +// PS. +// Iridium + Platyna Platinum +// Gold + Coal Gold +// Silver + Coal Silver +// Copper + Tin Bronze (9:1) +// Terranite + Coal Terranite +// Iron + Coal Iron + +// Resitance and Weight increasing ores +// Lead → +++ res, ++++ wei +// Titanium → + res, + wei + +// Originals: Setzer, Steel Shield, Chain Mail, Light Platemail, Warlord Plate, Warlord Boots + +012-5,36,26,0 script Nicholas NPC_NICHOLAS,{ + goto L_Menu; + + // blacksmith_header() + function blacksmith_header { + mesn; + mesq l("Very well! We have seven class of items: Wood, Iron, Terranite, Bronze, Silver, Gold and Platinum."); + mesq l("Each of them require different items, I'll sort from weakest to strongest, so choose wisely."); + return; + } + + // blacksmith_create( BaseItem1, Amount, BaseItem2, Amount, PrizeItem, Price ) + function blacksmith_create { + .@base1=getarg(0); + .@amon1=getarg(1); + .@base2=getarg(2); + .@amon2=getarg(3); + .@prize=getarg(4); + .@price=getarg(5); + + // Adjust price + .@price=POL_AdjustPrice(.@price); + + mesn; + mesq l("Do you want to craft @@? For that I will need:", getitemlink(.@prize)); + mesc l("@@/@@ @@", countitem(.@base1), .@amon1, getitemlink(.@base1)); + mesc l("@@/@@ @@", countitem(.@base2), .@amon2, getitemlink(.@base2)); + mesc l("@@/@@ GP", format_number(Zeny), format_number(.@price)); + + select + l("Yes"), + l("No"); + + if (@menu == 2) + return; + + if (countitem(.@base1) >= .@amon1 && + countitem(.@base2) >= .@amon2 && + Zeny >= .@price) { + inventoryplace .@prize, 1; + delitem .@base1, .@amon1; + delitem .@base2, .@amon2; + POL_PlayerMoney(.@price); + + // craft the item with +30% crit dmg (weapons) or +1 random stat (etc) + if (array_find(.WeaponCraft, .@prize) >= 0) + CsysNpcCraft(.@prize, IOPT_CRITDMG, rand(28,32)); + else + CsysNpcCraft(.@prize, any(0, VAR_STRAMOUNT, VAR_AGIAMOUNT, VAR_VITAMOUNT, VAR_INTAMOUNT, VAR_DEXAMOUNT, VAR_LUKAMOUNT), any(1,1,1,2)); + + // Yield some experience for the craft + .@xp=getiteminfo(.@base1, ITEMINFO_SELLPRICE)*.@amon1+getiteminfo(.@base2, ITEMINFO_SELLPRICE)*.@amon2; + .@xp=.@xp*2/3; + getexp .@xp, rand(1,10); + + mes ""; + mesn; + mesq l("Many thanks! Come back soon."); + } else { + speech S_FIRST_BLANK_LINE,// | S_LAST_NEXT, + l("You don't have enough material, sorry."); + } + return; + } + +L_Menu: + mesn; + mesq l("Hello there, I am @@, blacksmith of this fine city. What do you want to forge today?", .name$); + mes ""; + select + l("I just want to trade."), + l("I want to forge One Handed Weapons!"), + l("I want to forge Shields!"), + l("I want to forge Quivers!"), + rif(getskilllv(TMW2_CRAFT), l("I would like to REMOVE an item options")), + rif(getskilllv(TMW2_CRAFT), l("I would like to change an item options")), + l("Nothing, thanks!"); + + mes ""; + switch (@menu) { + case 1: + npcshopattach(.name$); + openshop; + closedialog; + close; + break; + case 2: + goto L_Weapon; + case 3: + goto L_Shield; + case 4: + goto L_Quiver; + case 5: + mesn; + mesc b(l("You are REMOVING an item option.")), 1; + mesq col(b(l("Are you sure?"))+" "+l("I don't know for what crazy reason you might want to do this, there is absolutely no benefit to this. I think you just selected the wrong option."), 1); + next; + if (askyesno() == ASK_NO) { + mes ""; + mesc l("Wise choice."), 3; + mes ""; + goto L_Menu; + } + mes ""; + SmithTweakReset(); + goto L_Menu; + case 6: + mesn; + mesq l("You'll be charged even if you fail, be warned."); + next; + if (!SmithTweakSystem()) { + mes ""; + mesn; + mesq l("You can always try again another day!"); + } + next; + goto L_Menu; + } + close; + + + + + + + + + + +L_Weapon: + blacksmith_header(); + select + l("Nothing, sorry!"), + l("Wooden Sword"), + l("Iron Bug Slayer"), + l("Iron Short Gladius"), + rif(countitem(BronzeGladius), l("Bronze Gladius")), + l("Iron Backsword"); + + switch (@menu) { + case 1: + goto L_Menu; + case 2: + blacksmith_create(WoodenLog, 20, RawLog, 5, WoodenSword, 450); + break; + case 3: + blacksmith_create(IronIngot, 8, Coal, 12, BugSlayer, 1000); + break; + case 4: + blacksmith_create(IronIngot, 12, Coal, 16, ShortGladius, 1550); + break; + case 5: + blacksmith_create(CopperIngot, 18, TinIngot, 2, RealBronzeGladius, 500); + break; + case 6: + blacksmith_create(IronIngot, 18, TinIngot, 4, Backsword, 4550); + break; + } + goto L_Weapon; + + + + + + +L_Shield: + blacksmith_header(); + select + l("Nothing, sorry!"), + l("Wooden Shield"), + l("Iron Blade Shield"), + rif(getq(NivalisQuest_Baktar) >= 3, l("Bronze Braknar Shield")); + + switch (@menu) { + case 1: + goto L_Menu; + case 2: + blacksmith_create(WoodenLog, 40, LeatherPatch, 2, WoodenShield, 500); + break; + case 3: + blacksmith_create(IronIngot, 14, TinIngot, 4, BladeShield, 1500); + break; + case 4: + blacksmith_create(CopperIngot, 18, TinIngot, 2, BraknarShield, 8000); + break; + } + goto L_Shield; + + + + + + +L_Quiver: + blacksmith_header(); + select + l("Nothing, sorry!"), + l("Leather Quiver"), + l("Iron Quiver"), + l("Bronze Quiver"), + l("Platinum Quiver"); + + switch (@menu) { + case 1: + goto L_Menu; + case 2: + blacksmith_create(LeatherPatch, 35, CottonCloth, 5, LeatherQuiver, 2000); + break; + case 3: + blacksmith_create(IronIngot, 16, Coal, 21, IronQuiver, 3000); + break; + case 4: + blacksmith_create(CopperIngot, 27, TinIngot, 3, BronzeQuiver, 4000); + break; + case 5: + blacksmith_create(PlatinumIngot, 12, IridiumIngot, 5, PlatinumQuiver, 50000); + break; + } + goto L_Quiver; + + + + + + + + +OnInit: + .sex = G_MALE; + .distance = 5; + setarray .WeaponsCraft, WoodenSword, BugSlayer, ShortGladius, RealBronzeGladius, Backsword; + + sleep(SHOPWAIT); + tradertype(NST_MARKET); + sellitem Backsword, -1, 1; + sellitem ShortGladius, -1, 1; + sellitem BugSlayer, -1, 1; + sellitem WoodenSword, -1, 3; + sellitem Dagger, 600, 5; + sellitem SharpKnife, 450, 10; + end; + +OnClock0009: + restoreshopitem Backsword, 1; + restoreshopitem ShortGladius, 1; + restoreshopitem BugSlayer, 1; +OnClock0603: +OnClock1207: +OnClock1801: + restoreshopitem Dagger, 600, 5; + restoreshopitem SharpKnife, 450, 10; + end; +// Pay your taxes! +OnBuyItem: + PurchaseTaxes("Hurns"); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes("Hurns"); + end; +} diff --git a/npc/012-6/_import.txt b/npc/012-6/_import.txt new file mode 100644 index 0000000..c690c77 --- /dev/null +++ b/npc/012-6/_import.txt @@ -0,0 +1,4 @@ +// Map 012-6: Hurnscald Hospital +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/012-6/_warps.txt", +"npc/012-6/nurse.txt", diff --git a/npc/012-6/_warps.txt b/npc/012-6/_warps.txt new file mode 100644 index 0000000..6ee0897 --- /dev/null +++ b/npc/012-6/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-6: Hurnscald Hospital warps +012-6,58,69,0 warp #012-6_58_69 0,0,012-1,110,57 +012-6,64,64,0 warp #012-6_64_64 1,0,012-6,28,32 +012-6,28,33,0 warp #012-6_28_33 1,0,012-6,64,65 diff --git a/npc/012-6/nurse.txt b/npc/012-6/nurse.txt new file mode 100644 index 0000000..8e8d9af --- /dev/null +++ b/npc/012-6/nurse.txt @@ -0,0 +1,110 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Crazyfefe +// Notes: +// HurnscaldQuest_BloodDonor +// 1 - Donation finished +// 2 - Donation in progress +// Structure +// Status, Next Attempt Timer + +012-6,56,67,6 script Hurnscald's Nurse NPC_FEMALE,{ + .@q2=getq2(HurnscaldQuest_BloodDonor); + if (.@q2 > gettimetick(2)) { + npctalk3 l("You can donate blood again in %s", FuzzyTime(.@q2)); + Nurse(.name$, 10, 6); + close; + } + mesn; + mesq l("Hello there. Do you want to donate blood?"); + mesc l("Donating blood will BLOCK YOUR MOVEMENT for three minutes."); + mesc l("Do not disconnect while waiting. You need 100% HP to donate."); + +L_BLCore: + mes ""; + select + l("I need infirmary services."), + l("Info"), + rif(readparam(Hp) == readparam(MaxHp), l("Yes, please use my blood to save lifes.")); + mes ""; + switch (@menu) { + case 1: + Nurse(.name$, 10, 6); + close; + case 2: + mesn; + mesq l("Donating blood is a noble act, which allows to save lifes."); + next; + mesn; + mesq l("When people loses too much blood, they die, unless we can lend them somebody's else blood."); + next; + mesn; + mesq l("Not everyone can be a blood donor. For example, you must be healthy."); + mesq l("You can find more info about this on these links:"); + mes ""; + mes "[@@https://www.blood.co.uk/|https://www.blood.co.uk/@@]"; + mes "[@@https://www.nhsbt.nhs.uk/what-we-do/blood-services/blood-donation/|https://www.nhsbt.nhs.uk/what-we-do/blood-services/blood-donation/@@]"; + mes l("France: [@@https://dondesang.efs.sante.fr/|https://dondesang.efs.sante.fr/@@]"); + mes l("Brazil: [@@http://www.prosangue.sp.gov.br/home/Default.html|http://www.prosangue.sp.gov.br/home/Default.html@@]"); + next; + goto L_BLCore; + case 3: + mesn; + mesq l("Please don't sound like I was some sort of vampire or something..."); + next; + mesn; + mesq l("This will take only a short while."); + next; + setq1 HurnscaldQuest_BloodDonor, 2; + percentheal -90, 0; + addtimer(180000, "Hurnscald's Nurse::OnDonationComplete"); + slide 34, 29; + setpcblock(PCBLOCK_SOFT, true); + dispbottom l("Any movement/skill/item will be without effect until time is up."); + closedialog; + close; + } + + close; + +OnDonationComplete: + if (checkpcblock() & PCBLOCK_ATTACK) + setpcblock(PCBLOCK_SOFT, false); + slide 58, 67; + percentheal 100, 0; + getexp readparam(Hp)*2, readparam(Hp)/100; + .@q3=getq3(HurnscaldQuest_BloodDonor)+1; + // Cooldown: 60*60*24*30: 30 days + setq HurnscaldQuest_BloodDonor, 1, gettimetick(2)+(2592000), .@q3; + if (BaseLevel >= 30) + getitem Bread, 1; + if (BaseLevel >= 50) + getitem ChocolateBar, 1; + if (BaseLevel >= 70) + getitem BottleOfWoodlandWater, 1; + mesn; + mesq l("Thanks for donating blood. You can donate again in 30 days."); + mesq l("You should eat and drink water after donating blood. Thanks for saving lifes!"); + if (.@q3 == 5) { + getitem Slippers, 1; + next; + mesn; + mesq l("Now you must feel homelike already here... take these slippers so your feet stays warm!"); + } + close; + +OnInit: + .@npcId = getnpcid(.name$); + //setunitdata(.@npcId, UDT_HEADTOP, BrimmedFeatherHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, MiniSkirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, ShortTankTop); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 16); + + .sex = G_FEMALE; + .distance = 5; + end; + +} + diff --git a/npc/012-7/_import.txt b/npc/012-7/_import.txt new file mode 100644 index 0000000..aaf345c --- /dev/null +++ b/npc/012-7/_import.txt @@ -0,0 +1,7 @@ +// Map 012-7: Town Hall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/012-7/_warps.txt", +"npc/012-7/airlia.txt", +"npc/012-7/celestia.txt", +"npc/012-7/politics.txt", +"npc/012-7/slots.txt", diff --git a/npc/012-7/_warps.txt b/npc/012-7/_warps.txt new file mode 100644 index 0000000..7cc2e52 --- /dev/null +++ b/npc/012-7/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-7: Town Hall warps +012-7,35,65,0 warp #012-7_35_65 0,0,012-1,102,70 diff --git a/npc/012-7/airlia.txt b/npc/012-7/airlia.txt new file mode 100644 index 0000000..9b0eaf5 --- /dev/null +++ b/npc/012-7/airlia.txt @@ -0,0 +1,182 @@ +// TMW2 Script +// Author: +// Jesusalva +// Personality Traits: +// Bitter-sweet, resentful, lovely, cute +// Description: +// Former Town's Mayor Daughter. +// Have a grudge against current mayor. +// Her father was banished from the town after the famine and the Monster Attack (Siege) +// TODO: Remember to use disablenpc() and enablenpc() to keep consistency with Hurns Liberation Day +// A new mayor was elected, which was her family political enemy. She misses her father, +// which she doesn't knows where he is because the banishment. Main storyline unlocks +// this side quest, but the main storyline keeps going forward regardless of that. +// NOTE: Here, 'main storyline' is character story, do not mistake with world main storyline. + +012-7,52,58,0 script Airlia NPC_ELF_F,{ + .@q1=getq(HurnscaldQuest_Farmers); + .@n = getq(General_Narrator); + if (strcharinfo(0) == $MOST_HEROIC$) npctalk3 l("Ah, if it isn't @@? You're the talk of the town!", $MOST_HEROIC$); + if (.@q1 == 4) goto L_Letter; + if (.@n >= 3 && .@n < 6) goto L_Mayor; + if (.@n == 9) goto L_Nivalis; + mesn; + mesq l("Oh, a visitor. Welcome to the city hall. Please, enjoy your stay."); + close; + +// Hurnscald Farmers Quest +L_Letter: + mesn; + mesq l("You're the savior of Hurnscald crops. Half from the world would die from famine, weren't for you."); + next; + mesn; + mesq l("We're currently the biggest exporter of food. We may be a small town, but Tulimshar and Halinarzo can barely hold themselves."); + next; + mesn; + mesq l("Here, take this @@. This is the proof that you've helped Hurnscald greatly. This shall open new opportunities to you.", getitemlink(CottonBoots)); + inventoryplace CottonBoots, 1; + setq HurnscaldQuest_Farmers, 5; + getitem CottonBoots, 1; + getexp 0, 1000; + close; + +// Main Storyline & Airlia Quest +L_Mayor: + .@q=getq2(General_Narrator); + + // 3 - Quest Not Assigned + if (.@n == 3) + goto L_In1; + + // 4 - Airlia doesn't wants to talk about </3 + if (.@n == 4) { + if (.@q == 2) goto L_In3; + if (.@q == 1) goto L_In2; + } + + // 5 - Access to Rakinorf was granted + if (.@n == 5) + goto L_In4; + + mesc l("Script Error"), 1; + close; + +L_In1: + mesn strcharinfo(0); + mesq l("Hello! Could I see the Mayor?"); + next; + mesn; + mesq l("Ah... The mayor. The current mayor. Why do you even want to talk to him, anyway?"); + next; + mesn; + mesq l("Anyway, he is not available at the moment. Try your luck again later."); + setq General_Narrator, 4, 1; + close; + +L_In2: + mesn strcharinfo(0); + mesq l("Hello! Could I see the Mayor?"); + next; + mesn; + mesq l("I already told you he is out! Why do you even want to talk to him?! Haven't I told you to... to... leave me alone!?"); + setq2 General_Narrator, 2; + close; + +L_In3: + mesn strcharinfo(0); + mesq l("Hello! Could I see the Mayor?"); + next; + mesn; + mes "..."; + next; + mes "... ..."; + next; + mes "... ... ..."; + next; + mesn; + mesq l("Fine."); + next; + mesn; + mesq l("The mayor, Rakinorf, is probably on the Inn upstairs, drinking himself to death."); + next; + mesn; + mesq l("Usually only sponsors are allowed up there, but I'll make an exception today. Go meet the mayor."); + setq General_Narrator, 5, 0; + close; + +L_In4: + if (.@q == 5) + goto L_In5; + mesn; + mesq l("The mayor, Rakinorf, is probably on the Inn upstairs, drinking himself to death."); + next; + mesn; + mesq l("Usually only sponsors are allowed up there, but I'll make an exception today. Go meet the mayor."); + close; + +L_In5: + mesn; + mesq l("Go away."); + next; + mesn strcharinfo(0); + mesq l("I can't. Rakinorf told me I should bring this matter to you instead."); + next; + mesn; + mesq l("That lazy mayor... He can't do anything on his own!! Sure, what do you need?"); + next; + mesn strcharinfo(0); + mesc l("You explain to Airlia about you wanting to travel to Halinarzo to find clues."); + next; + mesn; + mesq l("What, only that? Well, this is actually simpler than it looks."); + next; + mesn; + mesq l("Basically, there's no sea route to Halinarzo. You must head southeast of Tulimshar."); + next; + mesn; + mesq l("Halinarzo lies past the Canyon, but the @@ there are level @@.", getmonsterlink(Snake), strmobinfo(3, Snake)); + next; + mesn; + mesq l("So, you would die a few thousands of times before ever reaching Halinarzo."); + next; + mesn; + mesq l("But with a @@, when you are about to die, you can warp back to Soul Menhir and bypass death penalty.", getitemlink(ReturnPotion)); + next; + mesn; + mesq l("That's a super rare potion, and it expires after some time. Take care!"); + setq2 General_Narrator, 6; + close; + +L_Nivalis: + // Supposed to send player to talk with another NPC on Nivalis about World's Edge + mesn; + mesq l("Going to World Edge? Never heard of."); + next; + mesn; + mesq l("My father probably knew... Well, you better ask Blue Sage then."); + next; + mesn; + mesq l("The Blue Sage is Nivalis Mayor and very knowledgeable on stuff. Lemme write a letter of recommendation for you and sign it in Rakinorf's stead..."); + next; + mesc b(l(".:: Main Quest 4-1 ::.")), 3; + mesc l("* Meet the Blue Sage"), 9; + setq General_Narrator, 10, 0; + next; + mesn; + mesq l("There you go. I wish you good luck. @@", col(l("*grumpf*"), 9)); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, Earmuffs); + setunitdata(.@npcId, UDT_HEADMIDDLE, RedknightArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, Boots); + setunitdata(.@npcId, UDT_WEAPON, CottonSkirt); + setunitdata(.@npcId, UDT_HAIRSTYLE, 20); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/012-7/celestia.txt b/npc/012-7/celestia.txt new file mode 100644 index 0000000..b49b52a --- /dev/null +++ b/npc/012-7/celestia.txt @@ -0,0 +1,396 @@ +// TMW2 Scripts +// Author: +// gumi (TMW Org. Team) +// Jesusalva +// Description: +// Celestia Yeti King's quest. Designed so if you're with 4 players, all 4 can +// summon the Yeti King, helping you while doing the quest by themselves. + + +012-7,35,49,0 script Celestia NPC_ELF_F,{ + .@q1=getq(HurnscaldQuest_Celestia); + .@q2=getq(HurnscaldQuest_TeaParty); + mesn; + mesq lg("Hello, darling. I was thinking in doing a tea party."); + if (.@q1) + mesc l("You notice that even if she tries to calm down everyone and serve tea, she's pretty distressed with the Monster King herself."); + next; + mesn strcharinfo(0); + menu + l("Sorry, I'm more of a coffee person."), L_Coffee, + l("What an amazing crown you have! How do I get it?!"), L_Crown, + rif(getvaultid() && get_nibble(##01_TMWQUEST, 0) == 2, l("Actually, have you ever heard of Yeti's kidnapping little girls?")), L_VaultQuest, + rif(.@q2 == 0 && BaseLevel >= 35, l("Tea party! I want to participate.")), L_TeaQuest, + rif(.@q2 == 1, l("I have some sweeties for the Tea Party!")), L_TeaCheck, + rif(.@q2 >= 2, l("Tea party! I want to participate.")), L_TeaParty, + rif(.@q2 >= 2, l("Do you sell tea?")), L_TeaShop, + rif(.@q1 == 0, l("There are Monster Attacks every month, how do you have time for tea parties?!")), L_MainQuest, + rif(.@q1 && CINDY_PLAYER_STRUCK && getq(NivalisQuest_Well) < 2, l("I found switches which only a Yeti may flip...")), L_OtherQuest, + rif(.@q1 >= 6, l("I want to try convincing the Yeti King again.")), L_MainQuestCore; + +L_Coffee: + mes ""; + mesn; + mesq l("Hmpf! Coffee is an heresy!"); + // Except I love coffee ~ Jesusalva + close; + +L_Crown: + mes ""; + mesn; + mesq l("Ooh, this nice shiny thing? It was an event."); + next; + mesn; + mesq l("Saulc is rich, you know. He challenged a group of thirty NPCs to defeat him, whoever won would get this @@.", getitemlink(MurdererCrown)); + next; + mesn; + mesq l("Well, Andrei Sakar said it was a waste a time, and the other NPCs started fighting against themselves, so I won."); + next; + mesn; + if (BaseLevel < 100) + mesq l("...Besides, I have this nice @@ with me. I'm not low-level like you.", getitemlink(BansheeBow)); + else + mesq l("...Besides, I have this nice @@ with me. It's a reliable bow.", getitemlink(BansheeBow)); + close; + +L_MainQuest: + mes ""; + mesn; + mesc l("Celestia eyes sparkles."); + mesq l("Someone who have clear priorities appeared! I'm glad!"); + next; + mesn; + mesq l("You see, most people think that all monsters are controlled by the Monster King, but that's not quite right."); + next; + mesn; + mesq l("Some are just living their own lives. In special, I've spent part of my life studying the Yeti Society."); + next; + mesn; + mesq l("And guess what? They have a King of their own!"); + mesq l("If we could convince them to distract the Monster King, we could have chances to do a counter-attack!"); + next; + mesn; + mesq l("But that's not so easy... I already sent many people, and even come myself, but he refuses to help."); + next; + +L_MainQuestCore: + mesn; + mesq l("Maybe you could convince him. There's a summoning circle on Tulimshar Sewers."); + next; + mesn; + mesq l("Go very close to it and, speak the four four-letters magic words, pour some @@, and he'll appear to you.", getitemlink(EverburnPowder)); + next; + mesn; + mesq l("Don't go alone, though! He would not listen to me, it is not you alone he'll pay attention. You will lose reagents!"); + next; + mesc l("Accept quest?"); + if (askyesno() == ASK_YES) { + setq HurnscaldQuest_Celestia, 1; + @YetiKing_Challenger=0; + mes ""; + mesn; + mesq l("Good, good. The chant is ##B EMOC OTEM ITEY GNIK ##b. Good luck!"); + } + close; + +L_TeaQuest: + mes ""; + mesn; + mesq l("Well, the reason why I'm not doing it already is because, you see, I lack cookies."); + if (countitem(DeliciousCookie)) { + next; + mesn; + mesq l("And anyone like you probably already knows that, well... Cookies are life!"); + } + next; + mesn; + mesq l("But I can still make it without cookies, as long that you bring me some other sweeties."); + next; + mesn l("Item List"); + mes l("@@/50 @@", countitem(Acorn), getitemlink(Acorn)); + mes l("@@/20 @@", countitem(ChocolateMouboo), getitemlink(ChocolateMouboo)); + mes l("@@/40 @@", countitem(ChocolateBar), getitemlink(ChocolateBar)); + mes l("@@/15 @@", countitem(Candy), getitemlink(Candy)); + mes l("@@/15 @@", countitem(OrangeCupcake), getitemlink(OrangeCupcake)); + mes l("@@/10 @@", countitem(CherryCake), getitemlink(CherryCake)); + mes l("@@/5 @@", countitem(GingerBreadMan), getitemlink(GingerBreadMan)); + mes l("@@/2 @@", countitem(ChocolateBiscuit), getitemlink(ChocolateBiscuit)); + /* We can't ask for THAT many! + mes l("@@/20 @@", countitem(ApanaCake), getitemlink(ApanaCake)); + mes l("@@/20 @@", countitem(Mashmallow), getitemlink(Mashmallow)); + mes l("@@/20 @@", countitem(TonoriDelight), getitemlink(TonoriDelight)); + mes l("@@/20 @@", countitem(EasterEgg), getitemlink(EasterEgg)); + mes l("@@/20 @@", countitem(ChocolateBunny), getitemlink(ChocolateBunny)); + mes l("@@/20 @@", countitem(FrozenYetiTear), getitemlink(FrozenYetiTear)); + mes l("@@/20 @@", countitem(DeliciousCookie), getitemlink(DeliciousCookie)); + */ + setq HurnscaldQuest_TeaParty, 1; + close; + +L_TeaCheck: + mesn l("Item List"); + mes l("@@/50 @@", countitem(Acorn), getitemlink(Acorn)); + mes l("@@/20 @@", countitem(ChocolateMouboo), getitemlink(ChocolateMouboo)); + mes l("@@/40 @@", countitem(ChocolateBar), getitemlink(ChocolateBar)); + mes l("@@/15 @@", countitem(Candy), getitemlink(Candy)); + mes l("@@/15 @@", countitem(OrangeCupcake), getitemlink(OrangeCupcake)); + mes l("@@/10 @@", countitem(CherryCake), getitemlink(CherryCake)); + mes l("@@/5 @@", countitem(GingerBreadMan), getitemlink(GingerBreadMan)); + mes l("@@/2 @@", countitem(ChocolateBiscuit), getitemlink(ChocolateBiscuit)); + next; + select + l("I will be back shortly."), + l("I have all."); + mes ""; + + if (@menu == 1) { + closedialog; + goodbye; + close; + } + if (countitem(Acorn) < 50 || + countitem(ChocolateMouboo) < 20 || + countitem(ChocolateBar) < 40 || + countitem(Candy) < 15 || + countitem(OrangeCupcake) < 15 || + countitem(CherryCake) < 10 || + countitem(GingerBreadMan) < 5 || + countitem(ChocolateBiscuit) < 2) + goto L_Lying; + + inventoryplace CelestiaTea, 3; + + delitem Acorn, 50; + delitem ChocolateMouboo, 20; + delitem ChocolateBar, 40; + delitem Candy, 15; + delitem OrangeCupcake, 15; + delitem CherryCake, 10; + delitem GingerBreadMan, 5; + delitem ChocolateBiscuit, 2; + + getexp rand(13500, 14500), 0; // level exp cap: 15,775 + getitem CelestiaTea, 3; + setq HurnscaldQuest_TeaParty, 2, gettimetick(2); + + mes ""; + mesn; + mesq l("Many thanks! I'll arrange the table."); + close; + +L_Lying: + mesn; + mesq l("Wha- How dare you, to lie to me!"); + // She'll try to drain all your MP. If you're not MP Full, you'll die. And will have EXP penalty, of course. + if (Sp == MaxSp) + percentheal 0, -100; + else + die(); + close; + +// begin: Gumi Script +L_TeaParty: + .@q2=getq2(HurnscaldQuest_TeaParty); + mes ""; + mesn; + mesq l("Wonderful, I am happy to have you over. Lets get things started with some puerh tea."); + next; + mesc l("Celestia hands you a cup filled with some type of tea that is unlike anything you have seen before."); + mesc l("It has a very dark color and an unusual aroma reminiscent of a moist forest."); + next; + mesc l("For a moment you wonder if Celestia might have gotten confused and tossed in a handful of forest dirt into the teapot."); + mesc l("You ponder whether or not it would be wise to drink it."); + menu + l("Uh are you sure this is fit to drink?"), L_QuestionTea, + l("(Drink the tea, hoping for the best)"), L_DrinkTea; + +L_DrinkTea: + if (.@q2 < gettimetick(2)) { + percentheal 0, 100; + setq2 HurnscaldQuest_TeaParty, gettimetick(2)+180; + } + mesc l("To your pleasant surprise the tea is actually quite good."); + next; + mesc l("Despite its initial dubious fragrance, the tea comes off as very smooth and mellow with a bit of natural sweetness and a touch of an earthy forest like flavor, but in a very good way."); + next; + mesc l("Clearly an exotic tea, with a refined flavor fit for a refined woman such as Celestia."); + close; + +L_QuestionTea: + mesq l("Yes, did you really think I was some kind of monster that would try to poison you in my own house?"); + mes l("##a(it would be way too messy anyway)##0"); + next; + mesc l("Celestia then picks up the cup of tea and drinks it in front of you to demonstrate that it is not only harmless but also quite delectable."); + if (.@q2 < gettimetick(2)) { + getitem CelestiaTea, 1; + setq2 HurnscaldQuest_TeaParty, gettimetick(2)+(60*60*2); + } + close; +// end: Gumi Script + +L_TeaShop: + mesn; + mesq l("Well, my tea is just too good."); + next; + closeclientdialog; + npcshopattach(.name$); + shop .name$; + close; + +L_OtherQuest: + mesn strcharinfo(0); + mesq l("Basically, I need to find a friendly Yeti. Do they exist?"); + next; + mesn; + mesq l("Of course. The adviser of the Yeti King, for instance, is very friendly."); + next; + mesn; + mesq l("But he have lots of enemies... It would not surprise me to find him struck somewhere."); + next; + mesn; + mesq l("He usually guards a central position on the caves... He could help you, if he is there."); + next; + mesn; + mesq l("Otherwise, his enemies might have bested him, but I doubt any Yeti would dare to kill him."); + next; + mesn; + mesq l("Wasn't there something on the Holy Bible? About a young boy trapped by his brothers somewhere because envy?"); + mesc l("If you don't want to look at the Bible, you can try to solve the following riddle:"); + mesc l("\"When you're thirsty, you may look for me. I'm often in desert areas, but this time, the winter has come.\""), 3; + CINDY_PLAYER_STRUCK=false; + next; + clear; + mesn l("Quest Tip"); + mesc l("\"When you're thristy, you may look for me. I'm often in desert areas, but this time, the winter has come.\""), 3; + close; + +// Mirror Lake +L_VaultQuest: + if (##02_MLWORLD & MLP_TMW_CELESTIA) { + mesn; + mesq l("Yes, try to summon the Yeti King in Tulimshar Sewers. Make sure you have someone else with you to help."); + close; + } + mesn; + mesq l("While I don't think the Yeti King would ever do or endorse that, yes, I've heard this happens."); + next; + select + l("Wait, Yeti's have a king?"), + l("I thought they ate little girls."), + l("Haha, as if I believe you."); + mes ""; + if (@menu == 3) close; + if (@menu == 2) { + mesn; + mesq l("What? Don't be ridiculous. Not even the Monster King does that! I think."); + next; + mesn; + mesq l("In other words, I never heard of a Yeti eating a kid before. They prefer %s.", getitemlink(MoubooSteak)); + next; + } if (@menu == 1) { + // Player already know that ¬.¬ + if (getq(HurnscaldQuest_Celestia) != 0) { + mesn; + mesq l("Are you trying to provoke me?"); + close; + } + mesn; + mesq l("You see, most people think that all monsters are controlled by the Monster King, but that's not quite right."); + next; + mesn; + mesq l("Some are just living their own lives. In special, I've spent part of my life studying the Yeti Society."); + next; + mesn; + mesq l("And guess what? They have a King of their own!"); + next; + } + mesn; + mesq l("Anyway, why do you ask? Surely not to waste my time."); + next; + select + l("[Explain the situation]"), + l("[Make up a lie]"); + mes ""; + mesn; + mesq l("...Okay, I asked you to explain, and you ended up wasting my time instead. %%n"); + next; + mesn; + mesq l("Listen, I was trying to get the Yeti King to help me to fight the Monster King. If you want to go there and do it, be my guest; Maybe they'll even help you with whatever your problem is."); + if (countitem(MirrorLakeArmor)) + mesc l("PS. This char will be reset on logout."); + next; + select + l("Sure, I'll help."), + l("No way!"); + if (@menu == 2) close; + mes ""; + mesn; + mesq l("Alright. Listen, you'll need %s to summon it, and you cannot be alone. I only have so much of that to share, so be careful to don't use all.", getitemlink(EverburnPowder)); + next; + if (countitem(MirrorLakeArmor)) { + mesn; + mesq l("Your weird robes; They seem like they'll deny your damage, so you definitely want to summon multiple people to help. Try the #world tab."); + next; + } + mesn; + mesq l("There's a summoning circle on Tulimshar Sewers. You can reach either by boat, if you have the money - I'll lend you some; Or by the long way."); + next; + mesn; + mesq l("Go very close to it and, speak the four four-letters magic words, pour some @@, and he'll appear to you.", getitemlink(EverburnPowder)); + next; + mesn; + mesq l("Don't go alone, though! He would not listen to me, it is not you alone he'll pay attention. You will lose reagents!"); + next; + ##02_MLWORLD=##02_MLWORLD|MLP_TMW_CELESTIA; + Zeny+=410; + getitembound(EverburnPowder, 5, 4); + setq HurnscaldQuest_Celestia, 1; + @YetiKing_Challenger=0; + mesq l("The chant is ##B EMOC OTEM ITEY GNIK ##b. Good luck!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, MurdererCrown); + setunitdata(.@npcId, UDT_HEADMIDDLE, ValentineDress); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 19); + setunitdata(.@npcId, UDT_HAIRCOLOR, 10); + + npcsit; + + .sex = G_FEMALE; + .distance = 5; + + tradertype(NST_MARKET); + sellitem CelestiaTea, -1, 1; + sellitem YerbaMate, -1, 2; + sellitem JasmineTea, -1, 3; + sellitem OolongTea, -1, 4; + sellitem SpearmintTea, -1, 5; + sellitem ChamomileTea, -1, 10; + end; + +OnClock0002: + restoreshopitem CelestiaTea, 1; + restoreshopitem YerbaMate, 2; + restoreshopitem JasmineTea, 3; + restoreshopitem OolongTea, 4; + restoreshopitem SpearmintTea, 5; + restoreshopitem ChamomileTea, 10; + end; + + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} diff --git a/npc/012-7/politics.txt b/npc/012-7/politics.txt new file mode 100644 index 0000000..7d9a0bb --- /dev/null +++ b/npc/012-7/politics.txt @@ -0,0 +1,58 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Town Administrator file, see npc/functions/politics.txt +// User variables: +// #POL_APPLYWEEK = Week of last application +// #POL_VOTEDAY = Day of last vote + +012-7,51,60,0 script Hurnscald Office NPC_POLITICS,{ +do +{ + mesc ".:: "+l("Hurnscald Townhall")+" ::.", 2; + mesc l("Current Town Administrator: ")+$HURNS_MAYOR$, 3; + POL_TownInfo("HURNS"); + mesc l("Application fee: @@ GP", .applytax); + next; + select + l("Information"), + rif(strcharinfo(0) == $HURNS_MAYOR$, l("Manage Town")), + rif(#POL_APPLYWEEK != gettimeparam(GETTIME_WEEKDAY), l("Apply for the office!")), + l("View Candidate List and cast a vote"), + l("[Quit]"); + + switch (@menu) { + case 1: + POL_Information(); + break; + case 2: + POL_Manage("HURNS"); + break; + case 3: + // array_push might be too sensible for getd/setd + if (Zeny < .applytax) + break; + Zeny-=.applytax; + $HURNS_MONEY+=.applytax; + #POL_APPLYWEEK=gettimeparam(GETTIME_WEEKDAY); + array_push($HURNS_CANDIDATE$, strcharinfo(0)); + array_push($HURNS_VOTES, 0); + mesc l("Application successful!"), 3; + next; + break; + case 4: + POL_Candidate("HURNS"); + break; + default: + close; + } +} while (true); +end; + +OnInit: + .applytax=100; + .distance=4; + end; +} + diff --git a/npc/012-7/slots.txt b/npc/012-7/slots.txt new file mode 100644 index 0000000..3a37c0a --- /dev/null +++ b/npc/012-7/slots.txt @@ -0,0 +1,97 @@ +// TMW2 Scripts +// Author: +// Jesusalva, Saulc +// Description: +// Slot Machine for bets in Hurnscald +// Rare Reward: Paper Bag + +012-7,53,31,0 script Slot Machine#012-7a NPC_SLOTMACHINE,{ + function symbol { + switch (getarg(0)) { + case 1: + mesn "%%A"; + break; + case 2: + mesn "%%B"; + break; + case 3: + mesn "%%C"; + break; + case 4: + mesn "%%D"; + break; + case 5: + mesn "%%E"; + break; + case 6: + mesn "%%F"; + break; + case 7: + mesn "7"; + break; + case 8: + mesn "%%8"; + break; + default: + mesn "%%@"; + break; + } + return; + } + +L_Menu: + mesn; + mesc l("Spin three symbols, and jackpot great rewards!"); + mesc l("Just one coin for spin."); + next; + menu + rif(countitem(CasinoCoins) >= 1, l("Spin!")), L_Spin, + l("Prizes"), L_Info, + l("Leave"), -; + close; + +L_Info: + mes ""; + mesc l("Prizes:"); + mes l("##9 777: @@.", getitemlink(PaperBag)); + mesc l("Three equal: @@.", "20 casino coins"); + mesc l("Two equal: 1 casino coin."); + next; + goto L_Menu; + + +L_Spin: + mesc l("Spinning..."); + next; + delitem CasinoCoins, 1; + .@a=rand2(1,8); + .@b=rand2(1,8); + .@c=rand2(1,8); + symbol(.@a); + symbol(.@b); + symbol(.@c); + next; + mesn; + if (.@a == .@b && .@a == .@c && .@a == 7) { + getitem PaperBag, 1; + mesc l("Jackpot! You got the Paper Bag!"), 3; + } else if (.@a == .@b && .@a == .@c) { + getitem CasinoCoins, 20; + mesc l("Congrats! A pity it was not 777..."), 3; + } else if (.@a == .@b || .@a == .@c || .@b == .@c) { + getitem CasinoCoins, 1; + mesc l("Lucky! You got the coin back!"), 3; + } else { + mesc l("It wasn't this time..."), 3; + } + next; + goto L_Menu; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; + + +} + diff --git a/npc/012-8/_import.txt b/npc/012-8/_import.txt new file mode 100644 index 0000000..615e3e7 --- /dev/null +++ b/npc/012-8/_import.txt @@ -0,0 +1,5 @@ +// Map 012-8: Real Estate +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/012-8/_warps.txt", +"npc/012-8/doorbell.txt", +"npc/012-8/utils.txt", diff --git a/npc/012-8/_warps.txt b/npc/012-8/_warps.txt new file mode 100644 index 0000000..e431d2b --- /dev/null +++ b/npc/012-8/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 012-8: Real Estate warps +012-8,34,34,0 warp #012-8_34_34 1,0,012-1,95,70 diff --git a/npc/012-8/doorbell.txt b/npc/012-8/doorbell.txt new file mode 100644 index 0000000..e9f5877 --- /dev/null +++ b/npc/012-8/doorbell.txt @@ -0,0 +1,335 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Doorbell allows you to purchase mobilia, besides loading it when server starts +// Each layer can have 32 different furniture pieces because bitmask limit. +// This file is custom to every room + +// ID: 1 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +012-8,32,34,0 script Doorbell#RES_0128 NPC_NO_SPRITE,{ + // Name, Layer, Price, ID, x1, y1, x2, y2, + function create_object { + array_push(.nams$, getarg(0)); + array_push(.layer, getarg(1)); + array_push(.price, getarg(2)); + array_push(.objid, getarg(3)); + array_push(.x1, getarg(4)); + array_push(.y1, getarg(5)); + array_push(.x2, getarg(6)); + array_push(.y2, getarg(7)); + return; + } + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + close; + +// When using setcells() a player could get trapped! +// This label will slide the player back to entrance, which should be a safe spot +OnSlide: + slide 33, 33; + end; + +// If someone press the doorbell from outside and doorbell is enabled +OnDoorbell: + if ($ESTATE_DOORBELL[.id]) + end; + + if (.dpost < gettimetick(2)) { + npctalk (strcharinfo(0)+" is pressing the doorbell."); // We actually don't want l() + } + .dpost=gettimetick(2)+.delay; + end; + +// Managment Menu +L_Manage: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Managment Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Rent time available: @@", FuzzyTime($ESTATE_RENTTIME[.id])); + mesc l("Total Credits and GP: @@", format_number(.@gp)); + mes ""; + mesc l("Room password: @@", $ESTATE_PASSWORD$[.id]); + if ($ESTATE_DOORBELL[.id]) + mesc l("Doorbell is disabled"), 1; + + next; + select + l("Leave"), + l("Enable/disable doorbell"), + l("Manage Furniture"), + l("Set room password"); + + switch (@menu) { + case 1: + close; + break; + case 2: + $ESTATE_DOORBELL[.id]=!$ESTATE_DOORBELL[.id]; + break; + case 3: + goto L_Furniture; + break; + case 4: + mesc l("(Leave the password blank to disable)"); + mesc l("Current Room password: @@", $ESTATE_PASSWORD$[.id]); + mesc l("Input new password: "); + input .@password$; + mesc l("Repeat new password: "); + input .@passwordc$; + if (.@password$ == .@passwordc$) { + $ESTATE_PASSWORD$[.id]=.@password$; + mesc l("Password changed with success!"), 3; + } else { + mesc l("The passwords doesn't match."), 1; + } + break; + } + goto L_Manage; + +L_Furniture: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Furniture Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Total Credits and GP: @@", format_number(.@gp)); + + next; + select + l("Finish"), + l("Manage Beds"), + l("Manage Utilities"), + l("Manage Luxury furniture"), + l("Manage Decoration"), + l("Manage Chairs"), + l("Manage Paintings"); + mes ""; + + switch (@menu) { + case 1: + goto L_Manage; + break; + case 2: + mesc ".:: "+ l("Beds") + " ::.", 3; + @re_col=RES_OBJECTS; + break; + case 3: + mesc ".:: "+ l("Utilities") + " ::.", 3; + @re_col=RES_UTILITIES; + break; + case 4: + mesc ".:: "+ l("Luxury furniture") + " ::.", 3; + @re_col=RES_LUXURY; + break; + case 5: + mesc ".:: "+ l("Decoration") + " ::.", 3; + @re_col=RES_DECORATION; + break; + case 6: + mesc ".:: "+ l("Chairs") + " ::.", 3; + @re_col=RES_SITTABLE; + break; + case 7: + mesc ".:: "+ l("Paintings") + " ::.", 3; + @re_col=RES_WALLDECORATION; + break; + } + +// L_ContinuousLoop +// Requires the following variables: +// @re_col +// Target Collision ID +L_ContinuousLoop: + deletearray @valid_ids; + + // Create a second array (@valid_ids) with the ID of objects within @re_col group + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + //debugmes "Found object ID %d named %s on layer %s coords (%d,%d) - Looking for layer %d", .@i, .nams$[.@i], .layer[.@i], .x1[.@i], .y1[.@i], @re_col; + if (.layer[.@i] == @re_col) + array_push(@valid_ids, .@i); + } + //debugmes "Found %d valid objects", getarraysize(@valid_ids); + + // Create the menu with @valid_ids - Check if you already have the item to decide if you're buying or selling + @menuentries$="Finish:"; + for (.@j=0; .@j < getarraysize(@valid_ids); .@j++) { + .@i=@valid_ids[.@j]; + if (realestate_hasmobilia(.id, .layer[.@i], .objid[.@i])) + @menuentries$+=l("Sell ")+.nams$[.@i]+l(" for ") + format_number( realestate_sellprice(.id,.price[.@i]) ) +":"; + else + @menuentries$+=l("Purchase ")+.nams$[.@i]+(" for ") + format_number( .price[.@i] )+":"; + } + select (@menuentries$); + mes ""; + + // First option to return to previous menu + if (@menu == 1) + goto L_Furniture; + + // Otherwise, we know then that (@menu-2) is the ID in @valid_ids + // So we save .@id with the correct ID in object arrays. + // We also calculate how much aggregated money you have. + .@id=@valid_ids[@menu-2]; + .@gp=REAL_ESTATE_CREDITS+Zeny; + + if (realestate_hasmobilia(.id, .layer[.@id], .objid[.@id])) { + // If you have the mobilia, you're selling it for Mobiliary Credits + delcells realestate_cellname(.id, .@id); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0128"); + REAL_ESTATE_CREDITS+=realestate_sellprice(.id,.price[.@i]); + mesc l("Sale successful!"); + next; + } else { + // Else, you're buying it, so we must check if you have the moolah first + .@price=.price[.@id]; + if (.@gp > .@price) { + realestate_payment(.@price); + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + areatimer(.mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], 10, "::OnSlide"); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0128"); + mesc l("Purchase successful!"); + next; + } else { + mesc l("Not enough funds!"); + next; + } + } + + // This loops forever + goto L_ContinuousLoop; + + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=1; // Estate ID + .delay=15; // Forced wait between rings + .dpost=0; // Last doorbell ring + .mapa$="012-8"; + + // Arrays + // We go element by element on the array building the menu + .nams$=""; + .layer=0; + .price=0; + .objid=0; + .x1=0; + .y1=0; + .x2=0; + .y2=0; + + // Furniture Settings + // Name, Collision Layer, Price, ID, x1, y1, x2, y2 + // For Collision Layer, see constants.conf ("Real Estate Collisions") + create_object("Placeholder" ,99,999999,99999, 99, 99, 99, 99); + + create_object("Bed 01" , 5, 5000, 1, 24, 24, 25, 27); + create_object("Bed 02" , 5, 5000, 2, 26, 24, 27, 27); + create_object("Bed 03" , 5, 5000, 4, 28, 24, 29, 27); + create_object("Bed 04" , 5, 5000, 8, 30, 24, 31, 27); + create_object("Bed 05" , 5, 5000, 16, 24, 29, 25, 32); + create_object("Bed 06" , 5, 5000, 32, 26, 29, 27, 32); + create_object("Bed 07" , 5, 5000, 64, 28, 29, 29, 32); + create_object("Bed 08" , 5, 5000, 128, 30, 29, 31, 32); + + create_object("Wardrobe" , 1, 7000, 1, 21, 23, 22, 23); + create_object("Cauldron" , 1, 5000, 2, 28, 24, 29, 24); + create_object("Shelf 01" , 1, 2000, 4, 25, 23, 25, 23); + create_object("Shelf 02" , 1, 2000, 8, 26, 23, 26, 23); + create_object("Shelf 03" , 1, 2000, 16, 27, 23, 27, 23); + create_object("Shelf 04" , 1, 2000, 32, 30, 23, 30, 23); + create_object("Shelf 05" , 1, 2000, 64, 31, 23, 31, 23); + create_object("Shelf 06" , 1, 2000, 128, 32, 23, 32, 23); + create_object("Shelf 07" , 1, 2000, 256, 33, 23, 33, 23); + create_object("Shelf 08" , 1, 2000, 512, 34, 23, 34, 23); + create_object("Shelf 09" , 1, 2000, 1024, 35, 23, 35, 23); + create_object("Shelf 10" , 1, 2000, 2048, 36, 23, 36, 23); + create_object("Shelf 11" , 1, 2000, 4096, 37, 23, 37, 23); + create_object("Shelf 12" , 1, 2000, 8192, 38, 23, 38, 23); + + create_object("Piano" , 3, 10000, 1, 33, 25, 35, 25); + + create_object("Left Desk" , 2, 5000, 1, 20, 25, 22, 27); + create_object("Right Desk" , 2, 5000, 2, 36, 30, 38, 32); + + create_object("Left Chair" , 4, 2000, 1, 21, 28, 21, 28); + create_object("Right Chair" , 4, 2000, 2, 37, 29, 37, 29); + + create_object("Painting 01" , 6, 3000, 1, 21, 20, 21, 20); + create_object("Painting 02" , 6, 3000, 2, 23, 21, 23, 21); + create_object("Painting 03" , 6, 3000, 4, 25, 20, 25, 20); + create_object("Painting 04" , 6, 3000, 8, 28, 21, 28, 21); + create_object("Painting 05" , 6, 3000, 16, 31, 20, 31, 20); + create_object("Painting 06" , 6, 3000, 32, 36, 20, 36, 20); + + // Load Mobilia already existing + //debugmes "[REAL ESTATE] Now loading mobilia"; + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + switch (.layer[.@i]) { + case 1: + if ($ESTATE_MOBILIA_64[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 2: + if ($ESTATE_MOBILIA_4[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 3: + if ($ESTATE_MOBILIA_8[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 4: + if ($ESTATE_MOBILIA_32[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 5: + if ($ESTATE_MOBILIA_128[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 6: + if ($ESTATE_MOBILIA_2[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + default: + // We do nothing by default + //debugmes("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", .@i, .layer[.@i]); + break; + } + } + //debugmes "Found %d valid objects", getarraysize(.valid_ids); + for (.@j=0; .@j < getarraysize(.valid_ids); .@j++) { + .@id=.valid_ids[.@j]; + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + //debugmes "Creating %s in %s", realestate_cellname(.id, .@id), .mapa$; + } + deletearray .valid_ids; + // Load NPCs + donpcevent "NPCs#RES_0128::OnReload"; + end; + +} + + diff --git a/npc/012-8/utils.txt b/npc/012-8/utils.txt new file mode 100644 index 0000000..7e34e91 --- /dev/null +++ b/npc/012-8/utils.txt @@ -0,0 +1,71 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Utils take care of NPCs - Their code, and enable/disable using check_cell +// This file is custom to every room + +// ID: 1 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// Real Estate NPCs and settings +// The sign is the main controller for rent system +// Doorbell is the main controller for indoor +// This is the NPC script controller +012-8,0,0,0 script NPCs#RES_0128 NPC_HIDDEN,{ + // load_npc ( name , map, x , y{, cell} ) + function load_npc { + if (checknpccell(getarg(1), getarg(2), getarg(3), getarg(4, cell_chknopass))) { + enablenpc getarg(0); + //debugmes "ENABLING NPC %s", getarg(0); + } else { + disablenpc getarg(0); + //debugmes "Disabling NPC %s", getarg(0); + } + + /* + debugmes "----- %s (%d,%d) cell report", getarg(1), getarg(2), getarg(3); + debugmes "cell_chknopass: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknopass); + debugmes "cell_chknoreach: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknoreach); + debugmes "cell_chkbasilica: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkbasilica); + debugmes ""; + debugmes "cell_chkwater: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwater); + debugmes "cell_chkwall: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwall); + debugmes "cell_chkcliff: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkcliff); + debugmes "----- Npc Id: %s", getarg(0); + */ + return; + } + end; + +OnInit: + // NPC Settings + .sex = G_OTHER; + .distance = 3; + end; + +// Load or unload accordingly +OnReload: + //debugmes "[REAL ESTATE] NPC ONRELOAD"; + // load_npc ( name , map, x , y{, cell} ) + load_npc("Wardrobe#RES_0128", .map$, 21, 23); + load_npc("Cauldron#RES_0128", .map$, 28, 24); + load_npc("Piano#RES_0128" , .map$, 34, 25); + end; + +} + diff --git a/npc/013-1/_import.txt b/npc/013-1/_import.txt new file mode 100644 index 0000000..f926914 --- /dev/null +++ b/npc/013-1/_import.txt @@ -0,0 +1,4 @@ +// Map 013-1: Woodlands Central Plateau +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/013-1/_mobs.txt", +"npc/013-1/_warps.txt", diff --git a/npc/013-1/_mobs.txt b/npc/013-1/_mobs.txt new file mode 100644 index 0000000..0727c3e --- /dev/null +++ b/npc/013-1/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 013-1: Woodlands Central Plateau mobs +013-1,69,83,31,14 monster Pinkie 1132,9,30000,50000 +013-1,76,82,49,21 monster Mauve Plant 1135,4,90000,90000 diff --git a/npc/013-1/_warps.txt b/npc/013-1/_warps.txt new file mode 100644 index 0000000..84ad4e2 --- /dev/null +++ b/npc/013-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 013-1: Woodlands Central Plateau warps +013-1,118,27,0 warp #013-1_118_27 0,0,015-3,172,195 diff --git a/npc/014-1/_import.txt b/npc/014-1/_import.txt new file mode 100644 index 0000000..83582d7 --- /dev/null +++ b/npc/014-1/_import.txt @@ -0,0 +1,5 @@ +// Map 014-1: Woodland Mining Camp +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/014-1/_mobs.txt", +"npc/014-1/_warps.txt", +"npc/014-1/galimatia.txt", diff --git a/npc/014-1/_mobs.txt b/npc/014-1/_mobs.txt new file mode 100644 index 0000000..f0d8a65 --- /dev/null +++ b/npc/014-1/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-1: Woodland Mining Camp mobs +014-1,97,59,86,45 monster Silk Worm 1034,15,30000,10000 +014-1,97,67,50,45 monster Mouboo 1023,12,30000,35000 +014-1,85,82,54,35 monster Squirrel 1032,11,30000,45000 +014-1,87,80,52,18 monster Tipiou 1016,3,30000,40000 +014-1,135,91,6,28 monster Blub 1008,2,30000,20000 +014-1,138,23,25,10 monster Bee Hive 1047,2,30000,20000 +014-1,140,25,26,15 monster Bee 1065,5,30000,20000 +014-1,95,58,105,59 monster Cobalt Plant 1136,2,45000,50000 +014-1,88,68,104,43 monster Mauve Plant 1135,3,45000,50000 +014-1,66,53,43,62 monster Gamboge Plant 1134,3,45000,50000 diff --git a/npc/014-1/_warps.txt b/npc/014-1/_warps.txt new file mode 100644 index 0000000..6fa9bd8 --- /dev/null +++ b/npc/014-1/_warps.txt @@ -0,0 +1,24 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-1: Woodland Mining Camp warps +014-1,79,98,0 warp #014-1_79_98 3,0,012-1,78,18 +014-1,79,57,0 warp #014-1_79_57 1,0,015-1,99,132 +014-1,158,34,0 script #014-1_158_34 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 153,47; end; +} +014-1,153,46,0 script #014-1_153_46 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 158,35; end; +} +014-1,147,40,0 script #014-1_147_40 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 112,62; end; +} +014-1,112,61,0 script #014-1_112_61 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 147,41; end; +} diff --git a/npc/014-1/galimatia.txt b/npc/014-1/galimatia.txt new file mode 100644 index 0000000..745a2ec --- /dev/null +++ b/npc/014-1/galimatia.txt @@ -0,0 +1,53 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// A bee keeper + +014-1,126,21,0 script Galimatia NPC_BEEKEEPER,{ + mesn; + mesq l("Feel free to collect honey from the hives. Remember to bottle them before picking up... You can use the discardable bottles I keep near them for that."); + if (!countitem(Honey)) close; + if (BaseLevel < 30) close; + next; + mesn; + mesq l("I can also pay %d GP per %s.", .price, getitemlink(Honey)); + if (countitem(BeeStinger)) mesc l("But if you bring me %d %s and %d %s and %s GP, I can give you a %s!", 20, getitemlink(BeeStinger), 10, getitemlink(Honey), fnum(3500)); + next; + do + { + select + rif(countitem(Honey), "Sell a honey for %d GP", .price), + rif(countitem(Honey) >= 10 && + countitem(BeeStinger) >= 20 && + Zeny >= 3500, "Deal, I want the hat!"), + l("Nice, thanks."); + mes ""; + switch (@menu) { + case 1: + delitem Honey, 1; + Zeny+=.price; + break; + case 2: + delitem Honey, 10; + delitem BeeStinger, 20; + Zeny-=3500; + getitem BeeKeeperHat, 1; + getexp 5000, 4500; + mesn; + mesq l("Thanks, here is your hat!"); + close; + break; + case 3: + close; + break; + } + } while (true); + close; + +OnInit: + .sex = G_FEMALE; + .distance = 4; + .price = getiteminfo(Honey, ITEMINFO_SELLPRICE) * 15 / 10; + end; +} diff --git a/npc/014-2-1/_import.txt b/npc/014-2-1/_import.txt new file mode 100644 index 0000000..a6f5cc1 --- /dev/null +++ b/npc/014-2-1/_import.txt @@ -0,0 +1,4 @@ +// Map 014-2-1: Woody House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/014-2-1/_warps.txt", +"npc/014-2-1/woody.txt", diff --git a/npc/014-2-1/_warps.txt b/npc/014-2-1/_warps.txt new file mode 100644 index 0000000..5dec41a --- /dev/null +++ b/npc/014-2-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-2-1: Woody House warps +014-2-1,33,45,0 warp #014-2-1_33_45 0,0,014-2,103,30 diff --git a/npc/014-2-1/woody.txt b/npc/014-2-1/woody.txt new file mode 100644 index 0000000..68b5637 --- /dev/null +++ b/npc/014-2-1/woody.txt @@ -0,0 +1,134 @@ +// TMW2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Woody, is the oldest player of TMW2, he deserves his own npc :d an old trapper who give help to new player to craft Squirrel boots +// id:100 HurnscaldQuest_Woody +// <2 - See his wife +// 3 - Access to House +// 4 - Quest Accepted +// 5 - Quest Complete + +014-2-1,37,43,4 script Woody NPC_PLAYER,{ + .@Woo = getq(HurnscaldQuest_Woody); + if (BaseLevel < 26) goto L_TooWeak; + if (.@Woo == 4) goto L_Check; + if (.@Woo == 5) goto L_Complete; + +L_GiveTask: + mesn; + mesq l("Hi, I'm Woody!"); + next; + mesq l("Do you need something in particular?"); + next; + + menu + l("To be honest, I don't know. Your wife just allowed me to pass the door!"), L_Quit, + l("As I know, you're kind of hunter crafter, Right?"), L_Quest, + l("Sorry, It's not place for me."), L_Quit; + + +L_Quest: + mes ""; + mesn; + mesq l("Ah yes... I make fine @@...", getitemlink(SquirrelBoots)); + next; + mesn; + mesq l("If you bring me some items, I can make some for you!"); + next; + + menu + l("Really? What do you need?"), L_Start, + l("Better do this some other time..."), L_Quit; + + +L_Start: + setq HurnscaldQuest_Woody, 4; + mes ""; + mesn; + mesq l("Ok, what I need is:"); + goto L_List; + +L_Quit: + mes ""; + mesn; + mesq l("Alright."); + close; + +L_List: + mes ""; + mesn; + mes l("Here's what I need:"); + mes l("@@/50 @@", countitem(SquirrelPelt), getitemlink(SquirrelPelt)); + mes l("@@/10 @@", countitem(CottonCloth), getitemlink(CottonCloth)); + mes l("@@/1 @@", countitem(Boots), getitemlink(Boots)); + close; + +L_Check: + mesn; + mesq l("Did you brought me everything I asked for?"); + mesq l("Remember, I'll make a @@ for you!", getitemlink(SquirrelBoots)); + next; + menu + l("Yes!"), L_Give, + l("I forgot what you need!"), L_List, + l("No!"), L_Quit; + +L_Give: + if ( + countitem(Boots) < 1 || + countitem(CottonCloth) < 10 || + countitem(SquirrelPelt) < 50 + ) goto L_Lying; + + inventoryplace SquirrelBoots, 1; + + delitem(Boots, 1); + delitem(CottonCloth, 10); + delitem(SquirrelPelt, 50); + + getitem(SquirrelBoots, 1); + getexp(3000, 20); + setq(HurnscaldQuest_Woody, 5); + + mes ""; + mesn; + mesq l("Here, all yours. Ah, if I still could walk... I would hunt those annoying squirrels myself!"); + close; + +L_Complete: + mesn; + mesq l("Wandering too much? Take care to don't get lost."); + close; + +L_Lying: + mesn; + mesq l("No no no, that's wrong."); + next; + mesn; + mesq l("You don't have everything I've asked for!"); + next; + goto L_List; + +L_TooWeak: + mesn; + mesq l("Hey kid, go play somewhere else, I don't have time! It's soon over!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, MinerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 4); + setunitdata(.@npcId, UDT_HAIRCOLOR, 3); + + npcsit; + .sex = G_MALE; + .distance = 4; + + end; +} + diff --git a/npc/014-2-2/_import.txt b/npc/014-2-2/_import.txt new file mode 100644 index 0000000..1526445 --- /dev/null +++ b/npc/014-2-2/_import.txt @@ -0,0 +1,5 @@ +// Map 014-2-2: Forsaken Inn +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/014-2-2/_warps.txt", +"npc/014-2-2/gemini.txt", +"npc/014-2-2/valia.txt", diff --git a/npc/014-2-2/_warps.txt b/npc/014-2-2/_warps.txt new file mode 100644 index 0000000..ff20d0a --- /dev/null +++ b/npc/014-2-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-2-2: Forsaken Inn warps +014-2-2,34,35,0 warp #014-2-2_34_35 0,0,014-2,183,77 diff --git a/npc/014-2-2/gemini.txt b/npc/014-2-2/gemini.txt new file mode 100644 index 0000000..b70afa7 --- /dev/null +++ b/npc/014-2-2/gemini.txt @@ -0,0 +1,139 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Central Core + +034-1,0,0,0 script #Gemini NPC_HIDDEN,{ + end; + +OnCoreInit: + debugmes "CORE INIT, Inst = %d", instance_id(); + // Oops! You do not have enough level! + if (BaseLevel < 30) { + warp "014-2-2", 36, 24; + end; + } + .@p = getcharid(1); + instance_attach($@VALIA_INST[.@p]); + @mystatus = 0; + sleep2(200); + if (!$@VALIA_STATUS[.@p]) + $@VALIA_STATUS[.@p] = 1; + // FALLTHROUGH +OnCore: + if (!compare(getmap(), "val")) end; + .@p = getcharid(1); + instance_attach($@VALIA_INST[.@p]); + // Always true on first interaction + // This handles new stages for person + if (@mystatus < $@VALIA_STATUS[.@p]) { + // No retroaction! + switch ($@VALIA_STATUS[.@p]) { + case 1: // Quest initiated + // Spawn monsters + areamonster("val1@"+.@p, 20, 20, 60, 60, strmobinfo(1, GreenSlime), GreenSlime, (BaseLevel/15), "Valia::OnGSlime"); + areamonster("val1@"+.@p, 20, 20, 70, 60, strmobinfo(1, LogHead), LogHead, (BaseLevel/15), "Valia::OnLHead"); + areamonster("val1@"+.@p, 20, 20, 70, 60, strmobinfo(1, ForestMushroom), ForestMushroom, (BaseLevel/12), "Valia::OnFShroom"); + areamonster("val1@"+.@p, 20, 20, 70, 60, strmobinfo(1, RedMushroom), RedMushroom, 1+(BaseLevel/30), "Valia::OnRShroom"); + break; + case 2: // Looking for a runestone + if (@mystatus) + getexp 4000, 100; + break; + case 3: // Runestone was found + if (@mystatus) + getexp 8000, 200; + break; + case 4: // Desert unlocked, but no clue on what to do + dispbottom l("You hear a noise from distance."); + // Spawn monsters + areamonster("val1@"+.@p, 100, 20, 170, 60, strmobinfo(1, CaveSnake), CaveSnake, 3, "Valia::OnCSnake"); + areamonster(getmap(), 100, 20, 170, 60, strmobinfo(1, MagicGoblin), MagicGoblin, 1, "Valia::OnMGoblin"); + // These refuse to respawn but are numerous + // Thus far, the quest is still easy. But soon it won't be. + areamonster(getmap(), 100, 20, 170, 60, strmobinfo(1, MountainSnake), MountainSnake, 1+(BaseLevel/24)); + areamonster(getmap(), 100, 20, 170, 60, strmobinfo(1, DarkLizard), DarkLizard, 1+(BaseLevel/20)); + areamonster(getmap(), 100, 20, 170, 60, strmobinfo(1, WickedMushroom), WickedMushroom, 1+(BaseLevel/32)); + areamonster(getmap(), 100, 20, 170, 60, strmobinfo(1, EliteDuck), EliteDuck, 1); + break; + case 5: // JackO shows up + if (@mystatus) + getexp 16000, 400; + break; + case 6: // Passage Open + if (countitem(SealedSoul)) + delitem SealedSoul, countitem(SealedSoul); + dispbottom l("You hear waterfall opening in distance."); + if (@mystatus) + getexp 32000, 800; + break; + case 7: // Ambushed & Captured + if (!is_gm()) + nude(); + @lockpicks=false; + // Prision Area + .@m=any(Bandit, RobinBandit, SleepingBandit); + areamonster("val2@"+.@p, 52, 93, 70, 98, strmobinfo(1, .@m), .@m, 1); + .@m=any(Bandit, RobinBandit, SleepingBandit); + areamonster("val2@"+.@p, 52, 93, 70, 98, strmobinfo(1, .@m), .@m, 1); + // Ante-chamber + areamonster("val2@"+.@p, 69, 96, 85, 107, strmobinfo(1, Bandit), Bandit, 1); + .@m=any(Bandit, RobinBandit, SleepingBandit, RobinBandit); + areamonster("val2@"+.@p, 69, 96, 85, 107, strmobinfo(1, .@m), .@m, 1); + // Reception & Lord Desk + areamonster("val2@"+.@p, 50, 30, 80, 75, strmobinfo(1, Bandit), Bandit, (BaseLevel/15)); + areamonster("val2@"+.@p, 50, 30, 80, 75, strmobinfo(1, RobinBandit), RobinBandit, (BaseLevel/25)); + // Whole Map + areamonster("val2@"+.@p, 20, 24, 78, 72, strmobinfo(1, Archant), Archant, (BaseLevel/8)); + // Great Hall + areamonster("val2@"+.@p, 25, 24, 50, 45, strmobinfo(1, RobinBandit), RobinBandit, (BaseLevel/22)); + areamonster("val2@"+.@p, 25, 24, 50, 45, strmobinfo(1, Assassin), Assassin, (BaseLevel/20)); + areamonster("val2@"+.@p, 25, 24, 50, 45, strmobinfo(1, Bandit), Bandit, (BaseLevel/18)); + areamonster("val2@"+.@p, 25, 24, 50, 45, strmobinfo(1, HoodedNinja), HoodedNinja, 1); + break; + case 8: // Bandit Key retrieved + if (@mystatus) + getexp 64000, 1600; + break; + case 9: // Door seal broken + if (@mystatus) + getexp 128000, 3200; + break; + case 10: // Luvia first cutscene + // Limit exports of Lazurite outside the area + if (countitem(LazuriteShard) > 2) + delitem LazuriteShard, countitem(LazuriteShard)/2; + // These two remain for internal quests + if (countitem(LazuriteCrystal)) + delitem LazuriteCrystal, countitem(LazuriteCrystal); + if (countitem(LazuriteHeart)) + delitem LazuriteHeart, countitem(LazuriteHeart); + break; + case 11: // Survived cutscene + if (@mystatus) + getexp 256000, 6400; + break; + case 12: // Storage cutscene + break; + case 13: // Survived storage + if (@mystatus) + getexp 384000, 9600; + break; + case 14: // Luvia Showdown cutscene + break; + case 15: // Defeated Luvia and now on pursuit! + if (@mystatus) + getexp 512000, 12800; + break; + case 16: // Cooldown in progress + break; + } + @mystatus = $@VALIA_STATUS[.@p]; + } + addtimer(3000, "#Gemini::OnCore"); + end; +} + +// Max experience gain: 1,404,000 XP and 35,100 JXP + diff --git a/npc/014-2-2/valia.txt b/npc/014-2-2/valia.txt new file mode 100644 index 0000000..f954ad2 --- /dev/null +++ b/npc/014-2-2/valia.txt @@ -0,0 +1,302 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Valia Gemini. The quest can be resumed within 4 hours. + +034-1 mapflag zone MMO +034-2 mapflag zone MMO +034-3 mapflag zone MMO +034-4 mapflag zone MMO + +// Check if you can do this action or not (hero) +function script GeminiCheck { + .@st = getarg(0, 0); + if (getq(General_Narrator) < 16 || @mystatus < .@st) { + warp "Save", 0, 0; + dispbottom l("You don't feel so well... And then, you're dead."); + die(); + end; + } + return; +} + +// You killed a mob +function script GeminiKill { + if (!compare(getmap(), "val")) return; + .@p=getcharid(1); + switch ($@VALIA_STATUS[.@p]) { + case 2: // Looking for a runestone + if (killedrid == GreenSlime) { + if (!rand2(40) || $@GM_OVERRIDE) { + getexp 8132, 657; + dispbottom l("You found the Runestone! You gave it to the party leader."); + $@VALIA_STATUS[.@p] = 3; + } + } + break; + case 4: // Desert unlocked, but no clue on what to do + case 5: // Desert unlocked, cutscene over + if (killedrid == CaveSnake) { + if (!rand2(4) && countitem(SealedSoul) < 10) { + getitem SealedSoul, 1; + areamonster(getmap(), 100, 20, 170, 60, strmobinfo(1, MagicGoblin), MagicGoblin, 1, "Valia::OnMGoblin"); + } + } + break; + case 6: // Just entered the Den + break; + case 7: // Just got stunned + break; + // Forest -> Desert -> Bandit Cave -> Island -> Fake showdown -> Storage -> Showdown -> Chest, Lore and Exit -> Wumpus' Trap -> End + } + return; +} + +// Valia Gemini +014-2-2,36,22,0 script Valia NPC_RED_WIZARD_F,{ + GeminiCheck(); + .@q = getq(HurnscaldQuest_Gemini); + .@t = getq3(HurnscaldQuest_Gemini); + mesn; + mesq l("Greetings, adventurer. I am Valia Gemini, owner of this abode."); + // If your level is too low (just reborn), it could break scripts + // So close dialog here. + if (BaseLevel < 30) close; + next; + if (getcharid(1) >= 1) { + if (($@VALIA_STATUS[getcharid(1)] >= 16) && + (gettimetick(2) < (.@t + 14400))) { + mesn; + mesq l("You are welcome here, but don't overstay your welcome and leave as soon as possible."); + mesc l("Please wait %s before trying again.", FuzzyTime(.@t + 14400)); + close; + } + } + mesn; + mesq l("I usually don't let anyone in, but I had to make an exception for the likes of you."); + next; + mesn; + mesq l("You see, I have a sister. Luvia Gemini. Very talented, but unfortunately, not enough to prevent being captured and brainwashed by Isbamuth."); + next; + mesn; + mesq l("I am sure you already fought him. And survived, too, apparently. Help her, and I will reward your efforts appropriately."); + next; + mesn; + mesq l("You can bring up to %d other friends with you, but they must be on the same party and map as you and you must be the party leader.", ($EVENT$ == "Gemini" ? 5 : 3)); + if (gettimetick(2) < (.@t + 14400)) + mesc l("The previous attempt will be resumed."), 1; + next; + select + l("I'm not interested, sorry."), + l("Why don't you go yourself?"), + l("Alright, I will help."); + mes ""; + switch (@menu) { + case 1: + close; + case 2: + mesn; + mesq l("Because Isbamuth is tracking me? It would be pointless if he noticed. If you can't even understand this, maybe I overestimated you, after all..."); + close; + } + // You accepted the quest + mesn; + // ...But you lack a party + if (getcharid(1) <= 0) { + mesq l("You don't have a party. You're useless to me."); + tutmes l("You can make a party of 1 if you want to do it alone. This, however, is NOT ADVISED."), "Protip", false; + close; + } + // ...But you are not the leader + if (strcharinfo(0) != getpartyleader(getcharid(1))) { + mesq l("Bring me %s or make your own party if you want to help me.", getpartyleader(getcharid(1))); + close; + } + // ...But the party is too large + getpartymember(getcharid(1)); + .@count = $@partymembercount; + // The amount varies based on the season + if (.@count > ($EVENT$ == "Gemini" ? 6 : 4)) { + mesq l("Your party is too big. Discretion is a key if we want to succeed."); + close; + } + // Alright. + mesq l("Good. I hope you have stocked everything. The time limit is 4 hours."); + + // Only first attempt is free - all others are charged + if ($EVENT$ != "Gemini" || + ($EVENT$ == "Gemini" && GEMINI_DAY == gettime(5))) + { + if (getq(HurnscaldQuest_Gemini) && !is_gm()) { + next; + mesn; + mesq l("Actually, I need help with reagents this time. Bring me a %s and a %s and I'll gladly warp you... again.", getitemlink(GemPowder), getitemlink(EverburnPowder)); + if (!countitem(GemPowder) || !countitem(EverburnPowder)) close; + next; + } + } + mesc l("Are you and your party ready?"), 1; + if (askyesno() != ASK_YES) + close; + + // Not eligible for free lunch + if ($EVENT$ != "Gemini" || + ($EVENT$ == "Gemini" && GEMINI_DAY == gettime(5))) + { + if (getq(HurnscaldQuest_Gemini) && !is_gm()) { + delitem GemPowder, 1; + delitem EverburnPowder, 1; + } + } else if ($EVENT$ == "Gemini") { + GEMINI_DAY = gettime(5); + } + + // Handle first attempt + clear; + mesn; + mesq l("Good. Here, take this key and hide it. I will quickly brief you:"); + mes ""; + mesc l(".:: GEMINI ASSASSINS QUEST ::."), 3; + mesc l("Recommended Level: %d+", 85); + mes ""; + mes l("- The goal is to rescue Luvia from Isabamuth. She has been ##Bbrainwashed##b."); + mes l("- Luvia is a powerful mage and Isbamuth is watching, so teleporting directly would be risky."); + mes l("- Instead, the party will be teleported ##Bto a forest.##b"); + mes l("- Find a path to the island where she is hiding herself!"); + if (!getq(HurnscaldQuest_Gemini)) + mesc l("- If you fail, you'll need to pay me ingredients to try again!"), 1; + mes ""; + mesc l("Good luck!"); + next; + closeclientdialog; + + compareandsetq HurnscaldQuest_Gemini, 0, 1; + setq3 HurnscaldQuest_Gemini, gettimetick(2); + + // Create instance + .@p=getcharid(1); + .@inst = instance_create("Gemini "+.@p, .@p, IOT_PARTY); + + // If it worked then setup + if (.@inst >= 0) { + // Attach maps + instance_attachmap("034-1", .@inst, false, "val1@"+.@p); + instance_attachmap("034-2", .@inst, false, "val2@"+.@p); + instance_attachmap("034-3", .@inst, false, "val3@"+.@p); + instance_attachmap("034-4", .@inst, false, "val4@"+.@p); + // Save the instance ID + $@VALIA_STATUS[.@p]=0; + $@VALIA_INST[.@p]=.@inst; + $@VALIA_MAP$[.@p]=""; + } + + // Instance lasts 4 hours - your time limit + instance_set_timeout(14400, 14400, $@VALIA_INST[.@p]); + + // Do we need to initialize or are we just restarting? + if (.@inst >= 0) + instance_init(.@inst); + + // Warp everyone and add timers + partytimer("014-2-2", 10, "#Gemini::OnCoreInit", getcharid(1)); + sleep2(100); + warpparty("val1@"+.@p, 24, 24, getcharid(1), "014-2-2", true); + close; + +// Macros +OnGSlime: + fix_mobkill(GreenSlime); + .@p = getcharid(1); + areamonster("val1@"+.@p, 20, 20, 60, 60, strmobinfo(1, GreenSlime), GreenSlime, 1, "Valia::OnGSlime"); + end; + +OnLHead: + fix_mobkill(LogHead); + .@p = getcharid(1); + areamonster("val1@"+.@p, 20, 20, 70, 60, strmobinfo(1, LogHead), LogHead, 1, "Valia::OnLHead"); + end; + +OnFShroom: + fix_mobkill(ForestMushroom); + .@p = getcharid(1); + areamonster("val1@"+.@p, 20, 20, 70, 60, strmobinfo(1, ForestMushroom), ForestMushroom, 1, "Valia::OnFShroom"); + end; + +OnRShroom: + fix_mobkill(RedMushroom); + .@p = getcharid(1); + areamonster("val1@"+.@p, 20, 20, 70, 60, strmobinfo(1, RedMushroom), RedMushroom, 1, "Valia::OnRShroom"); + end; + +/////////////////////////////////// +OnCSnake: + fix_mobkill(CaveSnake); + .@p = getcharid(1); + areamonster("val1@"+.@p, 100, 20, 170, 60, strmobinfo(1, CaveSnake), CaveSnake, 1, "Valia::OnCSnake"); + end; + +OnMGoblin: + fix_mobkill(MagicGoblin); + .@p = getcharid(1); + if (mobcount("val1@"+.@p, "Valia::OnMGoblin") > getmapusers("val1@"+.@p) * 50) + end; + // Spawn a high precision one + .@mg=areamonster("val1@"+.@p, 100, 20, 170, 60, strmobinfo(1, MagicGoblin), MagicGoblin, 1, "Valia::OnMGoblin"); + setunitdata(.@mg, UDT_HIT, 42 + mobcount("val1@"+.@p, "Valia::OnMGoblin") * 2); + // And randomly spawn another (33% chance) + if (!rand2(3)) + areamonster("val1@"+.@p, 100, 20, 170, 60, strmobinfo(1, MagicGoblin), MagicGoblin, 1, "Valia::OnMGoblin"); + end; + +/////////////////////////////////// +OnKey1: + .@p = getcharid(1); + @tmp+=1; + sleep2(rand2(1000, 3000) + (@tmp*300)); // Prevent outright farming + .@mob=monster("val2@"+.@p, 57, 104, strmobinfo(1, CopperSlime), CopperSlime, 1, "Valia::OnKey1"); + // This should wipe the monster experience value + setunitdata(.@mob, UDT_LEVEL, 1); + if (countitem(Lockpicks) || @lockpicks || rand2(3)) end; + getitem Lockpicks, 1; + @lockpicks=true; + end; + +OnKey2: + .@p = getcharid(1); + @tmp+=1; + sleep2(rand2(1000, 3000) + (@tmp*300)); // Prevent outright farming + .@mob=monster("val2@"+.@p, 61, 104, strmobinfo(1, CopperSlime), CopperSlime, 1, "Valia::OnKey2"); + // This should wipe the monster experience value + setunitdata(.@mob, UDT_LEVEL, 1); + if (countitem(Lockpicks) || @lockpicks || rand2(3)) end; + getitem Lockpicks, 1; + @lockpicks=true; + end; + +OnKey3: + .@p = getcharid(1); + @tmp+=1; + sleep2(rand2(1000, 3000) + (@tmp*300)); // Prevent outright farming + .@mob=monster("val2@"+.@p, 66, 104, strmobinfo(1, CopperSlime), CopperSlime, 1, "Valia::OnKey3"); + // This should wipe the monster experience value + setunitdata(.@mob, UDT_LEVEL, 1); + if (countitem(Lockpicks) || @lockpicks || rand2(3)) end; + getitem Lockpicks, 1; + @lockpicks=true; + end; + +OnLord: + .@p = getcharid(1); + if (!mobcount("val2@"+.@p, "Valia::OnLord")) { + $@VALIA_STATUS[.@p] = 8; + Zeny+=15000; + dispbottom l("This lord dropped a key and a stash of gold!"); + } + end; + +OnInit: + .distance=4; + end; +} + diff --git a/npc/014-2/_import.txt b/npc/014-2/_import.txt new file mode 100644 index 0000000..1ee7fb0 --- /dev/null +++ b/npc/014-2/_import.txt @@ -0,0 +1,8 @@ +// Map 014-2: Woodlands Southeast +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/014-2/_mobs.txt", +"npc/014-2/_warps.txt", +"npc/014-2/carla.txt", +"npc/014-2/guards.txt", +"npc/014-2/mouboo.txt", +"npc/014-2/trap.txt", diff --git a/npc/014-2/_mobs.txt b/npc/014-2/_mobs.txt new file mode 100644 index 0000000..1f638e0 --- /dev/null +++ b/npc/014-2/_mobs.txt @@ -0,0 +1,13 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-2: Woodlands Southeast mobs +014-2,150,63,86,43 monster Log Head 1066,25,30000,20000 +014-2,66,45,58,26 monster Mouboo 1023,6,30000,20000 +014-2,108,60,52,58 monster Forest Mushroom 1060,12,40000,25000 +014-2,29,74,19,27 monster Fluffy 1022,5,30000,20000 +014-2,207,65,40,50 monster Sea Slime 1093,6,42000,20000 +014-2,125,84,157,54 monster Alpha Mouboo 1056,2,120000,120000 +014-2,132,59,90,35 monster Squirrel 1032,28,30000,42000 +014-2,143,74,137,117 monster Cobalt Plant 1136,3,45000,50000 +014-2,148,70,137,117 monster Mauve Plant 1135,3,45000,50000 +014-2,154,66,137,117 monster Gamboge Plant 1134,3,45000,50000 +014-2,125,23,125,37 monster Clover Field 1028,6,45000,50000 diff --git a/npc/014-2/_warps.txt b/npc/014-2/_warps.txt new file mode 100644 index 0000000..c159673 --- /dev/null +++ b/npc/014-2/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-2: Woodlands Southeast warps +014-2,179,21,0 warp #014-2_179_21 3,0,012-1,78,101 +014-2,232,20,0 warp #014-2_232_20 2,0,012-1,132,100 +014-2,183,76,0 warp #014-2_183_76 0,0,014-2-2,34,34 +014-2,49,24,0 warp #014-2_49_24 1,0,014-3,130,134 diff --git a/npc/014-2/carla.txt b/npc/014-2/carla.txt new file mode 100644 index 0000000..ae1a738 --- /dev/null +++ b/npc/014-2/carla.txt @@ -0,0 +1,197 @@ +// TMW2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Carla the Woody wife in old shaman girl. She kept house entrance from years. +// id:100 HurnscaldQuest_Woody +// 0 - Not aware of the quest +// 1 - Know about winter +// 2 - Quest Accepted +// 3 - Quest Complete +// 4+ - see woody + +014-2,104,31,0 script Carla NPC_RAIJIN_FEMALE_WAITRESS,{ + .@quest = getq(HurnscaldQuest_Woody); + if (BaseLevel < 25) goto L_TooWeak; + if (.@quest == 1) goto L_Start; + if (.@quest == 2) goto L_Check; + if (.@quest >= 3) goto L_Complete; + +L_GiveTask: + mesn; + mesq lg("Hello, new wanderer!"); + next; + mesq l("Be careful to not get lost in the forest."); + next; + + menu + l("Hey, do not mistake me for a child."), L_Quit, + l("Hello, I'm looking for bandit hoods!"), L_Hood, + l("Hey! I want to enter in this house!"), L_Force, + l("Hello, who are you?"), L_Quest, + l("I don't have time sorry."), L_Quit; + + +L_Quest: + mes ""; + mesn; + mesq l("I'm Carla the Woody Wife, I have lived here with Woody for many years! We decided to live away from civilization, as hunter and shaman life is way better."); + next; + mesn; + mesq l("But now, we are quite old. I remember the time when I was your age. My generation doesn't do stupid things like yours!"); + next; + + menu + l("Isn't hard to live alone?"), L_Start, + l("Where is Woody?"), L_Woody, + l("Ok grandma CYA!"), L_Quit; + + +L_Start: + setq HurnscaldQuest_Woody, 1; + mes ""; + mesn; + mesq l("No, we've done this for our whole life, and we have survived!"); + next; + mesn; + mesq l("My shaman skills protected us against monsters and thieves!"); + next; + + menu + l("Hey if you want I can do some physical work for you!"), L_Ask, + l("Can you teach me a basic shaman skill? please?"), L_Skill, + l("Oh no! I will miss my bus! Gotta go!"), L_Quit; + +L_Ask: + setq HurnscaldQuest_Woody, 2; + mes ""; + mesn; + mesq l("Oh yeah some work is really painful, especially for my back, and I cant bother Woody to do it, he has so many difficulties moving."); + next; + mesn; + if (season() != WINTER) + mesq l("I need you to bring me some basic items to face Winter! Fire is the most important for us."); + else + mesq l("Winter this year is really harsh! Fire is the most important for us."); + next; + goto L_List; + +L_Quit: + mes ""; + mesn; + mesq l("Alright."); + close; + +L_List: + mes ""; + mesn; + mes l("Would be nice if you could bring me these:"); + mes l("@@/40 @@", countitem(RawLog), getitemlink(RawLog)); + mes l("@@/5 @@", countitem(Coal), getitemlink(Coal)); + close; + +L_Check: + mesn; + mesq l("Hey, did you bring me everything I asked for?"); + next; + menu + l("Yes! It is heavy."), L_Give, + l("humm... Sorry, I forgot what you need!"), L_List, + l("I don't have them, actually."), L_Quit; + +L_Give: + if ( + countitem(RawLog) < 40 || + countitem(Coal) < 5 + ) goto L_Nope; + + inventoryplace RedApple, 3; + + delitem(RawLog, 40); + delitem(Coal, 5); + + getitem(RedApple, 3); + getexp(3000, 50); + setq(HurnscaldQuest_Woody, 3); + + mes ""; + mesn; + mesq l("Thank you, We are now friends, You can visit us when you want!"); + close; + +L_Complete: + mesn; + mesq l("Wandering too much? Take care to not get lost."); + close; + +L_Woody: + mes ""; + mesn; + mesq l("Woody is inside, he is probably crafting some stuff with animals pelts."); + close; + +L_Hood: + mes ""; + mesn; + mesq l("You search for Bandit hoods."); + next; + mesn; + mesq l("You could find some bandits in the Bandit cave. Yeah, the name wasn't given for nothing."); + next; + mesn; + mesq l("To get there you need to go left, follow rock wall and go up when you can. The entrance to the Bandit cave is on left side of the waterfall."); + close; + +L_Force: + mes ""; + mesn; + mesq l("Strangers and monsters aren't allowed in MY house!"); + next; + mesn; + mesq l("Anyway, my herb mixture keeps monsters away."); + close; + +L_Skill: + mes ""; + mesn; + //mesq l("I don't know if jesusalva allow me to teach you a basic shaman skill!? ."); + // I don't, but a monster repellent would be nice. Does such skill exist/work? + mesq l("Well, I know a mixture to keep monsters away. I just can't share it with you now."); + next; + mesn; + mesq l("You see, it need certain special items... Oh, just blame Jesusalva for this one."); + close; + +L_Nope: + mesn; + mesc l("humm... You don't have everything I've asked for!"); + next; + mesn; + mesq l("Can you come back with all the items I asked for? Please."); + next; + goto L_List; + +L_TooWeak: + mesn; + mesq l("You should not be there! This forest is home to monsters."); + close; + +OnInit: + .sex = G_FEMALE; + .distance = 4; + end; +} + +014-2,103,29,0 script Woody Barrier NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@q=getq(HurnscaldQuest_Woody); + if (.@q >= 3) + warp "014-2-1", 33, 44; + else + npctalk3 l("The door is locked, you should speak to Carla about it."); + end; +} + diff --git a/npc/014-2/guards.txt b/npc/014-2/guards.txt new file mode 100644 index 0000000..ef41931 --- /dev/null +++ b/npc/014-2/guards.txt @@ -0,0 +1,47 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Access to Eternal Swamps + +014-2,58,103,0 script Guard Jennifer NPC_GUARD2,{ +OnMain: + mesn; + mesq l("Halt! Beyond this gate, is the Great River and the Eternal Swamps."); + next; + mesn; + mesq l("Was not it only flooded constantly, the graveyard is not too far. If you stray away from the path, you'll get lost."); + next; + .@fd=!(getmapmask("011-3")&1024); // .@fd - is flooded? + if (!.@fd) { + mesn; + mesq l("Various people already went missing, including GMs. This is why if you plan to cross, ")+l(b("You won't be allowed to walk sideways, except to avoid a monster or two.")); + mesc l("If you try to walk west or east too much, you'll hit an \"invisible wall\" to prevent you from getting lost."); + next; + select + l("I'm fine, thanks."), + l("My equipment is good, let me through!"); + mes ""; + if (@menu == 2) { + warp "011-3", 38, 21; + closedialog; + close; + } + } else { + mesn; + mesq l("In fact, it is flooded at the moment. Come back later."); + } + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} +014-2,56,107,0 script #ToEternalSwamps NPC_HIDDEN,2,0,{ + end; +OnTouch: + doevent "Guard Jennifer::OnMain"; + end; +} + diff --git a/npc/014-2/mouboo.txt b/npc/014-2/mouboo.txt new file mode 100644 index 0000000..62b4577 --- /dev/null +++ b/npc/014-2/mouboo.txt @@ -0,0 +1,235 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Part from a bigger, very important quest for learning about the MOUBOOTAUR. +// Interacts with Sagratha (for uncursing). +// +// Quest Structure: +// HurnscaldQuest_InjuriedMouboo - STATUS - HPBAR +// Quest Status: +// 0 - First Met +// 1 - Examined (HP Bar stored on field 2) +// 2 - Healed (Curse not solved - ready for Sagratha Quest) +// 3 - Curse removed (Sagratha's Arc) +// 9 - KILLED YOUR MOUBOO. YOU EVIL MONSTER. + +014-2,144,80,0 script Mouboo#0142injuried NPC_INJURIED_MOUBOO,{ + .@q=getq(HurnscaldQuest_InjuriedMouboo); + + if (.@q < 2) goto L_Begin; + if (.@q == 2) goto L_Healed; + if (.@q == 3) goto L_Uncursed; + if (.@q == 9) goto L_Killed; + goodbye; + end; + +// Main Quest +L_Begin: + .@q=getq(HurnscaldQuest_InjuriedMouboo); + .@q2=getq2(HurnscaldQuest_InjuriedMouboo); + mesn l("Injured Mouboo"); + mesq l("Booo...."); + mesc l("The Mouboo seems to be lying in pain."); + next; + select + rif(.@q == 1, l("Attempt to heal the Mouboo")), + rif(.@q == 1, l("Attempt to kill the Mouboo")), + l("Examine the Mouboo"), + l("Leave the Mouboo alone"); + mes ""; + switch (@menu) { + case 3: + mesc l("The Mouboo is lying down on the grass."); + mesc l("There doesn't seem to be any physical wound."); + mesc l("A closer inspection suggests this mouboo has been... CURSED."); + if (.@q == 0) + setq HurnscaldQuest_InjuriedMouboo, 1, 100; + next; + goto L_Begin; + case 1: + mes "##B" + l("Drag and drop a healing item from your inventory.") + "##b"; + + .@id = requestitem(); + + // If ID is invalid, there's not enough items, it is bound = Cannot bury + if (.@id < 1) close; + if (.@id < 1 || countitem(.@id) < 1 || checkbound(.@id) || getiteminfo(.@id, ITEMINFO_TYPE) != IT_HEALING) { + if (checkbound(.@id)) + mesc l("You cannot part with this item!"); + else if (.@id == MoubooFigurine) + mesc l("Haha, this wooden figurine won't do the job, sorry."); + else if (.@id == PlushMouboo) + mesc l("This seems like a good idea at first, but actually, it isn't."); + else if (.@id == MoubooPendant) + mesc l("Wise choice! But can you really part with it? Better think on something else!"); + else if (.@id == MoubooHat || .@id == AlphaMoubooHat) + mesc l("...I don't think giving it this hat will really help..."); + else if (.@id == DeathPotion || .@id == NymphPoison) + mesc l("...Are you crazy?"); + else if (getiteminfo(.@id, ITEMINFO_TYPE) != IT_HEALING) + mesc l("This is not a healing item."); + else + mesc l("You give up."); + close; + } + + // Bad healing items + if (.@id == MoubooSteak) { + mesc l("Uhm, this seems like a bad idea."); + close; + } else if (.@id == DeathPenalty) { + mesc l("Uhm, that's an... @@. Do you... want to grow a tree on the mouboo or something? Can't you think on something else?", getitemlink(DeathPenalty)); + close; + } else if (.@id == BottleOfSewerWater) { + mesc l("Uhm... I thought you were trying to heal the mouboo. Why are you giving it poisonous stuff?!"); + close; + } else if (.@id == Coffee) { + mesc l("Uhm, I better heal it first before trying to do anything about the curse."); + close; + } + + mesc l("Really give your @@ to the Mouboo?", getitemlink(.@id)), 1; + mesc l("The item will be lost forever."); + next; + if (askyesno() == ASK_YES) { + delitem .@id, 1; + itemeffect(.@id); // As a bonus, you'll be healed too, because I need to retrieve @max value + + // You need about 9.000 HP-worth healing items, and you can't use Mouboo Steak + @val1*=rand2(3,5); // Average delay (FIXME: Is @val1 available?) + @val1=max(10, @val1); // Hack + if (.@id == ElixirOfLife) + setq2 HurnscaldQuest_InjuriedMouboo, 9999; + else + setq2 HurnscaldQuest_InjuriedMouboo, .@q2+((@val1)/10); + + // Get rid of temporary variables, if it haven't happened yet + @min = 0; + @max = 0; + @val1 = 0; + + .@q2=getq2(HurnscaldQuest_InjuriedMouboo); + if (.@q2 > 1000) { + mesc l("This will be remembered."), 6; + setq HurnscaldQuest_InjuriedMouboo, 2; + getexp 800, 0; + close; + } + + // Progress Report + if (.@q2 > 900) { + mesc l("The mouboo seems to want to thank you, but is still too weak. Let's finish this healing."); + } + else if (.@q2 > 700) { + mesc l("The mouboo looks a bit more healthy. A little more should do."); + } + else if (.@q2 > 400) { + mesc l("The mouboo looks a bit less sickly. But this is not enough."); + } + else if (.@q2 > 100) { + mesc l("The mouboo still looks terrible, but you can see a small difference."); + } + else { + // Mouboo begins at 100 HP, so this should never happen + mesc l("Barely any difference, maybe I should use better healing items on it."); + } + } + break; + case 2: + .@id = getequipid(EQI_HAND_R); + + // If ID is invalid, there's not enough items, it is bound = Cannot bury + if (.@id < 1) { + mesc l("You are not using a weapon for a merciful last blow."); + close; + } + + mesc l("Really attack the Mouboo with a(n) @@?", getitemlink(.@id)), 1; + mesc l("There will be consequences."); + next; + if (askyesno() == ASK_YES) { + .@atk=getiteminfo(.@id, ITEMINFO_ATK); + mesn l("Injured Mouboo"); + if (.@q2 < 20) + mesq l("Re-ref... Reflect."); + else + mesq l("Ma... Magical Re... Re-ref... Reflection."); + setq2 HurnscaldQuest_InjuriedMouboo, .@q2-(.@atk/5); + .@q2=getq2(HurnscaldQuest_InjuriedMouboo); + + if (.@q2 <= 0) { + mes l("The... Mou... Boo... Taur..."); + mes l("Is... Dan... Ger... Ous..."); + mes l("...Help... Me..."); + setq HurnscaldQuest_InjuriedMouboo, 9; + getexp 800, 0; + next; + + mesc l("This will be remembered."), 6; + heal -.@atk, -(.@atk/10); + close; + } else { + mesc l("This wasn't enough."); + } + + heal -.@atk, -(.@atk/10); + next; + } + break; + default: + close; + } + mes ""; + goto L_Begin; + + +// Sagratha Arc +L_Healed: + .@q=getq(HurnscaldQuest_InjuriedMouboo); + .@q2=getq2(HurnscaldQuest_InjuriedMouboo); + mesn l("Injuried Mouboo"); + mesq l("Zzzzzz...."); + mesc l("The mouboo is sleeping soundly. The curse is still there."); + if (countitem(Coffee)) { + next; + mesc l("Give a @@ to the Mouboo?", getitemlink(Coffee)); + next; + if (askyesno() == ASK_YES) { + mesn l("Injured Mouboo"); + mesc l("*screams*"); + mes ""; + mesc l("The Mouboo doesn't want to drink the Coffee. Try to force it to drink anyway?"); + next; + if (askyesno() == ASK_YES) { + delitem Coffee, 1; + mesc l("There's no change in the condition of the poor Mouboo."); + next; + mesc l("Maybe the Hurnscald alchemist, Wyara, could explain why."); + } else { + mesc l("Coffee is good, why does the Mouboo refuse to drink it?"); + mesc l("Maybe someone in Hurnscald can help me."); + } + } + } + close; + +// Final Results +L_Uncursed: + mesn; + mesq l("Hello, @@. Thanks for helping me.", strcharinfo(0)); + next; + mesn; + mesq l("Don't challenge the Moubootaur. You saw what happened to me."); + close; + +L_Killed: + mesc l("Did this Mouboo just blink? No, I made sure it was truly dead. Must have been my imagination."); + close; + +OnInit: + .sex=G_OTHER; + .distance=2; + end; +} diff --git a/npc/014-2/trap.txt b/npc/014-2/trap.txt new file mode 100644 index 0000000..924943d --- /dev/null +++ b/npc/014-2/trap.txt @@ -0,0 +1,52 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Traps + +014-2,230,58,0 script #GeminiTrap1 NPC_HIDDEN,0,0,{ +OnTouch: + if (getq(General_Narrator) < 16) { + mesc l("WARNING:"), 1; + mes l("You are not welcome on this island."); + mes ""; + mes l("No mercy will be shown, turn back now!"); + mes ""; + mes l("-- Valia G."); + close; + } + end; +} + +014-2,230,60,0 script #GeminiTrap2 NPC_HIDDEN,0,0,{ +OnTouch: + if (getq(General_Narrator) < 16) { + slide 230, 56; + dispbottom l("You are pushed back with extreme force!"); + percentheal -15, 0; + } + end; +} + +014-2,228,64,0 script #GeminiTrap3 NPC_HIDDEN,0,0,{ +OnTouch: + if (getq(General_Narrator) < 16) { + slide 230, 56; + dispbottom l("You are pushed back with absolute force!"); + percentheal -85, 0; + } + end; +} + +// Should never trigger +014-2,221,69,0 script #GeminiTrap4 NPC_HIDDEN,1,1,{ +OnTouch: + if (getq(General_Narrator) < 16) { + warp "Save", 0, 0; + dispbottom l("You don't feel so well... And then, you're dead."); + die(); + end; + } + end; +} + diff --git a/npc/014-3/_import.txt b/npc/014-3/_import.txt new file mode 100644 index 0000000..a7f1841 --- /dev/null +++ b/npc/014-3/_import.txt @@ -0,0 +1,6 @@ +// Map 014-3: Woodlands Central Area +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/014-3/_mobs.txt", +"npc/014-3/_warps.txt", +"npc/014-3/fountain.txt", +"npc/014-3/oscar.txt", diff --git a/npc/014-3/_mobs.txt b/npc/014-3/_mobs.txt new file mode 100644 index 0000000..70d2831 --- /dev/null +++ b/npc/014-3/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-3: Woodlands Central Area mobs +014-3,115,75,91,52 monster Mouboo 1023,22,30000,35000 +014-3,63,74,41,48 monster Forest Mushroom 1060,10,60000,35000 +014-3,104,102,90,35 monster Squirrel 1032,26,30000,32000 +014-3,44,82,37,32 monster Centaur 1139,3,80000,60000 +014-3,164,75,31,14 monster Pinkie 1132,9,30000,20000,Oscar::OnKillPinkie +014-3,95,65,49,25 monster Poison Spiky Mushroom 1043,11,45000,25000 +014-3,133,89,49,25 monster Poison Spiky Mushroom 1043,7,45000,45000 +014-3,128,112,70,21 monster Mauve Plant 1135,5,90000,90000 +014-3,44,82,37,32 monster Chagashroom Field 1128,4,90000,90000 +014-3,95,65,49,25 monster Plushroom Field 1011,4,90000,90000 diff --git a/npc/014-3/_warps.txt b/npc/014-3/_warps.txt new file mode 100644 index 0000000..7d0cb6f --- /dev/null +++ b/npc/014-3/_warps.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-3: Woodlands Central Area warps +014-3,120,121,0 warp #014-3_120_121 0,0,015-3,172,195 +014-3,131,135,0 warp #014-3_131_135 1,0,014-2,48,25 +014-3,206,92,0 warp #014-3_206_92 0,4,012-1,23,61 +014-3,155,40,0 warp #014-3_155_40 1,0,014-5,81,228 +014-3,120,67,0 warp #014-3_120_67 0,0,015-2,49,100 +014-3,55,42,0 warp #014-3_55_42 0,0,014-3,77,40 +014-3,77,39,0 warp #014-3_77_39 0,0,014-3,55,43 +014-3,29,70,0 warp #014-3_29_70 0,3,014-4,158,68 diff --git a/npc/014-3/fountain.txt b/npc/014-3/fountain.txt new file mode 100644 index 0000000..d70d6d4 --- /dev/null +++ b/npc/014-3/fountain.txt @@ -0,0 +1,134 @@ +// TMW-2 Script +// Author: +// Saulc +// Jesusalva +// Description: +// Hurnscald doesn't have a well, but have a MAGIC FOUNTAIN *** +// Req. Int 40 to finish Tier Elevation quest. + +014-3,134,100,0 script Fountain#Hurns NPC_NO_SPRITE,{ + if (ST_TIER == 5 && gettimetick(2) < QUEST_ELEVARTEMPO) goto L_Tier2; + + mesn l("Mana Saulc"); + mesc l("Go away, I am too magical for you. %%n"); // quote + close; + + // TODO FIXME: We don't have a "Magic" water, and we might add + // Mahed or Mahad, another well master (Mehoud? Muhoud?) + input .@count; + + if (.@count == 0) + close; + .@Cost = .@count * .COST_PER_BOTTLE; + .@empty = countitem("EmptyBottle"); + + if (.@empty < .@count) + goto L_NotEnoughBottles; + if (Zeny < .@Cost) + goto L_NotEnoughMoney; + + inventoryplace BottleOfTonoriWater, .@count; + + Zeny=Zeny-.@Cost; + delitem "EmptyBottle", .@count; + getitem "BottleOfTonoriWater", .@count; + close; + +L_NotEnoughBottles: + mes ""; + mesn; + mes l("You don't have that many empty bottles!"); + close; + +L_NotEnoughMoney: + mes ""; + mesn; + mes l("You don't have enough gold! You need @@ gp.", .@Cost); + close; + + +L_Tier2: + if (readparam2(bInt) < 10) goto L_Dumb; + mesn; + mesc l("WHAT ARE YOU GOING TO DO?"); + next; + select + l("Do nothing"), // 1 + l("Drink the potion"), // 2 + l("Pour the potion"), // 3 + l("Wash yourself with the potion"), // 4 + l("Burn the potion"), // 5 + l("Drink the potion, and say magic words"), // 6 + l("Say magic words"), // 7 + l("Do a weird dance"), // 8 + l("Blame Saulc"); // 9 + + mes ""; + if (@menu == 1) + close; + + if (@menu == 3) + goto L_Tier2Ok; + + if (@menu <= 6) + goto L_Failed; + + if (@menu == 9) + mesc l("Whatever you're blaming Saulc about, this one time, he is innocent."); + mesc l("Nothing happens."); + close; + +L_Fail2: + mesc l("Your low intelligence prevents anything from happening with you."); + mes ""; + +L_Failed: + mesn strcharinfo(0); + mesq l("Ah no... That's not what I had to do... I wasted the potion..."); + QUEST_ELEVARTEMPO=gettimetick(2); + close; + +L_Dumb: + mesn strcharinfo(0); + mesq l("Hello there pretty fountain, what about granting me magic?"); + next; + mesc l("Unsurprisingly, nothing happens."); + close; + +L_Tier2Ok: + mesc l("You pour the whole potion on the fountain."); + next; + if (readparam2(bInt) < 20) goto L_Fail2; + mesc l("You hear birds singing! That is what you had to do!"); + next; + if (readparam2(bInt) < 30) goto L_Fail2; + mesc l("Your body starts to glow. You're not sure why, the fountain did that!"); + next; + if (readparam2(bInt) < 40) goto L_Fail2; + mesn; + mes l("I am the Magic Fountain of Hurnscald. You look qualified."); + next; + mesn; + mes l("Your next step is to get the book of the Second Sage Of Fate."); + next; + mesn; + mes l("I predict you're closer to find the Secret Of Mana, and therefore, be part on saving our world."); + next; + mesn; + mes l("I will empower you with raw mana now."); + next; + ST_TIER=6; + // Ref. 240 xp. You will be penalized with 1 xp for over-level. (waw...) + if (BaseLevel < 300) + getexp 300-BaseLevel, 0; + mesn; + mesq l("Run like the wind, @@! For you shall have only @@ to finish the ritual!", strcharinfo(0), FuzzyTime(QUEST_ELEVARTEMPO,2,2)); + close; + +OnInit: + .COST_PER_BOTTLE = 0; + .sex = G_OTHER; + .distance = 3; + + end; +} diff --git a/npc/014-3/oscar.txt b/npc/014-3/oscar.txt new file mode 100644 index 0000000..336af69 --- /dev/null +++ b/npc/014-3/oscar.txt @@ -0,0 +1,155 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Hurnscald farmer + +014-3,169,66,0 script Oscar NPC_RAIJIN,{ + .@q1=getq(HurnscaldQuest_Farmers); + .@q2=getq2(HurnscaldQuest_Farmers); + mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Ah, if it isn't @@? You're the talk of the town!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + if (.@q1 == 2) goto L_Hello; + if (.@q1 == 3) goto L_Check; + if (.@q1 == 4) goto L_Letter; + if (.@q1 == 5) goto L_Thanks; + + mesq l("Don't bother me, I'm busy right now. Since the Monster King left, I barely got a night of rest..."); + close; + +L_Hello: + mesq l("Ah! @@! I heard you helped Hinnak with his Pinkies problem!", strcharinfo(0)); + next; + mesn; + mesq l("Wouldn't you like to help me too?"); + if (askyesno() != ASK_YES) { + mesq l("Sorry, I have to go."); + close; + } + next; + mesn; + mesq l("Great! Excellent! Because I am also diving in problems!!"); + next; + if (readparam2(bAgi) < 30) goto L_Slow; + if (BaseLevel < 20) goto L_Weak; + mesn; + // Est. kill 400 Pinkies + mesq l("I hate so much the Pinkies on my fields, I want to do a soup! Can you provide me 10 @@? Many thanks.", getitemlink(PinkieLeg)); + setq HurnscaldQuest_Farmers, 3, 0; + close; + +L_Slow: + mesn; + mesq l("But you're too slow. Sorry."); + next; + mesc l("Raise agility to do this quest."); + close; + +L_Weak: + mesn; + mesq l("But you're too weak. Sorry."); + close; + +L_Check: + mesq l("Hey, how is the monster extermination going?"); + next; + mesn; + // You in average need to kill 400 Pinkies. But ya killed 200~250 previously. You get this bonus for not leaving the fields. + if (.@q2 >= 250) + mesq l("You killed so many Pinkies! My wife and I love you!"); + else if (.@q2 >= 200) + mesq l("You are doing GREAT! Keep slaying them, hahaah!"); + else if (.@q2 >= 150) + mesq l("I see you are doing good. Keep slaying them, hahaah!"); + else if (.@q2 >= 100) + mesq l("Good job, you already killed over 100! Hahah, that sure teach them a lesson!"); + else if (.@q2 >= 50) + mesq l("Yeah, teach them a lesson! Keep going!"); + else + mesq l("Go kill them!!"); + next; + menu + rif (.@q2 >= 250 && countitem(PinkieLeg) >= 10, l("Today is soup of Pinkie Legs!")), L_RealHelp, + rif (.@q2 >= 250 && countitem(PinkieLeg) < 10, l("Thanks, they hate to drop their Legs.")), L_TrueHelp, + rif (.@q2 >= 100 && .@q2 < 250 && countitem(PinkieLeg) >= 10, l("Here are the Pinkie Legs.")), L_Help, + rif (.@q2 < 100 && countitem(PinkieLeg) >= 10, l("Here are the Pinkie Legs.")), L_NoKill, + l("I'm not done yet. I'll be back."), -; + close; + +L_RealHelp: + delitem PinkieLeg, 10; + getexp 2000, 60; + Zeny=Zeny+5000; + setq HurnscaldQuest_Farmers, 4, 0; + mes ""; + mesn; + mesq l("Waw! You really did it, you're the savior of my farm!! You have my eternal gratitute. Here is 5000 GP for your troubles!"); + close; + +L_TrueHelp: + mes ""; + mesn; + mesq l("I know how hard it is! I hate them with all my heart, only seeing you killing them left and right was GREAT!"); + next; + getexp 1600, 0; + Zeny=Zeny+2500; + setq HurnscaldQuest_Farmers, 4, 0; + mesn; + mesq l("You have my eternal gratitute. Here is 2500 GP for your troubles."); + close; + +L_Help: + delitem PinkieLeg, 10; + getexp 1400, 0; + Zeny=Zeny+2000; + setq HurnscaldQuest_Farmers, 4, 0; + mes ""; + mesn; + mesq l("Many thanks. You have my eternal gratitute. Here is 2000 GP for your troubles."); + close; + +L_NoKill: + mesn; + mesq l("Ahahahah, do you really think I'll accept legs if you don't help me cleaning my fields?"); + next; + mesn; + mesq l("Kill at least a hundred of Pinkies here, and then we can talk."); + close; + +L_Letter: + mesq l("You're the savior of Hurnscald crops. Half from the world would die from famine, weren't for you."); + next; + mesn; + mesq l("@@ and I signed this letter. Deliver it to Airlia on the Town Hall, and she'll reward you correctly.", l("Hinnak")); + close; + +L_Thanks: + mesq l("Many thanks for all the help!"); + close; + +OnKillPinkie: + .@q1=getq(HurnscaldQuest_Farmers); + .@q2=getq2(HurnscaldQuest_Farmers); + if (.@q1 == 3) { + setq2 HurnscaldQuest_Farmers, .@q2+1; + if (! (.@q2+1) % 10) + dispbottom l("@@ pinkies killed on @@'s field.", .@q2+1, l("Oscar")); + } + fix_mobkill(Pinkie); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FarmerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, TulimsharGuardBoots); + setunitdata(.@npcId, UDT_WEAPON, CottonTrousers); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 0); + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/014-4/_import.txt b/npc/014-4/_import.txt new file mode 100644 index 0000000..56c0bfb --- /dev/null +++ b/npc/014-4/_import.txt @@ -0,0 +1,8 @@ +// Map 014-4: Thunderhill +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/014-4/_mobs.txt", +"npc/014-4/_warps.txt", +"npc/014-4/kamelot.txt", +"npc/014-4/mapflags.txt", +"npc/014-4/slide.txt", +"npc/014-4/thorn.txt", diff --git a/npc/014-4/_mobs.txt b/npc/014-4/_mobs.txt new file mode 100644 index 0000000..4f3abd8 --- /dev/null +++ b/npc/014-4/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-4: Thunderhill mobs +014-4,134,85,26,22 monster Centaur 1139,3,90000,80000 +014-4,61,38,43,23 monster Mana Piou 1155,7,25000,5000 +014-4,130,68,24,52 monster Forest Mushroom 1060,10,60000,45000 +014-4,70,77,53,13 monster Living Potato 1181,5,60000,45000 +014-4,20,26,4,20 monster Bluepar 1177,4,30000,30000 +014-4,83,65,44,36 monster Red Butterfly 1025,30,60000,45000 diff --git a/npc/014-4/_warps.txt b/npc/014-4/_warps.txt new file mode 100644 index 0000000..901a638 --- /dev/null +++ b/npc/014-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-4: Thunderhill warps +014-4,95,32,0 warp #014-4_95_32 0,0,017-1,104,102 +014-4,159,69,0 warp #014-4_159_69 0,3,014-3,30,69 diff --git a/npc/014-4/kamelot.txt b/npc/014-4/kamelot.txt new file mode 100644 index 0000000..04c80a2 --- /dev/null +++ b/npc/014-4/kamelot.txt @@ -0,0 +1,284 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// This NPC is a work on progress. It measures all players equal. +// Controls the weekly guild dungeon feature. +// Variable structure: + +// $@KAMELOT_ID [ guild_id ] = instance id +// $@KAMELOT_WAVE [ guild_id ] = contains a temporary control value +// $KAMELOT_MX [ guild_id ] = player average level - difficulty record +// $KAMELOT_PC [ guild_id ] = total members in guild +// $KAMELOT_QUEST [ guild_id ] = quest status (bitmask) + // 0 - Not started + // 1 - Quest Assigned by Arthur (new players cannot enter anymore) + // 2 - Guinevere dialog clear + // 4 - Weapon Room cutscene seen + // 8 - Magic Seal 01 Broken + // 16 - Magic Seal 02 Broken + // 32 - Door opened + // 64 - Boss defeated + // 128 - Guild Reward Claimed +// $KAMELOT_KEYMASK [ guild_id ] = (bitmask) Keys and Lockpicks + // 1 - Treasure A (Boss Room key) + // 2 - Treasure B + // 4 - Treasure C + // 8 - Treasure D + // 16 - Weapon Room Key +// $KAMELOT_PASSMASK [ guild_id ] = (bitmask) Switches in 042-10 + +// $KAMELOT_KEY [ guild_id ] = Which bit was assigned as the key +// $KAMELOT_PASSCODE [ guild_id ] = A bitmask of passcode to use the key +// $KAMELOT_COOLDOWN [ guild_id ] = Last attempt (weekly attempts) + +// KamelotCleanup(guildid) +function script KamelotCleanup { + .@g=getarg(0); + //$@KAMELOT_ID[.@g]=0; + $@KAMELOT_WAVE[.@g]=0; + $KAMELOT_MX[.@g]=0; + $KAMELOT_PC[.@g]=0; + $KAMELOT_KEY[.@g]=0; + $KAMELOT_QUEST[.@g]=0; + $KAMELOT_KEYMASK[.@g]=0; + $KAMELOT_PASSCODE[.@g]=0; + $KAMELOT_PASSMASK[.@g]=0; + // TODO: Instance Destroy? D: + // And it couldn't be ran from Arthur? D: + // Timers keep running and previous attempts leave artifacts D: + return; +} + +014-4,67,26,0 script #KamelotEnter NPC_HIDDEN,0,0,{ + end; + +OnTouch: + // Not in a guild: Gate is sealed (MK might be excluded as well) + if (getcharid(2) < 1) { + dispbottom l("The gates are firmly shut. A warning is on the door, \"DO NOT ENTER. Guild Only.\""); + end; + } + + // Save your GID for reference + .@g=getcharid(2); + + // Quest already started + if ($KAMELOT_QUEST[.@g] & 1) { + + // Aurora Event Framework Handler + if ($EVENT$ == "Kamelot") { + FYE_Kamelot(); + } + + // Quest started but Cooldown expired + // Needs to cleanup - wipe all variables + if ($KAMELOT_COOLDOWN[.@g] < gettimeparam(GETTIME_WEEKDAY)) { + KamelotCleanup(.@g); + } else { + dispbottom l("The gates are firmly shut by some weird magical power. %s", (TUTORIAL ? l("Perhaps we should wait a few days."):"")); + end; + } + } + + // Build instance if it doesn't exists or has been reallocated + if (instanceowner($@KAMELOT_ID[.@g]) != .@g) { + debugmes "Creating Instance for guild %d", .@g; + .@inst=instance_create("Kamelot@"+.@g, .@g, IOT_GUILD); + if (.@inst < 0) + Exception("Kamelot Instance Error", RB_DEFAULT|RB_ISFATAL); + instance_attachmap("042-0", .@inst, .@g, "042-0@"+.@g); + instance_attachmap("042-1", .@inst, .@g, "042-1@"+.@g); + instance_attachmap("042-2", .@inst, .@g, "042-2@"+.@g); + instance_attachmap("042-3", .@inst, .@g, "042-3@"+.@g); + instance_attachmap("042-4", .@inst, .@g, "042-4@"+.@g); + instance_attachmap("042-5", .@inst, .@g, "042-5@"+.@g); + instance_attachmap("042-6", .@inst, .@g, "042-6@"+.@g); + instance_attachmap("042-7", .@inst, .@g, "042-7@"+.@g); + instance_attachmap("042-8", .@inst, .@g, "042-8@"+.@g); + instance_attachmap("042-9", .@inst, .@g, "042-9@"+.@g); + instance_attachmap("042-10", .@inst, .@g, "042-10@"+.@g); + instance_attachmap("042-11", .@inst, .@g, "042-11@"+.@g); + instance_set_timeout(21600, 21600, .@inst); // Instance lasts 6 hours + instance_init(.@inst); + $@KAMELOT_ID[.@g] = .@inst; + } + + // You can enter in every other state + warp "042-0@"+.@g, any(59, 60), 80; + + // Renew the instance clock : 6 hours + instance_set_timeout(21600, 21600, $@KAMELOT_ID[.@g]); + end; + +// Debug is only allowed if server is in override mode +OnDebug: + if (!$@GM_OVERRIDE) goto L_SimplifiedDebug; + /* + mes "Guild ID:"; + input .@g; + + if (.@g < 1 || .@g > 100) + .@g=getcharid(2); + */ + + .@g=getcharid(2); + if (.@g < 1) end; + mes "Kamelot Debug"; + mesf "Guild: %d", .@g; + mes ""; + mesf "Keys: %d", $KAMELOT_KEYMASK[.@g]; + mesf "Quest: %d", $KAMELOT_QUEST[.@g]; + mesf "Wave: %d", $@KAMELOT_WAVE[.@g]; + mes ""; + mesf "Maze Password: %d", $KAMELOT_PASSCODE[.@g]; + mesf "Maze Status: %d", $KAMELOT_PASSMASK[.@g]; + mes ""; + mesf "Guild Power: %d", $KAMELOT_MX[.@g]; + mesf "Guild Count: %d", $KAMELOT_PC[.@g]; + select + l("Abort"), + l("Reset"), + l("Reset & Destroy"), + l("Quest - Skip Weapons Room"), + l("Quest - Skip Krukan fight"), + l("Quest - Find all Keys"), + l("Quest - Unlock Boss Room"), + l("Quest - Mark as Completed"), + "", + l("Warp - Jump to Entrance"), // 10 + l("Warp - Jump to Weapon Room"), + l("Warp - Jump to Basement"), + l("Warp - Jump to Jail"), + l("Warp - Jump to Sewer Entrance"), + l("Warp - Jump to Cave Exit"), + l("Warp - Arrest me!"); + mes ""; + switch (@menu) { + // 3 inheirs 2 + case 3: + instance_destroy($@KAMELOT_ID[.@g]); + case 2: + $KAMELOT_COOLDOWN[.@g] = 0; + KamelotCleanup(.@g); + break; + // We now order from bottom-up + case 8: + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|64; + mes "WARNING: Quest concluded"; + case 7: + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|8; + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|16; + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|32; + mes "WARNING: Boss Room was unlocked"; + case 6: + $KAMELOT_KEYMASK[.@g]=$KAMELOT_KEYMASK[.@g]|1; + $KAMELOT_KEYMASK[.@g]=$KAMELOT_KEYMASK[.@g]|2; + $KAMELOT_KEYMASK[.@g]=$KAMELOT_KEYMASK[.@g]|4; + $KAMELOT_KEYMASK[.@g]=$KAMELOT_KEYMASK[.@g]|8; + mes "WARNING: Kamelot Basement Chests skipped (not lootable)."; + case 5: + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|4; + mes "WARNING: Krukan Fight and jail skipped."; + case 4: + $KAMELOT_KEYMASK[.@g]=$KAMELOT_KEYMASK[.@g]|16; + mes "WARNING: Weapons Room skipped."; + break; + // 10+ are the warp triggers + case 10: + warp "042-0@"+.@g, 60, 40; break; + case 11: + warp "042-1@"+.@g, 40, 65; break; + case 12: + warp "042-2@"+.@g, 44, 45; break; + case 13: + warp "042-3@"+.@g, 60, 130; break; + case 14: + warp "042-4@"+.@g, 60, 67; break; + case 15: + warp "042-10@"+.@g, 95, 52; break; + case 16: + .@t$="042-3@"+.@g; + .@n$=instance_npcname("#KSlimeSpawn", $KAMELOT_ID[.@g]); + setarray .@x, 33, 84, 41, 74, 36, 57, 79, 43, 24, 86, 59, 38; + setarray .@y, 135, 127, 119, 107, 96, 84, 63, 67, 67, 22, 49, 27; + specialeffect(FX_HIT, AREA, getcharid(3)); + .@v = (rand2(12) % 12); + .@r=attachrid(getcharid(3)); + if (.@r) { + warp .@t$, .@x[.@v], .@y[.@v]; + if (!countitem(Lockpicks)) + addtimer 700, .@n$+"::OnFirstSlime"; + setpcblock(PCBLOCK_HARD, false); + } else { + unitwarp(getcharid(3), .@t$, .@x[.@v], .@y[.@v]); // And good luck D: + consolebug "FATAL: Could not attach: %d", getcharid(3); + } + break; + } + close; + +L_SimplifiedDebug: + mesc "THIS IS FOR JESUSALVA DEBUG ONLY - DO NOT USE NORMALLY", 1; + mes "Guild ID:"; + input .@gid; + if (.@gid < 1 || .@gid > 100) + close; + $KAMELOT_COOLDOWN[.@gid] = 0; + KamelotCleanup(.@gid); + mesc "THE GUILD WAS WIPED FROM EXISTENCE.", 1; + mesc "Have a nice day! %%l", 3; + close; + +OnInit: + bindatcmd "kdebug", "#KamelotEnter::OnDebug", 99, 100, 1; + end; +} + +///////////////////////////////////////////////////////////////////////////// +// KamelotCaveSpawn(Amount, x1, y1, x2, y2, power{, map}) +function script KamelotCaveSpawn { + .@label$=instance_npcname(.name$)+"::OnKillMob"; + .@gcount=getarg(0); + .@x1=getarg(1); + .@y1=getarg(2); + .@x2=getarg(3); + .@y2=getarg(4); + .@avg=getarg(5); + .@m$=instance_mapname(getarg(6, .map$)); + //debugmes "Total %d, map %s (power %d)", .@gcount, .@m$, .@avg; + freeloop(true); + for (.@i=0; .@i < .@gcount; .@i++) { + .@mobId=any(CursedSoldier, CursedArcher); // 50-50 ratio + .@mob=areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mobId), .@mobId, 1, .@label$); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg+1); + setunitdata(.@mob, UDT_STR, 1+.@avg*6/10); + setunitdata(.@mob, UDT_AGI, 1+.@avg*5/10); + setunitdata(.@mob, UDT_VIT, 1+.@avg*5/10); + setunitdata(.@mob, UDT_INT, 1+.@avg*6/10); + setunitdata(.@mob, UDT_DEX, 1+.@avg*6/10); + setunitdata(.@mob, UDT_LUK, 1+.@avg*5/10); + setunitdata(.@mob, UDT_ADELAY, 1372); + setunitdata(.@mob, UDT_ATKRANGE, (.@mobId == CursedArcher ? any(6,7) : any(1,2))); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*40); + setunitdata(.@mob, UDT_HP, .@avg*40); + setunitdata(.@mob, UDT_ATKMIN, .@avg*47/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*67/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg*12/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*8/10); + setunitdata(.@mob, UDT_HIT, .@avg*65/10); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*45/10); // Advised: x4 + // Critical calculation + .@min=15; + .@max=max(.@min, min(40, .@avg/3)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + // Loop through + } + freeloop(false); + return; +} + + diff --git a/npc/014-4/mapflags.txt b/npc/014-4/mapflags.txt new file mode 100644 index 0000000..351108a --- /dev/null +++ b/npc/014-4/mapflags.txt @@ -0,0 +1,4 @@ +014-4 mapflag magic_damage_rate 120 +014-4 mapflag misc_damage_rate 120 +014-4 mapflag weapon_damage_rate 80 + diff --git a/npc/014-4/slide.txt b/npc/014-4/slide.txt new file mode 100644 index 0000000..28cbc67 --- /dev/null +++ b/npc/014-4/slide.txt @@ -0,0 +1,27 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Allows movement sliding though the bridge on 014-4 + +014-4,58,51,0 script #014-4_58_51 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 51, 51; + end; +} + +014-4,52,51,0 script #014-4_52_51 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 59, 51; + end; +} + +014-4,25,47,0 script #014-4_25_47 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 25, 49; + end; +} + diff --git a/npc/014-4/thorn.txt b/npc/014-4/thorn.txt new file mode 100644 index 0000000..6a06704 --- /dev/null +++ b/npc/014-4/thorn.txt @@ -0,0 +1,141 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Spring Quest, disabled during Easter +// Sighly inspired with Soren and Demure relationship, I guess +// +// SQuest_Spring +// 0 - Not Assigned +// 1 - + +014-4,149,98,0 script Thorn NPC_THORN_NPC,{ + if (season() != SPRING && !$@GM_OVERRIDE) + goto L_OutOfSeason; + if ($EVENT$ == "Easter") + goto L_QuestDisabled; + + .@q=getq(SQuest_Spring); + mesn; + mesq l("Blossom, blossom, why you do this to me..."); + if (.@q < 1) + goto L_SpringQuest; + +L_Main: + next; + .@q=getq(HurnscaldQuest_Thorn); + if (.@q == 0) { + mesn; + mesq l("I'm actually half-wose. This means part of me is tree."); + next; + mesn; + mesq l("I can walk, I'm just... Trapped here. That's why I need someone to help me to cultivate my wose part!"); + next; + mesn; + mesq l("Could you please bring me @@ @@ and @@ @@? I'll give you a bunch of herbs if you help me!", 12, getitemlink(Root), 35, getitemlink(Acorn)); + next; + mesc l("Give him the materials he asked for?"); + if (askyesno() == ASK_YES) { + inventoryplace ShadowHerb, 20, ArtichokeHerb, 20; + if (!transcheck(Root, 12, Acorn, 35)) + close; + getitem ShadowHerb, 20; + getitem ArtichokeHerb, 20; + getexp 600, 120; // Reference: Level 20 + mesn; + mesq l("Thanks for helping me out. I know this is nothing, but please accept these herbs I've collected."); + setq HurnscaldQuest_Thorn, 1; + next; + } + } + closeclientdialog; + goodbye; + end; + +L_OutOfSeason: + mesn; + mesq l("Maybe on spring Blossom will look at me... For now, my wose part is withering."); + goto L_Main; + +L_QuestDisabled: + mesn; + mesq l("Happy Easter!"); + mesc l("The @@ quest is disabled during Easter event.", getitemlink(Wreath)); + next; + goto L_Main; + +L_SpringQuest: + next; + mesn; + mesq l("Hey, you! Help me!"); + next; + mesn; + mesq l("My waifu Blossom has me trapped here, and she won't even look at me, because I'm part-Wose!"); + next; + mesn; + mesq l("But it is spring! Perhaps, if you bring me some flowers and fruits which only grow at Spring, I could impress her!"); + next; + mesn; + mes l("What about you bring me:"); + mes l("@@/55 @@", countitem(Rose), getitemlink(Rose)); + mes l("@@/55 @@", countitem(Tulip), getitemlink(Tulip)); + mes l("@@/50 @@", countitem(Blueberries), getitemlink(Blueberries)); + mes l("@@/10 @@", countitem(GrassSeeds), getitemlink(GrassSeeds)); + + mes l("@@/80 @@", countitem(MauveHerb), getitemlink(MauveHerb)); + mes l("@@/80 @@", countitem(GambogeHerb), getitemlink(GambogeHerb)); + mes l("@@/80 @@", countitem(CobaltHerb), getitemlink(CobaltHerb)); + mes l("@@/80 @@", countitem(AlizarinHerb), getitemlink(AlizarinHerb)); + next; + select + l("Not now, thanks"), + l("To be honest, I have that with me!"); + + mes ""; + if (@menu == 1) + goto L_Main; + if (@menu == 2) { + if ( + countitem(Rose) < 55 || + countitem(Tulip) < 55 || + countitem(Blueberries) < 50 || + countitem(GrassSeeds) < 10 || + countitem(MauveHerb) < 80 || + countitem(GambogeHerb) < 80 || + countitem(CobaltHerb) < 80 || + countitem(AlizarinHerb) < 70 + ) goto L_Lying; + + inventoryplace Wreath, 1; + delitem Rose, 55; + delitem Tulip, 55; + delitem Blueberries, 50; + delitem GrassSeeds, 10; + delitem MauveHerb, 80; + delitem GambogeHerb, 80; + delitem CobaltHerb, 80; + delitem AlizarinHerb, 80; + getitem Wreath, 1; + getexp (210*BaseLevel), 120; + setq1 SQuest_Spring, 1; + mesn; + mesq l("Many thanks! If I let my wose side blossom, I'm sure she'll look at me!"); + next; + mesn; + mesq l("Here, take the reward as promised!"); + goto L_Main; + } + + close; + +L_Lying: + mesn; + mesq l("Please don't lie to me...") + " </3"; + goto L_Main; + +OnInit: + .sex=G_MALE; + .distance=4; + end; + +} diff --git a/npc/014-5-1/_import.txt b/npc/014-5-1/_import.txt new file mode 100644 index 0000000..16e2148 --- /dev/null +++ b/npc/014-5-1/_import.txt @@ -0,0 +1,5 @@ +// Map 014-5-1: Forgotten Hut +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/014-5-1/_warps.txt", +"npc/014-5-1/objects.txt", +"npc/014-5-1/sagratha.txt", diff --git a/npc/014-5-1/_warps.txt b/npc/014-5-1/_warps.txt new file mode 100644 index 0000000..0d236b1 --- /dev/null +++ b/npc/014-5-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-5-1: Forgotten Hut warps +014-5-1,33,45,0 warp #014-5-1_33_45 0,0,014-5,122,138 diff --git a/npc/014-5-1/objects.txt b/npc/014-5-1/objects.txt new file mode 100644 index 0000000..ed35209 --- /dev/null +++ b/npc/014-5-1/objects.txt @@ -0,0 +1,90 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Sagratha household items + +014-5-1,33,38,0 script Pan#Saggy NPC_NO_SPRITE,{ + @qsagratha=@qsagratha|1; + mesn; + mesc l("The pan is empty, but it smells like fresh cookies."); + close; +OnInit: + .distance=5; + disablenpc .name$; + end; +} + + +014-5-1,33,34,0 script Painting#Saggy NPC_NO_SPRITE,{ + @qsagratha=@qsagratha|2; + mesn; + mesc l("Definitely not suspcious at all."); + if (@qsagratha >= 31) { + next; + mesn; + mesc l("Thinking well... There's something behind it! A secret window!"); + if (getq(HurnscaldQuest_Sagratha) <= 2) { + setq3 HurnscaldQuest_Sagratha, 1; + } else { + next; + mesn l("Secret Window"); + mesc l("Enter though the window to the secret caves?"); + next; + if (askyesno() == ASK_YES) { + warp "015-8", 99, 178; + closeclientdialog; + end; + } + } + next; + mesn l("Secret Window"); + mesc l("The window appears to be unlocked. It might have been open some time ago."); + } + close; +OnInit: + .distance=5; + disablenpc .name$; + end; +} + + +014-5-1,29,37,0 script Fireplace#Saggy NPC_NO_SPRITE,{ + @qsagratha=@qsagratha|4; + mesn; + mesc l("Some smoke still remains, but the fire seems to have died."); + close; +OnInit: + .distance=5; + disablenpc .name$; + end; +} + + + + +014-5-1,29,39,0 script Book#Saggy NPC_NO_SPRITE,{ + @qsagratha=@qsagratha|8; + mesn; + mesc l("Seems to be a recipe book about apple cookies."); + close; +OnInit: + .distance=5; + disablenpc .name$; + end; +} + + + + +014-5-1,29,41,0 script Bed#Saggy NPC_NO_SPRITE,{ + @qsagratha=@qsagratha|16; + mesn; + mesc l("Smells like mouboos."); + close; +OnInit: + .distance=5; + disablenpc .name$; + end; +} + diff --git a/npc/014-5-1/sagratha.txt b/npc/014-5-1/sagratha.txt new file mode 100644 index 0000000..6b727f4 --- /dev/null +++ b/npc/014-5-1/sagratha.txt @@ -0,0 +1,650 @@ +// TMW2 scripts. +// Authors: +// Jesusalva and TMW Org. +// Description: +// Sagratha (variables SAGRATHA_SCORE and SAGRATHA_FRIENDSHIP) + +// SaggyScoreUpdate( amount ) +function script SaggyScoreUpdate { + .@val=getarg(0); + SAGRATHA_SCORE=limit(-50, SAGRATHA_SCORE+.@val, 50); + return; +} + +function script SaggyMobCount { + switch (killedrid) { + // She gets angry + case AlphaMouboo: + SaggyScoreUpdate(-3); + break; + case Mouboo: + SaggyScoreUpdate(any(-2, -3)); + break; + case Pollet: + case Fluffy: + case IcedFluffy: + SaggyScoreUpdate(any(-1, -2, -3)); + break; + case PoisonSpikyMushroom: + case LogHead: + case ForestMushroom: + SaggyScoreUpdate(any(-1, -2)); + break; + case Squirrel: + case SpringSquirrel: + case LofSquirrel: + case FrozenSquirrel: + case FairysSquirrel: + case MananaTree: + SaggyScoreUpdate(-1); + break; + + // She may get happy. Who knows. + case ViciousSquirrel: + case WickedMushroom: + case Bluepar: + case BlackScorpion: + case Bandit: + case RobinBandit: + SaggyScoreUpdate(any(0,0,0,0,1)); + break; + } + // Full hate vs Ghosts, Undead and shadow + .@mr=getmonsterinfo(killedrid, MOB_ELEMENT); + switch (.@mr) { + case Ele_Dark: + case Ele_Ghost: + //case Ele_Undead: + SaggyScoreUpdate(any(0,0,0,0,1)); + break; + } + return; +} + +014-5-1,33,37,0 script Sagratha NPC_SAGRATHA,{ + function teachMagic; + function requireHelp; + if (array_find(.SaggyHats, getequipid(EQI_HEAD_TOP)) >= 0) + goto L_HatAttack; + + .@q=getq(HurnscaldQuest_Sagratha); + .@m=getq(HurnscaldQuest_InjuriedMouboo); + + // Safety - Impossible Situation + if (.@m < 2 || .@m > 3 || .@q < 6) { + Exception("Player found cheating/breaking the rules. Character banned. Please contact GM Staff if you believe this is an error.", RB_DEFAULT|RB_SPEECH); + //atcommand "@jailfor 7h "+strcharinfo(0); + Exception ("Exception at exception, shutting down! (m is "+.@m+", q is "+.@q+")", RB_SPEECH|RB_ISFATAL); + close; + } + + mesn; + + if (SAGRATHA_FRIENDSHIP >= 2) + mesc l("@@ nods as she notices you.", .name$); + else if (SAGRATHA_FRIENDSHIP == 1) + mesc l("@@ raises an eyebrow as you address her.", .name$); + else + mesc l("@@ glances at you, suspicion evident in her eyes.", .name$); + + mesq l("Hello."); + next; + + if (.@m == 2 && .@q >= 6) + goto L_Reward; + if (.@q == 6) + goto L_Finish; + if (SAGRATHA_SCORE < 0) + goto L_Unhappy; + + do + { + .@x=getq(General_Sagratha); + mesn strcharinfo(0); + select + l("Can I return to the shrine?"), + l("I would like some Mouboo Milk."), + l("Can you teach me magic? Pretty please?"), + l("Can I help you with something?"), + l("What can you tell me about the Moubootaur?"), + l("Good bye."); + mes ""; + switch (@menu) { + // XXX: Sealed Shrine + case 1: + mesn; + mesq l("Why would you want to go back there?"); + mesc l("@@ snarls.", .name$); + next; + mesn; + mesq l("That was a Moubootaur's temple. It is not the kind of place to give a stroll on the park."); + next; + mesn; + mesq l("Did you noticed how no monster got close to it?"); + next; + select + l("You're right. Sorry."), + l("Don't worry, I'll stay at the caves."), + l("I want to pay my respects as a fellow Pink Mouboo cult member."), + l("But I need to!"); + mes ""; + if (@menu == 3) { + mesn; + mesq l("As a what?"); + next; + } + else if (@menu == 4) { + mesn; + mesq l("No you don't. Don't disturb the forest for no reason."); + close; + } + else if (@menu == 1) { + mesn; + mesq l("Hmpf. Wise choice."); + next; + break; // Return to the while loop + } + mesn; + mesq l("As you wish. But do not mess with things you don't understand."); + next; + closeclientdialog; + warp "015-8", 99, 178; + close; + // XXX: Mouboo Milk + case 2: + if (SAGRATHA_SCORE < 45) { + mesn; + mesq l("You don't need, nor deserve it."); + next; + mesn; + mesq l("Learn milking a Mouboo. And if you harm them, I'll cast a lightning bolt at you!"); + next; + break; + } + if (SAGRATHA_MILK > gettimeparam(GETTIME_HOUR)) { + mesn; + mesq l("I just gave you it. Come back later."); + next; + break; + } + inventoryplace Milk, 2; + SAGRATHA_SCORE-=8; // :o + SAGRATHA_MILK=gettimeparam(GETTIME_HOUR)+4; + getitem Milk, any(1,1,1,1,1,1,2); + mesn; + mesq l("Here. You know that milking Mouboos won't cause them harm, right? It's a different story with @@.", getitemlink(MoubooSteak)); + close; + break; + // XXX: Teach magic / etc + case 3: + case 4: + if (.@x % 2 == 0) + teachMagic(); + else + requireHelp(); + break; + // XXX: Moubootaur + case 5: + mesn; + mesq l("It's a scary legend of old. It's truth, but I don't know how much it is."); + next; + mesn; + mesq l("Anyway, the Moubootaur certainly existed, and probably is still alive."); + next; + mesn; + mesq l("How powerful they were or are, their cruelty, their judgment against the Terranite Civilization and the other bloody details of the legend... I cannot confirm them to you."); + next; + break; + default: + closeclientdialog; + goodbye; + close; + } + } while (true); + + close; + +L_Finish: + // WHAT + if (.@m != 3) { + Exception("Player found cheating/breaking the rules. Character banned. Please contact GM Staff if you believe this is an error.", RB_DEFAULT|RB_SPEECH); + atcommand "@jailfor 7h "+strcharinfo(0); + close; + } + mesn; + mesq l("I haven't thanked you yet."); + next; + select + l("You're welcome."), + l("For the rescue? You didn't seemed to be in need of aid."), + l("Yes, I'm awesome, praise me more."), + l("For the mouboo? I was only doing my duty."); + mes ""; + mesn; + if (@menu != 4) + mesq l("Not for that! For helping out the injuried mouboo, of course."); + else + mesq l("Hmpf, it's good to see you're at least trying to follow Wyara's example."); + next; + mesn; + mesq l("Anyway, Wyara told me about the assassins. I'm surprised they followed me until the ruins."); + next; + mesn; + mesq l("I thought I would be safe in the shrine, but seems like they are after the Moubootaur. Great danger will befall all forest creatures if they accomplish that."); + next; + mesn; + mesq l("This will affect humans, too, so don't act as if it is not your bussiness either."); + next; + mesn; + mesq l("For now, take this @@. I hope that you will use this power for something good now.", getitemlink(AlchemyBlueprintB)); + inventoryplace AlchemyBlueprintB, 1; + getitem AlchemyBlueprintB, 1; + getexp 24000, 0; // ~70% from level 40 (quest estimate level) + Zeny+=3000; // About 50 carps sold + SAGRATHA_FRIENDSHIP+=1; + setq HurnscaldQuest_Sagratha, 7; + close; + +L_Reward: + select + l("Hi! My name is @@.", strcharinfo(0)), + l("Are you Sagratha?"), + l("Good bye."); + mes ""; + if (@menu == 3) + close; + if (@menu == 1) { + mesn; + mesq l("Ah."); + next; + select + l("Are you Sagratha?"), + l("Erm... Good bye."); + mes ""; + if (@menu == 2) + close; + } + mesn; + mesq l("Yes."); + next; + mesn; + mesq l("Do you need something from me?"); + next; + // A check is not needed, because quest pre-requisites + // If you don't have the quest in clearable state... CHEAT! + select + l("Yep. There was a cursed mouboo on the road."), + l("Not really."); + mes ""; + if (@menu == 2) { + mesn; + mesq l("Okay then."); + close; + } + mesn; + mesq l("Yes, the cursed Mouboo you told me earlier, on the way out."); + next; + mesn; + mesq l("The curse was done by nobody less than... the Moubootaur."); + next; + mesn; + mesq l("Well. I'll take care of that, don't worry with that."); + next; + mesn; + mesq l("It's my duty to protect the beings in the forest... Not all of them are monsters as @@s like you think.", get_race()); + compareandsetq HurnscaldQuest_InjuriedMouboo, 2, 3; + SAGRATHA_FRIENDSHIP+=1; + close; + +L_HatAttack: + mesn; + mesc l("@@ seems to be trembling with disgust as she stares at your headgear.", .name$); + next; + mesn; + mesq l("Do you think that is funny?"); + mesc l("@@ snarls.", .name$); + next; + mesn; + mesq l("You have no idea what that poor creature felt!"); + next; + mesn; + mesc l("She snaps her fingers."); + mesq l("Let me show you..."); + specialeffect 312, SELF, getcharid(3); + percentheal -30, 0; + close; + +L_Unhappy: + mesn; + mesc l("@@ glares at you in anger.", .name$); + mesq l("I wonder if you can still sleep after killing those innocent forest creatures!"); + next; + mesn; + mesq l("I am sure that they will come back to haunt you in your dreams!"); + close; +// Functions - TODO: Where are Mouboo Summon? Loghead summon? etc.? +function teachMagic { + .@x=getarg(0,getq(General_Sagratha)); + switch (.@x) { + case 0: + if (MAGIC_LVL < 1) + goto L_Magicless; + mesn; + mesc l("@@ nods.", .name$); + mesq l("For now, yes. But you will have to prove that you really care about more than yourself."); + next; + mesn; + mesq l("I'll teach you a spell. It is called CUTE HEART. It summons Fluffies. They are so cute, I want to hug them..."); + skill TMW2_CUTEHEART, 1, 0; + setq General_Sagratha, 1; + break; + case 2: + if (MAGIC_LVL < 2) + goto L_Magicless; + mesn; + mesc l("@@ nods.", .name$); + mesq l("Be careful, because the more magic power you ask the Mana Seed, the more difficult to control it will be."); + next; + mesn; + mesq l("This is how the Monster King became evil. Keep practicing magic, so you get more comfortable with it and fail less often."); + next; + mesn; + mesq l("Anyway. This is the KALBOO spell. It'll summon mouboos."); + next; + mesn; + mesq l("The world belong to Mouboos. They are sublime creatures!"); + skill TMW2_KALBOO, 1, 0; + setq General_Sagratha, 3; + break; + case 4: + if (MAGIC_LVL < 3) + goto L_Magicless; + mesn; + mesc l("@@ nods.", .name$); + mesq l("You've been doing well. Be careful out there."); + next; + mesn; + mesq l("Anyway. This is the KALWULF spell. It'll spawn Wolverns."); + next; + mesn; + mesq l("Wolverns are fierce creatures who likes cold places. Their fur is soft."); + next; + mesn; + mesq l("You can also use PLANT KINGDOM to spawn several plants at once, too. Let's make this world more green."); + skill TMW2_KALWULF, 1, 0; + skill TMW2_PLANTKINGDOM, 1, 0; + setq General_Sagratha, 5; + break; + case 6: + if (MAGIC_LVL < 4) + goto L_Magicless; + mesn; + mesc l("@@ nods.", .name$); + mesq l("You've been doing quite well, indeed. Ever visited Lilit?"); + next; + mesn; + mesq l("Lilit is the fairy kingdom, governed by Lilit the Fairy."); + next; + mesn; + mesq l("Anyway. This is the FAIRY KINGDOM spell. It'll spawn fairies."); + next; + mesn; + mesq l("Fairies usually behaves well, but they hate snakes, so avoid casting halhiss and fairy kingdom at once. You never know."); + skill TMW2_FAIRYKINGDOM, 1, 0; + setq General_Sagratha, 7; + break; + case 8: + if (MAGIC_LVL < 5) + goto L_Magicless; + mesn; + mesc l("@@ nods.", .name$); + mesq l("You've been doing very well, I'm surprised."); + next; + mesn; + mesq l("The most powerful spells can, sometimes, summon some monster you didn't wanted to."); + next; + mesn; + mesq l("This is the FROZENHEART spell. It'll summon... Yeti."); + next; + mesn; + mesq l("But if you're not skilled enough, or lose control of it, a Moggun might be spawned instead. Which is a young, weak, baby Yeti."); + skill TMW2_FROZENHEART, 1, 0; + setq General_Sagratha, 9; + break; + case 10: + if (MAGIC_LVL < 6) + goto L_Magicless; + mesn; + mesc l("@@ nods.", .name$); + mesq l("You've been asking for way too much power from the Mana Seed."); + next; + mesn; + mesq l("Too much power can corrupt you. I've seen this happening before... countless times."); + next; + mesn; + mesq l("This is the STONEHEART spell. It'll summon the fierce Terranite."); + next; + mesn; + mesq l("They used to be an advanced civilization long long ago, but they decided to go against the Moubootaur."); + next; + mesn; + mesq l("They are now like most monsters. I pity them. Their sacrifice was not in vain, though."); + skill TMW2_STONEHEART, 1, 0; + setq General_Sagratha, 11; + break; + case 12: + if (MAGIC_LVL < 7) + goto L_Magicless; + mesn; + mesc l("@@ nods reluctantly.", .name$); + mesq l("If you try take any more power from the Mana Seed, I'll kill you."); + next; + mesn; + mesq l("Seven levels is what the Mana Seed may give to any race but Saviors."); + next; + mesn; + mesq l("Of course, the Savior race is extinct as far as history book concern with."); + next; + mesn; + mesq l("This is the FAIRYEMPIRE spell. It'll summon Pixies."); + next; + mesn; + mesq l("For simple-minded individuals like you, pixies are fairies."); + skill TMW2_FAIRYEMPIRE, 1, 0; + setq General_Sagratha, 13; + break; + } + /* + Plants Lv 1 + Kalmurk Lv 10 + Zarkor Lv 15 + Fluffy Lv 15 + Poison Spiky Mushroom Lv 25 + Limerizer Lv 30 → Unused + + Mouboo Lv 35 + Halhiss Lv 40 + Wolvern Lv 45 + + Fairy Kingdom Lv 50 + Frozenheart Lv 60 + Dragokin Lv 70 → Unused (Lilit reward) + Stoneheart Lv 80 + Pixies Lv 90 + + Never released: + TODO Scorpions Lv 30~50 + TODO Black Mamba Lv 80 + TODO Moonshroom Lv 80 + TODO Centaur Lv 80 + + skill TMW2_DRAGOKIN, 5; + */ + next; + return; +} +function requireHelp { + .@x=getarg(0,getq(General_Sagratha)); + switch (.@x) { + case 1: + mesn; + mesq l("I need help because, you see, I don't carry a cauldron around."); + next; + mesn; + mesq l("I need @@ @@, if you could arrange me that I would be grateful.", 35, getitemlink(PiberriesInfusion)); + if (countitem(PiberriesInfusion) < 35) + break; + next; + mesc l("Deliver the potions to Sagratha?"); + if (askyesno() == ASK_YES) { + delitem PiberriesInfusion, 35; + getexp 800, 0; + mesn; + mesq l("Thanks. Do you know the KALSPIKE magic? It allows you to summon Poison Spiky Mushroom. Just use a spore."); + skill TMW2_KALSPIKE, 1, 0; + setq General_Sagratha, 2; + } + break; + case 3: + .@m=Wolvern; + if (BaseLevel < getmonsterinfo(.@m, MOB_LV)) { + mesn; + mesq l("When you're stronger I may consider asking for your help."); + break; + } + mesn; + mesq l("I need help because, you see, I don't carry a cauldron around."); + next; + mesn; + mesq l("I need @@ @@, if you could arrange me that I would be grateful.", 1, getitemlink(GoldenApple)); + if (countitem(GoldenApple) < 1) + break; + next; + mesc l("Deliver the items to Sagratha?"); + if (askyesno() == ASK_YES) { + delitem GoldenApple, 1; + .@xp=getmonsterinfo(.@m, MOB_BASEEXP); + .@jp=getmonsterinfo(.@m, MOB_JOBEXP); + getexp .@xp*20, .@jp*15; + mesn; + mesq l("Thanks. I am willing to share you more magic, if you're interested."); + setq General_Sagratha, 4; + } + break; + case 5: + .@m=FireFairy; + if (BaseLevel < getmonsterinfo(.@m, MOB_LV)) { + mesn; + mesq l("When you're stronger I may consider asking for your help."); + break; + } + mesn; + mesq l("I need help because, you see, I don't carry a cauldron around."); + next; + mesn; + mesq l("I need @@ @@, if you could arrange me that I would be grateful.", 1, getitemlink(LilitWarpCrystal)); + if (countitem(LilitWarpCrystal) < 1) + break; + next; + mesc l("Deliver the items to Sagratha?"); + if (askyesno() == ASK_YES) { + delitem LilitWarpCrystal, 1; + .@xp=getmonsterinfo(.@m, MOB_BASEEXP); + .@jp=getmonsterinfo(.@m, MOB_JOBEXP); + getexp .@xp*20, .@jp*15; + mesn; + mesq l("Thanks. I am willing to share you more magic, if you're interested."); + setq General_Sagratha, 6; + } + break; + case 7: + .@m=Yeti; + if (BaseLevel < getmonsterinfo(.@m, MOB_LV)) { + mesn; + mesq l("When you're stronger I may consider asking for your help."); + break; + } + mesn; + mesq l("I need help because, you see, I don't carry a cauldron around."); + next; + mesn; + mesq l("I need @@ @@, if you could arrange me that I would be grateful.", 20, getitemlink(SacredManaPotion)); + if (countitem(SacredManaPotion) < 20) + break; + next; + mesc l("Deliver the items to Sagratha?"); + if (askyesno() == ASK_YES) { + delitem SacredManaPotion, 20; + .@xp=getmonsterinfo(.@m, MOB_BASEEXP); + .@jp=getmonsterinfo(.@m, MOB_JOBEXP); + getexp .@xp*20, .@jp*15; + mesn; + mesq l("Thanks. I am willing to share you more magic, if you're interested."); + setq General_Sagratha, 8; + } + break; + case 9: + .@m=Terranite; + if (BaseLevel < getmonsterinfo(.@m, MOB_LV)) { + mesn; + mesq l("When you're stronger I may consider asking for your help."); + break; + } + mesn; + mesq l("I need help because, you see, I don't carry a cauldron around."); + next; + mesn; + mesq l("I need @@ @@, if you could arrange me that I would be grateful.", 20, getitemlink(SacredImmortalityPotion)); + if (countitem(SacredImmortalityPotion) < 20) + break; + mesq l("Ah! I also want @@ @@.", 1, getitemlink(Arcanum)); + if (countitem(Arcanum) < 1) + break; + next; + mesc l("Deliver the items to Sagratha?"); + if (askyesno() == ASK_YES) { + delitem SacredImmortalityPotion, 20; + delitem Arcanum, 1; + .@xp=getmonsterinfo(.@m, MOB_BASEEXP); + .@jp=getmonsterinfo(.@m, MOB_JOBEXP); + getexp .@xp*20, .@jp*15; + mesn; + mesq l("Thanks. I am willing to share you more magic, if you're interested."); + setq General_Sagratha, 10; + } + break; + default: + mesn; + mesq l("Maybe. Come back later."); + next; + break; + } + next; + return; +} + +L_Lie: + mesn; + mesq l("I'm not blind."); + close; + +L_Magicless: + mesn; + mesq l("Not yet. You have to ask the mana seed to give you more power."); + next; + mesn; + mesq l("Your skill in magic is not great enough to use some of the spells yet, so keep practicing and visiting the mana seed until it is."); + close; + +OnInit: + setarray .SaggyHats, FluffyHat, MoubooHat, AlphaMoubooHat; + + .distance=5; + npcsit; + end; + +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + diff --git a/npc/014-5/_import.txt b/npc/014-5/_import.txt new file mode 100644 index 0000000..6920787 --- /dev/null +++ b/npc/014-5/_import.txt @@ -0,0 +1,8 @@ +// Map 014-5: North Woodlands +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/014-5/_mobs.txt", +"npc/014-5/_warps.txt", +"npc/014-5/blossom.txt", +"npc/014-5/events.txt", +"npc/014-5/sagratha.txt", +"npc/014-5/sign.txt", diff --git a/npc/014-5/_mobs.txt b/npc/014-5/_mobs.txt new file mode 100644 index 0000000..bdb0413 --- /dev/null +++ b/npc/014-5/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-5: North Woodlands mobs +014-5,156,106,48,44 monster Log Head 1066,21,20000,20000 +014-5,68,178,48,44 monster Mouboo 1023,18,20000,10000 +014-5,90,82,62,55 monster Poison Spiky Mushroom 1043,21,20000,10000 +014-5,56,114,36,30 monster Red Mushroom 1042,7,40000,10000 +014-5,130,132,48,44 monster Forest Mushroom 1060,11,20000,10000 +014-5,76,70,48,44 monster Plushroom Field 1011,16,80000,80000 +014-5,66,110,44,38 monster Chagashroom Field 1128,16,80000,80000 +014-5,153,88,48,44 monster Cobalt Herb 1136,8,80000,80000 +014-5,132,140,61,33 monster Gamboge Herb 1134,6,80000,80000 +014-5,91,132,54,63 monster Mauve Herb 1135,9,80000,80000 diff --git a/npc/014-5/_warps.txt b/npc/014-5/_warps.txt new file mode 100644 index 0000000..e8fbbdc --- /dev/null +++ b/npc/014-5/_warps.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 014-5: North Woodlands warps +014-5,48,161,0 warp #014-5_48_161 0,0,015-7,38,94 +014-5,59,154,0 warp #014-5_59_154 0,0,015-7,84,65 +014-5,82,229,0 warp #014-5_82_229 1,0,014-3,155,41 +014-5,86,31,0 warp #014-5_86_31 0,0,015-5,81,139 +014-5,192,16,0 warp #014-5_192_16 2,0,019-4,53,109 diff --git a/npc/014-5/blossom.txt b/npc/014-5/blossom.txt new file mode 100644 index 0000000..2193075 --- /dev/null +++ b/npc/014-5/blossom.txt @@ -0,0 +1,139 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Spring Quest, disabled during Easter +// +// SQuest_SPring + +014-5,79,168,0 script Blossom NPC_BLOSSOM,{ + if (season() != SPRING && !$@GM_OVERRIDE) + goto L_OutOfSeason; + if ($EVENT$ == "Easter") + goto L_QuestDisabled; + + .@q=getq(SQuest_Spring); + mesn; + mesq l("Thorn is so annoying..."); + if (.@q < 1) + goto L_SpringQuest; + +L_Main: + next; + .@q=getq(HurnscaldQuest_Blossom); + if (.@q == 0) { + mesn; + mesq l("Anyway, I got this really nice @@ from Audsbel. He said I could collect herbs more efficiently with it.", getitemlink(EnchantedHerbBag)); + next; + mesn; + mesq l("I am willing to give it to you, if help me out. I promise it won't be a bother, and it'll really be useful for you, I swear."); + next; + mesn; + mesq l("I would like @@ @@, @@ @@ and @@ @@!", 30, getitemlink(HalfEggshell), 7, getitemlink(RedApple), 1, getitemlink(Orange)); + mesc l("@@ drolls without realizing.", .name$); + next; + mesc l("Give her the materials she asked for?"); + if (askyesno() == ASK_YES) { + inventoryplace EnchantedHerbBag, 1; + if (!transcheck(HalfEggshell, 30, RedApple, 7, Orange, 1)) + close; + getitem EnchantedHerbBag, 1; + getexp 600, 120; // Reference: Level 20 + mesn; + mesq l("Thanks for helping me out! Here, take this bag. Now if you excuse me..."); + mesc l("*drolls*"); + setq HurnscaldQuest_Blossom, 1; + next; + } + } + closeclientdialog; + goodbye; + end; + +L_OutOfSeason: + mesn; + mesq l("Hmm, Woodlands is a wonderful place to live in! Although Thorn gets really a thorn in my boots during spring..."); + goto L_Main; + +L_QuestDisabled: + mesn; + mesq l("Happy Easter!"); + mesc l("The @@ quest is disabled during Easter event.", getitemlink(Wreath)); + next; + goto L_Main; + +L_SpringQuest: + next; + mesn; + mesq l("Hey, you! Aid me!"); + next; + mesn; + mesq l("There's an annoying half-wose-half-elf called Thorn trying to impress me with his wose side!"); + next; + mesn; + mesq l("I do not like him, but even so, he keeps bothering me! Maybe I could out-impress him, though!"); + next; + mesn; + mes l("What about you bring me:"); + mes l("@@/55 @@", countitem(Rose), getitemlink(Rose)); + mes l("@@/55 @@", countitem(Tulip), getitemlink(Tulip)); + mes l("@@/50 @@", countitem(Blueberries), getitemlink(Blueberries)); + mes l("@@/10 @@", countitem(GrassSeeds), getitemlink(GrassSeeds)); + + mes l("@@/80 @@", countitem(MauveHerb), getitemlink(MauveHerb)); + mes l("@@/80 @@", countitem(GambogeHerb), getitemlink(GambogeHerb)); + mes l("@@/80 @@", countitem(CobaltHerb), getitemlink(CobaltHerb)); + mes l("@@/80 @@", countitem(AlizarinHerb), getitemlink(AlizarinHerb)); + next; + select + l("Not now, thanks"), + l("To be honest, I have that with me!"); + + mes ""; + if (@menu == 1) + goto L_Main; + if (@menu == 2) { + if ( + countitem(Rose) < 55 || + countitem(Tulip) < 55 || + countitem(Blueberries) < 50 || + countitem(GrassSeeds) < 10 || + countitem(MauveHerb) < 80 || + countitem(GambogeHerb) < 80 || + countitem(CobaltHerb) < 80 || + countitem(AlizarinHerb) < 70 + ) goto L_Lying; + + inventoryplace Wreath, 1; + delitem Rose, 55; + delitem Tulip, 55; + delitem Blueberries, 50; + delitem GrassSeeds, 10; + delitem MauveHerb, 80; + delitem GambogeHerb, 80; + delitem CobaltHerb, 80; + delitem AlizarinHerb, 80; + getitem Wreath, 1; + getexp (210*BaseLevel), 120; + setq1 SQuest_Spring, 1; + mesn; + mesq l("Many thanks! At least he'll know he haven't impressed me yet!"); + next; + mesn; + mesq l("Here, take the reward as promised!"); + goto L_Main; + } + + close; + +L_Lying: + mesn; + mesq l("Please don't lie to me..."); + goto L_Main; + +OnInit: + .sex=G_FEMALE; + .distance=8; + end; + +} diff --git a/npc/014-5/events.txt b/npc/014-5/events.txt new file mode 100644 index 0000000..c6d6b67 --- /dev/null +++ b/npc/014-5/events.txt @@ -0,0 +1,200 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// St. Patrick Day +// Variables: +// #PATRICK_DAY +// When you spinned this +// #PATRICK_CTRL +// Controls rarity, lowering boost +// $@PATRICK_DAYMAX +// Last day in St. Patrick event + +014-5,47,53,0 script St. Patrick Gold Pot NPC_GOLDPOT,{ + function symbol{ + switch (getarg(0)) { + case 0: + mesn "%%t"; + break; + case 1: + mesn "%%g"; + break; + case 2: + mesn "%%H"; + break; + case 3: + mesn "%%G"; + break; + case 4: + mesn "%%Q"; + break; + case 5: + mesn "%%N"; + break; + case 6: + mesn "%%K"; + break; + default: + mesn "%%@"; + break; + } + } + + if ($EVENT$ != "Patrick" && !$@GM_OVERRIDE) { + channelmes("#world", "ERROR, ST. PATRICK GOLD POT: NOEVENT ERROR"); + disablenpc .name$; + atcommand("@kick "+strcharinfo(0)); + close; + } + if (#PATRICK_DAY == gettime(GETTIME_DAYOFMONTH)) { + mesn; + mesc l("You already claimed a reward today! Come back tomorrow, and remember to wear GREEN!"), 2; + close; + } + mesn; + mesc l("Lucky you, you found me! Do you want to SPIN THE WHEEL and gain GREAT rewards?"), 2; + mesc l("Remember that wearing green BOOSTS the rates at which good stuff will be found!"), 2; + mesc l("Only green from @@ will be taken in account.", getitemlink(GreenDye)); + next; + select + l("Spin it!"), + l("How does spinning works?"), + l("Don't spin it!"); + mes ""; + if (@menu == 3) + close; + + if (@menu == 2) { + mes ("%%0 - "+l("GP")); + mes ("%%1 - "+getitemlink(CasinoCoins)); + mes ("%%2 - "+getitemlink(Topaz)); + mes ("%%3 - "+getitemlink(StrangeCoin)); + mes ("%%4 - "+getitemlink(GoldOre)); + mes ("%%5 - "+getitemlink(GoldenGift)); + mes ("%%6 - "+getitemlink(GoldenApple)); + close; + } + + if (@menu == 1) { + // SPINNING! Set that you can't spin again. + #PATRICK_DAY=gettime(GETTIME_DAYOFMONTH); + + // Each green accessory raises boost gauge in 25. Full set raises in further 25. + // Hat, Shirt, Pants, Boots and Gloves. Max boost gauge from dye is 150 atm. + .@boost=0; + if (getequipcardid(EQI_HEAD_TOP, 0) == GreenDye) + .@boost+=25; + if (getequipcardid(EQI_HEAD_MID, 0) == GreenDye) + .@boost+=25; + if (getequipcardid(EQI_HEAD_LOW, 0) == GreenDye) + .@boost+=25; + if (getequipcardid(EQI_SHOES, 0) == GreenDye) + .@boost+=25; + if (getequipcardid(EQI_GARMENT, 0) == GreenDye) + .@boost+=25; + if (.@boost >= 125) + .@boost+=25; + + // Blv+Jlv gives a small boost. The "maximum" sum is 250, but we divide by 5 + // So this grants a further boost of 4~50 depending on level. + .@boost+=(BaseLevel+JobLevel)/5; + + // .@boost, right now, is a number from 0 to 200. + // Randomness factor can make that 20% higher, up to 240. + .@r=rand(0,20); + .@boost+=(.@boost*.@r/100); + // Then it may add or subtract 10 points from boost. Apply a cap to range 0~250. + .@boost+=rand(-10, 10); + + // Now, you can't get 5 Golden Apples straight, right? + // You lose boost as you get rewards. You lose boost the farther from + // event last day you are, too. + .@boost-=#PATRICK_CTRL; + .@boost-=($@PATRICK_DAYMAX-#PATRICK_DAY); + + // sanitize boost so it ranges from 0 to 250. + .@boost=max(0, .@boost); + + // REFERENCE: At release time, max levels ingame were about 60/40. + // That would give 10~34 (not green) to 160~214 (fully green) boost value. + mesc l("Spinning!"), 2; + + // .@v holds the reward you'll get + if (.@boost > 240) + .@v=6; + else if (.@boost > 200) + .@v=5; + else if (.@boost > 160) + .@v=4; + else if (.@boost > 120) + .@v=3; + else if (.@boost > 80) + .@v=2; + else if (.@boost > 40) + .@v=1; + else + .@v=0; + + // Tell players what they spinned, and lower rarity on next tries + symbol(.@v); + #PATRICK_CTRL+=.@v+(.@v/2); + + // Grant players the reward + switch (.@v) { + case 0: // GP + Zeny=Zeny+rand(10, .@boost+10); + break; + case 1: // Casino Coins + getitem CasinoCoins, max(1, .@boost/10); + Zeny=Zeny+rand(20, .@boost+10); + break; + case 2: // Topaz + getitem Topaz, 1; + Zeny=Zeny+rand(30, .@boost+10); + break; + case 3: // Strange Coins + getitem StrangeCoin, max(1, .@boost/10); + Zeny=Zeny+rand(40, .@boost+10); + break; + case 4: // Gold Ore + getitem GoldOre, 1; + Zeny=Zeny+rand(50, .@boost+10); + break; + case 5: // Golden Gift + getitem GoldenGift, 1; + Zeny=Zeny+rand(60, .@boost+10); + break; + case 6: // Golden Apple (OP) + getitem GoldenApple, 1; + Zeny=Zeny+rand(70, .@boost+10); + break; + } + next; + mesn; + mesc l("Did you like it? Find me tomorrow, until day @@, for another gift!", $@PATRICK_DAYMAX), 2; + close; + } + // Should not reach here + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + + if ($EVENT$ != "Patrick") { + disablenpc(.name$); + end; + } +OnClock0000: +OnForcedMove: + if ($EVENT$ == "Patrick") { + setarray .@vx, 134, 162, 108, 105, 67, 191, 168, 170, 191, 188, 160, 145, 196, 120; + setarray .@vy, 36, 61, 45, 96, 76, 74, 72, 111, 113, 129, 125, 119, 143, 104; + .@r=rand(0,getarraysize(.@vx)-1); + movenpc .name$, .@vx[.@r], .@vy[.@r]; + debugmes "Warping to %d,%d (seed %d)", .@vx[.@r], .@vy[.@r], .@r; + } + end; +} + diff --git a/npc/014-5/sagratha.txt b/npc/014-5/sagratha.txt new file mode 100644 index 0000000..36614af --- /dev/null +++ b/npc/014-5/sagratha.txt @@ -0,0 +1,191 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Sagratha's House Door +// +// getq(HurnscaldQuest_Sagratha) +// Field 1 +// 0 - Sagratha is home and annyoed +// 1 - Wyara sent player saggy way +// 2 - Rumor Confirmed, check again +// Field 2 +// RESERVED - INSTANCE ID +// Field 3 +// During stage 1 and 2: Holds if player looked everywhere +// During stage 3 and 4: Control puzzles +// During stage 5 boss fight + +// Check instances and rebuild if needed +// Returns map name +// SaggyInstCheck( {house=true} ) +function script SaggyInstCheck { + .@house=getarg(0, true); + + .@q2=getq2(HurnscaldQuest_Sagratha); + // Map name limit: 4 chars (sgt1) + .@mapn$="sgt1@"+getcharid(0); + .@map2$="sgt2@"+getcharid(0); + if (!(isinstance(.@q2) && .@q2 != 0 && instanceowner(.@q2) == getcharid(3))) { + .@inst = instance_create("Sagratha House "+getcharid(0), getcharid(3), IOT_CHAR); + instance_attachmap("014-5-1", .@inst, false, .@mapn$); + //instance_attachmap("015-8", .@inst, false, .@mapn$); + instance_attachmap("015-8-1", .@inst, false, .@map2$); + // Instance lasts one hour + instance_set_timeout(3600, 3600, .@inst); + instance_init(.@inst); + setq2 HurnscaldQuest_Sagratha, .@inst; + } + + // It broke + if (getmapinfo(MAPINFO_SIZE_X, .@mapn$) <= 0) { + setq2 HurnscaldQuest_Sagratha, 0; + // Infinite Loop? + return callfunc("SaggyInstCheck", .@house); + } + + if (.@house) { + return .@mapn$; + } else { + return .@map2$; + } +} + +014-5,122,137,0 script Sagratha Door NPC_HIDDEN,0,0,{ + end; + +L_Magicless: + npctalk3 l("A light magic barrier prevents you from entering."); + dispbottom l("If I only knew some magic..."); + end; + +L_Lockpick: + mesn; + mesc l("The door is locked!"); + select + l("Knock on the door?"), + l("Attempt to lockpick?"), + l("Leave it alone?"); + mes ""; + + if (@menu == 1) { + /* + if (getq(HurnscaldQuest_InjuriedMouboo) == 2)) { + mesn strcharinfo(0); + mesc l("Nobody answers."); + mes ""; + } + */ + mesn l("Sagratha"); + mesq l("No, I don't need monster repellent nor anything! Go away!"); + close; + } else if (@menu == 2) { + if (LockPicking(4, 5)) { + getexp BaseLevel*4, 5; + mesn; + mesc l("*click*"); + next; + mesn l("Sagratha"); + mesq l("Who is there? Are they trying to break my lock again?!"); + next; + select + l("Run away?"), + l("Stay there?"); + mes ""; + + if (@menu == 2) { + mesn l("Sagratha"); + mesq l("Oh my Jesusalva - You broke my lock! Why?!"); + next; + select + l("I need to talk with you!"), + l("No reason."); + mes ""; + if (@menu == 1) { + mesn l("Sagratha"); + // I don't take strangers as students. + mesq l("No, I don't teach magic to strangers. No, I'm not interested in @@ affairs.", get_race()); + next; + mesn l("Sagratha"); + mesq l("Yes, I know when next Alliance's Council meeting will be. No, I don't need anything."); + next; + mesn l("Sagratha"); + mesq l("Now begone."); + } else { + mesn l("Sagratha"); + mesq l("What?! Do my house look like the place to you pratice your thief skills?!"); + mesq col(l("DIE!"), 1); + specialeffect(312, SELF, getcharid(3)); + percentheal -40, -100; + } + } + + } else { + mesn l("Sagratha"); + mesq l("Who is there? Are they trying to break my lock again?!"); + mesq col(l("DIE!"), 1); + specialeffect(312, SELF, getcharid(3)); + percentheal -40, -100; + } + } + close; + +L_Open: + mesn; + mesc l("The door is locked!"); + select + l("Knock on the door?"), + l("Attempt to lockpick?"), + l("Say that Mouboos are Cute?"), + l("Leave it alone?"); + mes ""; + if (@menu == 4) + close; + if (@menu == 3) { + mesc l("Surprisingly, nothing happens."); + next; + mesc l("A close inspection suggests the lock was busted. You approach to inspect."); + next; + } + mesc l("*CREAK*"); + mesc l("The pressure you did made the door burst open. It is unusually quiet inside..."); + next; + //enable_items(); + //mesc l("WARNING: Save your game now."), 3; + //mesc l("WARNING: Change your equipment now."), 3; + //next; + //disable_items(); + /* + if (has_instance("014-5-1") == "") { + .@inst = instance_create("Sagratha House", getcharid(3), IOT_CHAR); + instance_attachmap("014-5-1", .@inst); + // 20 minutes (1200s) inside, or 5 minutes (300s) outside + instance_set_timeout(1200, 30, .@inst); + instance_init(.@inst); + } + warp "014-5-1", 33, 44; + //addmapmask instance_mapname("014-5-1"), 1; + changemusic "014-5-1", "eric_matyas_ghouls.ogg"; + */ + .@mapn$=SaggyInstCheck(); + warp .@mapn$, 33, 44; + changemusic .@mapn$, "eric_matyas_ghouls.ogg"; + closeclientdialog; + close; + +L_Welcome: + warp "014-5-1", 33, 44; + dispbottom l("Mouboos are cute"); + end; + +OnTouch: + .@q=getq(HurnscaldQuest_Sagratha); + if (!MAGIC_LVL) goto L_Magicless; + if (.@q >= 1 && .@q < 6) goto L_Open; + if (.@q >= 6) goto L_Welcome; + + // No special condition + goto L_Lockpick; + end; + +} diff --git a/npc/014-5/sign.txt b/npc/014-5/sign.txt new file mode 100644 index 0000000..c133544 --- /dev/null +++ b/npc/014-5/sign.txt @@ -0,0 +1,24 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Random Sign nobody bothers reading. + +014-5,179,29,0 script Sign#014517929 NPC_SWORDS_SIGN,{ + mesc l("Welcome to the Greenlands, the place cursed by the Monster King to be ice forever, and now known as Icelands."); + next; + mesc l("It's advised to come ready for the cold, the hungry beasts, and other dangers snow may bring."); + if (getgmlevel()) { + mesc l("Blame @@, missing map between 014-5 and 019-1, solely to make trip longer.", any("jesusalva", "saulc", "lawncable", "mishana", "dragonstar", "4144", "everyone", "developers", "the Monster King", strcharinfo(0))), 1; + } + /* + next; + mesc l("WARNING: Path temporaly closed due strong snowstorm."), 3; + */ + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} diff --git a/npc/015-1/_import.txt b/npc/015-1/_import.txt new file mode 100644 index 0000000..69c3370 --- /dev/null +++ b/npc/015-1/_import.txt @@ -0,0 +1,6 @@ +// Map 015-1: Woodland Mines +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-1/_mobs.txt", +"npc/015-1/_warps.txt", +"npc/015-1/boss.txt", +"npc/015-1/treasure.txt", diff --git a/npc/015-1/_mobs.txt b/npc/015-1/_mobs.txt new file mode 100644 index 0000000..bb8deb2 --- /dev/null +++ b/npc/015-1/_mobs.txt @@ -0,0 +1,16 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-1: Woodland Mines mobs +015-1,121,105,119,97 monster Bif 1058,14,45000,45000 +015-1,130,81,119,97 monster Cave Bat 1039,40,60000,60000 +015-1,120,136,91,81 monster Small Amethyst Bif 1110,5,45000,45000 +015-1,80,175,142,39 monster Copper Slime 1088,5,20000,45000 +015-1,167,36,11,13 monster Fire Skull 1193,1,35000,60000 +015-1,139,32,57,31 monster Archant 1026,4,30000,30000 +015-1,100,97,26,27 monster Black Slime 1178,4,40000,30000 +015-1,73,111,119,97 monster Red Slime 1092,75,20000,20000 +015-1,147,91,119,97 monster Yellow Slime 1091,50,30000,20000 +015-1,41,136,37,49 monster Black Scorpion 1074,4,30000,25000 +015-1,88,36,142,39 monster Copper Slime 1088,3,45000,40000 +015-1,99,73,3,3 monster Green Slime 1085,2,45000,60000 +015-1,156,144,44,41 monster Black Scorpion 1074,4,30000,25000 +015-1,112,24,39,37 monster Black Scorpion 1074,4,20000,30000 diff --git a/npc/015-1/_warps.txt b/npc/015-1/_warps.txt new file mode 100644 index 0000000..fef1a8b --- /dev/null +++ b/npc/015-1/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-1: Woodland Mines warps +015-1,100,133,0 warp #015-1_100_133 1,0,014-1,79,58 +015-1,90,49,0 warp #015-1_90_49 0,0,015-4,50,38 +015-1,177,56,0 warp #015-1_177_56 0,0,015-4,76,19 diff --git a/npc/015-1/boss.txt b/npc/015-1/boss.txt new file mode 100644 index 0000000..1591e05 --- /dev/null +++ b/npc/015-1/boss.txt @@ -0,0 +1,28 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Spider Queen Boss + +015-1,0,0,0 script #BossCtrl_015-1 NPC_HIDDEN,{ + end; + +// Respawn every half hour +OnTimer1800000: + stopnpctimer; +OnInit: + areamonster "015-1", 20, 20, getmapinfo(MAPINFO_SIZE_X, "015-1"), getmapinfo(MAPINFO_SIZE_Y, "015-1"), strmobinfo(1, SpiderQueen), SpiderQueen, 1, "#BossCtrl_015-1::OnBossDeath"; + end; + +OnBossDeath: + initnpctimer; + .@party=getcharid(1); + if (.@party > 0) { + mapannounce getmap(), "Boss deafeated by Party: " + getpartyname(.@party), bc_all; + } else { + mapannounce getmap(), "Boss deafeated by: " + strcharinfo(0), bc_all; + } + fix_mobkill(SpiderQueen); + end; + +} diff --git a/npc/015-1/treasure.txt b/npc/015-1/treasure.txt new file mode 100644 index 0000000..6812d3b --- /dev/null +++ b/npc/015-1/treasure.txt @@ -0,0 +1,8 @@ +// TMW2 Script + +// (Random) Treasure Chest +// Authored by Jesusalva +// Regenerates every 6 hours + +015-1,0,0,0 duplicate(#chest_00710) #chest_01510 NPC_CHEST + diff --git a/npc/015-2/_import.txt b/npc/015-2/_import.txt new file mode 100644 index 0000000..29bd5d5 --- /dev/null +++ b/npc/015-2/_import.txt @@ -0,0 +1,8 @@ +// Map 015-2: Bandit Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-2/_mobs.txt", +"npc/015-2/_warps.txt", +"npc/015-2/ben.txt", +"npc/015-2/contrabandist.txt", +"npc/015-2/guard.txt", +"npc/015-2/lordcave.txt", diff --git a/npc/015-2/_mobs.txt b/npc/015-2/_mobs.txt new file mode 100644 index 0000000..876f5a4 --- /dev/null +++ b/npc/015-2/_mobs.txt @@ -0,0 +1,51 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-2: Bandit Cave mobs +015-2,152,72,141,63 monster Silk Worm 1034,9,30000,100000 +015-2,263,38,15,17 monster Bandit 1024,1,30000,30000 +015-2,262,114,19,19 monster Bandit 1024,1,30000,30000 +015-2,132,119,8,10 monster Bandit 1024,1,30000,30000 +015-2,170,66,8,10 monster Bandit 1024,1,30000,30000 +015-2,67,70,8,10 monster Bandit 1024,1,30000,30000 +015-2,196,107,8,10 monster Bandit 1024,1,30000,30000 +015-2,196,29,8,10 monster Bandit 1024,1,30000,30000 +015-2,36,37,8,10 monster Bandit 1024,1,30000,30000 +015-2,112,40,22,24 monster Bandit 1024,2,30000,60000 +015-2,31,99,8,10 monster Bandit 1024,1,30000,30000 +015-2,223,250,61,32 monster Bat 1039,5,30000,100000 +015-2,270,175,13,12 monster Bat 1039,3,30000,100000 +015-2,257,77,2,63 monster Bat 1039,6,30000,100000 +015-2,192,77,2,63 monster Bat 1039,9,30000,100000 +015-2,167,110,43,6 monster Bat 1039,9,30000,100000 +015-2,75,70,52,48 monster Bat 1039,21,30000,100000 +015-2,78,195,64,48 monster Bat 1039,9,30000,100000 +015-2,168,109,18,18 monster Snake 1122,1,30000,100000 +015-2,199,76,18,18 monster Snake 1122,2,30000,100000 +015-2,88,163,14,13 monster Snake 1122,1,30000,100000 +015-2,48,113,17,9 monster Snake 1122,2,30000,100000 +015-2,177,94,10,14 monster Angry Red Scorpion 1130,4,30000,100000 +015-2,194,49,10,14 monster Angry Red Scorpion 1130,2,30000,100000 +015-2,51,115,23,12 monster Angry Red Scorpion 1130,2,30000,100000 +015-2,63,169,6,8 monster Angry Red Scorpion 1130,2,30000,100000 +015-2,100,226,14,7 monster Snake 1122,2,30000,100000 +015-2,260,44,26,8 monster Snake 1122,3,30000,100000 +015-2,272,48,10,14 monster Angry Red Scorpion 1130,2,30000,100000 +015-2,95,78,15,12 monster Yellow Slime 1091,3,30000,100000 +015-2,59,36,9,12 monster Yellow Slime 1091,3,30000,100000 +015-2,26,69,9,12 monster Yellow Slime 1091,3,30000,100000 +015-2,61,101,7,5 monster Yellow Slime 1091,3,30000,100000 +015-2,50,61,4,6 monster Yellow Slime 1091,2,30000,100000 +015-2,128,39,4,6 monster Yellow Slime 1091,2,30000,100000 +015-2,41,178,4,6 monster Yellow Slime 1091,1,30000,100000 +015-2,70,229,4,6 monster Yellow Slime 1091,1,30000,100000 +015-2,20,204,4,6 monster Yellow Slime 1091,1,30000,100000 +015-2,114,190,4,6 monster Yellow Slime 1091,3,30000,100000 +015-2,137,205,4,6 monster Yellow Slime 1091,2,30000,100000 +015-2,79,220,14,7 monster Cave Snake 1035,3,30000,100000 +015-2,115,210,14,7 monster Cave Snake 1035,3,30000,100000 +015-2,41,193,15,15 monster Cave Snake 1035,3,30000,100000 +015-2,70,143,15,15 monster Cave Snake 1035,3,30000,100000 +015-2,97,183,14,7 monster Cave Snake 1035,3,30000,100000 +015-2,172,252,4,6 monster Cave Maggot 1027,4,30000,100000 +015-2,263,236,8,2 monster Cave Maggot 1027,3,30000,100000 +015-2,143,128,140,125 monster Small Diamond Biff 1107,8,30000,100000 +015-2,93,172,6,8 monster Angry Red Scorpion 1130,2,30000,100000 diff --git a/npc/015-2/_warps.txt b/npc/015-2/_warps.txt new file mode 100644 index 0000000..1a63c34 --- /dev/null +++ b/npc/015-2/_warps.txt @@ -0,0 +1,24 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-2: Bandit Cave warps +015-2,51,75,0 warp #015-2_51_75 0,0,015-2,260,127 +015-2,61,71,0 warp #015-2_61_71 0,0,015-2,270,124 +015-2,270,125,0 warp #015-2_270_125 0,0,015-2,61,72 +015-2,260,128,0 warp #015-2_260_128 0,0,015-2,51,76 +015-2,145,118,0 warp #015-2_145_118 0,0,015-2,47,119 +015-2,196,26,0 warp #015-2_196_26 0,0,015-2,261,46 +015-2,261,47,0 warp #015-2_261_47 0,0,015-2,196,27 +015-2,271,52,0 warp #015-2_271_52 0,0,015-2,98,31 +015-2,98,30,0 warp #015-2_98_30 0,0,015-2,271,51 +015-2,47,120,0 warp #015-2_47_120 0,0,015-2,145,117 +015-2,49,101,0 warp #015-2_49_101 0,0,014-3,120,68 +015-2,24,206,0 warp #015-2_24_206 0,0,015-2,122,212 +015-2,47,185,0 warp #015-2_47_185 0,0,015-2,113,183 +015-2,97,169,0 warp #015-2_97_169 0,0,015-2,68,234 +015-2,113,182,0 warp #015-2_113_182 0,0,015-2,47,184 +015-2,122,211,0 warp #015-2_122_211 0,0,015-2,24,205 +015-2,68,235,0 warp #015-2_68_235 0,0,015-2,97,168 +015-2,251,278,0 warp #015-2_251_278 0,0,015-2,94,226 +015-2,266,182,0 warp #015-2_266_182 0,0,015-2,264,237 +015-2,174,256,0 warp #015-2_174_256 0,0,015-2,249,233 +015-2,249,232,0 warp #015-2_249_232 0,0,015-2,174,255 +015-2,264,236,0 warp #015-2_264_236 0,0,015-2,266,181 diff --git a/npc/015-2/ben.txt b/npc/015-2/ben.txt new file mode 100644 index 0000000..4f81e19 --- /dev/null +++ b/npc/015-2/ben.txt @@ -0,0 +1,162 @@ +// TMW2 Scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Ben Parkison invites players to the TMW2 Project +// Part of the THIEF/MERCHANT branches + +015-2,269,172,0 script Ben Parkison NPC_MOUBOO,{ + if (JobLevel > 20 && MERC_RANK == 0) goto L_Quest; + +L_Intro: + mesn; + mesq l("Hey newbie... I am a dangerous bandit."); + next; + mesq l("Yes, I am a mouboo. Why? Can't a mouboo be a dangerous bandit?!"); + next; + mesq l("Fear not! You can check our [@@https://wiki.moubootaurlegends.org|Wiki@@] to find that and other awesome stuff!"); + mesc l("(Even bandits doesn't breaks the rules. To see the rules, use ##B@rules##b.)"); + next; + mesq l("You can even join the project there. Contributors are greatly appreciated! %%N"); + close; + +L_Quest: + if (THIEF_RANK == 0) + goto L_Recruit; + mesn; + mesq l("Hello there, @@ the @@.", strcharinfo(0), thiefrank()); + if (THIEF_RANK == 5) goto L_Menu; + mesq l("I see you have collected some experience. Let me try to rank you up!"); + next; + if (THIEF_EXP > (THIEF_RANK*2)**5) { + THIEF_EXP-=(THIEF_RANK*2)**5; + THIEF_EXP=(THIEF_EXP*8/10); // Penalty for postponing rank up (you keep 80%) + THIEF_RANK+=1; + mesn; + mesq l("Congrats! You rank up! You are now a(n) @@!", thiefrank()); + if (THIEF_RANK == 2) goto L_Rank2; + if (THIEF_RANK == 3) goto L_Rank3; + if (THIEF_RANK == 4) goto L_Rank4; + } else { + mesn; + mesq l("Well, you need more experience. Keep trying!"); + mesc l("You may obtain Thief Exp by using @@.", getitemlink(Lockpicks)); + } + goto L_Menu; + close; + +L_Recruit: + mesn; + mesq l("You seem to be doing some money. Would you consider fighting for your own greed, or even be a Robin-Hood-of-sorts, stealing from the rich?"); + //mesc l("Notice: If you join the ##BBandits Guild##b now, you WON'T BE ABLE to be a merchant police later!"), 1; + mesc l("Note: You won't be able to leave the class later."), 1; + next; + if (askyesno() == ASK_YES) { + THIEF_EXP=0; + THIEF_RANK=1; + mes ""; + mesn; + mesq l("Welcome to the ##BThieves Guild##b! Follow those with higher rank than you, and happy stealing!"); + mesc l("You've learned how to use @@. Simple locks can now be broken.", getitemlink(Lockpicks)); + } else { + goto L_Intro; + } + close; + +// Learn STEAL +L_Rank2: + skill(TF_STEAL,1,0); + next; + mesn; + mesq l("Now, you'll learn a thief trick! This one allows you to steal drops from monsters! Ka-pow! Amazing!"); + next; + mesn; + mesq l("You must be close to it, and stealing won't change drops! If you fail, just try again! Good luck!"); + close; + +// Learn INCCARRY +L_Rank3: + skill(ALL_INCCARRY,1,0); + next; + mesn; + mesq l("Now, you'll learn a thief trick! What sort of thief loots so much that they get overweight penalty? That's not cool!"); + next; + mesn; + mesq l("You now gained two extra kilograms to your weight quota! Ka-pow, that's fantastic! Good luck!"); + close; + +// Learn OVERCHARGE +L_Rank4: + skill(MC_OVERCHARGE,1,0); + next; + mesn; + mesq l("Now, you'll learn an important trick! Stealing is nice, but scamming is even better!"); + mesc l("We must blame Saulc!"); + next; + mesn; + mesq l("This new skill will allow you to pinch every gold piece from a NPC when selling stuff! Ka-pow, now you can steal items and resell them for moar GP! AMAZING!"); + close; + +// Allow to level up thief skills +L_Menu: + next; + mesn; + mesq l("Do you want me to teach you how to improve an existing skill with MAGIC? There are no better mages than Mouboos!"); + mesc l("You also need @@/@@ Mob Points to improve thief skills.", 1000, format_number(Mobpt)); + if (Mobpt < 1000) + close; + next; + /* + menuint + rif(getskilllv(TF_STEAL), l("Stealing")), TF_STEAL, + rif(getskilllv(ALL_INCCARRY), l("Stealing")), ALL_INCCARRY, + rif(getskilllv(MC_OVERCHARGE), l("Stealing")), MC_OVERCHARGE, + l("None at the moment."), 0; + + // Handle result + if (@menuret) { + if (!learn_magic(@menuret)) + mesc l("You do not meet all requisites for this skill."), 1; + } else { + close; + } + */ + + select + rif(getskilllv(TF_STEAL), l("Improve Stealing to level ")+(getskilllv(TF_STEAL)+1)), + rif(getskilllv(ALL_INCCARRY), l("Improve Max Weight to level ")+(getskilllv(ALL_INCCARRY)+1)), + rif(getskilllv(MC_OVERCHARGE), l("Improve Barter to level ")+(getskilllv(MC_OVERCHARGE)+1)), + l("None at the moment."); + mes ""; + // BlueCoral, {CrocClaw, OceanCrocClaw: Empty Box}, PlushroomBox + switch (@menu) { + case 1: + if (!mlearn(TF_STEAL, 10, 1, BlueCoral, 6*getskilllv(TF_STEAL))) + mesc l("You do not meet all requisites for this skill."), 1; + else + Mobpt-=1000; + break; + case 2: + if (!mlearn(ALL_INCCARRY, 10, 1, BlueCoral, 9*getskilllv(ALL_INCCARRY))) + mesc l("You do not meet all requisites for this skill."), 1; + else + Mobpt-=1000; + break; + case 3: + if (!mlearn(MC_OVERCHARGE, 8, 1, PlushroomBox, 2*getskilllv(MC_OVERCHARGE))) // Max 21% discount (out of 24%) + mesc l("You do not meet all requisites for this skill."), 1; + else + Mobpt-=1000; + break; + default: + close; + } + goto L_Menu; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/015-2/contrabandist.txt b/npc/015-2/contrabandist.txt new file mode 100644 index 0000000..41f4a3c --- /dev/null +++ b/npc/015-2/contrabandist.txt @@ -0,0 +1,115 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Contraband of foreing goods. + +015-2,166,250,0 script #Contraband NPC_NO_SPRITE,{ + + // Store not available + if (!.active) + end; + + mesn; + mesc l("I have a few items for sale..."); + next; + menuint + rif(.CurrentValue & 1, l("Treasure Map")), 1, + rif(.CurrentValue & 2, l("Lock Picks")), 2, + rif(.CurrentValue & 4, l("Return Potion")), 4, + rif(.CurrentValue & 8, l("Golden Pearl Ring")), 8, + rif(.CurrentValue & 16, l("Mysterious Bottle")), 16, + rif(.CurrentValue & 32, l("Ancient Blueprint")), 32, + rif(.CurrentValue & 64, l("Arcmage Boxset")), 64, + rif($ARKIM_ST > 7500 && .CurrentValue & 128, l("Dark Desert Mushroom")), 128, + l("Nothing"), 0; + mes ""; + + // Fallback + if (!@menuret) + close; + + switch (@menuret) { + case 1: + .@price=600; + .@prize=TreasureMap; + break; + case 2: + .@price=1000; + .@prize=Lockpicks; + break; + case 4: + .@price=2200; + .@prize=ReturnPotion; + break; + case 8: + .@price=500000; + .@prize=GoldenPearlRing; + break; + case 16: + .@price=4000; + .@prize=MysteriousBottle; + break; + case 32: + .@price=15000; + .@prize=AncientBlueprint; + break; + case 64: + .@price=21000; + .@prize=ArcmageBoxset; + break; + case 128: + .@price=.DDMPrice; + .@prize=DarkDesertMushroom; + break; + default: + .@price=999999999; + .@prize=Acorn; + break; + } + mesc l("Only @@ GP.", .@price); + if (Zeny < .@price) + close; + if (askyesno() == ASK_YES) { + if (!(.CurrentValue & @menuret)) { + mesc l("Out of Stock."), 1; + close; + } + .CurrentValue=.CurrentValue^@menuret; + Zeny-=.@price; + getitem .@prize, 1; + mesc l("Item sold!"), 2; + } + close; + +// Load +OnMinute21: +OnMinute46: + setnpcdisplay .name$, NPC_KOGA; + .active=1; + + .CurrentValue=rand2(1,255); + .DDMPrice=max(30000,50000-($ARKIM_ST/2)+(7500/2)); + end; + +// Unload +OnMinute23: +OnMinute48: + setnpcdisplay .name$, NPC_NO_SPRITE; + .active=0; + end; + +OnInit: + .active=0; + .sex = G_OTHER; + .distance = 12; + + /* + if (debug) { + setnpcdisplay .name$, NPC_KOGA; + .active=1; + } + */ + end; + +} diff --git a/npc/015-2/guard.txt b/npc/015-2/guard.txt new file mode 100644 index 0000000..28d505e --- /dev/null +++ b/npc/015-2/guard.txt @@ -0,0 +1,71 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Part of Helena's quest +// Gives spoilers about what Jesusalva plans in doing with Bryant + +015-2,28,199,0 script Helena's Teammate NPC_ORC,{ + .@q=getq(HurnscaldQuest_Bandits); + if (.@q == 5) goto L_SilverKey; + if (.@q == 6) goto L_BanditLord; + mesn; + mesq lg("Welcome."); + mesq l("This small island is a good place to heal our wounds, and the monsters here are not threating."); + close; + +L_SilverKey: + mesn; + mesq l("Don't say anything, I can smell the scent of Helena's hair on you."); + next; + inventoryplace SilverKey, 1, PiberriesInfusion, 3; + getitem SilverKey, 1; + getitem PiberriesInfusion, 3; + setq HurnscaldQuest_Bandits, 6; + mesn; + mesq l("I guess she sent you to kill another Bandit Lord, right? Ok, I'll give you the key for his room."); + next; + mesn; + mesq l("It is pretty close to here. I advise you to use a good sword, and heal yourself often."); + next; + mesn; + mesq l("I will give you 3 @@. Use them on this fight, or you're doomed to fail.", getitemlink(PiberriesInfusion)); + close; + +L_BanditLord: + mesn; + mesq l("What are you waiting for? Go kill the bandit lord."); + next; + mesn strcharinfo(0); + select + l("I'm going, don't worry."), + l("There was nobody on the Bandit Lord's room."); + if (@menu == 1) + close; + mes ""; + mesn; + mesq l("...Have you ever gone there yet?"); + next; + mesn; + mesq l("This is not The Mana World. My name is not Bryant."); + next; + mesn; + mesq l("In fact, Bryant is my brother, and he lives in a cave, and he probably have a high level quest too, but that's not important now."); + next; + mesn; + mesq l("Go kill the Bandit Lord!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FairyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, ForestArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansChaps); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 24); + setunitdata(.@npcId, UDT_HAIRCOLOR, 1); + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/015-2/lordcave.txt b/npc/015-2/lordcave.txt new file mode 100644 index 0000000..6303b2c --- /dev/null +++ b/npc/015-2/lordcave.txt @@ -0,0 +1,27 @@ +// TMW2 Script +// Author: +// Jesusalva + +015-2,94,225,0 script #BanditLordDen NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (countitem(SilverKey) >= 1) goto L_Summon; + dispbottom l("This passage seems sealed. If I only had a @@...", getitemlink(SilverKey)); + end; + +L_Summon: + .@q=getq(HurnscaldQuest_Bandits); + if (mobcount("015-2", "#BanditLordDen::OnLordDeath") == 0 && .@q == 6) + monster "015-2",260,250,"Bandit Lord",BanditLord,1, "#BanditLordDen::OnLordDeath"; + slide 251, 277; + end; + +OnLordDeath: + .@q=getq(HurnscaldQuest_Bandits); + if (.@q == 6 && !ispcdead()) { + setq HurnscaldQuest_Bandits, 7; + dispbottom l("Phew! The Bandit Lord was killed."); + } + end; +} diff --git a/npc/015-3-1/_import.txt b/npc/015-3-1/_import.txt new file mode 100644 index 0000000..b49c4f0 --- /dev/null +++ b/npc/015-3-1/_import.txt @@ -0,0 +1,5 @@ +// Map 015-3-1: Pirate Caves B1F +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-3-1/_mobs.txt", +"npc/015-3-1/_warps.txt", +"npc/015-3-1/pablin.txt", diff --git a/npc/015-3-1/_mobs.txt b/npc/015-3-1/_mobs.txt new file mode 100644 index 0000000..b527705 --- /dev/null +++ b/npc/015-3-1/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3-1: Pirate Caves B1F mobs +015-3-1,43,72,3,4 monster Thug 1442,2,100000,30000 +015-3-1,41,73,3,4 monster Swashbuckler 1443,1,100000,30000 +015-3-1,46,74,3,4 monster Grenadier 1444,1,100000,30000 +015-3-1,28,69,4,1 monster Thug 1442,2,100000,30000 +015-3-1,71,63,3,4 monster Thug 1442,2,100000,30000 +015-3-1,39,39,3,1 monster Swashbuckler 1443,2,100000,30000 +015-3-1,71,63,3,4 monster Grenadier 1444,1,100000,30000 +015-3-1,64,49,6,4 monster Swashbuckler 1443,1,100000,30000 +015-3-1,29,92,6,4 monster Swashbuckler 1443,1,100000,30000 +015-3-1,33,50,8,2 monster Thug 1442,2,100000,30000 diff --git a/npc/015-3-1/_warps.txt b/npc/015-3-1/_warps.txt new file mode 100644 index 0000000..7683a63 --- /dev/null +++ b/npc/015-3-1/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3-1: Pirate Caves B1F warps +015-3-1,49,80,0 warp #015-3-1_49_80 0,0,015-3-2,49,83 +015-3-1,31,73,0 warp #015-3-1_31_73 0,0,015-3-2,31,70 +015-3-1,49,56,0 warp #015-3-1_49_56 0,0,015-3-2,49,51 +015-3-1,90,64,0 warp #015-3-1_90_64 0,0,015-3-2,90,68 +015-3-1,80,22,0 warp #015-3-1_80_22 0,0,015-3,138,196 +015-3-1,77,40,0 warp #015-3-1_77_40 0,0,015-3-2,78,34 diff --git a/npc/015-3-1/pablin.txt b/npc/015-3-1/pablin.txt new file mode 100644 index 0000000..33debb9 --- /dev/null +++ b/npc/015-3-1/pablin.txt @@ -0,0 +1,29 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// A retired pirate + +015-3-1,83,39,0 script Pablin NPC_OLDPIRATE,{ + .@q = getq(HurnscaldQuest_PirateCave); + if (.@q > 7) goto L_Post; + mesn; + mesq l("Hey, you! What are you doing here?!"); + next; + mesn; + mesq l("These are dangerous caves since Marley and her gang made it a hideout."); + next; + mesn; + mesq l("I won't stop you from going inside, but be careful, you hear me?"); + close; + +L_Post: + mesn; + mesq l("Marley is furious that someone stole her treasure... You wouldn't happen to know anything about this, would you...?"); + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/015-3-2/_import.txt b/npc/015-3-2/_import.txt new file mode 100644 index 0000000..cc4a4e5 --- /dev/null +++ b/npc/015-3-2/_import.txt @@ -0,0 +1,4 @@ +// Map 015-3-2: Pirate Caves B2F +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-3-2/_mobs.txt", +"npc/015-3-2/_warps.txt", diff --git a/npc/015-3-2/_mobs.txt b/npc/015-3-2/_mobs.txt new file mode 100644 index 0000000..61612b7 --- /dev/null +++ b/npc/015-3-2/_mobs.txt @@ -0,0 +1,14 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3-2: Pirate Caves B2F mobs +015-3-2,35,62,2,10 monster Grenadier 1444,1,100000,30000 +015-3-2,37,61,2,10 monster Swashbuckler 1443,2,100000,30000 +015-3-2,60,54,6,3 monster Thug 1442,2,100000,30000 +015-3-2,39,85,6,3 monster Thug 1442,2,100000,30000 +015-3-2,39,53,3,11 monster Thug 1442,2,100000,30000 +015-3-2,29,83,5,3 monster Swashbuckler 1443,2,100000,30000 +015-3-2,64,51,6,4 monster Swashbuckler 1443,2,100000,30000 +015-3-2,61,51,6,2 monster Grenadier 1444,1,100000,30000 +015-3-2,31,90,6,2 monster Grenadier 1444,1,100000,30000 +015-3-2,97,47,3,11 monster Thug 1442,2,100000,30000 +015-3-2,90,57,2,10 monster Swashbuckler 1443,2,100000,30000 +015-3-2,83,40,9,5 monster Grenadier 1444,1,100000,30000 diff --git a/npc/015-3-2/_warps.txt b/npc/015-3-2/_warps.txt new file mode 100644 index 0000000..1a1878e --- /dev/null +++ b/npc/015-3-2/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3-2: Pirate Caves B2F warps +015-3-2,49,81,0 warp #015-3-2_49_81 0,0,015-3-1,49,76 +015-3-2,31,72,0 warp #015-3-2_31_72 0,0,015-3-1,31,74 +015-3-2,49,53,0 warp #015-3-2_49_53 0,0,015-3-1,49,57 +015-3-2,90,70,0 warp #015-3-2_90_70 0,0,015-3-1,90,66 +015-3-2,24,64,0 warp #015-3-2_24_64 0,0,015-3-3,34,67 +015-3-2,78,32,0 warp #015-3-2_78_32 0,0,015-3-1,77,38 diff --git a/npc/015-3-3/_import.txt b/npc/015-3-3/_import.txt new file mode 100644 index 0000000..365dfee --- /dev/null +++ b/npc/015-3-3/_import.txt @@ -0,0 +1,5 @@ +// Map 015-3-3: Pirate Den +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-3-3/_mobs.txt", +"npc/015-3-3/_warps.txt", +"npc/015-3-3/boss.txt", diff --git a/npc/015-3-3/_mobs.txt b/npc/015-3-3/_mobs.txt new file mode 100644 index 0000000..ab1dbe6 --- /dev/null +++ b/npc/015-3-3/_mobs.txt @@ -0,0 +1,16 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3-3: Pirate Den mobs +015-3-3,50,67,4,3 monster Grenadier 1444,1,100000,30000 +015-3-3,47,72,8,3 monster Swashbuckler 1443,2,100000,30000 +015-3-3,53,74,5,2 monster Thug 1442,2,100000,30000 +015-3-3,50,90,3,2 monster Grenadier 1444,1,100000,30000 +015-3-3,46,84,6,2 monster Thug 1442,2,100000,30000 +015-3-3,40,89,6,3 monster Swashbuckler 1443,2,100000,30000 +015-3-3,61,37,4,3 monster Grenadier 1444,1,100000,30000 +015-3-3,66,40,5,1 monster Thug 1442,2,100000,30000 +015-3-3,71,36,5,2 monster Swashbuckler 1443,2,100000,30000 +015-3-3,93,40,4,4 monster Grenadier 1444,1,100000,30000 +015-3-3,87,37,5,3 monster Thug 1442,2,100000,30000 +015-3-3,102,39,4,2 monster Swashbuckler 1443,2,100000,30000 +015-3-3,46,52,5,7 monster Thug 1442,2,100000,30000 +015-3-3,48,44,5,7 monster Swashbuckler 1443,2,100000,30000 diff --git a/npc/015-3-3/_warps.txt b/npc/015-3-3/_warps.txt new file mode 100644 index 0000000..5d6a177 --- /dev/null +++ b/npc/015-3-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3-3: Pirate Den warps +015-3-3,34,65,0 warp #015-3-3_34_65 0,0,015-3-2,24,62 diff --git a/npc/015-3-3/boss.txt b/npc/015-3-3/boss.txt new file mode 100644 index 0000000..0fd764e --- /dev/null +++ b/npc/015-3-3/boss.txt @@ -0,0 +1,81 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Pirate Den - Boss Fight & Treasure Chest + +015-3-3,100,67,0 script Pirate Treasure Chest NPC_CHEST,{ + /* Boss Showdown incomplete */ + if (getq(HurnscaldQuest_PirateCave) < 7) { + warp "015-3-2", 24, 62; + end; + } + /* Treasure Chest logic */ + if (getq(HurnscaldQuest_PirateCave) == 7) { + // You'll always get the maximum prize the first time + TreasureBox(99999); + Zeny+=rand2(5700, 6200); + getexp 1000, 320; + compareandsetq HurnscaldQuest_PirateCave, 7, 8; + } else { + TreasureBox(50); // 0.50% extra chance + } + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=2; + end; +} + +// Boss Showdown Intercept +015-3-3,100,62,0 script #Marley NPC_HIDDEN,2,0,{ + end; +OnTouch: + if (getq(HurnscaldQuest_PirateCave) >= 7) end; + slide 70, 50; + if (mobcount("015-3-3", "#Marley::OnWin")) end; + // Cutscene + Boss! + sleep2(500); // For ManaPlus + dispbottom l("??? : Wait, how did you got here? WHO ARE YOU?"); + sleep2(2500); + dispbottom l("??? : Are you trying to steal MY treasure?!"); + sleep2(2500); + dispbottom l("??? : I am Pirate Captain Marley, and on the honor of my pirates..."); + sleep2(2500); + dispbottom l("Marley : I shall stop you!"); + // Someone has beat you to the punch! + if (mobcount("015-3-3", "#Marley::OnWin")) end; + // Spawn the boss! + monster("015-3-3", 69, 54, strmobinfo(1, Marley), Marley, 1, "#Marley::OnWin"); + // And the reinforcements... + areamonster("015-3-3", 62, 47, 81, 57, strmobinfo(1, Thug), Thug, 4); + areamonster("015-3-3", 62, 47, 81, 57, strmobinfo(1, Swashbuckler), Swashbuckler, 4); + areamonster("015-3-3", 62, 47, 81, 57, strmobinfo(1, Grenadier), Grenadier, 4); + initnpctimer; + end; + +// Extra reinforcements if Marley still alive +OnTimer30000: + if (!mobcount("015-3-3", "#Marley::OnWin")) end; + areamonster("015-3-3", 62, 47, 81, 57, strmobinfo(1, Thug), Thug, 3); + areamonster("015-3-3", 62, 47, 81, 57, strmobinfo(1, Swashbuckler), Swashbuckler, 3); + areamonster("015-3-3", 62, 47, 81, 57, strmobinfo(1, Grenadier), Grenadier, 3); + end; + +OnWin: + dispbottom l("Marley : You... thief... *grumble*"); + compareandsetq HurnscaldQuest_PirateCave, 6, 7; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; +} + +// Exit Gate, only usable if Marley isn't around anymore +015-3-3,88,66,0 script #MarleyOut NPC_HIDDEN,0,2,{ + end; +OnTouch: + if (mobcount("015-3-3", "#Marley::OnWin")) end; + slide 100, 60; + end; +} + + diff --git a/npc/015-3/_config.txt b/npc/015-3/_config.txt new file mode 100644 index 0000000..19625ca --- /dev/null +++ b/npc/015-3/_config.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3: Butterfly Caves conf + +015-3,24,36,0 script #015-3_24_36 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=2; + end; +} diff --git a/npc/015-3/_import.txt b/npc/015-3/_import.txt new file mode 100644 index 0000000..8933299 --- /dev/null +++ b/npc/015-3/_import.txt @@ -0,0 +1,8 @@ +// Map 015-3: Butterfly Caves +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-3/_config.txt", +"npc/015-3/_mobs.txt", +"npc/015-3/_warps.txt", +"npc/015-3/arkim.txt", +"npc/015-3/logic.txt", +"npc/015-3/pooh.txt", diff --git a/npc/015-3/_mobs.txt b/npc/015-3/_mobs.txt new file mode 100644 index 0000000..c8aa5cb --- /dev/null +++ b/npc/015-3/_mobs.txt @@ -0,0 +1,19 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3: Butterfly Caves mobs +015-3,160,179,19,18 monster Cave Bat 1039,24,30000,40000 +015-3,142,31,24,14 monster Green Slime 1085,5,30000,40000 +015-3,118,179,22,18 monster Cave Snake 1035,9,30000,20000 +015-3,157,110,22,18 monster Snake 1122,12,30000,20000 +015-3,144,94,32,39 monster Black Scorpion 1074,12,30000,20000 +015-3,156,66,24,14 monster Robin Bandit 1153,5,30000,40000 +015-3,113,100,55,37 monster Red Slime 1092,47,30000,40000 +015-3,107,177,14,8 monster Archant 1026,2,30000,40000 +015-3,46,44,31,18 monster Night Scorpion 1077,1,3600000,1800000 +015-3,86,59,28,32 monster Terranite 1167,3,100000,40000 +015-3,69,89,33,28 monster Wicked Mushroom 1176,6,100000,30000 +015-3,84,142,23,50 monster Troll 1171,6,100000,30000 +015-3,32,37,31,12 monster Copper Slime Mother 1238,1,300000,120000 +015-3,38,49,26,17 monster Gobo Bear 1214,5,40000,20000 +015-3,132,189,8,10 monster Snake 1122,1,30000,20000 +015-3,102,105,81,87 monster Small Emerald Bif 1104,6,65000,45000 +015-3,98,123,24,62 monster Copper Slime 1088,10,40000,40000 diff --git a/npc/015-3/_warps.txt b/npc/015-3/_warps.txt new file mode 100644 index 0000000..46a752f --- /dev/null +++ b/npc/015-3/_warps.txt @@ -0,0 +1,59 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-3: Butterfly Caves warps +015-3,152,162,0 script #015-3_152_162 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 123,38; end; +} +015-3,171,161,0 script #015-3_171_161 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 147,39; end; +} +015-3,172,196,0 warp #015-3_172_196 0,0,014-3,120,122 +015-3,138,197,0 warp #015-3_138_197 0,0,015-3-1,80,23 +015-3,129,199,0 script #015-3_129_199 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 152,57; end; +} +015-3,119,196,0 script #015-3_119_196 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 134,58; end; +} +015-3,98,161,0 script #015-3_98_161 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 123,38; end; +} +015-3,123,39,0 script #015-3_123_39 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 152,163; end; +} +015-3,147,40,0 script #015-3_147_40 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 171,162; end; +} +015-3,152,56,0 script #015-3_152_56 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 129,198; end; +} +015-3,134,57,0 script #015-3_134_57 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 119,195; end; +} +015-3,64,74,0 script #015-3_64_74 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 47,61; end; +} +015-3,47,62,0 script #015-3_47_62 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 64,73; end; +} diff --git a/npc/015-3/arkim.txt b/npc/015-3/arkim.txt new file mode 100644 index 0000000..64f22af --- /dev/null +++ b/npc/015-3/arkim.txt @@ -0,0 +1,252 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Heremit +// Variables: +// $ARKIM_ST +// How many Bat Teeth/Wings were given +// q1 +// Controls your own progress helping Arkim - Unused, might give place to some real quest later. +// q2 +// Controls your own progress helping Arkim - Items today +// q3 +// Controls your own progress helping Arkim - Your timer + +015-3,170,169,0 script Arkim NPC_TERRY,{ + function pmap_intro; + function pmap_wrong; + function pmap_right; + mesn; + mesq lg("Hello, young girl...", "Hello, young boy..."); + next; + +L_Loop: + .@q1=getq(HurnscaldQuest_Arkim); + .@q3=getq3(HurnscaldQuest_Arkim); + mesn; + mesq l("I am doing a great research with Bats, and thus far I collected @@ Bat Wings and Teeths.", $ARKIM_ST); + next; + // It was yesterday + if (.@q3 < $@ARKIM_QTIMER) { + setq2 HurnscaldQuest_Arkim, 0; + setq3 HurnscaldQuest_Arkim, $@ARKIM_QTIMER; + } + + // Daily limit reached + .@q2=getq2(HurnscaldQuest_Arkim); + if (.@q2 >= (BaseLevel-17)/3) goto L_Timer; + select + rif(countitem(BatWing) >= 1, l("Donate a Bat Wing")), + rif(countitem(BatTeeth) >= 1, l("Donate a Bat Teeth")), + l("I better leave this crazy man to his ordeals..."), + l("Had you any breakthrough?"); + mes ""; + + switch (@menu) { + case 1: + delitem BatWing, 1; + getexp 25, 0; + Zeny=Zeny+50; + break; + case 2: + delitem BatTeeth, 1; + getexp 36, 0; + Zeny=Zeny+70; + break; + case 3: + goto L_Assign; + close; // Will never reach. + break; + case 4: + goto L_Research; + break; + } + $ARKIM_ST=$ARKIM_ST+1; + setq2 HurnscaldQuest_Arkim, .@q2+1; + goto L_Loop; + +L_Timer: + if (BaseLevel < 20) + mesc l("You need at least level 20 to help."); + mesn; + mesq l("You've helped me plenty. Please come back in @@", FuzzyTime($@ARKIM_TIMER+(60*60*24),2,2)); + goto L_Assign; + close; // Will never reach. + +// TODO +L_Research: + mesn; + mesq l("Let me see... The more Wings and Teethes I collect, the more my research shall advance."); + next; + select + l("Thanks."), + rif($ARKIM_ST >= 1400, l("Cursed Arrows")), + rif($ARKIM_ST >= 2800, l("Poison Arrows")), + rif($ARKIM_ST >= 1200, l("Piberries Infusion")), + rif($ARKIM_ST >= 2600, l("Fate's Potion")), + rif($ARKIM_ST >= 4000, l("Clotho Liquor")), + rif($ARKIM_ST >= 4700, l("Lachesis Brew")), + rif($ARKIM_ST >= 6600, l("Atropos Mixture")), + rif($ARKIM_ST >= 7500, l("Dark Desert Mushroom")); + mes ""; + mesn; + + switch (@menu) { + case 1: + mesq l("Good bye!"); + goto L_Assign; + close; // Will never reach. + break; + case 2: + mesq l("The @@ are specially dangerous, and archers love them.", getitemlink(CursedArrow)); + next; + mesn; + mesq l("These are being crafted by Alan."); + break; + case 3: + mesq l("The @@ are specially dangerous, and archers love them.", getitemlink(PoisonArrow)); + next; + mesn; + mesq l("These are being crafted by Alan."); + break; + case 4: + case 5: + case 6: + case 7: + case 8: + mesq l("The @@ is a powerful healing drink.", getitemlink(PiberriesInfusion-4+@menu)); + next; + mesn; + + switch (@menu) { + case 4: + mesq l("@@ is developing these potions.", "Wyara"); break; + case 5: + mesq l("@@ is developing these potions.", "Fate, in Nivalis,"); break; // TODO: Missing NPC + case 6: + mesq l("@@ is developing these potions.", "Frostia Dwarves"); break; + case 7: + mesq l("@@ is developing these potions.", "Frostia Dwarves"); break; + default: + mesq l("@@ is developing these potions.", "Jesusalva"); // TODO: Lachesis and Atropos + break; + } + + break; + case 9: + mesq l("Ah! @@. A very rare drop!", getitemlink(DarkDesertMushrooom)); + next; + mesn; + mesq l("You can find it in the bandit market, but it is EXPENSIVE."); + break; + + } + next; + goto L_Research; + +L_Assign: + // NOTE: Map will be destroyed after first time, MAKE SURE TO MEMORIZE. + // Or you'll need to find ANOTHER copy, but this one won't be burned. + if (getq(HurnscaldQuest_PirateCave) >= 2 && countitem(PirateTreasureMap)) { + mesc l("Should we ask about the %s before leaving?", getitemlink(PirateTreasureMap)); + select + l("Ask."), + l("Don't ask."); + mes ""; + if (@menu == 1) goto L_PirateMap; + } + GHQ_Assign(Bat, "Bat"); + end; + +L_PirateMap: + .@q = getq(HurnscaldQuest_PirateCave); + if (.@q == 2) pmap_intro(); + else if (.@q == 3) pmap_intro(); + else if (.@q == 4) pmap_wrong(); + // IMPLICIT FALLTHROUGH + .@q = getq(HurnscaldQuest_PirateCave); + if (.@q == 5) pmap_right(); + close; + +function pmap_intro { + mesn; + mesq l("There's no one who knows Butterfly Caves better than me, child. These are MY caves, after all."); + next; + mesn; + mesc l("%s makes a confused face.", .name$); + mesq l("But your silly map must be wrong. These passages do not connect themselves like this, but this should not be an issue, as they're not relevant."); + next; + mesn; + mesq l("To reach this place... You just need to follow the path after the waterfall. Come back to me after that."); + compareandsetq HurnscaldQuest_PirateCave, 2, 3; + close; +} + +function pmap_wrong { + mesn; + mesq l("Welcome back, children. So, how was your treasure hunt? Did you manage to find the path?"); + next; + mesn strcharinfo(0); + select + l("NO!"), + l("Not at all!"), + l("You barely know these caves."), + l("You send me the wrong way."); + mes ""; + mesn; + mesq l("Calm down, don't get mad! I was wrong in trusting this map, it is definitely faulty. Lemme see it again..."); + if (!countitem(PirateTreasureMap)) close; // Last chance... + next; + mesn; + mesc l("%s eyebrow furrows, as he brings the map closer to a torch.", .name$); + mesq l("These paths are all wrong! What is the problem with this map?!"); + next; + mesc l("The map starts getting darker with the heat of the flame, and curiously, new shapes starts to form..."); + next; + mesn; + mesq l("Aha! So that's what it was! The map was hiding its secrets in drawings made with acid. An old technique... But a very useful one."); + next; + delitem PirateTreasureMap, 1; + setq HurnscaldQuest_PirateCave, 5; + getexp 0, 600; + mesc l("Suddenly, the map %s!", col(l("burst up in flames"), 1)); + mesc l("Seems like Arkim held it too close to the flame..."); + next; + mesn; + mes "\""+l("I have some good and some bad news for you."); + mes l("The good one, is that now I know the right path."); + mes l("The bad one, is that you'll have to guide yourself without the map.")+"\""; + next; + return; +} + +function pmap_right { + mes ".:: " + l("Direction for Pirate Caves") + " ::."; + mes "1. "+l("Go through the waterfall path."); + mes "2. "+l("Use the abandoned passage to the north... Usually, no one uses it."); + mes "3. "+l("After that, follow the path until you reach a great chamber..."); + mes "4. "+l("Then take the middle entrance."); + next; + mesn; + mesq l("I wish you good luck. I remember these caves... They're dangerous. Make sure to be prepared before venturing further."); + close; +} + +OnInit: + .sex=G_MALE; + .distance=5; + + $@ARKIM_QTIMER=gettimeparam(GETTIME_DAYOFMONTH); + $@ARKIM_TIMER=gettimetick(2); + $@ARKIM_TIMER-=gettime(1); + $@ARKIM_TIMER-=gettime(2)*60; + $@ARKIM_TIMER-=gettime(3)*3600; + end; + +OnHour00: + $@ARKIM_QTIMER=gettimeparam(GETTIME_DAYOFMONTH); + $@ARKIM_TIMER=gettimetick(2); + end; + +} diff --git a/npc/015-3/logic.txt b/npc/015-3/logic.txt new file mode 100644 index 0000000..0044017 --- /dev/null +++ b/npc/015-3/logic.txt @@ -0,0 +1,27 @@ +// TMW2 scripts. +// Authors: +// Diogo_RBG +// Jesusalva +// Description: +// Pirate Caves gateway main logic +// Adapted from TMW-BR for Moubootaur Legends + +015-3,128,161,0 script #0153WG1 NPC_HIDDEN,0,0,{ + end; +OnTouch: + compareandsetq HurnscaldQuest_PirateCave, 3, 4; + compareandsetq HurnscaldQuest_PirateCave, 5, 6; + slide 166, 38; + end; +} + +015-3,132,23,0 script #0153WG2 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (getq(HurnscaldQuest_PirateCave) >= 6) { + slide 98, 162; + } + end; +} + + diff --git a/npc/015-3/pooh.txt b/npc/015-3/pooh.txt new file mode 100644 index 0000000..fc14040 --- /dev/null +++ b/npc/015-3/pooh.txt @@ -0,0 +1,221 @@ +// TMW2 scripts. +// Author: +// Diogo RBG from TMW-BR +// Jesusalva +// Description: +// This is a very cute bear, it'll definitely earn your sympathy. +// Pooh (original) has became Public Domain in January 2022. + +015-3,127,181,0 script Pooh NPC_POOH,{ + function poohNear; + function poohFood; + function poohSmell; + inventoryplace BearHat, 1; + .@q = getq(HurnscaldQuest_TeddyBear); + mesc l("OMG so cute! This must be the cutest bear you ever seen your whole life."); + // Minimum level not met (does it even make sense? It is within Pirate Map) + if (BaseLevel < 39) + close; + mesc l("It seems to be concentrated on its small pot... So concentrated, it doesn't even realizes you're there."); + mesc l("This is so amusing, you decide to stare for a while longer."); + next; + // Quest already finished + if (.@q >= 3) { + mesc l("It then looks at you with a pleasant smile, you definitely earned a new cute friend!"); + close; + } + mesc l("And then you notice the small teddy is opening and closing the lid of this pot repeated times, as if it were looking for something."); + mesc l("You ask yourself: What is the teddy looking for? How can I help him?"); + compareandsetq HurnscaldQuest_TeddyBear, 0, 1; + next; + .@cont = false; + // Main menu + do + { + mesn strcharinfo(0); + select + l("Sing a song to brighten its mood."), + l("Pet his wonderfully fluffy pelt."), + l("Approach it softly."), + l("Offer food to it."), + l("Leave the poor thing in peace."); + mes ""; + switch (@menu) { + case 1: + mesc l("You sing a pretty song to the teddy, and this makes it very happy."); + mesc l("You know this, because their big round eyes were shining."); + next; + mesc l("However, this only distracts him for a short while. Soon after, he returns to his small ritual with the pot."); + next; + mesc l("Poor teddy, not even a pretty music can comfort him for long. What can we do now?"); + next; + break; + case 2: + mesc l("With a big smile, you stretch your hand in the teddy's direction; But the little one gets scared with the movement and starts to move away from you."); + next; + mesc l("You give up after seeing its reaction, and try your best to resist the temptation of hugging this fluffy ball. Maybe we have another idea?"); + next; + break; + case 3: + poohNear(); + next; + break; + case 4: + poohFood(); + .@cont = true; + break; + case 5: + mesc l("You leave the teddy alone with its pot."); + .@cont = true; + break; + default: + mesc "FIXME", 1; + next; + break; + } + } while (!.@cont); + close; + +function poohNear { + mesc l("You take off your bag and put it on the floor, as you lower yourself and try to slowly get close to it."); + next; + mesc l("But the teddy, noticing your movement, starts getting farther from you. It doesn't seems scared, but it doesn't allows you to approach either."); + next; + if (countitem(Honey) || countitem(BeeStinger)) { + poohSmell(); + return; + } + mesc l("Insisting will do us no good, maybe we should try to gain its trust first?"); + return; +} + +function poohFood { + mesc l("Maybe the teddy is hungry? What could we offer to it?"); + .@honey = false; + do + { + mes ""; + mes "##B" + l("Drag and drop a healing item from your inventory.") + "##b"; + + .@id = requestitem(); + + // If ID is invalid, there's not enough items, it is bound, etc; + if (.@id < 1) return; + if (.@id < 1 || countitem(.@id) < 1 || checkbound(.@id) || getiteminfo(.@id, ITEMINFO_TYPE) != IT_HEALING) { + if (checkbound(.@id)) { + mesc l("You cannot part with this item!"); + } else if (.@id == DeathPotion || .@id == NymphPoison) { + mesc l("...Are you crazy?"); + } else if (.@id == MagicApple || .@id == MysteriousFruit) { + mesc l("This is too precious to offer, I think."); + } else if (getiteminfo(.@id, ITEMINFO_TYPE) != IT_HEALING) { + mesc l("I'm not sure if that's edible. For teddies, at least."); + } else { + mesc l("You give up."); + mesc l("Maybe it is not hungry, after all."); + return; + } + next; + continue; + } + + // Bad healing items + if (.@id == Beer || + .@id == RedPlushWine || + .@id == DwarvenSake || + .@id == CrazyRum || + .@id == WhiskeyAle) { + mesc l("Offering alcohol to a teddy seems like a ugly thing to do. We should think on something else."); + next; + continue; + } else if (.@id == ElixirOfLife) { + mesc l("...I don't think the teddy is sick, so there's no point giving it an Elixir."); + next; + continue; + } else if (.@id == BottleOfSewerWater) { + mesc l("...I refuse to comment on this ridiculous idea."); + next; + continue; + } else if (.@id == Honey) { + .@honey = true; + break; + } + + mesc l("Really give your %s to the teddy?", getitemlink(.@id)), 1; + mesc l("The item will be lost forever."); + if (askyesno() == ASK_YES) { + mes ""; + mesc l("The teddy stares at it but does not approach. Maybe it is not hungry, or it doesn't want the food you offered."); + // Item is NOT deleted because it did not approach + next; + } + } while (!.@honey); + // You're offering HONEY! But we must check if you CAN do this + if (getq(HurnscaldQuest_TeddyBear) < 2) { + percentheal -2, 0; + SC_Bonus(15, SC_POISON, 1); + getmapxy(.@m$, .@x, .@y, 0); + .@u = monster(.@m$, .@x, .@y, strmobinfo(1, Bee), Bee, 1); + unitattack(.@u, getcharid(3)); + closeclientdialog; + dispbottom l("Suddenly, a bee appears and attacks you out of NOWHERE!"); + end; // TERM + } + // You can, so we go ahead. + mesc l("Our plushy friend knows what is really sweet on this life. It likes honey a great deal, and by the way it looks you, it probably wants more."); + next; + mesc l("It is... SO CUTE! You won't refuse honey to a teddy, will you?"); + mesc l("How much honey will you give it? (You have: %d)", countitem(Honey)), 3; + input .@honey, 0, countitem(Honey); + if (.@honey < 1) return; + delitem Honey, .@honey; + setq2 HurnscaldQuest_TeddyBear, getq2(HurnscaldQuest_TeddyBear) + .@honey; + getexp 0, .@honey * 14; + mesc l("The teddy quickly takes the honey and fills the pot. It is really hungry!"); + if (getq2(HurnscaldQuest_TeddyBear) >= 27) { + mesc l("...And at long last, the teddy's pot is full of honey."); + mesc l("It seems happy, but it quickly goes to a dark honey after finishing."); + next; + mesc l("When it returns, it is holding a cute %s. Understanding it is a gift, you take it.", getitemlink(BearHat)); + mesc l("Doesn't it feel good to help?"); + getitem BearHat, 1; + setq HurnscaldQuest_TeddyBear, 3; + // Half of needed exp, but treasure map may require more level + getexp 15750, 9200; // Job Exp: Lv 28 as reference + } + return; +} + +function poohSmell { + mesc l("But the teddy unexpectedly starts moving towards your bag, possibly attracted by some smell. You give up on approaching it, and starts watching it."); + next; + if (@stingpooh) { + mesc l("However, it stops abruptly and quickly moves away from it."); + mesc l("I guess it still remembers the sting on the snout. Ouch!"); + return; + } + mesc l("Without any ceremony or further ado, the teddy starts poking on your things."); + next; + if (countitem(BeeStinger)) { + mesc l("However, it comes back crying! Poor teddy, it got attracted by a sweet smell but it found a %s instead!", getitemlink(BeeStinger)); + delitem BeeStinger, 1; + @stingpooh = true; + next; + mesc l("Unsatisfied, it goes away from you and your bag. Such a shame, we almost had got its trust."); + return; + } + mesc l("After turning it upside down, it finds something which makes it really happy: %s! And there's nothing a teddy likes more than honey.", getitemlink(Honey)); + next; + mesc l("The teddy picks it and goes running to somewhere it can eat in peace. Now we know how we can please our little friend - with %s! How we didn't think on that before?!", getitemlink(Honey)); + delitem Honey, 1; + compareandsetq HurnscaldQuest_TeddyBear, 1, 2; + setq2 HurnscaldQuest_TeddyBear, getq2(HurnscaldQuest_TeddyBear) + 1; + getexp 0, 14; + return; +} + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} diff --git a/npc/015-4/_import.txt b/npc/015-4/_import.txt new file mode 100644 index 0000000..167d402 --- /dev/null +++ b/npc/015-4/_import.txt @@ -0,0 +1,4 @@ +// Map 015-4: Woodland Mines +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-4/_mobs.txt", +"npc/015-4/_warps.txt", diff --git a/npc/015-4/_mobs.txt b/npc/015-4/_mobs.txt new file mode 100644 index 0000000..7bd3f0c --- /dev/null +++ b/npc/015-4/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-4: Woodland Mines mobs +015-4,49,25,35,12 monster Angry Bat 1194,15,60000,60000 +015-4,56,36,35,12 monster Silk worm 1034,15,60000,70000 +015-4,72,23,15,15 monster Red Slime 1092,4,60000,60000 +015-4,54,21,29,14 monster Ruby Bif 1099,1,90000,90000 diff --git a/npc/015-4/_warps.txt b/npc/015-4/_warps.txt new file mode 100644 index 0000000..40f118d --- /dev/null +++ b/npc/015-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-4: Woodland Mines warps +015-4,50,39,0 warp #015-4_50_39 0,0,015-1,90,50 +015-4,76,18,0 warp #015-4_76_18 0,0,015-1,177,55 diff --git a/npc/015-5/_import.txt b/npc/015-5/_import.txt new file mode 100644 index 0000000..85922cc --- /dev/null +++ b/npc/015-5/_import.txt @@ -0,0 +1,6 @@ +// Map 015-5: Abandoned Mines +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-5/_mobs.txt", +"npc/015-5/_warps.txt", +"npc/015-5/sign.txt", +"npc/015-5/treasure.txt", diff --git a/npc/015-5/_mobs.txt b/npc/015-5/_mobs.txt new file mode 100644 index 0000000..939e5a1 --- /dev/null +++ b/npc/015-5/_mobs.txt @@ -0,0 +1,28 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-5: Abandoned Mines mobs +015-5,353,65,9,3 monster Cave Maggot 1027,5,100000,30000 +015-5,347,45,9,3 monster Cave Maggot 1027,5,100000,30000 +015-5,317,31,9,3 monster Cave Maggot 1027,5,100000,30000 +015-5,252,39,4,8 monster Cave Maggot 1027,5,100000,30000 +015-5,268,101,8,9 monster Cave Maggot 1027,10,100000,30000 +015-5,351,81,9,3 monster Cave Maggot 1027,5,100000,30000 +015-5,293,138,8,3 monster Cave Maggot 1027,5,100000,30000 +015-5,314,78,4,8 monster Cave Maggot 1027,5,100000,30000 +015-5,287,152,8,3 monster Cave Maggot 1027,5,100000,30000 +015-5,335,117,4,8 monster Cave Maggot 1027,5,100000,30000 +015-5,235,100,4,8 monster Cave Maggot 1027,5,100000,30000 +015-5,174,138,8,9 monster Cave Maggot 1027,10,100000,30000 +015-5,93,52,8,9 monster Troll 1171,6,100000,30000 +015-5,70,106,8,9 monster Cave Maggot 1027,10,100000,30000 +015-5,297,95,39,38 monster Small Emerald Bif 1104,1,45000,45000 +015-5,237,143,95,39 monster Copper Slime 1088,14,45000,45000 +015-5,186,64,113,48 monster Earth Fairy 1182,8,35000,35000 +015-5,215,108,5,6 monster Small Emerald Bif 1104,1,45000,45000 +015-5,69,172,32,15 monster Mouboo Slime 1201,12,360000,180000 +015-5,215,108,8,9 monster Cave Maggot 1027,10,100000,30000 +015-5,127,154,35,41 monster Archant 1026,6,30000,30000 +015-5,370,67,35,41 monster Archant 1026,6,30000,30000 +015-5,78,109,48,98 monster Cave Snake 1035,8,30000,30000 +015-5,154,55,33,43 monster Dark Lizard 1051,4,100000,30000 +015-5,214,100,189,95 monster Cave Maggot 1027,160,70000,30000 +015-5,206,167,175,36 monster Mineral Bif 1058,6,100000,30000 diff --git a/npc/015-5/_warps.txt b/npc/015-5/_warps.txt new file mode 100644 index 0000000..4ba354f --- /dev/null +++ b/npc/015-5/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-5: Abandoned Mines warps +015-5,81,138,0 warp #015-5_81_138 0,0,014-5,86,32 +015-5,353,61,0 warp #015-5_353_61 0,0,015-6,193,57 diff --git a/npc/015-5/sign.txt b/npc/015-5/sign.txt new file mode 100644 index 0000000..9d92af6 --- /dev/null +++ b/npc/015-5/sign.txt @@ -0,0 +1,22 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// You're at a PVP Area + +015-5,356,61,0 script WARNING#015535661 NPC_SWORDS_SIGN2,{ + mesn; + mesc l("You are entering on a PVP Area with lowered death penalty."); + next; + mesn; + mesc l("The mines beyond this point have been shut at 280 AT because Terranite. You've been warned!"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +015-5,350,61,0 duplicate(WARNING#015535661) WARNING#015535061 NPC_SWORDS_SIGN2 + diff --git a/npc/015-5/treasure.txt b/npc/015-5/treasure.txt new file mode 100644 index 0000000..0fb2a85 --- /dev/null +++ b/npc/015-5/treasure.txt @@ -0,0 +1,64 @@ +// TMW2 Script + +// (Random) Treasure Chest +// Authored by Jesusalva +// Regenerates every 6 hours + +015-5,0,0,0 script #chest_01550 NPC_CHEST,{ + + if (!.busy && !.empty) { + TreasureBox(); + + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + .dir = .dir == 0 ? 2 : 6; // closed ? opening : closing + .busy = true; // lock until available again + initnpctimer; + } else if (!.busy) { + mesc l("Someone looted this treasure box already..."); + } else { + end; + } + close; + +OnTimer160: + .dir = .dir == 6 ? 0 : 4; // closing ? closed : open + end; + +OnTimer500: + .busy = false; // unlock + if (.dir == 0 || .dir == 4) + stopnpctimer; // stop here if the chest is closed + end; + +OnInit: + .busy = false; + .distance = 2; + .empty = false; + +OnClock0156: +OnClock0756: +OnClock1356: +OnClock1956: + // Try to warp randomly to a walkable spot, up to 20 attempts + // Otherwise, it'll stay where it already is (but will close and refill). + .@e=0; .@x=0; .@y=0; + while (!checkcell(.map$, .@x, .@y, cell_chkpass)) + { + if (.@e == 20) { + .@x=.x; + .@y=.y; + break; + } + // Remember the +20 -20 margin adjustment + .@x = rand(20, 405); + .@y = rand(20, 190); + ++.@e; + } + .busy=false; + .empty=false; + movenpc .name$, .@x, .@y, 0; + end; +} + +015-5,0,0,0 duplicate(#chest_01550) #chest_01551 NPC_CHEST + diff --git a/npc/015-6/_import.txt b/npc/015-6/_import.txt new file mode 100644 index 0000000..f75a7dd --- /dev/null +++ b/npc/015-6/_import.txt @@ -0,0 +1,7 @@ +// Map 015-6: Terranite Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-6/_mobs.txt", +"npc/015-6/_warps.txt", +"npc/015-6/boss.txt", +"npc/015-6/mapflags.txt", +"npc/015-6/treasure.txt", diff --git a/npc/015-6/_mobs.txt b/npc/015-6/_mobs.txt new file mode 100644 index 0000000..16a975d --- /dev/null +++ b/npc/015-6/_mobs.txt @@ -0,0 +1,72 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-6: Terranite Cave mobs +015-6,217,45,6,1 monster Moggun 1070,3,100000,30000 +015-6,223,30,6,1 monster Moggun 1070,3,100000,30000 +015-6,239,48,6,1 monster Moggun 1070,3,100000,30000 +015-6,197,39,3,4 monster Moggun 1070,3,100000,30000 +015-6,210,35,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,241,38,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,237,59,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,226,73,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,209,94,4,2 monster Angry Bat 1194,5,100000,30000 +015-6,263,29,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,274,48,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,267,70,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,259,79,3,4 monster Angry Bat 1194,15,100000,30000 +015-6,274,79,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,268,91,12,7 monster Moggun 1070,10,100000,30000 +015-6,209,146,34,27 monster Wicked Mushroom 1176,5,100000,30000 +015-6,254,163,9,23 monster Angry Yellow Slime 1198,10,100000,30000 +015-6,167,175,8,11 monster Angry Yellow Slime 1198,10,100000,30000 +015-6,148,176,8,11 monster Red Slime 1092,15,100000,30000 +015-6,131,88,8,11 monster Red Slime 1092,10,100000,30000 +015-6,140,131,8,11 monster Angry Yellow Slime 1198,10,100000,30000 +015-6,42,128,8,4 monster Angry Yellow Slime 1198,10,100000,30000 +015-6,64,62,12,15 monster Wicked Mushroom 1176,6,100000,30000 +015-6,69,89,8,10 monster Angry Yellow Slime 1198,10,100000,30000 +015-6,34,45,8,16 monster Terranite 1167,3,100000,30000 +015-6,64,36,12,8 monster Cave Maggot 1027,3,100000,30000 +015-6,100,115,12,8 monster Cave Maggot 1027,3,100000,30000 +015-6,90,137,8,11 monster Angry Yellow Slime 1198,10,100000,30000 +015-6,59,113,7,8 monster Cave Maggot 1027,28,100000,30000 +015-6,42,176,7,6 monster Terranite 1167,3,100000,30000 +015-6,120,177,12,8 monster Cave Maggot 1027,3,100000,30000 +015-6,287,180,12,8 monster Cave Maggot 1027,3,100000,30000 +015-6,340,175,12,8 monster Cave Maggot 1027,3,100000,30000 +015-6,390,156,34,27 monster Wicked Mushroom 1176,7,100000,30000 +015-6,358,37,10,11 monster Terranite 1167,3,100000,30000 +015-6,481,110,9,12 monster Terranite 1167,3,100000,30000 +015-6,435,114,12,8 monster Cave Maggot 1027,3,100000,30000 +015-6,477,82,12,8 monster Cave Maggot 1027,3,100000,30000 +015-6,354,106,9,23 monster Angry Yellow Slime 1198,15,100000,30000 +015-6,342,139,8,11 monster Red Slime 1092,10,100000,30000 +015-6,389,81,16,7 monster Red Slime 1092,16,100000,30000 +015-6,409,35,3,4 monster Angry Bat 1194,11,100000,30000 +015-6,413,73,3,4 monster Angry Bat 1194,11,100000,30000 +015-6,395,63,3,4 monster Angry Bat 1194,11,100000,30000 +015-6,314,182,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,409,121,3,4 monster Angry Bat 1194,18,100000,30000 +015-6,456,99,3,4 monster Angry Bat 1194,11,100000,30000 +015-6,133,153,4,2 monster Angry Bat 1194,11,100000,30000 +015-6,153,98,4,2 monster Angry Bat 1194,5,100000,30000 +015-6,126,65,4,2 monster Angry Bat 1194,5,100000,30000 +015-6,81,180,4,2 monster Angry Bat 1194,5,100000,30000 +015-6,61,159,4,2 monster Angry Bat 1194,5,100000,30000 +015-6,271,157,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,270,182,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,322,141,8,11 monster Red Slime 1092,10,100000,30000 +015-6,451,111,3,4 monster Angry Bat 1194,5,100000,30000 +015-6,422,50,5,16 monster Wicked Mushroom 1176,5,100000,30000 +015-6,380,40,7,11 monster Wicked Mushroom 1176,5,100000,30000 +015-6,124,49,4,12 monster Terranite 1167,1,100000,30000 +015-6,80,110,4,2 monster Angry Bat 1194,5,100000,30000 +015-6,42,142,10,5 monster Cave Maggot 1027,3,100000,30000 +015-6,41,159,5,10 monster Wicked Mushroom 1176,4,100000,30000 +015-6,95,180,4,2 monster Angry Bat 1194,5,100000,30000 +015-6,91,180,11,5 monster Wicked Mushroom 1176,8,100000,30000 +015-6,263,167,21,22 monster Big Sapphire Bif 1115,3,150000,30000 +015-6,483,173,21,22 monster Big Sapphire Bif 1115,3,150000,30000 +015-6,233,41,49,22 monster Big Amethyst Bif 1112,3,150000,30000 +015-6,230,43,49,22 monster Amethyst Bif 1111,3,120000,30000 +015-6,201,134,22,35 monster Amethyst Bif 1111,5,135000,30000 +015-6,394,81,55,59 monster Sapphire Bif 1114,6,135000,30000 diff --git a/npc/015-6/_warps.txt b/npc/015-6/_warps.txt new file mode 100644 index 0000000..6aba506 --- /dev/null +++ b/npc/015-6/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-6: Terranite Cave warps +015-6,193,58,0 warp #015-6_193_58 0,0,015-5,353,62 +015-6,363,26,0 warp #015-6_363_26 0,0,015-6,474,187 +015-6,474,188,0 warp #015-6_474_188 0,0,015-6,363,27 diff --git a/npc/015-6/boss.txt b/npc/015-6/boss.txt new file mode 100644 index 0000000..d843496 --- /dev/null +++ b/npc/015-6/boss.txt @@ -0,0 +1,31 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Terranite King Boss + +015-6,0,0,0 script #BossCtrl_015-6 NPC_HIDDEN,{ + end; + +// Respawn every hour +OnTimer3600000: + stopnpctimer; +OnInit: + setarray .xp, 268, 55, 371, 482, 212; + setarray .yp, 90, 45, 38, 114, 148; + .@tg=rand(getarraysize(.xp)-1); + monster "015-6", .xp[.@tg], .yp[.@tg], strmobinfo(1, TerraniteKing), TerraniteKing, 1, "#BossCtrl_015-6::OnBossDeath"; + end; + +OnBossDeath: + initnpctimer; + .@party=getcharid(1); + if (.@party > 0) { + mapannounce "015-6", "Boss deafeated by Party: " + getpartyname(.@party), bc_all; + } else { + mapannounce "015-6", "Boss deafeated by: " + strcharinfo(0), bc_all; + } + fix_mobkill(TerraniteKing); + end; + +} diff --git a/npc/015-6/mapflags.txt b/npc/015-6/mapflags.txt new file mode 100644 index 0000000..4ec943d --- /dev/null +++ b/npc/015-6/mapflags.txt @@ -0,0 +1 @@ +015-6 mapflag pvp diff --git a/npc/015-6/treasure.txt b/npc/015-6/treasure.txt new file mode 100644 index 0000000..8b7723c --- /dev/null +++ b/npc/015-6/treasure.txt @@ -0,0 +1,64 @@ +// TMW2 Script + +// (Random) Treasure Chest +// Authored by Jesusalva +// Regenerates every 6 hours + +015-6,0,0,0 script #chest_01560 NPC_CHEST,{ + + if (!.busy && !.empty) { + TreasureBox(); + + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + .dir = .dir == 0 ? 2 : 6; // closed ? opening : closing + .busy = true; // lock until available again + initnpctimer; + } else if (!.busy) { + mesc l("Someone looted this treasure box already..."); + } else { + end; + } + close; + +OnTimer160: + .dir = .dir == 6 ? 0 : 4; // closing ? closed : open + end; + +OnTimer500: + .busy = false; // unlock + if (.dir == 0 || .dir == 4) + stopnpctimer; // stop here if the chest is closed + end; + +OnInit: + .busy = false; + .distance = 2; + .empty = false; + +OnClock0156: +OnClock0756: +OnClock1356: +OnClock1956: + // Try to warp randomly to a walkable spot, up to 20 attempts + // Otherwise, it'll stay where it already is (but will close and refill). + .@e=0; .@x=0; .@y=0; + while (!checkcell(.map$, .@x, .@y, cell_chkpass)) + { + if (.@e == 20) { + .@x=.x; + .@y=.y; + break; + } + // Remember the +20 -20 margin adjustment + .@x = rand(20, 500); + .@y = rand(20, 190); + ++.@e; + } + .busy=false; + .empty=false; + movenpc .name$, .@x, .@y, 0; + end; +} + +015-6,0,0,0 duplicate(#chest_01560) #chest_01561 NPC_CHEST + diff --git a/npc/015-7/_import.txt b/npc/015-7/_import.txt new file mode 100644 index 0000000..1954ecd --- /dev/null +++ b/npc/015-7/_import.txt @@ -0,0 +1,4 @@ +// Map 015-7: Small Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-7/_mobs.txt", +"npc/015-7/_warps.txt", diff --git a/npc/015-7/_mobs.txt b/npc/015-7/_mobs.txt new file mode 100644 index 0000000..5b3ec36 --- /dev/null +++ b/npc/015-7/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-7: Small Cave mobs +015-7,58,66,37,29 monster Cave Snake 1035,21,20000,20000 +015-7,57,67,31,24 monster Cave Maggot 1027,16,20000,20000 diff --git a/npc/015-7/_warps.txt b/npc/015-7/_warps.txt new file mode 100644 index 0000000..e143291 --- /dev/null +++ b/npc/015-7/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-7: Small Cave warps +015-7,38,95,0 warp #015-7_38_95 0,0,014-5,48,162 +015-7,84,66,0 warp #015-7_84_66 0,0,014-5,59,155 diff --git a/npc/015-8-1/_import.txt b/npc/015-8-1/_import.txt new file mode 100644 index 0000000..016ede1 --- /dev/null +++ b/npc/015-8-1/_import.txt @@ -0,0 +1,4 @@ +// Map 015-8-1: Abandoned Ruins +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-8-1/campaign.txt", +"npc/015-8-1/puzzle.txt", diff --git a/npc/015-8-1/campaign.txt b/npc/015-8-1/campaign.txt new file mode 100644 index 0000000..9a9401c --- /dev/null +++ b/npc/015-8-1/campaign.txt @@ -0,0 +1,311 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Sagratha Quest (field 3) + +015-8-1,49,38,0 script #SaggyDungeonCore NPC_NO_SPRITE,{ + // Not in instance, we don't care. + if (instance_id() < 0) + end; + if (!.SAGRATHA) + dispbottom l("I cannot read the signs from this far away."); + if (@swin) + goto OnSW06; + end; + +// There is no instance init +OnBegin: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + .PLAYERID=getcharid(3); + + // PCBLOCK_HARD = PCBLOCK_ATTACK|PCBLOCK_SKILL|PCBLOCK_USEITEM|PCBLOCK_COMMANDS|PCBLOCK_SITSTAND|PCBLOCK_IMMUNE|PCBLOCK_CHAT|PCBLOCK_MOVE + setpcblock(PCBLOCK_HARD, true); + setq3 HurnscaldQuest_Sagratha, 1; + + // Setup boss units + .SAGRATHA=monster(.@m$, 52, 40, "Sagratha", Sagratha, 1, .@n$+"::OnSagrathaDie", Size_Medium, 2); + .BOSS=monster(.@m$, 47, 40, l("Masked Assassin"), HoodedAssassin, 1, .@n$+"::OnSagrathaWin"); + + unitstop(.SAGRATHA); + unitstop(.BOSS); + sc_start(SC_STUN, 9000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .SAGRATHA); + sc_start(SC_STUN, 9000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .BOSS); + + // FIXME: Seems like this is an "unsafe command" in 4144's opinion + // Prevent players from engaging Sagratha and allies + clientcommand "addignoreattack Sagratha"; + clientcommand "addignoreattack "+l(strmobinfo(1, Mouboo)); + clientcommand "addignoreattack "+l(strmobinfo(1, ForestMushroom)); + clientcommand "addignoreattack "+strmobinfo(1, Mouboo); + clientcommand "addignoreattack "+strmobinfo(1, ForestMushroom); + + unittalk(.SAGRATHA, l("What are you doing here, @@!", get_race())); + + addtimer(2000, .@n$+"::OnS02"); + end; + +OnS02: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(.SAGRATHA, l("I don't need help!")); + unittalk(.BOSS, l("Die already!")); + + addtimer(3000, .@n$+"::OnS03"); + end; + +OnS03: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(.SAGRATHA, l("If you don't want to die, fight!")); + unittalk(.BOSS, lg("Murder her too!", "Murder him too!")); + + addtimer(3000, .@n$+"::OnS04"); + end; + +OnS04: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + // Assassin's Army + areamonster .@m$, 44, 40, 55, 50, strmobinfo(1, HoodedNinja), HoodedNinja, 2, .@n$+"::OnError"; + areamonster .@m$, 44, 40, 55, 50, strmobinfo(1, Assassin), Assassin, 4, .@n$+"::OnError"; + + // Sagratha's Army + areamonster .@m$, 44, 40, 55, 50, strmobinfo(1, Mouboo), Mouboo, 5, .@n$+"::OnError", Size_Medium, 2; + areamonster .@m$, 44, 40, 55, 50, strmobinfo(1, ForestMushroom), ForestMushroom, 2, .@n$+"::OnError", Size_Medium, 2; + + // Heal them because you should not be fighting (yet). + setunitdata(.BOSS, UDT_HP, getunitdata(.BOSS, UDT_MAXHP)); + setunitdata(.SAGRATHA, UDT_HP, getunitdata(.SAGRATHA, UDT_MAXHP)); + unitstop(.SAGRATHA); + unitstop(.BOSS); + + // Objective announce + mapannounce(.@m$, "##2"+l("Victory Conditions: Protect Sagratha!"), 0); + mapannounce(.@m$, "##2"+l("Victory Conditions: Defeat the assassin!"), 0); + mapannounce(.@m$, "##1"+l("Defeat Conditions: Your death!"), 0); + mapannounce(.@m$, "##1"+l("Defeat Conditions: Time run out!"), 0); + mapannounce(.@m$, "##1"+l("Defeat Conditions: Sagratha gets killed!"), 0); + + // Status cleanup + // PCBLOCK_HARD = PCBLOCK_ATTACK|PCBLOCK_SKILL|PCBLOCK_USEITEM|PCBLOCK_COMMANDS|PCBLOCK_SITSTAND|PCBLOCK_IMMUNE|PCBLOCK_CHAT|PCBLOCK_MOVE + setpcblock(PCBLOCK_HARD, false); + // TODO: After x time, reinforcements on both sides + // BOSS: “Kill 'em all!” + // SAGGY: “Beings of florest, come to my aid and protect me!” + addtimer(20000, .@n$+"::OnW01"); + end; + +OnW01: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(.BOSS, l("Kill 'em all!")); + unittalk(.SAGRATHA, l("It's an ambush!")); + + monster .@m$, 39, 39, strmobinfo(1, HoodedNinja), HoodedNinja, 1, .@n$+"::OnError"; + monster .@m$, 60, 39, strmobinfo(1, HoodedNinja), HoodedNinja, 1, .@n$+"::OnError"; + + monster .@m$, 39, 54, strmobinfo(1, Assassin), Assassin, 1, .@n$+"::OnError"; + monster .@m$, 60, 54, strmobinfo(1, Assassin), Assassin, 1, .@n$+"::OnError"; + addtimer(rand(10000,15000), .@n$+"::OnW02"); + end; + +OnW02: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(.SAGRATHA, l("Mouboos, come to my aid and protect me!")); + + monster .@m$, 46, 59, strmobinfo(1, Mouboo), Mouboo, 1, .@n$+"::OnError", Size_Medium, 2; + monster .@m$, 44, 61, strmobinfo(1, Mouboo), Mouboo, 1, .@n$+"::OnError", Size_Medium, 2; + monster .@m$, 49, 61, strmobinfo(1, Mouboo), Mouboo, 1, .@n$+"::OnError", Size_Medium, 2; + addtimer(30000, .@n$+"::OnW03"); + end; + +OnW03: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(.SAGRATHA, l("Do not think you can defeat me yet!")); + unittalk(.BOSS, l("You'll all die here!")); + + monster .@m$, 49, 34, strmobinfo(1, ForestMushroom), ForestMushroom, 1, .@n$+"::OnError", Size_Medium, 2; + monster .@m$, 47, 36, strmobinfo(1, Assassin), Assassin, 1, .@n$+"::OnError"; + monster .@m$, 52, 36, strmobinfo(1, Assassin), Assassin, 1, .@n$+"::OnError"; + addtimer(20000, .@n$+"::OnW04"); + end; + +OnW04: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(.SAGRATHA, l("It's far from over! You cannot defeat me!")); + + monster .@m$, 46, 59, strmobinfo(1, Wolvern), Wolvern, 1, .@n$+"::OnError", Size_Medium, 2; + monster .@m$, 44, 61, strmobinfo(1, ForestMushroom), ForestMushroom, 1, .@n$+"::OnError", Size_Medium, 2; + monster .@m$, 49, 61, strmobinfo(1, PoisonSpikyMushroom), PoisonSpikyMushroom, 1, .@n$+"::OnError", Size_Medium, 2; + end; + +// Assassin, HoodedNinja, HoodedAssassin (boss) +// Mouboo, ForestMushroom, Fluffy, Sagratha (boss) +OnSagrathaDie: + .@n$=instance_npcname(.name$); + addtimer(70, .@n$+"::OnSagrathaReallyDie", .PLAYERID); + end; + +OnSagrathaReallyDie: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + deltimer(.@n$+"::OnW01"); + deltimer(.@n$+"::OnW02"); + deltimer(.@n$+"::OnW03"); + deltimer(.@n$+"::OnW04"); + killmonster(.@m$, .@n$+"::OnError"); + unitkill(.BOSS); + mapannounce(.@m$, "##2"+l("You lose!"), 0); + + dispbottom l("You failed to protect Sagratha..."); // and will need to start the quest again..."); + setq1 HurnscaldQuest_Sagratha, 3; + setq3 HurnscaldQuest_Sagratha, 0; + die(); + end; + +OnSagrathaWin: + .@n$=instance_npcname(.name$); + addtimer(70, .@n$+"::OnSagrathaReallyWin", .PLAYERID); + @swin=true; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +OnSagrathaReallyWin: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + @swin=true; + + if (ispcdead()) + end; + + deltimer(.@n$+"::OnW01"); + deltimer(.@n$+"::OnW02"); + deltimer(.@n$+"::OnW03"); + killmonster(.@m$, .@n$+"::OnError"); + mapannounce(.@m$, "##2"+l("You win!"), 0); + mapannounce(.@m$, "##2"+l("You must go talk with the Obelisk!"), 0); + + npctalk l("YOU WHO DEFILE THIS PLACE..."); + // How does she even knows your name? + unittalk(.SAGRATHA, l("@@, watch out! The obelisk - it is talking!", strcharinfo(0))); + unitstop(.SAGRATHA); + + addtimer(300, .@n$+"::OnSW01"); + end; + +OnSW01: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unitwalk(.SAGRATHA, 50, 39); + addtimer(2000, .@n$+"::OnSW02"); + end; + +OnSW02: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + npctalk l("YOU WHO DEFILE THIS PLACE..."); + addtimer(2000, .@n$+"::OnSW03"); + end; + +OnSW03: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + // Perhaps “Cursed” instead of “Sacred”? + npctalk l("THIS IS THE MOUBOOTAUR TEMPLE RUINS... THIS PLACE IS SACRED..."); + addtimer(3000, .@n$+"::OnSW04"); + end; + +OnSW04: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + npctalk l("THE BLOOD HERE SPILLED TODAY... ONLY ENRAGES HIM MORE..."); + unittalk(.SAGRATHA, l("Yikes, the Moubootaur!")); + addtimer(3000, .@n$+"::OnSW05"); + end; + +OnSW05: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + npctalk l("LEAVE NOW... BEFORE IT BECOMES TOO LATE...."); + addtimer(3000, .@n$+"::OnSW06"); + end; + +OnSW06: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + if (getq(HurnscaldQuest_Sagratha) != 5) + end; + + mesn l("Sagratha"); + mesq l("@@, we should leave here now.", strcharinfo(0)); + next; + select + l("What is the Moubootaur?"), + l("I came here to ask for help with curses."), + l("WHAT IN THE NAME OF THE FREAKING PENGUIN WAS THAT?!"); + mes ""; + mesn l("Sagratha"); + mesq l("That's none of your bussiness! We should leave here NOW!"); + next; + mesn l("Sagratha"); + mesq l("You can tell me the details about why you're here on the way out..."); + next; + mesn l("Sagratha"); + mesq l("But for now, we must NOT enrage the Moubootaur!!"); + next; + @swin=false; + setq HurnscaldQuest_Sagratha, 6, 0, 0; + warp "014-5", 122, 139; + clear; + mesc l(".:: Victory ::."), 3; + mesc l("You successfully completed Sagratha's Campaign."), 3; + close; + +OnError: + end; + +OnInit: + .SAGRATHA=0; + end; + +} + +// Dummy NPC to fire #SaggyDungeonCore when you get close to battle scene +015-8-1,49,39,0 script #SaggyDungeonFire NPC_HIDDEN,1,0,{ + end; + +OnTouch: + if (instance_id() < 0) + end; + + if (!getq3(HurnscaldQuest_Sagratha)) { + .@in=getq2(HurnscaldQuest_Sagratha); + doevent(instance_npcname("#SaggyDungeonCore", .@in)+"::OnBegin"); + } + end; + +OnInit: + .distance=0; + end; + +} + + diff --git a/npc/015-8-1/puzzle.txt b/npc/015-8-1/puzzle.txt new file mode 100644 index 0000000..2cf669c --- /dev/null +++ b/npc/015-8-1/puzzle.txt @@ -0,0 +1,111 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// PUZZLES and TRAPS submodule - Sagratha's Cave Boss Room - 015-8-1 + +// Exit area +015-8-1,50,79,0 script #Exit01581 NPC_HIDDEN,1,0,{ + end; +OnTouch: + .@q=getq(HurnscaldQuest_Sagratha); + // Cheater Detected + if (!MAGIC_LVL || .@q < 5) { + setq HurnscaldQuest_Sagratha, 0, 0, 0; + sc_end SC_CASH_PLUSEXP; + sc_end SC_OVERLAPEXPUP; + sc_start SC_OVERLAPEXPUP, 300000, -20; + warp "Save", 0, 0; + end; + } + if (.@q == 5) { + dispbottom l("You are NOT allowed to leave here!"); + } else { + warp "015-8", 94, 21; + } + end; +} + + +015-8-1,0,0,0 script #SaggyBossTrap01 NPC_TRAP,0,0,{ + end; +OnTouchNPC: +OnTouch: + // instance_id() + if (instance_id() >= 0) + SteelTrap(rand2(10, 40), 5, any(0,1,1,2), instance_npcname(.name$)); + else + SteelTrap(rand2(10, 40), 5, any(0,1,1,2)); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay instance_npcname(.name$), NPC_TRAP; + end; + + +OnInstanceInit: + .@x=rand(20,80); + .@y=rand(20,80); + movenpc instance_npcname(.name$), .@x, .@y; + // It's on a wall, let's remove it + if (!checkcell(instance_mapname("015-8-1"), .@x, .@y, cell_chkpass)) { + disablenpc instance_npcname(.name$); + } + end; + +OnInit: + disablenpc .name$; + end; +} + +// Create more traps. (They can be on walls so amount is random >.<) +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap02 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap03 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap04 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap05 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap06 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap07 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap08 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap09 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap10 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap11 NPC_TRAP,0,0 +015-8-1,0,0,0 duplicate(#SaggyBossTrap01) #SaggyBossTrap12 NPC_TRAP,0,0 +// twelve traps should be enough + +// Transitional, dummy NPCs +015-8-1,25,34,0 script #LockedDoor01581A NPC_NO_SPRITE,0,0,{ + end; +OnTouch: + npctalkonce l("This door is locked."); + end; +} + +015-8-1,26,68,0 duplicate(#LockedDoor01581A) #LockedDoor01581B NPC_NO_SPRITE,0,0 +015-8-1,37,68,0 duplicate(#LockedDoor01581A) #LockedDoor01581C NPC_NO_SPRITE,0,0 + +015-8-1,53,31,0 script #PaperNote01581A NPC_NO_SPRITE,{ + npctalkonce any(l("You cannot decipher what's written in there."), l("It's written in Mananese, you cannot read."), l("It's full of Mouboo drawings."), l("This note is too old and difficult to read."), l("The only readable thing is an old stain of blood."), l("This note is not interesting, maybe the obelisk is more.")); + end; +OnInit: + .distance=2; + end; +} + +015-8-1,56,31,0 duplicate(#PaperNote01581A) #PaperNote01581B NPC_NO_SPRITE +015-8-1,59,31,0 duplicate(#PaperNote01581A) #PaperNote01581C NPC_NO_SPRITE +015-8-1,62,31,0 duplicate(#PaperNote01581A) #PaperNote01581D NPC_NO_SPRITE +015-8-1,65,31,0 duplicate(#PaperNote01581A) #PaperNote01581E NPC_NO_SPRITE +015-8-1,68,31,0 duplicate(#PaperNote01581A) #PaperNote01581F NPC_NO_SPRITE +015-8-1,71,31,0 duplicate(#PaperNote01581A) #PaperNote01581G NPC_NO_SPRITE + +015-8-1,42,51,0 script #Sign01581A NPC_NO_SPRITE,{ + npctalkonce l("It's a strange drawing of a Mouboo."); + dispbottom l("Where is here? What was this place used for?!"); + end; +OnInit: + .distance=2; + end; +} + diff --git a/npc/015-8/_import.txt b/npc/015-8/_import.txt new file mode 100644 index 0000000..27ea890 --- /dev/null +++ b/npc/015-8/_import.txt @@ -0,0 +1,7 @@ +// Map 015-8: Ancient Hideout +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/015-8/015-8_blackbox.txt", +"npc/015-8/_mobs.txt", +"npc/015-8/config.txt", +"npc/015-8/puzzle.txt", +"npc/015-8/sealedshrine.txt", diff --git a/npc/015-8/_mobs.txt b/npc/015-8/_mobs.txt new file mode 100644 index 0000000..843eed7 --- /dev/null +++ b/npc/015-8/_mobs.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 015-8: Ancient Hideout mobs +015-8,99,99,69,81 monster Mouboo 1023,32,30000,30000 +015-8,114,95,51,69 monster Robin Bandit 1153,16,30000,30000 +015-8,89,140,62,30 monster Angry Yellow Slime 1198,12,30000,30000 +015-8,97,57,62,46 monster Old Snake 1199,12,20000,20000 +015-8,67,94,34,79 monster Red Slime 1092,12,30000,30000 +015-8,100,56,48,36 monster Vampire Bat 1063,14,27000,22000 +015-8,103,95,69,81 monster Cave Maggot 1027,24,30000,30000 diff --git a/npc/015-8/config.txt b/npc/015-8/config.txt new file mode 100644 index 0000000..b03dce1 --- /dev/null +++ b/npc/015-8/config.txt @@ -0,0 +1,35 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// 015-8 Sagratha Cave Configuration File + +015-8 mapflag zone MMO +015-8-1 mapflag zone MMO + +015-8,99,179,0 script #Exit0158 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@q=getq(HurnscaldQuest_Sagratha); + .@q2=getq2(HurnscaldQuest_Sagratha); + // Cheater Detected + if (!MAGIC_LVL || .@q < 3) { + setq HurnscaldQuest_Sagratha, 0, 0, 0; + sc_end SC_CASH_PLUSEXP; + sc_end SC_OVERLAPEXPUP; + sc_start SC_OVERLAPEXPUP, 300000, -20; + warp "Save", 0, 0; + return false; + } + + mesc l("Return to Sagratha's House?"); + if (askyesno() == ASK_YES) { + .@mapn$=SaggyInstCheck(); + warp .@mapn$, 33, 35; + changemusic .@mapn$, "eric_matyas_ghouls.ogg"; + } + closeclientdialog; + close; +} + diff --git a/npc/015-8/puzzle.txt b/npc/015-8/puzzle.txt new file mode 100644 index 0000000..a62a56a --- /dev/null +++ b/npc/015-8/puzzle.txt @@ -0,0 +1,133 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// PUZZLES and TRAPS submodule - Sagratha's Cave - 015-8 +// Uses setq3 - setq1 stage 3 +// NPC_FAKIR + NPC_STATUE_FAFA + +// Chasm at third seal +015-8,120,158,0 script #SaggyPuzzleChasm NPC_HIDDEN,0,4,{ + end; +OnTouch: + do { + if (@saggychasmok) { + slide 125, 160; + closedialog; + end; + } + + mesc l("There is a chasm."); + select + l("Drop a coin to hear how deep it is"), + l("Climb on the walls!"), + l("Run and try jumping it!"), + l("Build a bridge with wooden logs!"), + l("Uhh... I'm scared!"); + mes ""; + switch (@menu) { + case 1: + if (!Zeny) { + mesc l("You don't have money."); + } else { + Zeny-=1; + mesc l("You throw a coin on the chasm."); + next; + mesc l("... ... ..."); + next; + mesc l("You don't hear anything. This chasm must be incredibly deep."); + next; + mesc l("Falling means certain death."); + } + break; + // Let's climb the wall! + case 2: + if (readparam2(bStr) < 20 || (!countitem(MinerGloves) && !countitem(LeatherGloves))) { + mesc l("You don't have proper equipment for it."); + } else { + mesc l("Taking your sturdy glove, you attempt to walk by the wall! You are betting your life on it!"); + next; + mesc l("A stone seemed to be loose, but you barely managed to avoid falling!"); + next; + mesc l("This is difficult, but you are giving your best!!"); + next; + mesc l("You are close... You are almost there!"); + next; + // We get a bit more than 50% - we take 60%, so your chances are + // higher than jumping... But you need two stats, instead of one! + // (Top will be 120 instead of 100 - thus, 720 out of 640) + .@ponderate=(readparam2(bStr)+readparam2(bVit))/100*60; + if (rand(0,1000) < 640-(.@ponderate*6)) { + mesc l("But unfortunately, a loose stone lodges and you fall to your death."), 1; + die(); + close; + } + mesc l("You succeed in crossing the chasm!"); + @saggychasmok=true; + } + break; + // Let's run it! + case 3: + if (readparam2(bAgi) < 20 || Weight > 1500) { + mesc l("You don't have enough agility / is carrying too much to attempt it."); + } else { + mesc l("You'll give your best and attempt to run!"); + next; + mesc l("You take distance and..."); + next; + mesc l("YOU JUMP FOR ALL IT IS WORTH!!"); + // (You can always fall as top is 600 of 640) + if (rand(0,1000) < 640-(readparam2(bAgi)*6)) { + mesc l("But unfortunately, it was harder than you thought. You are dead."), 1; + die(); + close; + } + mesc l("You succeed in crossing the chasm!"); + @saggychasmok=true; + } + break; + // Let's make a bridge! + case 4: + if (countitem(WoodenLog) < 5 || !getskilllv(TMW2_CRAFT)) { + mesc l("You don't have enough wood (5 @@) or crafting skills to do it.", getitemlink(WoodenLog)); + } else { + mesc l("..."); + next; + mesc l("After hours of hard work..."); + next; + mesc l("You succeed in crossing the chasm!"); + delitem WoodenLog, 5; + @saggychasmok=true; + } + break; + default: + close; + } + next; + } while (true); + close; +} + +015-8,124,158,0 script #SaggyPuzzleChasmSkip NPC_HIDDEN,0,4,{ + end; +OnTouch: + slide 119, 160; + end; +} + + +// Controls a chest/mimic on the trap room +015-8,0,0,0 script #SaggyChestCtrl NPC_HIDDEN,{ + end; + +OnDelay: + initnpctimer; + end; + +OnTimer180000: + stopnpctimer; +OnInit: + monster "015-8", 119+rand2(-3, 3), 35+rand2(-3, 3), "Treasure Chest", any(BronzeChest,BronzeMimic,SilverChest,SilverMimic), 1, "#SaggyChestCtrl::OnDelay"; + end; +} + diff --git a/npc/015-8/sealedshrine.txt b/npc/015-8/sealedshrine.txt new file mode 100644 index 0000000..d4e4170 --- /dev/null +++ b/npc/015-8/sealedshrine.txt @@ -0,0 +1,659 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// SEALED SHRINE submodule - Sagratha's Cave - 015-8 +// Uses setq3 - setq1 stage 3 +// NPC_FAKIR + NPC_STATUE_FAFA + +// Check for validity +function script SaggySealCheck { + .@q=getq(HurnscaldQuest_Sagratha); + .@q3=getq3(HurnscaldQuest_Sagratha); + // Cheater Detected + if (!MAGIC_LVL || .@q < 3) { + setq HurnscaldQuest_Sagratha, 0, 0, 0; + sc_end SC_CASH_PLUSEXP; + sc_end SC_OVERLAPEXPUP; + sc_start SC_OVERLAPEXPUP, 300000, -20; + warp "Save", 0, 0; + closeclientdialog; + end; + return false; + } + + // Okay, if it is not stage 3, we don't need to unseal + if (.@q != 3) + return false; + return true; +} + +// Seal Spawn +// (name, seal x, seal y, stageId, {seal map}) +function script SaggySealInit { + .@n$=getarg(0); + .@x=getarg(1); + .@y=getarg(2); + .@z=getarg(3); + .@m$=getarg(4, "015-8"); + + // Do nothing if @SaggySeal$ is busy - we will NOT begin another unsealing. + if (@SaggySeal$ != "" && !.@z) + end; + else + @SaggySeal$=.@n$; + + // You're dead, drop the script + if (ispcdead()) { + npctalk l("You are dead. Aborting."); + @SaggySeal$=""; + end; + } + + // Drain some Mana + // Max wave is 9... So it should cap at 10% + .@val=7+(.@z/3); + + if (Sp < MaxSp/100*.@val) { + npctalk l("You don't have enough mana to continue. The seal remains active."); + @SaggySeal$=""; + end; + } + percentheal 0, -(.@val); + + // BaseLevel: 37 + areamonster .@m$, .@x-1, .@y-1, .@x+1, .@y+1, "Seal Guardian", MagicGoblin, (.@z/3)+1; + + switch (.@z) { + case 0: + .@mobId=any(HouseMaggot,SlimeBlast); break; + case 1: + .@mobId=AngryScorpion; break; + case 2: + .@mobId=AngryBat; break; + case 3: + .@mobId=RedSlime; break; + case 4: + .@mobId=AngryRedScorpion; break; + case 5: + .@mobId=Bandit; break; + case 6: + .@mobId=Skeleton; break; + case 7: + .@mobId=BlueSlime; break; + case 8: + .@mobId=RedMushroom; break; + case 9: + .@mobId=BlackSlime; break; + default: + Exception("ERROR, INVALID Z VALUE FOR SAGRATHA SEAL: "+.@z, RB_DEFAULT|RB_IRCBROADCAST); + .@mobId=WickedMushroom; break; + } + + monster .@m$, .@x, .@y, "Seal Protector", .@mobId, 1; + + // Schedule next sequence + if (.@z+1 < 10) + .@nx$="0"+(.@z+1); + else + .@nx$=str(.@z+1); + // 3.5s each wave, giving you ~1s to kill/endure each "boss" + .@t=3500+(.@z*980); + + addtimer2(.@t, .@n$+"::OnBreakSeal"+.@nx$); + return; +} + +// Main menu - SaggySealTrueInit +// ( .name$, x, y ) +function script SaggySealTrueInit { + // Do nothing if @SaggySeal$ is busy - we will NOT begin another unsealing. + if (@SaggySeal$ != "") + end; + + // Okay, we can begin! (variable will be filled on SaggySealInit) + mesc l("Attempt to break the seal?"); + mesc l("Warning: This will drain mana and spawn monsters. You shall not leave this cave section!"); + if (askyesno() == ASK_YES) { + npctalk3 l("You started the seal break sequence. Please stand by."); + SaggySealInit(getarg(0), getarg(1), getarg(2), 0); + } + closeclientdialog; + return; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +015-8,0,0,0 script #SaggySeal1 NPC_SUMMONING_CIRC,0,0,{ + end; + +OnTouch: + .@q=getq(HurnscaldQuest_Sagratha); + .@q3=getq3(HurnscaldQuest_Sagratha); + // Check if it can be operated + if (!SaggySealCheck()) + end; + + // Check if this seal is ok + if (.@q3 & .sealId) { + npctalkonce l("This seal was already broken, proceed to the next seal!"); + end; + } + + // It is stage 3, so we will write to setq3 that the seal is open... Soon. + SaggySealTrueInit(.name$, .x, .y); + end; + +OnBreakSeal01: + SaggySealInit(.name$, .x, .y, 1); + end; + +OnBreakSeal02: + SaggySealInit(.name$, .x, .y, 2); + end; + +OnBreakSeal03: + SaggySealInit(.name$, .x, .y, 3); + end; + +OnBreakSeal04: + SaggySealInit(.name$, .x, .y, 4); + end; + +OnBreakSeal05: + SaggySealInit(.name$, .x, .y, 5); + end; + +OnBreakSeal06: + SaggySealInit(.name$, .x, .y, 6); + end; + +OnBreakSeal07: + SaggySealInit(.name$, .x, .y, 7); + end; + +OnBreakSeal08: + SaggySealInit(.name$, .x, .y, 8); + end; + +OnBreakSeal09: + SaggySealInit(.name$, .x, .y, 9); + end; + +OnBreakSeal10: + .@q3=getq3(HurnscaldQuest_Sagratha); + npctalk3 l("The seal was broken!"); + @SaggySeal$=""; + setq3 HurnscaldQuest_Sagratha, .@q3 | .sealId; + end; + +OnInit: + .sealId=1; + + setarray .xpos, 115, 119, 116, 121, 126, 125, 137, 136, 115; + setarray .ypos, 117, 117, 119, 122, 125, 115, 117, 123, 118; + .@z=rand(getarraysize(.xpos)); + movenpc .name$, .xpos[.@z], .ypos[.@z], 0; + end; +} + + + + + + + + + + + + + + +015-8,0,0,0 script #SaggySeal2 NPC_SUMMONING_CIRC,0,0,{ + end; + +OnTouch: + .@q=getq(HurnscaldQuest_Sagratha); + .@q3=getq3(HurnscaldQuest_Sagratha); + // Check if it can be operated + if (!SaggySealCheck()) + end; + + // Check if this seal is ok + if (.@q3 & .sealId) { + npctalkonce l("This seal was already broken, proceed to the next seal!"); + end; + } + + // It is stage 3, so we will write to setq3 that the seal is open... Soon. + SaggySealTrueInit(.name$, .x, .y); + end; + +OnBreakSeal01: + SaggySealInit(.name$, .x, .y, 1); + end; + +OnBreakSeal02: + SaggySealInit(.name$, .x, .y, 2); + end; + +OnBreakSeal03: + SaggySealInit(.name$, .x, .y, 3); + end; + +OnBreakSeal04: + SaggySealInit(.name$, .x, .y, 4); + end; + +OnBreakSeal05: + SaggySealInit(.name$, .x, .y, 5); + end; + +OnBreakSeal06: + SaggySealInit(.name$, .x, .y, 6); + end; + +OnBreakSeal07: + SaggySealInit(.name$, .x, .y, 7); + end; + +OnBreakSeal08: + SaggySealInit(.name$, .x, .y, 8); + end; + +OnBreakSeal09: + SaggySealInit(.name$, .x, .y, 9); + end; + +OnBreakSeal10: + .@q3=getq3(HurnscaldQuest_Sagratha); + npctalk3 l("The seal was broken!"); + @SaggySeal$=""; + setq3 HurnscaldQuest_Sagratha, .@q3 | .sealId; + end; + +OnInit: + .sealId=2; + + setarray .xpos, 39, 33, 43, 53, 51, 55, 60, 68, 63, 56; + setarray .ypos, 120, 130, 134, 132, 119, 121, 122, 120, 129, 124; + .@z=rand(getarraysize(.xpos)); + movenpc .name$, .xpos[.@z], .ypos[.@z], 0; + end; +} + + + + + + + + + + + + + + +015-8,0,0,0 script #SaggySeal3 NPC_SUMMONING_CIRC,0,0,{ + end; + +OnTouch: + .@q=getq(HurnscaldQuest_Sagratha); + .@q3=getq3(HurnscaldQuest_Sagratha); + // Check if it can be operated + if (!SaggySealCheck()) + end; + + // Check if this seal is ok + if (.@q3 & .sealId) { + npctalkonce l("This seal was already broken, proceed to the next seal!"); + end; + } + + // It is stage 3, so we will write to setq3 that the seal is open... Soon. + SaggySealTrueInit(.name$, .x, .y); + end; + +OnBreakSeal01: + SaggySealInit(.name$, .x, .y, 1); + end; + +OnBreakSeal02: + SaggySealInit(.name$, .x, .y, 2); + end; + +OnBreakSeal03: + SaggySealInit(.name$, .x, .y, 3); + end; + +OnBreakSeal04: + SaggySealInit(.name$, .x, .y, 4); + end; + +OnBreakSeal05: + SaggySealInit(.name$, .x, .y, 5); + end; + +OnBreakSeal06: + SaggySealInit(.name$, .x, .y, 6); + end; + +OnBreakSeal07: + SaggySealInit(.name$, .x, .y, 7); + end; + +OnBreakSeal08: + SaggySealInit(.name$, .x, .y, 8); + end; + +OnBreakSeal09: + SaggySealInit(.name$, .x, .y, 9); + end; + +OnBreakSeal10: + .@q3=getq3(HurnscaldQuest_Sagratha); + npctalk3 l("The seal was broken!"); + @SaggySeal$=""; + setq3 HurnscaldQuest_Sagratha, .@q3 | .sealId; + end; + +OnInit: + .sealId=4; + + setarray .xpos, 140, 143, 146, 147, 145, 143; + setarray .ypos, 140, 141, 139, 144, 147, 143; + .@z=rand(getarraysize(.xpos)); + movenpc .name$, .xpos[.@z], .ypos[.@z], 0; + end; +} + + + + + + + +// Sagratha Seals Eletronic Barriers +015-8,120,130,0 script #SaggySealBarrier1 NPC_HIDDEN,0,4,{ +OnTouch: + if (@SaggySeal$ != "") { + npctalk3 l("The seal retaliates! It was super effective. You are dead!"); + @SaggySeal$=""; + die(); + end; + } + end; +} + +015-8,74,142,0 script #SaggySealBarrier2 NPC_HIDDEN,0,4,{ +OnTouch: + if (@SaggySeal$ != "") { + npctalk3 l("The seal retaliates! It was super effective. You are dead!"); + @SaggySeal$=""; + die(); + end; + } + end; +} + +015-8,137,154,0 script #SaggySealBarrier3 NPC_HIDDEN,0,5,{ +OnTouch: + if (@SaggySeal$ != "") { + npctalk3 l("The seal retaliates! It was super effective. You are dead!"); + @SaggySeal$=""; + die(); + end; + } + end; +} + + + + + +// Magic Barrier +015-8,94,104,0 script Dog Statue#Saggy NPC_STATUE_FAFA,{ +OnDialog: + mesn; + mesq l("This is a magical barrier, powered by three seals in your part."); + next; + mesn; + mesq l("You must break all three seals to cross this barrier! For that, walk on them!"); + next; + mesn; + mesq l("Breaking a seal is easy, just flow mana on it for enough time to it shatter."); + next; + mesn; + mesq l("I, Fafa, the Magical Statue, grant you this power! Prove your worth! And never run from the seal, or die for your cowardice!"); + close; + +OnInit: + .distance=5; + end; +} + +015-8,93,109,0 script #SaggyBarrier NPC_HIDDEN,2,0,{ + end; + +OnTouch: + if (!SaggySealCheck()) { + slide 93, 106; + end; + } + .@q=getq(HurnscaldQuest_Sagratha); + .@q3=getq3(HurnscaldQuest_Sagratha); + + // Magic Number + if (.@q3 == 7) { + npctalk3 l("The seals having been broken, the barrier was weakened! You did it! The path is now open!"); + setq1 HurnscaldQuest_Sagratha, 4; + setq3 HurnscaldQuest_Sagratha, 0; + slide 93, 106; + } else { + getmapxy(.@m$, .@x, .@y, 0); + slide .@x, .@y+3; + dispbottom l("A powerful magic barrier repels you!"); + addtimer(200, "Dog Statue#Saggy::OnDialog"); + } + end; + +} + + +015-8,93,107,0 script #SaggyBarrier2 NPC_HIDDEN,2,0,{ + end; + +OnTouch: + // You should not be on this side of the barrier + if (SaggySealCheck()) { + setq HurnscaldQuest_Sagratha, 0, 0, 0; + sc_end SC_CASH_PLUSEXP; + sc_end SC_OVERLAPEXPUP; + sc_start SC_OVERLAPEXPUP, 300000, -20; + warp "Save", 0, 0; + closeclientdialog; + end; + } + slide 93, 110; + end; + +} + + + + + +// Shrine Statue Control +// SaggyShrineStatue( .name$ ) +function script SaggyShrineStatue { + .@n$=getarg(0); + .@q=getq(HurnscaldQuest_Sagratha); + + // Initial check + if (!MAGIC_LVL || .@q < 4) { + setq HurnscaldQuest_Sagratha, 0, 0, 0; + sc_end SC_CASH_PLUSEXP; + sc_end SC_OVERLAPEXPUP; + sc_start SC_OVERLAPEXPUP, 300000, -20; + warp "Save", 0, 0; + closeclientdialog; + end; + return false; + } + + mesn l("Guardian Statue"); + if (.@q != 4) { + mesq l("..."); + next; + mesc l("The statue doesn't replies."); + close; + } + mesq l("Decipher me... Or I will devour you..."); + next; + mesc l("Take the riddle?"), 1; + mesc l("Time limit for answer: 2 minutes"); + next; + if (askyesno() == ASK_YES) { + // You have unlimited time if you don't close the dialog + addtimer(120000, .@n$+"::OnScheduledDeath"); + if (0158_Riddle_BlackBox()) { + deltimer(.@n$+"::OnScheduledDeath"); + setq1 HurnscaldQuest_Sagratha, 5; + setq3 HurnscaldQuest_Sagratha, 0; + mesn l("Guardian Statue"); + mesq l("You shall pass. Beyond this gate, she waits for you."); + next; + mesn l("Guardian Statue"); + mesq l("Your worth shall be tested, and from inside, you shall not leave."); + } else { + deltimer(.@n$+"::OnScheduledDeath"); + doevent(.@n$+"::OnScheduledDeath"); + closeclientdialog; + } + } + return; +} + +// The Sealed Shrine Itself and the guardians +015-8,96,22,0 script #SaggyShrineRight NPC_FAKIR,{ + SaggyShrineStatue(.name$); + close; + +OnScheduledDeath: + npctalk3 l("Be devoured!"); + die(); + end; + +OnInit: + .distance=5; + end; +} + +015-8,93,22,0 script #SaggyShrineLeft NPC_FAKIR,{ + SaggyShrineStatue(.name$); + close; + +OnScheduledDeath: + npctalk3 l("Be devoured!"); + die(); + end; + +OnInit: + .distance=5; + end; +} + +015-8,94,20,0 script #ShrineGate NPC_NO_SPRITE,1,0,{ + + .@q=getq(HurnscaldQuest_Sagratha); + .@q3=getq3(HurnscaldQuest_Sagratha); + // Cheater Detected + if (!MAGIC_LVL || .@q < 4) { + setq HurnscaldQuest_Sagratha, 0, 0, 0; + sc_end SC_CASH_PLUSEXP; + sc_end SC_OVERLAPEXPUP; + sc_start SC_OVERLAPEXPUP, 300000, -20; + warp "Save", 0, 0; + closeclientdialog; + end; + return false; + } + + // Quest Status 4: Riddle pending + if (.@q == 4) { + npctalk3 l("The gates are firmly shut, no amount of force will break them."); + } + + // Quest Status 5: Riddle solved, access granted + if (.@q == 5) { + enable_items(); + mesc l(".:: The Mouboo Temple ::."), 3; + mesc l("WARNING: You are about to enter a HIGH-RISK zone."), 1; + mesc l("We advise you to change your equipment now."), 1; + if (getmapusers("015-8") > 1) + mesc l("THIS IS A SOLO FIGHT, TEAMS ARE NOT ALLOWED INSIDE."), 1; + next; + disable_items(); + if (askyesno() == ASK_YES) { + setq3 HurnscaldQuest_Sagratha, 0; + closeclientdialog; + + .@map2$=SaggyInstCheck(false); + warp .@map2$, 50, 78; + end; + } + close; + } + + // Quest Status 6: Boss defeated, no instance needed + if (.@q >= 6) { + warp "015-8-1", 50, 78; + } + end; +} + diff --git a/npc/016-1/_import.txt b/npc/016-1/_import.txt new file mode 100644 index 0000000..75e6e99 --- /dev/null +++ b/npc/016-1/_import.txt @@ -0,0 +1,7 @@ +// Map 016-1: La Marine First Deck +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/016-1/_mobs.txt", +"npc/016-1/captain.txt", +"npc/016-1/doors.txt", +"npc/016-1/laranja.txt", +"npc/016-1/teraa.txt", diff --git a/npc/016-1/_mobs.txt b/npc/016-1/_mobs.txt new file mode 100644 index 0000000..621c460 --- /dev/null +++ b/npc/016-1/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 016-1: La Marine First Deck mobs +016-1,31,26,9,4 monster Piou 1002,2,30000,20000 diff --git a/npc/016-1/captain.txt b/npc/016-1/captain.txt new file mode 100644 index 0000000..c6f1198 --- /dev/null +++ b/npc/016-1/captain.txt @@ -0,0 +1,178 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Temporary, placeholder. + +016-1,19,29,0 script Captain NPC_NARD,{ + .@price=410; + if (BaseLevel < 20 && !REBIRTH && !countitem(MirrorLakeArmor)) + goto L_TooWeak; + + mesn; + mesq l("Hi @@.", strcharinfo(0)); + next; + mesq l("You are currently at @@.", LOCATION$); + mesc l("Note: Onboard, Destructive, Fire, and AoE Magic are NOT allowed."); + mes ""; + + menu + rif(LOCATION$ != "Tulim", l("To Tulimshar.")), L_TTulim, + rif(LOCATION$ != "Hurns", l("To Hurnscald.")), L_THurns, + rif(LOCATION$ != "Nival", l("To Nivalis.")), L_TNival, + l("No, I'll save my money."), -; + + close; + +///// ---------- Tulimshar ---------- +L_TTulim: + .@x=(reputation("Tulim")+reputation(LOCATION$))/2; + if (.@x >= 50) .@x+=10; + .@price-=min(400, (.@x/10)*40); + + mes ""; + mesn; + mesq l("It'll cost you @@ GP.", .@price); + mes ""; + + if (Zeny < .@price) { + mes l("You still need @@ GP to afford it.", (.@price-Zeny)); + close; + } + + if (askyesno() != ASK_YES) + close; + + Zeny=Zeny-.@price; + PC_DEST$="Tulim"; + @timer_navio_running = 1; + + mes ""; + mesn; + mesq l("Tulimshar, right? The oldest human city-state!"); + next; + mesq l("I was planning to go there soon, anyway. All aboard!"); + close2; + goto L_DoWarp; + + + + + + + +///// ---------- Hurnscald ---------- +L_THurns: + .@x=(reputation("Hurns")+reputation(LOCATION$))/2; + if (.@x >= 50) .@x+=10; + .@price-=min(400, (.@x/10)*40); + + mes ""; + mesn; + mesq l("It'll cost you @@ GP.", .@price); + mes ""; + + // Should not even be allowed on the ship, but well. Double-check + if (!$HURNS_LIBDATE) { + mesc l("BUG, REPORT ME! Hurncald Liberation Day check FAIL"), 1; + close; + } + + if (Zeny < .@price) { + mes l("You still need @@ GP to afford it.", (.@price-Zeny)); + close; + } + + if (askyesno() != ASK_YES) + close; + + Zeny=Zeny-.@price; + PC_DEST$="Hurns"; + + mes ""; + mesn; + mesq l("Hurnscald? Small farming towns are always nice to visit."); + next; + mesq l("I was planning to go there soon, anyway. All aboard!"); + close2; + goto L_DoWarp; + + + + + + + +///// ---------- Nivalis ---------- +L_TNival: + .@x=(reputation("Nival")+reputation(LOCATION$))/2; + if (.@x >= 50) .@x+=10; + .@price-=min(400, (.@x/10)*40); + + // Nivalis Liberation Day. Zero could cause weird bugs. + if (!$NIVALIS_LIBDATE) + .@price=1; + + // Maybe this destination is NOT AVAILABLE + if (!$NLIB_DAY && !$NIVALIS_LIBDATE) { + mesn; + mesq l("I would love to, but the Monster King laid siege there."); + mesc l("A Game Master is required to begin the Liberation Day."), 1; + close; + } + + mes ""; + mesn; + mesq l("It'll cost you @@ GP.", .@price); + mes ""; + + if (Zeny < .@price) { + mes l("You still need @@ GP to afford it.", (.@price-Zeny)); + close; + } + + if (askyesno() != ASK_YES) + close; + + Zeny=Zeny-.@price; + PC_DEST$="Nival"; + + mes ""; + mesn; + mesq l("Nivalis? It's frozen during the whole year! I hope you have good ice gear and a high level..."); + next; + mesq l("I was planning to go there soon, anyway. All aboard!"); + close2; + goto L_DoWarp; + + + + + + + +///// ---------------- Core Utils +L_TooWeak: + mesn; + mesq l("The sea route I take is very dangerous, and full of pirates. You're too weak to travel with me."); + close; + +L_DoWarp: + addtimer nard_time(PC_DEST$), "#MarineShip::OnEvent"; + @timer_navio_running = 1; + warp "016-6", 40, 32; + + // 10% base chance of Pirate Attack! + // Each level INCREASES this in 0.1%. + // So for a level 40 player, chances are 14%. + if (rand(1, 10000) < 1000+(BaseLevel*10)) + addtimer rand(3000,6000), "#MarineShipAttack::OnEvent"; + end; + + +OnInit: + .sex = G_MALE; + .distance = 5; + end; + +} diff --git a/npc/016-1/doors.txt b/npc/016-1/doors.txt new file mode 100644 index 0000000..0c7ebd6 --- /dev/null +++ b/npc/016-1/doors.txt @@ -0,0 +1,26 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// La Marine ship Doors NPCs. + +016-1,21,25,0 script #MarineToOutside NPC_HIDDEN,0,0,{ + +OnTouch: + if (LOCATION$ == "Hurns") { + warp "012-1", 156, 65; + close; + } + if (LOCATION$ == "Tulim") { + warp "003-1", 119, 25; + close; + } + if (LOCATION$ == "Nival") { + warp "019-2", 119, 113; + close; + } + mesc l("Oh noes! The door is locked!! Quick! Call a GM!!!"), 1; + close; +} + + diff --git a/npc/016-1/laranja.txt b/npc/016-1/laranja.txt new file mode 100644 index 0000000..f293fb3 --- /dev/null +++ b/npc/016-1/laranja.txt @@ -0,0 +1,110 @@ +// TMW2 Script +// Author: +// Saulc +// Povo +// Description: +// Laranja, orange in portuges is a orange dye seller in ship + +016-1,25,24,0 script Laranja NPC_FEMALE,{ + + mesn; + mesq l("I'm Laranja."); + next; + mesn; + mesq l("My father runs a dye business, I'm in charge of selling %s around the world.", getitemlink(OrangeDye)); + next; + mesn; + mesq l("How can I help you?"); + mes ""; + menu + l("Orange Dye? How much does it cost?"), -, + l("Nothing. Take care!"), L_Quit; + +L_Dye: + mesn; + mesq l("Thanks to pirate activity, dye has become more difficult to obtain lately."); + mesq l("In fact, %s is considered by most to be rare these days.", getitemlink(OrangeDye)); + next; + if (BaseLevel < 45) { + mesn; + mesq l("Since supplies are limited, I currently prioritize orders placed by higher level players."); + mesq l("Come back when you are stronger and we can trade."); + next; + goto L_Quit; + } + + mesn; + mesq l("I can trade some with you. Just bring me the following cash payment and supplies:"); + mes ""; + mesq l("%s/%s GP",fnum(Zeny), fnum(3000)); + mesq l("%d/%d %s",countitem(BottleOfTonoriWater), 1, getitemlink(BottleOfTonoriWater)); + mesq l("%d/%d %s",countitem(GambogeHerb), 60, getitemlink(GambogeHerb)); + mesq l("%d/%d %s",countitem(AlizarinHerb), 70, getitemlink(AlizarinHerb)); + mesq l("%d/%d %s",countitem(Piberries), 5, getitemlink(Piberries)); + mesq l("%d/%d %s",countitem(PileOfAsh), 5, getitemlink(PileOfAsh)); + compareandsetq TulimsharQuest_Laranja, 0, 1; + next; + select + l("Yeah, I need one."), + l("No thank you."); + mes ""; + if (@menu == 2) + goto L_Quit; + if (countitem(BottleOfTonoriWater) >= 1 && + countitem(GambogeHerb) >= 60 && + countitem(AlizarinHerb) >= 75 && + countitem(Piberries) >= 5 && + countitem(PileOfAsh) >= 5 && Zeny >= 1500) { + inventoryplace OrangeDye, 1, EmptyBottle, 1; + delitem BottleOfTonoriWater, 1; + delitem GambogeHerb,60; + delitem AlizarinHerb, 70; + delitem Piberries, 5; + delitem PileOfAsh, 5; + Zeny=Zeny-1500; + getitem OrangeDye, 1; + getitem EmptyBottle, 1; + if (getq(TulimsharQuest_Laranja) == 1) { + setq TulimsharQuest_Laranja, 2; + getexp 9000, 0; + } + + mesn; + mesq l("Pleasure doing business with you! Do you want any more?"); + next; + goto L_Dye; + } else { + mesn; + mesq l("Looks like you are a bit short on supplies. Come back when you have everything I need."); + } + close; + +L_Quit: + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, ShortTankTop); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 10); + setunitdata(.@npcId, UDT_HAIRCOLOR, 9); + + .sex = G_FEMALE; + .distance = 4; + end; + +OnInstanceInit: + .@npcId = getnpcid(instance_npcname(.name$)); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, ShortTankTop); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 10); + setunitdata(.@npcId, UDT_HAIRCOLOR, 9); + + end; +} diff --git a/npc/016-1/teraa.txt b/npc/016-1/teraa.txt new file mode 100644 index 0000000..3cad0c3 --- /dev/null +++ b/npc/016-1/teraa.txt @@ -0,0 +1,17 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Tortuga Renting (var MOUBOO_RENTTIME ) + +016-1,32,28,0 script Teraa NPC_ELVEN_MAN_STANDING,{ + TortugaRent(); + closeclientdialog; + goodbye(); + close; + +OnInit: + .distance=4; + end; +} + diff --git a/npc/016-6/_import.txt b/npc/016-6/_import.txt new file mode 100644 index 0000000..62a745b --- /dev/null +++ b/npc/016-6/_import.txt @@ -0,0 +1,5 @@ +// Map 016-6: Ocean +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/016-6/_mobs.txt", +"npc/016-6/main.txt", +"npc/016-6/mapflags.txt", diff --git a/npc/016-6/_mobs.txt b/npc/016-6/_mobs.txt new file mode 100644 index 0000000..06d4415 --- /dev/null +++ b/npc/016-6/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 016-6: Ocean mobs +016-6,33,32,9,2 monster Ocean Croc 1133,4,16000,10000 diff --git a/npc/016-6/main.txt b/npc/016-6/main.txt new file mode 100644 index 0000000..6f62007 --- /dev/null +++ b/npc/016-6/main.txt @@ -0,0 +1,63 @@ +// TMW2 script +// Originals from TMW-BR +// Imported by Jesusalva + +016-6,0,0,0 script #MarineShip NPC_HIDDEN,117,29,{ + end; + +OnEvent: + // Handle travel + if (@timer_navio_running == 0) end; + if (PC_DEST$ == "Tulim") goto L_Tulim; + else if (PC_DEST$ == "Hurns") goto L_Hurns; + else if (PC_DEST$ == "Nival") goto L_Nival; + else goto L_Error; + end; + +L_Tulim: + PC_DEST$ = ""; + EnterTown("Tulim"); + @timer_navio_running = 0; + warp "003-1", 119, 25; + message strcharinfo(0), l("@@ disembarks at Tulimshar.", strcharinfo(0)); + goto L_CleanUp; + +L_Hurns: + PC_DEST$ = ""; + EnterTown("Hurns"); + @timer_navio_running = 0; + warp "012-1", 156, 65; + message strcharinfo(0), l("@@ disembarks at Hurnscald.", strcharinfo(0)); + goto L_CleanUp; + +L_Nival: + PC_DEST$ = ""; + EnterTown("Nival"); + @timer_navio_running = 0; + warp "019-2", 119, 113; + message strcharinfo(0), l("@@ disembarks at Nivalis.", strcharinfo(0)); + goto L_CleanUp; + +L_Error: + consolebug "ERROR: PLAYER INVALID PC_DEST ON #MarineShip: " + PC_DEST$; + PC_DEST$ = ""; + @timer_navio_running = 0; + warp "000-1", 22, 22; + dispbottom l("An error on your travel happened. Please report."); + goto L_CleanUp; + +L_CleanUp: + // Clean up pirate ship, if needed. + if (mobcount("016-7", "#MarineShipAttack::OnPirateDie") && !getmapusers("016-7")) { + killmonster("016-7", "all"); + } + if (mobcount("016-7", "#MarineShipAttack::OnVictory") && !getmapusers("016-7")) { + killmonster("016-7", "all"); + } + if (mobcount("016-7", "#MarineShipAttack::OnVictory") > 1) { + killmonster("016-7", "#MarineShipAttack::OnVictory"); + monster "016-7", 38, 27, "Pirate Captain", OceanPirate, "#MarineShipAttack::OnVictory"; + } + end; +} + diff --git a/npc/016-6/mapflags.txt b/npc/016-6/mapflags.txt new file mode 100644 index 0000000..27a1ef2 --- /dev/null +++ b/npc/016-6/mapflags.txt @@ -0,0 +1 @@ +016-6 mapflag zone ship diff --git a/npc/016-7/_import.txt b/npc/016-7/_import.txt new file mode 100644 index 0000000..45f28ee --- /dev/null +++ b/npc/016-7/_import.txt @@ -0,0 +1,5 @@ +// Map 016-7: Pirate Attack +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/016-7/_mobs.txt", +"npc/016-7/main.txt", +"npc/016-7/mapflags.txt", diff --git a/npc/016-7/_mobs.txt b/npc/016-7/_mobs.txt new file mode 100644 index 0000000..b5b9691 --- /dev/null +++ b/npc/016-7/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 016-7: Pirate Attack mobs +016-7,37,29,14,4 monster Ocean Croc 1133,6,36000,30000 diff --git a/npc/016-7/main.txt b/npc/016-7/main.txt new file mode 100644 index 0000000..7dcf563 --- /dev/null +++ b/npc/016-7/main.txt @@ -0,0 +1,65 @@ +// TMW2 script +// Author: +// Saulc +// Jesusalva +// Description: +// Handles pirate attacks + +016-7,0,0,0 script #MarineShipAttack NPC_HIDDEN,117,29,{ + end; + +OnEvent: + warp "016-7", 31, 33; + dispbottom l("##1The ship is under a pirate's attack! ##BKill all or survive!"); + addtimercount "#MarineShip::OnEvent", 10000; + addtimer 2000, "#MarineShipAttack::OnStart"; + end; + +OnStart: + // Define number of pirates based on player level. + // Player count is not important here, as this is run for every player. + .@c=(BaseLevel/15); + areamonster "016-7", 23, 25, 51, 30, strmobinfo(1, OceanPirate), OceanPirate, .@c, "#MarineShipAttack::OnPirateDie"; + end; + +OnPirateDie: + addtimercount "#MarineShip::OnEvent", 5000; + if (!mobcount("016-7", "#MarineShipAttack::OnPirateDie")) { + addtimercount "#MarineShip::OnEvent", 15000; + mapannounce "016-7", l("The pirate ship captain appear! TAKE CARE!!"), bc_map; + .@pirate=monster("016-7", 38, 27, "Pirate Captain", OceanPirate, 1, "#MarineShipAttack::OnVictory"); + sc_start SC_INCMHP, 900000, 1000, 10000, SCFLAG_FIXEDTICK|SCFLAG_NOAVOID|SCFLAG_NOICON, .@pirate; + sc_start SC_ATTHASTE_POTION1, 900000, 40, 10000, SCFLAG_FIXEDTICK|SCFLAG_NOAVOID|SCFLAG_NOICON, .@pirate; + sc_start SC_INCHIT, 900000, 1000, 10000, SCFLAG_FIXEDTICK|SCFLAG_NOAVOID|SCFLAG_NOICON, .@pirate; + } + end; + +OnVictory: + if (rand2(10000) < REBIRTH+((readparam2(bLuk) + readparam2(bVit)) / 2)) + getitem PirateBandana, 1; + areatimer("016-7", 23, 25, 52, 35, 10, "#MarineShipAttack::OnReward"); + end; + +OnReward: + // That would give 10% from missing exp, but I didn't like it. + //getexp ((NextBaseExp-BaseExp)/10), 0; + addtimercount "#MarineShip::OnEvent", 3500; + + // Lv 40 rewards: 400 exp, 40 jxp, 200~600 GP + getexp BaseLevel*10, BaseLevel; + Zeny=Zeny+BaseLevel*rand2(5,15); + //getitem SailorShirt, 1; // I already tried to do this before...? + // 7% chance to get Crazy Rum + if (rand2(10000) < 700+(readparam2(bLuk)*3)) + getitem CrazyRum, 1; + dispbottom l("Congratulations!"); + if (isin("016-7", 23, 25, 52, 35)) + addtimer(3000, "#MarineShipAttack::OnResumeTravel"); + end; + +OnResumeTravel: + if (@timer_navio_running) + warp "016-6", 31, 33; + end; +} + diff --git a/npc/016-7/mapflags.txt b/npc/016-7/mapflags.txt new file mode 100644 index 0000000..ddc0c01 --- /dev/null +++ b/npc/016-7/mapflags.txt @@ -0,0 +1 @@ +016-7 mapflag nosave 000-1,22,22 diff --git a/npc/017-0/_import.txt b/npc/017-0/_import.txt new file mode 100644 index 0000000..8597a69 --- /dev/null +++ b/npc/017-0/_import.txt @@ -0,0 +1,5 @@ +// Map 017-0: Mystic Forest +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-0/_mobs.txt", +"npc/017-0/_warps.txt", +"npc/017-0/wizard.txt", diff --git a/npc/017-0/_mobs.txt b/npc/017-0/_mobs.txt new file mode 100644 index 0000000..85cebf7 --- /dev/null +++ b/npc/017-0/_mobs.txt @@ -0,0 +1,28 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-0: Mystic Forest mobs +017-0,54,49,26,23 monster Centaur 1139,8,90000,80000 +017-0,55,40,32,19 monster Living Potato 1181,15,60000,45000 +017-0,57,76,20,19 monster Fluffy 1022,8,60000,45000 +017-0,171,43,81,19 monster Living Potato 1181,15,60000,45000 +017-0,241,92,43,51 monster Wind Fairy 1185,11,60000,45000 +017-0,222,154,25,49 monster Nature Fairy 1186,12,60000,45000 +017-0,201,182,25,49 monster Forest Mushroom 1060,13,60000,45000 +017-0,163,196,25,29 monster Tipiu 1015,8,60000,45000 +017-0,72,123,26,19 monster Archant 1026,8,60000,45000 +017-0,102,85,19,19 monster Skeleton 1137,8,60000,45000 +017-0,144,107,25,49 monster Forain 1061,12,60000,45000 +017-0,164,89,30,24 monster Scar 1045,8,60000,45000 +017-0,95,178,39,43 monster Sea Slime Mother 1244,8,60000,45000 +017-0,94,123,26,25 monster Bluepar 1177,8,60000,45000 +017-0,237,150,25,24 monster Red Mushroom 1042,18,60000,45000 +017-0,145,127,26,24 monster Wicked Mushroom 1176,12,60000,45000 +017-0,149,98,29,53 monster Vampire Bat 1063,18,60000,45000 +017-0,253,186,6,11 monster Training Dummy 1021,6,10000,10000 +017-0,53,83,43,59 monster Red Butterfly 1025,30,60000,45000 +017-0,262,182,66,63 monster Red Butterfly 1025,30,60000,45000 +017-0,162,121,133,110 monster Cyan Butterfly 1172,140,60000,30000 +017-0,125,131,108,114 monster Alizarin Plant 1188,45,45000,45000 +017-0,161,130,98,102 monster Cobalt Plant 1136,45,45000,45000 +017-0,187,119,98,102 monster Gamboge Plant 1134,45,45000,45000 +017-0,156,143,98,102 monster Mauve Plant 1135,45,45000,45000 +017-0,152,112,31,32 monster Shadow Plant 1189,25,60000,45000 diff --git a/npc/017-0/_warps.txt b/npc/017-0/_warps.txt new file mode 100644 index 0000000..499b7ac --- /dev/null +++ b/npc/017-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-0: Mystic Forest warps +017-0,23,78,0 warp #017-0_23_78 0,3,017-1,229,86 diff --git a/npc/017-0/wizard.txt b/npc/017-0/wizard.txt new file mode 100644 index 0000000..966ca0a --- /dev/null +++ b/npc/017-0/wizard.txt @@ -0,0 +1,31 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Wizard at Magic Academy Entrance +// TODO: Training for Academy Students + +017-0,245,223,0 script Red Wizard NPC_RED_WIZARD_F,{ + mesn; + mesq l("Hello. I am the instructor assigned to the magic range training field."); + if (!MAGIC_LVL) + close; + next; + mesn; + mesq l("Do you wish to return to the Academy?"); + next; + select + l("Not yet."), + l("Yes please."); + mes ""; + closeclientdialog; + if (@menu == 2) + warp "027-1", 46, 91; + close; + +OnInit: + .distance = 4; + .sex = G_FEMALE; + end; +} + diff --git a/npc/017-1/_import.txt b/npc/017-1/_import.txt new file mode 100644 index 0000000..42925f9 --- /dev/null +++ b/npc/017-1/_import.txt @@ -0,0 +1,24 @@ +// Map 017-1: Land Of Fire Village +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-1/017-1_stranger_blackbox.txt", +"npc/017-1/_mobs.txt", +"npc/017-1/_warps.txt", +"npc/017-1/boringnpc.txt", +"npc/017-1/drowned_man.txt", +"npc/017-1/estate.txt", +"npc/017-1/fairy_collector.txt", +"npc/017-1/guards.txt", +"npc/017-1/guild.txt", +"npc/017-1/mapflags.txt", +"npc/017-1/misc.txt", +"npc/017-1/nowhere_man.txt", +"npc/017-1/paxel.txt", +"npc/017-1/pet_detective.txt", +"npc/017-1/roger.txt", +"npc/017-1/shops.txt", +"npc/017-1/signs.txt", +"npc/017-1/soul-menhir.txt", +"npc/017-1/stranger.txt", +"npc/017-1/town.txt", +"npc/017-1/townhall.txt", +"npc/017-1/wateranimation.txt", diff --git a/npc/017-1/_mobs.txt b/npc/017-1/_mobs.txt new file mode 100644 index 0000000..fd9ccb5 --- /dev/null +++ b/npc/017-1/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-1: Land Of Fire Village mobs +017-1,105,129,75,76 monster Cyan Butterfly 1172,40,30000,30000 +017-1,125,69,103,47 monster Squirrel 1053,25,30000,30000 +017-1,123,176,103,58 monster Living Potato 1181,36,30000,30000 +017-1,128,122,108,114 monster Alizarin Plant 1188,45,45000,45000 diff --git a/npc/017-1/_warps.txt b/npc/017-1/_warps.txt new file mode 100644 index 0000000..d20e41f --- /dev/null +++ b/npc/017-1/_warps.txt @@ -0,0 +1,14 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-1: Land Of Fire Village warps +017-1,180,193,0 warp #017-1_180_193 0,0,018-2,63,105 +017-1,152,224,0 warp #017-1_152_224 0,0,018-1,32,68 +017-1,119,60,0 warp #017-1_119_60 0,0,017-2,41,30 +017-1,104,103,0 warp #017-1_104_103 0,0,014-4,95,33 +017-1,92,88,0 warp #017-1_92_88 2,0,017-3,32,44 +017-1,150,87,0 warp #017-1_150_87 0,0,017-4,27,41 +017-1,155,87,0 warp #017-1_155_87 0,0,017-4,35,41 +017-1,133,155,0 warp #017-1_133_155 0,0,017-5,23,33 +017-1,37,140,0 warp #017-1_37_140 0,1,017-6,29,43 +017-1,37,155,0 warp #017-1_37_155 0,1,017-6,29,52 +017-1,37,132,0 warp #017-1_37_132 0,1,017-6,29,31 +017-1,230,87,0 warp #017-1_230_87 0,3,017-0,24,78 diff --git a/npc/017-1/boringnpc.txt b/npc/017-1/boringnpc.txt new file mode 100644 index 0000000..2ffcf29 --- /dev/null +++ b/npc/017-1/boringnpc.txt @@ -0,0 +1,67 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Permanently repeatable quest, without any special limit + +017-1,123,58,0 script Boring NPC NPC_RUMLY,{ + .@price=35; + mesn; + mesq l("Hwellow. I am a boring NPC which makes green dye."); // Typo on purpose: Uwhaaaaah (as in waking up) + hello + next; + mesn; + mesq l("Hwaaaah... Man, I'm bored. Will you be getting @@?", getitemlink(GreenDye)); + if ($EVENT$ == "Patrick") { + mesc l("It's St. Patrick Day, so be warned everyone wants a green dye... So, I raise the prices."); + .@price*=2; + } + next; + mesn; + mes l("For you, boring person like me, It'll cost the small amount of:"); + mes l("* @@/@@ @@", countitem(CobaltHerb), .@price+10, getitemlink(CobaltHerb)); + mes l("* @@/@@ @@", countitem(GambogeHerb), .@price+10, getitemlink(GambogeHerb)); + mes l("* @@/@@ @@", countitem(BugLeg), .@price-10, getitemlink(BugLeg)); + mes l("@@/@@ GP", format_number(Zeny), format_number(.@price*2)); + next; + select + l("Do it!"), + l("What a rip-off!"); + mes ""; + if (@menu == 2) { + mesn; + mesq l("Aha! I knew you were a boring person..."); + close; + } + if ( + countitem(CobaltHerb) < .@price+10 || + countitem(GambogeHerb) < .@price+10 || + countitem(BugLeg) < .@price-10 || + Zeny < .@price*2 + ) + goto L_NotEnough; + inventoryplace GreenDye, 1; + delitem CobaltHerb, .@price+10; + delitem GambogeHerb, .@price+10; + delitem BugLeg, .@price-10; + Zeny-=.@price*2; + getitem GreenDye, 1; + getexp 200, 100; // Job Experience >>> Base Experience + mesn; + mesq l("There you go, boring person... You will look like the trees here. Ugh, disgusting."); + close; + +L_NotEnough: + mesn; + mesq l("Please don't bore me even more... That's clearly not everything."); + next; + mesn; + mesq l("I wonder if anyone will notice or care if I take a nap here..."); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/017-1/drowned_man.txt b/npc/017-1/drowned_man.txt new file mode 100644 index 0000000..9604612 --- /dev/null +++ b/npc/017-1/drowned_man.txt @@ -0,0 +1,177 @@ +// TMW2/TMWBR Script +// Author: +// Jesusalva +// Description: +// Access to Lilit - He aids you in jumping off the bridge and reaching Lilit +// Of course, to swim such large distance, you need to have plenty vit and str... +// Right spot: Between the crack (shallow) and the plant (shallow). + +// .@q = LilitQuest_Access +// 0 - Access not granted +// 1 - Access granted +// 2 - Tree Minigame complete. + +// TODO: Only allow "I want to swim" if the game knows you know about Lilit +// That is what Saulc wants, after all + +017-1,160,227,0 script #DrownedMan NPC_NO_SPRITE,{ + if (!isin("017-1", 158, 222, 1)) + end; + mesc l("Someone seems to be drowned in the water."); + select + l("Hello! Do you need help?"), + l("Why shouldn't I jump here?"), + l("But I want to swim!"); + mes ""; + switch (@menu) + { + case 1: + mesn l("Drowned Man"); + mesq l("I'm afraid you can't help me... I still have one HP left."); + next; + mesn l("Drowned Man"); + mesq l("I also broke my bones, and I was equipping a weapon which prevents HP regen..."); + next; + mesn l("Drowned Man"); + mesq l("I did some tritan friends, though. They teached me how to breath underwater. It's not so bad."); + next; + mesn l("Drowned Man"); + mesq l("In the first eight months I wanted to go back, but now I'm pretty used to living here. Please don't rescue me."); + break; + case 2: + mesn l("Drowned Man"); + mesq l("Because it may be a fatal fall!"); + next; + mesn l("Drowned Man"); + mesq l("Look the height of this bridge! Worse, the water around here is pretty shallow."); + next; + mesn l("Drowned Man"); + mesq l("Well, there might be a deep water spot, but it would still be dangerous."); + next; + mesn l("Drowned Man"); + mesq l("Unless you're looking forward a death penalty, DO NOT JUMP!"); + // We're in town, are you stupid >.> + break; + case 3: + mesn l("Drowned Man"); + mesq l("Well, you see, you can't jump very far. And the water near land is shallow."); + next; + mesn l("Drowned Man"); + mesq l("I guess, there might be a single spot where water is a bit deeper, but I wasn't lucky."); + next; + mesn l("Drowned Man"); + mes l("Remember to store somewhere any heavy stuff you might be carrying."); + mes l("I guess you cannot bring more than 1kg with full health. Less, if you're hurt."); + next; + mesn l("Drowned Man"); + mesq l("Also, you need vitality and strength to survive the swimming trip. The closest island is very far away."); + mesc l("You need at least 35 str and 35 vit to do the trip safely - bonuses not counted - or you will lose HP."); // Can be safely mixed + break; + } + close; + +OnWarn: + @lofcanjump=true; + npctalkonce l("@@, don't jump there! It may be fatal!", strcharinfo(0)); + end; + +// DO NOT LOWER +OnInit: + .distance=7; + end; +} + +// Warning Event +017-1,158,222,0 script #DrownedManArea NPC_NO_SPRITE,0,0,{ + end; +OnTouch: + doevent("#DrownedMan::OnWarn"); + end; +OnInit: + .distance=1; + end; +} + + +// Any Jump. Code is obfuscated to inhibit cheaters :< +// Of course, I could use a blackbox too, but this is not the purpose. +// You don't want to solve the map hint, then you'll solve my formula!! /tableflip +017-1,162,226,0 script #JumpArea01 NPC_NO_SPRITE,0,0,{ + // Do you want to jump? + if (!@lofcanjump) { + dispbottom l("What interesting water. Just don't ask me why."); + end; + } + + // Are you in the bridge? + if (!isin("017-1", 158, 222, 1)) { + dispbottom l("I'm too far to jump there."); + end; + } + + mesc l("Are you really going to jump here?"); + next; + if (askyesno() == ASK_YES) { + // Do the jump! + closeclientdialog; + slide .x, .y; + if (is_between(5220, 5270, .x*32) && is_between(7330, 7390, .y*32)) + goto L_GoodJump; + dispbottom l("The water was too shallow..."); + die(); + end; + } + closeclientdialog; + end; + +L_GoodJump: + dispbottom l("You jump in deep waters."); + // You can only carry exact 2kg with full health. + // Weight will be 2000g - so penalty will be 100% HP. + .@penalty=max(0, (Weight/20)-1); + percentheal -.@penalty, 0; + @finalhp = Hp; + + // vit/str counts on swimming minigame + mesn l("Drowned Man"); + mesq l("Good job! I wish you luck, because now you need to SWIM!"); + next; + closeclientdialog; + setparam(Hp, @finalhp); + @finalhp=0; + addtimer(3000, "#01850SwimmingCtrl::OnLoop"); + warp "018-5-0", 20, 25; + end; + +OnInit: + .alwaysVisible=true; + end; +} + +017-1,162,227,0 duplicate(#JumpArea01) #JumpArea02 NPC_NO_SPRITE +017-1,162,228,0 duplicate(#JumpArea01) #JumpArea03 NPC_NO_SPRITE +017-1,162,229,0 duplicate(#JumpArea01) #JumpArea04 NPC_NO_SPRITE +017-1,162,230,0 duplicate(#JumpArea01) #JumpArea05 NPC_NO_SPRITE +017-1,162,231,0 duplicate(#JumpArea01) #JumpArea06 NPC_NO_SPRITE + +017-1,163,226,0 duplicate(#JumpArea01) #JumpArea07 NPC_NO_SPRITE +017-1,163,227,0 duplicate(#JumpArea01) #JumpArea08 NPC_NO_SPRITE +017-1,163,228,0 duplicate(#JumpArea01) #JumpArea09 NPC_NO_SPRITE +017-1,163,229,0 duplicate(#JumpArea01) #JumpArea10 NPC_NO_SPRITE +017-1,163,230,0 duplicate(#JumpArea01) #JumpArea11 NPC_NO_SPRITE +017-1,163,231,0 duplicate(#JumpArea01) #JumpArea12 NPC_NO_SPRITE + +017-1,164,226,0 duplicate(#JumpArea01) #JumpArea13 NPC_NO_SPRITE +017-1,164,227,0 duplicate(#JumpArea01) #JumpArea14 NPC_NO_SPRITE +017-1,164,228,0 duplicate(#JumpArea01) #JumpArea15 NPC_NO_SPRITE +017-1,164,229,0 duplicate(#JumpArea01) #JumpArea16 NPC_NO_SPRITE +017-1,164,230,0 duplicate(#JumpArea01) #JumpArea17 NPC_NO_SPRITE +017-1,164,231,0 duplicate(#JumpArea01) #JumpArea18 NPC_NO_SPRITE + +017-1,165,226,0 duplicate(#JumpArea01) #JumpArea19 NPC_NO_SPRITE +017-1,165,227,0 duplicate(#JumpArea01) #JumpArea20 NPC_NO_SPRITE +017-1,165,228,0 duplicate(#JumpArea01) #JumpArea21 NPC_NO_SPRITE +017-1,165,229,0 duplicate(#JumpArea01) #JumpArea22 NPC_NO_SPRITE +017-1,165,230,0 duplicate(#JumpArea01) #JumpArea23 NPC_NO_SPRITE +017-1,165,231,0 duplicate(#JumpArea01) #JumpArea24 NPC_NO_SPRITE + diff --git a/npc/017-1/estate.txt b/npc/017-1/estate.txt new file mode 100644 index 0000000..434b477 --- /dev/null +++ b/npc/017-1/estate.txt @@ -0,0 +1,171 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System + +// ID: 2 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +017-1,86,177,0 script Sign#RES_0177 NPC_SWORDS_SIGN,{ + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + if (is_admin() && $@GM_OVERRIDE) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + mesc l("Press the doorbell?"); + next; + if (askyesno() == ASK_YES) + doevent "Doorbell#RES_0177::OnDoorbell"; + close; + +L_RentAvailable: + realestate_rent(.id, .price); + close; + +L_Manage: + realestate_manage(.id, (.price*7/10)); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=2; // Estate ID + .price=40000; // Monthly rent price + end; + +} + +// Door entrance +017-1,85,175,0 script #RES_0177 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3) || $ESTATE_PASSWORD$[.id] == "") + goto L_Warp; + + mesc l("The door is locked"); + next; + mesc l("However, it can be unlocked if you know the password:"); + if (is_gm()) mesc l("You can use super password \"mouboo\" to unlock the door."), 1; + input .@password$; + // GMs can use super password "mouboo" + if (.@password$ == $ESTATE_PASSWORD$[.id] || (is_gm() && .@password$ == "mouboo")) + goto L_Warp; + close; + +L_Warp: + warp "017-7", 33, 33; + closeclientdialog; + close; + +L_RentAvailable: + dispbottom l("This estate is available for rent, talk to the sign to rent it."); + close; + +OnInit: + // Estate Settings + .id=2; // Estate ID + end; + +} + +////////////////////////////////////////////////////////////////////////////// +// ID: 3 + +// The sign is the main controller +017-1,138,22,0 script Sign#RES_0178 NPC_SWORDS_SIGN,{ + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + if (is_admin() && $@GM_OVERRIDE) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + mesc l("Press the doorbell?"); + next; + if (askyesno() == ASK_YES) + doevent "Doorbell#RES_0178::OnDoorbell"; + close; + +L_RentAvailable: + realestate_rent(.id, .price); + close; + +L_Manage: + realestate_manage(.id, (.price*7/10)); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=3; // Estate ID + .price=60000; // Monthly rent price + end; + +} + +// Door entrance +017-1,139,20,0 script #RES_0178 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if ($ESTATE_RENTTIME[.id] < gettimetick(2)) + goto L_RentAvailable; + + if ($ESTATE_OWNER[.id] == getcharid(3) || $ESTATE_PASSWORD$[.id] == "") + goto L_Warp; + + mesc l("The door is locked"); + next; + mesc l("However, it can be unlocked if you know the password:"); + if (is_gm()) mesc l("You can use super password \"mouboo\" to unlock the door."), 1; + input .@password$; + // GMs can use super password "mouboo" + if (.@password$ == $ESTATE_PASSWORD$[.id] || (is_gm() && .@password$ == "mouboo")) + goto L_Warp; + close; + +L_Warp: + warp "017-8", 33, 33; + closeclientdialog; + close; + +L_RentAvailable: + dispbottom l("This estate is available for rent, talk to the sign to rent it."); + close; + +OnInit: + // Estate Settings + .id=3; // Estate ID + end; + +} + diff --git a/npc/017-1/fairy_collector.txt b/npc/017-1/fairy_collector.txt new file mode 100644 index 0000000..a8ab6af --- /dev/null +++ b/npc/017-1/fairy_collector.txt @@ -0,0 +1,318 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Collect every piece of equipment ingame for no real reason +// Cannot be completed yet, because not every piece of equipment is available ingame + +017-1,135,106,0 script Royal Fairy NPC_FAIRY_B,{ + .@n=getq(LoFQuest_Fairy); + mesn; + + if (.@n == 0) goto L_Busy; + if (.@n < 3) goto L_Incomplete; + if (.@n >= 3) goto L_Main; + +L_Busy: + mesq l("Hi, I'm too busy to talk right now, please go away. Very pressing matters at hand."); + close; + +L_Incomplete: + mesq l("Hello, I noticed my dear friend, Susanne, asked you to help us save my sisters; do not forget to ask Susanne where the Fafi Dragon was last seen."); + close; + +L_Main: + if (!(TELEPORTERS & TP_LILIT) && rand2(3) == 2) { + mesn; + mesq l("Have you ever met our Queen Lilit? She is lovely but hates @@ for some reason...", get_race()); + next; + mesc l("Do you want a tip? \"It's not suicide if you know how to swim.\""); + next; + } + if (#COLLECTOR == (COLLECT_ALL-1)) { + mesn; + mesc l("OMG you got all equipment in Moubootaur Legends. You are truly a legend of legends."), 1; + next; + inventoryplace NPCEyes, 5; + mesn; + mesc l("Here, you can have the partly unobtainable items reserved to developers and contributors. And, uh, these NPC-only items, don't let developers know I gave you this. It'll be our little secret!"), 1; + #COLLECTOR = #COLLECTOR | COLLECT_ALL; + getitembound DEVCap, 1, 1; + getitembound ContributorSweater, 1, 1; + getitembound CommunityShirt, 1, 1; + getitembound RiceHat, 1, 1; + getitembound AlchemistArmor, 1, 1; + // Should we give them a NPCEyes? Or better not (would break checks)? + } + /* + COLLECT_CHESTPLATE: 1 + COLLECT_HEADGEAR: 2 + COLLECT_PANTS: 4 + COLLECT_SHOES: 8 + COLLECT_NECKLACES: 16 + COLLECT_RINGS: 32 + COLLECT_ACESSORIES: 64 + COLLECT_SCARFS: 128 + COLLECT_GLOVES: 256 + COLLECT_SHIELDS: 512 + COLLECT_1HSWORDS: 1024 + COLLECT_2HSWORDS: 2048 + COLLECT_BOWS: 4096 + COLLECT_FIREGUNS: 8192 + COLLECT_WANDS: 16384 + COLLECT_QUIVERS: 32768 + COLLECT_MOUNTS: 65536 + COLLECT_PETS: 131072 + + */ + do { + mesn; + mesq l("Are you trying to collect every piece of equipment ingame? Come tell me if you are and I'll give you a collector stamp!"); + mesc l("Not every piece of equipment is ingame yet."); + mes ""; + select + l("Good bye!"), + rif(false && !(#COLLECTOR & COLLECT_CHESTPLATE), l("Chestplates")), + rif(false && !(#COLLECTOR & COLLECT_HEADGEAR), l("Headgear")), + rif(false && !(#COLLECTOR & COLLECT_PANTS), l("Pants")), + rif(false && !(#COLLECTOR & COLLECT_SHOES), l("Shoes")), + rif(false && !(#COLLECTOR & COLLECT_NECKLACES), l("Necklaces")), + rif(false && !(#COLLECTOR & COLLECT_RINGS), l("Rings")), + rif(false && !(#COLLECTOR & COLLECT_ACESSORIES), l("Accessories")), + rif(false && !(#COLLECTOR & COLLECT_SCARFS), l("Scarfs")), + rif(false && !(#COLLECTOR & COLLECT_GLOVES), l("Gloves")), + rif(false && !(#COLLECTOR & COLLECT_SHIELDS), l("Shields")), + rif(false && !(#COLLECTOR & COLLECT_1HSWORDS), l("1 Hand Swords")), + rif(false && !(#COLLECTOR & COLLECT_2HSWORDS), l("2 Hand Swords")), + rif(true && !(#COLLECTOR & COLLECT_BOWS), l("Bows")), + rif(true && !(#COLLECTOR & COLLECT_FIREGUNS), l("Fire Staves")), + rif(true && !(#COLLECTOR & COLLECT_WANDS), l("Wands")), + rif(true && !(#COLLECTOR & COLLECT_QUIVERS), l("Quivers")), + rif(false && !(#COLLECTOR & COLLECT_MOUNTS), l("Mounts")), + rif(false && !(#COLLECTOR & COLLECT_PETS), l("Pets")), + l("Trade stamps"); + mes ""; + switch (@menu+1) { + // Chestplate + case 1: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Headgear (2x bells) + case 2: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Pants + case 3: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Shoes + case 4: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Necklaces + case 5: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Rings + case 6: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Accessories + case 7: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Scarfs + case 8: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Gloves + case 9: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // Shields + case 10: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // 1H Swords + case 11: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + // 2H Swords + case 12: + msObjective(countitem(Scythe), "* "+getitemlink(Scythe)); + msObjective(countitem(ThunderStaff), "* "+getitemlink(ThunderStaff)); + msObjective(countitem(MiereCleaver), "* "+getitemlink(MiereCleaver)); + msObjective(countitem(Broadsword), "* "+getitemlink(Broadsword)); + msObjective(countitem(Kanabo), "* "+getitemlink(Kanabo)); + msObjective(countitem(BlacksmithAxe), "* "+getitemlink(BlacksmithAxe)); + msObjective(countitem(CentaurSpear), "* "+getitemlink(CentaurSpear)); + msObjective(false, l("* More equipment coming soon...")); + next; + break; + // Bows + case 13: + msObjective(countitem(TrainingBow), "* "+getitemlink(TrainingBow)); + msObjective(countitem(WoodenBow), "* "+getitemlink(WoodenBow)); + msObjective(countitem(ShortBow), "* "+getitemlink(ShortBow)); + msObjective(countitem(ForestBow), "* "+getitemlink(ForestBow)); + msObjective(countitem(ElficBow), "* "+getitemlink(ElficBow)); + msObjective(countitem(ChampionshipBow), "* "+getitemlink(ChampionshipBow)); + msObjective(countitem(BansheeBow), "* "+getitemlink(BansheeBow)); + next; + if (countitem(PynRifle) && + countitem(PynGatling) && + countitem(PynShotgun) && + countitem(PynRevolver) && + countitem(Dustynator)) { + #COLLECTOR = #COLLECTOR|COLLECT_BOWS; + getitembound SilverBell, 1, 1; + mesc l("CONGRATULATIONS! You have obtained the %s.", getitemlink(SilverBell)), 3; + } + break; + // Firearms + case 14: + msObjective(countitem(PynRifle), "* "+getitemlink(PynRifle)); + msObjective(countitem(PynGatling), "* "+getitemlink(PynGatling)); + msObjective(countitem(PynShotgun), "* "+getitemlink(PynShotgun)); + msObjective(countitem(PynRevolver), "* "+getitemlink(PynRevolver)); + msObjective(countitem(Dustynator), "* "+getitemlink(Dustynator)); + next; + if (countitem(PynRifle) && + countitem(PynGatling) && + countitem(PynShotgun) && + countitem(PynRevolver) && + countitem(Dustynator)) { + #COLLECTOR = #COLLECTOR|COLLECT_FIREGUNS; + getitembound SilverBell, 1, 1; + mesc l("CONGRATULATIONS! You have obtained the %s.", getitemlink(SilverBell)), 3; + next; + } + break; + // Wands + case 15: + msObjective(countitem(TrainingWand), "* "+getitemlink(TrainingWand)); + msObjective(countitem(NoviceWand), "* "+getitemlink(NoviceWand)); + msObjective(countitem(ReinbooWand), "* "+getitemlink(ReinbooWand)); + msObjective(countitem(ApprenticeWand), "* "+getitemlink(ApprenticeWand)); + msObjective(countitem(LeaderWand), "* "+getitemlink(LeaderWand)); + msObjective(countitem(MysticWand), "* "+getitemlink(MysticWand)); + next; + if (countitem(TrainingWand) && + countitem(NoviceWand) && + countitem(ReinbooWand) && + countitem(ApprenticeWand) && + countitem(LeaderWand) && + countitem(MysticWand)) { + #COLLECTOR = #COLLECTOR|COLLECT_WANDS; + getitembound SilverBell, 1, 1; + mesc l("CONGRATULATIONS! You have obtained the %s.", getitemlink(SilverBell)), 3; + next; + } + break; + // Quivers + case 16: + msObjective(countitem(LeatherQuiver), "* "+getitemlink(LeatherQuiver)); + msObjective(countitem(IronQuiver), "* "+getitemlink(IronQuiver)); + msObjective(countitem(BronzeQuiver), "* "+getitemlink(BronzeQuiver)); + msObjective(countitem(PlatinumQuiver), "* "+getitemlink(PlatinumQuiver)); + next; + if (countitem(LeatherQuiver) && + countitem(IronQuiver) && + countitem(BronzeQuiver) && + countitem(PlatinumQuiver)) { + #COLLECTOR = #COLLECTOR|COLLECT_QUIVERS; + getitembound SilverBell, 1, 1; + mesc l("CONGRATULATIONS! You have obtained the %s.", getitemlink(SilverBell)), 3; + next; + } + break; + // Mounts + case 17: + msObjective(countitem(LegendaryTortuga), "* "+getitemlink(LegendaryTortuga)); + msObjective(countitem(LegendaryMouboo), "* "+getitemlink(LegendaryMouboo)); + next; + if (countitem(LegendaryTortuga) && + countitem(LegendaryMouboo)) { + #COLLECTOR = #COLLECTOR|COLLECT_MOUNTS; + getitembound SilverBell, 1, 1; + mesc l("CONGRATULATIONS! You have obtained the %s.", getitemlink(SilverBell)), 3; + next; + } + break; + // Pets (2x bells) + case 18: + msObjective(countitem(CreasedShirt), "* "+getitemlink(CreasedShirt)); + next; + break; + case 19: + openshop; + closeclientdialog; + close; + break; + } + + } while (@menu != 0); + close; + +OnInit: + .distance=5; + tradertype(NST_CUSTOM); + + // Sell: Aethyr Points, Strange Coins, Manapple, Supreme Gift, Housing Letter 3, Blueprints E, SaviorBlueprint, BoxsetEE, Tux/Linarian Soul, SunnyCrystal?, X Cards, + // Pets → Strange Coin + // You can get up to 18 stamps, but pets and hats give 2x + // Plan wisely and accordingly + sellitem SunnyCrystal, 12; + sellitem LegendaryMouboo, 11; + sellitem LegendaryTortuga, 10; + sellitem SupremeGift, 9; + sellitem PrismGift, 8; + sellitem MysteriousFruit, 7; + sellitem LinarianSoul, 6; + sellitem SaviorBlueprint, 5; + sellitem TuxSoul, 4; + sellitem GoldenGift, 3; + sellitem MercBoxEE, 2; + sellitem HousingLetterIII, 2; + sellitem ReflectCardX, 2; + sellitem SpeedCardX, 2; + sellitem PowerCardX, 2; + sellitem WallCardX, 2; + sellitem NecromancerCardX, 1; + sellitem HeroCardX, 1; + sellitem KnightCardX, 1; + sellitem ClericCardX, 1; + sellitem DruidCardX, 1; + sellitem MageCardX, 1; + sellitem NinjaCardX, 1; + sellitem NatureCardX, 1; + sellitem LightGreenDiamond, 1; + end; + +/* set currency to be item 828 */ +OnCountFunds: + setcurrency(countitem(SilverBell)); + end; + +/* @price is total cost. @points is if we accept two items as currency. */ +OnPayFunds: + //dispbottom "Hi: price="+@price+" and points="+@points; + if( countitem(SilverBell) < @price ) + end; + delitem SilverBell, @price; + purchaseok(); + end; + +} diff --git a/npc/017-1/guards.txt b/npc/017-1/guards.txt new file mode 100644 index 0000000..b5606c5 --- /dev/null +++ b/npc/017-1/guards.txt @@ -0,0 +1,80 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Protect LoF + +// Handle Guard's logic +function script LofGuard { + mesn; + mesq l("I am stationed here to protect Land Of Fire from monsters."); + next; + mesn; + mesq l("I mean, there's even an Monster King? What sort of world is this?!"); + close; + return; +} + +017-1,79,43,0 script Guard#lof-1 NPC_GUARD2,{ + LofGuard(); + end; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + + +017-1,220,44,0 script Guard#lof-2 NPC_GUARD1,{ + LofGuard(); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +017-1,193,69,0 script Guard#lof-3 NPC_GUARD2,{ + LofGuard(); + end; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + + +017-1,69,108,0 script Guard#lof-4 NPC_GUARD1,{ + LofGuard(); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +017-1,90,149,0 script Guard#lof-5 NPC_GUARD1,{ + LofGuard(); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + + +017-1,85,223,0 script Guard#lof-6 NPC_GUARD2,{ + LofGuard(); + end; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/017-1/guild.txt b/npc/017-1/guild.txt new file mode 100644 index 0000000..d984285 --- /dev/null +++ b/npc/017-1/guild.txt @@ -0,0 +1,46 @@ +// Moubootaur Legends Script +// Author: +// Jesusalva +// Description: +// Guild House + +017-1,57,33,0 script #GDoor NPC_NO_SPRITE,0,0,{ + end; +OnTouch: + if (getcharid(2) < 1) { + dispbottom l("The door is locked."); + end; + } + // Warp you to your guild house if exist. + // Build the instance otherwise. + + // Well, "checking if instance exist by mapname" is an illusion. + // So we try to build and if we fail, we warp the player to the instance. + .@ID=getcharid(2); + @MAP_NAME$="guild@"+str(.@ID); // Max 4 chars for map name + + .@INSTID = instance_create("guilds@a"+(.@ID), getcharid(2), IOT_GUILD); + + // Instance already exists - .@INSTID returns "-4" + if (.@INSTID == -4) { + warp @MAP_NAME$, any(34,35), 48; + end; + } + + .@instanceMapName$ = instance_attachmap("guilds", .@INSTID, 0, @MAP_NAME$); + + instance_set_timeout(0, 0, .@INSTID); + instance_init(.@INSTID); + warp @MAP_NAME$, any(34,35), 48; + end; + +} + +017-1,58,32,0 script #GSign NPC_NO_SPRITE,{ + mesc l("Guild Hall"); + close; + +OnInit: + .distance=3; + end; +} diff --git a/npc/017-1/mapflags.txt b/npc/017-1/mapflags.txt new file mode 100644 index 0000000..99a6ba9 --- /dev/null +++ b/npc/017-1/mapflags.txt @@ -0,0 +1,9 @@ +017-1 mapflag town +017-1 mapflag nopenalty +017-2 mapflag town +017-3 mapflag town +017-4 mapflag town +017-5 mapflag town +017-6 mapflag town +017-7 mapflag town +017-8 mapflag town diff --git a/npc/017-1/misc.txt b/npc/017-1/misc.txt new file mode 100644 index 0000000..5dabba3 --- /dev/null +++ b/npc/017-1/misc.txt @@ -0,0 +1,257 @@ +// TMW2/LoF scripts. +// Authors: +// Jesusalva +// Description: +// Essential scripts any city must have + +// Description: +// The Travelers travel around the world telling stories. +017-1,144,204,0 script Elen The Traveler NPC_F_COINKEEPER,{ + + mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Wow! Are you @@? Everyone, in every city, talks about you!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + + mesq l("Hello. I am @@, and I am from a family of travellers. We travel though the whole world, looking for exotic goods.", .name$); + next; + mesq l("You can buy rare items with me, or I can tell you about different cities in our world."); + +L_Menu: + mes ""; + menu + l("I want to trade with you."), L_Trade, + l("Tell me about Tulimshar."), L_Tulim, + l("Tell me about Hurnscald."), L_Hurns, + l("Tell me about Nivalis."), L_Nival, + l("Tell me about Artis."), L_Artis, + l("Tell me about Frostia."), L_Frost, + l("Tell me about Halinarzo."), L_Halin, + l("Sorry, I'll pass."), L_Close; + +L_Tulim: + mes ""; + mesn; + mesq l("Tulimshar is the oldest human city, and its foundation is the year zero of our calendar."); + next; + mesq l("The city only flourished because Janett Platinum had the idea to build city walls surrounding this city."); + next; + mesq l("The desert climate means you'll find mostly maggots and scorpions. Their drops include cactus drinks, cake, knifes, black pearls, gold, and other common things."); + next; + mesq l("You can find for a good price desert equipment and some kind of dyes. You find all sort of crafters, artisans and warriors here."); + next; + goto L_Menu; + +L_Hurns: + mes ""; + mesn; + mesq l("Hurnscald was founded after Tulimshar, in more fertile lands. Their walls are not so sturdy as the ones of Tulimshar."); + next; + mesq l("Under the leadership of King Wusher, they were the first to accept immigrants from other races. You will find humans and non-humans there."); + next; + mesq l("The fertile climate is ideal for mushrooms. You can also find lots of wood."); + next; + mesq l("Their economy provide many edible items and potions."); + next; + goto L_Menu; + +L_Nival: + mes ""; + mesn; + mesq l("Nivalis was the last human settlement built during the First Era."); + next; + mesq l("It's cold, harsh climate makes difficult to live there. It was founded by people thrown away from Tulimshar and Hunrscald for political reasons."); + next; + mesq l("The cold climate is ideal for slimes, penguins, and other icy creatures. You can find lots of... ice, of course!"); + next; + mesq l("Some items are only produced in Nivalis. After all, it is hard to work properly with ice in a desert!"); + next; + goto L_Menu; + + +L_Artis: + mes ""; + mesn; + mesq l("Artis is a city port founded after the Great Fire on the other continent."); + next; + mesq l("People say it is the second biggest city from the world."); + next; + mesq l("Different kind of monsters live near the city. For example, blubs. I have no idea of what are those."); + next; + mesq l("People usually dock there when travelling to the second continent. Nothing exceptional about economy."); + next; + if ($FIRESOFSTEAM) { + mesq l("They used to export food and other things but there has been radio silence recentely; Which is why Andrei Sakar and a group of adventurers borrowed Nard's ship and went to investigate."); + next; + } + goto L_Menu; + + +L_Frost: + mes ""; + mesn; + mesq l("Frostia is the only city known that was not founded by humans."); + next; + mesq l("They are strict with who is allowed inside, so you'll need either elf or ukar friends to pass."); + next; + mesq l("It is on a huge, icy mountain peak. Rumors about dragons and legendary items to be found."); + next; + mesq l("Some of finest elven craftmanship can be found there, like bows, for example."); + next; + goto L_Menu; + +L_Halin: + mes ""; + mesn; + mesq l("Halinarzo was founded to explore Mana Stones."); + next; + mesq l("You can find both huge swamps, as huge desertic areas near and on it."); + next; + mesq l("Lizards are the main monster found, and they steal gold from innocent bypassers."); + next; + mesq l("Without any mana stone left, and because the walls were not very strong, most of the city was destroyed."); + next; + mesq l("Unlike many other cities, if you want people in eternal need of items, there is a good place to look."); + next; + goto L_Menu; + + +L_Trade: + mesn; + mesq l("Use your @@ as currency!", getitemlink(StrangeCoin)); + tutmes l("%s is obtained during events, daily logins, heroic deeds, gifts, etc. But cannot be bought with real money.", getitemlink(StrangeCoin)); + next; + openshop "Aeros Trader"; + closedialog; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, UglyChristmasSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 27); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; +} + +// Description: +// Banker. +017-3,85,41,0 script Stalman NPC_LLOYD,{ + Banker(.name$, "Land Of Fire Village", 10000); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +// Description: +// Barber. + +017-1,147,176,0 script Milocat NPC_ELVEN_FEMALE_ARMOR_SHOP,{ + function setRace { + clear; + setnpcdialogtitle l("%s - Modify Race", .name$); + mes l("Race") + ": " + get_race(); + next; + mes l("Please select the desired race."); + select + l("Kaizei Human"), + l("Argaes Human"), + l("Tonori Human"), + l("Elf"), + l("Orc"), + l("Raijin"), + l("Tritan"), + l("Ukar"), + l("Redy"), + l("Savior"); + switch (@menu) + { + default: + jobchange max(0, @menu-1); + } + return; + } + + + mesn; + mesq l("Hi! Do you want a hair cut?"); + + do + { + select + l("What is my current hairstyle and hair color?"), + l("I'd like to get a different style."), + l("Can you do something with my color?"), + rif(is_gm() || REBIRTH >= 5, l("I want to change my Race!")), + l("I'm fine for now, thank you."); + + switch (@menu) + { + case 1: + BarberSayStyle 3; + break; + case 2: + BarberChangeStyle; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Enjoy your new style."); + l("Anything else?"); + break; + case 3: + BarberChangeColor; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("I hope you like this color."); + l("Anything else?"); + break; + case 4: + setRace; + break; + case 5: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Feel free to come visit me another time."); + + goodbye; + } + } while (1); + close; + + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + + + + + + + + +// Whatever event use #RARE_POINTS +// Which is a script variable, meaning it will be a really special event. +// Side Note: Might as well add a special cap for such situations. +017-1,118,83,0 script Pydisgner#spoints NPC_GUGLI,{ + mesn; + mesq l("Hello %s and welcome to Land Of Fire Village. This used to be a whole server before the Monster King crashed it down here.", strcharinfo(0)); + next; + mesn; + mesq l("In this town you'll find many critical things for your journey, like artifacts, dungeons, crafts and refiners. Please enjoy your stay. And keep tuned for news about us!"); + mes "@@https://discord.gg/q3Bwzgf|LoF Discord Server@@"; + close; + +OnInit: + .sex = G_OTHER; + .distance=5; + end; +} diff --git a/npc/017-1/nowhere_man.txt b/npc/017-1/nowhere_man.txt new file mode 100644 index 0000000..a743cbb --- /dev/null +++ b/npc/017-1/nowhere_man.txt @@ -0,0 +1,137 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Pachua's Cousin. On LoF/TMWA he also crafted Iron Powder, Sulfur Powder, and Yellow Powder (for magic) +// IMPORTANT @Saulc : DO NOT GIVE ANY EXPERIENCE ON THIS NPC. +// This is sort of a bet, you know. “Go hunt Mountain Snakes”, or bet some precious +// crafting material on this NPC. Two choices. The easy way is RISKY, not REWARDING. +// The refund is the exact fair price, retrieved from database. The only "loss" here +// is that you would rather do something else with Leather Patch but instead, you "sold" it. +// If you sell items you don't get experience, so you shouldn't get any exp here either. +// +// This NPC could as well do Jeans Shorts, but I'm too lazy to calculate formula now. +// The price is Cave Snake Skin in proportion to Jean Shorts rarity, and cost in proportion +// to sell price difference. + +017-1,155,162,0 script Nowhere Man NPC_KAYLO,{ + mesn; + mesq l("Welcome! I come from nowhere, and my family hunt Snakes. We also make fine leather items from their skin!"); + next; + mesn; + mesq l("Next time an annoying snake refuses to drop their precious leather drops, come talk to us!"); + // 160 * 1 = 160 GP vs 500 GP (340 GP) + mesq l("And if you bring me 1 @@ and 440 GP, I can make a @@ for you.", getitemlink(SnakeSkin), getitemlink(LeatherPatch)); + mesq l("Or I could recover the @@ for 10 @@ and the modest fee of 4400 GP.", getitemlink(SnakeSkin), getitemlink(LeatherPatch)); + next; + select + l("Nice to know. I'll come to you when random numbers try to kill me."), + rif(countitem(SnakeSkin) >= 1 && Zeny >= 440, l("I want Leather Patch")), + rif(countitem(LeatherPatch) >= 10 && Zeny >= 4400, l("I want Snake Skin")); + + switch (@menu) { + case 2: + inventoryplace LeatherPatch, 1; + delitem SnakeSkin, 1; + Zeny=Zeny-440; + getitem LeatherPatch, 1; + mesn; + mesq l("Many thanks!"); + break; + case 3: + inventoryplace SnakeSkin, 1; + delitem LeatherPatch, 10; + Zeny=Zeny-4400; + getitem SnakeSkin, 1; + mesn; + mesq l("Many thanks!"); + break; + } + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + + + +017-1,174,169,0 script Nowhere Jeane NPC_SAMANTHA,{ + mesn; + // 95 * 2 = 190 GP vs 400 GP (210 GP) + mesq l("If you bring me 2 @@ and 210 GP, I can make a @@ for you.", getitemlink(CaveSnakeSkin), getitemlink(JeansShorts)); + mesq l("Or I can swap the @@ with a @@ - for only 2100 GP.", getitemlink(JeansShorts), getitemlink(CaveSnakeSkin)); + next; + select + l("Nice to know. I'll come to you when random numbers try to kill me."), + rif(countitem(CaveSnakeSkin) >= 2 && Zeny >= 210, l("I want it.")), + rif(countitem(JeansShorts) && Zeny >= 2100, l("I don't care for equipment, gimme a snake skin.")); + + switch (@menu) { + case 2: + inventoryplace JeansShorts, 1; + delitem CaveSnakeSkin, 2; + Zeny=Zeny-210; + getitem JeansShorts, 1; + mesn; + mesq l("Many thanks!"); + break; + case 3: + inventoryplace CaveSnakeSkin, 1; + delitem JeansShorts, 1; + Zeny=Zeny-2100; + getitem CaveSnakeSkin, 1; + mesn; + mesq l("Many thanks!"); + break; + } + close; + + +OnInit: + .sex=G_FEMALE; + .distance=5; + end; + +} + + +017-1,143,161,0 script Nowhere Gluvine NPC_SERENA,{ + mesn; + // 320 * 6 = 1920 GP vs 450 GP (-) + mesq l("If you bring me 6 @@ I can make a @@ for you.", getitemlink(MountainSnakeSkin), getitemlink(LeatherGloves)); + mesq l("Or I can swap the @@ with a @@ - for free.", getitemlink(LeatherGloves), getitemlink(MountainSnakeSkin)); + next; + select + l("Nice to know. I'll come to you when random numbers try to kill me."), + rif(countitem(MountainSnakeSkin) >= 6, l("I want it.")), + rif(countitem(LeatherGloves), l("I don't care for shorts, gimme a snake skin.")); + + switch (@menu) { + case 2: + inventoryplace LeatherGloves, 1; + delitem MountainSnakeSkin, 6; + getitem LeatherGloves, 1; + mesn; + mesq l("Many thanks!"); + break; + case 3: + inventoryplace MountainSnakeSkin, 1; + delitem LeatherGloves, 6; + getitem MountainSnakeSkin, 1; + mesn; + mesq l("Many thanks!"); + break; + } + close; + + +OnInit: + .sex=G_FEMALE; + .distance=5; + end; + +} + diff --git a/npc/017-1/paxel.txt b/npc/017-1/paxel.txt new file mode 100644 index 0000000..8a461c2 --- /dev/null +++ b/npc/017-1/paxel.txt @@ -0,0 +1,263 @@ +// TMW2/LoF Script +// This is from LoF Forums, to replace LoF Paxel +//---------------------------------------------------- +// PRSM Helmet quest (seasonal quest) +// Create Quest: Darlord (Depredador) +//---------------------------------------------------- + +// Old Paxel turned Raw Coal in Coal... But we don't have Raw Coal. +// He probably used the nice furnace NPC too... We will need another NPC to use that... + +017-1,131,22,0 script Paxel NPC_PLAYER,0,0,{ + function maybeTowel; + if (maybeTowel()) + npctalk3 l("I hope that strange towel man doesn't bother me again..."); + else if (rand2(10) == 4) + npctalk3 l("I'm glad there's no annoying towel man. He keeps coming back to annoy me!"); + .@q=getq(SQuest_Paxel); + if(.@q == 2) goto L_Done; + if (season() != SPRING && !$@GM_OVERRIDE) goto L_OutOfSeason; + if(.@q == 1) goto L_Ask2; + + if (BaseLevel >= 55) goto L_Ask1; + +L_OutOfSeason: + mesn; + mesq l("Hello! Calm wind today!"); + menu + l("Yes, but I like windy days."), L_Wind, + l("Hmm... sorry but gotta go..."), L_Go; + close; + +L_Go: + mesn; + mesq l("Ok, good trip then."); + close; + +L_Wind: + mesn; + mesq l("I don't like them, I can't fly straight."); + next; + mesn; + if (BaseLevel < 55) + mesq l("Well, not like you could understand that with your level! %%a"); + else + mesq l("This is why I love spring!"); + close; + +// ---------------------------------------------------- +// Desert Helmet quest start +// ---------------------------------------------------- +L_Ask1: + mesn; + mesq l("Hello @@! Do you have some @@ with you?", strcharinfo(0), getitemlink(TerraniteOre)); + + menu + rif(countitem(TerraniteOre),l("Yes... right... what about Terranite Ore?")), L_Quest1, + rif(countitem(TerraniteOre),l("Yes, but I have grown attached to Terranite Ore...")), L_No, + l("Right, but now I have business in other places."), L_Rude; + close; + +L_Quest1: + mesn; + mesq l("Terranite Ore is a really special item. People without helmets can't understand @@'s real power, anyway if you could give me some of them you would understand why they are so special.", getitemlink(TerraniteOre)); + + menu + l("Certainly, you can have all my Terranite Ore."), L_Check1, + l("Sorry but I am not interested in that."), L_No; + close; + +L_No: + mesn; + mesq l("Ok, come back me when you want to know the @@ real power!", getitemlink(TerraniteOre)); + close; + +L_Rude: + mesn; + mesq l("Well, good luck with your business."); + close; + + +// ---------------------------------------------------- +// Arc 1: Terranite Ores +// ---------------------------------------------------- +L_Check1: + if (countitem(TerraniteOre) < 4) + goto L_More_Needed1; + + delitem TerraniteOre, 4; + getexp 15000, 150; + setq SQuest_Paxel, 1; + mesn; + mesq l("Perfect! you have enough Terranite Ore, I will take them for now but I need other items in order to bring them to life."); + next; + +L_Ask2: + mesn; + mesq l("I need a suitable base helmet, some herbs to use my magical dye, and gold:"); + mesc l("@@/1 @@", countitem(MinerHat), getitemlink(MinerHat)); + mesc l("@@/5 @@", countitem(GrassSeeds), getitemlink(GrassSeeds)); + mesc l("@@/100 @@", countitem(PinkAntenna), getitemlink(PinkAntenna)); + mesc l("@@/?? @@", countitem(CobaltHerb), getitemlink(CobaltHerb)); + mesc l("@@/@@ GP", format_number(Zeny), format_number(10000)); + + menu + l("Please have a look, I have what you asked"), L_Check2, + l("On my way to get what you need."), -; + close; + + +// ---------------------------------------------------- +// Arc 2: Prsm Helmet +// ---------------------------------------------------- +L_Check2: + if (countitem(PinkAntenna) < 100 || countitem(MinerHat) < 1 || countitem(GrassSeeds) < 5 || countitem(CobaltHerb) < 60) + goto L_More_Needed2; + if (Zeny < 10000) { + mesn; + mesq l("You can't afford my work! Do some odd jobs and come back."); + close; + } + + inventoryplace PrsmHelmet, 1; + delitem PinkAntenna, 100; + delitem MinerHat, 1; + delitem GrassSeeds, 5; + delitem CobaltHerb, 60; + //delitem TerraniteOre, 20; + getexp 60000, 0; + getitem PrsmHelmet, 1; + setq SQuest_Paxel, 2; + mesn; + mesq l("There you go, a special and rare @@!", getitemlink(PrsmHelmet)); + close; + +// ---------------------------------------------------- +// Failure: Insufficient material +// ---------------------------------------------------- +L_More_Needed1: + mesn; + mesq l("Your @@ is not enough, please look for more.", getitemlink(TerraniteOre)); + close; + +L_More_Needed2: + mesn; + mesq l("I am sorry but I can see that you don't have all that I asked you."); + next; + mesn; + mesq l("Please bring me that or I won't be able to make something really special for you."); + close; + + +// ---------------------------------------------------- +// Quest complete +// ---------------------------------------------------- +L_Done: + mesn; + mesq l("Hello winged friend! How is the wind today?"); + + menu + rif(season() == SPRING || $@GM_OVERRIDE, l("It's spring, when the wind is always good.")), L_Spring, + l("Calm, perfect for a fly!"), L_Fly, + l("Gale, not good to fly."), L_Wind2; + close; + +L_Fly: + mesn; + mesq l("Haha yes, you are right!"); + close; + +L_Wind2: + mesn; + mesq l("Yep, I never fly in these days."); + close; + +// TODO: Maybe we can add something else here +L_Spring: + mesn; + mesq l("Hahah, good to hear! Do you know some items are only dropped on spring? You should go after them!"); + close; + +// Saulc/Omatt/Prsm minigame +OnTouch: + if (BaseLevel < 20) + { + die(); // Shouldn't be here + end; + } + addtimer(100, "Paxel::OnOmattizator"); + end; + +OnOmattizator: + if (!isin("017-1", 131, 22, 0)) + end; + if (!issit()) + { + addtimer(100, "Paxel::OnOmattizator"); + end; + } + npctalk3 l("Unsit me at once!"); + emotion E_FURIOUS; + addtimer(3000, "Paxel::OnOmattizator2"); + end; + +OnOmattizator2: + if (!isin("017-1", 131, 22, 0)) + end; + if (!issit()) + { + addtimer(100, "Paxel::OnOmattizator"); + end; + } + npctalk3 l("Unsit me OR I'LL KILL YOU in name of my father, Prsm!"); + emotion E_FURIOUS; + addtimer(3000, "Paxel::OnOmattizator3"); + end; + +OnOmattizator3: + if (!isin("017-1", 131, 22, 0)) + end; + if (!issit()) + { + addtimer(100, "Paxel::OnOmattizator"); + end; + } + npctalk3 l("I HAVE WARNED YOU!!!!!"); + emotion E_FURIOUS; + addtimer(3000, "Paxel::OnOmattizator4"); + end; + +OnOmattizator4: + if (!isin("017-1", 131, 22, 0)) + end; + if (!issit()) + { + addtimer(100, "Paxel::OnOmattizator"); + end; + } + RegEasterEgg(EE_PRSM, 3); + npctalk3 l("DIE!"); + + slide 130, 23; + emotion E_FURIOUS; + die(); + end; + +function maybeTowel { + return (gettime(GETTIME_MONTH) % 3 == 2 && gettime(GETTIME_DAYOFMONTH) >= 18 && gettime(GETTIME_DAYOFMONTH) <= 25); +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, PrsmHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, WarlordPlate); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, JeansChaps); + setunitdata(.@npcId, UDT_HAIRSTYLE, 24); + setunitdata(.@npcId, UDT_HAIRCOLOR, 14); + + .sex=G_MALE; + .distance=5; + end; + +} diff --git a/npc/017-1/pet_detective.txt b/npc/017-1/pet_detective.txt new file mode 100644 index 0000000..388df8a --- /dev/null +++ b/npc/017-1/pet_detective.txt @@ -0,0 +1,139 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Sell pets (LoFQuest_Pets) (mesn Ace Ventura) + +017-1,192,23,0 script Pet Detective NPC_PET_TAMER,{ + // Not registered + .@q=getq(LoFQuest_Pets); + if (!.@q) + goto L_Register; + + // Out Of Stock + if (.stock <= 0) + goto L_OutOfStock; + + // Normal + goto L_Menu; + +L_Register: + mesn l("Ace Ventura"); + mesq l("I investigate missing pets. Many flee from their owners because they die pathetically or forgot to feed them."); + next; + mesn l("Ace Ventura"); + mesq l("If they at least read the @@, they wouldn't have such problems.", getitemlink(PetcaringGuide)); + mesq l("The Pet Caring Guild will collect them after a while, and capture many others."); + next; + mesn l("Ace Ventura"); + mesq l("So, what do you say about a deal? I'll affiliate you on the Pet Caring Guild for only 5.000 GP!"); + if (Zeny < 5000) + close; + next; + if (askyesno() == ASK_NO) + close; + mes ""; + if (Zeny < 5000) { + mesn l("Ace Ventura"); + mesq l("Oh, but we are affiliated with Jesusalva, whom hates cheaters. So, meet your dismissal!"); + die(); + /* + .@val=(Zeny/10)*2; + Zeny=(Zeny/10)*8; // You pay 20% of your GP as tax-money. Hasan gladly takes the money. Jesusalva's happy. + $HASAN_GP+=.@val; + */ + close; + } + Zeny=Zeny-5000; + getexp 0, 500; + setq LoFQuest_Pets, 1; + setarray PETMEMO, 0, 0; + mesn l("Ace Ventura"); + mesq l("Congrats! You are now part from the Pet Caring Guild. You can now buy pets! %%G"); + close; + +L_Menu: + mesn l("Ace Ventura"); + mesq l("I currently have some pets with me. For most, you need to finish their Grand Hunter Quest, of course."); + mes ""; + mesc l("Note: some pets still weren't implemented!"); + mesc l("If you unlock a pet but it is missing on this list, this means nobody elese unlocked them yet."), 1; + mesc l("So, you are the first and must contact Jesusalva %%g"), 1; + mes ""; + select + rif(PDQ_CheckGHQ(Maggot) >= 10000 && !countitem(MaggotCocoon), l("Cute Maggot")), + rif(PDQ_CheckGHQ(ForestMushroom) >= 10000 && !countitem(ForestShroomEgg), l("Forest Mushroom")), + rif(PDQ_CheckGHQ(Fluffy) >= 10000 && !countitem(FluffyEgg), l("Commmon Fluffy")), + rif(PDQ_CheckGHQ(Duck) >= 10000 && !countitem(DuckEgg), l("Duck")), + rif(PDQ_CheckGHQ(Bat) >= 10000 && !countitem(BatEgg), l("Bat")), + rif(PDQ_CheckGHQ(Moggun) >= 10000 && !countitem(MoggunEgg), l("Moggun")), + rif(PDQ_CheckGHQ(GreenDragon) >= 10000 && !countitem(DragonHorn), l("Green Dragon")), + rif(PDQ_CheckGHQ(Snake) >= 10000 && !countitem(TamedSnakeEgg), l("Tamed Snake")), + rif(#LOGIN_ALLTIME >= 6 && !countitem(PiouEgg), l("Piou")), + l("Nothing at the moment."); + + mes ""; + switch (@menu) { + case 1: + if (PDQ_InnerSwitch(Maggot, MaggotSlime, 35)) + .stock-=1; + break; + case 2: + if (PDQ_InnerSwitch(ForestMushroom, MushroomSpores, 30)) + .stock-=1; + break; + case 3: + if (PDQ_InnerSwitch(Fluffy, WhiteFur, 80)) + .stock-=1; + break; + case 4: + if (PDQ_InnerSwitch(Duck, CommonCarp, 4)) + .stock-=1; + break; + case 5: + if (PDQ_InnerSwitch(Bat, BatWing, 80)) + .stock-=1; + break; + case 6: + if (PDQ_InnerSwitch(Moggun, WhiteFur, 120)) + .stock-=1; + break; + case 7: + if (PDQ_InnerSwitch(GreenDragon, DragonScales, 65)) + .stock-=1; + break; + case 8: + if (PDQ_InnerSwitch(Snake, SnakeTongue, 17)) + .stock-=1; + break; + case 9: + if (PDQ_InnerSwitch(Piou, PiouFeathers, 80)) + .stock-=1; + break; + default: + closedialog; + goodbye; + close; + } + + next; + goto L_Menu; + +L_OutOfStock: + mesn l("Ace Ventura"); + mesq l("Sorry, I don't have any pets at the moment."); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + .stock=1; + end; + +OnSun0000: +OnWed1200: + .stock+=2; + end; +} + diff --git a/npc/017-1/roger.txt b/npc/017-1/roger.txt new file mode 100644 index 0000000..7514d11 --- /dev/null +++ b/npc/017-1/roger.txt @@ -0,0 +1,183 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Part of George quest. +// PS. (96,120) save these coordinates + +017-1,68,87,0 script Roger NPC_JOURNALMAN,{ + .@q=getq(LoFQuest_George); + mesn; + mesq l("Paper! Paper! Get your copy of The Land of Fire Gazette here!"); + next; + mesn; + mesq l("Hello @@, may I interest you in a copy of the Gazette? It's only 1 GP!", strcharinfo(0)); + next; + menu + l("Yes, please."), L_GetRead, + l("No, thank you, kid."), L_TooBad, + l("Do you have anything else to read?"), L_ExtraRead; + +L_GetRead: + mesn; + mesq l("Let me see....."); + next; + if (.@q == 5) + goto L_LOF_NEWS; + mesq l("Oh yes, here you go, that will be 1 GP."); + if (Zeny < 1) + goto L_Poor; + Zeny = (Zeny-1); + mesc l("You are handed the Land of Fire Gazette. You look at the headline..."); + if (!getskilllv(TMW2_ANCIENTLANGUAGES)) { + mesc l("...but it is written in Mananese, a language you know nothing about."); + } else { + mesc l("...but it is saying some stuff about a merge which happened literally eons ago."); + } + next; + mesc l("You toss the paper in the trash."); + close; + +L_TooBad: + mesn; + mesq l("Too bad, There is some really good news this week. Things like how to go to Candor for free."); + mesq l("And a special coupon section to get rare items for little or no money!"); + next; + menu + l("I changed my mind, I think I would like a copy!"), L_GetRead, + l("That's ok, kid... I can't read, anyway."), L_TooBadTwo; + +L_TooBadTwo: + mesn; + mesq l("Too bad, you're really missing out!"); + close; + +L_ExtraRead: + mesn; + mesq l("Now that you ask...."); + next; + if (.@q == 5) + goto L_MadMag; + mesn; + mesq l("I might be able to get you a rare copy of the Maritime Almanac Scroll. A yearly publication for seafaring men."); + mesq l("It is very hard to come by, however, my father has a copy he might sell to you for the right price."); + menu + l("I will trade you a Dagger and Sailor's Hat for it."), L_BadOffer1, + l("I will pay you 5,000 GP in pure gold for it!"), L_BadOffer2, + l("I can give you a rare...(cough, cough).. Mouboo Figurine for it."), L_Success, + l("I have no interest in things such as that."), -; + close; + +L_Success: + .@q2=getq2(LoFQuest_George); + if (.@q2 == 1) + goto L_HaveScroll; + if (countitem(MoubooFigurine) < 1) + goto L_NoMouboo; + if (countitem(SailorHat) == 0) + goto L_StatNone; + mesn; + mesq l("This is great! My collection of rare figurines is now complete."); + next; + mesn; + mesq l("I asked my father and he agreed to trade it to you, so here is the last copy of the Maritime Almanac Scroll."); + inventoryplace Almanac, 1; + delitem MoubooFigurine, 1; + getitembound Almanac, 1, 1; + setq2 LoFQuest_George, 1; + close; + +L_BadOffer1: + mesn; + mesq l("Wow, a Dagger and a Sailor's Hat!"); + next; + mesn; + mesq l("Although I really want the Sailor's Hat, but daggers are dangerous for kids to play with."); + mesq l("Besides that, do I look like a warrior to you? Why would you give a kid a sharp dagger?"); + close; + +L_BadOffer2: + mesn; + mesq l("5000 GP? That sure is a lot of gold..."); + mesq l("but I was hoping for something more valuable. No, thanks."); + close; + +L_NoMouboo: + mesn; + mesq l("You said you would trade a real Mouboo Figurine, but now I see you don't have one. Come back when you do."); + close; + +L_StatNone: + mesq l("You might have the Mouboo Figurine, but I don't trust everyone."); + if(.@q >= 3) + goto L_ShowHat; + mesq l("My friend George is much older than I am, go see him and if he trusts you, so will I."); + if (.@q < 2) + setq LoFQuest_George, 1; + close; + +L_ShowHat: + mesn; + mesq l("I heard you have met my good friend George, and you gave him some items that he needed..."); + next; + mesn; + mesq l("Didn't he give you anything?"); + next; + mesn; + mesq l("If you could show me what he gave you, I might trust you."); + close; + +L_MadMag: + /* + mesn; + mesq l("I have my favorite book of all times, Moby Dick. Let me read some of it to you...."); + next; + mesc l("Roger begins to read."); + mesn; + mesq l("Towards thee I roll, thou all-destroying but unconquering whale; to the last I grapple with thee; from hell’s heart I stab at thee; for hate’s sake I spit my last breath at thee. Sink all coffins and all hearses to one common pool! And since neither can be mine, let me then tow to pieces, while still chasing thee, though tied to thee, thou damned whale! Thus, I give up the spear!"); // SORRY TRANSLATORS + next; + mesn; + mesq l("I don't really understand it either, but it's not for sale. Maybe you could come back later so I can read more of it to you."); + next; + */ + Journalman(.name$); + // >> Will never go past this line + mesn; + mesq l("Have a nice day!"); + close; + +L_Poor: + mesn; + mesq l("You don't seem to have enough gold, not even 1 GP...you should really let go of some useless stuff on your inventory."); + close; + +L_LOF_NEWS: + mesn; + mesc l("Well I see that you have completed the George Quest!"); + mesc l("I am actually surprised you clicked me again. Well then, here's the Land of Fire News..."); + next; + mesc l("As you may know, LOF ran on a modified version of The Mana World game, before being merged on TMW2: Moubootaur Legends."); + mesc l("However, LoF admins and developers are working on a new game to offer a new experience for players."); + next; + mesc l("The project is involving only a small group of members, but would welcome anyone who knows how to assist in development."); + mesc l("You can always come to #devel on our Discord, or drop by #landoffire on IRC."); + next; + mesc l("You can also visit our website. And if you want to assist on this server (TMW2: ML), you're welcome too!"); + mesc l("Oh, and about the Candor travel for 200 GP... Which would be a rare for little or no gold... Well, that's just an attention grabber."); + next; + mesc l("BUT you can reduce the travel price to everywhere, to as low as 250 GP, by completing QUESTS!"); + mesc l("And some from the Grand Hunter Quests give you @@, which can be traded with the travelers for rares! Good luck!", getitemlink(StrangeCoin)); + close; + +L_HaveScroll: + mesn; + mesq l("Looks like I already gave you the Maritime Almanac Scroll. You need to go see George again. I am unable to get another copy for you."); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/017-1/shops.txt b/npc/017-1/shops.txt new file mode 100644 index 0000000..ac5affe --- /dev/null +++ b/npc/017-1/shops.txt @@ -0,0 +1,112 @@ +// TMW2/LoF scripts. +// Authors: +// Jesusalva +// Description: +// Random Sellers + +017-1,158,184,0 script Billy NPC_M_SHOPKEEPER,{ + npcshopattach(.name$); + shop .name$; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, any(ShortTankTop, CandorShirt, Chainmail)); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 10); + setunitdata(.@npcId, UDT_HAIRCOLOR, 9); + + .sex = G_MALE; + .distance = 4; + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem LOFCoin, -1, 1; + sellitem YellowDye, -1, 1; + sellitem CottonCloth, -1, 3; + sellitem ArrowAmmoBox, -1, 5; + sellitem Bullet, -1, 2000; + if (!rand2(4)) + sellitem OolongTea, getiteminfo(OolongTea, ITEMINFO_BUYPRICE)*15/10, rand2(1,3); + sellitem WoodenShield, -1, 1; + sellitem LeatherShirt, -1, 1; + end; + +OnClock1250: + restoreshopitem WoodenShield, 1; + restoreshopitem LeatherShirt, 1; +OnClock0112: + restoreshopitem LOFCoin, -1, 1; + restoreshopitem YellowDye, -1, 1; + restoreshopitem CottonCloth, -1, 3; + restoreshopitem ArrowAmmoBox, -1, 5; + restoreshopitem Bullet, -1, 2000; + if (!rand2(4)) + restoreshopitem OolongTea, getiteminfo(OolongTea, ITEMINFO_BUYPRICE)*15/10, rand2(1,3); + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} + +// Anagram for 'Demure'. +// Sells wands and ammo (therefore, not demure, who plays speed-melee, unless she have a secret shop quest) +017-1,135,185,0 script Drueme NPC_F_SHOPKEEPER,{ + npcshopattach(.name$); + shop .name$; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, any(ShortTankTop, CandorShirt, Chainmail)); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 10); + setunitdata(.@npcId, UDT_HAIRCOLOR, 9); + + .sex = G_FEMALE; + .distance = 4; + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem TrainingWand, -1, 3; + sellitem NoviceWand, -1, 2; + restoreshopitem ArrowAmmoBox, -1, 5; + restoreshopitem IronAmmoBox, -1, 2; + restoreshopitem Bullet, -1, 5000; + end; + +OnSun0346: + restoreshopitem TrainingWand, -1, 3; + restoreshopitem NoviceWand, -1, 2; +OnClock0848: +OnClock2023: + restoreshopitem ArrowAmmoBox, -1, 5; + restoreshopitem IronAmmoBox, -1, 2; + restoreshopitem Bullet, -1, 5000; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} diff --git a/npc/017-1/signs.txt b/npc/017-1/signs.txt new file mode 100644 index 0000000..cbedf30 --- /dev/null +++ b/npc/017-1/signs.txt @@ -0,0 +1,106 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Easier navigation on LoF Village + +// Main Signs +017-1,71,96,0 script Northwest Avenue#lof NPC_NO_SPRITE,{ + mesn; + mesc "↑ "+l("Unused House"); + mesc "→ "+l("Northeast Avenue"); + mesc "↓ "+l("Inn, Dimond's Cove"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + + +017-1,114,96,0 script Northeast Avenue#lof NPC_NO_SPRITE,{ + mesn; + mesc "↑ "+l("Houses, Exit"); + mesc "← "+l("Northwest Avenue"); + mesc "→ "+l("Tech-User Forge"); + mesc "↓ "+l("Unused House, Market"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + + +017-1,114,147,0 script Southeast Avenue#lof NPC_NO_SPRITE,{ + mesn; + mesc "↑ "+l("↑ Houses, Soul Menhir"); + mesc "← "+l("Dimond's Cove"); + mesc "→ "+l("Market Place"); + mesc "↓ "+l("Houses, Lake Of Tears"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + + + + +017-1,71,147,0 script Southwest Avenue#lof NPC_NO_SPRITE,{ + mesn; + mesc "↑ "+l("Inn, Houses, Northwest Avenue"); + mesc "← "+l("Alchemy Shop"); + mesc "→ "+l("Market Place"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + + +// Misc signs +017-1,71,110,0 script West Avenue#lof NPC_NO_SPRITE,{ + mesn; + mesc "↑ "+l("Northwest Avenue"); + mesc "← "+l("Fire Breath Inn"); + mesc "↓ "+l("Southwest Avenue"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +017-1,114,63,0 script North Avenue#lof NPC_NO_SPRITE,{ + mesn; + mesc "→ "+l("Dead End"); + mesc "↓ "+l("Northeast Avenue"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + +017-1,212,67,0 script Signpost#lof NPC_NO_SPRITE,{ + mesn; + mesc "↑ "+l("↑ Town Hall, Noble District"); + mesc "← "+l("Land Of Fire Village"); + mesc "↓ "+l("Transcendence Gate"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} + diff --git a/npc/017-1/soul-menhir.txt b/npc/017-1/soul-menhir.txt new file mode 100644 index 0000000..74ad4f6 --- /dev/null +++ b/npc/017-1/soul-menhir.txt @@ -0,0 +1,20 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Soul Menhir + +017-1,120,86,0 script Soul Menhir#lof NPC_SOUL_OLD,{ + @map$ = "017-1"; + setarray @Xs, 119, 120, 121, 119, 120, 121; + setarray @Ys, 87, 88, 87, 88, 87, 88; + @x = 0; + @y = 0; + callfunc "SoulMenhir"; + @map$ = ""; + cleararray @Xs[0], 0, getarraysize(@Xs); + cleararray @Ys[0], 0, getarraysize(@Ys); + @x = 0; + @y = 0; + close; +} diff --git a/npc/017-1/stranger.txt b/npc/017-1/stranger.txt new file mode 100644 index 0000000..c60e988 --- /dev/null +++ b/npc/017-1/stranger.txt @@ -0,0 +1,184 @@ +// TMW2/LoF scripts. +// Authors: +// var +// TMW-LoF Team +// Jesusalva +// Description: +// Special quest, can be done every three months, on days 18~25. +// Months: February, May, August, November + +017-1,44,37,0 script Stranger#LoF NPC_LOF_STRANGER,{ + if (!$@GM_OVERRIDE && (gettime(GETTIME_MONTH) % 3 != 2 || (gettime(GETTIME_DAYOFMONTH) < 18 && gettime(GETTIME_DAYOFMONTH) > 25))) { + dispbottom l("An error with Stranger#LoF happened! Please report!!"); + disablenpc .name$; + end; + } + + .@q=getq(LoFQuest_Hitchhiker); + if (.@q == 2) goto L_Complete; + if (.@q == 1) goto L_Quizz; + mesn; + mesq l("Hello, hello! It's great to see you. Maybe you can help me with a little problem I have."); + next; + mesn; + mesq l("I need some materials to repair my spacesh- ahm, it's not important why I need it."); // Translators: Spacesh(ip) + next; + mesn; + mesq l("Anyway, I'd be really happy if you could give me @@ @@, @@ @@, @@ @@ and @@ @@.", + 100, getitemlink(Chagashroom), + 3, getitemlink(FluoPowder), + 1, getitemlink(TinIngot), + 1, getitemlink(TitaniumIngot)); + + if (countitem(Chagashroom) < 100 || + countitem(FluoPowder) < 3 || + countitem(TinIngot) < 1 || + countitem(TitaniumIngot) < 1) + close; + next; + mesn; + mesq l("Do you have that for me?"); + if (askyesno() == ASK_NO) + close; + mes ""; + // We already checked, cheaters will have their inventory ripped apart and I don't care ~ Jesusalva + delitem Chagashroom, 100; + delitem FluoPowder, 3; + delitem TinIngot, 1; + delitem TitaniumIngot, 1; + getexp (BaseLevel**2)*5, JobLevel*2; + setq LoFQuest_Hitchhiker, 1; + mesn; + mesq l("Great! Thank you!"); + next; + mesn; + mesq l("I have a lot of work to do now. But before you leave..."); + goto L_Quizz; + +L_Quizz: + inventoryplace OldTowel, 1; + mesn; + mesq l("Did you knew May 25th it is a very special day - the Towel Day?"); + next; + mesn; + mesq l("To celebrate this I'll ask you some questions about my favourite book, The Hitchhiker's Guide to the Galaxy. If you can answer them, I'll give you something very useful."); + next; + mesn; + mesq l("The answers might need to have whitespaces. DO NOT, BY ALL MEANS, insert two whitespaces instead of one."); + mesq l("I'll get so mad at you that even if you are correct, I'll say otherwise. Also, mind special characters."); + mesq l("If I don't know the answer in your language, try answering in English. That always works."); + mesc l("Are you ready?"), 1; + mesc l("There's no time limit on this riddle."); + next; + if (askyesno() == ASK_NO) + close; + goto L_Easy; + +L_Easy: + mes ""; + mesn; + mesq l("The first one is easy."); + next; + mes ""; + if (0171_Stranger_BlackBox(EASY)) + goto L_Medium; + else + goto L_Fail; + +L_Medium: + mes ""; + mesn; + mesq l("You're absolutely right. The next one will be more tricky."); + next; + mes ""; + if (0171_Stranger_BlackBox(MEDIUM)) + goto L_Hard; + else + goto L_Fail; + +L_Hard: + mes ""; + mesn; + mesq l("Not bad, not bad. Let's see if you can answer an even more difficult one."); + next; + mes ""; + if (0171_Stranger_BlackBox(HARD)) + goto L_Reward; + else + goto L_Fail; + + +// Failure +L_Fail: + mes ""; + mesn; + if (rand2(1,2) == 1) { + mesq l("Sorry, but that's not right."); + next; + mesn; + mesq l("Come try again, some other time."); + } else { + mesq l("Nah, that's wrong. Come back if you want to try again."); + } + mesc l("If you are sure your answer is correct, please contact Jesusalva for bug fixing."); + close; + +// Reward +L_Reward: + mes ""; + mesn; + mesq l("Unbelievable! You're really a wise person."); + next; + mesn; + mesq l("One last question, but this shouldn't be a problem for you."); + next; + mesn; + mesq l("What is the most important item for every hitchhiker to have?"); + input .@answer$; + if (strip(strtolower(.@answer$)) != "towel" && strip(strtolower(.@answer$)) != l("towel")) + goto L_Fail; + mes ""; + mesn; + mesq l("There you are! A towel is really the most important item for a hitchhiker to have."); + + getitem OldTowel, 1; + // If you complete the quest in May, you get 50% more experience + if (gettime(GETTIME_MONTH) == MAY) + getexp JobLevel*BaseLevel*3/2, BaseLevel*3; + else + getexp JobLevel*BaseLevel, BaseLevel*2; + setq LoFQuest_Hitchhiker, 2; + next; + mesn; + mesq l("It really comes in handy that I have my towel with me. A towel is about the most massively useful thing an inte- ahm, a person can have."); + close; + +// TODO: Enable another quest after main quest has been completed +// TODO: Completing sub quest on May shall give better bonus (gettime(GETTIME_MONTH) == MAY) +// TODO: Keep your Towel well guarded; It can only be obtained once and is required in further quests. +L_Complete: + mesn; + mesq l("I have a lot of work to do now."); + if (rand2(3) == 2) { + next; + mesn; + mesq l("Annoying Paxel, for example %%o"); + } + close; + +// Proccessing Core +OnInit: + .sex=G_OTHER; + .distance=5; +OnClock0000: + if (gettime(GETTIME_MONTH) % 3 == 2) { + if (gettime(GETTIME_DAYOFMONTH) >= 18 && gettime(GETTIME_DAYOFMONTH) <= 25) { + enablenpc .name$; + end; + } + } + disablenpc .name$; + end; + +} + diff --git a/npc/017-1/town.txt b/npc/017-1/town.txt new file mode 100644 index 0000000..9856b71 --- /dev/null +++ b/npc/017-1/town.txt @@ -0,0 +1,10 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Reset LOCATION$ when entering a town + +017-1,104,103,0 script #LocLoF NPC_HIDDEN,2,2,{ +OnTouch: + EnterTown("LoF"); end; +} diff --git a/npc/017-1/townhall.txt b/npc/017-1/townhall.txt new file mode 100644 index 0000000..4251629 --- /dev/null +++ b/npc/017-1/townhall.txt @@ -0,0 +1,116 @@ +// TMW2/LoF scripts. +// Authors: +// Jesusalva +// Description: +// LoF Townhall Access Control + +017-1,180,20,0 script #LoF_Townhall01 NPC_NO_SPRITE,0,0,{ + end; + +OnTouch: + // Sponsor and Main Quest 17+ - access granted + .@q=getq(General_Narrator); + if (.@q >= 17 || getgmlevel()) { + warp "017-10", 56, 78; + } else { + npctalkonce l("The door is locked, I should come back later."); + } + end; + +} + +017-1,182,20,0 script #LoF_Townhall02 NPC_NO_SPRITE,0,0,{ + end; + +OnTouch: + // Sponsor and Main Quest 17+ - access granted + .@q=getq(General_Narrator); + if (.@q >= 17 || getgmlevel()) { + warp "017-10", 60, 78; + } else { + npctalkonce l("The door is locked, I should come back later."); + } + end; +} + +017-1,171,20,0 script #LoF_Townhall03 NPC_NO_SPRITE,0,0,{ + end; + +OnTouch: + // Sponsor and Main Quest 17+ - access granted + .@q=getq(General_Narrator); + if (.@q >= 17 || getgmlevel()) { + warp "017-10", 36, 78; + } else { + npctalkonce l("The door is locked, I should come back later."); + } + end; +} + +017-1,190,20,0 script #LoF_Townhall04 NPC_NO_SPRITE,0,0,{ + end; + +OnTouch: + // Sponsor and Main Quest 17+ - access granted + .@q=getq(General_Narrator); + if (.@q >= 17 || getgmlevel()) { + warp "017-10", 80, 78; + } else { + npctalkonce l("The door is locked, I should come back later."); + } + end; +} +///////////////////////////// +// Main Quest 6 +// LoF Arc +///////////////// +// Check instances and rebuild if needed +// Returns map name +// BarbaraInstCheck( {submap=mines} ) +// 0- Chamber ; 1 - Mines ; 2 - Hall ; 3 - Shrine +function script BarbaraInstCheck { + .@house=getarg(0, true); + + .@q2=getq2(LoFQuest_Barbara); + // Map name limit: 4 chars (sgt1) + .@map0$="brb0@"+getcharid(0); + .@map1$="brb1@"+getcharid(0); + .@map2$="brb2@"+getcharid(0); + .@map3$="brb3@"+getcharid(0); + if (!(isinstance(.@q2) && .@q2 != 0)) { + .@inst = instance_create("Forgotten Shrine "+getcharid(0), getcharid(3), IOT_CHAR); + instance_attachmap("018-6-0", .@inst, false, .@map0$); + instance_attachmap("018-6-1", .@inst, false, .@map1$); + instance_attachmap("018-6-2", .@inst, false, .@map2$); + instance_attachmap("018-6-3", .@inst, false, .@map3$); + // Instance lasts two hours + instance_set_timeout(7200, 7200, .@inst); + instance_init(.@inst); + setq2 LoFQuest_Barbara, .@inst; + } + + // It broke + if (getmapinfo(MAPINFO_SIZE_X, .@map1$) <= 0) { + setq2 LoFQuest_Barbara, 0; + // Infinite Loop? + return callfunc("BarbaraInstCheck", .@house); + } + + // Return map name + return getd(".@map"+.@house+"$"); +} + +017-1,32,44,0 script #LoF_EleniumMines NPC_NO_SPRITE,0,0,{ + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + if (.@q >= 1) { + addmapmask BarbaraInstCheck(1), 2; + warp BarbaraInstCheck(1), 80, 199; + } else { + warp "018-6-1", 80, 199; + } + end; +} + diff --git a/npc/017-1/wateranimation.txt b/npc/017-1/wateranimation.txt new file mode 100644 index 0000000..802b154 --- /dev/null +++ b/npc/017-1/wateranimation.txt @@ -0,0 +1,24 @@ +// TMW2 scripts. +// Author: +// gumi +// Reid +// Saulc +// Jesusalva +// Description: +// Water animations, splash, fishes, etc... + +017-1,109,223,0 script #Lof_WAM0 NPC_WATER_SPLASH,{ + + fishing; // begin or continue fishing + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +017-1,97,133,0 duplicate(#Lof_WAM0) #Lof_WAM1 NPC_WATER_SPLASH +017-1,88,130,0 duplicate(#Lof_WAM0) #Lof_WAM2 NPC_WATER_SPLASH +017-1,81,126,0 duplicate(#Lof_WAM0) #Lof_WAM3 NPC_WATER_SPLASH + diff --git a/npc/017-10/_import.txt b/npc/017-10/_import.txt new file mode 100644 index 0000000..a17ef5e --- /dev/null +++ b/npc/017-10/_import.txt @@ -0,0 +1,8 @@ +// Map 017-10: LoF Townhall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-10/_warps.txt", +"npc/017-10/airship.txt", +"npc/017-10/dispatcher.txt", +"npc/017-10/kenton.txt", +"npc/017-10/politics.txt", +"npc/017-10/vault.txt", diff --git a/npc/017-10/_warps.txt b/npc/017-10/_warps.txt new file mode 100644 index 0000000..b4d6087 --- /dev/null +++ b/npc/017-10/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-10: LoF Townhall warps +017-10,36,79,0 warp #017-10_36_79 0,0,017-1,171,21 +017-10,56,79,0 warp #017-10_56_79 0,0,017-1,180,21 +017-10,60,79,0 warp #017-10_60_79 0,0,017-1,182,21 +017-10,80,79,0 warp #017-10_80_79 0,0,017-1,190,21 diff --git a/npc/017-10/airship.txt b/npc/017-10/airship.txt new file mode 100644 index 0000000..48851f4 --- /dev/null +++ b/npc/017-10/airship.txt @@ -0,0 +1,68 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Notes: +// Fortress Island South (Lv 70~150 Area) + +017-10,63,22,0 script #ToFortress NPC_NO_SPRITE,1,0,{ + end; +OnTouch: + .@q=getq(General_Narrator); + if (.@q < 20) + end; + + mesc l(".:: Fortress Island ::."), 1; + mes ""; + if ($GAME_STORYLINE < 3) { + mesc l("Travel to this area is impossible, due to the hordes of Monster Armies."), 1; + mes ""; + mesc l("If only we defeated their generals and put the army in disarray... It would be possible to visit there."); + close; + } + if (BaseLevel < 70) { + mesc l("This area is only for level 70 upwards."), 1; + close; + } + mesc l("Do you want to go to Fortress Island, where the Monster King lives?!"), 1; + removespecialeffect(FX_CIRCLE, SELF, getcharid(3)); // FIXME :( + next; + mesc l("WARNING: HIGH LEVEL AREA. COME PREPARED."), 1; + mesc l("Note: This travel takes a whole minute to complete!!"); + mesc l("Note 2: If you leave this plataform, it'll reset!"); + mesc l("Note 3: All maps in Fortress Island have penalty, even towns!"); + next; + mesc l("BEGIN WARP PROCEDURES?"), 1; + if (askyesno() == ASK_YES) { + specialeffect FX_CIRCLE, SELF, getcharid(3); + addtimer2 70000, "#ToFortress::OnWarper"; + addtimer2 60000, "#ToFortress::OnNotif"; + } + close; + +OnNotif: + specialeffect FX_MAGIC, SELF, getcharid(3); + dispbottom l("WARPING IN 10 SECONDS"); + end; + +OnWarper: + removespecialeffect(FX_CIRCLE, SELF, getcharid(3)); + if (getmap() != "017-10") + end; + if (!FORT_1ST_VISIT) + FORT_1ST_VISIT = gettimetick(2); + warp "025-2", 97, 119; + specialeffect FX_SUCCESS, SELF, getcharid(3); + dispbottom l("You have arrived at Fortress Island."); + end; +} + +017-10,63,28,0 script #ToFortressAbort NPC_NO_SPRITE,5,0,{ + end; +OnTouch: + removespecialeffect(FX_CIRCLE, SELF, getcharid(3)); + atcommand("@refresh"); // FIXME :( + deltimer "#ToFortress::OnWarper"; + deltimer "#ToFortress::OnNotif"; + end; +} + diff --git a/npc/017-10/dispatcher.txt b/npc/017-10/dispatcher.txt new file mode 100644 index 0000000..b47060d --- /dev/null +++ b/npc/017-10/dispatcher.txt @@ -0,0 +1,411 @@ +// TMW2/LoF scripts. +// Authors: +// Jesusalva +// Description: +// Deploys an Homunculus in a mission during your offline time +// Variables +// HOMUN_DEPLOYDATE +// When the Homunculus is scheduled to arrive +// HOMUN_DEPLOYFIELD +// ID of the field which Homunculus was deployed + +017-10,61,34,0 script Erik NPC_JOURNALMAN,{ + function lootField; + mesn; + mesq l("Hello. My name is Erik, and I have Homunculus go out in errands in my stead."); + + // If you haven't summoned yet, or killed it... + if (!gethominfo(0)) { + mesc l("You do not own an Homunculus."), 1; + close; + } + + // Check what your homunculus is up to + switch (homstatus()) { + case 0: + mesc l("Your homunculus is ready!"), 2; + break; + case 1: + mesc l("Your homunculus is sleeping! Wake them up!"), 3; + close; + break; + case 2: + mesc l("Your homunculus is already on a mission!"), 1; + + // Homunculus should return from mission + if (gettimetick(2) > HOMUN_DEPLOYDATE) { + select + l("Recall"); + .@xp=lootField(HOMUN_DEPLOYFIELD); + recallhomunculus(); + gethomunexp(.@xp); + .@lv=gethominfo(6); + getexp 0, (.@lv*min(10000, gethominfo(3))/10000)*5; + close; + } + mesc l("It should be back in @@", FuzzyTime(HOMUN_DEPLOYDATE)), 1; + close; + break; + default: + Exception("ERROR! homstatus code "+homstatus(), RB_DEFAULT|RB_SPEECH|RB_ISFATAL); + break; + } + + // This is a bug: They are ready but shouldn't be. + if (gettimetick(2) < HOMUN_DEPLOYDATE) { + Exception("ERROR: YOU WERE BANNED FROM USING ERIK SERVICES. (might be a bug).", RB_SPEECH|RB_ISFATAL); + close; + } + +L_Main: + select + l("Deploy to Piou Fields [1h]"), + l("Deploy to Herb Fields [1h]"), + rif(gethominfo(6) >= 10, l("Deploy to Ore Mines [2h]")), + rif(gethominfo(6) >= 10, l("Deploy to Rare Mines [2h]")), + rif(gethominfo(6) >= 15, l("Deploy to Slime Nest [2h]")), + rif(gethominfo(6) >= 30, l("Deploy to Snake Pit [3h]")), + rif(gethominfo(6) >= 55 && $GAME_STORYLINE >= 3, l("Deploy to Dangerous Area [6h]")), + rif(gethominfo(6) >= 70 && $GAME_STORYLINE >= 4, l("Deploy to Monster King Basement [8h]")), + "", + l("Deploy to The Market [1h]"), + l("Don't deploy"); + mes ""; + // Common drops: At least 90% drop + // Rare drops: 5% or less drop rate + switch (@menu) { + case 1: + mesc ".:: " + l("Piou Fields") + " ::."; + mesc l("The most basic grinding camp for your homunculus."); + mesc l("Average EXP: "+200), 3; + mesc l("Common Drops: "+ + getitemlink(PiouFeathers)); + mesc l("Regular Drops: "+ + getitemlink(PiouLegs)+ + getitemlink(HalfEggshell)); + mesc l("Rare Drops: "+ + getitemlink(GoldenApple)); + next; + .@dest=HCD_PIOUFIELDS; + .@hours=1; + break; + case 2: + mesc ".:: " + l("Herb Fields") + " ::."; + mesc l("If you need more herbs, the best place to look."); + mesc l("Average EXP: "+10), 3; + mesc l("Common Drops: "+ + getitemlink(ArtichokeHerb)); + mesc l("Regular Drops: "+ + getitemlink(CobaltHerb)+ + getitemlink(GambogeHerb)+ + getitemlink(MauveHerb)+ + getitemlink(AlizarinHerb)); + mesc l("Rare Drops: "+ + getitemlink(ReedBundle)); + next; + .@dest=HCD_HERBFIELDS; + .@hours=1; + break; + case 3: + mesc ".:: " + l("Ore Mines") + " ::."; + mesc l("Send your Homunculus to mineration!"); + mesc l("Average EXP: "+24), 3; + mesc l("Common Drops: "+ + getitemlink(Coal)); + mesc l("Regular Drops: "+ + getitemlink(IronOre)+ + getitemlink(CopperOre)+ + getitemlink(SilverOre)+ + getitemlink(GoldOre)); + mesc l("Rare Drops: "+ + getitemlink(TinOre)+ + getitemlink(LeadOre)+ + getitemlink(TitaniumOre)+ + getitemlink(IridiumOre)+ + getitemlink(PlatinumOre)); + next; + .@dest=HCD_OREMINES; + .@hours=2; + break; + case 4: + mesc ".:: " + l("Rare Mines") + " ::."; + mesc l("Send your Homunculus to mineration!"); + mesc l("Average EXP: "+30), 3; + mesc l("Common Drops: "+ + getitemlink(Pearl)); + mesc l("Regular Drops: "+ + getitemlink(Diamond)+ + getitemlink(Ruby)+ + getitemlink(Emerald)+ + getitemlink(Sapphire)+ + getitemlink(Topaz)+ + getitemlink(Amethyst)); + mesc l("Rare Drops: "+ + getitemlink(LightGreenDiamond)+ + getitemlink(BlackPearl)+ + getitemlink(BlueManaPearl)); + next; + .@dest=HCD_RAREMINES; + .@hours=2; + break; + case 5: + mesc ".:: " + l("Slime Nest") + " ::."; + mesc l("Send your Homunculus to a slime nest!"); + mesc l("Average EXP: "+1600), 3; + mesc l("Common Drops: "+ + getitemlink(MaggotSlime)+ + getitemlink(Arrow)); + mesc l("Regular Drops: "+ + getitemlink(IronArrow)+ + getitemlink(TreasureKey)+ + getitemlink(CactusDrink)+ + getitemlink(CoinBag)+ + getitemlink(CasinoCoins)+ + getitemlink(Coal)+ + getitemlink(Dagger)+ + getitemlink(Candy)+ + getitemlink(ChocolateBar)); + mesc l("Rare Drops: "+ + getitemlink(Milk)+ + getitemlink(IcedBottle)); + next; + .@dest=HCD_SLIMENEST; + .@hours=2; + break; + case 6: + mesc ".:: " + l("Snake Pit") + " ::."; + mesc l("Send your Homunculus to hold off snakes!"); + mesc l("Average EXP: "+format_number(4500)), 3; + mesc l("Common Drops: "+ + getitemlink(SnakeEgg)+ + getitemlink(SnakeTongue)); + mesc l("Regular Drops: "+ + getitemlink(SnakeSkin)); + mesc l("Rare Drops: "+ + getitemlink(JeansShorts)+ + getitemlink(DivineApple)+ + getitemlink(Kanabo)); + next; + .@dest=HCD_SNAKEPIT; + .@hours=3; + break; + case 7: + mesc ".:: " + l("Dangerous Area") + " ::."; + mesc l("Send your Homunculus to loot the Impregnable Fortress!"); + mesc l("Average EXP: "+format_number(140000)), 3; + mesc l("Common Drops: None"); + mesc l("Regular Drops: "+ + getitemlink(MoubooSteak)+ + getitemlink(HastePotion)+ + getitemlink(StrengthPotion)+ + getitemlink(SmokeGrenade)+ + getitemlink(Grenade)+ + getitemlink(MercBoxA)+ + getitemlink(AncientBlueprint)+ + getitemlink(Lockpicks)+ + getitemlink(StatusResetPotion)+ + getitemlink(DungeonMap)+ + getitemlink(TreasureMap)); + mesc l("Rare Drops: "+ + getitemlink(MagicApple)); + next; + .@dest=HCD_DANGERAREA; + .@hours=6; + break; + case 8: + mesc ".:: " + l("Monster King Basement") + " ::."; + mesc l("Send your Homunculus to one of the most dangerous...!"); + mesc l("Average EXP: "+format_number(1000000)), 3; + mesc l("Common Drops: None"); + mesc l("Regular Drops: "+ + getitemlink(Curshroom)+ + getitemlink(HastePotion)+ + getitemlink(StrengthPotion)+ + getitemlink(SmokeGrenade)+ + getitemlink(Grenade)+ + getitemlink(AncientBlueprint)+ + getitemlink(DungeonMap)+ + getitemlink(TreasureMap)+ + getitemlink(CrazyRum)+ + getitemlink(MercBoxB)); + mesc l("Rare Drops: "+ + getitemlink(WhiskeyAle)+ + getitemlink(GoldenApple)+ + getitemlink(DivineApple)+ + getitemlink(MagicApple)); + next; + .@dest=HCD_MKBASEMENT; + .@hours=8; + break; + case 10: + mesc ".:: " + l("The Market") + " ::."; + mesc l("Send your Homunculus to make money!"); + mesc l("Average EXP: "+16), 3; + mesc l("Maximum Profit: @@ GP", format_number(.zenycap)), 3; + mesc l("Common Drops: "+ + getitemlink(Piberries)); + mesc l("Regular Drops: "+ + getitemlink(Bread)+ + getitemlink(Cheese)+ + getitemlink(Beer)); + mesc l("Rare Drops: "+ + getitemlink(TreasureMap)); + next; + .@dest=HCD_MARKET; + .@hours=1; + break; + default: + close; + } + + // Confirmation + mesc l("Really deploy your homunculus?"), 1; + if (askyesno() == ASK_NO) + goto L_Main; + + // Deploy Homunculus (if possible) + if (!homstatus() && gethominfo(0)) { + deployhomunculus(); + mesc l("Deployed!"), 2; + HOMUN_DEPLOYFIELD=.@dest; + HOMUN_DEPLOYDATE=gettimetick(2)+(60*60*.@hours); + } + close; + +// lootField (Field ID) +// Returns amount of Homunculus EXP to get +function lootField { + // Ensure you have free space + inventoryplace Iten, 3, NPCEyes, 9; + .@lv=gethominfo(6); + + // Base EXP is based on Homunculus level and intimacy + .@val=.@lv*min(10000, gethominfo(3))/10000; + + switch (getarg(0)) { + case HCD_PIOUFIELDS: + .@val+=200; + anyloot(PiouFeathers, .@lv, 10000, + PiouLegs, max(1, .@lv/2), 8000, + HalfEggshell, .@lv, 6000, + GoldenApple, 1, 100); + break; + case HCD_HERBFIELDS: + .@val+=10; + anyloot(ArtichokeHerb, .@lv, 10000, + CobaltHerb, max(1, .@lv/2), 6000, + GambogeHerb, max(1, .@lv/2), 6000, + MauveHerb, max(1, .@lv/2), 6000, + AlizarinHerb, max(1, .@lv/2), 6000, + ShadowHerb, max(1, .@lv/3), 4000, + ReedBundle, 2, 800); + break; + case HCD_OREMINES: + .@val+=24; + anyloot(Coal, .@lv, 9999, + IronOre, max(1, .@lv/2), 2000, + CopperOre, max(1, .@lv/2), 1000, + SilverOre, max(1, .@lv/3), 700, + GoldOre, max(1, .@lv/4), 450, + TinOre, max(1, .@lv/5), 350, + LeadOre, max(1, .@lv/6), 300, + TitaniumOre, max(1, .@lv/8), 100, + (REBIRTH ? IridiumOre : TitaniumOre), max(1, .@lv/10), 80, + (REBIRTH ? PlatinumOre : IridiumOre), 1, 20); + break; + case HCD_RAREMINES: + .@val+=30; + anyloot(Pearl, 1, 9999, + Diamond, limit(1, .@lv/2, 3), 2000, + Ruby, limit(1, .@lv/2, 3), 1700, + Emerald, limit(1, .@lv/2, 3), 1400, + Sapphire, limit(1, .@lv/2, 3), 1100, + Topaz, limit(1, .@lv/2, 3), 800, + Amethyst, limit(1, .@lv/2, 3), 500, + LightGreenDiamond, 1, 100, + BlackPearl, 1, 10, + BlueManaPearl, 1, 1); + break; + case HCD_SLIMENEST: + .@val+=1600; + anyloot(MaggotSlime, .@lv, 10000, + Arrow, .@lv, 9000, + IronArrow, .@lv, 4000, + TreasureKey, max(1, .@lv/10), 6000, + CactusDrink, limit(1, .@lv/10, 3), 4000, + CoinBag, limit(1, .@lv/8, 5), 4000, + CasinoCoins, limit(1, .@lv/6, 10), 3200, + Coal, limit(1, .@lv/10, 3), 3000, + Dagger, limit(1, .@lv/15, 3), 3000, + Candy, limit(1, .@lv/5, 6), 2000, + ChocolateBar, limit(1, .@lv/20, 3), 1000, + Milk, 1, 200, + IcedBottle, 1, 60); + break; + case HCD_SNAKEPIT: + .@val+=4500; + anyloot(SnakeEgg, limit(1, .@lv/8, 10), 9600, + SnakeTongue, limit(1, .@lv/16, 5), 9500, + SnakeSkin, limit(1, .@lv/20, 3), 7000, + JeansShorts, 1, 300, + DivineApple, 1, 10, + Kanabo, 1, 1); // Teasing Xanthem for mapping here + break; + case HCD_DANGERAREA: + .@val+=140000; + anyloot(HastePotion, limit(1, .@lv/20, 10), 4000, + StrengthPotion, limit(1, .@lv/20, 10), 4000, + SmokeGrenade, limit(1, .@lv/18, 8), 4000, + Grenade, limit(1, .@lv/28, 6), 3000, + MoubooSteak, limit(1, .@lv/28, 6), 2500, + MercBoxA, 1, 2000, + AncientBlueprint, 1, 1500, + Lockpicks, limit(1, .@lv/30, 4), 1000, + StatusResetPotion, 1, 1000, + DungeonMap, 1, 700, + TreasureMap, 1, 700, + MagicApple, 1, 40); + break; + case HCD_MKBASEMENT: + .@val+=1000000; + anyloot(HastePotion, limit(1, .@lv/20, 10), 6000, + StrengthPotion, limit(1, .@lv/20, 10), 6000, + Curshroom, limit(1, .@lv/28, 6), 4500, + SmokeGrenade, limit(1, .@lv/18, 8), 4500, + Grenade, limit(1, .@lv/28, 6), 3500, + AncientBlueprint, 1, 2500, + DungeonMap, 1, 2000, + TreasureMap, 1, 2000, + CrazyRum, 1, 1500, + MercBoxB, 1, 1000, + WhiskeyAle, 1, 400, + GoldenApple, 1, 80, + DivineApple, 1, 60, + MagicApple, 1, 45); + break; + case HCD_MARKET: + .@val+=16; + // Sending a fresh homun in market may yield no GP. + Zeny+=min(.zenycap, 1000*.@lv*min(10000, gethominfo(3))/100000); + anyloot(Piberries, .@lv, 9000, + Bread, limit(1, .@lv/3, 12), 7000, + Cheese, .@lv, 5000, + Beer, limit(1, .@lv/10, 10), 1200, + TreasureMap, 1, 420); + break; + } + // A final boost for sake of randomness + .@val+=rand2(0, 10); + return .@val; +} + +OnInit: + .sex=G_MALE; + .distance=5; + // Old Maximum Profit: 10,000 GP + // Coin Bag farming: 180 mobs, at 5% drop rate: 5,000 GP max + .zenycap=7500; + end; + +} + diff --git a/npc/017-10/kenton.txt b/npc/017-10/kenton.txt new file mode 100644 index 0000000..3ffc2fa --- /dev/null +++ b/npc/017-10/kenton.txt @@ -0,0 +1,204 @@ +// TMW2/LoF scripts. +// Authors: +// Jesusalva +// Description: +// Part of player story + +017-10,57,34,0 script Kenton NPC_KENTON,{ + .@q=getq(General_Narrator); + mesn; + mesq l("Hello! My name is Kenton, and I'm in charge of Public Affairs of Land Of Fire."); + next; + mesn; + mesq l("You see, Pihro and Pyndragon, the mayors, are really busy people. They're developing a game or something as we speak."); + next; + mesn; + mesq l("So if you have any issue which would require the Mayor to see it, you'll need me first."); + switch (.@q) { + case 0: + Exception("ERROR", RB_SPEECH|RB_ISFATAL); + case 17: + next; + mesc l("@@ raise an eyebrow as you hand him Gelid's letter.", .name$); + next; + mesn; + mesq l("That's a really strange letter from Mr. Frozenheart."); + next; + mesn; + mesq l("But also very amusing. I'm sure Pihro and Pyndragon, the town mayors, will fancy this request of yours."); + next; + mesn; + mesq l("Anyway, as you might be aware, Land Of Fire came here in an incident after the Monster King took all Mana Stones... So, we have a good magical affinity."); + next; + mesn; + mesq l("Most of our professors moved on to the Academy Island, and are with Tulimshar's and Frostia's professors working right now. But..."); + next; + mesn; + mesq l("...Lalica, the witch, is still with us. And she came to complain that a petty thief stolen a very precious item of hers."); + next; + mesn; + mesq l("I don't know what, but it is ")+b(l("small, red, round and shiny."))+l("And we know who the thief is."); + next; + mesn; + mesq l("I've dispatched Benjamin and some other city guards after her flew to Elenium Mines, but thus far, they haven't returned."); + next; + mesn; + mesq l("I need you to find her to and bring her back there, along whatever she stole. Then I'll honor your request."); + next; + mesn; + mesq l("After all, that was two nights ago, and none of them returned..."); + mesc b(l("WARNING: "))+l("Minimum advised level 72 to do this quest."), 1; + mesc b(l("WARNING: "))+l("You would do well as come prepared, as failing in the last stage will RESET current progress on the quest."), 1; + setq LoFQuest_Barbara, 1; + setq General_Narrator, 18; + break; + // Barbara Quest in progress + case 18: + .@s=getq(LoFQuest_Barbara); + .@s3=getq3(LoFQuest_Barbara); + switch (.@s) { + case 5: + // No apple - you fail + if (!.@s3) { + mesn; + mesq l("Good luck arresting the criminal!"); + mesc l("You need both the stolen item as the thief to complete the quest."), 1; + close; + } + + // Good reporting + mesn; + if (!BARBARA_STATE) + mesq l("I see you've brought Barbara here, excellent. She seems pretty beat up."); + else + mesq l("...Where's Barbara, the thief?"); + next; + mesn; + mesq l("Also, do you have the stolen item?"); + // You fail + if (!(askyesno() == ASK_YES && countitem(MagicApple))) { + mes ""; + mesn; + mesq l("Bring me the stolen item, pretty please."); + close; + } + mes ""; + mesn; + mesq l("Good, there they are."); + next; + if (BARBARA_STATE) { + mesn strcharinfo(0); + mesq l("I regret to inform Barbara was killed in battle before I could find her. I burried her in the mines."); + next; + mesn; + mesq l("Oh... What a bummer. But you've brought the item back, so we're OK."); + next; + // Barbara is alive + } else { + mesc l("Pledge for Barbara's innocence?"); + // Pledge menu + if (askyesno() == ASK_YES) { + BARBARA_STATE=3; + mesn; + mesq lg("...That's a surprise, the noble @@ defending a criminal like her.", strcharinfo(0)); + next; + mesn; + mesq l("I will let Lalica, Pihro and Pyndragon know that you find her not guilty, but..."); + next; + mesn; + mesq l("The final sentence still belongs to LoF Admins."); + next; + } else { + BARBARA_STATE=2; + } + // EOF: Pledge Menu + mesn; + mesq l("I'll have her hospitalized now."); + next; + } + mesn; + mesq l("Anyway, here is your reward."); + delitem MagicApple, 1; + setq General_Narrator, 19; + setq LoFQuest_Barbara, 0, 0, 0; + getexp 400000, 15000; // Needed: 1,148,484 + Zeny+=3300; // You get a reward in cash + getvaultexp(10); + next; + mesn; + mesq l("Please come back later to know how things are going."); + break; + // Bad reporting + case 4: + mesn; + mesq l("Good luck arresting the criminal!"); + mesc l("You need both the stolen item as the thief to complete the quest."), 1; + break; + // Cutscene not seen + case 1: + mesn; + mesq l("Good luck arresting the criminal!"); + break; + // Default message + default: + mesn; + mesq l("Benjamin reported in. Good luck arresting the criminal!"); + break; + } + break; + // Barbara Quest was complete and solution was marked in BARBARA_STATE + case 19: + next; + mesn; + mesq l("So, where we were again... Oh right, travel to world edge, look for family or whatever, right?"); + next; + mesn; + mesq l("Yes. I mean, yes, Pihro and Pyndragon will fancy you an airship."); + next; + select + l("What is an airship? O.o"), + l("Cool, an airship!"); + mes ""; + if (@menu == 1) { + mesn; + mesq l("An Airship? It is just a ship. Which flies."); + next; + } + mesn; + mesq l("Now, crafting an airship requires a lot of efforts, resources, money and specially time. Fourteen months, to be exact."); + next; + mesn; + mesq l("By the way , it is done already. You took a long time, you know? Also, we're talking about Pihro and Pyndragon."); + next; + mesn; + mesq l("Besides, the Alliance High Council had plans for an expedition on the Fortress Island, but had no personel to do it."); + next; + mesn; + mesq l("Yes, you are now personel! Doesn't it feels exciting? Please tell me it does, I have nothing else to convince you to go otherwise."); + next; + mesn; + mesq l("The Fortress Island is a terribly dangerous place, so the Alliance sent a scout party beforehand, and set up a tower in front of an... erm... Very disturbing walled place we found."); + next; + mesn; + mesq l("Now go and save the world or something like that. I mean, it is not like I really cared with the world, anyway... With luck you'll even find what you are looking for."); + setq General_Narrator, 20; + // FALLTHROUGH + case 20: + case 21: + next; + setcamnpc "#ToFortress"; + mesn; + mesq l("Just go over there and you'll be able to board the Airship. The travel takes a while so please be patient and good luck!"); + restorecam; + // TODO: Maybe investigate & report to Tulimshar Magic Council? + break; + } + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; + +} + diff --git a/npc/017-10/politics.txt b/npc/017-10/politics.txt new file mode 100644 index 0000000..8bcd243 --- /dev/null +++ b/npc/017-10/politics.txt @@ -0,0 +1,58 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Town Administrator file, see npc/functions/politics.txt +// User variables: +// #POL_APPLYWEEK = Week of last application +// #POL_VOTEDAY = Day of last vote + +017-10,54,35,0 script Land Of Fire Office NPC_POLITICS,{ +do +{ + mesc ".:: "+l("Land Of Fire Townhall")+" ::.", 2; + mesc l("Current Town Administrator: ")+$LOF_MAYOR$, 3; + POL_TownInfo("LOF"); + mesc l("Application fee: @@ GP", .applytax); + next; + select + l("Information"), + rif(strcharinfo(0) == $LOF_MAYOR$, l("Manage Town")), + rif(#POL_APPLYWEEK != gettimeparam(GETTIME_WEEKDAY), l("Apply for the office!")), + l("View Candidate List and cast a vote"), + l("[Quit]"); + + switch (@menu) { + case 1: + POL_Information(); + break; + case 2: + POL_Manage("LOF"); + break; + case 3: + // array_push might be too sensible for getd/setd + if (Zeny < .applytax) + break; + Zeny-=.applytax; + $LOF_MONEY+=.applytax; + #POL_APPLYWEEK=gettimeparam(GETTIME_WEEKDAY); + array_push($LOF_CANDIDATE$, strcharinfo(0)); + array_push($LOF_VOTES, 0); + mesc l("Application successful!"), 3; + next; + break; + case 4: + POL_Candidate("LOF"); + break; + default: + close; + } +} while (true); +end; + +OnInit: + .applytax=100; + .distance=4; + end; +} + diff --git a/npc/017-10/vault.txt b/npc/017-10/vault.txt new file mode 100644 index 0000000..86bfeff --- /dev/null +++ b/npc/017-10/vault.txt @@ -0,0 +1,20 @@ +// TMW2/LoF Script. +// Author: +// Jesusalva +// Notes: +// Based on BenB idea. + +017-10,20,41,0 script Vault#01710 NPC_NO_SPRITE,{ + LootableVault(3, 5, "01710"); + close; + +OnInit: + .distance=3; + end; + +OnClock0202: +OnClock1419: + $VAULT_01710+=rand2(50,100); + end; +} + diff --git a/npc/017-2-1/_import.txt b/npc/017-2-1/_import.txt new file mode 100644 index 0000000..8b16332 --- /dev/null +++ b/npc/017-2-1/_import.txt @@ -0,0 +1,4 @@ +// Map 017-2-1: Jack's Basement +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-2-1/_mobs.txt", +"npc/017-2-1/_warps.txt", diff --git a/npc/017-2-1/_mobs.txt b/npc/017-2-1/_mobs.txt new file mode 100644 index 0000000..7f12149 --- /dev/null +++ b/npc/017-2-1/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-2-1: Jack's Basement mobs +017-2-1,62,83,5,6 monster Big Emerald Bif 1106,1,45000,45000 +017-2-1,73,61,54,42 monster Dark Lizard 1051,32,25000,25000 +017-2-1,49,71,40,58 monster Red Slime 1092,29,45000,45000 +017-2-1,72,43,58,28 monster Copper Slime 1088,2,45000,45000 diff --git a/npc/017-2-1/_warps.txt b/npc/017-2-1/_warps.txt new file mode 100644 index 0000000..e9ec14c --- /dev/null +++ b/npc/017-2-1/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-2-1: Jack's Basement warps +017-2-1,121,115,0 warp #017-2-1_121_115 0,0,017-2,31,29 +017-2-1,27,127,0 warp #017-2-1_27_127 0,0,018-3,114,24 +017-2-1,39,23,0 warp #017-2-1_39_23 0,0,017-2-2,71,77 diff --git a/npc/017-2-2/_import.txt b/npc/017-2-2/_import.txt new file mode 100644 index 0000000..1f63289 --- /dev/null +++ b/npc/017-2-2/_import.txt @@ -0,0 +1,4 @@ +// Map 017-2-2: Skull Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-2-2/_mobs.txt", +"npc/017-2-2/_warps.txt", diff --git a/npc/017-2-2/_mobs.txt b/npc/017-2-2/_mobs.txt new file mode 100644 index 0000000..d409717 --- /dev/null +++ b/npc/017-2-2/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-2-2: Skull Cave mobs +017-2-2,70,57,16,20 monster Shadow Plant 1189,4,60000,10000 +017-2-2,70,55,14,20 monster Dark Lizard 1051,18,25000,25000 diff --git a/npc/017-2-2/_warps.txt b/npc/017-2-2/_warps.txt new file mode 100644 index 0000000..7ee8b20 --- /dev/null +++ b/npc/017-2-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-2-2: Skull Cave warps +017-2-2,71,78,0 warp #017-2-2_71_78 1,0,017-2-1,39,24 diff --git a/npc/017-2/_import.txt b/npc/017-2/_import.txt new file mode 100644 index 0000000..6665a1d --- /dev/null +++ b/npc/017-2/_import.txt @@ -0,0 +1,4 @@ +// Map 017-2: Jack's House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-2/_warps.txt", +"npc/017-2/vault.txt", diff --git a/npc/017-2/_warps.txt b/npc/017-2/_warps.txt new file mode 100644 index 0000000..12b4bba --- /dev/null +++ b/npc/017-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-2: Jack's House warps +017-2,41,32,0 warp #017-2_41_32 0,0,017-1,119,61 +017-2,31,30,0 warp #017-2_31_30 0,0,017-2-1,121,116 diff --git a/npc/017-2/vault.txt b/npc/017-2/vault.txt new file mode 100644 index 0000000..3f3e49c --- /dev/null +++ b/npc/017-2/vault.txt @@ -0,0 +1,20 @@ +// TMW2/LoF Script. +// Author: +// Jesusalva +// Notes: +// Based on BenB idea. + +017-2,45,22,0 script Vault#0172 NPC_VAULT,{ + LootableVault(0, 3, "0172"); + close; + +OnInit: + .distance=3; + end; + +OnClock0201: +OnClock1418: + $VAULT_0172+=rand2(3,7); + end; +} + diff --git a/npc/017-3/_import.txt b/npc/017-3/_import.txt new file mode 100644 index 0000000..bbf474d --- /dev/null +++ b/npc/017-3/_import.txt @@ -0,0 +1,14 @@ +// Map 017-3: Dimond's Cove +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-3/_warps.txt", +"npc/017-3/chagashroom.txt", +"npc/017-3/chef.txt", +"npc/017-3/dimonds.txt", +"npc/017-3/doctor.txt", +"npc/017-3/doug.txt", +"npc/017-3/gambler.txt", +"npc/017-3/loratay.txt", +"npc/017-3/nico.txt", +"npc/017-3/slots.txt", +"npc/017-3/vault.txt", +"npc/017-3/workers.txt", diff --git a/npc/017-3/_warps.txt b/npc/017-3/_warps.txt new file mode 100644 index 0000000..3decc18 --- /dev/null +++ b/npc/017-3/_warps.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-3: Dimond's Cove warps +017-3,27,24,0 warp #017-3_27_24 0,0,017-3,35,68 +017-3,32,45,0 warp #017-3_32_45 0,0,017-1,92,89 +017-3,40,27,0 warp #017-3_40_27 2,0,017-3,83,28 +017-3,36,68,0 warp #017-3_36_68 0,0,017-3,28,24 +017-3,67,27,0 warp #017-3_67_27 2,0,017-3,67,72 +017-3,83,29,0 warp #017-3_83_29 2,0,017-3,40,28 +017-3,67,73,0 warp #017-3_67_73 2,0,017-3,67,28 diff --git a/npc/017-3/chagashroom.txt b/npc/017-3/chagashroom.txt new file mode 100644 index 0000000..9a449bc --- /dev/null +++ b/npc/017-3/chagashroom.txt @@ -0,0 +1,163 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Chagashroom → Red Plush Wine +// Variables: +// REDWINE_DATE = When the wine started being done +// REDWINE_DONE = When the wine will be ready +// REDWINE_AMMO = How much Wine you're trying to make +// Success Rate is based on how much you're trying to do and how long ago that was + +017-3,25,78,0 script Wine Making Barrel NPC_NO_SPRITE,{ + goto L_Main; + // redwine_chance() + // Returns chance (0~10,000) to successfully obtain wine + // REDWINE_DONE/REDWINE_DATE is taken in account + function redwine_chance { + .@max=10000; + .@base=REDWINE_DATE;//-(REDWINE_DONE-REDWINE_DATE); + // .@c = how much time is left until completion + // .@d = original amount of time required + // .@e = Current time + .@c=REDWINE_DONE-.@base; //-gettimetick(2); + .@d=REDWINE_DATE-.@base; //-REDWINE_DONE; + .@e=gettimetick(2)-.@base; + + // We must divide everything by 10 to cause imprecision + // aka. don't cause overflow bug + .@c=.@c/10; + .@d=.@d/10; + .@e=.@e/10; + + //debugmes "%d - %d - %d", .@d, .@e, .@c; + //debugmes "Start - Now - Finish"; + if (.@c == 0) + return .@max; + if ($@GM_OVERRIDE) debugmes "Ratio: %d/%d = %d", .@e, .@c, (.@e*.@max)/.@c; + return min(10000, (.@e*.@max)/.@c); + } + +L_Main: + if (!REDWINE_DATE) { + mesn; + mesc l("Do you want to make wine?"); + next; + select + l("Information"), + l("Yes"), + l("No"); + mes ""; + + switch (@menu) { + case 1: + mesc l("Produced item:"); + mesc l("@@", getitemlink(RedPlushWine)); + mes ""; + mesc l("Cost per two glass:"); + mesc l("* @@/@@ @@", countitem(SeaDrops), 1, getitemlink(SeaDrops)); + mesc l("* @@/@@ @@", countitem(Plushroom), 1, getitemlink(Plushroom)); + mesc l("* @@/@@ @@", countitem(Chagashroom), 30, getitemlink(Chagashroom)); + mesc l("* @@ Water Bottle", 1); + next; + break; + case 2: + mesc l("How many batches do you want to produce? (max. 5)"); + input .@glass_count; + if (.@glass_count < 1 || + .@glass_count > 5 || + countitem(SeaDrops) < .@glass_count || + countitem(Plushroom) < .@glass_count || + countitem(Chagashroom) < .@glass_count*30 + ) { + mesc l("Not enough ingredients or invalid amount."), 1; + break; + } + mesc l("Which water will you use?"); + mesc l("The bottom-most the water, the better the bonus."); + menuint + l("Cancel"), -1, + rif(countitem(BottleOfSewerWater) >= .@glass_count, l("Sewer Water")), 0, + rif(countitem(BottleOfSeaWater) >= .@glass_count, l("Sea Water")), 3600, + rif(countitem(BottleOfTonoriWater) >= .@glass_count, l("Tonori Water")), 11760, + rif(countitem(BottleOfWoodlandWater) >= .@glass_count, l("Woodland Water")), 12000, + rif(countitem(BottleOfDivineWater) >= .@glass_count, l("Divine Water")), 21600; + mes ""; + if (@menuret < 0) + break; + switch (@menuret) { + case 0: + .@bonus=@menuret; + .@water=BottleOfSewerWater; + break; + case 3600: + .@bonus=@menuret; + .@water=BottleOfSeaWater; + break; + case 11760: + .@bonus=@menuret; + .@water=BottleOfTonoriWater; + break; + case 12000: + .@bonus=@menuret; + .@water=BottleOfWoodlandWater; + break; + case 21600: + .@bonus=@menuret; + .@water=BottleOfDivineWater; + break; + default: + mesc l("Error, invalid return code, blame Saulc"), 1; + mes "==== SCRIPT ABORTED"; + close; + } + + // Save data + delitem SeaDrops, .@glass_count; + delitem Plushroom, .@glass_count; + delitem Chagashroom, .@glass_count*30; + delitem .@water, .@glass_count; + REDWINE_AMMO=.@glass_count; + REDWINE_DATE=gettimetick(2); + REDWINE_DONE=gettimetick(2)-.@bonus+.mintime; + REDWINE_DONE+=.cuptime*REDWINE_AMMO; + break; + case 3: + close; + break; + } + goto L_Main; + } else { + mesn; + mesc l("Your request for @@ @@ are being fermented for @@.", REDWINE_AMMO, getitemlink(RedPlushWine), FuzzyTime(REDWINE_DATE)); + next; + inventoryplace RedPlushWine, REDWINE_AMMO; + mesn; + mes l("Trying to retrieve it now will have @@ % chance to be successful.", redwine_chance()/100); + mes l("Attempt to retrieve it now?"); + next; + if (askyesno() == ASK_YES) { + if (rand(1000,10000) < redwine_chance()) { + mesc l("Success!"), 3; + getitem RedPlushWine, REDWINE_AMMO*2; + } else { + mesc l("The wine wasn't ready yet and you lost it..."), 1; + } + REDWINE_DATE=0; + REDWINE_AMMO=0; + } + } + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + + // Time to make each batch + .cuptime=(60*60*6); + // Base time to make any amount of cups + .mintime=(60*60*24); + end; + +} + diff --git a/npc/017-3/chef.txt b/npc/017-3/chef.txt new file mode 100644 index 0000000..d6f7db1 --- /dev/null +++ b/npc/017-3/chef.txt @@ -0,0 +1,410 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Dimond's Cove Chef. He'll disguise Mopox Cure Potion, and he is part from +// Easter 2011 and Easter 2012 quests. Assigns the Butcher Knife Quest. + +017-3,33,25,0 script Chef#dimond NPC_CHEF,{ + .@q=getq(LoFQuest_ButcherKnife); + showavatar NPC_CHEF; + if (.@q == 1) goto L_Report; + if (.@q == 0 && BaseLevel >= 34) goto L_Start; + goto L_Busy; + +L_Busy: + mesn; + mesq l("I'm too busy right now to talk. Dimond's recipes are the best!"); + close; + +L_Start: + mesn; + mesq l("The cooking contest is coming up, and I don't want to lose to @@... Halinarzo's cook is just THAT good.", l("Ryan")); + next; + mesn; + mesq l("However, I still have hope. Dimond's secret recipe. Ah, but I can't get the ingredients for it..."); + next; + mesn; + mesq lg("...Actually, you seem like a capable adventurer. If you give me some ingredients I can't get, I'll give you a @@.", "...Actually, you seem like a capable adventurer. If you give me some ingredients I can't get, I'll give you a @@.", getitemlink(ButcherKnife)); + mesq l("Are you, perhaps, interested?"); + next; + if (askyesno() == ASK_YES) { + mes ""; + mesn; + mesq l("Wonderful! Just bring me 12 @@ and 4 @@. I'm counting on you!", getitemlink(SmallMushroom), getitemlink(MoubooSteak)); + setq LoFQuest_ButcherKnife, 1; + } + close; + +L_Report: + mesn; + mesq l("Did you brought me 12 @@ and 4 @@? The @@ is waiting for you, sharp sharp!", getitemlink(SmallMushroom), getitemlink(MoubooSteak), getitemlink(ButcherKnife)); + mes ""; + select + l("Not yet, but I'll bring them."), + rif(countitem(SmallMushroom)>=12 && countitem(MoubooSteak)>=4, l("Yes, here they are.")); + mes ""; + if (@menu == 2) { + inventoryplace ButcherKnife, 1; + delitem SmallMushroom, 12; + delitem MoubooSteak, 4; + getitem ButcherKnife, 1; + getexp 4887, 37; + setq LoFQuest_ButcherKnife, 2; + mesn; + mesq l("Wonderful! Here, take the @@, as promised. I need to get back to cooking!", getitemlink(ButcherKnife)); + } + close; + + /* + .@q=getq(TulimsharQuest_Kadiya); + if ((.@q == 9) && (countitem ("MopoxCurePotion") > 0)) + goto L_lace_chococake; + if ((.@q == 11) && (countitem ("MopoxCurePotion") > 0)) + goto L_lace_cupcake; + close; + +L_lace_chococake: + next; + menu + "Sorry.", L_Close, + "I need your help with a sick young girl!", L_Next; + +L_Next: + mesn; + mes ""; + mes "You explain Kadiya's situation to him."; + mes "\"Hmrmf. I can't help you; I'm a chef, not a baker!\""; + close; + +L_lace_cupcake: + next; + menu + "Sorry.", L_Close, + "I need your help with a sick young girl!", L_Next1; + +L_Next1: + mesn; + mes ""; + mes "You explain Kadiya's situation to him."; + mes "\"Hmrmf. I was about to make another batch of orange cupcakes. Do you have that potion with you?\""; + next; + menu + "No, sorry.", L_bringit, + "Yes, here you are.", L_Next2; + +L_Next2: + if (countitem ("MopoxCurePotion") < 1) + goto L_bringit; + mesn; + mes ""; + mes "He shudders as he smells the bottle."; + mes "\"That is disgusting! How do you expect me to mask this?\""; + mes "He ponders his own question for a moment."; + next; + mesn; + mes ""; + mes "\"Ah yes. We need to bind the smell in gingerbread. Bring me three pieces of gingerbread, one orange, and 500 GP for the other ingredients, and I will make you your cupcake.\""; + next; + menu + "I'll go and get it.", L_Close, + "Here you are!", L_Next3, + "Not now.", L_Close; + +L_Next3: + if (countitem ("GingerBreadMan") < 3) + goto L_No_gingerbread; + if (countitem ("Orange") < 1) + goto L_No_orange; + if (countitem ("MopoxCurePotion") < 1) + goto L_No_Potion; + if (Zeny < 500) + goto L_No_Zeny; + + Zeny = Zeny - 500; + delitem "GingerBreadMan", 3; + delitem "Orange", 1; + delitem "MopoxCurePotion", 1; + + getitem "LacedOrangeCupcake", 1; + + mesn; + mes ""; + mes "\"Good, good. This should work.\""; + mes "He crumbles the gingerbread, mixes it with the potion and some spices, and finally adds some cupcake batter and the orange pieces."; + next; + mesn; + mes ""; + mes "\"Take a seat.\""; + mes "He fills the mixture into a cupcake form and puts it into the oven. You watch the cupcake grow for a while."; + mes "Finally, he removes the form and carefully extracts the cupcake."; + next; + mesn; + mes ""; + mes "\"Here you are. It's a bit bigger than a regular cupcake and will taste a bit like ginger, I think, but otherwise it should be fine.\""; + close; + +L_No_gingerbread: + mesn; + mes ""; + mes "\"I will need three pieces of gingerbread.\""; + close; + +L_No_orange: + mesn; + mes ""; + mes "\"Please bring me an orange for these cupcakes.\""; + close; + +L_No_Zeny: + mesn; + mes ""; + mes "\"No. I need extra ingredients worth 500 GP, and I expect you to pay for those.\""; + close; + +L_No_Potion: + mesn; + mes ""; + mes "\"Where did your Mopox potion go?\""; + close; + +L_bringit: + mesn; + mes ""; + mes "\"Bring it here when you do.\""; + next; + +L_Close: + close; + */ + +/* XXX: Easter 2011 stuff +L_JellyBean: + mesn; + mes ""; + mes "\"I can't talk right now... I'm too busy.\""; + next; + mes "\"Oh, wait. I see you are the adventurer who was speaking with the Easter Bunny. He has a really bad memory, you know? Every year he forgets who he needs to ask to get Jelly Beans, and every year an adventurer comes to my kitchen asking if I can help the Easter Bunny get some.\""; + next; + mes "\"Even though I am really busy this time of year, I will help you make some Jelly Beans to put in the basket if you wish. Just to keep the tradition alive...\""; + menu + "I would really appreciate that!", L_SetJellyBean, + "Nah, I decided not to do that stupid quest.", L_Close; + +L_SetJellyBean: + QUEST_Easter11 = 8; + goto L_MakeJellyBean; + +L_MakeJellyBean: + mesn; + mes ""; + mes "\"I will need you to bring me some candies.\""; + mes "\"I think 15 should be enough.\""; + mes "\"Sea slimes seem to have those, so try them.\""; + mes "\"And because it's the holidays, I'll add in the other ingredient for free.\""; + menu + "I have some candies!", L_CandyCheck, + "Ok, I will be back soon.", L_Close, + "I changed my mind, forget about it.", L_Close; + +L_CandyCheck: + if(countitem("Candy") < 15) + goto L_EasterNotEnough; + if(countitem("Candy") >= 15) + goto L_EasterEnough; + close; + +L_EasterNotEnough: + mesn; + mes ""; + mes "\"You do not have enough candies for me to work with yet.\""; + mes "\"Please go get more.\""; + close; + +L_EasterEnough: + getinventorylist; + if (@inventorylist_count == 100 && countitem("Candy") > 15) + goto L_EasterTooMany; + if(countitem("Candy") < 15) + goto L_EasterNotEnough; + delitem "Candy", 15; + getitem "JellyBeans", 1; + QUEST_Easter11 = 9; + mesn; + mes ""; + mes "\"You have gathered enough candies for me to make the Jelly Beans.\""; + mes "He puts the candies into a pot and starts melting them."; + mes "He adds in some gooey stuff, then lets the solution cool for a moment before forming the Jelly Beans."; + mes "\"Here are your Jelly Beans.\""; + close; + +L_EasterTooMany: + mesn; + mes ""; + mes "\"You don't have enough room for the Jelly Beans. Come back later.\""; + close; + +L_SeeBunny: + mesn; + mes ""; + mes "\"Oh yeah, you need to return to the Easter Bunny now.\""; + next; + goto L_Begin; + close; +*/ + + + + +/* XXX: Easter 2012 stuff +// Really cool script, we must reintroduce it %%e + +L_Easter2012_Begin: + mesn; + mes "\"Ahh, " + strcharinfo(0) + "! I really need to talk to you. Did the Easter Bunny give you a very thick and heavy pan?\""; + menu + "Yes, he did. Why?", L_Easter2012_Continue; + +L_Easter2012_Continue: + mesn; + mes "\"Good to know! I am asking because now you are my only hope. I have invented the ultimate culinary wonder, a recipe for the most incredibly tasty food you can imagine!\""; + next; + mes "\"But here is the problem, to complete this recipe I need a very resistant pan, because in the process I need to use a very strong acid solution based on yeti saliva!\""; + next; + mes "\"Can you believe I ruined ALL of my pans and 17 infantry helmets while trying to cook it? That is why I am asking for this favor. Could you lend me your pan? I promise I will give you a jar of the final product.\""; + next; + mes "\"Oh, and I will need to keep it with me for a while, as the ingredients need to rest in the acid solution for a long time, but you can always come here to see how it is going.\""; + menu + "Ok, but I want my pan back when it is done!", L_Accept_Cooking, + "No way, I won't let you fill my hat with corrosive yeti saliva!", L_NoWay; + +L_NoWay: + mesn; + mes "\"Ahh, don't say that! Please, think about it and come back if you change your mind.\""; + close; + +L_Accept_Cooking: + mesn; + mes "\"Great! But I can't promise anything regarding your hat. What I can promise is: IF it works, you will eat the most spectacular food of all time! I won't say what it is, but it is going to be great! What do you say, are you sure?\""; + menu + "Ahh, just take the pan and do your thing.", L_CarryOn, + "No way!", L_Close; + +L_CarryOn: + if (countitem("PanHat") < 1) + goto L_No_PanHat; + delitem "PanHat", 1; + QUEST_Easter12 = 7; + mes "\"Very good! Now I just need to put the basic ingredients inside a metal pan filled with water, yeti saliva, a little bit of salt and finally... The secret ingredient!\""; + next; + mes "The chef turns his back to you so he can hide what he is using. But you can clearly distinguish a smell of coal..."; + mes "\"Hahahah! And now we wait!\""; + close; + +L_Easter2012_Wait: + if ((gettime(6) == 4 && gettime(5) >= $@easter2012_reward_start_day)) + goto L_Easter2012_Result; + mesn; + mes "\"Be patient... The basic ingredients are not ready yet.\""; + close; + +L_Easter2012_Result: + @temp = rand(99); + mesn; + mes "\"Hey! I have some good news and some bad news... Which one do you want to hear first?\""; + menu + "The good one.", L_SelectNews, + "The bad one.", L_SelectNews, + "Oh no, I don't even want to hear!", L_Close; + +L_SelectNews: + if (@temp < 15) goto L_Easter2012_Lucky; + if (@temp >= 15) goto L_Easter2012_Unlucky; + close; + +L_Easter2012_Lucky: + mesn; + mes "\"Wait! Did I mention bad news? I must be crazy... There is no bad news my friend! The ultimate pickled beets is now ready AND your pan is intact!\""; + next; + mes "\"Here! Take your pan back and a jar of the best pickled beets of the mana world!\""; + QUEST_Easter12 = 10; + getitem "PickledBeets", 1; + getitem "PanHat", 1; + close; + +L_Easter2012_Unlucky: + mesn; + mes "\"Ahh, actually, it doesn't matter! The important thing is that the recipe was a success! I just can't believe how something can be so tasty!\""; + next; + menu + "Nice! Now, what about my hat?", L_HatNext; + +L_HatNext: + mes "\"Oh, that... Ehh, unfortunately, the pan is not anymore. It was completely destroyed by the time the pickled beets were ready! But don't worry, I will keep my promise and give you a jar of this culinary masterpiece!\""; + next; + menu + "Wait, wait... Are you saying I lost my hat?", L_HatNext2; + +L_HatNext2: + mesn; + mes "\"Hmm, yes. But I must say I really appreciate your effort to help me! You sacrificed your cool hat in the name of the culinary arts, only a great person would do that!\""; + next; + menu + "But... I... My hat...", L_HatNext3; + +L_HatNext3: + mesn; + mes "\"Here, take your pickled beets!\""; + getitem "PickledBeets", 1; + QUEST_Easter12 = 8; + close; + +OnEaster2012Out: + if (QUEST_Easter12 == 8) + QUEST_Easter12 = 9; + mesn; + mes "\"Hey, I couldn't help but notice your sadness... You really like these silly hats, don't you? Well, since your hat is completely destroyed, just like all my pans, I can give you one of these nice cooking hats. But only if you give my jar of pickled beets back. What do you say?\""; + menu + "A chef hat for a jar of pickled beets? Of course I want!", L_GetChefhat, + "Ahh, keep you silly hat! The pickled beets are way better!", L_KeepBeets; + close; + +L_GetChefhat: + if (countitem("PickledBeets") < 1) + goto L_No_Pickledbeets; + mesn; + mes "\"Really? Well, if you are sure about that... Here is your hat!\""; + delitem "PickledBeets", 1; + getitem "ChefHat", 1; + QUEST_Easter12 = 10; + close; + +L_KeepBeets: + mesn; + mes "\"Hmm, I see that at least you are a wise person. See you later!\""; + QUEST_Easter12 = 10; + close; + +L_No_PanHat: + mesn; + mes "\"So? You said you have the pan, where is it?\""; + close; + +L_No_Pickledbeets: + mesn; + mes "\"Hey, no pickled beets means no chef hat.\""; + close; + +L_Easter2012Foolplayers: + mes "The chef can't hear you. He keeps looking and feeling the scent of his new recipe."; + close; +*/ + +OnInit: + .sex=G_MALE; + .distance=5; + end; + +} + diff --git a/npc/017-3/dimonds.txt b/npc/017-3/dimonds.txt new file mode 100644 index 0000000..5b23e30 --- /dev/null +++ b/npc/017-3/dimonds.txt @@ -0,0 +1,641 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Dimond's Cove NPCs + +017-3,34,38,0 script Dimond NPC_DIMOND,{ + showavatar NPC_DIMOND; + mesn; + mesq l("Welcome to Dimond's Cove!"); + next; + mesn; + mesq l("Please, don't say I got fat. I DON'T WANNA HEAR IT FROM YOU, WAS I CLEAR?"); + next; + mesn; + mesq l("Please, enjoy the show! There are some people upstairs, too."); + //mesq l("Talk to the waitress to get some food. Enjoy the show! And you can sleep on the 3rd Floor."); + if (BaseLevel < 55) + close; + mesc l("Also..."); + GHQ_Assign(GreenDragon, "Land of Fire"); + close; + +OnInit: + .sex=G_FEMALE; + .distance=5; + end; +} + +017-3,40,35,0 script Phil NPC_BARD_TRUMP,{ + showavatar NPC_BARD_TRUMP; + if (season() == SPRING) + goto L_Quest; + mesn; + mesq l("We've finally solved the problem with our instruments!"); + next; + mesn; + mesq l("Now we can play any songs! Hooray!"); + close; + +L_Quest: + mesn; + mesq l("All spring it is the same thing... The instruments stop working!"); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + +017-3,40,37,0 script Jerry NPC_BARD_DRUMS,{ + showavatar NPC_BARD_DRUMS; + if (season() == SPRING) + goto L_Quest; + mesn; + mesq l("I finally figured out what was wrong. I must thank everyone who help us."); + next; + mesn; + mesq l("I can't believe it took so long to fix... And worse, next spring it'll be the same story all over again..."); + close; + +L_Quest: + mesn; + mesq l("All spring it is the same thing... The instruments stop working!"); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + +017-3,42,36,0 script Robert NPC_BARD_HARPS,{ + showavatar NPC_BARD_HARPS; + if (season() == SPRING) + goto L_Quest; + mesn; + mesq l("We held to our hope and Jerry figured it out."); + next; + mesn; + mesq l("Now we are no longer useless — we can play music again!"); + close; + +L_Quest: + mesn; + mesq l("All spring it is the same thing... The instruments stop working!"); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + + +017-3,35,35,0 script Shannon NPC_LOF_NOBLEMAN,{ + showavatar NPC_LOF_NOBLEMAN; + mesn; + mesq l("I'm a traveling poet and admirer of Robert Burns, a bard from the mana world."); + next; + mesn; + mesq l("I wonder if he exists on this world too. Because if he doesn't, I could tell many tales about him!"); + close; + /* + @month = 1; + @start_day = 24; + @end_day = 26; + if (gettime(6) != @month) goto L_No_Event; + if (gettime(5) < @start_day) goto L_No_Event; + if (gettime(5) > @end_day) goto L_No_Event; + if (gettimetick(2)-TUT_var < 2*7*86400 ) //player must be created at least 2 weeks ago - WAIT, THAT IS NOT POSSIBLE ON TMW2 + goto L_No_Event; + if (FLAGS & FLAG_HAS_BOWLER_BURNSDAY && FLAGS & FLAG_HAS_STEAMPUNK_BURNSDAY) goto L_Event_Done; + getinventorylist; + if (@inventorylist_count == 100) goto L_Full_Inv; + + mes "[Shannon]"; + mes "\"Welcome to Dimond's Cove. We are celebrating Robert Burns with supper and poems. You provide the supper and I'll provide a poem by Robert Burns. He was otherwise known as 'The Bard' and wrote many poems. The music of language is what I love most. The language of poetry. While his accent is thick, his poems tell stories that stir the soul. I'll read a poem by him and you can tell me what you think.\""; + next; + mes "\"Before I start, it is traditional to eat, drink and be merry, so help from you will be great and keep me reading.\""; + next; + mes "\"O thou! whatever title suit thee,--\""; + mes "\"Auld Hornie, Satan, Nick, or Clootie!\""; + mes "\"Wha in yon cavern, grim an' sootie,\""; + mes "\"Clos'd under hatches,\""; + mes "\"Spairges about the brunstane cootie\""; + mes "\"To scaud poor wretches!\""; + callsub S_food_menu; + mes "\"Hear me, Auld Hangie, for a wee,\""; + mes "\"An' let poor damned bodies be;\""; + mes "\"I'm sure sma' pleasure it can gie,\""; + mes "\"E'en to a deil,\""; + mes "\"To skelp an' scaud poor dogs like me,\""; + mes "\"An' hear us squeel!\""; + callsub S_food_menu; + mes "\"Great is thy pow'r, an' great thy fame;\""; + mes "\"Far ken'd an' noted is thy name;\""; + mes "\"An' tho' yon lowin heugh's thy hame,\""; + mes "\"Thou travels far;\""; + mes "\"An' faith! thou's neither lag nor lame,\""; + mes "\"Nor blate nor scaur.\""; + callsub S_food_menu; + mes "\"Whyles, ranging like a roarin lion,\""; + mes "\"For prey a' holes an' corners tryin;\""; + mes "\"Whyles, on the strong-wing'd tempest flyin,\""; + mes "\"Tirlin' the kirks;\""; + mes "\"Whyles, in the human bosom pryin,\""; + mes "\"Unseen thou lurks.\""; + callsub S_food_menu; + mes "\"I've heard my rev'rend graunie say,\""; + mes "\"In lanely glens ye like to stray;\""; + mes "\"Or whare auld ruin'd castles gray\""; + mes "\"Nod to the moon,\""; + mes "\"Ye fright the nightly wand'rer's way\""; + mes "\"Wi' eldritch croon.\""; + callsub S_food_menu; + mes "\"When twilight did my graunie summon\""; + mes "\"To say her pray'rs, douce honest woman!\""; + mes "\"Aft yont the dike she's heard you bummin,\""; + mes "\"Wi' eerie drone;\""; + mes "\"Or, rustlin thro' the boortrees comin,\""; + mes "\"Wi' heavy groan.\""; + callsub S_food_menu; + mes "\"Ae dreary, windy, winter night,\""; + mes "\"The stars shot down wi' sklentin light,\""; + mes "\"Wi' you mysel I gat a fright,\""; + mes "\"Ayont the lough;\""; + mes "\"Ye like a rash-buss stood in sight,\""; + mes "\"Wi' waving sugh.\""; + callsub S_food_menu; + mes "\"The cudgel in my nieve did shake,\""; + mes "\"Each bristl'd hair stood like a stake,\""; + mes "\"When wi' an eldritch, stoor 'Quaick, quaick,'\""; + mes "\"Amang the springs,\""; + mes "\"Awa ye squatter'd like a drake,\""; + mes "\"On whistling wings.\""; + callsub S_food_menu; + mes "\"Let warlocks grim an' wither'd hags\""; + mes "\"Tell how wi' you on ragweed nags\""; + mes "\"They skim the muirs an' dizzy crags\""; + mes "\"Wi' wicked speed;\""; + mes "\"And in kirk-yards renew their leagues,\""; + mes "\"Owre howket dead.\""; + callsub S_food_menu; + mes "\"Thence, countra wives wi' toil an' pain\""; + mes "\"May plunge an' plunge the kirn in vain;\""; + mes "\"For oh! the yellow treasure's taen\""; + mes "\"By witchin skill;\""; + mes "\"An' dawtet, twal-pint hawkie's gaen\""; + mes "\"As yell's the bill.\""; + callsub S_food_menu; + mes "\"Thence, mystic knots mak great abuse,\""; + mes "\"On young guidmen, fond, keen, an' croose;\""; + mes "\"When the best wark-lume i' the house,\""; + mes "\"By cantraip wit,\""; + mes "\"Is instant made no worth a louse,\""; + mes "\"Just at the bit.\""; + callsub S_food_menu; + mes "\"When thowes dissolve the snawy hoord,\""; + mes "\"An' float the jinglin icy-boord,\""; + mes "\"Then water-kelpies haunt the foord\""; + mes "\"By your direction,\""; + mes "\"An' nighted trav'lers are allur'd\""; + mes "\"To their destruction.\""; + callsub S_food_menu; + mes "\"And aft your moss-traversing spunkies\""; + mes "\"Decoy the wight that late an drunk is:\""; + mes "\"The bleezin, curst, mischievous monkeys\""; + mes "\"Delude his eyes,\""; + mes "\"Till in some miry slough he sunk is,\""; + mes "\"Ne'er mair to rise.\""; + callsub S_food_menu; + mes "\"When Masons' mystic word an grip\""; + mes "\"In storms an' tempests raise you up,\""; + mes "\"Some cock or cat your rage maun stop,\""; + mes "\"Or, strange to tell!\""; + mes "\"The youngest brither ye wad whip\""; + mes "\"Aff straught to hell!\""; + callsub S_food_menu; + mes "\"Lang syne, in Eden'd bonie yard,\""; + mes "\"When youthfu' lovers first were pair'd,\""; + mes "\"An all the soul of love they shar'd,\""; + mes "\"The raptur'd hour,\""; + mes "\"Sweet on the fragrant flow'ry swaird,\""; + mes "\"In shady bow'r;\""; + callsub S_food_menu; + mes "\"Then you, ye auld snick-drawin dog!\""; + mes "\"Ye cam to Paradise incog,\""; + mes "\"And play'd on man a cursed brogue,\""; + mes "\"(Black be your fa'!)\""; + mes "\"An gied the infant warld a shog,\""; + mes "\"Maist ruin'd a'.\""; + callsub S_food_menu; + mes "\"D'ye mind that day, when in a bizz,\""; + mes "\"Wi' reeket duds an reestet gizz,\""; + mes "\"Ye did present your smoutie phiz\""; + mes "\"Mang better folk,\""; + mes "\"An' sklented on the man of Uz\""; + mes "\"Your spitefu' joke?\""; + callsub S_food_menu; + mes "\"An' how ye gat him i' your thrall,\""; + mes "\"An' brak him out o' house and hal',\""; + mes "\"While scabs and blotches did him gall,\""; + mes "\"Wi' bitter claw,\""; + mes "\"An' lows'd his ill-tongued, wicked scaul,\""; + mes "\"Was warst ava?\""; + callsub S_food_menu; + mes "\"But a' your doings to rehearse,\""; + mes "\"Your wily snares an' fechtin fierce,\""; + mes "\"Sin' that day Michael did you pierce,\""; + mes "\"Down to this time,\""; + mes "\"Wad ding a Lallan tongue, or Erse,\""; + mes "\"In prose or rhyme.\""; + callsub S_food_menu; + mes "\"An' now, Auld Cloots, I ken ye're thinkin,\""; + mes "\"A certain Bardie's rantin, drinkin,\""; + mes "\"Some luckless hour will send him linkin,\""; + mes "\"To your black pit;\""; + mes "\"But faith! he'll turn a corner jinkin,\""; + mes "\"An' cheat you yet.\""; + callsub S_food_menu; + mes "\"But fare you weel, Auld Nickie-ben!\""; + mes "\"O wad ye tak a thought an' men'!\""; + mes "\"Ye aiblins might--I dinna ken--\""; + mes "\"Still hae a stake:\""; + mes "\"I'm wae to think upo' yon den,\""; + mes "\"Ev'n for your sake!\""; + next; + mes "\"Well, that was the poem. What do you think of it?\""; + menu + "It was a bit long and kind of hard to understand, but thanks for reading.", L_Next; + +L_Next: + mes "[Shannon]"; + mes "\"Well, thanks for coming to the supper. Also, thanks for helping me make it through the poem. Here, take this hat. When you wear it, wear it with the class you showed me today. Happy Burns' Supper!\""; + getinventorylist; + if (@inventorylist_count == 100) + goto L_Full_Inv; + if (FLAGS & FLAG_HAS_BOWLER_BURNSDAY) goto L_Get_SteamTopHat; + + getitem "BowlerHat", 1; + FLAGS = FLAGS | FLAG_HAS_BOWLER_BURNSDAY; + close; + +L_Get_SteamTopHat: + getitem "SteamTopHat", 1; + FLAGS = FLAGS | FLAG_HAS_STEAMPUNK_BURNSDAY; + close; + +L_No_Event: + mes "[Shannon]"; + mes "\"I'm a traveling poet and admirer of Robert Burns.\""; + close; + +L_Event_Done: + mes "[Shannon]"; + mes "\"Ah, my dear friend. Good to see you again in this remarkable time of the year.\""; + next; + mes "\"Welcome to Dimond's Cove. We are celebrating Robert Burns with supper and poems. You provide the supper and I'll provide a poem by Robert Burns. He was otherwise known as 'The Bard' and wrote many poems. The music of language is I love most. The language of poetry. While his accent is thick, his poems tell stories that stir the soul. I'll read a poem by him and you can tell me what you think.\""; + next; + mes "\"O thou! whatever title suit thee,--\""; + mes "\"Auld Hornie, Satan, Nick, or Clootie!\""; + mes "\"Wha in yon cavern, grim an' sootie,\""; + mes "\"Clos'd under hatches,\""; + mes "\"Spairges about the brunstane cootie\""; + mes "\"To scaud poor wretches!\""; + next; + mes "\"Hear me, Auld Hangie, for a wee,\""; + mes "\"An' let poor damned bodies be;\""; + mes "\"I'm sure sma' pleasure it can gie,\""; + mes "\"E'en to a deil,\""; + mes "\"To skelp an' scaud poor dogs like me,\""; + mes "\"An' hear us squeel!\""; + next; + mes "\"Great is thy pow'r, an' great thy fame;\""; + mes "\"Far ken'd an' noted is thy name;\""; + mes "\"An' tho' yon lowin heugh's thy hame,\""; + mes "\"Thou travels far;\""; + mes "\"An' faith! thou's neither lag nor lame,\""; + mes "\"Nor blate nor scaur.\""; + next; + mes "\"Whyles, ranging like a roarin lion,\""; + mes "\"For prey a' holes an' corners tryin;\""; + mes "\"Whyles, on the strong-wing'd tempest flyin,\""; + mes "\"Tirlin' the kirks;\""; + mes "\"Whyles, in the human bosom pryin,\""; + mes "\"Unseen thou lurks.\""; + next; + mes "\"I've heard my rev'rend graunie say,\""; + mes "\"In lanely glens ye like to stray;\""; + mes "\"Or whare auld ruin'd castles gray\""; + mes "\"Nod to the moon,\""; + mes "\"Ye fright the nightly wand'rer's way\""; + mes "\"Wi' eldritch croon.\""; + next; + mes "\"When twilight did my graunie summon\""; + mes "\"To say her pray'rs, douce honest woman!\""; + mes "\"Aft yont the dike she's heard you bummin,\""; + mes "\"Wi' eerie drone;\""; + mes "\"Or, rustlin thro' the boortrees comin,\""; + mes "\"Wi' heavy groan.\""; + next; + mes "\"Ae dreary, windy, winter night,\""; + mes "\"The stars shot down wi' sklentin light,\""; + mes "\"Wi' you mysel I gat a fright,\""; + mes "\"Ayont the lough;\""; + mes "\"Ye like a rash-buss stood in sight,\""; + mes "\"Wi' waving sugh.\""; + next; + mes "\"The cudgel in my nieve did shake,\""; + mes "\"Each bristl'd hair stood like a stake,\""; + mes "\"When wi' an eldritch, stoor 'Quaick, quaick,'\""; + mes "\"Amang the springs,\""; + mes "\"Awa ye squatter'd like a drake,\""; + mes "\"On whistling wings.\""; + next; + mes "\"Let warlocks grim an' wither'd hags\""; + mes "\"Tell how wi' you on ragweed nags\""; + mes "\"They skim the muirs an' dizzy crags\""; + mes "\"Wi' wicked speed;\""; + mes "\"And in kirk-yards renew their leagues,\""; + mes "\"Owre howket dead.\""; + next; + mes "\"Thence, countra wives wi' toil an' pain\""; + mes "\"May plunge an' plunge the kirn in vain;\""; + mes "\"For oh! the yellow treasure's taen\""; + mes "\"By witchin skill;\""; + mes "\"An' dawtet, twal-pint hawkie's gaen\""; + mes "\"As yell's the bill.\""; + next; + mes "\"Thence, mystic knots mak great abuse,\""; + mes "\"On young guidmen, fond, keen, an' croose;\""; + mes "\"When the best wark-lume i' the house,\""; + mes "\"By cantraip wit,\""; + mes "\"Is instant made no worth a louse,\""; + mes "\"Just at the bit.\""; + next; + mes "\"When thowes dissolve the snawy hoord,\""; + mes "\"An' float the jinglin icy-boord,\""; + mes "\"Then water-kelpies haunt the foord\""; + mes "\"By your direction,\""; + mes "\"An' nighted trav'lers are allur'd\""; + mes "\"To their destruction.\""; + next; + mes "\"And aft your moss-traversing spunkies\""; + mes "\"Decoy the wight that late an drunk is:\""; + mes "\"The bleezin, curst, mischievous monkeys\""; + mes "\"Delude his eyes,\""; + mes "\"Till in some miry slough he sunk is,\""; + mes "\"Ne'er mair to rise.\""; + next; + mes "\"When Masons' mystic word an grip\""; + mes "\"In storms an' tempests raise you up,\""; + mes "\"Some cock or cat your rage maun stop,\""; + mes "\"Or, strange to tell!\""; + mes "\"The youngest brither ye wad whip\""; + mes "\"Aff straught to hell!\""; + next; + mes "\"Lang syne, in Eden'd bonie yard,\""; + mes "\"When youthfu' lovers first were pair'd,\""; + mes "\"An all the soul of love they shar'd,\""; + mes "\"The raptur'd hour,\""; + mes "\"Sweet on the fragrant flow'ry swaird,\""; + mes "\"In shady bow'r;\""; + next; + mes "\"Then you, ye auld snick-drawin dog!\""; + mes "\"Ye cam to Paradise incog,\""; + mes "\"And play'd on man a cursed brogue,\""; + mes "\"(Black be your fa'!)\""; + mes "\"An gied the infant warld a shog,\""; + mes "\"Maist ruin'd a'.\""; + next; + mes "\"D'ye mind that day, when in a bizz,\""; + mes "\"Wi' reeket duds an reestet gizz,\""; + mes "\"Ye did present your smoutie phiz\""; + mes "\"Mang better folk,\""; + mes "\"An' sklented on the man of Uz\""; + mes "\"Your spitefu' joke?\""; + next; + mes "\"An' how ye gat him i' your thrall,\""; + mes "\"An' brak him out o' house and hal',\""; + mes "\"While scabs and blotches did him gall,\""; + mes "\"Wi' bitter claw,\""; + mes "\"An' lows'd his ill-tongued, wicked scaul,\""; + mes "\"Was warst ava?\""; + next; + mes "\"But a' your doings to rehearse,\""; + mes "\"Your wily snares an' fechtin fierce,\""; + mes "\"Sin' that day Michael did you pierce,\""; + mes "\"Down to this time,\""; + mes "\"Wad ding a Lallan tongue, or Erse,\""; + mes "\"In prose or rhyme.\""; + next; + mes "\"An' now, Auld Cloots, I ken ye're thinkin,\""; + mes "\"A certain Bardie's rantin, drinkin,\""; + mes "\"Some luckless hour will send him linkin,\""; + mes "\"To your black pit;\""; + mes "\"But faith! he'll turn a corner jinkin,\""; + mes "\"An' cheat you yet.\""; + next; + mes "\"But fare you weel, Auld Nickie-ben!\""; + mes "\"O wad ye tak a thought an' men'!\""; + mes "\"Ye aiblins might--I dinna ken--\""; + mes "\"Still hae a stake:\""; + mes "\"I'm wae to think upo' yon den,\""; + mes "\"Ev'n for your sake!\""; + next; + mes "\"Well, that was the poem. What do you think of it?\""; + menu + "It was a bit long and kind of hard to understand, but thanks for reading.", L_Close; + +L_Full_Inv: + mes "[Shannon]"; + mes "\"On second thought, your possessions seems to be a burden to you.\""; + next; + mes "\"Maybe you should turn away from the mundane world and get rid of this burden to change your view to the beauty of poetry.\""; + close; + +S_food_menu: + setarray @choice_idx, 0,0,0,0,0,0; + setarray @choice$, "","","","","",""; + + @C_steak = 676; + @C_chickenleg = 562; + @C_redapple = 535; + @C_greenapple = 719; + @C_beer = 539; + + // counter of available answers + @choices_nr = 0; + + if (countitem("Steak") == 0) + goto L_Nosteaks; + @choice_idx[@choices_nr] = @C_steak; + @choice$[@choices_nr] = "Offer him a steak."; + @choices_nr = @choices_nr + 1; + goto L_Nosteaks; + +L_Nosteaks: + if (countitem("ChickenLeg") == 0) + goto L_Nochickenleg; + @choice_idx[@choices_nr] = @C_chickenleg; + @choice$[@choices_nr] = "Offer him a chicken leg."; + @choices_nr = @choices_nr + 1; + goto L_Nochickenleg; + +L_Nochickenleg: + if (countitem("RedApple") == 0) + goto L_Noredapple; + @choice_idx[@choices_nr] = @C_redapple; + @choice$[@choices_nr] = "Offer him a red apple."; + @choices_nr = @choices_nr + 1; + goto L_Noredapple; + +L_Noredapple: + if (countitem("GreenApple") == 0) + goto L_Nogreenapple; + @choice_idx[@choices_nr] = @C_greenapple; + @choice$[@choices_nr] = "Offer him a green apple."; + @choices_nr = @choices_nr + 1; + goto L_Nogreenapple; + +L_Nogreenapple: + if (countitem("Beer") == 0) + goto L_NoBeer; + @choice_idx[@choices_nr] = @C_beer; + @choice$[@choices_nr] = "Offer him a beer."; + @choices_nr = @choices_nr + 1; + goto L_NoBeer; + +L_NoBeer: + @choice_idx[@choices_nr] = @C_nevermind; + @choice$[@choices_nr] = "Leave"; + + menu + @choice$[0],L_MenuItems, + @choice$[1],L_MenuItems, + @choice$[2],L_MenuItems, + @choice$[3],L_MenuItems, + @choice$[4],L_MenuItems, + @choice$[5],L_MenuItems, + @choice$[6],L_MenuItems, + @choice$[7],L_MenuItems, + @choice$[8],L_MenuItems, + @choice$[9],L_MenuItems, + @choice$[10],L_MenuItems; + +L_MenuItems: + @choose = @menu - 1; + if (@choice_idx[@choose] == 0) + goto L_Close; + if ((countitem("Steak") != 0) && (@choice_idx[@choose] == @C_steak)) + goto L_RmItem; + if ((countitem("ChickenLeg") != 0) && (@choice_idx[@choose] == @C_chickenleg)) + goto L_RmItem; + if ((countitem("RedApple") != 0) && (@choice_idx[@choose] == @C_redapple)) + goto L_RmItem; + if ((countitem("GreenApple") != 0) && (@choice_idx[@choose] == @C_greenapple)) + goto L_RmItem; + if ((countitem("Beer") != 0) && (@choice_idx[@choose] == @C_beer)) + goto L_RmItem; + + // fallthrough only when player remove the items while in menu, trying to cheat + // the quest will need to be started again + mes "\"What you are offering, you do not have. Do not try to betray me. Come back when you have better morals.\""; + close; + +L_RmItem: + delitem @choice_idx[@choose], 1; + return; +*/ +L_Close: + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + + +017-3,80,32,0 script Dimonds Cove Story NPC_NO_SPRITE,{ + mesn l("The Story of Dimonds Cove"); + next; + mes l("One day Dimond D. Stone dreamed of her own restaurant."); + mes l("Dimond sold her food alone in this spot for some time,"); + mes l("longing for the day she would have a building."); + mes l("She met an adventurer named Merlin outside of the"); + mes l("desert mines one day."); + next; + mes l("They became friends and Dimond told Merlin about her idea."); + mes l("Merlin was a accomplished carpenter and wanted to make a new"); + mes l("place in the world. He told Dimond that he would make her"); + mes l("restaurant for her. He gathered the tools and after much"); + mes l("hard work, Dimonds Cove was built."); + next; + mes l("At January 2008 - Construction of Dimonds Cove."); + close; + +OnInit: + .sex=G_OTHER; + .distance=3; + end; +} + +/* +017-3,71,30,0 script Inn NPC400,{ + mes "Welcome to the Dimonds Cove Inn"; + mes "Rooms are 200 gp a night."; + mes "Plese check in with Basil."; + close; +} + +017-3,24,27,0 shop Bartender NPC112,539:87,567:250,568:250 + +017-3,32,34,0 shop Waitress NPC139,519:50,533:55,534:45,562:125,676:100 + +017-3,85,41,0 shop Blacksmith NPC146,545:5000,529:2,603:1000 + +017-3,65,41,0 shop General Store#dimond NPC137,586:500,524:800,544:2000,632:500,528:500,735:500 + +017-3,75,68,0 script Basil NPC_TOMBOY,{ + showavatar NPC_TOMBOY; + if (!is_night()) + goto L_Day; + mesn; + mesq l("For only 100 GP, you can sleep on the beds of the Land Of Fire."); + if (Zeny < 100) + close; + next; + mesn; + mesq l("The nanorob-- err, the *magic* will seal all your wounds. You trust Basil, right?"); + next; + select + rif(Zeny > 100, l("Yes, please, book me a bed.")), + l("Uh... I *just* remembered I have something else to do!"); + if (@menu == 2) + close; + Zeny=Zeny-100; + recovery(getcharid(3)); + if (rand2(1,3) == 1) + sc_start SC_POISON, 60000, 0, 10000; + else + sc_start SC_ATTHASTE_POTION1, 60000, 5; + + close; + +L_Day: + mesn; + mesq l("You can't sleep here during daytime!"); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} +*/ + diff --git a/npc/017-3/doctor.txt b/npc/017-3/doctor.txt new file mode 100644 index 0000000..42ed43b --- /dev/null +++ b/npc/017-3/doctor.txt @@ -0,0 +1,377 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Part from THE EPISODE quest +// Reference: +// http://forums.landoffire.org/viewtopic.php?f=7&t=1320&sid=80d2c735b55ccb06a39955a8fbca3913 + +017-3,75,68,0 script The Doctor NPC_LOF_DOCTOR,{ + showavatar NPC_LOF_DOCTOR; + .@q=getq(LoFQuest_EPISODE); + if (BaseLevel < 30) goto L_Weak; + if (.@q == 13) goto L_Poison; + if (.@q == 14) goto L_TimeFlask; + if (.@q == 15) mesc l("You don't look so well, have you already used the %s to defeat the Soul Eater?", getitemlink(TimeFlask)), 1; + if (.@q == 1) goto L_Check; + if (.@q == 2 && BaseLevel >= 40) goto L_Miler; + if (.@q >= 2) goto L_Tea; + mesn; + mesq l("Quite interesting, quite interesting indeed."); + menu + l("Um, might I ask, what is so interesting?"), L_Intro, + l("Yes, uhh, very interesting indeed. Haha. I better leave..."), -; + mes ""; + mesc l("@@ stares you as you slowly get away from him. Odd person.", .name$); + close; + +L_Intro: + mes ""; + mesn; + mesq l("Well, you are. You're quite interesting. I've been watching you for some time now, as you've been helping so many people: you're quite a master at what you do, you know."); + menu + l("Well, thanks."), L_IntroContinue, + l("Ok then... Uh... Please excuse me....."), -; + mes ""; + mesc l("@@ stares you as you slowly get away from him. Odd person.", .name$); + close; + +L_IntroContinue: + mes ""; + mesn; + mesq l("I don't suppose you have some herbs and a few bottles of potion with you, do you?"); + menu + l("'Some herbs and potion'? Could you be more specific?"), L_IntroSpecify, + l("Certainly not. Uhh, please excuse me."), -; + mes ""; + mesc l("@@ stares you as you slowly get away from him. Odd person.", .name$); + close; + +L_IntroSpecify: + mes ""; + mesn; + mesq l("Ah, sorry, of course. I need quite a few herbs, look:"); + mesc l("@@/150 @@", countitem(MauveHerb), getitemlink(MauveHerb)); + mesc l("@@/150 @@", countitem(CobaltHerb), getitemlink(CobaltHerb)); + mesc l("@@/150 @@", countitem(GambogeHerb), getitemlink(GambogeHerb)); + mesc l("@@/100 @@", countitem(AlizarinHerb), getitemlink(AlizarinHerb)); + mesc l("@@/50 @@", countitem(ShadowHerb), getitemlink(ShadowHerb)); + mesc l("@@/10 @@", countitem(HastePotion), getitemlink(HastePotion)); + menu + l("That shouldn't been too hard, but do I get something in return?"), L_IntroReward, + l("O.o \"That's a lot. Maybe another day.\""), -; + mes ""; + mesc l("@@ stares you as you slowly get away from him. Odd person.", .name$); + close; + +L_IntroReward: + mes ""; + mesn; + mesq l("I suppose, what would you like?"); + next; + mesn; + mesq l("Er, nevermind, I've thought of something to give you. You can go off now and get what I need."); + setq LoFQuest_EPISODE, 1; + close; + +L_Check: + mesn; + mesq l("Did you brought what I asked for?"); + mesc l("@@/150 @@", countitem(MauveHerb), getitemlink(MauveHerb)); + mesc l("@@/150 @@", countitem(CobaltHerb), getitemlink(CobaltHerb)); + mesc l("@@/150 @@", countitem(GambogeHerb), getitemlink(GambogeHerb)); + mesc l("@@/100 @@", countitem(AlizarinHerb), getitemlink(AlizarinHerb)); + mesc l("@@/50 @@", countitem(ShadowHerb), getitemlink(ShadowHerb)); + mesc l("@@/10 @@", countitem(HastePotion), getitemlink(HastePotion)); + next; + if (askyesno() != ASK_YES) + close; + inventoryplace HerbalTea, 5; + if ( + countitem(MauveHerb) < 150 || + countitem(CobaltHerb) < 150 || + countitem(GambogeHerb) < 150 || + countitem(AlizarinHerb) < 100 || + countitem(ShadowHerb) < 50 || + countitem(HastePotion) < 10) + goto L_Missing; + delitem MauveHerb, 150; + delitem CobaltHerb, 150; + delitem GambogeHerb, 150; + delitem AlizarinHerb, 100; + delitem ShadowHerb, 50; + delitem HastePotion, 10; + getexp 7995, 0; + setq LoFQuest_EPISODE, 2; + getitem HerbalTea, 5; + mesn; + mesq l("Mmm, it's been so long since I have had herbal tea. You have my gratitude."); + next; + mesn strcharinfo(0); + mesq l("Seriously? What sort of reward is that?"); + next; + mesn; + mesq l("Well, I suppose you can have some of my tea."); + close; + +L_Tea: + mesn; + mesq l("If you want, you can bring me some more of those herbs and potions."); + mesc l("@@/30 @@", countitem(MauveHerb), getitemlink(MauveHerb)); + mesc l("@@/30 @@", countitem(CobaltHerb), getitemlink(CobaltHerb)); + mesc l("@@/30 @@", countitem(GambogeHerb), getitemlink(GambogeHerb)); + mesc l("@@/20 @@", countitem(AlizarinHerb), getitemlink(AlizarinHerb)); + mesc l("@@/10 @@", countitem(ShadowHerb), getitemlink(ShadowHerb)); + mesc l("@@/2 @@", countitem(HastePotion), getitemlink(HastePotion)); + menu + rif(getq(LoFQuest_EPISODE) == 16, l("Actually, about the Soul Eater...")), L_Cont, + l("Alright, I have them here!"), L_Check2, + l("No thanks, see ya!"), -; + close; + +L_Check2: + inventoryplace HerbalTea, 1; + if ( + countitem(MauveHerb) < 30 || + countitem(CobaltHerb) < 30 || + countitem(GambogeHerb) < 30 || + countitem(AlizarinHerb) < 20 || + countitem(ShadowHerb) < 10 || + countitem(HastePotion) < 2) + goto L_Missing; + delitem MauveHerb, 30; + delitem CobaltHerb, 30; + delitem GambogeHerb, 30; + delitem AlizarinHerb, 20; + delitem ShadowHerb, 10; + delitem HastePotion, 2; + getexp 122, 12; + getitem HerbalTea, 1; + mesn; + mesq l("Thanks, enjoy your tea! I will certainly enjoy mine!"); + close; + +L_Weak: + mesn; + mesq l("Hmm, it's very interesting, very ... (mumbling)."); + close; + +L_Missing: + mesn; + mesq l("Sorry, you do not have enough ingredients. You'd better search thoroughly."); + close; + +/////// Second Act +L_Miler: + mesn; + mesq l("Thank you for helping me make my tea. I hope the potions have been helpful..."); + next; + mesn; + mesq l("That reminds me. I have a friend in Nivalis named Miler who gave me some hints on the recipe. Would you take him a sample of what I gave you?"); + mesq l("If you've used all the ones I've given, you can always bring me more ingredients."); + menu + "I'll go right away.", -, + "Ah, I suppose I need to gather more ingredients first...", -; + setq LoFQuest_EPISODE, 3; + close; + +/////// Final Act +L_Poison: + mesn; + mesq l("%s, how in the world you got this status ailment?!", strcharinfo(0)); + next; + select + l("I don't know."), + l("Henry gave something to drink."); + mes ""; + mesn; + mesq l("Listen, this is not a disease. No remedy can cure you, and it cannot be inflicted by consumables."); + next; + mesn; + mesq l("Have you been fiddling with time? Anyway, this is not a disease, but a ##Bcomplex curse##b."); + next; + mesn; + mesq l("This curse reclaims your soul to the Soul Eater. Except, she perished on the Great Fire."); + next; + mesn; + mesq l("Meaning you must warp shortly before the Great Fire, defeat her, and be back before the Great Fire happen."); + next; + mesn; + mesq l("For now, eat this. It'll improve your condition... for now. I don't know where you'll find a time travelling device, but if you find one, come to me. I'll help you."); + setq LoFQuest_EPISODE, 14; + sc_end SC_POISON; + close; + +L_TimeFlask: + mesn; + mesq l("If you don't defeat the Soul Eater before the Great Fire happens, which for the record, has already happened, your soul will slowly be reclaimed by her until your existence ceases to serve as fuel to bring back one of the greatest horrors of story."); + next; + if (!countitem(TimeFlask)) { + mesn; + mesq l("You better find a way to travel back in time soon!"); + close; + } + if (BaseLevel < 70) { + mesn; + mesq l("It'll be too dangerous if you're not at least level %d. But I'm sure you can grind the required level in no time; Don't let your body down! Exercise!", 70); + close; + } else if (BaseLevel < 80) { + mesn; + mesq l("Mhm, you're still weak, I would prefer you to be level %d+ before doing this, but... Whatever, you might even survive.", 80); + next; + } + mesn; + mesq l("This %s will do. I mean, it won't be a comfortable trip, but you are in an emergency situation.", getitemlink(TimeFlask)); + next; + mesn; + mesq l("Listen, I'll use a trick which I learned in, uh, a past life of mine, so if you die, reality resets your being."); + next; + mesn; + mesq l("This means that if you die while in the past, you'll be brought back here as if you never traveled in time."); + next; + mesn; + mesq l("Sounds awesome, but this will also reset a bunch of your progress... And with something as lame as a %s, you'll need quite a while to warp again. Uh, if you can, bring friends, the Soul Eater is... not to be trifled with.", getitemlink(TimeFlask)); + next; + mesn; + mesq l("I'll configure your flask to a few hours before the Great Fire breaks out. You're looking for \"The Queen's party\" hideout. Do you need a quick history lesson?"); + if (askyesno() == ASK_YES) { + mes ""; + mesn; + mesq l("Very well. The Platinum Red Queen died in her sleep and the Soul Eater took her role. But the Soul Eater was a tyrant and soon enough, \"she\" was murdered. A Council has taken over Tulimshar on the meanwhile and is doing intense research."); + next; + mesn; + mesq l("This research caused a lot of neglect, so expect poverty and people unwilling to talk. Benjamin, head of the council, is doing a \"breaktrhough\" in the most literal sense possible (breaking through the whole town finances instead of revolutionizing the world)."); + next; + mesn; + mesq l("The situation is grim, and some want the return of the Platinum Red Queen. The \"Soul Eater\" is back, pretending to be her ghost, and the Black Razor believed on it. They made \"The Queen's Party\" and want to reinstate the queen."); + next; + mesn; + mesq l("They haven't found Mana Stones yet, so no magic, but alchemy is strong. I don't know what caused the Great Fire, and the whole event is time-locked. You can ask Falkrun for more about the Great Fire. That's what you need to know for now."); + next; + } + mes ""; + mesn; + mesq l("Good luck, %s. You'll need it.", strcharinfo(0)); + setq LoFQuest_EPISODE, 15; + close; + +/////// Epilogue +L_Cont: + mes ""; + mesn; + mesc l("The doctor quickly analyzes you with some tool. Which looks like a screwdriver? Probably just a generic scanner, though."); + mesq l("Gimme a second... Yes, you seem to be fine now, very well, congratulations!"); + next; + inventoryplace SaviorBlueprint, 1; + select + l("Why, thank you!"), + l("It was a tough fight."), + l("I met Elli."); + mes ""; + if (@menu == 1) close; + if (@menu == 2) { + mesn; + mesq l("I'm sure it was, now go rest a bit, you deserve it!"); + close; + } + mesc l("The doctor raises an eyebrow at you."); + next; + mesn; + mesq l("Figures she would be involved. What did she say?"); + do + { + next; + select + l("Nothing important."), + l("That she would stop the Soul Eater in past and future."), + l("That she was one of the originals."), + l("To defeat the Moubootaur."); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("That's because you weren't paying attention!"); + next; + mesn; + mesq l("Listen, kid, one does not simply ignore Elli! She is too scary to be ignored!"); + break; + case 2: + mesn; + mesq l("Of course. I bet Elli is the one who cursed you, who started the Great Fire and who put the time lock in place."); + next; + mesn; + mesq l("She wanted the Soul Eater defeated. Destroying the whole world never stopped her before."); + next; + mesn; + mesq l("She is a scary woman and you would do well to remember this."); + break; + case 3: + mesn; + mesq l("Ah, the originals. Had the displeasure to met a few of them in the past and in the future with my time machine."); + next; + mesn; + mesq l("There are only four which you could want to know about."); + next; + mesn; + mesq l("First, %s is the Guardian of Law and Order. He ensures all rules are followed, and if anything breaks them, he erases their existence.", b("Mr. Saves")); + next; + mesn; + mesq l("For example, when an item is dropped to the ground, it rots away and disappear. It cannot be recovered. If an item were to break such rule, Mr. Saves would intervene."); + next; + mesn; + mesq l("He is by far the most powerful, and has a few chosen ones to whom he granted what we call \"SQL Magic\". Everyone envies power, so there's the imitation of that, \"GM Magic\", but unrelated to Mr. Saves."); + next; + mesn; + mesq l("Next we have %s. It guards the world itself and grants all kinds of magic.", b(l("The Mana Source"))); + next; + mesn; + mesq l("Then we have %s. You met her. She guards all Talpan beings, such as Humans, Elves, Reidys, Orcs and whatever. She is an evil woman. She grants nothing to her followers.", b(l("Elli"))); + next; + mesn; + mesq l("I miss Ms. De'Kagen, she was very benevolent but from another world. Why does this one only have annoying pricks for Originals. Wait, I'm getting sidetracked."); + next; + mesn; + mesq l("The last one is %s. You will meet him in the future, so no spoilers. Time traveler word.", b(l("The Moubootaur"))); + break; + } + } while (@menu != 4); + mesn; + mesq l("Sorry, but no spoilers. You'll met the Moubootaur soon enough."); + next; + mesn; + mesq l("He guards what you call \"monsters\", but he favor Mouboos. He is possibly the oldest of the Originals."); + next; + mesn; + mesq l("He dislikes Talpans like you. The Mana Source chained him in ancient times, though, so the Talpans could flourish."); + next; + mesn; + mesq l("After the Great Fire... you don't find our world's Elli ever again."); + // The Great Fire is her final act. Elli is declared dead shortly after. The causes of her death are unknown but certainly violent. She likely knew her time was coming, and wanted both to get rid of the Soul Eater before this, as to ensure no one would miss her, adding her along the list of those claimed by the fire. + next; + mesn; + mesq l("Keep following the path. Take this and... good luck."); + getexp 300000, 0; + getitem SaviorBlueprint, 1; + setq LoFQuest_EPISODE, 17; + + // Less relevant + EPISODE_WINNER=gettimetick(2); + if ($EPISODE_WINNER$ == "") { + $EPISODE_WINNER$=strcharinfo(0); + channelmes("#world", $EPISODE_WINNER$+" is the first player to finish The Episode of Ozthokk!! GG, dude! %%N"); + announce "All hail ##B"+$EPISODE_WINNER$+"##b, first to complete ##3The Episode of Ozthokk!", bc_all|bc_npc; + getexp 0, 2000; + getitem PrismGift, 1; + mesc l("CONGRATULATIONS! You are the first player to finish The Episode of Ozthokk!!"), 2; + mesc l("You just gained a Prism Gift, and 2000 Job Exp for your bravery!"), 2; + next; + } + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; + +} diff --git a/npc/017-3/doug.txt b/npc/017-3/doug.txt new file mode 100644 index 0000000..a6ad8d8 --- /dev/null +++ b/npc/017-3/doug.txt @@ -0,0 +1,64 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Saulc +// Description: +// Weekly Quest + +017-3,68,87,0 script Doug NPC_PLAYER,{ + if (BaseLevel < 25) + goto L_Weak; + .@q2=getq2(LoFQuest_Doug); + if (.@q2 < santime()) { + mesn; + mesq l("This room is too dark. I want to brighten it up."); + next; + .@price=(getiteminfo(CaveSnakeLamp, ITEMINFO_SELLPRICE)*3)*8; + mesn; + mesq l("I am willing to pay @@ GP for 10 @@ you bring me!", .@price, getitemlink(CaveSnakeLamp)); + next; + select + rif(countitem(CaveSnakeLamp) >= 10, l("Here they are!")), + l("Not now..."); + mes ""; + if (@menu == 1) { + delitem CaveSnakeLamp, 10; + Zeny=Zeny+.@price; //864 + getexp (.@price*rand(100,200)/100), 10; // 864 - 1728 + setq2 LoFQuest_Doug, santime()+(60*60*24*7); + mesn; + mesq l("Many, many thanks!"); + next; + } + mesn; + mesq l("Too bad these lamps wear off after a while... I am making stocks of them now!"); + close; + } else { + mesn; + mesq l("Thanks for the help!"); + close; + } + +L_Weak: + mesn; + mesq l("Heya dude! Don't you think this room is too dark?"); + next; + mesn; + mesq l("I am trying to invent the light bulb, but my name is not Thomas..."); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, JeansShorts); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/017-3/gambler.txt b/npc/017-3/gambler.txt new file mode 100644 index 0000000..7d5c848 --- /dev/null +++ b/npc/017-3/gambler.txt @@ -0,0 +1,165 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Gambler: Can you remember the sequence? + +017-3,23,38,0 script Gambler#017-3 NPC_PLAYER,{ + function colorname { + switch (getarg(0)) { + case 1: + return "Green"; break; + case 2: + return "Blue"; break; + case 3: + return "Red"; break; + case 4: + return "Yellow"; break; + case 5: + return "Exit"; break; + default: + return l("ERROR: %d", getarg(0)); + } + } + + goto L_Menu; + +L_Menu: + showavatar NPC_FLOPPED_NOBLEMAN; + mesn; + mesc l("Gambling is for the weak, I offer you a true game!"); + mesc l("You need %d %s. I'll start showing you sequences of colors.", .price, getitemlink(CasinoCoins)); + mesc l("The farther you go on the sequence, the better the payout!"); + next; + menu + rif(countitem(CasinoCoins) >= .price, l("Let's play!")), L_Start, + l("Information"), L_Info, + l("Leave"), -; + close; + +L_Info: + mes ""; + mesc l("Rules:"); + mesc l("A color sequence will be displayed on the avatar frame."); + mesc l("You must then repeat the sequence at the board which will show."); + next; + mesc l("Prizes:"); + mesc l("You'll get %d GP every time you finish the sequence.", .prize); + next; + mesc l("Winning Strike Prizes:"); + mesc l("Every %d sequences, you'll get a %s!", 10, getitemlink(StrangeCoin)); + mesc l("If you get %d sequence, you'll get a %s!", 30, getitemlink(BronzeGift)); + mesc l("If you get %d sequence, you'll get a %s!", 50, getitemlink(SilverGift)); + //mesc l("If you get %d sequence, you'll get a %s!", 50, getitemlink(GoldenGift)); + next; + goto L_Menu; + + +L_Start: + showavatar AVATAR_SEQBOARD; + mesc l("Pay attention to the sequence!"); + next; + delitem CasinoCoins, .price; + deletearray(@sequence); + @streak=0; + +L_Sequence: + // Configure + setnpcdialogtitle l("Memorize the sequence!"); + array_push(@sequence, 1+rand2(4)); + sleep2(1000); + + // Display + freeloop(true); + for (.@i=0;.@i < getarraysize(@sequence);.@i++) { + showavatar 1200+@sequence[.@i]; + sleep2(1200-(@streak*20)); + } + freeloop(false); + + // Request + setnpcdialogtitle l("What was the sequence?"); + showavatar AVATAR_SEQBOARD; + sleep2(500); + + for (.@i=0;.@i < getarraysize(@sequence);.@i++) { + setskin "seqboard"; + select + l("Green"), + l("Blue"), + l("Red"), + l("Yellow"), + l("Exit"); + .@ans=@menu; + setskin ""; + mes ""; + mes l("%s", colorname(.@ans)); + //next; + setnpcdialogtitle strnpcinfo(1); + + // Exit + if (.@ans == 5) + goto L_Close; + + // Wrong reply + if (.@ans != @sequence[.@i]) + goto L_Wrong; + // Correct! + } + mes ""; + showavatar AVATAR_SEQBOARD_WELL; + + // Seems like everything is/was correct + mesn; + mesq l("Congratulations! Everything was correct!"); + Zeny+=.prize; + @streak+=1; + + // Winning Streak + if (@streak % 10 == 0) + getitem StrangeCoin, 1; + if (@streak == 30) + getitem BronzeGift, 1; + if (@streak == 50) + getitem SilverGift, 1; + mesc l("Your current win streak is @@!", @streak); + next; + // Game over + if (@streak == 50) + goto L_Close; + // Otherwise, go ahead + mesn; + mesc l("Continue?"), 1; + next; + if (askyesno() == ASK_YES) + goto L_Sequence; + goto L_Close; + +L_Wrong: + showavatar AVATAR_SEQBOARD_FAIL; + mesn; + mesq l("Oh no... That is wrong! %%3"); + next; + mesn; + mesq l("Better luck next time!"); + close; + +L_Close: + mesn; + mesq l("Thanks for playing!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TopHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + + .sex = G_MALE; + .distance = 4; + .price = 5; + .prize = 50; + npcsit; + end; +} + diff --git a/npc/017-3/loratay.txt b/npc/017-3/loratay.txt new file mode 100644 index 0000000..410f06e --- /dev/null +++ b/npc/017-3/loratay.txt @@ -0,0 +1,1221 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// TODO - Makes the Golden Warlord Plate into the Savior Plate. +// Not part from the EPISODE quest. +// Requires Titanium, Iridium, and Silk +// Requires some money and level too (Savior Plate is level 109 item iirc?) + +017-3,85,85,0 script Lora Tay NPC_GLASS_OLD_LADY,{ + showavatar NPC_GLASS_OLD_LADY; + // bugfix, please move to select() + if ($EVENT$ == "Valentine" && getq(SQuest_Valentine) != gettime(GETTIME_YEAR)) goto L_Valentine; + + mesn; + mesq l("Don't interrupt me, I'm busy!"); + next; + mesn; + mesq l("In a few weeks, I will finish the @@ plans, and there will be no doubts, that @@ is not the best weaver in the world!", getitemlink(SaviorArmor), .name$); + close; + +L_Valentine: + mesn; + mesq l("It's valentine day, and I got my hands on a @@ model!", getitemlink(ValentineDress, RedDye)); + next; + // Standard date: 12~15 feb + // Std price range: 288 ~ 383 points + .@todayprice=(gettime(GETTIME_DAYOFMONTH)*24)+gettime(GETTIME_HOUR); + mesn; + mesq l("You currently have @@ event points, but for ONLY @@ points I can make a dress for you!", #VALENTINE_POINTS, .@todayprice); + mesc l("The price raises hourly, don't leave to make it on the last day!"); + if (#VALENTINE_POINTS < .@todayprice) + close; + next; + mesn; + mesq l("Do you want to trade your points? The demand keep raising, and so does the price!"); + mesc l("Note: The points will NOT be consumed on 2020."), 3; + if (askyesno() == ASK_NO) + close; + + setq SQuest_Valentine, gettime(GETTIME_YEAR); + if (gettime(GETTIME_YEAR) != 2020) + #VALENTINE_POINTS-=.@todayprice; + getitem2(ValentineDress, 1, 1, 0, 0, any(RedDye, RedDye, DarkRedDye, RedDye), 0,0,0); // 25% chance to get an ugly dress + getexp BaseLevel*110, JobLevel*11; + mesn; + mesq l("There you go, your Valentine Dress!"); + mesq lg("Good luck with your boyfriend!", "Good luck with your girlfriend!"); + next; + + mesn strcharinfo(0); + if (getpartnerid()) { + mesc lg("I'm already married, though..."); + } else { + mesc lg("Thanks... I guess..."); + } + close; + + /* + set @ROBE_COCOONS_NR, 150; // must be multiple of ten + @ROBE_ZENY = 10000; + set @ROBE_SHEETS_NR, 6; // number of silk sheets required for the silk robe. Must be less than 10. + set @ROBE_SHEETS_ZENY, 6000; // price of the silk robe when bringing silk sheets. + set @Robe_with_sheets, 0; // will be set to 1 if silk robe is made with sheets + @FINEDRESS_COCOONS = 180; + @SORCERER_ROBE_SEW_ZENY = 25000; + @SORCERER_ROBE_NUM_COTTON_CLOTH = 2; + setarray @item_colors$, "red", "green", "dark blue", "yellow", "light blue", "pink", "black", "orange", "purple", "dark green", "white"; + + if (@loratay_asking_robe == 1) goto L_ExamineSorcererRobe; + if (QUEST_WG_state == 10) goto L_agostine_menu; + if (QUEST_WG_state == 11 || QUEST_WG_state == 12) goto L_regular_intro; + if (QUEST_WG_state == 13) goto L_proposal_intro; + if (QUEST_WG_state == 14) goto L_proposal_show_materials; + if (QUEST_WG_state == 15) goto L_proposal_search_items; + if (QUEST_WG_state == 16) goto L_proposal_retrieve_design; + if (QUEST_WG_state == 17) goto L_proposal_bringing_design; + if (QUEST_WG_state == 18) goto L_proposal_wait_dress_finished; + if (QUEST_WG_state == 19) goto L_proposal_dress_finished; + if (QUEST_WG_state >= 20 && QUEST_WG_state < 23) goto L_proposal_deliver_dress; + if (QUEST_WG_state >= 23) goto L_regular_intro; + + @t = rand(3); + if (@t == 0) goto L_Intro_0; + if (@t == 1) goto L_Intro_1; + + mes "You see a middle-aged woman furiously sewing away at what appears to be a very expensive dress. Her fingers are moving so nimbly that you find it near-impossible to follow the needle."; + goto L_Intro_over; + +L_Intro_0: + mes "You notice a middle-aged woman carefully drawing lines on a large sheet of cotton."; + goto L_Intro_over; + +L_Intro_1: + mes "You see a middle-aged woman cautiously but surprisingly swiftly cutting out shapes from a huge cotton sheet. The scissors glide along the cloth as if it were butter, never leaving the premarked lines."; + goto L_Intro_over; + +L_Intro_over: + next; + mes "She suddenly stops and looks up."; + next; + mes "\"Now who are you, and what are you doing here?\""; + mes "She squints, pushes her thick spectacles up the bridge of her nose and eyes you suspiciously."; + next; + + @agostine_msg0$ = ""; + @agostine_msg1$ = ""; + + if (QUEST_WG_state >= 1) + @agostine_msg0$ = "Do you know Agostine?"; + if ((QUEST_WG_state >= 9) && (countitem("FurBoots"))) + @agostine_msg1$ = "Can you make fur boots, like Agostine?"; + + menu + "Hello! My name is " + strcharinfo(0) + ".", L_hello_0, + "Hello; are you a tailor?", L_hello_1, + "Can you make something for me?", L_hello_2, + @agostine_msg0$, L_agostine_0, + @agostine_msg1$, L_agostine_1; + +L_hello_0: + mes "She raises an eyebrow."; + mes "\"That,\", she notes dryly, \"is hardly my fault. And now I would greatly appreciate it if you were to get out of here and leave me to my work!\""; + close; + +L_hello_1: + mes "She frowns."; + mes "\"No, I am an accupuncturist, I just happen to practice on cloth! Get out of here with your silly questions!\""; + close; + +L_hello_2: + mes "She glares at you indignantly."; + mes "\"Do you even know whom you are talking to? I am Lora Tay, greatest of the seven seamstresses of the South!\""; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"I do not work for petty peasants or arrogant adventurers. Get out of my sight!\""; + mes "She waves you away and returns to her work."; + close; + +L_agostine_0: + if (QUEST_WG_state < 1) + close; + mes "She shoots an incensed glare at you."; + mes "\"Ah, Agostine! The 'legendary tailor'! Now who has not heard of him, been inspired by him! Such charisma! Such popularity! Such unprecedented lack of talent!\""; + mes "You notice that she prononuces the 't's as if she were trying to slap someone."; + next; + mes "\"Please spare me your tales of this fool. He couldn't mend a sock without getting himself trapped inside!\""; + mes "She frowns and turns back to her work, clearly not interested in discussing the matter further."; + close; + +L_agostine_1: + if ((QUEST_WG_state < 9) || (countitem (655) == 0)) + close; + mes "[Lora Tay the Seamstress]"; + mes "She glares at your boots, her lips forming barely more than a thin line. Deep furrows form above her brows."; + mes "\"I can't believe it! He copied Illana's design but used fluffy fur in place of ice wolvern fur-- Fluffy fur! Of all things! Who would wear such an atrocity of fashion! Everyone knows that you need ice-white, not pale-white, to go with such leather!\""; + next; + mes "[Lora Tay the Seamstress]"; + mes "She looks at them more closely."; + mes "\"This insolent upstart! Even worse, he copied my very own double-backward cross-stitch pattern... I can't believe it!\""; + next; + mes "[Lora Tay the Seamstress]"; + mes "Angry red spots begin to form on her cheeks."; + mes "\"Get those... things out of my sight before I forget myself!\""; + if (QUEST_WG_state == 9) + QUEST_WG_state = 10; + close; + +L_agostine_menu: + mes "[Lora Tay the Seamstress]"; + mes "The seamstress looks up at you from her work."; + mes "\"I do hope that you have not had any more dealings with this... this lumberjack of a tailor?\""; + next; + if (countitem("WinterGloves") > 0) + menu + "You mean Agostine?", L_agostine_3, + "Actually, he also made me gloves...", L_agostine_2, + "Why don't you like him?", L_agostine_4, + "No, none, really.", L_agostine_5; + menu + "You mean Agostine?", L_agostine_3, + "Why don't you like him?", L_agostine_4, + "No, none, really.", L_agostine_5; + +L_agostine_2: + if ((QUEST_WG_state < 9) || (countitem("WinterGloves") == 0)) + close; + mes "[Lora Tay the Seamstress]"; + mes "Lora inspects your gloves, shock apparent on her face."; + mes "\"Winter gloves? And he managed the join stitches without adding a single crease... no, that can't be the Agostine we were talking about...\""; + mes "Without asking, she takes your gloves and inspects them further."; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"Triple inverted crossbar... he stole another of my stitching patterns! Except... no, that couldn't hold... what kind of yarn...\""; + mes "Torn between excitement, worry, and latent disgust, she frantically turns the gloves from side to side, looking for obvious flaws, her face reddening."; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"No, it's perfect... Inconceivable! How could that little upstart... I don't believe it!\""; + mes "Breathing heavily with something that might be anger or confusion, she turns them inside out."; + next; + mes "[Lora Tay the Seamstress]"; + mes "She slides her fingers over the now-exposed inside whilst the color of her face approaches that of an over-ripe tomato."; + mes "\"Almost perfectly smooth...\", she whispers."; + next; + mes "[Lora Tay the Seamstress]"; + mes "She jumps up in a fit of sudden rage."; + mes "\"That's impossible! Where did he learn to sew like that! How did he cut the shapes so perfectly! When did he..."; + mes "Suddenly, she breaks into a cough."; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"*cough* ... can't possibly... *cough*...\""; + mes "Lora sinks back on her chair, trying to control her cough."; + next; + if (countitem("BottleOfWater")) + menu + "(sneak out of the room)", L_End, + "Are you alright?", L_Next, + "Here, have a bottle of water...", L_Give_water, + "Can I help you?", L_Next; + + menu + "(sneak out of the room)", L_End, + "Are you alright?", L_Next, + "Can I help you?", L_Next; + +L_Next: + mes "[Lora Tay the Seamstress]"; + mes "She holds out your gloves for you to pick up, then waves you out of the room. Fortunately, her cough seems to be slowly subsiding."; + close; + +L_agostine_3: + mes "[Lora Tay the Seamstress]"; + mes "She frowns at you."; + mes "\"What other self-absorbed tailor-pretends with the sewing skill of a mouboo are there around? Stop wasting my time.\""; + close; + +L_agostine_4: + mes "[Lora Tay the Seamstress]"; + mes "\"Is it not obvious? We are talking about someone so self-absorbed that he chose to ignore the advice of myself, greatest of the seven southern seamstresses! I cannot stand such arrogance.\""; + close; + +L_agostine_5: + mes "[Lora Tay the Seamstress]"; + mes "\"Good for you. That arrogant good-for-nothing couldn't tell a piece of cotton from chain mail if he wiped his nose with it!\""; + close; + +L_No_Water: + mes "Checking more closely, you realize that you are out of water."; + mes "You apologise to Lora, though it fortunately seems that her cough is subsiding."; + mes "She waves you out of the room."; + close; + +L_regular_intro: + mes "[Lora Tay the Seamstress]"; + mes "\"Ah, you again. Is there anything in particular you need?\""; + next; + if (QUEST_WG_state == 12) + goto L_Main_menu2; + goto L_Main_menu; + +L_Give_water: + if (countitem("BottleOfWater") == 0) + goto L_No_Water; + delitem "BottleOfWater", 1; + QUEST_WG_state = 11; + + @xpval = 50000; + + getexp @xpval, 0; + mes "[Lora Tay the Seamstress]"; + mes "Lora grabs your water bottle and take a deep sip. Slowly, her head regains a more natural color. As she puts the bottle down, her cough seems to have subsided."; + mes "\"Thank you, dear; that was just the right thing at the right time.\""; + mes "[You gain " + @xpval + " experience points]"; + next; + mes "[Lora Tay the Seamstress]"; + mes "She hands you the winter gloves again."; + mes "\"Now what happened I don't know, but this wasn't made by the Agostine I remember. But I shan't be made a fool out of by one such as him.\""; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"If HE can sew petty every-day items for ordinary peasants, then so can I. Very well, is there anything in particular that you need made? I shall show you how a proper seamstress operates!\""; + next; + + if (QUEST_WG_state == 12) + goto L_Main_menu2; + goto L_Main_menu; + +L_Main_menu: + menu + "Can you trim something for me?", L_trim, + "Can you lengthen something for me?", L_lengthen, + "Can you make me a shirt?", L_Shirt, + "Can you make me a tank top?", L_tanktop, + "Can you make me a cape?", L_cape, + "Can you make fur boots for me?", L_Fur, + "Can you make winter gloves me?", L_Fur, + "Can you sew a robe for me?", L_robe, + "Can you make a desert hat for me?", L_desert_hat, + "Can you enhance a sorcerer robe with another line color?", L_sorcerer_robe_linecolor, + "Goodbye for now.", L_End; + +L_Fur: + mes "[Lora Tay the Seamstress]"; + mes "\"From what? Fluffy fur? I won't work with such lowly materials.\""; + next; + goto L_Main_menu; + +L_Main_menu2: + menu + "Can you trim something for me?", L_trim, + "Can you lengthen something for me?", L_lengthen, + "Can you make me a shirt?", L_Shirt, + "Can you make me a tank top?", L_tanktop, + "Can you make me a cape?", L_cape, + "Can you make fur boots for me?", L_Fur1, + "Can you make winter gloves me?", L_Fur1, + "Can you sew a robe for me?", L_robe, + "Can you make a desert hat for me?", L_desert_hat, + "Actually...I have a proposal for you...", L_agostine_proposal, + "Goodbye for now.", L_End; + +L_Fur1: + mes "[Lora Tay the Seamstress]"; + mes "\"From what? Fluffy fur? I won't work with such lowly materials.\""; + next; + goto L_Main_menu2; + +L_agostine_proposal: + mes "[Lora Tay the Seamstress]"; + mes "\"What kind of proposal are you asking of me?\""; + next; + + menu + "I have talked to Agostine and he needs your help.", L_Next1, + "I forgot...", L_Main_menu2; + +L_Next1: + mes "[Lora Tay the Seamstress]"; + mes "\"That cheat! What could he possibly want from me?"; + mes "We don't even have the same style in seaming. Is this a joke?\""; + next; + + menu + "It's no joke. He needs your help in creating a new fashion!", L_Next2, + "Yeah you're right, he was probably joking.", L_End; + +L_Next2: + mes "[Lora Tay the Seamstress]"; + mes "\"Well it depends what he wants from me."; + mes "Go see Agostine, ask him what he's planning and I will think about it.\""; + + QUEST_WG_state = 13; + + close; + +L_proposal_intro: + mes "Lora Tay seems lost in her thoughts... "; + menu + "Sorry to disturb you, but...", L_Main_menu, + "I better go talk to Agostine.", L_End; + +L_proposal_show_materials: + mes "[Lora Tay the Seamstress]"; + mes "\"Did you talk to Agostine about his idea?\""; + next; + menu + "Yes, he explained he saw a dress in a dream and gave me these materials.", L_Next3, + "No, that guy is a total wacko.", L_End; + +L_Next3: + mes "[Lora Tay the Seamstress]"; + mes "\"Wow these are such fine materials!"; + mes "These are certainly not for just any ordinary clothes..."; + mes "He must be thinking of something magnificent.\""; + next; + + menu + "He said this garment would be his first dress creation and that he wanted it to be fabulous!", L_Next4; + +L_Next4: + mes "[Lora Tay the Seamstress]"; + mes "\"Fine I will accept his offer..."; + mes "But the materials he has given you need some sewing on the edges.\""; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"I will fix them, but I cannot use normal threading to fix these materials."; + mes "I will need a soft thin thread so I won't spoil the textures.\""; + next; + goto L_proposal_search_items; + +L_proposal_search_items: + mes "[Lora Tay the Seamstress]"; + mes "\"Please bring me " + @FINEDRESS_COCOONS + " silk cocoons."; + mes "This should cover the materials needed, and will leave extra for sewing the pieces together later on.\""; + next; + + menu + "I will go get them right away!", L_Next5, + "Here you are.", L_proposal_give_items, + @FINEDRESS_COCOONS+" silk cocoons for a dress? Find somebody else.", L_End; + +L_Next5: + QUEST_WG_state = 15; + close; + +L_proposal_not_enough_items: + mes "[Lora Tay the Seamstress]"; + mes "\"This is not funny. I need " + @FINEDRESS_COCOONS + " silk cocoons. No less."; + close; + +L_proposal_give_items: + if (countitem ("SilkCocoon") < @FINEDRESS_COCOONS) + goto L_proposal_not_enough_items; + delitem "SilkCocoon", @FINEDRESS_COCOONS; + QUEST_WG_state = 16; + + mes "[Lora Tay the Seamstress]"; + mes "\"Great, now I can spin the silk threading and fix this mess made of these fine materials."; + mes "While I do this, you will need to go get the design drawings from Agostine..."; + mes "Every tailor puts their designs on paper before they forget about it.\""; + next; + goto L_proposal_retrieve_design; + +L_proposal_retrieve_design: + mes "[Lora Tay the Seamstress]"; + mes "\"May you go retrieve the design from Agostine?\""; + next; + menu + "Certainly I'll be back soon!", L_End, + "Actually I want something else...", L_Main_menu; + +L_proposal_bringing_design: + mes "[Lora Tay the Seamstress]"; + mes "\"Welcome back, did you get the drawings I requested?\""; + next; + menu + "Here they are!", L_Next6; + +L_Next6: + mes "[Lora Tay the Seamstress]"; + mes "\"Wonderful, this dress design is magnificent!"; + mes "I am done with the threading but I will now need to sew these materials together into the design.\""; + next; + QUEST_WG_state = 18; + goto L_proposal_wait_dress_finished; + +L_proposal_wait_dress_finished: + // Sets @time_start to the current time + // if not set yet, or if the player logged off. + if (@time_start == 0) set @time_start, gettimetick(2); + if (gettimetick(2) - @time_start > 30) + goto L_proposal_dress_finished; + mes "[Lora Tay the Seamstress]"; + mes "\"It will take some time to have the dress finished."; + mes "Meanwhile, why don't you go take some air outside?\""; + close; + +L_proposal_dress_finished: + QUEST_WG_state = 19; + mes "[Lora Tay the Seamstress]"; + mes "\"Well it was difficult seaming with such fragile materials."; + mes "But I have done it. The design is finished except for one part of it.\""; + next; + + menu + "Wow it looks great, but what's missing?", L_Next7; + +L_Next7: + mes "[Lora Tay the Seamstress]"; + mes "\"Well there are white flare designs on the bottom of the dress."; + mes "I'm not great with fine cloth design so this might be something Agostine must do on his own."; + next; + + mes "[Lora Tay the Seamstress]"; + mes "\"Please take the unfinished dress to him so he may finish the edges with the cloth.\""; + next; + + menu + "Okay, can't wait to see the finished product! Thanks for your help!", L_Next8; + +L_Next8: + QUEST_WG_state = 20; + close; + +L_proposal_deliver_dress: + mes "[Lora Tay the Seamstress]"; + mes "\"Please take the unfinished dress to him so he may finish the edges with the cloth.\""; + menu + "I'm on my way.", L_End, + "I have a request...", L_Main_menu; + +L_desert_hat: + mes "[Lora Tay the Seamstress]"; + mes "The seamstress sighs."; + mes "\"One of these days it would be nice to have an actual challenge... yes, of course I can make a desert hat. A Cotton Headband, three pieces of cotton cloth. Hmm. And let's add 300 GP to that.\""; + next; + @default_choice$ = "Never mind."; + setarray @items, 724, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149; + setarray @item_names$, "Here is a Cotton Headband.", "Here is a Red Cotton Headband.", "Here is a Green Cotton Headband.", "Here is a Dark Blue Cotton Headband.", "Here is a Yellow Cotton Headband.", "Here is a Light Blue Cotton Headband.", "Here is a Pink Cotton Headband.", "Here is a Black Cotton Headband.", "Here is an Orange Cotton Headband.", "Here is a Puple Cotton Headband.", "Here is a Dark Green Cotton Headband."; + @items_nr = 11; + + callsub S_pick_one_of_many_items; + if (@item == 0) + goto L_Main_menu; + if (@item == 724) + @genitem = 723; + if (@item != 724) + @genitem = @item - 10; + if (countitem(@item) < 1) + goto L_desert_hat_noheadband; + if (countitem("CottonCloth") < 3) + goto L_desert_hat_nocotton; + if (Zeny < 300) + goto L_desert_hat_nogp; + delitem @item, 1; + delitem "CottonCloth", 3; + Zeny = Zeny - 300; + getitem @genitem, 1; + mes "[Lora Tay the Seamstress]"; + mes "\"Now then, this should only take a minute.\""; + mes "The seamstress folds your cotton cloth in a complicated fashion, wraps the resulting bundle into and out of the headband, and finally sews together several loose ends."; + next; + mes "[Lora Tay the Seamstress]"; + mes "The result resembles a huge knot."; + mes "Lora picks two ends and pulls, hard-- to your amazement, the knot unfolds, yielding a Desert Hat."; + mes "\"Here you are.\""; + next; + goto L_Main_menu; + +L_desert_hat_nocotton: + mes "[Lora Tay the Seamstress]"; + mes "\"You're asking me to make a desert hat without enough cloth? Here, have your headband back-- see, that's how much I can do without cloth! And since I'm generous today, you can even keep your GP...\""; + next; + goto L_Main_menu; + +L_desert_hat_nogp: + mes "[Lora Tay the Seamstress]"; + mes "\"No, no, no. 300 GP. That's not that much, I've been told. I can't just work for free, now can I?\""; + next; + goto L_Main_menu; + +L_desert_hat_noheadband: + mes "[Lora Tay the Seamstress]"; + mes "\"Now that is odd. Where did your headband vanish to? Well, I'm not going to make you something as silly as a headband, so get one from elsewhere.\""; + next; + goto L_Main_menu; + +L_trim: + mes "[Lora Tay the Seamstress]"; + mes "\"Trimming... you mean that you can't do that yourself? Well, alright... I suppose I should charge 100 GP and do it.\""; + mes "\"Alright. What is it that you want trimmed?\""; + next; + + @default_choice$ = "Never mind."; + setarray @items, 1202, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 688, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 632, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 648, 2180, 2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189; + setarray @item_names$, "Cotton Shirt", "Red Cotton Shirt", "Green Cotton Shirt", "Dark Blue Cotton Shirt", "Yellow Cotton Shirt", "Light Blue Cotton Shirt", "Pink Cotton Shirt", "Black Cotton Shirt", "Orange Cotton Shirt", "Purple Cotton Shirt", "Dark Green Cotton Shirt", "Tank Top", "Red Tank Top", "Green Tank Top", "Dark Blue Tank Top", "Yellow Tank Top", "Light Blue Tank Top", "Pink Tank Top", "Black Tank Top", "Orange Tank Top", "Purple Tank Top", "Dark Green Tank Top", "Cotton Skirt", "Red Cotton Skirt", "Green Cotton Skirt", "Dark Blue Cotton Skirt", "Yellow Cotton Skirt", "Light Blue Cotton Skirt", "Pink Cotton Skirt", "Black Cotton Skirt", "Orange Cotton Skirt", "Purple Cotton Skirt", "Dark Green Cotton Skirt", "Cotton Trousers", "Red Cotton Trousers", "Green Cotton Trousers", "Dark Blue Cotton Trousers", "Yellow Cotton Trousers", "Light Blue Cotton Trousers", "Pink Cotton Trousers", "Black Cotton Trousers", "Orange Cotton Trousers", "Purple Cotton Trousers", "Dark Green Cotton Trousers"; + @items_nr = 44; + callsub S_pick_one_of_many_items; + if (@item == 0) + goto L_Main_menu; + @delitem = @item; + @genitem = 0; + if (@item == 1202) + @genitem = 688; + if (@item == 688) + @genitem = 689; + if (@item == 632) + @genitem = 771; + if (@item == 648) + @genitem = 586; + if (@item >= 2090 && @item <= 2099) + @genitem = @item + 30; + if (@item >= 2050 && @item <= 2059) + @genitem = @item + 40; + if (@item >= 2100 && @item <= 2109) + @genitem = @item + 70; + if (@item >= 2180 && @item <= 2189) + @genitem = @item - 70; + if (@genitem == 0) + goto L_trim_impossible; + if (Zeny < 100) + goto L_trim_noZeny; + if (countitem(@delitem) < 1) + goto L_Main_menu; + + delitem @delitem, 1; + getitem @genitem, 1; + Zeny = Zeny - 100; + mes "[Lora Tay the Seamstress]"; + mes ""; + mes "\"There you are, dear.\""; + next; + goto L_Main_menu; + +L_trim_impossible: + mes "[Lora Tay the Seamstress]"; // INTERNAL ERROR + mes "\"I don't really want to shorten this any more.\""; + next; + goto L_Main_menu; + +L_trim_noZeny: + mes "[Lora Tay the Seamstress]"; + mes "\"Not enough GP? Well, just do it yourself-- trimming really is trivial.\""; + next; + goto L_Main_menu; + +L_lengthen: + mes "[Lora Tay the Seamstress]"; + mes "\"Naturally, I will need a piece of Cotton Cloth to sew onto it; I must also charge you some... hmm... does 500 GP sound reasonable? Yes, I think I shall charge that.\""; + mes "\"Now, let me see. What is it that you want lengthened?\""; + next; + + @default_choice$ = "Never mind."; + setarray @items, 688, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 689, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 771, 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 586, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119; + setarray @item_names$, "Tank Top", "Red Tank Top", "Green Tank Top", "Dark Blue Tank Top", "Yellow Tank Top", "Light Blue Tank Top", "Pink Tank Top", "Black Tank Top", "Orange Tank Top", "Purple Tank Top", "Dark Green Tank Top", "Short Tank Top", "Red Short Tank Top", "Green Short Tank Top", "Dark Blue Short Tank Top", "Yellow Short Tank Top", "Light Blue Short Tank Top", "Pink Short Tank Top", "Black Short Tank Top", "Orange Short Tank Top", "Purple Short Tank Top", "Dark Green Short Tank Top", "Miniskirt", "Red Miniskirt", "Green Miniskirt", "Dark Blue Miniskirt", "Yellow Miniskirt", "Light Blue Miniskirt", "Pink Miniskirt", "Black Miniskirt", "Orange Miniskirt", "Purple Miniskirt", "Dark Green Miniskirt", "Cotton Shorts", "Red Cotton Shorts", "Green Cotton Shorts", "Dark Blue Cotton Shorts", "Yellow Cotton Shorts", "Light Blue Cotton Shorts", "Pink Cotton Shorts", "Black Cotton Shorts", "Orange Cotton Shorts", "Purple Cotton Shorts", "Dark Green Cotton Shorts"; + + @items_nr = 44; + callsub S_pick_one_of_many_items; + if (@item == 0) + goto L_Main_menu; + + @delitem = @item; + @genitem = 0; + if (@item == 688) + @genitem = 1202; + if (@item == 689) + @genitem = 688; + if (@item == 771) + @genitem = 632; + if (@item == 586) + @genitem = 648; + if (@item >= 2120 && @item <= 2129) + @genitem = @item - 30; + if (@item >= 2090 && @item <= 2099) + @genitem = @item - 40; + if (@item >= 2170 && @item <= 2179) + @genitem = @item - 70; + if (@item >= 2110 && @item <= 2119) + @genitem = @item + 70; + if (@genitem == 0) + goto L_lengthen_impossible; + if (Zeny < 500) + goto L_lengthen_noZeny; + if (countitem ("CottonCloth") < 1) + goto L_lengthen_nocotton; + if (countitem(@delitem) < 1) + goto L_Main_menu; + + delitem @delitem, 1; + delitem "CottonCloth", 1; + getitem @genitem, 1; + Zeny = Zeny - 500; + mes "[Lora Tay the Seamstress]"; + mes "The seamstress cuts your piece of cotton cloth into stripes and sews them on. Using some odd liquid, she flattens the seams and borders."; + if (@delitem > 2000) // dyed + mes "She then applies another liquid-- smelling strangely of fermented apples-- to the result, wraps it up, and wrings it tightly. As she unwraps it, you observe to your amazement that the dye has spread to the newly attached area."; + mes "\"There you are, dear.\""; + next; + goto L_Main_menu; + +L_lengthen_nocotton: + mes "[Lora Tay the Seamstress]"; + mes "\"Who do you think I am? The cotton fairy? Get yourself some cloth before you ask me to sew it on.\""; + next; + goto L_Main_menu; + +L_lengthen_noZeny: + mes "[Lora Tay the Seamstress]"; + mes "\"No gold, no sewing.\""; + next; + goto L_Main_menu; + +L_lengthen_impossible: + mes "[Lora Tay the Seamstress]"; // INTERNAL ERROR + mes "\"I really can't lengthen that any more.\""; + next; + goto L_Main_menu; + +L_Shirt: + mes "[Lora Tay the Seamstress]"; + mes "\"Oh, how silly. You can buy those everywhere. Please don't bore me with such trifles, dear.\""; + next; + goto L_Main_menu; + +L_tanktop: + mes "[Lora Tay the Seamstress]"; + mes "\"A tank top? Well, hardly a challenge, but I suppose I could do that. Please get me some cloth-- plain cotton will do-- and, let's see, how does 100,000 GP for my efforts sound?\""; + mes "\"Wait, I keep forgetting, you are just an adventurer... let's make that 1000 GP, then.\""; + next; + goto L_tanktop_menu; + +L_tanktop_menu: + menu + "I think I would like something else.", L_Main_menu, + "A tank top, please (6 Cotton Cloth)", L_tanktop_long, + "A short tank top, please (5 Cotton Cloth)", L_tanktop_short, + "Goodbye for now.", L_End; + +L_tanktop_short: + if (countitem ("CottonCloth") < 5) + goto L_tanktop_ic; + if (Zeny < 1000) + goto L_tanktop_ins_Z; + getinventorylist; + if (@inventorylist_count == 100 && countitem("CottonCloth") > 5) + goto L_TooMany; + Zeny = Zeny - 1000; + delitem "CottonCloth", 5; + getitem "ShortTankTop", 1; + mes "[Lora Tay the Seamstress]"; + mes "You watch as Lora sews the pieces of cloth together, then flattens the seams and ends with some odd liquid."; + mes "Amazingly, the result looks like a single piece of cloth."; + mes "After little more than a few elegant cuts and folds, your tank top is ready."; + mes "\"Oh dear, it seems that I am done already. Do you need anything else?\""; + next; + goto L_tanktop_menu; + +L_tanktop_long: + if (countitem ("CottonCloth") < 6) + goto L_tanktop_ic; + if (Zeny < 1000) + goto L_tanktop_ins_Z; + getinventorylist; + if (@inventorylist_count == 100 && countitem("CottonCloth") > 6) + goto L_TooMany; + Zeny = Zeny - 1000; + delitem "CottonCloth", 6; + getitem "TankTop", 1; + mes "[Lora Tay the Seamstress]"; + mes "After combining your pieces of cloth into one-- miraculously making the seams and stitches vanish-- the seamstress cuts out all that doesn't belong into a tank top, then folds and stabilises the edges with additional seam lines."; + mes "\"There you are. Don't worry, I have trimmed it to match your size perfectly.\""; + next; + goto L_tanktop_menu; + +L_tanktop_ic: + mes "[Lora Tay the Seamstress]"; + mes "\"No, no, no, count again-- how much Cotton Cloth did I say I needed? That's not enough.\""; + next; + goto L_tanktop_menu; + +L_tanktop_ins_Z: + mes "[Lora Tay the Seamstress]"; + mes "As you note that you don't have enough GP on you, Lora rolls her eyes."; + mes "\"I don't work for free, you know. I already gave you a discounted peasant price.\""; + next; + goto L_Main_menu; + +L_cape: + if (countitem("GoldenWarlordPlate") > 0) goto L_cape2; + mes "[Lora Tay the Seamstress]"; + mes "She shakes her head."; + mes "\"No capes. You're an adventurer, right? Do you want to get caught by a closing portcullis? Or strangled when your cape gets tangled in a tree? Or brought down by a mushroom stomping on your cape while you're trying to run away? No capes.\""; + next; + goto L_Main_menu; + +L_cape2: + mes "[Lora Tay the Seamstress]"; + mes "\"Well, it looks like you have some pretty golden armor, I can add a cape if you wish....\""; + next; + goto L_capemenu; + +L_capemenu: + mes "[Lora Tay the Seamstress]"; + mes "\"That will be 20 cotton cloth, 5 iron ingots, and 240,000gp.\""; + menu + "That sounds awesome! Yes!", L_capeyes, + "What a ripoff!?", L_End; + +L_capeyes: + if (countitem("CottonCloth") < 20) goto L_Missing; + if (countitem("IronIngot") < 5) goto L_Missing; + if (countitem("GoldenWarlordPlate") < 1) goto L_Missing; + if (Zeny < 240000) goto L_NotEnoughMoney; + getinventorylist; + if (@inventorylist_count == 100 && countitem("Cottoncloth") > 5 && countitem("Ironingot") < 5) goto L_TooMany; + delitem "CottonCloth", 20; + delitem "IronIngot", 5; + delitem "GoldenWarlordPlate", 1; + Zeny = Zeny - 240000; + getitem "WhiteSaviorArmor", 1; + mes "[Lora Tay]"; + mes "\"There you go!\""; + next; + goto L_End; + +L_NotEnoughMoney: + mes "[Lora Tay]"; + mes "\"I'm not doing this work for charity! Find your money or stop bothering me.\""; + next; + goto L_Main_menu; + +L_Missing: + mes "[Lora Tay]"; + mes "\"You seem to be missing some things.\""; + next; + goto L_Main_menu; + +L_robe: + mes "[Lora Tay the Seamstress]"; + mes "\"A robe? Well, as long as you're not asking me to make it out of cotton...\""; + next; + if (countitem("SilkCocoon")== 0 && countitem("SilkSheet")== 0) + goto L_robe_nosilk; + goto L_robe_menu; + +L_robe_menu: + menu + "Here, I have some silk cocoons!", L_Next9, + "Here, I have some silk sheets!", L_robe_sheets, + "Where can I find silk?", L_where_silk, + "How much silk do you need?", L_how_much_silk, + "Oh, never mind.", L_Main_menu; + +L_Next9: + mes "[Lora Tay the Seamstress]"; + mes "The seamstress stares at you as if you had gone out of your mind."; + mes "\"And what precisely do you expect me to do with Silk Cocoons? String them together in some baubly chain? Somehow remove the thread and spin and weave it so that some random person can have their robe?\""; + next; + menu + "Will you trade cocoons for sheets?", L_Next10, + "Never mind.", L_Main_menu; + +L_Next10: + mes "[Lora Tay the Seamstress]"; + mes "Lora frowns."; + mes "\"It's not all that easy to get properly processed silk around here. Easier for me than for you, I admit...\""; + next; + mes "[Lora Tay the Seamstress]"; + mes "She sighs."; + mes "\"All right, very well then. I have enough spare silk sheets right now, but you pay the silk processing for your cocoons.\""; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"Let's see... " + @ROBE_COCOONS_NR + " silk cocoons and " + @ROBE_ZENY + " GP should just about cover that.\""; + next; + menu + "I'm not interested.", L_Main_menu, + "Very well then, here you are.", L_Next11; + +L_Next11: + if (countitem("SilkCocoon") < @ROBE_COCOONS_NR) + goto L_robe_missing_cocoons; + if (Zeny < @ROBE_ZENY) + goto L_robe_missing_Zeny; + getinventorylist; + if (@inventorylist_count == 100 && countitem("SilkCocoon") > @ROBE_COCOONS_NR) + goto L_TooMany; + Zeny = Zeny - @ROBE_ZENY; + delitem "SilkCocoon", @ROBE_COCOONS_NR; + mes "[Lora Tay the Seamstress]"; + mes "The seamstress puts your cocoons and GP away, takes your measurements and pulls out several silk sheets. She asks you to stretch out your arms, then pins the silk sheets in place all over your body."; + next; + goto L_Get_robe; + +L_robe_sheets: + @Robe_with_sheets = 1; + mes "[Lora Tay the Seamstress]"; + mes "\"Let's see... " + @ROBE_SHEETS_NR + " silk sheets and " + @ROBE_SHEETS_ZENY + " GP should just about cover that.\""; + next; + menu + "I'm not interested.", L_Main_menu, + "Very well then, here you are.", L_Next12; + +L_Next12: + if (countitem("SilkSheet") < @ROBE_SHEETS_NR) + goto L_robe_missing_sheets; + if (Zeny < @ROBE_SHEETS_ZENY) + goto L_robe_sheet_missing_Zeny; + getinventorylist; + if (@inventorylist_count == 100 && countitem("SilkSheet") > @ROBE_SHEETS_NR) + goto L_TooMany; + Zeny = Zeny - @ROBE_SHEETS_ZENY; + delitem "SilkSheet", @ROBE_SHEETS_NR; + mes "[Lora Tay the Seamstress]"; + mes "The seamstress puts your GP away, takes your measurements and puts the silk sheets on the table. She asks you to stretch out your arms, then pins the silk sheets in place all over your body."; + next; + goto L_Get_robe; + +L_Get_robe: + mes "[Lora Tay the Seamstress]"; + mes "She proceeds to make a number of mysterious marks on the sheets with a charcoal pen. Meanwhile, your arms are getting tired, but you're afraid to lower them-- she used a lot of pins to put everything into place, and you lost track of where they went..."; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"Splendid.\""; + mes "After removing the sheets (and pins!), she picks up a pair of scissors and plows through the cloth at her usual amazing pace, only stopping briefly to sew together parts here and there."; + next; + mes "[Lora Tay the Seamstress]"; + mes "She then treats the seams with some odorless liquid, making them vanish completely. Finally she hands you the finished robe."; + getitem "SilkRobe", 1; + if (@Robe_with_sheets == 1) + goto L_robe_sheet_end; + mes "\"Here you are now, this will fit. And don't you dare tell anyone that I did this! I'm not about to start a silk cocoon collection!\""; + next; + goto L_Main_menu; + +L_robe_sheet_end: + mes "\"Here you are now, this will fit. Thank you for bringing the sheets. People are so lazy nowadays! \""; + next; + goto L_Main_menu; + +L_robe_missing_cocoons: + mes "[Lora Tay the Seamstress]"; + mes "\"" + @ROBE_COCOONS_NR + " cocoons is what I said, not " + countitem("SilkCocoon") + "."; + mes @ROBE_COCOONS_NR + " is " + (@ROBE_COCOONS_NR / 10) + " as many times as you have fingers, in case that helps.\""; + next; + goto L_Main_menu; + +L_robe_missing_Zeny: + mes "[Lora Tay the Seamstress]"; + mes "\"No, no no. The way this works is that you give me the GP first and I give you the robe afterwards. You're " + (@ROBE_ZENY - Zeny) + " GP short, so come back when you can afford the robe!\""; + next; + goto L_Main_menu; + +L_robe_missing_sheets: + mes "[Lora Tay the Seamstress]"; + mes "\"" + @ROBE_SHEETS_NR + " silk sheets is what I said, not " + countitem("SilkSheet") + "."; + mes @ROBE_SHEETS_NR + " is " + (10-@ROBE_SHEETS_NR) + " less than you have fingers, in case that helps.\""; + next; + goto L_Main_menu; + +L_robe_sheet_missing_Zeny: + mes "[Lora Tay the Seamstress]"; + mes "\"No, no no. The way this works is that you give me the GP first and I give you the robe afterwards. You're " + (@ROBE_SHEETS_ZENY - Zeny) + " GP short, so come back when you can afford the robe!\""; + next; + goto L_Main_menu; + +L_where_silk: + mes "[Lora Tay the Seamstress]"; + mes "\"Oh, Hetchel on the Tulimshar Marketplace can weave them for you.\""; + next; + mes "[Lora Tay the Seamstress]"; + mes "She hesitates."; + mes "\"I didn't see her there the last time I visited, though. She may be visiting family. Tough luck.\""; + next; + goto L_robe_menu; + +L_how_much_silk: + mes "[Lora Tay the Seamstress]"; + mes "\"Six sheets should do fine, if they're the usual double-elbow squares.\""; + next; + goto L_robe_menu; + +L_robe_nosilk: + mes "[Lora Tay the Seamstress]"; + mes "\"What is that? You don't have any silk? Well, you're out of luck, I would say. I shan't make one out of those cotton rags; you might as well be wearing an apple sack.\""; + next; + goto L_Main_menu; + +L_End: + @ROBE_COCOONS_NR = 0; + @ROBE_ZENY = 0; + @ROBE_SHEETS_NR = 0; + @ROBE_SHEETS_ZENY = 0; + @Robe_with_sheets = 0; + close; + +S_pick_one_of_many_items: + @c = 0; + @i = 0; + + setarray @choice_n$, "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", ""; + goto L_SUB_pick_choice_loop; + +L_SUB_pick_choice_loop: + if (@i >= @items_nr) + goto L_SUB_choice_init_done; + @current = @items[@i]; + @current_name$ = @item_names$[@i]; + @i = @i + 1; + + if (countitem(@current) == 0) + goto L_SUB_pick_choice_loop; + @choice_v[@c] = @current; + @choice_n$[@c] = @current_name$; + @c = @c + 1; + goto L_SUB_pick_choice_loop; + +L_SUB_choice_init_done: + @choice_v[@c] = 0; + @choice_n$[@c] = @default_choice$; + @c = @c + 1; + + if (@c < 10) + menu + @choice_n$[0], L_SUB_choice_join, + @choice_n$[1], L_SUB_choice_join, + @choice_n$[2], L_SUB_choice_join, + @choice_n$[3], L_SUB_choice_join, + @choice_n$[4], L_SUB_choice_join, + @choice_n$[5], L_SUB_choice_join, + @choice_n$[6], L_SUB_choice_join, + @choice_n$[7], L_SUB_choice_join, + @choice_n$[8], L_SUB_choice_join, + @choice_n$[9], L_SUB_choice_join; + + if (@c < 20) + menu + @choice_n$[0], L_SUB_choice_join, + @choice_n$[1], L_SUB_choice_join, + @choice_n$[2], L_SUB_choice_join, + @choice_n$[3], L_SUB_choice_join, + @choice_n$[4], L_SUB_choice_join, + @choice_n$[5], L_SUB_choice_join, + @choice_n$[6], L_SUB_choice_join, + @choice_n$[7], L_SUB_choice_join, + @choice_n$[8], L_SUB_choice_join, + @choice_n$[9], L_SUB_choice_join, + @choice_n$[10], L_SUB_choice_join, + @choice_n$[11], L_SUB_choice_join, + @choice_n$[12], L_SUB_choice_join, + @choice_n$[13], L_SUB_choice_join, + @choice_n$[14], L_SUB_choice_join, + @choice_n$[15], L_SUB_choice_join, + @choice_n$[16], L_SUB_choice_join, + @choice_n$[17], L_SUB_choice_join, + @choice_n$[18], L_SUB_choice_join, + @choice_n$[19], L_SUB_choice_join; + + menu + @choice_n$[0], L_SUB_choice_join, + @choice_n$[1], L_SUB_choice_join, + @choice_n$[2], L_SUB_choice_join, + @choice_n$[3], L_SUB_choice_join, + @choice_n$[4], L_SUB_choice_join, + @choice_n$[5], L_SUB_choice_join, + @choice_n$[6], L_SUB_choice_join, + @choice_n$[7], L_SUB_choice_join, + @choice_n$[8], L_SUB_choice_join, + @choice_n$[9], L_SUB_choice_join, + @choice_n$[10], L_SUB_choice_join, + @choice_n$[11], L_SUB_choice_join, + @choice_n$[12], L_SUB_choice_join, + @choice_n$[13], L_SUB_choice_join, + @choice_n$[14], L_SUB_choice_join, + @choice_n$[15], L_SUB_choice_join, + @choice_n$[16], L_SUB_choice_join, + @choice_n$[17], L_SUB_choice_join, + @choice_n$[18], L_SUB_choice_join, + @choice_n$[19], L_SUB_choice_join, + @choice_n$[20], L_SUB_choice_join, + @choice_n$[21], L_SUB_choice_join, + @choice_n$[22], L_SUB_choice_join, + @choice_n$[23], L_SUB_choice_join, + @choice_n$[24], L_SUB_choice_join, + @choice_n$[25], L_SUB_choice_join, + @choice_n$[26], L_SUB_choice_join, + @choice_n$[27], L_SUB_choice_join, + @choice_n$[28], L_SUB_choice_join, + @choice_n$[29], L_SUB_choice_join, + @choice_n$[30], L_SUB_choice_join, + @choice_n$[31], L_SUB_choice_join; + +L_SUB_choice_join: + @menu = @menu - 1; + @item = @choice_v[@menu]; + if (@menu >= @c) + @item = 0; + return; + +L_TooMany: + mes "[Lora Tay the Seamstress]"; + mes "\"You don't have anywhere to put this. Come back when you have more room.\""; + close; + +L_sorcerer_robe_linecolor: + mes "[Lora Tay the Seamstress]"; + mes "\"Hmm, this has a line sewed on it already. I don't see another way than remove it and sew a new one."; + mes "That is quite a difficult work... for a casual tailor of course. I'll do that in a heart beat.\""; + next; + mes "\"However, you can expect this will cost you a large fee. Let's say " + @SORCERER_ROBE_SEW_ZENY + " GP."; + mes "Also, I will need " + @SORCERER_ROBE_NUM_COTTON_CLOTH + " sheets of Cotton Cloth of the color you want.\""; + next; + mes "\"Ok. Now please wear the robe you want me to change.\""; + @loratay_asking_robe = 1; + close; + +L_ExamineSorcererRobe: + @loratay_asking_robe = 0; + mes "[Lora Tay the Seamstress]"; + mes "\"Let's see...\""; + next; + @chest_equip_id = getequipid(equip_torso); + callsub S_Get_SorcererRobeColors; + mes "Lora Tay takes a closer look at what you are wearing."; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"Hmm. This " + @item_colors$[@current_linecolor] + " lined " + @item_colors$[@current_maincolor] + " sorcerer robe. I see. Is that ok?\""; + if (@current_maincolor == -1 || @current_linecolor == -1) + goto L_NotSorcererRobe; + menu + "Yes, that's fine. Take this one.", L_ChooseSorcererLineColor, + "No, I changed my mind.", L_CleanSorcererRobe; + +L_NotSorcererRobe: + mes "\"This isn't a Sorcerer Robe dear. Stop taking me for a fool.\""; + goto L_CleanSorcererRobe; + +L_ChooseSorcererLineColor: + // Unequip the robe, just to simulate Lora Tay really took it + unequipbyid(equip_torso); + mes "[Lora Tay the Seamstress]"; + mes "\"And which Cotton Cloth color will you give me?\""; + menu + "A " + @item_colors$[0] + " one.", L_ItemMenus2, + "A " + @item_colors$[1] + " one.", L_ItemMenus2, + "A " + @item_colors$[2] + " one.", L_ItemMenus2, + "A " + @item_colors$[3] + " one.", L_ItemMenus2, + "A " + @item_colors$[4] + " one.", L_ItemMenus2, + "A " + @item_colors$[5] + " one.", L_ItemMenus2, + "A " + @item_colors$[6] + " one.", L_ItemMenus2, + "A " + @item_colors$[7] + " one.", L_ItemMenus2, + "A " + @item_colors$[8] + " one.", L_ItemMenus2, + "A " + @item_colors$[9] + " one.", L_ItemMenus2, + "A " + @item_colors$[10] + " one.", L_ItemMenus2, + "Hum. I changed my mind. Later maybe.", L_End; + +L_ItemMenus2: + @chosen_color = @menu - 1; + @cotton_cloth_id = 2250 + @chosen_color; + // The White Cotton Cloth is in fact the undyed one. + if (@cotton_cloth_id == 2260) + @cotton_cloth_id = 660; + if (countitem(@cotton_cloth_id) < 2) + goto L_NoColoredCottonCloth; + if (@current_linecolor == @chosen_color) + goto L_SorcererRobe_SameColor; + callsub S_Get_NewSorcererRobeId; + // Should not happen since it has been checked the player submitted a sorcerer robe + if (@new_sorcerer_robe_id == 0) + goto L_End; + + if (Zeny < @SORCERER_ROBE_SEW_ZENY) + goto L_SorcererRobe_NoZeny; + if (countitem(@chest_equip_id) == 0) + goto L_SorcererRobe_Vanished; + delitem @cotton_cloth_id, @SORCERER_ROBE_NUM_COTTON_CLOTH; + // Since we delete an unstackable item, we don't need to check + // for an available inventory slot. + delitem @chest_equip_id, 1; + Zeny = Zeny - @SORCERER_ROBE_SEW_ZENY; + getitem @new_sorcerer_robe_id, 1; + mes "Lora Tay adjusts her glasses and removes carefully the " + @item_colors$[@current_linecolor] + " string of the robe."; + mes "Then she grabs the " + @item_colors$[@chosen_color] + " cotton cloth, unfolds it and pins her needle in it."; + next; + mes "She begins to sew the new string onto the sorcerer robe."; + next; + mes "She regularly flips and flips again the robe, which starts to show her new color."; + next; + mes "After a few minutes you barely noticed, the robe seems to be ready."; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"There you are dear. I have to say I made a perfect job... As always."; + mes "I hope you'll enjoy your new robe.\""; + goto L_CleanSorcererRobe; + +L_SorcererRobe_SameColor: + mes "[Lora Tay the Seamstress]"; + mes "\"Are you blind? Your robe is already of this color dear!\""; + goto L_CleanSorcererRobe; + +L_SorcererRobe_Vanished: + mes "Lora Tay adjusts her glasses and looks around, as if something was missing."; + next; + mes "[Lora Tay the Seamstress]"; + mes "\"Uh? Where is your sorcerer robe?\""; + goto L_CleanSorcererRobe; + +S_Get_NewSorcererRobeId: + if (@chosen_color == 0 && @current_maincolor == 10) + @new_sorcerer_robe_id = 798; + if (@chosen_color == 0 && @current_maincolor < 10) + @new_sorcerer_robe_id = 2220 + @current_maincolor; + if (@chosen_color > 0) + @new_sorcerer_robe_id = 5000 + @current_maincolor + (@chosen_color-1)*11; + return; + +L_NoColoredCottonCloth: + mes "[Lora Tay the Seamstress]"; + mes "\"You don't seem to own enough Cotton Cloth of this color. I need " + @SORCERER_ROBE_NUM_COTTON_CLOTH + " of them. Too bad.\""; + goto L_CleanSorcererRobe; + +L_SorcererRobe_NoZeny: + mes "[Lora Tay the Seamstress]"; + mes "\"You don't seem to be able to afford my services, dear.\""; + goto L_CleanSorcererRobe; + +S_Get_SorcererRobeColors: + @current_maincolor = -1; + @current_linecolor = -1; + // Line color + if (@chest_equip_id == 798 || (@chest_equip_id >= 2220 && @chest_equip_id <= 2229)) + @current_linecolor = 0; + if (@chest_equip_id >= 5000 && @chest_equip_id <= 5109) + @current_linecolor = 1 + (@chest_equip_id-5000)/11; + // Main color + if (@chest_equip_id == 798) + @current_maincolor = 10; + if (@chest_equip_id >= 2220 && @chest_equip_id <= 2229) + @current_maincolor = @chest_equip_id - (@chest_equip_id/10)*10; + if (@chest_equip_id >= 5000 && @chest_equip_id <= 5109) + @current_maincolor = @chest_equip_id - 5000 - (@current_linecolor-1)*11; + return; + +L_CleanSorcererRobe: + @current_maincolor = 0; + @current_linecolor = 0; + @chest_equip_id = 0; + @new_sorcerer_robe_id = 0; + @chosen_color = 0; + @cotton_cloth_id = 0; + close; + + */ + +OnInit: + .sex=G_FEMALE; + .distance=5; + end; +} diff --git a/npc/017-3/nico.txt b/npc/017-3/nico.txt new file mode 100644 index 0000000..19dd79e --- /dev/null +++ b/npc/017-3/nico.txt @@ -0,0 +1,302 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Arcmage Cards Enhancer + +017-3,37,84,0 script Nico Goethe NPC_PLAYER,{ + mesn; + mesq l("My name is Nico Goethe, an %s card player.", "[@@https://arcmage.org|Arcmage@@]"); + next; + mesn; + mesq l("Would love to invite you to a card minigame... But alas, Kenton told me that minigames are a waste of time, and that I should not bother adventurers with it."); + next; + mesn; + mesq l("However, I am still up for trading cards, if you wish."); + next; + goto L_Main; + +L_Main: + select + l("Trade a card"), + l("Evolve a card"), + l("Lets play!"), + l("Bye."); + mes ""; + switch (@menu) { + // Trade Card + case 1: + mesn; + mesq l("Give me a card and select another card of same class. I charge %d GP for simple exchanges.", 5000); + next; + if (Zeny < 5000) + break; + + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + .@card = requestitem(); + if (.@card <= 1) break; + + @menuret = 0; + switch (.@card) { + case HeroCard: + case KnightCard: + case ClericCard: + case DruidCard: + case MageCard: + case NinjaCard: + case NatureCard: + case NecromancerCard: + menuint + "Sorry, I don't want to trade it.", 0, + getitemname(HeroCard), HeroCard, + getitemname(KnightCard), KnightCard, + getitemname(ClericCard), ClericCard, + getitemname(DruidCard), DruidCard, + getitemname(MageCard), MageCard, + getitemname(NinjaCard), NinjaCard, + getitemname(NatureCard), NatureCard, + getitemname(NecromancerCard), NecromancerCard; + mes ""; + break; + case SpeedCard: + case ReflectCard: + case PowerCard: + case WallCard: + menuint + "Sorry, I don't want to trade it.", 0, + getitemname(SpeedCard), SpeedCard, + getitemname(ReflectCard), ReflectCard, + getitemname(PowerCard), PowerCard, + getitemname(WallCard), WallCard; + mes ""; + break; + case HeroCardS: + case KnightCardS: + case ClericCardS: + case DruidCardS: + case MageCardS: + case NinjaCardS: + case NatureCardS: + case NecromancerCardS: + menuint + "Sorry, I don't want to trade it.", 0, + getitemname(HeroCardS), HeroCardS, + getitemname(KnightCardS), KnightCardS, + getitemname(ClericCardS), ClericCardS, + getitemname(DruidCardS), DruidCardS, + getitemname(MageCardS), MageCardS, + getitemname(NinjaCardS), NinjaCardS, + getitemname(NatureCardS), NatureCardS, + getitemname(NecromancerCardS), NecromancerCardS; + mes ""; + break; + case SpeedCardS: + case ReflectCardS: + case PowerCardS: + case WallCardS: + menuint + "Sorry, I don't want to trade it.", 0, + getitemname(SpeedCardS), SpeedCardS, + getitemname(ReflectCardS), ReflectCardS, + getitemname(PowerCardS), PowerCardS, + getitemname(WallCardS), WallCardS; + mes ""; + break; + case HeroCardX: + case KnightCardX: + case ClericCardX: + case DruidCardX: + case MageCardX: + case NinjaCardX: + case NatureCardX: + case NecromancerCardX: + menuint + "Sorry, I don't want to trade it.", 0, + getitemname(HeroCardX), HeroCardX, + getitemname(KnightCardX), KnightCardX, + getitemname(ClericCardX), ClericCardX, + getitemname(DruidCardX), DruidCardX, + getitemname(MageCardX), MageCardX, + getitemname(NinjaCardX), NinjaCardX, + getitemname(NatureCardX), NatureCardX, + getitemname(NecromancerCardX), NecromancerCardX; + mes ""; + break; + case SpeedCardX: + case ReflectCardX: + case PowerCardX: + case WallCardX: + menuint + "Sorry, I don't want to trade it.", 0, + getitemname(SpeedCardX), SpeedCardX, + getitemname(ReflectCardX), ReflectCardX, + getitemname(PowerCardX), PowerCardX, + getitemname(WallCardX), WallCardX; + mes ""; + break; + default: + mesn; + mesq l("Sorry, I only deal with Arcmage cards."); + break; + } + if (@menuret < 1) + break; + Zeny -= 5000; + delitem .@card, 1; + getitem @menuret, 1; + break; + // Evolve Card + case 2: + mesn; + mesq l("Give me %s copies of the same card and %s GP, and I'll improve its tier.", b(l("three")), fnum(25000)); + mesc l("Normal Cards can be upgraded to S-Tier, and S Cards can be upgraded to X-Tier. X-Tier cards cannot be upgraded."); + next; + if (Zeny < 25000) + break; + + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + .@card = requestitem(); + if (.@card <= 1) break; + if (countitem(.@card) < 3) { + mesn; + mesq l("You don't have enough cards of this type to upgrade."); + next; + break; + } + switch (.@card) { + case HeroCard: + delitem .@card, 3; Zeny -= 25000; + getitem HeroCardS, 1; + break; + case KnightCard: + delitem .@card, 3; Zeny -= 25000; + getitem KnightCardS, 1; + break; + case ClericCard: + delitem .@card, 3; Zeny -= 25000; + getitem ClericCardS, 1; + break; + case DruidCard: + delitem .@card, 3; Zeny -= 25000; + getitem DruidCardS, 1; + break; + case MageCard: + delitem .@card, 3; Zeny -= 25000; + getitem MageCardS, 1; + break; + case NinjaCard: + delitem .@card, 3; Zeny -= 25000; + getitem NinjaCardS, 1; + break; + case NatureCard: + delitem .@card, 3; Zeny -= 25000; + getitem NatureCardS, 1; + break; + case NecromancerCard: + delitem .@card, 3; Zeny -= 25000; + getitem NecromancerCardS, 1; + break; + case SpeedCard: + delitem .@card, 3; Zeny -= 25000; + getitem SpeedCardS, 1; + break; + case ReflectCard: + delitem .@card, 3; Zeny -= 25000; + getitem ReflectCardS, 1; + break; + case PowerCard: + delitem .@card, 3; Zeny -= 25000; + getitem PowerCardS, 1; + break; + case WallCard: + delitem .@card, 3; Zeny -= 25000; + getitem WallCardS, 1; + break; + + + case HeroCardS: + delitem .@card, 3; Zeny -= 25000; + getitem HeroCardX, 1; + break; + case KnightCardS: + delitem .@card, 3; Zeny -= 25000; + getitem KnightCardX, 1; + break; + case ClericCardS: + delitem .@card, 3; Zeny -= 25000; + getitem ClericCardX, 1; + break; + case DruidCardS: + delitem .@card, 3; Zeny -= 25000; + getitem DruidCardX, 1; + break; + case MageCardS: + delitem .@card, 3; Zeny -= 25000; + getitem MageCardX, 1; + break; + case NinjaCardS: + delitem .@card, 3; Zeny -= 25000; + getitem NinjaCardX, 1; + break; + case NatureCardS: + delitem .@card, 3; Zeny -= 25000; + getitem NatureCardX, 1; + break; + case NecromancerCardS: + delitem .@card, 3; Zeny -= 25000; + getitem NecromancerCardX, 1; + break; + case SpeedCardS: + delitem .@card, 3; Zeny -= 25000; + getitem SpeedCardX, 1; + break; + case ReflectCardS: + delitem .@card, 3; Zeny -= 25000; + getitem ReflectCardX, 1; + break; + case PowerCardS: + delitem .@card, 3; Zeny -= 25000; + getitem PowerCardX, 1; + break; + case WallCardS: + delitem .@card, 3; Zeny -= 25000; + getitem WallCardX, 1; + break; + + default: + mesn; + mesq l("Sorry, I only deal with Arcmage cards."); + break; + } + break; + // TODO: Play minigame + case 3: + mesn; + mesq l("...Are you trying to troll me?"); + next; + break; + // Leave + default: + closeclientdialog; + goodbye; + close; + break; + } + goto L_Main; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TopHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, GoldenLightPlatemail); + setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); + setunitdata(.@npcId, UDT_WEAPON, ManaGloves); + setunitdata(.@npcId, UDT_HAIRSTYLE, 24); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + npcsit; + + .sex = G_MALE; + .distance = 4; + end; + +} + diff --git a/npc/017-3/slots.txt b/npc/017-3/slots.txt new file mode 100644 index 0000000..3394a8f --- /dev/null +++ b/npc/017-3/slots.txt @@ -0,0 +1,113 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Slot Machine for bets + +017-3,84,67,0 script Slot Machine#017-3a NPC_SLOTMACHINE,{ + function symbol{ + switch (getarg(0)) { + case 1: + mesn "%%A"; + break; + case 2: + mesn "%%B"; + break; + case 3: + mesn "%%C"; + break; + case 4: + mesn "%%D"; + break; + case 5: + mesn "%%E"; + break; + case 6: + mesn "%%F"; + break; + case 7: + mesn "7"; + break; + default: + mesn "%%@"; + break; + } + } + +L_Menu: + mesn; + mesc l("Spin three symbols, and jackpot great rewards!"); + mesc l("Just two coins for spin."); + next; + menu + rif(countitem(CasinoCoins) >= 2, l("Spin!")), L_Spin, + l("Prizes"), L_Info, + l("Leave"), L_Quit; + +L_Info: + mes ""; + mesc l("Prizes:"); + mes l("##9 777: @@.", getitemlink(Bloodstone)); + mes l("##9 %%A%%A%%A: @@.", getitemlink(Sapphire)); + mes l("##9 %%B%%B%%B: @@.", getitemlink(Amethyst)); + mes l("##9 %%C%%C%%C: @@.", getitemlink(Diamond)); + mes l("##9 %%D%%D%%D: @@.", getitemlink(Emerald)); + mes l("##9 %%E%%E%%E: @@.", getitemlink(Topaz)); + mes l("##9 %%F%%F%%F: @@.", getitemlink(Ruby)); + mesc l("Two equal: 1 casino coin."); + next; + goto L_Menu; + + +L_Spin: + mesc l("Spinning..."); + next; + delitem CasinoCoins, 2; + .@a=rand2(1,7); + .@b=rand2(1,7); + .@c=rand2(1,7); + symbol(.@a); + symbol(.@b); + symbol(.@c); + next; + mesn; + if (.@a == .@b && .@a == .@c) { + .@it=Iten; + switch (.@a) { + case 1: + .@it=Sapphire; break; + case 2: + .@it=Amethyst; break; + case 3: + .@it=Diamond; break; + case 4: + .@it=Emerald; break; + case 5: + .@it=Topaz; break; + case 6: + .@it=Ruby; break; + case 7: + .@it=Bloodstone; break; + } + getitem .@it, 1; + mesc l("Jackpot! You got the @@!", .@it), 3; + } else if (.@a == .@b || .@a == .@c || .@b == .@c) { + getitem CasinoCoins, 1; + mesc l("Lucky! You got the coin back!"), 3; + } else { + mesc l("It wasn't this time..."), 3; + } + next; + goto L_Menu; + +L_Quit: + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; + + +} + diff --git a/npc/017-3/vault.txt b/npc/017-3/vault.txt new file mode 100644 index 0000000..c520330 --- /dev/null +++ b/npc/017-3/vault.txt @@ -0,0 +1,20 @@ +// TMW2/LoF Script. +// Author: +// Jesusalva +// Notes: +// Based on BenB idea. + +017-3,80,39,0 script Vault#0173 NPC_NO_SPRITE,{ + LootableVault(1, 3, "01738039"); + close; + +OnInit: + .distance=3; + end; + +OnClock0201: +OnClock1418: + $VAULT_01738039+=rand2(5,25); + end; +} + diff --git a/npc/017-3/workers.txt b/npc/017-3/workers.txt new file mode 100644 index 0000000..8b640ea --- /dev/null +++ b/npc/017-3/workers.txt @@ -0,0 +1,21 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Shady NPCs with questionable purposes. + +//010-2,23,70,0 script Josh NPC_TECH_EXPLORER,{ +017-3,23,70,0 script Jenny NPC_FEMALE_WORKER,{ + showavatar NPC_FEMALE_WORKER; + mesn; + mesq l("We're busy here, so please don't bother us."); + close; +} + +017-3,39,75,0 script Zack NPC_TECH_EXPLORER,{ + showavatar NPC_TECH_EXPLORER; + mesn; + mesq l("Why are you here? This area is off-limits!"); + close; +} diff --git a/npc/017-4/_import.txt b/npc/017-4/_import.txt new file mode 100644 index 0000000..b16bdba --- /dev/null +++ b/npc/017-4/_import.txt @@ -0,0 +1,7 @@ +// Map 017-4: Tech-User Forge +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-4/_warps.txt", +"npc/017-4/pihro.txt", +"npc/017-4/pyndragon.txt", +"npc/017-4/refine.txt", +"npc/017-4/vault.txt", diff --git a/npc/017-4/_warps.txt b/npc/017-4/_warps.txt new file mode 100644 index 0000000..5447202 --- /dev/null +++ b/npc/017-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-4: Tech-User Forge warps +017-4,27,42,0 warp #017-4_27_42 0,0,017-1,150,88 +017-4,35,42,0 warp #017-4_35_42 0,0,017-1,155,88 diff --git a/npc/017-4/pihro.txt b/npc/017-4/pihro.txt new file mode 100644 index 0000000..6b5b9ae --- /dev/null +++ b/npc/017-4/pihro.txt @@ -0,0 +1,116 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Forge bullets +017-4,35,35,0 script Pihro NPC_PLAYER,{ + // cond_check(ItemID, ItemAmount) + // Returns 1 if ID is zero + function cond_check { + if (getarg(0) == 0) + return 1; + else + return (countitem(getarg(0)) >= getarg(1)); + } + + // craft_ammo( PrizeItem, Price, Lead Amount, {Elemental Item, Amount, {Elemental Item2, Amount2}} ) + function craft_ammo { + .@prize=getarg(0); + .@price=getarg(1); + .@base1=LeadIngot; + .@amon1=getarg(2); + .@base2=getarg(3,0); + .@amon2=getarg(4,0); + .@base3=getarg(5,0); + .@amon3=getarg(6,0); + + .@price=POL_AdjustPrice(.@price); + + mesn; + mesq l("Do you want to craft @@? For that I will need:", getitemlink(.@prize)); + mesc l("@@/@@ @@", countitem(.@base1), .@amon1, getitemlink(.@base1)); + if (.@amon2 > 0) + mesc l("@@/@@ @@", countitem(.@base2), .@amon2, getitemlink(.@base2)); + if (.@amon3 > 0) + mesc l("@@/@@ @@", countitem(.@base3), .@amon3, getitemlink(.@base3)); + mesc l("@@/@@ GP", format_number(Zeny), .@price); + next; + + select + l("Yes"), + l("No"); + + if (@menu == 2) + return; + + if (countitem(.@base1) >= .@amon1 && + cond_check(.@base2, .@amon2) && + cond_check(.@base3, .@amon3) && + Zeny >= .@price) { + inventoryplace .@prize, 1024; + delitem .@base1, .@amon1; + if (.@base2 > 0) + delitem .@base2, .@amon2; + if (.@base3 > 0) + delitem .@base3, .@amon3; + POL_PlayerMoney(.@price); + getitem .@prize, rand2(980, 1024); + getexp rand2(980, 1024), rand2(270, 320); + Mobpt+=rand2(270, 320); + + mes ""; + mesn; + mesq l("Many thanks! Come back soon."); + } else { + speech S_FIRST_BLANK_LINE,// | S_LAST_NEXT, + l("You don't have enough material, sorry."); + } + return; + } + mesn; + mesq l("Hello there! I make bullets for the weapons my friend Pyndragon makes."); + next; + +L_Craft: + mesn; + mesq l("Some bullets have Elemental Properties, by the way. Now, what will it be?"); + next; + select + l("Nothing, sorry."), + l("Normal Bullet"), + l("Sacred Bullet"), + l("Evil Bullet"), + l("Explosive ARROW"); + + switch (@menu) { + case 2: + // craft_ammo( PrizeItem, Price, Lead Amount, {Elemental Item, Amount, {Elemental Item2, Amount2}} ) + craft_ammo(Bullet, 200, 1); + goto L_Craft; + case 3: + craft_ammo(SacredBullet, 200, 1, WhiteFur, 5); + goto L_Craft; + case 4: + craft_ammo(EvilBullet, 200, 1, Bone, 1); + goto L_Craft; + case 5: + craft_ammo(ExplosiveArrow, 300, 1, SulfurPowder, 5, WoodenLog, 3); + goto L_Craft; + } + close; + + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, Monocle); + setunitdata(.@npcId, UDT_HEADMIDDLE, WarlordPlate); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, JeansChaps); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex=G_MALE; + .distance=5; + end; +} diff --git a/npc/017-4/pyndragon.txt b/npc/017-4/pyndragon.txt new file mode 100644 index 0000000..d112b46 --- /dev/null +++ b/npc/017-4/pyndragon.txt @@ -0,0 +1,252 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Forge firearms +017-4,34,23,0 script Pyndragon NPC_PLAYER,{ + function explainMelee; + // craft_gun( BaseItem1, Amount, BaseItem2, Amount, BaseItem3, Amount, PrizeItem, Price ) + function craft_gun { + .@base1=getarg(0); + .@amon1=getarg(1); + .@base2=getarg(2); + .@amon2=getarg(3); + .@base3=getarg(4); + .@amon3=getarg(5); + .@prize=getarg(6); + .@price=getarg(7); + + .@price=POL_AdjustPrice(.@price); + + mesn; + mesq l("Do you want to craft @@? For that I will need:", getitemlink(.@prize)); + mesc l("@@/@@ @@", countitem(.@base1), .@amon1, getitemlink(.@base1)); + mesc l("@@/@@ @@", countitem(.@base2), .@amon2, getitemlink(.@base2)); + mesc l("@@/@@ @@", countitem(.@base3), .@amon3, getitemlink(.@base3)); + mesc l("@@/@@ GP", format_number(Zeny), fnum(.@price)); + next; + + select + l("Yes"), + l("No"); + + if (@menu == 2) + return; + + if (countitem(.@base1) >= .@amon1 && + countitem(.@base2) >= .@amon2 && + countitem(.@base3) >= .@amon3 && + Zeny >= .@price) { + inventoryplace .@prize, 1; + delitem .@base1, .@amon1; + delitem .@base2, .@amon2; + delitem .@base3, .@amon3; + POL_PlayerMoney(.@price); + //getitem .@prize, 1; + CsysNpcCraft(.@prize, IOPT_CRITDMG, rand2(20, 30), IOPT_DOUBLEATTACK, rand2(5, 15)); + + mes ""; + mesn; + mesq l("Many thanks! Come back soon."); + } else { + speech S_FIRST_BLANK_LINE,// | S_LAST_NEXT, + l("You don't have enough material, sorry."); + } + return; + } + + .@q=getq(HurnscaldQuest_LOFPass); + mesn; + if (is_night()) + mesq l("Good @@. My name is @@ and I make @@.", l("evening"), .name$, l("firearms")); + else + mesq l("Good @@. My name is @@ and I make @@.", l("day"), .name$, l("firearms")); + next; + mesn strcharinfo(0); + mesq l("Firearms? What would that be? @@", "%%4"); + next; + mesn; + mesq l("Oh, just an empty staff with black powder inside it. Which explodes. And then kills monsters."); + tutmes l("%s makes specialized weapons for high level players. If you tweak with Nicholas, in Hurnscald, the weapon options, you can get really powerful.", .name$); + next; + if (.@q == 1) goto L_LOFBOT; + mes ""; + if (BaseLevel < 50) + goto L_TooWeak; + else if (BaseLevel < 60) + goto L_Weak; + goto L_Menu; + +L_TooWeak: + mesn; + mesq l("These weapons are only for masters, so you must get levels before being able to use them."); + close; + +L_Weak: + mesn; + mesq l("You need level 60 to use these guns, but if you want to start collecting materials, you're allowed to."); + next; + goto L_Menu; + +L_Menu: + menu + l("I would like some information"), L_Info, + l("I want a gun!"), L_Craft, + l("Do you have non-ranged weapons?"), L_CraftMelee, + l("I don't want anything right now, bye."), L_Close; + +L_Info: + mes ""; + mesn; + mesq l("There are four basic class:"); + mesc l("SHORT GUNSTAFF");//aka revolver + mesc l("* The only one hand ranged weapon you'll ever find!"); + mes l(" I cut it in half, and to fix shooting speed, I added some extra cogs."); + mes l(" It didn't got too much lighter, and it got really weak compared to a bow."); + mesc l("POWERFUL GUNSTAFF"); + mesc l("* Huge damage and more criticals, but slow fire rate."); + mes l(" The standard gunstaff! It is way stronger than a bow!"); + mes l(" Oh, and it comes with added shooting range, but it is slow as hell to reload."); + mesc l("RAPID GUNSTAFF"); + mesc l("* Low damage, highest attack speed from all."); + mes l(" One have lots of powder to attack non-stop, but oh god that is heavy."); + mes l(" It is also much weaker and less precise than a bow. But it is so fun using..."); + mesc l("EXPLOSIVE GUNSTAFF"); + mesc l("* Causes splash damage, and are very expensive."); + mes l(" You know, I wanted to make the powder explode when hitting!"); + mes l(" It is the same as a bow, including in penalty. But it deals splash damage!"); + next; + mesn; + mesq l("Select carefully which weapon you want, so there are no regrets."); + next; + goto L_Menu; + +L_Craft: + select + l("I changed my mind."), + l("I want a SHORT GUNSTAFF."), + l("I want a POWERFUL GUNSTAFF."), + l("I want a RAPID GUNSTAFF."), + l("I want an EXPLOSIVE GUNSTAFF."), + rif(false, l("I want something more magical.")); + mes ""; + + switch (@menu) { + case 2: + craft_gun( LeadIngot, 4, TitaniumIngot, 5, Coal, 11, PynRevolver, 3000 ); + goto L_Craft; + case 3: + craft_gun( LeadIngot, 6, TitaniumIngot, 7, Coal, 14, PynRifle, 3000 ); + goto L_Craft; + case 4: + craft_gun( LeadIngot, 6, TitaniumIngot, 7, Coal, 14, PynGatling, 4000 ); + goto L_Craft; + case 5: + craft_gun( LeadIngot, 9, TitaniumIngot, 10, Coal, 16, PynShotgun, 6000 ); + goto L_Craft; + case 6: + mesn; + mesq l("I suppose I can make you a %s. It sometimes casts magic skills upon your foes. Well, seldomly, but it also deals more damage than normal wands.", getitemlink(PynScepter)); + next; + craft_gun( LeadIngot, 10, TitaniumIngot, 12, Coal, 24, PynScepter, 24000 ); + goto L_Craft; + } + goto L_Menu; + + +L_CraftMelee: + select + l("I changed my mind."), + l("Which melee you craft?"), + l("I want a PORTABLE LIGHTSABER."), + l("I want a POWERFUL LIGHTSABER."), + rif(getskilllv(TF_STEAL) && false, l("I want a KUNAI.")), + l("I want a WHIP."); + mes ""; + + switch (@menu) { + case 2: + explainMelee(); + goto L_CraftMelee; + case 3: + craft_gun( EverburnPowder, 30, TitaniumIngot, 7, LeadIngot, 4, Lightsaber, 25000 ); + goto L_CraftMelee; + case 4: + craft_gun( EverburnPowder, 30, TitaniumIngot, 7, LeadIngot, 4, PowerfulLightsaber, 25000 ); + goto L_CraftMelee; + case 5: + craft_gun( GoldIngot, 3, HeroCoin, 500, LOFCoin, 4, PynKunai, 25000 ); + goto L_CraftMelee; + case 6: + craft_gun( LOFCoin, 3, TitaniumIngot, 5, LeadIngot, 3, PynWhip, 22000 ); + goto L_CraftMelee; + } + goto L_Menu; + + +L_Close: + closedialog; + goodbye; + close; + +// Takes priority over craft +L_LOFBOT: + select + l("A friend of yours called LOF BOT asked for a coin..."), + l("Sorry, I'm in hurry."); + mes ""; + if (@menu == 2) close; + mesn; + mesq l("Ah, so LOF Bot wants a souvenir after all!"); + next; + mesn; + mesq l("Everyone loves the Land of Fire, it is impossible to not love it."); + next; + inventoryplace LOFCoin, 1; + mesn; + mesq l("Here, please take this to them. Tell them they are welcome here anytime! %%2"); + getitem LOFCoin, 1; + setq HurnscaldQuest_LOFPass, 2; + close; + +function explainMelee { + mesc l("PORTABLE LIGHTSABER"); + mesc l("* Very quick and can be used in a single hand."); + mesc l("POWERFUL LIGHTSABER"); + mesc l("* Very quick, two handed, and evil."); + mes ""; + mes l(" Actually, Lalica cursed one of my firestaves and it got a mind of its own D:"); + mes l(" Then I found out that I could set the staff in flames using Everburn Powder!"); + mes l(" It was quite a challenge, and I still need Lalica to keep curse them."); + mes l(" But the result is a fast weapon which is also powerful."); + mes ""; + next; + mesc l("KUNAI"); + mesc l("* More for bandits than assassins or ninjas."); + mes l(" See, I was lazy and there were only short knives around... So I made a Kunai."); + mes l(" It can steal items or collect drops on the floor randomly. Also improves your evasion."); + mes l(" It is not that amazing, though. I will trade with you for more useful items."); + mes ""; + mesc l("WHIP"); + mesc l("* Has non-cumulative area of effect damage."); + mes l(" A fair all-rounder weapon; Stronger than you would expect and not so clumsy."); + mes l(" But its splash damage does not stack, so be careful when adding options."); + next; + return; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, WarlordHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, GoldenWarlordPlate); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, BromenalPants); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex=G_MALE; + .distance=5; + end; + +} diff --git a/npc/017-4/refine.txt b/npc/017-4/refine.txt new file mode 100644 index 0000000..2bed9ce --- /dev/null +++ b/npc/017-4/refine.txt @@ -0,0 +1,74 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Refine and Repair broken equipment. +017-4,38,35,0 script Fortiun NPC_FORTIUN,{ + showavatar NPC_FORTIUN; + @menu=0; + do + { + refineMaster(); + } while (1); + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + +017-4,23,23,0 script Fusus NPC_FUSUS,{ + showavatar NPC_FUSUS; + select + l("Repair items"), + l("Compact forge stones"); + mes ""; + if (@menu == 2) + goto L_Fusus; + @menu=0; + do + { + repairMaster(); + } while (getbrokencount() >= 0); + +L_Fusus: + inventoryplace Iten, 1; + mesn; + mesq l("I can fuse an @@ and 20 @@, besides @@ GP, into 3~5 @@.", getitemlink(CopperIngot), getitemlink(SilkCocoon), 500, getitemlink(Wurtzite)); + mesc l("5 @@ can be fused in a @@", getitemlink(Wurtzite), getitemlink(Graphene)); + mesc l("5 @@ can be fused in a @@", getitemlink(Graphene), getitemlink(Arcanum)); + select + rif(Zeny >= 500, l("I want the Wurtzite")), + rif(countitem(Wurtzite) >= 5, l("I want Graphene")), + rif(countitem(Graphene) >= 5, l("I want Arcanum")), + l("Do nothing"); + mes ""; + switch (@menu) { + case 4: + close; + case 1: + if (!transcheck(CopperIngot, 1, SilkCocoon, 20)) + close; + Zeny -= 500; + getitem Wurtzite, any(3,3,4,4,4,5); // 3: ~33%. 4: =50%. 5: ~17%. AVG: 3.8 + break; + case 2: + delitem Wurtzite, 5; + getitem Graphene, 1; + break; + case 3: + delitem Graphene, 5; + getitem Arcanum, 1; + break; + } + mesc l("Done!"), 2; + next; + goto L_Fusus; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/017-4/vault.txt b/npc/017-4/vault.txt new file mode 100644 index 0000000..0301190 --- /dev/null +++ b/npc/017-4/vault.txt @@ -0,0 +1,20 @@ +// TMW2/LoF Script. +// Author: +// Jesusalva +// Notes: +// Based on BenB idea. + +017-4,20,41,0 script Vault#0174 NPC_VAULT,{ + LootableVault(2, 5, "0174"); + close; + +OnInit: + .distance=3; + end; + +OnClock0201: +OnClock1418: + $VAULT_0174+=rand2(27,40); + end; +} + diff --git a/npc/017-5/_import.txt b/npc/017-5/_import.txt new file mode 100644 index 0000000..26bd1dd --- /dev/null +++ b/npc/017-5/_import.txt @@ -0,0 +1,6 @@ +// Map 017-5: LoF Weapon Smith & Shop +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-5/_warps.txt", +"npc/017-5/nahrec.txt", +"npc/017-5/silversmith.txt", +"npc/017-5/vault.txt", diff --git a/npc/017-5/_warps.txt b/npc/017-5/_warps.txt new file mode 100644 index 0000000..df29132 --- /dev/null +++ b/npc/017-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-5: LoF Weapon Smith & Shop warps +017-5,23,34,0 warp #017-5_23_34 0,0,017-1,133,156 diff --git a/npc/017-5/nahrec.txt b/npc/017-5/nahrec.txt new file mode 100644 index 0000000..b2b3c71 --- /dev/null +++ b/npc/017-5/nahrec.txt @@ -0,0 +1,219 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// General Purpose Blacksmith +// TODO: Silversmith on 24, 25 + +017-5,44,24,0 script Nahrec NPC_PLAYER,{ + function blacksmith_create; + function NahrecStory; + function NahrecMain; + function NahrecEnd; + function NahrecSavior; + function Mylarin; + + mesn; + mesq l("Almost there! The War Lord will be proud with this new armor I am inventing for him! Just a little more...!"); + next; + + mesn; + mesq l("Ah, hello! It is good to see another traveler in this town!"); + next; + select + l("Really? Where are you from?"), + l("It's good to see you too, do you happen to be able to make stuff?"), + l("Alright."); + mes ""; + if (@menu == 1) + NahrecStory(); + if (@menu == 2) // Changed by option 1, do not use a switch + NahrecMain(); + NahrecEnd(); // changed by option 2, do not use a switch + +function NahrecEnd { + closedialog; + goodbye; + close; +} + +function NahrecStory { + mesn; + mesq l("I came from Thermin, a town far far away."); + next; + mesn; + mesq l("I'm an experienced weapon master helping this smithy here, but I am new in this town. I am best at smithing armor."); + next; + select + l("Good to know."), + l("'Best'? So you can make other things?"); + return; +} + +function NahrecMain { + do + { + mesn; + mesq l("Yes, I can craft many things. In addition to plating and improving armor, I can craft smaller items made of gold and other metals."); + select + rif(!RECIPES_EQUIPMENT[CraftGoldenLightPlatemail], l("Can you improve my Light Platemail for me?")), + rif(!RECIPES_EQUIPMENT[CraftGoldenWarlordPlate], l("Can you improve my Warlord Plate for me?")), + l("Can you craft Chainmail?"), + l("Can you craft Light Platemail?"), + l("Can you craft Warlord Plate?"), + rif(countitem(MylarinDust) && !NAHREC_RECIPE, l("Can you craft Savior Equipment?")), + l("Can you craft Chainmail Skirt?"), + l("Do you know something about 'Mylarin Dust'?"), + l("Nevermind, bye!"); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("For %d GP I'll teach you, do we have a deal?", fnum(30000)); + next; + if (askyesno() == ASK_YES && Zeny > 30000) { + mes ""; + Zeny-=30000; + RECIPES_EQUIPMENT[CraftGoldenLightPlatemail]=true; + mesn; + mesc l("Nahrec discretly gives you a recipe."); + mesc l("\"*psst, just follow what is outlined here to make it golden.*\""); + } + break; + case 2: + mesn; + mesq l("For %d GP I'll teach you, do we have a deal?", fnum(200000)); + next; + if (askyesno() == ASK_YES && Zeny > 200000) { + mes ""; + Zeny-=200000; + RECIPES_EQUIPMENT[CraftGoldenWarlordPlate]=true; + mesn; + mesc l("Nahrec discretly gives you a recipe."); + mesc l("\"*psst, just follow what is outlined here to make it golden.*\""); + } + break; + break; + case 3: + blacksmith_create(IronIngot, 10, SilverIngot, 2, Chainmail, 20000); + break; + case 4: + blacksmith_create(IronIngot, 15, SilverIngot, 5, LightPlatemail, 40000); + break; + case 5: + blacksmith_create(IronIngot, 5, GoldenLightPlatemail, 1, WarlordPlate, 5000); + break; + case 6: + NahrecSavior(); + case 7: + blacksmith_create(IronIngot, 10, SilverIngot, 15, ChainmailSkirt, 35000); + break; + case 8: + Mylarin(); + case 9: + return; + } + } while (true); + return; +} + +function Mylarin { + mesn; + mesq l("Mylarin dust?! Where would you get that? That's amazing!"); + next; + mesq l("It has eluded so many craftsmiths, who knows what can be done with that!"); + next; + mesq l("I can make some stuff with it, but I have no idea where it can be found. Mylarin dust... amazing."); + next; + return; +} + +function NahrecSavior { + if (NAHREC_RECIPE) end; + mesn; + mesq l("Yes, I can craft Savior Pants. But it is not cheap. I'll need Platinum, Iridium, and Mylarin Dust. And gold. Much gold."); + next; + mesn; + mesq l("But you already have Mylarin Dust! That's good, because I no longer craft savior pieces. But. For %s GP I'll give you a %s and if you're lucky, it'll have the recipe you're after.", fnum(50000), getitemlink(SaviorBlueprint)); + mes ""; + if (Zeny < 50000) return; + next; + select + l("Alright, I'll pay."), + l("I'm a bit short in cash right now."); + mes ""; + if (@menu == 2) return; + inventoryplace SaviorBlueprint, 1; + Zeny -= 50000; + NAHREC_RECIPE = true; + getitem SaviorBlueprint, 1; + mesn; + mesq l("Deal. Pleasure doing business with you."); + next; + return; +} + +// blacksmith_create( BaseItem1, Amount, BaseItem2, Amount, PrizeItem, Price ) +function blacksmith_create { + .@base1=getarg(0); + .@amon1=getarg(1); + .@base2=getarg(2); + .@amon2=getarg(3); + .@prize=getarg(4); + .@price=getarg(5); + + .@price=POL_AdjustPrice(.@price); + + mesn; + mesq l("Do you want to craft @@? For that I will need @@ @@, @@ @@ and @@ gp.", + getitemlink(.@prize), .@amon1, getitemlink(.@base1), .@amon2, getitemlink(.@base2), .@price); + + select + l("Yes"), + l("No"); + + if (@menu == 2) + return; + + if (countitem(.@base1) >= .@amon1 && + countitem(.@base2) >= .@amon2 && + Zeny >= .@price) { + inventoryplace .@prize, 1; + delitem .@base1, .@amon1; + delitem .@base2, .@amon2; + POL_PlayerMoney(.@price); + if (.@prize == WarlordPlate) { + .@b=any(IOPT_SPLASHDAMAGE, VAR_ITEMDEFPOWER); + if (.@b == IOPT_SPLASHDAMAGE) + CsysNpcCraft(.@prize, .@b, 1); + else + CsysNpcCraft(.@prize, VAR_ITEMDEFPOWER, rand2(50), VAR_MDEFPOWER, 30); + } else { + CsysNpcCraft(.@prize, IOPT_SCRESIST_POISON, 10, IOPT_SCRESIST_SILENCE, 10, IOPT_SCRESIST_BLIND, 10, IOPT_SCRESIST_CURSE, 10); + } + + mes ""; + mesn; + mesq l("Many thanks! Come back soon."); + } else { + speech S_FIRST_BLANK_LINE,// | S_LAST_NEXT, + l("You don't have enough material, sorry."); + } + return; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, WarlordPlate); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, SaviorPants); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex=G_MALE; + .distance=5; + end; + +} diff --git a/npc/017-5/silversmith.txt b/npc/017-5/silversmith.txt new file mode 100644 index 0000000..6255ee6 --- /dev/null +++ b/npc/017-5/silversmith.txt @@ -0,0 +1,91 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Crafts silver objects + +017-5,24,25,0 script Silversmith NPC_SILVERSMITH,{ + showavatar NPC_SILVERSMITH; + goto L_Menu; + + // blacksmith_create( BaseItem1, Amount, BaseItem2, Amount, PrizeItem, Price ) + function blacksmith_create { + .@base1=getarg(0); + .@amon1=getarg(1); + .@base2=getarg(2); + .@amon2=getarg(3); + .@prize=getarg(4); + .@price=getarg(5); + + .@price=POL_AdjustPrice(.@price); + + mesn; + mesq l("Do you want to craft @@? For that I will need:", getitemlink(.@prize)); + mesc l("@@/@@ @@", countitem(.@base1), .@amon1, getitemlink(.@base1)); + mesc l("@@/@@ @@", countitem(.@base2), .@amon2, getitemlink(.@base2)); + mesc l("@@/@@ GP", format_number(Zeny), format_number(.@price)); + + select + l("Yes"), + l("No"); + + if (@menu == 2) + return; + + if (countitem(.@base1) >= .@amon1 && + countitem(.@base2) >= .@amon2 && + Zeny >= .@price) { + inventoryplace .@prize, 1; + delitem .@base1, .@amon1; + delitem .@base2, .@amon2; + POL_PlayerMoney(.@price); + getitem .@prize, 1; + .@xp=getiteminfo(.@base1, ITEMINFO_SELLPRICE)*.@amon1+getiteminfo(.@base2, ITEMINFO_SELLPRICE)*.@amon2; + .@xp=.@xp*2/3; + getexp .@xp, rand(1,10); + + mes ""; + mesn; + mesq l("Many thanks! Come back soon."); + } else { + speech S_FIRST_BLANK_LINE,// | S_LAST_NEXT, + l("You don't have enough material, sorry."); + } + return; + } + + +L_Menu: + mesn l("Smith Silvers"); + mesq l("Hello, I am your local silversmith, here for all of your smithing needs!"); + next; + select + l("Nothing, sorry!"), + l("I'd like my Crozenite Clover Silvered."), + l("Silver Ring!"), + l("Miere Cleaver!"), + l("Broadsword!"); + + switch (@menu) { + case 2: + blacksmith_create(SilverIngot, 3, CrozeniteFourLeafAmulet, 1, SilverFourLeafAmulet, 500); + break; + case 3: + blacksmith_create(SilverIngot, 4, TinIngot, 2, SilverRing, 1000); + break; + case 4: + blacksmith_create(SilverIngot, 12, Coal, 8, MiereCleaver, 8000); + break; + case 5: + blacksmith_create(SilverIngot, 27, Coal, 20, Broadsword, 15000); + break; + } + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/017-5/vault.txt b/npc/017-5/vault.txt new file mode 100644 index 0000000..2b85756 --- /dev/null +++ b/npc/017-5/vault.txt @@ -0,0 +1,20 @@ +// TMW2/LoF Script. +// Author: +// Jesusalva +// Notes: +// Based on BenB idea. + +017-5,53,26,0 script Vault#0175 NPC_NO_SPRITE,{ + LootableVault(1, 5, "0175"); + close; + +OnInit: + .distance=3; + end; + +OnClock0202: +OnClock1419: + $VAULT_0175+=rand2(21,36); + end; +} + diff --git a/npc/017-6/_import.txt b/npc/017-6/_import.txt new file mode 100644 index 0000000..197e95a --- /dev/null +++ b/npc/017-6/_import.txt @@ -0,0 +1,5 @@ +// Map 017-6: LoF Alchemy Shop +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-6/_warps.txt", +"npc/017-6/axzell.txt", +"npc/017-6/sheila.txt", diff --git a/npc/017-6/_warps.txt b/npc/017-6/_warps.txt new file mode 100644 index 0000000..5d600c7 --- /dev/null +++ b/npc/017-6/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-6: LoF Alchemy Shop warps +017-6,30,44,0 warp #017-6_30_44 0,1,017-1,38,139 +017-6,30,32,0 warp #017-6_30_32 0,1,017-1,38,131 +017-6,30,53,0 warp #017-6_30_53 0,1,017-1,38,154 diff --git a/npc/017-6/axzell.txt b/npc/017-6/axzell.txt new file mode 100644 index 0000000..4148d53 --- /dev/null +++ b/npc/017-6/axzell.txt @@ -0,0 +1,173 @@ +// TMW2/LoF scripts. +// Authors: +// Jesusalva +// Dye Quest added by: +// Povo +// Description: +// Status Reset. Formely named Bobby + +017-6,25,30,0 script Axzell the Alchemist NPC_PLAYER,{ + + speech S_LAST_NEXT, + l("I am @@, an alchemist specialized in reset potions.", .name$); + +L_Menu: + .@plush_count = BaseLevel*190-(9*210); + // Lv 10: 10 GP + // Lv 90: 1.690 GP + if (BaseLevel > 10) + .@plush_count = .@plush_count/(BaseLevel/10); + + select + l("Can you reset my stats please?"), + l("Hey, nice hat! Can I have it?"), + l("Do you make anything else?"), + lg("You are weird, I have to go sorry."); + + mes ""; + switch (@menu) { + case 1: + goto L_ResetStats; + case 2: + mesn; + mesq l("Maybe."); + next; + mesn strcharinfo(0); + mes l("*_* \"Pretty please?\""); + next; + mesn; + mesq l("Well, I and my brother could make another one, but they are being checked for safety."); + next; + mesn; + mesq l("After all, what would you do if it open a black hole on your head by accident? %%4"); + next; + mesn strcharinfo(0); + mesq l("Welp, no, please!"); + next; + mesn; + mesq l("%%@ Then wait until Saulc says they are safe!"); + next; + goto L_Menu; + case 3: + goto L_Dye; + case 4: + goto L_Quit; + } + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + ConfirmStatusReset(); + goto L_Quit; + +L_Dye: + mesn; + mesq l("Since red is the official color of Land of Fire Village, I craft dye for players who will properly represent our town.", + getitemlink(RedDye)); + if (BaseLevel < 50) { + mesn; + mesq l("Right now you are too weak."); + mesq l(" What would people think of our village if they come accross a defeated noob wearing our proud colors!"); + next; + mesq l("Come back when you are at a higher level."); + next; + goto L_Menu; + } + next; + mesq l("Bring me %d %s, %d %s, %d %s, and %d %s, and I will make it for you. I also charge %d GP commission.", + 1, getitemlink(BottleOfSewerWater), + 200, getitemlink(AlizarinHerb), + 20, getitemlink(Coral), + 10, getitemlink(SulfurPowder), 3500); + mesq l("Do you want one?"); + compareandsetq LoFQuest_Axzell, 0, 1; + next; + select + l("Yeah, I need one."), + l("Thanks for the help, but no."), + l("Actually, nevermind. Good bye!"); + mes ""; + if (@menu == 2) + goto L_Menu; + + if (@menu == 3) + goto L_Quit; + + L_DyeLoop: + if (countitem(BottleOfSewerWater) >= 1 && + countitem(AlizarinHerb) >= 200 && + countitem(Coral) >= 20 && + countitem(SulfurPowder) >= 10 && + Zeny >= 3500) { + inventoryplace RedDye, 1, EmptyBottle, 1; + delitem BottleOfSewerWater, 1; + delitem AlizarinHerb, 200; + delitem Coral, 20; + delitem SulfurPowder, 10; + Zeny-=3500; + getitem RedDye, 1; + getitem EmptyBottle, 1; + if (getq(LoFQuest_Axzell) == 1) { + setq LoFQuest_Axzell, 2; + getexp 15000, 0; + } + mesn; + mesq l("Thanks! Here you go. Perhaps you want another one?"); + next; + if (askyesno() == ASK_YES) + goto L_DyeLoop; + } else { + mesn; + mesq l("Sorry, you don't seem to have everything I need."); + } + close; + +L_Quit: + closedialog; + goodbye; + close; + +OnTimer1000: + domovestep; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, ChemistHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, AlchemistArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 14); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + initpath "move", 25, 30, + "dir", DOWN, 0, + "wait", 15, 0, + "move", 29, 21, + "dir", LEFT, 0, + "wait", 5, 0, + "dir", DOWN, 0, + "wait", 15, 0, + "move", 28, 34, + "dir", RIGHT, 0, + "wait", 15, 0, + "move", 22, 37, + "dir", RIGHT, 0, + "wait", 30, 0, + "move", 22, 48, + "dir", UP, 0, + "wait", 15, 0, + "move", 28, 62, + "dir", UP, 0, + "wait", 15, 0, + "move", 29, 47, + "dir", UP, 0, + "wait", 15, 0; + initialmove; + initnpctimer; + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/017-6/sheila.txt b/npc/017-6/sheila.txt new file mode 100644 index 0000000..9c01e11 --- /dev/null +++ b/npc/017-6/sheila.txt @@ -0,0 +1,47 @@ +// TMW2/LoF scripts. +// Authors: +// Jesusalva +// Description: +// Makes the Time Potion Of Ozthokk + +017-6,27,48,0 script Sheila the Clockmaker NPC_SHEILA,{ + showavatar NPC_SHEILA; + mesn; + mesq l("Ah, hello... is it @@? Nice to meet you!", strcharinfo(0)); + next; + mesn; + mesq l("I am @@, student from the ancient secrets of the Mage of Time, Ozthokk.", .name$); + next; + mesn; + mesq l("My greatest realization thus far, besides some time rewinding clocks, is the @@!", getitemlink(TimeFlask)); + next; + mesn; + mesq l("I'm not sure what it needs to move on time, but I managed to make it move in space, back to this village, in no time!"); + mesq l("Well, maybe it takes a few hours or days of your life, but who cares! It is instant teleport!"); + next; + mesn; + mesq l("If you bring me a @@ and 500 GP I can make one for you!", getitemlink(DragonScales)); + if (!countitem(DragonScales) || Zeny < 500) + close; + select + l("Nice, but no, thanks."), + l("Pretty cool! I am interested!"); + mes ""; + if (@menu == 1) + close; + inventoryplace TimeFlask, 1; + delitem DragonScales, 1; + Zeny=Zeny-500; + getitem TimeFlask, 1; + mesn; + mesq l("Neat, neat! Thanks! Here you go, this amazing bottle! It is life-bond, so having multiple won't reduce the time you need to wait..."); + next; + mesn; + mesq l("Do not open. Do not attempt to eat. Be careful with it, to don't cause any time paradoxes. I also heard a NPC may be interested on that, by the way!"); + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/017-7/_import.txt b/npc/017-7/_import.txt new file mode 100644 index 0000000..65d2ab7 --- /dev/null +++ b/npc/017-7/_import.txt @@ -0,0 +1,5 @@ +// Map 017-7: Real Estate +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-7/_warps.txt", +"npc/017-7/doorbell.txt", +"npc/017-7/utils.txt", diff --git a/npc/017-7/_warps.txt b/npc/017-7/_warps.txt new file mode 100644 index 0000000..a47e31a --- /dev/null +++ b/npc/017-7/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-7: Real Estate warps +017-7,34,34,0 warp #017-7_34_34 1,0,017-1,85,176 diff --git a/npc/017-7/doorbell.txt b/npc/017-7/doorbell.txt new file mode 100644 index 0000000..1be18ad --- /dev/null +++ b/npc/017-7/doorbell.txt @@ -0,0 +1,335 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Doorbell allows you to purchase mobilia, besides loading it when server starts +// Each layer can have 32 different furniture pieces because bitmask limit. +// This file is custom to every room + +// ID: 2 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +017-7,32,34,0 script Doorbell#RES_0177 NPC_NO_SPRITE,{ + // Name, Layer, Price, ID, x1, y1, x2, y2, + function create_object { + array_push(.nams$, getarg(0)); + array_push(.layer, getarg(1)); + array_push(.price, getarg(2)); + array_push(.objid, getarg(3)); + array_push(.x1, getarg(4)); + array_push(.y1, getarg(5)); + array_push(.x2, getarg(6)); + array_push(.y2, getarg(7)); + return; + } + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + close; + +// When using setcells() a player could get trapped! +// This label will slide the player back to entrance, which should be a safe spot +OnSlide: + slide 33, 33; + end; + +// If someone press the doorbell from outside and doorbell is enabled +OnDoorbell: + if ($ESTATE_DOORBELL[.id]) + end; + + if (.dpost < gettimetick(2)) { + npctalk (strcharinfo(0)+" is pressing the doorbell."); // We actually don't want l() + } + .dpost=gettimetick(2)+.delay; + end; + +// Managment Menu +L_Manage: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Managment Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Rent time available: @@", FuzzyTime($ESTATE_RENTTIME[.id])); + mesc l("Total Credits and GP: @@", format_number(.@gp)); + mes ""; + mesc l("Room password: @@", $ESTATE_PASSWORD$[.id]); + if ($ESTATE_DOORBELL[.id]) + mesc l("Doorbell is disabled"), 1; + + next; + select + l("Leave"), + l("Enable/disable doorbell"), + l("Manage Furniture"), + l("Set room password"); + + switch (@menu) { + case 1: + close; + break; + case 2: + $ESTATE_DOORBELL[.id]=!$ESTATE_DOORBELL[.id]; + break; + case 3: + goto L_Furniture; + break; + case 4: + mesc l("(Leave the password blank to disable)"); + mesc l("Current Room password: @@", $ESTATE_PASSWORD$[.id]); + mesc l("Input new password: "); + input .@password$; + mesc l("Repeat new password: "); + input .@passwordc$; + if (.@password$ == .@passwordc$) { + $ESTATE_PASSWORD$[.id]=.@password$; + mesc l("Password changed with success!"), 3; + } else { + mesc l("The passwords doesn't match."), 1; + } + break; + } + goto L_Manage; + +L_Furniture: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Furniture Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Total Credits and GP: @@", format_number(.@gp)); + + next; + select + l("Finish"), + l("Manage Beds"), + l("Manage Utilities"), + l("Manage Luxury furniture"), + l("Manage Decoration"), + l("Manage Chairs"), + l("Manage Paintings"); + mes ""; + + switch (@menu) { + case 1: + goto L_Manage; + break; + case 2: + mesc ".:: "+ l("Beds") + " ::.", 3; + @re_col=RES_OBJECTS; + break; + case 3: + mesc ".:: "+ l("Utilities") + " ::.", 3; + @re_col=RES_UTILITIES; + break; + case 4: + mesc ".:: "+ l("Luxury furniture") + " ::.", 3; + @re_col=RES_LUXURY; + break; + case 5: + mesc ".:: "+ l("Decoration") + " ::.", 3; + @re_col=RES_DECORATION; + break; + case 6: + mesc ".:: "+ l("Chairs") + " ::.", 3; + @re_col=RES_SITTABLE; + break; + case 7: + mesc ".:: "+ l("Paintings") + " ::.", 3; + @re_col=RES_WALLDECORATION; + break; + } + +// L_ContinuousLoop +// Requires the following variables: +// @re_col +// Target Collision ID +L_ContinuousLoop: + deletearray @valid_ids; + + // Create a second array (@valid_ids) with the ID of objects within @re_col group + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + //debugmes "Found object ID %d named %s on layer %s coords (%d,%d) - Looking for layer %d", .@i, .nams$[.@i], .layer[.@i], .x1[.@i], .y1[.@i], @re_col; + if (.layer[.@i] == @re_col) + array_push(@valid_ids, .@i); + } + //debugmes "Found %d valid objects", getarraysize(@valid_ids); + + // Create the menu with @valid_ids - Check if you already have the item to decide if you're buying or selling + @menuentries$="Finish:"; + for (.@j=0; .@j < getarraysize(@valid_ids); .@j++) { + .@i=@valid_ids[.@j]; + if (realestate_hasmobilia(.id, .layer[.@i], .objid[.@i])) + @menuentries$+=l("Sell ")+.nams$[.@i]+l(" for ") + format_number( realestate_sellprice(.id,.price[.@i]) ) +":"; + else + @menuentries$+=l("Purchase ")+.nams$[.@i]+(" for ") + format_number( .price[.@i] )+":"; + } + select (@menuentries$); + mes ""; + + // First option to return to previous menu + if (@menu == 1) + goto L_Furniture; + + // Otherwise, we know then that (@menu-2) is the ID in @valid_ids + // So we save .@id with the correct ID in object arrays. + // We also calculate how much aggregated money you have. + .@id=@valid_ids[@menu-2]; + .@gp=REAL_ESTATE_CREDITS+Zeny; + + if (realestate_hasmobilia(.id, .layer[.@id], .objid[.@id])) { + // If you have the mobilia, you're selling it for Mobiliary Credits + delcells realestate_cellname(.id, .@id); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0177"); + REAL_ESTATE_CREDITS+=realestate_sellprice(.id,.price[.@i]); + mesc l("Sale successful!"); + next; + } else { + // Else, you're buying it, so we must check if you have the moolah first + .@price=.price[.@id]; + if (.@gp > .@price) { + realestate_payment(.@price); + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + areatimer(.mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], 10, "::OnSlide"); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0177"); + mesc l("Purchase successful!"); + next; + } else { + mesc l("Not enough funds!"); + next; + } + } + + // This loops forever + goto L_ContinuousLoop; + + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=2; // Estate ID + .delay=15; // Forced wait between rings + .dpost=0; // Last doorbell ring + .mapa$="017-7"; + + // Arrays + // We go element by element on the array building the menu + .nams$=""; + .layer=0; + .price=0; + .objid=0; + .x1=0; + .y1=0; + .x2=0; + .y2=0; + + // Furniture Settings + // Name, Collision Layer, Price, ID, x1, y1, x2, y2 + // For Collision Layer, see constants.conf ("Real Estate Collisions") + create_object("Placeholder" ,99,999999,99999, 99, 99, 99, 99); + + create_object("Bed 01" , 5, 5000, 1, 24, 24, 25, 27); + create_object("Bed 02" , 5, 5000, 2, 26, 24, 27, 27); + create_object("Bed 03" , 5, 5000, 4, 28, 24, 29, 27); + create_object("Bed 04" , 5, 5000, 8, 30, 24, 31, 27); + create_object("Bed 05" , 5, 5000, 16, 24, 29, 25, 32); + create_object("Bed 06" , 5, 5000, 32, 26, 29, 27, 32); + create_object("Bed 07" , 5, 5000, 64, 28, 29, 29, 32); + create_object("Bed 08" , 5, 5000, 128, 30, 29, 31, 32); + + create_object("Wardrobe" , 1, 7000, 1, 21, 23, 22, 23); + create_object("Cauldron" , 1, 5000, 2, 28, 24, 29, 24); + create_object("Shelf 01" , 1, 2000, 4, 25, 23, 25, 23); + create_object("Shelf 02" , 1, 2000, 8, 26, 23, 26, 23); + create_object("Shelf 03" , 1, 2000, 16, 27, 23, 27, 23); + create_object("Shelf 04" , 1, 2000, 32, 30, 23, 30, 23); + create_object("Shelf 05" , 1, 2000, 64, 31, 23, 31, 23); + create_object("Shelf 06" , 1, 2000, 128, 32, 23, 32, 23); + create_object("Shelf 07" , 1, 2000, 256, 33, 23, 33, 23); + create_object("Shelf 08" , 1, 2000, 512, 34, 23, 34, 23); + create_object("Shelf 09" , 1, 2000, 1024, 35, 23, 35, 23); + create_object("Shelf 10" , 1, 2000, 2048, 36, 23, 36, 23); + create_object("Shelf 11" , 1, 2000, 4096, 37, 23, 37, 23); + create_object("Shelf 12" , 1, 2000, 8192, 38, 23, 38, 23); + + create_object("Piano" , 3, 10000, 1, 33, 25, 35, 25); + + create_object("Left Desk" , 2, 5000, 1, 20, 25, 22, 27); + create_object("Right Desk" , 2, 5000, 2, 36, 30, 38, 32); + + create_object("Left Chair" , 4, 2000, 1, 21, 28, 21, 28); + create_object("Right Chair" , 4, 2000, 2, 37, 29, 37, 29); + + create_object("Painting 01" , 6, 3000, 1, 21, 20, 21, 20); + create_object("Painting 02" , 6, 3000, 2, 23, 21, 23, 21); + create_object("Painting 03" , 6, 3000, 4, 25, 20, 25, 20); + create_object("Painting 04" , 6, 3000, 8, 28, 21, 28, 21); + create_object("Painting 05" , 6, 3000, 16, 31, 20, 31, 20); + create_object("Painting 06" , 6, 3000, 32, 36, 20, 36, 20); + + // Load Mobilia already existing + //debugmes "[REAL ESTATE] Now loading mobilia"; + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + switch (.layer[.@i]) { + case 1: + if ($ESTATE_MOBILIA_64[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 2: + if ($ESTATE_MOBILIA_4[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 3: + if ($ESTATE_MOBILIA_8[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 4: + if ($ESTATE_MOBILIA_32[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 5: + if ($ESTATE_MOBILIA_128[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 6: + if ($ESTATE_MOBILIA_2[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + default: + // We do nothing by default + //debugmes("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", .@i, .layer[.@i]); + break; + } + } + //debugmes "Found %d valid objects", getarraysize(.valid_ids); + for (.@j=0; .@j < getarraysize(.valid_ids); .@j++) { + .@id=.valid_ids[.@j]; + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + //debugmes "Creating %s in %s", realestate_cellname(.id, .@id), .mapa$; + } + deletearray .valid_ids; + // Load NPCs + donpcevent "NPCs#RES_0177::OnReload"; + end; + +} + + diff --git a/npc/017-7/utils.txt b/npc/017-7/utils.txt new file mode 100644 index 0000000..28f69e2 --- /dev/null +++ b/npc/017-7/utils.txt @@ -0,0 +1,74 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Utils take care of NPCs - Their code, and enable/disable using check_cell +// This file is custom to every room + +// ID: 2 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller for rent system +// Doorbell is the main controller for indoor +// This is the NPC script controller +017-7,0,0,0 script NPCs#RES_0177 NPC_HIDDEN,{ + // load_npc ( name , map, x , y{, cell} ) + function load_npc { + if (checknpccell(getarg(1), getarg(2), getarg(3), getarg(4, cell_chknopass))) { + enablenpc getarg(0); + //debugmes "ENABLING NPC %s", getarg(0); + } else { + disablenpc getarg(0); + //debugmes "Disabling NPC %s", getarg(0); + } + + /* + debugmes "----- %s (%d,%d) cell report", getarg(1), getarg(2), getarg(3); + debugmes "cell_chknopass: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknopass); + debugmes "cell_chknoreach: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknoreach); + debugmes "cell_chkbasilica: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkbasilica); + debugmes ""; + debugmes "cell_chkwater: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwater); + debugmes "cell_chkwall: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwall); + debugmes "cell_chkcliff: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkcliff); + debugmes "----- Npc Id: %s", getarg(0); + */ + return; + } + end; + +OnInit: + // Estate Settings + .id=2; // Estate ID + .mapa$="017-7"; // Map name + + // NPC Settings + .sex = G_OTHER; + .distance = 3; + end; + +// Load or unload accordingly +OnReload: + //debugmes "[REAL ESTATE] NPC ONRELOAD"; + // load_npc ( name , map, x , y{, cell} ) + load_npc("Wardrobe#RES_0177", .mapa$, 21, 23); + load_npc("Cauldron#RES_0177", .mapa$, 28, 24); + load_npc("Piano#RES_0177" , .mapa$, 34, 25); + end; + +} + diff --git a/npc/017-8/_import.txt b/npc/017-8/_import.txt new file mode 100644 index 0000000..556bcbe --- /dev/null +++ b/npc/017-8/_import.txt @@ -0,0 +1,5 @@ +// Map 017-8: Real Estate +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/017-8/_warps.txt", +"npc/017-8/doorbell.txt", +"npc/017-8/utils.txt", diff --git a/npc/017-8/_warps.txt b/npc/017-8/_warps.txt new file mode 100644 index 0000000..50279ce --- /dev/null +++ b/npc/017-8/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 017-8: Real Estate warps +017-8,34,34,0 warp #017-8_34_34 1,0,017-1,139,21 diff --git a/npc/017-8/doorbell.txt b/npc/017-8/doorbell.txt new file mode 100644 index 0000000..cc26c46 --- /dev/null +++ b/npc/017-8/doorbell.txt @@ -0,0 +1,335 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Doorbell allows you to purchase mobilia, besides loading it when server starts +// Each layer can have 32 different furniture pieces because bitmask limit. +// This file is custom to every room + +// ID: 3 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +017-8,32,34,0 script Doorbell#RES_0178 NPC_NO_SPRITE,{ + // Name, Layer, Price, ID, x1, y1, x2, y2, + function create_object { + array_push(.nams$, getarg(0)); + array_push(.layer, getarg(1)); + array_push(.price, getarg(2)); + array_push(.objid, getarg(3)); + array_push(.x1, getarg(4)); + array_push(.y1, getarg(5)); + array_push(.x2, getarg(6)); + array_push(.y2, getarg(7)); + return; + } + + if ($ESTATE_OWNER[.id] == getcharid(3)) + goto L_Manage; + + mesc l("This estate currently belongs to @@.", $ESTATE_OWNERNAME$[.id]); + close; + +// When using setcells() a player could get trapped! +// This label will slide the player back to entrance, which should be a safe spot +OnSlide: + slide 33, 33; + end; + +// If someone press the doorbell from outside and doorbell is enabled +OnDoorbell: + if ($ESTATE_DOORBELL[.id]) + end; + + if (.dpost < gettimetick(2)) { + npctalk (strcharinfo(0)+" is pressing the doorbell."); // We actually don't want l() + } + .dpost=gettimetick(2)+.delay; + end; + +// Managment Menu +L_Manage: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Managment Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Rent time available: @@", FuzzyTime($ESTATE_RENTTIME[.id])); + mesc l("Total Credits and GP: @@", format_number(.@gp)); + mes ""; + mesc l("Room password: @@", $ESTATE_PASSWORD$[.id]); + if ($ESTATE_DOORBELL[.id]) + mesc l("Doorbell is disabled"), 1; + + next; + select + l("Leave"), + l("Enable/disable doorbell"), + l("Manage Furniture"), + l("Set room password"); + + switch (@menu) { + case 1: + close; + break; + case 2: + $ESTATE_DOORBELL[.id]=!$ESTATE_DOORBELL[.id]; + break; + case 3: + goto L_Furniture; + break; + case 4: + mesc l("(Leave the password blank to disable)"); + mesc l("Current Room password: @@", $ESTATE_PASSWORD$[.id]); + mesc l("Input new password: "); + input .@password$; + mesc l("Repeat new password: "); + input .@passwordc$; + if (.@password$ == .@passwordc$) { + $ESTATE_PASSWORD$[.id]=.@password$; + mesc l("Password changed with success!"), 3; + } else { + mesc l("The passwords doesn't match."), 1; + } + break; + } + goto L_Manage; + +L_Furniture: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Furniture Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Total Credits and GP: @@", format_number(.@gp)); + + next; + select + l("Finish"), + l("Manage Beds"), + l("Manage Utilities"), + l("Manage Luxury furniture"), + l("Manage Decoration"), + l("Manage Chairs"), + l("Manage Paintings"); + mes ""; + + switch (@menu) { + case 1: + goto L_Manage; + break; + case 2: + mesc ".:: "+ l("Beds") + " ::.", 3; + @re_col=RES_OBJECTS; + break; + case 3: + mesc ".:: "+ l("Utilities") + " ::.", 3; + @re_col=RES_UTILITIES; + break; + case 4: + mesc ".:: "+ l("Luxury furniture") + " ::.", 3; + @re_col=RES_LUXURY; + break; + case 5: + mesc ".:: "+ l("Decoration") + " ::.", 3; + @re_col=RES_DECORATION; + break; + case 6: + mesc ".:: "+ l("Chairs") + " ::.", 3; + @re_col=RES_SITTABLE; + break; + case 7: + mesc ".:: "+ l("Paintings") + " ::.", 3; + @re_col=RES_WALLDECORATION; + break; + } + +// L_ContinuousLoop +// Requires the following variables: +// @re_col +// Target Collision ID +L_ContinuousLoop: + deletearray @valid_ids; + + // Create a second array (@valid_ids) with the ID of objects within @re_col group + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + //debugmes "Found object ID %d named %s on layer %s coords (%d,%d) - Looking for layer %d", .@i, .nams$[.@i], .layer[.@i], .x1[.@i], .y1[.@i], @re_col; + if (.layer[.@i] == @re_col) + array_push(@valid_ids, .@i); + } + //debugmes "Found %d valid objects", getarraysize(@valid_ids); + + // Create the menu with @valid_ids - Check if you already have the item to decide if you're buying or selling + @menuentries$="Finish:"; + for (.@j=0; .@j < getarraysize(@valid_ids); .@j++) { + .@i=@valid_ids[.@j]; + if (realestate_hasmobilia(.id, .layer[.@i], .objid[.@i])) + @menuentries$+=l("Sell ")+.nams$[.@i]+l(" for ") + format_number( realestate_sellprice(.id,.price[.@i]) ) +":"; + else + @menuentries$+=l("Purchase ")+.nams$[.@i]+(" for ") + format_number( .price[.@i] )+":"; + } + select (@menuentries$); + mes ""; + + // First option to return to previous menu + if (@menu == 1) + goto L_Furniture; + + // Otherwise, we know then that (@menu-2) is the ID in @valid_ids + // So we save .@id with the correct ID in object arrays. + // We also calculate how much aggregated money you have. + .@id=@valid_ids[@menu-2]; + .@gp=REAL_ESTATE_CREDITS+Zeny; + + if (realestate_hasmobilia(.id, .layer[.@id], .objid[.@id])) { + // If you have the mobilia, you're selling it for Mobiliary Credits + delcells realestate_cellname(.id, .@id); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0178"); + REAL_ESTATE_CREDITS+=realestate_sellprice(.id,.price[.@i]); + mesc l("Sale successful!"); + next; + } else { + // Else, you're buying it, so we must check if you have the moolah first + .@price=.price[.@id]; + if (.@gp > .@price) { + realestate_payment(.@price); + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + areatimer(.mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], 10, "::OnSlide"); + realestate_togglemobilia(.id, .layer[.@id], .objid[.@id], "NPCs#RES_0178"); + mesc l("Purchase successful!"); + next; + } else { + mesc l("Not enough funds!"); + next; + } + } + + // This loops forever + goto L_ContinuousLoop; + + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Estate Settings + .id=3; // Estate ID + .delay=15; // Forced wait between rings + .dpost=0; // Last doorbell ring + .mapa$="017-8"; + + // Arrays + // We go element by element on the array building the menu + .nams$=""; + .layer=0; + .price=0; + .objid=0; + .x1=0; + .y1=0; + .x2=0; + .y2=0; + + // Furniture Settings + // Name, Collision Layer, Price, ID, x1, y1, x2, y2 + // For Collision Layer, see constants.conf ("Real Estate Collisions") + create_object("Placeholder" ,99,999999,99999, 99, 99, 99, 99); + + create_object("Bed 01" , 5, 5000, 1, 24, 24, 25, 27); + create_object("Bed 02" , 5, 5000, 2, 26, 24, 27, 27); + create_object("Bed 03" , 5, 5000, 4, 28, 24, 29, 27); + create_object("Bed 04" , 5, 5000, 8, 30, 24, 31, 27); + create_object("Bed 05" , 5, 5000, 16, 24, 29, 25, 32); + create_object("Bed 06" , 5, 5000, 32, 26, 29, 27, 32); + create_object("Bed 07" , 5, 5000, 64, 28, 29, 29, 32); + create_object("Bed 08" , 5, 5000, 128, 30, 29, 31, 32); + + create_object("Wardrobe" , 1, 7000, 1, 21, 23, 22, 23); + create_object("Cauldron" , 1, 5000, 2, 28, 24, 29, 24); + create_object("Shelf 01" , 1, 2000, 4, 25, 23, 25, 23); + create_object("Shelf 02" , 1, 2000, 8, 26, 23, 26, 23); + create_object("Shelf 03" , 1, 2000, 16, 27, 23, 27, 23); + create_object("Shelf 04" , 1, 2000, 32, 30, 23, 30, 23); + create_object("Shelf 05" , 1, 2000, 64, 31, 23, 31, 23); + create_object("Shelf 06" , 1, 2000, 128, 32, 23, 32, 23); + create_object("Shelf 07" , 1, 2000, 256, 33, 23, 33, 23); + create_object("Shelf 08" , 1, 2000, 512, 34, 23, 34, 23); + create_object("Shelf 09" , 1, 2000, 1024, 35, 23, 35, 23); + create_object("Shelf 10" , 1, 2000, 2048, 36, 23, 36, 23); + create_object("Shelf 11" , 1, 2000, 4096, 37, 23, 37, 23); + create_object("Shelf 12" , 1, 2000, 8192, 38, 23, 38, 23); + + create_object("Piano" , 3, 10000, 1, 33, 25, 35, 25); + + create_object("Left Desk" , 2, 5000, 1, 20, 25, 22, 27); + create_object("Right Desk" , 2, 5000, 2, 36, 30, 38, 32); + + create_object("Left Chair" , 4, 2000, 1, 21, 28, 21, 28); + create_object("Right Chair" , 4, 2000, 2, 37, 29, 37, 29); + + create_object("Painting 01" , 6, 3000, 1, 21, 20, 21, 20); + create_object("Painting 02" , 6, 3000, 2, 23, 21, 23, 21); + create_object("Painting 03" , 6, 3000, 4, 25, 20, 25, 20); + create_object("Painting 04" , 6, 3000, 8, 28, 21, 28, 21); + create_object("Painting 05" , 6, 3000, 16, 31, 20, 31, 20); + create_object("Painting 06" , 6, 3000, 32, 36, 20, 36, 20); + + // Load Mobilia already existing + //debugmes "[REAL ESTATE] Now loading mobilia"; + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + switch (.layer[.@i]) { + case 1: + if ($ESTATE_MOBILIA_64[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 2: + if ($ESTATE_MOBILIA_4[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 3: + if ($ESTATE_MOBILIA_8[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 4: + if ($ESTATE_MOBILIA_32[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 5: + if ($ESTATE_MOBILIA_128[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 6: + if ($ESTATE_MOBILIA_2[.id] & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + default: + // We do nothing by default + //debugmes("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", .@i, .layer[.@i]); + break; + } + } + //debugmes "Found %d valid objects", getarraysize(.valid_ids); + for (.@j=0; .@j < getarraysize(.valid_ids); .@j++) { + .@id=.valid_ids[.@j]; + setcells .mapa$, .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + //debugmes "Creating %s in %s", realestate_cellname(.id, .@id), .mapa$; + } + deletearray .valid_ids; + // Load NPCs + donpcevent "NPCs#RES_0178::OnReload"; + end; + +} + + diff --git a/npc/017-8/utils.txt b/npc/017-8/utils.txt new file mode 100644 index 0000000..7016113 --- /dev/null +++ b/npc/017-8/utils.txt @@ -0,0 +1,74 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Utils take care of NPCs - Their code, and enable/disable using check_cell +// This file is custom to every room + +// ID: 3 +// $ESTATE_OWNER[.id] → Account ID owner of the Real Estate +// $ESTATE_OWNERNAME$[.id] → Human readable name of Real Estate owner +// $ESTATE_RENTTIME[.id] → When the rent will expire +// $ESTATE_MOBILIA_2[.id] → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// $ESTATE_MOBILIA_4[.id] → Bitmask of mobilia currently purchased on Air Collision (2) +// $ESTATE_MOBILIA_8[.id] → Bitmask of mobilia currently purchased on Water Collision (3) +// $ESTATE_MOBILIA_32[.id] → Bitmask of mobilia currently purchased on Yellow Collision (4) +// $ESTATE_MOBILIA_64[.id] → Bitmask of mobilia currently purchased on Player Collision (5) +// $ESTATE_MOBILIA_128[.id] → Bitmask of mobilia currently purchased on Normal Collision (1) +// $ESTATE_PASSWORD$[.id] → Password to enter the estate. If it is "", then no password required +// Note: GMs and Administrators can always use super password "mouboo" to enter a locked estate +// $ESTATE_DOORBELL[.id] → If doorbell is disabled (enabled by default) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller for rent system +// Doorbell is the main controller for indoor +// This is the NPC script controller +017-8,0,0,0 script NPCs#RES_0178 NPC_HIDDEN,{ + // load_npc ( name , map, x , y{, cell} ) + function load_npc { + if (checknpccell(getarg(1), getarg(2), getarg(3), getarg(4, cell_chknopass))) { + enablenpc getarg(0); + //debugmes "ENABLING NPC %s", getarg(0); + } else { + disablenpc getarg(0); + //debugmes "Disabling NPC %s", getarg(0); + } + + /* + debugmes "----- %s (%d,%d) cell report", getarg(1), getarg(2), getarg(3); + debugmes "cell_chknopass: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknopass); + debugmes "cell_chknoreach: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chknoreach); + debugmes "cell_chkbasilica: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkbasilica); + debugmes ""; + debugmes "cell_chkwater: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwater); + debugmes "cell_chkwall: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkwall); + debugmes "cell_chkcliff: %d", checknpccell(getarg(1), getarg(2), getarg(3), cell_chkcliff); + debugmes "----- Npc Id: %s", getarg(0); + */ + return; + } + end; + +OnInit: + // Estate Settings + .id=3; // Estate ID + .mapa$="017-8"; // Map name + + // NPC Settings + .sex = G_OTHER; + .distance = 3; + end; + +// Load or unload accordingly +OnReload: + //debugmes "[REAL ESTATE] NPC ONRELOAD"; + // load_npc ( name , map, x , y{, cell} ) + load_npc("Wardrobe#RES_0178", .mapa$, 21, 23); + load_npc("Cauldron#RES_0178", .mapa$, 28, 24); + load_npc("Piano#RES_0178" , .mapa$, 34, 25); + end; + +} + diff --git a/npc/018-1-1/_import.txt b/npc/018-1-1/_import.txt new file mode 100644 index 0000000..29f69c5 --- /dev/null +++ b/npc/018-1-1/_import.txt @@ -0,0 +1,4 @@ +// Map 018-1-1: Sincerity Island Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-1-1/_mobs.txt", +"npc/018-1-1/_warps.txt", diff --git a/npc/018-1-1/_mobs.txt b/npc/018-1-1/_mobs.txt new file mode 100644 index 0000000..ea6abcd --- /dev/null +++ b/npc/018-1-1/_mobs.txt @@ -0,0 +1,13 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-1-1: Sincerity Island Cave mobs +018-1-1,53,51,14,12 monster Silkworm 1034,10,30000,3000 +018-1-1,43,35,0,0 monster Evil Mushroom 1042,3,40000,5000 +018-1-1,27,31,0,0 monster Evil Mushroom 1042,3,40000,5000 +018-1-1,50,49,30,30 monster Bat 1039,10,60000,10000 +018-1-1,65,52,12,24 monster Chocolate Slime 1180,6,30000,60000 +018-1-1,49,65,27,10 monster Wicked Mushroom 1176,4,30000,3000 +018-1-1,35,39,13,17 monster Lavern 1175,6,30000,3000 +018-1-1,71,33,0,0 monster Evil Mushroom 1042,3,40000,5000 +018-1-1,49,63,30,10 monster Shadow Plant 1189,4,60000,10000 +018-1-1,53,48,27,21 monster Black Mamba 1174,4,60000,10000 +018-1-1,50,50,29,29 monster Vicious Squirrels 1187,9,60000,10000 diff --git a/npc/018-1-1/_warps.txt b/npc/018-1-1/_warps.txt new file mode 100644 index 0000000..c30c4fc --- /dev/null +++ b/npc/018-1-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-1-1: Sincerity Island Cave warps +018-1-1,33,78,0 warp #018-1-1_33_78 0,0,018-1,54,38 diff --git a/npc/018-1/_import.txt b/npc/018-1/_import.txt new file mode 100644 index 0000000..98e287a --- /dev/null +++ b/npc/018-1/_import.txt @@ -0,0 +1,5 @@ +// Map 018-1: Sincerity +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-1/_mobs.txt", +"npc/018-1/_warps.txt", +"npc/018-1/george.txt", diff --git a/npc/018-1/_mobs.txt b/npc/018-1/_mobs.txt new file mode 100644 index 0000000..33884d1 --- /dev/null +++ b/npc/018-1/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-1: Sincerity mobs +018-1,69,65,6,6 monster Evil Mushroom 1042,4,30000,40000 +018-1,32,33,6,6 monster Squirrel 1032,3,30000,60000 +018-1,46,48,2,0 monster Clover Patch 1028,1,10000,120000 +018-1,37,60,11,10 monster Poison Spiky Mushroom 1043,5,10000,30000 +018-1,54,36,8,9 monster Bat 1039,4,25000,30000 +018-1,59,51,6,4 monster Log Head 1066,2,22000,30000 diff --git a/npc/018-1/_warps.txt b/npc/018-1/_warps.txt new file mode 100644 index 0000000..55139b1 --- /dev/null +++ b/npc/018-1/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-1: Sincerity warps +018-1,54,37,0 warp #018-1_54_37 0,0,018-1-1,33,77 +018-1,32,67,0 warp #018-1_32_67 0,0,017-1,152,223 diff --git a/npc/018-1/george.txt b/npc/018-1/george.txt new file mode 100644 index 0000000..235151d --- /dev/null +++ b/npc/018-1/george.txt @@ -0,0 +1,231 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Old pirate with quests. +// LOFQuest_George +// 1 - Roger assigned the task +// 2 - First task accepted +// 3 - First task complete +// 4 - Second task accepted +// 5 - Second task complete + +018-1,26,36,0 script George NPC_GEORGE,{ + .@q=getequipid(EQI_HEAD_TOP); + .@q1=getequipid(EQI_ACC_R); + if (.@q == CorsairHat) goto L_Pirate; + if (.@q == PirateBandana) goto L_Bandana; + if (.@q == RightEyePatch || .@q1 == EyePatch) goto L_EyePatch; + + mesn l("George the Pirate"); + mesq l("Yar! Do you need something, matey?"); + goto L_Main; + +// Intro dialogs +L_Pirate: + mesn l("George the Pirate"); + mesq l("Arrrrh! Ye be wearing a pirate's hat!"); + goto L_Main; + +L_Bandana: + mesn l("George the Pirate"); + mesq l("Arrrrh! Ye remind me of my old first mate!"); + goto L_Main; + +L_EyePatch: + mesn l("George the Pirate"); + mesq l("Arrrrh! Looks like ye lost an eye there!"); + goto L_Main; + +// Main Menu +L_Main: + .@q=getq(LoFQuest_George); + + menu + l("What is this island?"), L_Explain, + rif(.@q == 1, l("Actually, I heard from Roger that you may need some help.")), L_RequestOne, + rif(.@q == 2, l("About the items you asked me to collect...")), L_ProgressOne, + rif(.@q == 3, l("Do you need any more help?")), L_RequestTwo, + rif(.@q == 4, l("About the items you asked me to collect...")), L_ProgressTwo, + rif(.@showShovel, l("Could you sell me a treasure map and a shovel?")), L_Shop, + rif(getq(HurnscaldQuest_PirateCave), l("About this pirate treasure map...")), L_PirateMap, + l("Nothing I guess"), L_Close; + +// Context menu +L_Explain: + mesn l("George the Pirate"); + mesq l("Arrr matey! This is @@, yarr.", l("##BSincerity Island##b")); + next; + mesn l("George the Pirate"); + mesq l("Nobody lives here for years, arr. Monsters have taken over everything, yarr."); + next; + mesn l("George the Pirate"); + mesq l("It is a pirate treasure hideout, yarr arr! If you have a @@ and a @@, you may get luck, yarr!", getitemlink(PirateTreasureMap), getitemlink(IronShovel)); + next; + .@showShovel=true; + goto L_Main; + + +// Requests +L_RequestOne: + if (BaseLevel < 40) + goto L_Weak; + mesn l("George the Pirate"); + mesq l("Roger, eh? Aye, I do need help. If you bring me 30 @@, 50 @@, and 30 @@, maybe I can give you a reward.", getitemlink(Beer), getitemlink(EmptyBottle), getitemlink(CasinoCoins)); + setq LoFQuest_George, 2; + close; + +L_RequestTwo: + if (BaseLevel < 60) + goto L_Weak; + mesn l("George the Pirate"); + mesq l("Arrr matey! My captain asked me to have you get him some items. Please bring us %d %s and %d %s, my captain likes to snack on those during our long voyages. Oh, and something to read too!", + 50, getitemlink(Potatoz), + 40, getitemlink(Dragonfruit)); + setq LoFQuest_George, 4; + close; + +// Progress Report 1 +L_ProgressOne: + if (countitem(Beer) < 30 || + countitem(EmptyBottle) < 50 || + countitem(CasinoCoins) < 30) + goto L_NotEnough; + + mesn l("George the Pirate"); + mesq l("Did you brought everything I ask for?"); + select + l("Yes"), + l("Not sure"); + + if (@menu == 2) + goto L_NotEnough; + + mesn l("George the Pirate"); + mesq l("Yar matey! You have the items I asked for! You have the honor and trust we pirates bestow each other!"); + + inventoryplace SailorHat, 1; + delitem Beer, 30; + delitem EmptyBottle, 50; + delitem CasinoCoins, 30; + mes ""; + mesq l("Here is a reward for your effort! Now you can be a true buccaneer!"); + getitem SailorHat, 1; + getexp 50000, 0; + setq LoFQuest_George, 3; + goto L_Close; + +// Progress Report 2 +L_ProgressTwo: + if (countitem(Potatoz) < 50 || + countitem(Dragonfruit) < 40 || + countitem(Almanac) < 1) + goto L_NotEnough; + + mesn l("George the Pirate"); + mesq l("Did you brought everything I ask for?"); + select + l("Yes"), + l("Not sure"); + + if (@menu == 2) + goto L_NotEnough; + + mesn l("George the Pirate"); + mesq l("Arrr! Good job matey! You brought us everything we asked for, even the Maritime Almanac Scroll!"); + + inventoryplace CorsairHat, 1; + delitem Potatoz, 50; + delitem Dragonfruit, 40; + delitem Almanac, 1; + mesq l("My captain wanted me to give this to you as a special reward for your efforts."); + getitem CorsairHat, 1; + getexp 150000, 0; + setq LoFQuest_George, 5; + next; + mesn; + mesq l("You can call yourself a pirate now! HAR HAR HAR!!"); + goto L_Close; + +// Fallbacks +L_Weak: + mesn l("George the Pirate"); + mesq l("Arr, you are not experienced enough to help me yet!"); + close; + +L_NotEnough: + mesn l("George the Pirate"); + mesq l("Arrr! You did not bring me everything I asked for!"); + if (.@q == 2) + goto L_NotEnoughOne; + if (.@q == 4) + goto L_NotEnoughTwo; + close; + +L_NotEnoughOne: + mesq l("Remember, I need 30 @@, 50 @@, and 30 @@.", getitemlink(Beer), getitemlink(EmptyBottle), getitemlink(CasinoCoins)); + goto L_Close; + +L_NotEnoughTwo: + mesq l("Remember, I need %d %s, %d %s, and something interesting to read.", + 50, getitemlink(Potatoz), + 40, getitemlink(Dragonfruit)); + goto L_Close; + +L_Shop: + mesn; + mesq l("Arr ha ha ha arr!"); + next; + mesn; + mesq l("If I had a map, I would be digging the treasures, not selling it, arr!"); + next; + mesn; + .@price=getiteminfo(IronShovel, ITEMINFO_BUYPRICE); + mesq l("But I can sell ya a %s for %d GP. Interested?", getitemlink(IronShovel), .@price); + if (Zeny < .@price) { + mesc l("You do not have enough money."); + next; + goto L_Main; + } + if (askyesno() == ASK_YES) { + inventoryplace IronShovel, 1; + Zeny-=.@price; + getitem IronShovel, 1; + mesn; + mesq l("Arr! Here you go, matey!"); + next; + } + .@showShovel=false; + goto L_Main; + +L_PirateMap: + if (getq(HurnscaldQuest_PirateCave) >= 2) { + mesn l("George the Pirate"); + mesq l("I'm not familiar with the Butterfly Caves. Try asking a local, maybe Arkim, they ought know the way."); + next; + goto L_Close; + } + mesn l("George the Pirate"); + mesq l("Arr, that's a fine map ya got there! And these are definitely Hurnscald Caves, yarr!"); + next; + mesn l("George the Pirate"); + mesq l("However..."); + next; + mesn l("George the Pirate"); + mesq l("I'm not familiar with the Butterfly Caves. Try asking a local?"); + setq HurnscaldQuest_PirateCave, 2; + next; + goto L_Close; + +L_Close: + sailortalk; + closedialog; + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/018-2-1/_import.txt b/npc/018-2-1/_import.txt new file mode 100644 index 0000000..1d1edd9 --- /dev/null +++ b/npc/018-2-1/_import.txt @@ -0,0 +1,6 @@ +// Map 018-2-1: Heroes' Hold - Castle +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-2-1/_warps.txt", +"npc/018-2-1/dustman.txt", +"npc/018-2-1/mapflags.txt", +"npc/018-2-1/warps.txt", diff --git a/npc/018-2-1/_warps.txt b/npc/018-2-1/_warps.txt new file mode 100644 index 0000000..1cf37a7 --- /dev/null +++ b/npc/018-2-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-2-1: Heroes' Hold - Castle warps +018-2-1,26,33,0 warp #018-2-1_26_33 2,0,018-2,72,65 diff --git a/npc/018-2-1/dustman.txt b/npc/018-2-1/dustman.txt new file mode 100644 index 0000000..905e791 --- /dev/null +++ b/npc/018-2-1/dustman.txt @@ -0,0 +1,243 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Take care of hardcore players + +018-2-1,22,29,0 script Colonel DUSTMAN NPC_CRUSADER,{ + .@cod=getq(LoFQuest_COD); + .@hh=getq(LoFQuest_HH); + mesn; + mesq l("Hello adventurer. Are you interested in the Heroes Hold, or in the Call Of Dusty?"); + next; + select + l("Heroes Hold"), + l("Call Of Dusty"), + l("Neither, sorry"); + if (@menu == 3) + close; + else if (@menu == 2) + goto L_COD; + + mes ""; + if (BaseLevel < 40) { + mesn; + mesq l("Well, my men went down and still haven't returned."); + next; + mesn; + mesq l("I'm worried with them. Anyway, I think only heroes are allowed inside, and you are a few levels short of being a Hero..."); + next; + mesn; + mesq l("Take care, adventurer!"); + close; + } + mesn; + mesq l("This whole region is called the Heroes Hold. It's designed for hard core, bragging-rights-hunters, high level, supreme players."); + next; + mesn; + mesq l("This is the main hall. Below this stairs, you'll find the Heroes Hold Main Dungeon. It's a cave in a... uhm. Water."); + mesq l("I thought it was meant to be lava, this being in the Land Of Fire and all, but seems like someone must bug Saulc to fix it."); + if (.@hh > 1) { + next; + mesn; + mesq l("Where could my man have went?!"); + next; + mesn; + mesq l("Perhaps this has something to do with the factions."); + next; + mesn; + mesq l("I wonder what secrets the Heroes Hold could be hiding..."); + } else if (.@hh == 1) { + next; + mesn; + mesq l("Hm, good luck out there."); + if ((getq2(LoFQuest_HH) & HH_MASTER)) { + mes ""; + mesn; + mesq l("I see you finished the Master Dungeon and didn't found my men. I'll think on something."); + next; + mesn; + mesq l("Thanks for your help, anyway."); + inventoryplace CrusadeHelmet, 1, MercBoxA, 5, AncientBlueprint, 2, HeroCoin, 60; + next; + mes ""; + mesn; + mesc l("You can have my team's helmet, you're now officially a crusader!"); + setq1 LoFQuest_HH, 2; + CsysNpcCraft(CrusadeHelmet, CLASS_DAMAGE_BOSS_TARGET, 10); + getitem MercBoxA, 5; + getitem AncientBlueprint, 2; + getitem HeroCoin, 60; + getexp 100000, 1000; + } + } else if (.@hh == 0) { + next; + mesn; + mesq l("So, let's return to the business at hand. My men went down and haven't returned. That was long ago, by the way."); + mesq l("I can send you to at least five from the seven cave levels this dungeon have."); + next; + mesn; + mesq l("Do you want to try to find them? Just search the first five floors: Novice, Intermediary, Advanced, Expert and Master."); + mesq l("The Supreme and Ultimate floors are just a WIP bonus. So if you don't find them on the five and report back, that'll do."); + next; + mesc l("Accept quest?"); + if (askyesno() == ASK_YES) { + mes ""; + mesn; + mesq l("Wonderful! You must finish Novice, Intermediary, Advanced and Expert levels before I unlock Master level for you."); + mesq l("I don't really understand how the dungeon works. I promise you a reward when you finish the task."); + next; + mesc l("You need to choose a Heroes Hold Group. This currently makes absolutely no difference whatsoever."), 1; + mesc l("However, each group may be expanded later. You can reset group by clearing the Master Floor.", 1); + mesc l("PS. The group names still aren't set. You're free to pinch in suggestions at LoF Discord (Legacy #discussion) :>"); + next; + select + l("The Loyalists"), + l("The Wildlife"); + setq LoFQuest_HH, 1, 0, (@menu-1); + } + } + close; + +L_COD: + mes ""; + // First time you're hearing about? + if (BaseLevel >= 50 && .@cod < 1) { + mesn; + mesq l("Hey, do you know the ##BCall Of Dusty##b event?"); + next; + setq1 LoFQuest_COD, 1; + goto L_InformationCOD; + } + + // Begin here + mesn; + mesq l("Just @@ ago, thieves stole Pihro & Pyndragon weapons.", FuzzyTime(1542570030)); + next; + + // Handle initial message + if (BaseLevel >= 50) { + mesn; + mesq l("Pyndragon said he'll craft a @@ to whoever reduces them to dust. Interested?", getitemlink(Dustynator)); // BottledDust, Dustynator + mesc l("Note: Dustynator is a high level weapon. You might not have enough level to use it yet."); + next; + } else if (BaseLevel < 50) { + mesn; + mesq l("It was a real problem, threatening them to go out of business. I think they are trying to recover the lost weapons."); + mesc l("Attain level 50 to try this quest."); + close; + } + +L_SkipIntro: + mes ""; + // Is the event currently active? Or is it exchange time? + if (.state == 0) { + mesn; + mesq l("The event starts hourly, when the minute clock hits zero. I'll tell everyone on this room when it starts and ends."); + next; + mesn; + mesc l("Do you want to exchange some of your @@ @@ for items?", countitem(BottledDust), getitemlink(BottledDust)); + mes ""; + if (askyesno() == ASK_YES) { + openshop; + closedialog; + } + close; + } + + mesc l("Do you want to participate on Call Of Dusty?"); + // Do you want to participate? + if (askyesno() == ASK_YES) { + mes ""; + mesn; + mesq l("Do you need a refresher of the rules?"); + if (askyesno() == ASK_YES) { + goto L_InformationCOD; + } + closeclientdialog; + @COD_CHECKPOINT=0; + warp "001-10", rand(163,171), rand(119,124); + addtimer(120000,"#COD_BossRoomCheck::OnTimeDefeat"); + dispbottom l("Good luck!"); + close; + } + close; + +// Information about COD +L_InformationCOD: + mes ""; + mesn; + mesc l("Call Of Dusty is a timed event quest. It begins hourly, when the minute clock marks zero."); + mesc l("When the boss is defeated, or the minute clock marks fifteen, event ends."); + next; + mesn; + mesc l("This is a @@, meaning only the victor party will receive the great prize.", b(l("Party Quest"))); + mesc l("You will get @@ which can then be exchanged for items once CoD event quest ends.", countitem(BottledDust)); + next; + mesn; + mesc l("You need be at minimum level 50 to participate. It is PVP, so watch out!"); + mesc l("The first stage is on the desert canyons. You must give a full circle on the canyon in 90 seconds."); + mesc l("The second stage is in the first cave to northwest you can find."); + // The full circle have about 268 tiles in distance. You have the needed time to do 2.2 circles in the most efficient fashion. + // This means you have a time worth of 590 tiles, at 150ms, granting you 90 seconds to do this quest. (everything rounded up) + // But due a small thing called LAG and MANAPLUS DESYNC I'm now granting a whole two minutes for this stage. + next; + mesn; + mesc l("Once inside the BOSS Cave, you get a five minutes time limit to slay the boss with your party."); + mesc l("@@. PK, however, is allowed!", b(l("Only the party from the MVP will get the reward."))); + next; + mesc l("The MVP is the player who did more damage to the boss."); + mesc l("The title is transferred when the MVP is killed (either in PVP or by the boss), or when time runs out."); + next; + mesn; + mesc l("The boss can blind players. Blind will reduce hit rate and evasion in 25%."); + mesc l("Form the strongest party and win! Remember that if you don't kill the boss, you'll never win!"); + next; + goto L_SkipIntro; + +OnInit: + .sex=G_MALE; + .distance=5; + .state=0; // 0- Closed, 1- Open TODO begin at right state + + tradertype(NST_CUSTOM); + + // CoD Exchange Booth Selection + sellitem Dustynator,3000; + sellitem ArcmageBoxset,880; + sellitem BronzeGift,100; + sellitem HeroCoin,1; + end; + +OnMinute00: + .state=1; + donpcevent("#COD_BossManager::OnEventStart"); + npctalk("Call Of Dusty: Event Start!"); + end; + +OnCoDEnd: +OnMinute15: + .state=0; + npctalk("Call Of Dusty: Event End!"); + end; + +OnMinute30: + .state=0; // fail-safe + donpcevent("#COD_BossManager::OnCleanUp"); + end; + +// CoD Exchange Booth Prices +OnCountFunds: + setcurrency(countitem(BottledDust)); + end; + +OnPayFunds: + if( countitem(BottledDust) < @price ) + end; + delitem BottledDust,@price; + purchaseok(); + end; + +} + diff --git a/npc/018-2-1/mapflags.txt b/npc/018-2-1/mapflags.txt new file mode 100644 index 0000000..e148e82 --- /dev/null +++ b/npc/018-2-1/mapflags.txt @@ -0,0 +1,6 @@ +// The whole Heroes Hold but outside area is a MMO area. + +018-2-2 mapflag zone MMO +018-2-3 mapflag zone MMO +018-2-4 mapflag zone MMO +018-2-5 mapflag zone MMO diff --git a/npc/018-2-1/warps.txt b/npc/018-2-1/warps.txt new file mode 100644 index 0000000..876eaae --- /dev/null +++ b/npc/018-2-1/warps.txt @@ -0,0 +1,138 @@ +// Map 018-2-1: Heroes' Hold - Castle manual warps +// Author: Jesusalva + +// LoFQuest_HH +// Field 1 +// 1- Accepted +// 2- Master Finished +// +// Field 2 +// BITWISE: +// 1 - Novice +// 2 - Intermediary +// 4 - Advanced +// 8 - Expert +// 16 - Master +// 32 - Ultimate +// 64 - Supreme +// 128 - Saulc's Madness +// 256 - The Mouboo Realm +// 512 - The Moubootaur Dungeon +// +// Field 3 +// 0- The Loyalists +// 1- The Wildlife + +018-2-1,26,26,0 script #018-2-1_26_26 NPC_HIDDEN,1,1,{ + end; + +OnTouch: + .@hh=getq(LoFQuest_HH); + if (.@hh <= 0) { + npctalk3 l("The stairs lead to nowhere. However, there is a magic sigil on the bottom."); + end; + } + if (HH_COOLDOWN > gettimetick(2)) { + npctalk3 l("You still need to wait @@ before going to HH again.", FuzzyTime(HH_COOLDOWN)); + end; + } + + .@q=getq2(LoFQuest_HH); + mesn l("Heroes Hold"); + mes l("This is only for the skilled players. Newbies, KEEP OUT!"); + mes l("Time Limit: 25 minutes on any dungeon."); + mes l("Please select target dungeon:"); + mes ""; + select + l("Sorry, I am a newbie."), + rif(BaseLevel >= 40, l("Novice Dungeon (Lv 40+)")), // Level 0-40 + rif(.@q & HH_NOVICE, l("Intermediary Dungeon (Lv 60+)")), // Level 21-60 + rif(.@q & HH_INTERMEDIARY, l("Advanced Dungeon (Lv 80+)")), // Level 41-80 + rif(.@q & HH_ADVANCED, l("Expert Dungeon (Lv 100+)")), // Level 61-100 + rif(.@q & HH_EXPERT, l("Master Dungeon (BOSS)")), // Boss Only + l("Heroes Hold - Exchange Hall"), + l("Information"); + mes ""; + if (@menu > 1 && @menu < 7) + HH_COOLDOWN=gettimetick(2)+rand(90,150); + switch (@menu) { + case 2: + @HH_LEVEL=HH_NOVICE; + @HH_TIMER=0; + warp "018-2-2@No", 37, 196; + addtimer(500, "#HH_CONTROLLER01::OnPlayerCycle"); + closedialog; + break; + case 3: + @HH_LEVEL=HH_INTERMEDIARY; + @HH_TIMER=0; + warp "018-2-3@In", 204, 40; + addtimer(500, "#HH_CONTROLLER01::OnPlayerCycle"); + closedialog; + break; + case 4: + @HH_LEVEL=HH_ADVANCED; + @HH_TIMER=0; + warp "018-2-2@Ad", 209, 178; + addtimer(500, "#HH_CONTROLLER01::OnPlayerCycle"); + closedialog; + break; + case 5: + @HH_LEVEL=HH_EXPERT; + @HH_TIMER=0; + warp "018-2-3@Ex", 51, 40; + addtimer(500, "#HH_CONTROLLER01::OnPlayerCycle"); + closedialog; + break; + case 6: + @HH_LEVEL=HH_MASTER; + @HH_TIMER=0; + warp "018-2-5@Ma", 132, 92; + addtimer(500, "#HH_CONTROLLER01::OnPlayerCycle"); + closedialog; + break; + case 7: + .@g=getq3(LoFQuest_HH); + // Wildlife + if (.@g) + warp "018-2-4", 24, 54; + else + warp "018-2-4", 24, 33; + // Loyalists + + closedialog; + break; + case 8: + mes ""; + mesn l("Heroes Hold"); + mes l("The Heroes Hold is divided in seven dungeons level: Novice, Intermediary, Advanced, Expert, Master, Ultimate and Supreme."); + next; + mesn l("Heroes Hold"); + mes l("It is NOT designed for noobs. It is for the pain-seeking pro adventurers who laugh at death, and see danger as fun."); + next; + mesn l("Heroes Hold"); + mes l("The monsters on each Heroes Hold Dungeon will drop @@, a coin which can only be found here.", getitemlink(HeroCoin)); + next; + mesn l("Heroes Hold"); + mes l("Use these coins to exchange for stuff. But beware: Each dungeon difficulty will increase the coin drop in the square value of previous."); + next; + mes l("This means that if you drop a coin on Advanced Dungeon, 4 coins will be dropped instead. On Expert, that would be 8."); + mes l("Needless to say, monsters from Expert Dungeon usually drops more often than the ones from Advanced Dungeon."); + next; + mesn l("Heroes Hold"); + mes l(".:: Victory Conditions ::."); + mes l("- Defeat the BOSS on each dungeon!"); + mes ""; + mes l(".:: Withdraw Conditions ::."); + mes l("- Time runs out (25m)."); + mes l("- Get killed yourself."); + mes l("- Teleport yourself away."); + next; + mes l("Dying inside the Heroes Hold Main Dungeon does not have a penalty. However, dying outside the castle have."); + break; + + } + close; +} + + diff --git a/npc/018-2-2/_import.txt b/npc/018-2-2/_import.txt new file mode 100644 index 0000000..83e89e8 --- /dev/null +++ b/npc/018-2-2/_import.txt @@ -0,0 +1,3 @@ +// Map 018-2-2: Heroes Hold - Main Dungeon +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-2-2/main.txt", diff --git a/npc/018-2-2/main.txt b/npc/018-2-2/main.txt new file mode 100644 index 0000000..7ba051e --- /dev/null +++ b/npc/018-2-2/main.txt @@ -0,0 +1,782 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Controls the four first levels from Heroes Hold + +018-2-2,0,0,0 script #HH_CONTROLLER01 NPC_HIDDEN,{ + end; + +// Boss-Slaying related + function DungeonClear { + getmapxy(.@m$, .@x, .@y, 0); + areatimer(.@m$, .@x-15, .@y-15, .@x+15, .@y+15, 100, "#HH_CONTROLLER01::OnFinish"); + unitskilluseid(getcharid(3), BS_GREED, 1, getcharid(3)); + return; + } + +OnFinish: + if (ispcdead()) + end; + .@g=getq2(LoFQuest_HH); + setq2 LoFQuest_HH, .@g|@HH_LEVEL; + dispbottom l("Dungeon cleared!"); + getitem HeroCoin, @HH_LEVEL*3; + getexp 4800-@HH_TIMER, @HH_LEVEL*20; + deltimer("#HH_CONTROLLER01::OnPlayerCycle"); + setpcblock(PCBLOCK_SOFT, true); + addtimer(4000, "#HH_CONTROLLER01::OnCompulsoryWarp"); + //recovery(getcharid(3)); + end; + +OnCompulsoryWarp: + setpcblock(PCBLOCK_SOFT, false); + warp "018-2-1", 0, 0; + end; + +OnNoviceBossKilled: + monster "018-2-2@No", 188, 29, "Novice Dungeon Boss", AlphaMouboo, 1, "#HH_CONTROLLER01::OnNoviceBossKilled"; + DungeonClear(); + end; + +OnIntermBossKilled: + monster "018-2-3@In", 52, 196, "Intermiary Dungeon Boss", FafiDragon, 1, "#HH_CONTROLLER01::OnIntermBossKilled"; + DungeonClear(); + end; + +OnAdvancedBossKilled: + monster "018-2-2@Ad", 52, 29, "Advanced Dungeon Boss", GiantMutatedBat, 1, "#HH_CONTROLLER01::OnAdvancedBossKilled"; + DungeonClear(); + end; + +OnExpertBossKilled: + monster "018-2-3@Ex", 188, 196, "Expert Dungeon Boss", FallenKing1, 1, "#HH_CONTROLLER01::OnExpertBossKilled"; + DungeonClear(); + end; + +OnMasterBossKilled: + if (!playerattached()) + end; + if (!HEROESHOLD_WINNER) + HEROESHOLD_WINNER = gettimetick(2); + if ($HEROESHOLD_WINNER$ == "") { + $HEROESHOLD_WINNER$=strcharinfo(0); + channelmes("#world", $HEROESHOLD_WINNER$+" is the first player to finish HEROES HOLD Master Dungeon!! GG, dude! %%N"); + announce "All hail ##B"+$HEROESHOLD_WINNER$+"##b, first to complete the ##3HEROES HOLD Master Dungeon!", bc_all|bc_npc; + getexp 0, 2000; + getitem PrismGift, 1; + mesc l("CONGRATULATIONS! You are the first player to finish Heroes Hold Master Dungeon!!"), 2; + mesc l("You just gained a Prism Gift, and 2000 Job Exp for your bravery!"), 2; + } + monster "018-2-5@Ma", any(52,188), any(29,196), "Master Dungeon Boss", MonsterKing, 1, "#HH_CONTROLLER01::OnMasterBossKilled"; + DungeonClear(); + end; + +// Some cleanup might be needed to don't raise difficulty infinitely +// So every day, at 03:23 AM, if no one is trying the quest, it'll get rid +// of Heroes Hold Master Dungeon +OnClock0323: + if (getareausers("018-2-5@Ma") == 0) { + killmonster("018-2-5@Ma", "#HH_CONTROLLER01::OnMasterBossKilled"); + monster "018-2-5@Ma", any(52,188), any(29,196), "Master Dungeon Boss", MonsterKing, 1, "#HH_CONTROLLER01::OnMasterBossKilled"; + } + end; + + +// Everytime loop +OnPlayerCycle: + @HH_TIMER+=1; + // 25 minutes have passed and your time is over + if (@HH_TIMER >= 3000) { + warp "018-2-1", 0, 0; + dispbottom l("You were rescued by DUSTMAN."); + end; + } + // TODO: Handle traps (We'll use isin() command because you can give 3~4 steps each counter) + if (rand2(1000) <= @HH_LEVEL) { + dispbottom l("You set off a trap!"); + heal -(@HH_LEVEL*rand2(3,6)), -(@HH_LEVEL/2); + if (rand2(250) > readparam2(bVit)) { + // Determine the dangers of the random trap + // SC_Bonus(delay, SC, min{, max}) + if (@HH_LEVEL >= HH_MASTER) + SC_Bonus(10+rand2(20), any(SC_DPOISON, SC_DEEP_SLEEP, SC_BURNING, SC_CURSE), 1); + else if (@HH_LEVEL >= HH_EXPERT) + SC_Bonus(1+rand2(20), any(SC_POISON, SC_CURSE, SC_SILENCE, SC_BLOODING, SC_BLIND, SC_SLEEP), 1); + else if (@HH_LEVEL >= HH_ADVANCED) + SC_Bonus(1+rand2(10), any(SC_DEC_AGI, SC_POISON, SC_CURSE, SC_SILENCE), 1); + else if (@HH_LEVEL >= HH_INTERMEDIARY) + SC_Bonus(1+rand2(5), any(SC_DEC_AGI, SC_COLD, SC_CONFUSION), 1); + } + } + + // Continue the execution + if (compare(getmap(), "018-2-")) + addtimer(500, "#HH_CONTROLLER01::OnPlayerCycle"); + end; + +// Initialize Variables. Remember this causes a search for On<Difficulty><SeqNumber>. And keep same number or CRASH +OnInit: + // Novice + setarray .Novice_Mobs, AngryScorpion,CaveMaggot,MagicGoblin,ViciousSquirrel,AngryBat,RedSlime,AngryRedScorpion,Bandit,Skeleton,GreenSlime, + BlueSlime,LavaSlime,RedMushroom,RobinBandit,AngryYellowSlime,OldSnake,GrassSnake,BlackSlime,SmallMagicBif,BronzeChest, + Bif; + setarray .Novice_Ammo, 35, 35, 40, 30, 40, 80, 20, 20, 10, 90, + 30, 30, 25, 10, 35, 10, 15, 45, 7, 2, + 2; + + // Intermiary + setarray .Interm_Mobs, RedSlime,AngryRedScorpion,Bandit,Skeleton,GreenSlime, BlueSlime,LavaSlime,RedMushroom,RobinBandit,Bif, + AngryYellowSlime,OldSnake,GrassSnake,BlackSlime,Wolvern,DarkLizard,BlackScorpion,DustRevolver,MagicBif,SilverChest; + setarray .Interm_Ammo, 80, 50, 50, 10, 90, 30, 60, 35, 20, 5, + 50, 20, 25, 50, 30, 20, 25, 5, 7, 2; + + // Advanced + setarray .Advanc_Mobs, AngryYellowSlime,Snake,GrassSnake,BlackSlime,Wolvern,DarkLizard,BlackScorpion,DustRevolver,MagicBif,SilverChest, + MountainSnake, Yeti, WickedMushroom,Forain,GoldenChest,BigMagicBif,DustGatling,Archant,Bif; + setarray .Advanc_Ammo, 50, 30, 35, 90, 30, 30, 45, 15, 7, 2, + 15, 10, 30, 5, 2, 1, 10, 18, 9; + + // Expert + setarray .Expert_Mobs, MountainSnake, Yeti, WickedMushroom,Forain,GoldenChest,BigMagicBif,DustGatling,BlackMamba,Terranite, Wolvern, + PrismChest, BlueSlime, GreenDragon,Bif,GoboBear; + setarray .Expert_Ammo, 45, 30, 90, 35, 3, 7, 30, 20, 15, 10, + 1, 140, 15, 13, 15; + + // Master + setarray .Master_Mobs, PrismChest,BlueSlime,MurdererScorpion,Tipiou,AlphaMouboo,BanditLord,Tipiu,GreenDragon,GiantMutatedBat,FallenKing1, + FallenKing2, EvilScythe, YetiKing, Tipiu, Yetifly; + setarray .Master_Ammo, 2, 140, 20, 20, 20, 20, 20, 20, 20, 10, + 10, 1, 7, 8, 3; + + end; + +OnHHInit: + initnpctimer; + end; + + +// Controls logic for each instance (TODO) +// Initialize each instance (Currently waits 3 seconds, could work with just 4, but better safe than sorry) +OnTimer3000: + stopnpctimer; + debugmes "Heroes Hold Monsters: Initializing"; + + // HH_Novice + freeloop(true); + for (.@i=0;.@i<getarraysize(.Novice_Mobs);.@i++) { + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[.@i]), .Novice_Mobs[.@i], .Novice_Ammo[.@i], "#HH_CONTROLLER01::OnNovice"+.@i; + } + freeloop(false); + + // HH_INTERMEDIARY + freeloop(true); + for (.@i=0;.@i<getarraysize(.Interm_Mobs);.@i++) { + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[.@i]), .Interm_Mobs[.@i], .Interm_Ammo[.@i], "#HH_CONTROLLER01::OnInterm"+.@i; + } + freeloop(false); + + // HH_ADVANCED + freeloop(true); + for (.@i=0;.@i<getarraysize(.Advanc_Mobs);.@i++) { + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[.@i]), .Advanc_Mobs[.@i], .Advanc_Ammo[.@i], "#HH_CONTROLLER01::OnAdvanc"+.@i; + } + freeloop(false); + + // HH_EXPERT + freeloop(true); + for (.@i=0;.@i<getarraysize(.Expert_Mobs);.@i++) { + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[.@i]), .Expert_Mobs[.@i], .Expert_Ammo[.@i], "#HH_CONTROLLER01::OnExpert"+.@i; + } + freeloop(false); + + // HH_MASTER + freeloop(true); + for (.@i=0;.@i<getarraysize(.Master_Mobs);.@i++) { + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[.@i]), .Master_Mobs[.@i], .Master_Ammo[.@i], "#HH_CONTROLLER01::OnMaster"+.@i; + } + freeloop(false); + + // Boss for each dungeon + monster "018-2-2@No", 188, 29, "Novice Dungeon Boss", AlphaMouboo, 1, "#HH_CONTROLLER01::OnNoviceBossKilled"; + monster "018-2-3@In", 52, 196, "Intermiary Dungeon Boss", FafiDragon, 1, "#HH_CONTROLLER01::OnIntermBossKilled"; + monster "018-2-2@Ad", 52, 29, "Advanced Dungeon Boss", GiantMutatedBat, 1, "#HH_CONTROLLER01::OnAdvancedBossKilled"; + monster "018-2-3@Ex", 188, 196, "Expert Dungeon Boss", FallenKing1, 1, "#HH_CONTROLLER01::OnExpertBossKilled"; + monster "018-2-5@Ma", any(52,188), any(29,196), "Master Dungeon Boss", MonsterKing, 1, "#HH_CONTROLLER01::OnMasterBossKilled"; + + // TODO: We still need the main logic for this. I mean, what is the objective on each floor of Master Dungeon? + debugmes "Heroes Hold Monsters: Success"; + end; + + +///////////////////////////////////////////////////////////////////////////////// +// Respawn Arrays (Autogenerated, Python Script at bottom. 14 pages worth.) +// Drop Chances are: 0.05% each luck point, 0.20% each mob level, 0.05% each job experience given +// So killing a Mouboo with 20 luck will give: 1% (luck) + 7% (level) + 0.3% (jexp) => 8.3% drop chance + +OnNovice0: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[0])*4)+strmobinfo(7,.Novice_Mobs[0])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[0]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[0]), .Novice_Mobs[0], 1, "#HH_CONTROLLER01::OnNovice0"; end; +OnNovice1: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[1])*4)+strmobinfo(7,.Novice_Mobs[1])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[1]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[1]), .Novice_Mobs[1], 1, "#HH_CONTROLLER01::OnNovice1"; end; +OnNovice2: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[2])*4)+strmobinfo(7,.Novice_Mobs[2])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[2]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[2]), .Novice_Mobs[2], 1, "#HH_CONTROLLER01::OnNovice2"; end; +OnNovice3: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[3])*4)+strmobinfo(7,.Novice_Mobs[3])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[3]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[3]), .Novice_Mobs[3], 1, "#HH_CONTROLLER01::OnNovice3"; end; +OnNovice4: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[4])*4)+strmobinfo(7,.Novice_Mobs[4])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[4]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[4]), .Novice_Mobs[4], 1, "#HH_CONTROLLER01::OnNovice4"; end; +OnNovice5: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[5])*4)+strmobinfo(7,.Novice_Mobs[5])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[5]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[5]), .Novice_Mobs[5], 1, "#HH_CONTROLLER01::OnNovice5"; end; +OnNovice6: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[6])*4)+strmobinfo(7,.Novice_Mobs[6])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[6]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[6]), .Novice_Mobs[6], 1, "#HH_CONTROLLER01::OnNovice6"; end; +OnNovice7: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[7])*4)+strmobinfo(7,.Novice_Mobs[7])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[7]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[7]), .Novice_Mobs[7], 1, "#HH_CONTROLLER01::OnNovice7"; end; +OnNovice8: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[8])*4)+strmobinfo(7,.Novice_Mobs[8])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[8]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[8]), .Novice_Mobs[8], 1, "#HH_CONTROLLER01::OnNovice8"; end; +OnNovice9: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[9])*4)+strmobinfo(7,.Novice_Mobs[9])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[9]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[9]), .Novice_Mobs[9], 1, "#HH_CONTROLLER01::OnNovice9"; end; +OnNovice10: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[10])*4)+strmobinfo(7,.Novice_Mobs[10])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[10]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[10]), .Novice_Mobs[10], 1, "#HH_CONTROLLER01::OnNovice10"; end; +OnNovice11: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[11])*4)+strmobinfo(7,.Novice_Mobs[11])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[11]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[11]), .Novice_Mobs[11], 1, "#HH_CONTROLLER01::OnNovice11"; end; +OnNovice12: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[12])*4)+strmobinfo(7,.Novice_Mobs[12])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[12]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[12]), .Novice_Mobs[12], 1, "#HH_CONTROLLER01::OnNovice12"; end; +OnNovice13: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[13])*4)+strmobinfo(7,.Novice_Mobs[13])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[13]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[13]), .Novice_Mobs[13], 1, "#HH_CONTROLLER01::OnNovice13"; end; +OnNovice14: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[14])*4)+strmobinfo(7,.Novice_Mobs[14])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[14]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[14]), .Novice_Mobs[14], 1, "#HH_CONTROLLER01::OnNovice14"; end; +OnNovice15: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[15])*4)+strmobinfo(7,.Novice_Mobs[15])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[15]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[15]), .Novice_Mobs[15], 1, "#HH_CONTROLLER01::OnNovice15"; end; +OnNovice16: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[16])*4)+strmobinfo(7,.Novice_Mobs[16])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[16]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[16]), .Novice_Mobs[16], 1, "#HH_CONTROLLER01::OnNovice16"; end; +OnNovice17: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[17])*4)+strmobinfo(7,.Novice_Mobs[17])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[17]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[17]), .Novice_Mobs[17], 1, "#HH_CONTROLLER01::OnNovice17"; end; +OnNovice18: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[18])*4)+strmobinfo(7,.Novice_Mobs[18])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[18]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[18]), .Novice_Mobs[18], 1, "#HH_CONTROLLER01::OnNovice18"; end; +OnNovice19: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[19])*4)+strmobinfo(7,.Novice_Mobs[19])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[19]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[19]), .Novice_Mobs[19], 1, "#HH_CONTROLLER01::OnNovice19"; end; +OnNovice20: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Novice_Mobs[20])*4)+strmobinfo(7,.Novice_Mobs[20])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 1, .@m$, .@x, .@y);} + fix_mobkill(.Novice_Mobs[20]); + } + areamonster "018-2-2@No", 20, 20, 220, 220, strmobinfo(1, .Novice_Mobs[20]), .Novice_Mobs[20], 1, "#HH_CONTROLLER01::OnNovice20"; end; + + +OnInterm0: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[0])*4)+strmobinfo(7,.Interm_Mobs[0])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[0]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[0]), .Interm_Mobs[0], 1, "#HH_CONTROLLER01::OnInterm0"; end; +OnInterm1: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[1])*4)+strmobinfo(7,.Interm_Mobs[1])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[1]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[1]), .Interm_Mobs[1], 1, "#HH_CONTROLLER01::OnInterm1"; end; +OnInterm2: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[2])*4)+strmobinfo(7,.Interm_Mobs[2])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[2]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[2]), .Interm_Mobs[2], 1, "#HH_CONTROLLER01::OnInterm2"; end; +OnInterm3: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[3])*4)+strmobinfo(7,.Interm_Mobs[3])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[3]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[3]), .Interm_Mobs[3], 1, "#HH_CONTROLLER01::OnInterm3"; end; +OnInterm4: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[4])*4)+strmobinfo(7,.Interm_Mobs[4])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[4]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[4]), .Interm_Mobs[4], 1, "#HH_CONTROLLER01::OnInterm4"; end; +OnInterm5: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[5])*4)+strmobinfo(7,.Interm_Mobs[5])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[5]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[5]), .Interm_Mobs[5], 1, "#HH_CONTROLLER01::OnInterm5"; end; +OnInterm6: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[6])*4)+strmobinfo(7,.Interm_Mobs[6])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[6]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[6]), .Interm_Mobs[6], 1, "#HH_CONTROLLER01::OnInterm6"; end; +OnInterm7: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[7])*4)+strmobinfo(7,.Interm_Mobs[7])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[7]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[7]), .Interm_Mobs[7], 1, "#HH_CONTROLLER01::OnInterm7"; end; +OnInterm8: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[8])*4)+strmobinfo(7,.Interm_Mobs[8])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[8]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[8]), .Interm_Mobs[8], 1, "#HH_CONTROLLER01::OnInterm8"; end; +OnInterm9: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[9])*4)+strmobinfo(7,.Interm_Mobs[9])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[9]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[9]), .Interm_Mobs[9], 1, "#HH_CONTROLLER01::OnInterm9"; end; +OnInterm10: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[10])*4)+strmobinfo(7,.Interm_Mobs[10])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[10]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[10]), .Interm_Mobs[10], 1, "#HH_CONTROLLER01::OnInterm10"; end; +OnInterm11: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[11])*4)+strmobinfo(7,.Interm_Mobs[11])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[11]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[11]), .Interm_Mobs[11], 1, "#HH_CONTROLLER01::OnInterm11"; end; +OnInterm12: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[12])*4)+strmobinfo(7,.Interm_Mobs[12])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[12]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[12]), .Interm_Mobs[12], 1, "#HH_CONTROLLER01::OnInterm12"; end; +OnInterm13: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[13])*4)+strmobinfo(7,.Interm_Mobs[13])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[13]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[13]), .Interm_Mobs[13], 1, "#HH_CONTROLLER01::OnInterm13"; end; +OnInterm14: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[14])*4)+strmobinfo(7,.Interm_Mobs[14])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[14]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[14]), .Interm_Mobs[14], 1, "#HH_CONTROLLER01::OnInterm14"; end; +OnInterm15: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[15])*4)+strmobinfo(7,.Interm_Mobs[15])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[15]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[15]), .Interm_Mobs[15], 1, "#HH_CONTROLLER01::OnInterm15"; end; +OnInterm16: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[16])*4)+strmobinfo(7,.Interm_Mobs[16])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[16]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[16]), .Interm_Mobs[16], 1, "#HH_CONTROLLER01::OnInterm16"; end; +OnInterm17: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[17])*4)+strmobinfo(7,.Interm_Mobs[17])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[17]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[17]), .Interm_Mobs[17], 1, "#HH_CONTROLLER01::OnInterm17"; end; +OnInterm18: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[18])*4)+strmobinfo(7,.Interm_Mobs[18])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[18]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[18]), .Interm_Mobs[18], 1, "#HH_CONTROLLER01::OnInterm18"; end; +OnInterm19: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Interm_Mobs[19])*4)+strmobinfo(7,.Interm_Mobs[19])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 2, .@m$, .@x, .@y);} + fix_mobkill(.Interm_Mobs[19]); + } + areamonster "018-2-3@In", 20, 20, 220, 220, strmobinfo(1, .Interm_Mobs[19]), .Interm_Mobs[19], 1, "#HH_CONTROLLER01::OnInterm19"; end; + + +OnAdvanc0: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[0])*4)+strmobinfo(7,.Advanc_Mobs[0])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[0]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[0]), .Advanc_Mobs[0], 1, "#HH_CONTROLLER01::OnAdvanc0"; end; +OnAdvanc1: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[1])*4)+strmobinfo(7,.Advanc_Mobs[1])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[1]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[1]), .Advanc_Mobs[1], 1, "#HH_CONTROLLER01::OnAdvanc1"; end; +OnAdvanc2: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[2])*4)+strmobinfo(7,.Advanc_Mobs[2])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[2]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[2]), .Advanc_Mobs[2], 1, "#HH_CONTROLLER01::OnAdvanc2"; end; +OnAdvanc3: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[3])*4)+strmobinfo(7,.Advanc_Mobs[3])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[3]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[3]), .Advanc_Mobs[3], 1, "#HH_CONTROLLER01::OnAdvanc3"; end; +OnAdvanc4: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[4])*4)+strmobinfo(7,.Advanc_Mobs[4])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[4]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[4]), .Advanc_Mobs[4], 1, "#HH_CONTROLLER01::OnAdvanc4"; end; +OnAdvanc5: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[5])*4)+strmobinfo(7,.Advanc_Mobs[5])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[5]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[5]), .Advanc_Mobs[5], 1, "#HH_CONTROLLER01::OnAdvanc5"; end; +OnAdvanc6: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[6])*4)+strmobinfo(7,.Advanc_Mobs[6])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[6]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[6]), .Advanc_Mobs[6], 1, "#HH_CONTROLLER01::OnAdvanc6"; end; +OnAdvanc7: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[7])*4)+strmobinfo(7,.Advanc_Mobs[7])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[7]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[7]), .Advanc_Mobs[7], 1, "#HH_CONTROLLER01::OnAdvanc7"; end; +OnAdvanc8: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[8])*4)+strmobinfo(7,.Advanc_Mobs[8])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[8]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[8]), .Advanc_Mobs[8], 1, "#HH_CONTROLLER01::OnAdvanc8"; end; +OnAdvanc9: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[9])*4)+strmobinfo(7,.Advanc_Mobs[9])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[9]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[9]), .Advanc_Mobs[9], 1, "#HH_CONTROLLER01::OnAdvanc9"; end; +OnAdvanc10: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[10])*4)+strmobinfo(7,.Advanc_Mobs[10])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[10]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[10]), .Advanc_Mobs[10], 1, "#HH_CONTROLLER01::OnAdvanc10"; end; +OnAdvanc11: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[11])*4)+strmobinfo(7,.Advanc_Mobs[11])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[11]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[11]), .Advanc_Mobs[11], 1, "#HH_CONTROLLER01::OnAdvanc11"; end; +OnAdvanc12: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[12])*4)+strmobinfo(7,.Advanc_Mobs[12])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[12]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[12]), .Advanc_Mobs[12], 1, "#HH_CONTROLLER01::OnAdvanc12"; end; +OnAdvanc13: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[13])*4)+strmobinfo(7,.Advanc_Mobs[13])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[13]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[13]), .Advanc_Mobs[13], 1, "#HH_CONTROLLER01::OnAdvanc13"; end; +OnAdvanc14: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[14])*4)+strmobinfo(7,.Advanc_Mobs[14])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[14]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[14]), .Advanc_Mobs[14], 1, "#HH_CONTROLLER01::OnAdvanc14"; end; +OnAdvanc15: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[15])*4)+strmobinfo(7,.Advanc_Mobs[15])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[15]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[15]), .Advanc_Mobs[15], 1, "#HH_CONTROLLER01::OnAdvanc15"; end; +OnAdvanc16: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[16])*4)+strmobinfo(7,.Advanc_Mobs[16])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[16]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[16]), .Advanc_Mobs[16], 1, "#HH_CONTROLLER01::OnAdvanc16"; end; +OnAdvanc17: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Advanc_Mobs[17])*4)+strmobinfo(7,.Advanc_Mobs[17])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 4, .@m$, .@x, .@y);} + fix_mobkill(.Advanc_Mobs[17]); + } + areamonster "018-2-2@Ad", 20, 20, 220, 220, strmobinfo(1, .Advanc_Mobs[17]), .Advanc_Mobs[17], 1, "#HH_CONTROLLER01::OnAdvanc17"; end; + + +OnExpert0: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[0])*4)+strmobinfo(7,.Expert_Mobs[0])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[0]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[0]), .Expert_Mobs[0], 1, "#HH_CONTROLLER01::OnExpert0"; end; +OnExpert1: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[1])*4)+strmobinfo(7,.Expert_Mobs[1])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[1]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[1]), .Expert_Mobs[1], 1, "#HH_CONTROLLER01::OnExpert1"; end; +OnExpert2: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[2])*4)+strmobinfo(7,.Expert_Mobs[2])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[2]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[2]), .Expert_Mobs[2], 1, "#HH_CONTROLLER01::OnExpert2"; end; +OnExpert3: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[3])*4)+strmobinfo(7,.Expert_Mobs[3])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[3]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[3]), .Expert_Mobs[3], 1, "#HH_CONTROLLER01::OnExpert3"; end; +OnExpert4: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[4])*4)+strmobinfo(7,.Expert_Mobs[4])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[4]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[4]), .Expert_Mobs[4], 1, "#HH_CONTROLLER01::OnExpert4"; end; +OnExpert5: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[5])*4)+strmobinfo(7,.Expert_Mobs[5])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[5]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[5]), .Expert_Mobs[5], 1, "#HH_CONTROLLER01::OnExpert5"; end; +OnExpert6: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[6])*4)+strmobinfo(7,.Expert_Mobs[6])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[6]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[6]), .Expert_Mobs[6], 1, "#HH_CONTROLLER01::OnExpert6"; end; +OnExpert7: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[7])*4)+strmobinfo(7,.Expert_Mobs[7])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[7]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[7]), .Expert_Mobs[7], 1, "#HH_CONTROLLER01::OnExpert7"; end; +OnExpert8: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[8])*4)+strmobinfo(7,.Expert_Mobs[8])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[8]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[8]), .Expert_Mobs[8], 1, "#HH_CONTROLLER01::OnExpert8"; end; +OnExpert9: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[9])*4)+strmobinfo(7,.Expert_Mobs[9])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[9]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[9]), .Expert_Mobs[9], 1, "#HH_CONTROLLER01::OnExpert9"; end; +OnExpert10: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[10])*4)+strmobinfo(7,.Expert_Mobs[10])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[10]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[10]), .Expert_Mobs[10], 1, "#HH_CONTROLLER01::OnExpert10"; end; +OnExpert11: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[11])*4)+strmobinfo(7,.Expert_Mobs[11])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[11]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[11]), .Expert_Mobs[11], 1, "#HH_CONTROLLER01::OnExpert11"; end; +OnExpert12: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[12])*4)+strmobinfo(7,.Expert_Mobs[12])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[12]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[12]), .Expert_Mobs[12], 1, "#HH_CONTROLLER01::OnExpert12"; end; +OnExpert13: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[13])*4)+strmobinfo(7,.Expert_Mobs[13])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[13]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[13]), .Expert_Mobs[13], 1, "#HH_CONTROLLER01::OnExpert13"; end; +OnExpert14: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Expert_Mobs[14])*4)+strmobinfo(7,.Expert_Mobs[14])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 8, .@m$, .@x, .@y);} + fix_mobkill(.Expert_Mobs[14]); + } + areamonster "018-2-3@Ex", 20, 20, 220, 220, strmobinfo(1, .Expert_Mobs[14]), .Expert_Mobs[14], 1, "#HH_CONTROLLER01::OnExpert14"; end; + + +OnMaster0: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[0])*4)+strmobinfo(7,.Master_Mobs[0])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[0]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[0]), .Master_Mobs[0], 1, "#HH_CONTROLLER01::OnMaster0"; end; +OnMaster1: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[1])*4)+strmobinfo(7,.Master_Mobs[1])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[1]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[1]), .Master_Mobs[1], 1, "#HH_CONTROLLER01::OnMaster1"; end; +OnMaster2: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[2])*4)+strmobinfo(7,.Master_Mobs[2])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[2]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[2]), .Master_Mobs[2], 1, "#HH_CONTROLLER01::OnMaster2"; end; +OnMaster3: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[3])*4)+strmobinfo(7,.Master_Mobs[3])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[3]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[3]), .Master_Mobs[3], 1, "#HH_CONTROLLER01::OnMaster3"; end; +OnMaster4: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[4])*4)+strmobinfo(7,.Master_Mobs[4])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[4]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[4]), .Master_Mobs[4], 1, "#HH_CONTROLLER01::OnMaster4"; end; +OnMaster5: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[5])*4)+strmobinfo(7,.Master_Mobs[5])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[5]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[5]), .Master_Mobs[5], 1, "#HH_CONTROLLER01::OnMaster5"; end; +OnMaster6: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[6])*4)+strmobinfo(7,.Master_Mobs[6])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[6]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[6]), .Master_Mobs[6], 1, "#HH_CONTROLLER01::OnMaster6"; end; +OnMaster7: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[7])*4)+strmobinfo(7,.Master_Mobs[7])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[7]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[7]), .Master_Mobs[7], 1, "#HH_CONTROLLER01::OnMaster7"; end; +OnMaster8: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[8])*4)+strmobinfo(7,.Master_Mobs[8])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[8]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[8]), .Master_Mobs[8], 1, "#HH_CONTROLLER01::OnMaster8"; end; +OnMaster9: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[9])*4)+strmobinfo(7,.Master_Mobs[9])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[9]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[9]), .Master_Mobs[9], 1, "#HH_CONTROLLER01::OnMaster9"; end; +OnMaster10: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[10])*4)+strmobinfo(7,.Master_Mobs[10])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[10]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[10]), .Master_Mobs[10], 1, "#HH_CONTROLLER01::OnMaster10"; end; +OnMaster11: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[11])*4)+strmobinfo(7,.Master_Mobs[11])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[11]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[11]), .Master_Mobs[11], 1, "#HH_CONTROLLER01::OnMaster11"; end; +OnMaster12: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[12])*4)+strmobinfo(7,.Master_Mobs[12])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[12]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[12]), .Master_Mobs[12], 1, "#HH_CONTROLLER01::OnMaster12"; end; +OnMaster13: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[13])*4)+strmobinfo(7,.Master_Mobs[13])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[13]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[13]), .Master_Mobs[13], 1, "#HH_CONTROLLER01::OnMaster13"; end; +OnMaster14: + if (playerattached()) { + if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.Master_Mobs[14])*4)+strmobinfo(7,.Master_Mobs[14])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, 16, .@m$, .@x, .@y);} + fix_mobkill(.Master_Mobs[14]); + } + areamonster "018-2-5@Ma", 20, 20, 220, 220, strmobinfo(1, .Master_Mobs[14]), .Master_Mobs[14], 1, "#HH_CONTROLLER01::OnMaster14"; end; + +} + +/*python +# df (name, mobs, reward, mapid) +df=[('Novice', 21, 1, 2), + ('Interm', 20, 2, 3), + ('Advanc', 18, 4, 2), + ('Expert', 15, 8, 3), + ('Master', 15, 16,5)] + +for a in df: + print("\n") + i=0 + while (i < a[1]): + print('On%s%d:' % (a[0], i)) + print(' if (playerattached()) {') + print(' if (rand2(0, 2000) <= readparam2(bLuk)+(strmobinfo(3,.%s_Mobs[%d])*4)+strmobinfo(7,.%s_Mobs[%d])) {getmapxy(.@m$, .@x, .@y, 0); makeitem(HeroCoin, %s, .@m$, .@x, .@y);}' % (a[0], i, a[0], i, a[2])) + print(' fix_mobkill(.%s_Mobs[%d]);' % (a[0],i)) + print(' }') + print(' areamonster "018-2-%d@%s", 20, 20, 220, 220, strmobinfo(1, .%s_Mobs[%d]), .%s_Mobs[%d], 1, "#HH_CONTROLLER01::On%s%d"; end;' % (a[3], a[0][:2],a[0],i,a[0],i,a[0],i)) + i+=1 +*/ diff --git a/npc/018-2-3/_import.txt b/npc/018-2-3/_import.txt new file mode 100644 index 0000000..bb95337 --- /dev/null +++ b/npc/018-2-3/_import.txt @@ -0,0 +1,2 @@ +// Map 018-2-3: Heroes Hold - Main Dungeon +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/018-2-4/_import.txt b/npc/018-2-4/_import.txt new file mode 100644 index 0000000..eb5706e --- /dev/null +++ b/npc/018-2-4/_import.txt @@ -0,0 +1,8 @@ +// Map 018-2-4: Heroes' Hold - Exchange Hall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-2-4/_warps.txt", +"npc/018-2-4/lv1.txt", +"npc/018-2-4/lv2.txt", +"npc/018-2-4/lv3.txt", +"npc/018-2-4/lv4.txt", +"npc/018-2-4/vault.txt", diff --git a/npc/018-2-4/_warps.txt b/npc/018-2-4/_warps.txt new file mode 100644 index 0000000..92ff8cc --- /dev/null +++ b/npc/018-2-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-2-4: Heroes' Hold - Exchange Hall warps +018-2-4,24,55,0 warp #018-2-4_24_55 2,0,018-2-1,26,25 +018-2-4,24,34,0 warp #018-2-4_24_34 2,0,018-2-1,26,25 diff --git a/npc/018-2-4/lv1.txt b/npc/018-2-4/lv1.txt new file mode 100644 index 0000000..bfcc77d --- /dev/null +++ b/npc/018-2-4/lv1.txt @@ -0,0 +1,59 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Heroes Hold Exchanger - Level 1 + +018-2-4,33,28,0 script Novice Exchanger#0 NPC_M_COINKEEPER,{ + .@q=getq2(LoFQuest_HH); + if (.@q & HH_NOVICE) + openshop; + goodbye; + closedialog; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TopHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, CandorShirt); + setunitdata(.@npcId, UDT_WEAPON, CandorShorts); + setunitdata(.@npcId, UDT_HEADBOTTOM, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 25); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex = G_OTHER; + .distance = 5; + npcsit; + + tradertype(NST_CUSTOM); + + sellitem EquipmentBlueprintA,1600; + sellitem RaidTrousers,1200; + sellitem BugSlayer,800; + sellitem LeatherShield,600; + sellitem TolchiAmmoBox,270; + sellitem Grenade,96; + + sellitem HastePotion,50; + sellitem StrengthPotion,50; + + sellitem Curshroom,10; + sellitem SmokeGrenade,4; + end; + +OnCountFunds: + setcurrency(countitem(HeroCoin)); + end; + +OnPayFunds: + if( countitem(HeroCoin) < @price ) + end; + delitem HeroCoin,@price; + purchaseok(); + end; + +} + +018-2-4,33,49,0 duplicate(Novice Exchanger#0) Novice Exchanger#1 NPC_M_COINKEEPER + diff --git a/npc/018-2-4/lv2.txt b/npc/018-2-4/lv2.txt new file mode 100644 index 0000000..3738e8e --- /dev/null +++ b/npc/018-2-4/lv2.txt @@ -0,0 +1,57 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Heroes Hold Exchanger - Level 2 + +018-2-4,40,28,0 script Advanced Exchanger#0 NPC_M_COINKEEPER,{ + .@q=getq2(LoFQuest_HH); + if (.@q & HH_INTERMEDIARY) + openshop; + goodbye; + closedialog; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TopHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, Chainmail); + setunitdata(.@npcId, UDT_WEAPON, JeansShorts); + setunitdata(.@npcId, UDT_HEADBOTTOM, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 25); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex = G_OTHER; + .distance = 5; + npcsit; + + tradertype(NST_CUSTOM); + + sellitem EquipmentBlueprintB,3000; + sellitem ShortSword,1800; + sellitem Chainmail,1400; + + sellitem BronzeGift,120; + sellitem CoinBag,60; + + sellitem ArrowAmmoBox,540; + sellitem Arrow,3; + sellitem CasinoCoins,4; + end; + +OnCountFunds: + setcurrency(countitem(HeroCoin)); + end; + +OnPayFunds: + if( countitem(HeroCoin) < @price ) + end; + delitem HeroCoin,@price; + purchaseok(); + end; + +} + +018-2-4,40,49,0 duplicate(Advanced Exchanger#0) Advanced Exchanger#1 NPC_M_COINKEEPER + diff --git a/npc/018-2-4/lv3.txt b/npc/018-2-4/lv3.txt new file mode 100644 index 0000000..e0927ce --- /dev/null +++ b/npc/018-2-4/lv3.txt @@ -0,0 +1,60 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Heroes Hold Exchanger - Level 3 + +018-2-4,47,28,0 script Master Exchanger#0 NPC_M_COINKEEPER,{ + .@q=getq2(LoFQuest_HH); + if (.@q & HH_ADVANCED) + openshop; + goodbye; + closedialog; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TopHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, WarlordPlate); + setunitdata(.@npcId, UDT_WEAPON, JeansChaps); + setunitdata(.@npcId, UDT_HEADBOTTOM, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 25); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex = G_OTHER; + .distance = 5; + npcsit; + + tradertype(NST_CUSTOM); + + sellitem Kanabo,5400; + sellitem EquipmentBlueprintC,4200; + sellitem ArcmageBoxset,3250; + + sellitem SteelShovel,2000; + sellitem MercBoxA,1200; + + sellitem SilverGift,480; + //sellitem Lockpicks,112; + + sellitem IronAmmoBox,1080; + sellitem IronArrow,6; + + end; + +OnCountFunds: + setcurrency(countitem(HeroCoin)); + end; + +OnPayFunds: + if( countitem(HeroCoin) < @price ) + end; + delitem HeroCoin,@price; + purchaseok(); + end; + +} + +018-2-4,47,49,0 duplicate(Master Exchanger#0) Master Exchanger#1 NPC_M_COINKEEPER + diff --git a/npc/018-2-4/lv4.txt b/npc/018-2-4/lv4.txt new file mode 100644 index 0000000..3855caa --- /dev/null +++ b/npc/018-2-4/lv4.txt @@ -0,0 +1,59 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Heroes Hold Exchanger - Level 4 + +018-2-4,54,28,0 script Ultimate Exchanger#0 NPC_M_COINKEEPER,{ + .@q=getq2(LoFQuest_HH); + if (.@q & HH_EXPERT) + openshop; + goodbye; + closedialog; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TopHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SaviorArmor); + setunitdata(.@npcId, UDT_WEAPON, JeansChaps); + setunitdata(.@npcId, UDT_HEADBOTTOM, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 25); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex = G_OTHER; + .distance = 5; + npcsit; + + tradertype(NST_CUSTOM); + + sellitem BlacksmithAxe,24000; + sellitem Judgement,14200; + + sellitem EquipmentBlueprintD,5500; + sellitem MercBoxB,2200; + sellitem GoldenGift,1920; + + sellitem IridiumOre,1600; + sellitem TitaniumOre,800; + + sellitem PoisonAmmoBox,2160; + sellitem PoisonArrow,12; + end; + +OnCountFunds: + setcurrency(countitem(HeroCoin)); + end; + +OnPayFunds: + if( countitem(HeroCoin) < @price ) + end; + delitem HeroCoin,@price; + purchaseok(); + end; + +} + +018-2-4,54,49,0 duplicate(Ultimate Exchanger#0) Ultimate Exchanger#1 NPC_M_COINKEEPER + diff --git a/npc/018-2-4/vault.txt b/npc/018-2-4/vault.txt new file mode 100644 index 0000000..9f33ea6 --- /dev/null +++ b/npc/018-2-4/vault.txt @@ -0,0 +1,23 @@ +// TMW2/LoF Script. +// Author: +// Jesusalva +// Notes: +// Based on BenB idea. + +018-2-4,23,24,0 script Vault#01824a NPC_NO_SPRITE,{ + LootableVault(2, 3, "01824"); + close; + +OnInit: + .distance=3; + end; + +OnClock0201: +OnClock1216: + $VAULT_01824+=rand2(15,35); + end; +} + + +018-2-4,23,45,0 duplicate(Vault#01824a) Vault#01824b NPC_NO_SPRITE + diff --git a/npc/018-2-5/_import.txt b/npc/018-2-5/_import.txt new file mode 100644 index 0000000..53f7e5b --- /dev/null +++ b/npc/018-2-5/_import.txt @@ -0,0 +1,2 @@ +// Map 018-2-5: Heroes Hold - Main Dungeon +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/018-2-6/_config.txt b/npc/018-2-6/_config.txt new file mode 100644 index 0000000..87f2d40 --- /dev/null +++ b/npc/018-2-6/_config.txt @@ -0,0 +1,71 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-2-6: Heroes Hold SS - B1F conf + +018-2-6,199,215,0 script #018-2-6_199_215 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; +OnTouch: +OnTouchNPC: + IronTrap(400, 15, 3); + end; +OnTimer15000: + stopnpctimer; setnpctimer 0; setnpcdisplay "#018-2-6_199_215", NPC_TRAP; end; +} + +018-2-6,216,223,0 script #018-2-6_216_223 NPC_TRAP,0,0,{ + mesn strcharinfo(0); + mesq l("Something seems off with that!"); + close; +OnTouch: +OnTouchNPC: + IronTrap(400, 15, 3); + end; +OnTimer15000: + stopnpctimer; setnpctimer 0; setnpcdisplay "#018-2-6_216_223", NPC_TRAP; end; +} + +018-2-6,161,99,0 script #018-2-6_161_99 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-6_161_99"; end; +OnEnable: +OnInit: + setcells "018-2-6", 161, 99, 163, 99, 1, "018-2-6_161_99"; +} + +018-2-6,156,101,0 script #018-2-6_156_101 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-6_156_101"; end; +OnEnable: +OnInit: + setcells "018-2-6", 156, 101, 156, 103, 1, "018-2-6_156_101"; +} + +018-2-6,186,108,0 script #018-2-6_186_108 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=3; + end; +} + +018-2-6,204,225,0 script #018-2-6_204_225 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-6_204_225"; end; +OnEnable: +OnInit: + setcells "018-2-6", 204, 225, 210, 225, 1, "018-2-6_204_225"; +} + +018-2-6,209,210,0 script #018-2-6_209_210 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-6_209_210"; end; +OnEnable: +OnInit: + setcells "018-2-6", 209, 210, 218, 210, 1, "018-2-6_209_210"; +} diff --git a/npc/018-2-6/_import.txt b/npc/018-2-6/_import.txt new file mode 100644 index 0000000..24cd9c7 --- /dev/null +++ b/npc/018-2-6/_import.txt @@ -0,0 +1,3 @@ +// Map 018-2-6: Heroes Hold SS - B1F +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-2-6/_config.txt", diff --git a/npc/018-2-7/_config.txt b/npc/018-2-7/_config.txt new file mode 100644 index 0000000..5556559 --- /dev/null +++ b/npc/018-2-7/_config.txt @@ -0,0 +1,29 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-2-7: Heroes Hold SS - B2F conf + +018-2-7,161,45,0 script #018-2-7_161_45 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-7_161_45"; end; +OnEnable: +OnInit: + setcells "018-2-7", 161, 45, 163, 45, 1, "018-2-7_161_45"; +} + +018-2-7,160,23,0 script #018-2-7_160_23 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-7_160_23"; end; +OnEnable: +OnInit: + setcells "018-2-7", 160, 23, 160, 37, 1, "018-2-7_160_23"; +} + +018-2-7,227,45,0 script #018-2-7_227_45 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-7_227_45"; end; +OnEnable: +OnInit: + setcells "018-2-7", 227, 45, 229, 45, 1, "018-2-7_227_45"; +} diff --git a/npc/018-2-7/_import.txt b/npc/018-2-7/_import.txt new file mode 100644 index 0000000..c6fb7da --- /dev/null +++ b/npc/018-2-7/_import.txt @@ -0,0 +1,3 @@ +// Map 018-2-7: Heroes Hold SS - B2F +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-2-7/_config.txt", diff --git a/npc/018-2-8/_config.txt b/npc/018-2-8/_config.txt new file mode 100644 index 0000000..da1889d --- /dev/null +++ b/npc/018-2-8/_config.txt @@ -0,0 +1,47 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-2-8: Heroes Hold SS - B3F conf + +018-2-8,108,60,0 script #018-2-8_108_60 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=3; + end; +} + +018-2-8,113,57,0 script #018-2-8_113_57 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-8_113_57"; end; +OnEnable: +OnInit: + setcells "018-2-8", 113, 57, 115, 57, 1, "018-2-8_113_57"; +} + +018-2-8,113,64,0 script #018-2-8_113_64 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-8_113_64"; end; +OnEnable: +OnInit: + setcells "018-2-8", 113, 64, 115, 64, 1, "018-2-8_113_64"; +} + +018-2-8,89,35,0 script #018-2-8_89_35 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-8_89_35"; end; +OnEnable: +OnInit: + setcells "018-2-8", 89, 35, 91, 35, 1, "018-2-8_89_35"; +} + +018-2-8,69,47,0 script #018-2-8_69_47 NPC_HIDDEN,{ + end; +OnDisable: + delcells "018-2-8_69_47"; end; +OnEnable: +OnInit: + setcells "018-2-8", 69, 47, 69, 49, 1, "018-2-8_69_47"; +} diff --git a/npc/018-2-8/_import.txt b/npc/018-2-8/_import.txt new file mode 100644 index 0000000..be9b71b --- /dev/null +++ b/npc/018-2-8/_import.txt @@ -0,0 +1,3 @@ +// Map 018-2-8: Heroes Hold SS - B3F +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-2-8/_config.txt", diff --git a/npc/018-2/_import.txt b/npc/018-2/_import.txt new file mode 100644 index 0000000..adb5e87 --- /dev/null +++ b/npc/018-2/_import.txt @@ -0,0 +1,4 @@ +// Map 018-2: Heroes' Hold - Outside +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-2/_mobs.txt", +"npc/018-2/_warps.txt", diff --git a/npc/018-2/_mobs.txt b/npc/018-2/_mobs.txt new file mode 100644 index 0000000..a27e664 --- /dev/null +++ b/npc/018-2/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-2: Heroes' Hold - Outside mobs +018-2,40,35,20,6 monster Moonshroom 1069,2,120000,40000 +018-2,21,46,0,0 monster Poison Fairy 1186,6,120000,40000 +018-2,93,37,29,10 monster Green Dragon 1195,2,120000,40000 +018-2,108,64,12,11 monster Earth Fairy 1182,4,120000,40000 +018-2,91,89,36,12 monster Grass Snake 1169,6,120000,40000 diff --git a/npc/018-2/_warps.txt b/npc/018-2/_warps.txt new file mode 100644 index 0000000..d529700 --- /dev/null +++ b/npc/018-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-2: Heroes' Hold - Outside warps +018-2,63,106,0 warp #018-2_63_106 0,0,017-1,180,194 +018-2,72,63,0 warp #018-2_72_63 3,0,018-2-1,26,32 diff --git a/npc/018-3/_import.txt b/npc/018-3/_import.txt new file mode 100644 index 0000000..54aa669 --- /dev/null +++ b/npc/018-3/_import.txt @@ -0,0 +1,5 @@ +// Map 018-3: Somber Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-3/_mobs.txt", +"npc/018-3/_warps.txt", +"npc/018-3/treasure.txt", diff --git a/npc/018-3/_mobs.txt b/npc/018-3/_mobs.txt new file mode 100644 index 0000000..5200a96 --- /dev/null +++ b/npc/018-3/_mobs.txt @@ -0,0 +1,18 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-3: Somber Cave mobs +018-3,81,91,1,1 monster Dark Lizard 1051,2,0,20000 +018-3,37,61,1,1 monster Dark Lizard 1051,2,0,20000 +018-3,45,114,1,1 monster Troll 1171,2,10000,60000 +018-3,75,53,1,1 monster Black Slime 1178,1,10000,20000 +018-3,115,116,2,2 monster Black Scorpion 1074,1,0,10000 +018-3,39,57,21,38 monster Lava Slime 1097,10,10000,0 +018-3,96,46,34,27 monster Lava Slime 1097,8,10000,0 +018-3,88,84,26,10 monster Lava Slime 1097,4,10000,0 +018-3,123,100,7,26 monster Lava Slime 1097,4,10000,0 +018-3,66,111,48,15 monster Lava Slime 1097,9,10000,0 +018-3,56,114,1,1 monster Black Scorpion 1074,1,0,10000 +018-3,41,76,1,1 monster Electro Worm 1173,1,0,30000 +018-3,95,94,8,4 monster Black Mamba 1174,3,20000,10000 +018-3,123,27,3,3 monster Shadow Plant 1189,2,60000,60000 +018-3,31,28,1,1 monster Gobo Bear 1214,1,0,20000 +018-3,74,72,56,53 monster Black Mamba 1174,8,20000,0 diff --git a/npc/018-3/_warps.txt b/npc/018-3/_warps.txt new file mode 100644 index 0000000..b044553 --- /dev/null +++ b/npc/018-3/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-3: Somber Cave warps +018-3,114,23,0 warp #018-3_114_23 0,0,017-2-1,27,126 +018-3,94,96,0 warp #018-3_94_96 0,0,018-4,169,114 diff --git a/npc/018-3/treasure.txt b/npc/018-3/treasure.txt new file mode 100644 index 0000000..c61aee7 --- /dev/null +++ b/npc/018-3/treasure.txt @@ -0,0 +1,64 @@ +// TMW2 Script + +// (Random) Treasure Chest +// Authored by Jesusalva +// Regenerates every 6 hours + +018-3,0,0,0 script #chest_01830 NPC_CHEST,{ + + if (!.busy && !.empty) { + TreasureBox(); + + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + .dir = .dir == 0 ? 2 : 6; // closed ? opening : closing + .busy = true; // lock until available again + initnpctimer; + } else if (!.busy) { + mesc l("Someone looted this treasure box already..."); + } else { + end; + } + close; + +OnTimer160: + .dir = .dir == 6 ? 0 : 4; // closing ? closed : open + end; + +OnTimer500: + .busy = false; // unlock + if (.dir == 0 || .dir == 4) + stopnpctimer; // stop here if the chest is closed + end; + +OnInit: + .busy = false; + .distance = 2; + .empty = false; + +OnClock0156: +OnClock0756: +OnClock1356: +OnClock1956: + // Try to warp randomly to a walkable spot, up to 20 attempts + // Otherwise, it'll stay where it already is (but will close and refill). + .@e=0; .@x=0; .@y=0; + while (!checkcell(.map$, .@x, .@y, cell_chkpass)) + { + if (.@e == 20) { + .@x=.x; + .@y=.y; + break; + } + // Remember the +20 -20 margin adjustment + .@x = rand(20, 130); + .@y = rand(20, 130); + ++.@e; + } + .busy=false; + .empty=false; + movenpc .name$, .@x, .@y, 0; + end; +} + +018-3,0,0,0 duplicate(#chest_01830) #chest_01831 NPC_CHEST + diff --git a/npc/018-4-1/_import.txt b/npc/018-4-1/_import.txt new file mode 100644 index 0000000..5c588d2 --- /dev/null +++ b/npc/018-4-1/_import.txt @@ -0,0 +1,4 @@ +// Map 018-4-1: sicave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-4-1/_mobs.txt", +"npc/018-4-1/_warps.txt", diff --git a/npc/018-4-1/_mobs.txt b/npc/018-4-1/_mobs.txt new file mode 100644 index 0000000..801f7d1 --- /dev/null +++ b/npc/018-4-1/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-4-1: Secret Island Cave mobs +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-4-1: sicave mobs +018-4-1,95,66,66,35 monster Cave Snake 1035,15,9000,3000 +018-4-1,95,84,66,18 monster Golden Scorpion 1078,7,10000,5000 +018-4-1,102,48,40,17 monster Giant Mutated Bat 1044,2,100000,300000 +018-4-1,142,64,27,44 monster Black Mamba 1174,3,100000,50000 +018-4-1,51,64,27,44 monster Troll 1171,2,90000,90000 +018-4-1,122,58,21,3 monster Shadow Plant 1189,3,30000,30000 diff --git a/npc/018-4-1/_warps.txt b/npc/018-4-1/_warps.txt new file mode 100644 index 0000000..070a064 --- /dev/null +++ b/npc/018-4-1/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-4-1: Secret Island Cave warps +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-4-1: sicave warps +018-4-1,102,86,0 warp #018-4-1_102_86 0,0,018-4,99,100 diff --git a/npc/018-4-2/_import.txt b/npc/018-4-2/_import.txt new file mode 100644 index 0000000..8ef7425 --- /dev/null +++ b/npc/018-4-2/_import.txt @@ -0,0 +1,4 @@ +// Map 018-4-2: Secret_Island_Indoor +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-4-2/_warps.txt", +"npc/018-4-2/susanne.txt", diff --git a/npc/018-4-2/_warps.txt b/npc/018-4-2/_warps.txt new file mode 100644 index 0000000..796a024 --- /dev/null +++ b/npc/018-4-2/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-4-2: Secret Island House warps +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-4-2: Secret_Island_Indoor warps +018-4-2,33,36,0 warp #018-4-2_33_36 0,0,018-4,78,36 diff --git a/npc/018-4-2/susanne.txt b/npc/018-4-2/susanne.txt new file mode 100644 index 0000000..165f0b1 --- /dev/null +++ b/npc/018-4-2/susanne.txt @@ -0,0 +1,179 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Slay the Fafi Dragon quest. Due the OP monster, you better find yourself a good team! +// {quest status, respawns issued} + +018-4-2,31,26,0 script Susanne NPC_FAIRY_A,{ + .@q=getq(LoFQuest_Fairy); + + if(BaseLevel < 53) goto L_Noob; + if(.@q == 1) goto L_Coward; + if(.@q == 2) goto L_Complete; + if(.@q >= 3) goto L_Thanks; + + mesn; + mesq l("Do you like fairies? Well, you should love them! We play with humans all the time, it is usually pretty fun..."); + next; + mesc l("(The girl now looks away sadly, thinking about what to say next.)"); + next; + mesn; + mesq l("But dragons came and started roaming this island. My friends are now either dead or missing. You look big and strong, do you want to avenge me?"); + goto L_Menu; + +L_Menu: + menu + l("Sure, I'd love to help! What can I do?"), L_Yes, + l("Nah, I've got more serious matters to attend to..."), L_No; + +L_Yes: + mesn; + mesq l("Thank you so much! That might not bring my friends back, but will make this island much safer!"); + next; + mesn; + mesq l("The Fafi Dragon are really really bad guys. I don't know how they came here, as they're not from the Lands Of Fire, nor from Mana World."); + next; + mesn; + mesq l("But this is not important. They are dangerous. They must be stop at any cost!"); + next; + mesn; + mesq l("So if you can help, go kill at least one on this island. They take a long time to respawn."); + next; + mesn; + mesq l("Thank you..."); + setq LoFQuest_Fairy, 1; + if (!mobcount("018-4","Susanne::OnKillFafi")) + goto L_Spawn; + close; + +OnKillFafi: + message strcharinfo(0), "You killed the Fafi Dragon."; + .@q=getq(LoFQuest_Fairy); + if (.@q == 1) { + setq LoFQuest_Fairy, 2, 0; + mapannounce "018-4-2", "" +strcharinfo(0)+ " has killed the Fafi Dragon!", 0; + mapannounce "018-4-1", "" +strcharinfo(0)+ " has killed the Fafi Dragon!", 0; + mapannounce "018-4", "" +strcharinfo(0)+ " has killed the Fafi Dragon!", 0; + mapannounce "017-1", "" +strcharinfo(0)+ " has killed the Fafi Dragon!", 0; + message strcharinfo(0), "Cheers are being heard throughout the land!"; + } + fix_mobkill(FafiDragon); + end; + +L_Complete: + mesn; + mesq l("Many thanks for killing it. Maybe more humans come here now, and play with me."); + next; + inventoryplace FafiMask, 1; + getitem FafiMask, 1; + getexp 80000, 200; + setq LoFQuest_Fairy, 3; + mesn; + mesq l("By the way, I found this Mask after you killed the Fafi Dragon, Maybe you can use it some day."); + next; + mesn; + mesq l("And some times, more Fafi dragons come... Don't neglect your aid."); + close; + +L_Thanks: + mesn; + mesq l("Sometimes humans come here and play with me. Many thanks for the help!"); + if (!mobcount("018-4","Susanne::OnKillFafi") && .respawnTime <= gettimetick(2)) { + next; + mesn; + mesq l("Actually, about Fafi dragons..."); + next; + goto L_Check; + } + close; + +L_No: + mesn; + mesq l("Ok...please come back when you aren't busy..."); + next; + mesc l("The girl turns around and you hear her sniffing, she is probably crying..."); + close; + +L_Noob: + mesn; + mesq l("Hey, have you come to play with me? I love playing with humans! Usually so few show in, it must be the dragons..."); + //mesq l("I need some help with something, but I don't think you're strong enough. Come back later please when you're stronger."); + close; + +L_Coward: + if (!mobcount("018-4","Susanne::OnKillFafi")) + goto L_Check; + mesn; + mesq l("What are you doing talking to me? Go fight, you coward!"); + close; + +L_Check: + if (.respawnTime > gettimetick(2) && (!mobcount("018-4","Susanne::OnKillFafi"))){ + mesn; + mesq l("Just wait. Fafi dragons are slow to show up..."); + } else { + .@q=limit(0, getq2(LoFQuest_Fairy), 3); + // First time is ALWAYS free + if (!.@q) + goto L_Spawn; + + mesn; + mesq l("The Fafi knows you are here to kill him. They are smart. But I can make a trap."); + next; + mesn; + mesq l("Bring me this and I'll spawn it for you:"); + // .@q valid values are 1 (Starter) and 3 (Veteran) + mesc l("@@/@@ @@", countitem(ShadowHerb), .@q*5, getitemlink(ShadowHerb)); + mesc l("@@/@@ @@", countitem(Moss), .@q*4, getitemlink(Moss)); + mesc l("@@/@@ @@", countitem(Root), .@q*3, getitemlink(Root)); + next; + mesn; + mesq l("I'll be attracted by the Shadow Herb. The moss will hide the roots, which will entrap it for a while."); + mesq l("So, do you have the items?"); + if (askyesno() != ASK_YES) { + close; + } + mes ""; + if (countitem(ShadowHerb) < .@q*5 || + countitem(Moss) < .@q*4 || + countitem(Root) < .@q*3) goto L_Fail; + if (.respawnTime > gettimetick(2)) { + mesn; + mesq l("Too slow."); + close; + } + delitem ShadowHerb, .@q*5; + delitem Moss, .@q*4; + delitem Root, .@q*3; + goto L_Spawn; + } + close; + +L_Spawn: + if (.respawnTime > gettimetick(2)){ + mesn; + mesq l("Just wait. Fafi dragons are slow to show up..."); + } else { + mesn; + mesc l("Screams"); + mesq l("I hear a Fafi Dragon on THIS very island!!"); + .respawnTime=gettimetick(2)+(60*30); + monster "018-4", any(64,83,100,123,139), any(64,74,89), strmobinfo(1, FafiDragon), FafiDragon, 1, "Susanne::OnKillFafi"; + setq2 LoFQuest_Fairy, getq2(LoFQuest_Fairy)+1; + } + close; + +L_Fail: + mesn; + mesq l("You're lucky that this is the Land Of Fire and I'm on a good mood, otherwise, I would have killed you for lying to me."); + close; + +OnInit: + .respawnTime=0; + .sex=G_FEMALE; + .distance=5; + end; + +} diff --git a/npc/018-4/_import.txt b/npc/018-4/_import.txt new file mode 100644 index 0000000..17bc538 --- /dev/null +++ b/npc/018-4/_import.txt @@ -0,0 +1,4 @@ +// Map 018-4: Secret Island +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-4/_mobs.txt", +"npc/018-4/_warps.txt", diff --git a/npc/018-4/_mobs.txt b/npc/018-4/_mobs.txt new file mode 100644 index 0000000..fbb94a6 --- /dev/null +++ b/npc/018-4/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-4: Secret Island mobs +018-4,125,78,46,21 monster Golden Scorpion 1078,7,10000,5000 +018-4,103,57,46,32 monster Black Scorpion 1074,5,20000,10000 +018-4,103,57,46,32 monster Black Slime 1178,10,15000,10000 +018-4,96,70,76,47 monster Red Slime 1092,12,12000,4000 +018-4,96,70,76,47 monster Yellow Slime 1091,15,30000,30000 +018-4,96,66,76,43 monster Clover Patch 1028,2,75000,35000 +018-4,96,70,76,47 monster Mouboo 1023,3,12000,4000 +018-4,103,76,40,12 monster Wicked Mushroom 1176,2,85000,65000 diff --git a/npc/018-4/_warps.txt b/npc/018-4/_warps.txt new file mode 100644 index 0000000..99e7a8d --- /dev/null +++ b/npc/018-4/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-4: Secret Island warps +018-4,99,99,0 warp #018-4_99_99 0,0,018-4-1,102,84 +018-4,169,115,0 warp #018-4_169_115 0,0,018-3,94,95 +018-4,78,34,0 warp #018-4_78_34 0,0,018-4-2,33,34 diff --git a/npc/018-5-0/_import.txt b/npc/018-5-0/_import.txt new file mode 100644 index 0000000..d7a946e --- /dev/null +++ b/npc/018-5-0/_import.txt @@ -0,0 +1,5 @@ +// Map 018-5-0: Heroes' Hold - Exchange Hall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-5-0/_mobs.txt", +"npc/018-5-0/_warps.txt", +"npc/018-5-0/core.txt", diff --git a/npc/018-5-0/_mobs.txt b/npc/018-5-0/_mobs.txt new file mode 100644 index 0000000..90eb88b --- /dev/null +++ b/npc/018-5-0/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-0: Heroes' Hold - Exchange Hall mobs +018-5-0,79,38,59,18 monster Bluepar 1177,40,30000,30000 diff --git a/npc/018-5-0/_warps.txt b/npc/018-5-0/_warps.txt new file mode 100644 index 0000000..2b05b60 --- /dev/null +++ b/npc/018-5-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-0: Heroes' Hold - Exchange Hall warps +018-5-0,139,25,0 warp #018-5-0_139_25 0,9,018-5,39,133 diff --git a/npc/018-5-0/core.txt b/npc/018-5-0/core.txt new file mode 100644 index 0000000..978c738 --- /dev/null +++ b/npc/018-5-0/core.txt @@ -0,0 +1,28 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Controls the swimming minigame. Basically, you lose 1% HP if lack total status. + +018-5-0,0,0,0 script #01850SwimmingCtrl NPC_HIDDEN,{ + end; + +OnLoop: + if (getmap() != "018-5-0") + end; + if (ispcdead()) + end; + // Sum everything - you can have 70 str instead of splitting in 35 str and 35 vit + .@status=readparam2(bStr)+readparam2(bVit); + .@reqst=.reqstr+.reqvit; + debugmes "Got %d/%d (%d), heal %d", .@status,.@reqst,.@status-.@reqst,min(0, .@status-.@reqst); + //percentheal min(0, .@status-.@reqst), 0; + heal (min(0, .@status-.@reqst)*3), 0; + addtimer(3000, "#01850SwimmingCtrl::OnLoop"); + end; + +OnInit: + .reqstr=35; + .reqvit=35; + end; +} diff --git a/npc/018-5-1/_import.txt b/npc/018-5-1/_import.txt new file mode 100644 index 0000000..54ff227 --- /dev/null +++ b/npc/018-5-1/_import.txt @@ -0,0 +1,4 @@ +// Map 018-5-1: Mountain Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-5-1/_mobs.txt", +"npc/018-5-1/_warps.txt", diff --git a/npc/018-5-1/_mobs.txt b/npc/018-5-1/_mobs.txt new file mode 100644 index 0000000..92d2bba --- /dev/null +++ b/npc/018-5-1/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-1: Mountain Cave mobs +018-5-1,62,64,61,51 monster Alizarin Plant 1188,16,30000,30000 +018-5-1,80,53,27,35 monster Snake 1122,8,30000,30000 +018-5-1,60,76,43,27 monster Black Slime 1178,9,30000,30000 +018-5-1,64,63,27,27 monster Dark Lizard 1051,10,30000,30000 +018-5-1,61,75,38,34 monster Black Scorpion 1074,10,30000,30000 +018-5-1,81,53,22,31 monster Black Mamba 1174,1,90000,60000 +018-5-1,65,100,2,2 monster Shadow Pixie 1217,1,300000,30000 +018-5-1,77,79,2,2 monster Holy Pixie 1216,1,300000,30000 diff --git a/npc/018-5-1/_warps.txt b/npc/018-5-1/_warps.txt new file mode 100644 index 0000000..d3a3e60 --- /dev/null +++ b/npc/018-5-1/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-1: Mountain Cave warps +018-5-1,36,99,0 warp #018-5-1_36_99 0,0,018-5,60,147 +018-5-1,79,68,0 warp #018-5-1_79_68 0,0,018-5,99,83 +018-5-1,77,54,0 warp #018-5-1_77_54 0,0,018-5,97,69 diff --git a/npc/018-5-2/_import.txt b/npc/018-5-2/_import.txt new file mode 100644 index 0000000..ef18d80 --- /dev/null +++ b/npc/018-5-2/_import.txt @@ -0,0 +1,4 @@ +// Map 018-5-2: Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-5-2/_warps.txt", +"npc/018-5-2/leona.txt", diff --git a/npc/018-5-2/_warps.txt b/npc/018-5-2/_warps.txt new file mode 100644 index 0000000..0465f07 --- /dev/null +++ b/npc/018-5-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-2: Indoors warps +018-5-2,33,47,0 warp #018-5-2_33_47 1,0,018-5,114,55 diff --git a/npc/018-5-2/leona.txt b/npc/018-5-2/leona.txt new file mode 100644 index 0000000..5b8d1f6 --- /dev/null +++ b/npc/018-5-2/leona.txt @@ -0,0 +1,117 @@ +// TMW2/LoF scripts. +// Authors: +// TMW BR Team +// Jesusalva +// Description: +// Exchanges Mountain Snake Plate (TBD) for a Nymph Necklace (TBD) +// Grand Hunter Quest + +018-5-2,33,36,0 script Leona NPC_FAIRY_B,{ + function leona_exchange; + mesn; + mesq l("Hello, @@!", get_race()); + next; + mesn; + mesq l("Do you have something to exchange with me? Or perhaps you want a Grand Hunter Quest?"); + next; + select + l("I've brought something to exchange."), + l("I'm interested in Grand Hunter Quest."), + l("Ops, sorry. I was going to the Soul Menhir and entered your house by accident."); + mes ""; + switch (@menu) { + case 3: + mesn; + mesq l("It happens."); + close; + case 2: + GHQ_Assign(MountainSnake, "Lilit"); + close; + case 1: + mesn; + mesq l("The most famous nymphs, are those who wear stuff made of Snake Skin."); + next; + mesn; + mesq l("Perhaps you have something like that?"); + next; + do + { + mesc l("What to exchange with Leona?"); + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .@id = requestitem(); + + // If ID is invalid, there's not enough items or if it is bound + if (.@id < 1) close; + if (countitem(.@id) < 1) close; + if (checkbound(.@id)) + { + mesc l("You cannot part with this item!"); + continue; + } + + // TODO: Check if item is OK + switch (.@id) { + // Specific Exchange + /* + case Backsword: + leona_exchange(.@id, ShortSword); + break; + */ + + // Generic Exchange + case LeatherShirt: + case LeatherBoots: + case LeatherGloves: + case JeansChaps: + case LeatherTrousers: + case SnakeSkin: + case MountainSnakeSkin: + case CaveSnakeSkin: + leona_exchange(.@id, 0); + break; + case BlackMambaSkin: + leona_exchange(.@id, FluoPowder); + break; + default: + mesn; + mesq l("I have no interest on this item."); + next; + break; + } + + } while (true); + } + close; + + // leona_exchange ( give, receive ) + // Receive should be item ID. If it is 0, you will get 1.4× the sell price + function leona_exchange { + .@what=getarg(0); + .@reward=getarg(1); + if (!.@reward) + .@gp=getiteminfo(.@what, ITEMINFO_SELLPRICE)*14/10; + mesn; + if (.@reward) + mesq l("For this @@, I offer you a(n) @@.", getitemlink(.@what), getitemlink(.@reward)); + else + mesq l("For this @@, I offer you @@ GP.", getitemlink(.@what), format_number(.@gp)); + next; + mesc l("Exchange the item with Leona?"); + if (askyesno() == ASK_YES) + { + delitem .@what, 1; + if (.@reward) + getitem .@reward, 1; + else + Zeny+=.@gp; + mesn; + mesq l("Many thanks! I'll be sooooo fashionable now!"); + next; + } + return; + } +OnInit: + .distance=5; + end; +} diff --git a/npc/018-5-3/_import.txt b/npc/018-5-3/_import.txt new file mode 100644 index 0000000..a61f6de --- /dev/null +++ b/npc/018-5-3/_import.txt @@ -0,0 +1,4 @@ +// Map 018-5-3: Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-5-3/_warps.txt", +"npc/018-5-3/shaabty.txt", diff --git a/npc/018-5-3/_warps.txt b/npc/018-5-3/_warps.txt new file mode 100644 index 0000000..66af0d9 --- /dev/null +++ b/npc/018-5-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-3: Indoors warps +018-5-3,33,47,0 warp #018-5-3_33_47 1,0,018-5,83,49 diff --git a/npc/018-5-3/shaabty.txt b/npc/018-5-3/shaabty.txt new file mode 100644 index 0000000..4d2f47e --- /dev/null +++ b/npc/018-5-3/shaabty.txt @@ -0,0 +1,151 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Description: +// Leather Trousers quest (lv 48) +// Variable: +// LilitQuest_Shaabty +// 0 - Not enough level +// 1 - Quest assigned +// 2 - Requested the material +// 3 - Craft is in progress +// 4 - Quest Complete +// Var2: +// Controls how many snakes you killed down there. +// It counts regardless of quest state. +// Var3: +// Timer for state 3 + +018-5-3,29,38,0 script Shaabty NPC_FAIRY_A,{ + function LTask; + .@q=getq(LilitQuest_Shaabty); + switch (.@q) { + case 0: + goto L_Start; break; + case 1: + goto L_Return; break; + case 2: + goto L_Craft; break; + case 3: + goto L_Craft2; break; + case 4: + goto L_Start; break; + } + Exception(l("Invalid quest state: @@", .@q), RB_DEFAULT|RB_SPEECH|RB_ISFATAL^RB_DISPBOTTOM); + close; + +L_Start: + mesn l("Shaabty the Fairy"); + mesq l("I always wanted to live in this town. I'm glad I managed to move from LoF Village to here."); + next; + mesn l("Shaabty the Fairy"); + mesq l("A shame there are snakes below the ground. I really, really hate snakes, like every fairy."); + if (BaseLevel < 45 || .@q >= 3) + close; + next; + mesn l("Shaabty the Fairy"); + mesq l("Maybe you could do me a favour, though?"); + setq1 LilitQuest_Shaabty, 1; + LTask(); + close; + +function LTask { + mesn l("Shaabty the Fairy"); + mesq l("I would love to see the skin of two @@.", getmonsterlink(MountainSnake)); + next; + mesn l("Shaabty the Fairy"); + mesq l("They live below this town. Can you do this for me?"); + next; + return; +} + +L_Return: + LTask(); + .@q2=getq2(LilitQuest_Shaabty); + // 0.98% DR (skin) - Reference is 0.32% + // 2 snake skins should take ~200 kills + if (countitem(MountainSnakeSkin) >= 2) { + if (.@q2 < 150) { + mesn l("Shaabty the Fairy"); + mesq l("I see two snake skins, but you didn't killed them down there. That defeats the whole purpose."); + } else { + goto L_Finish; + } + } else { + mesn l("Shaabty the Fairy"); + mesq l("That's not a couple of skins."); + } + close; + +L_Finish: + mesc l("Deliver @@ @@ to @@?", 2, getitemlink(MountainSnakeSkin), .name$), 1; + next; + if (askyesno() == ASK_YES) { + delitem MountainSnakeSkin, 2; + setq1 LilitQuest_Shaabty, 2; + getexp 20000, 200; + mesn l("Shaabty the Fairy"); + mesq l("Thanks. Please come back later, there is something I want to do for you."); + } + close; + +L_Craft: + mesn l("Shaabty the Fairy"); + mesq l("So, I was thinking in crafting a @@ for you.", getitemlink(LeatherTrousers)); + next; + mesn l("Shaabty the Fairy"); + mes l("For that, I'll need @@/@@ @@.", countitem(LeatherPatch), 10, getitemlink(LeatherPatch)); + mes l("And a commission of @@/@@ GP.", format_number(Zeny), format_number(8000)); + if (countitem(LeatherPatch) < 10 || + Zeny < 8000) + close; + next; + mesc l("Pay for her handi-work?"); + if (askyesno() == ASK_YES) { + delitem LeatherPatch, 10; + Zeny-=8000; + setq1 LilitQuest_Shaabty, 3; + //setq3 LilitQuest_Shaabty, gettimeparam(GETTIME_HOUR)+2; + setq3 LilitQuest_Shaabty, gettimetick(2)+7200; + mesn l("Shaabty the Fairy"); + mesq l("Thanks! Please come back in two hours, then I'll have the trousers ready."); + } + close; + +// Each time you bother her, she loses the line; +L_Craft2: + .@q3=getq3(LilitQuest_Shaabty); + if (gettimetick(2) < .@q3) { + mesn l("Shaabty the Fairy"); + .@mg$=any(l("stop interrupting me while I make your trousers."), l("stop making me lose the line by talking to me.")); + mesq l("Please be patient and "+.@mg$); + mesc l("Remaining time: @@", FuzzyTime(.@q3)); + setq3 LilitQuest_Shaabty, .@q3+rand2(1,3); + } else { + inventoryplace LeatherTrousers, 1; + getitem LeatherTrousers, 1; + getexp 0, 1000; + setq3 LilitQuest_Shaabty, 0; + setq1 LilitQuest_Shaabty, 4; + mesn l("Shaabty the Fairy"); + mesq l("Here you go. Please enjoy and thanks for making this town better to live. Maybe one day the snakes leave ^.^"); + } + close; + +///////////////////////////////////////////////////////// +OnKillSnake: + .@q1=getq(LilitQuest_Shaabty); + .@q2=getq2(LilitQuest_Shaabty); + setq2 LilitQuest_Shaabty, .@q2+1; + if (.@q1 == 1) { + if (! (.@q2+1) % 10) + dispbottom l("@@ @@ killed.", .@q2+1, getmonsterlink(MountainSnake)); + } + fix_mobkill(MountainSnake); + end; + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} diff --git a/npc/018-5-4/_import.txt b/npc/018-5-4/_import.txt new file mode 100644 index 0000000..13cfc00 --- /dev/null +++ b/npc/018-5-4/_import.txt @@ -0,0 +1,6 @@ +// Map 018-5-4: Duck Island +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-5-4/_mobs.txt", +"npc/018-5-4/_warps.txt", +"npc/018-5-4/elder.txt", +"npc/018-5-4/mapflags.txt", diff --git a/npc/018-5-4/_mobs.txt b/npc/018-5-4/_mobs.txt new file mode 100644 index 0000000..4db4efe --- /dev/null +++ b/npc/018-5-4/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-4: Duck Island mobs +018-5-4,39,70,16,8 monster Mana Piou 1155,8,35000,270000 +018-5-4,49,56,12,9 monster Vicious Squirrel 1187,8,35000,120000 +018-5-4,35,29,9,6 monster Piou 1002,6,200000,35000 +018-5-4,46,50,26,30 monster Duck 1029,23,200000,35000 +018-5-4,63,50,7,6 monster Piou 1002,6,200000,35000 +018-5-4,44,58,5,5 monster Forest Piou 1202,4,35000,120000 diff --git a/npc/018-5-4/_warps.txt b/npc/018-5-4/_warps.txt new file mode 100644 index 0000000..e4997c9 --- /dev/null +++ b/npc/018-5-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-4: Duck Island warps +018-5-4,35,44,0 warp #018-5-4_35_44 0,0,018-5,110,47 diff --git a/npc/018-5-4/elder.txt b/npc/018-5-4/elder.txt new file mode 100644 index 0000000..d3b0181 --- /dev/null +++ b/npc/018-5-4/elder.txt @@ -0,0 +1,421 @@ +// TMW2 Script +// Author: +// dangerDuck +// Description: +// Duck Elder is a friendly npc. +// The first stage of the Elder's quest consists of a supply restock +// Second/third stage of the quest consists of a pirate attack +// Fourth (last) stage will be the Duck Dungeon, an HH-like training ground for initiates of the Duck Side (WIP) +// Variables: +// LilitQuest_PiratesOfSARAH + +018-5-4,28,73,0 script Duck Elder NPC_DUCK_ELDER,{ + function elderQuest; + function elderSupplyMenu; + function elderSupplyList; + function elderSupplyGive; + function elderPirateAttack; + function elderPirateVictory; + function elderPirateDefeat; + function elderPirateReward; + function elderAbout; + function elderAboutSarah; + function elderInitiate; + function elderClose; + .@q=getq(LilitQuest_PiratesOfSARAH); + mesn; + mesq l("Hello %s. What brings you here?", get_race()); + next; + select + rif(.@q == 0, l("I've heard tidings of strange goings-on in this area...")), + rif(.@q == 1, l("I'm here to help you restock...")), + rif(.@q == 2, l("What did the Council of Elders say? Is there any news of the pirates?")), // FIXME Lv50 players missed this info S: + rif(.@q == 3, l("Have the pirates been routed? Will they return?")), + rif(.@q == 4, l("I'm here as an Initiate. May I enter Duck Dungeon?")), + l("Oh, I'm just exploring. What can you tell me about this island?"), + l("I came to enjoy a beautiful day with some ducks!"); + mes ""; + switch (@menu) { + case 1: + elderQuest(); + break; + case 2: + elderSupplyMenu(); + break; + case 3: + elderPirateAttack(); + break; + case 4: + elderPirateReward(); + break; + case 5: + elderInitiate(); + break; + case 6: + elderAbout(); + // fallthrough + default: + elderClose(); + break; + } + close; + +function elderQuest { + if (BaseLevel < 50) { + mesn; + mesq l("Unfortunately, a mere fledgling like you is not strong enough to help us."); + next; + elderClose(); + return; + } + mesn; + mesq l("Your information is correct, %s. Pirates have been plaguing our shores and cutting off our supply lines.", get_race()); + next; + mesn; + mesq l("We are in desperate need of food and nesting material, as well as our sacred supply of @@.", getitemlink(CrystallizedMaggot)); + next; + setq LilitQuest_PiratesOfSARAH, 1; + elderSupplyList(); + elderClose(); + return; +} + +function elderSupplyMenu { + do + { + mesn; + mesq l("Did you bring the supplies?"); + next; + select + l("Yes, I have them right here."), + l("What did you want? I've forgotten."), + l("Not yet. I've been... delayed."); + mes ""; + switch (@menu) { + case 1: + elderSupplyGive(); + break; + case 2: + elderSupplyList(); + break; + case 3: + elderClose(); + close; + break; + } + } while (true); + return; +} + +function elderSupplyList { + mesq ""; + mesn; + mesq l("We need:"); + mesq l("%d/%d %s", countitem(Moss), 300, getitemlink(Moss)); + mesq l("%d/%d %s", countitem(RoastedMaggot), 250, getitemlink(RoastedMaggot)); + mesq l("%d/%d %s", countitem(FishBox), 20, getitemlink(FishBox)); + mesq l("%d/%d %s", countitem(CrystallizedMaggot), 1, getitemlink(CrystallizedMaggot)); + next; + mesn; + mesq l("Of course, we'll find a way to reward you for your efforts."); + next; + return; +} + +function elderSupplyGive { + if (countitem(Moss) < 300 || + countitem(RoastedMaggot) < 250 || + countitem(FishBox) < 20 || + countitem(CrystallizedMaggot) < 1) { + mesn; + mesq l("This isn't what we requested. Perhaps you should count your items more carefully."); + next; + mesn; + mesq l("If you aren't more careful, we might... accidentally... mistake you for an agent of S.A.R.A.H."); + percentheal -99, 0; + close; + } + inventoryplace RubberDucky, 1; + + delitem(Moss, 300); + delitem(RoastedMaggot, 250); + delitem(FishBox, 20); + delitem(CrystallizedMaggot, 1); + + getitem(RubberDucky, 1); + getexp(34576, 19226); + setq(LilitQuest_PiratesOfSARAH, 2); + + mes ""; + mesn; + mesq l("We appreciate your help. Take this %s as a token of goodwill.", getitemlink(RubberDucky)); + next; + mesn; + mesq l("If you're ever in need, you may use it to call upon the aid of the ducks."); + next; + if (BaseLevel < 75) { + mesn; + mesq l("Go now, with the blessing of ducks. There may come a time when we require your aid once again, O %s.", get_race()); + } else { + mesn; + mesq l("We may require your aid again soon. The tritan pirates are amassing and we fear they are planning a siege of Duck Island. I must speak with the Council of Elders..."); + } + close; +} + +function elderPirateAttack { + // Level requirement + if (BaseLevel < 75) { + mesn; + mesq l("The pirates amass for their siege, but they will not attack yet. Return when you are stronger. King DD is working to find a solution."); + return; + } + + mesn; + mesq l("The pirates have been surrounding our island for many sun rises. We expect they will attack any time."); + next; + mesn; + mesq l("The Council of Elders has enlisted ducks of our island into a militia. In addition, the masters of the Duck Side have seen fit to spare %d Duck initiates to assist us.", (BaseLevel/20)); + next; + mesn; + mesq l("Please be careful when fighting. A mighty warrior such as yourself can harm their allies with any AoE effects they may use."); + next; + mesc l("Accept quest?"), 1; + if (askyesno() == ASK_NO) + return; + + .@mapn$="duck@"+getcharid(0); + + // Build instance if it doesn't exists or has been reallocated + if (instanceowner(@duckinst) != getcharid(3)) { + @duckinst = instance_create("duck@a"+getcharid(0), getcharid(3), IOT_CHAR); + instance_attachmap("018-5-4", @duckinst, false, .@mapn$); + // Instance lasts 6 minutes + instance_set_timeout(360, 360, @duckinst); + instance_init(@duckinst); + } else { + // Restart instance timer if it already exists + instance_set_timeout(360, 360, @duckinst); + } + getmapxy(.@m$, .@x, .@y, 0); + warp .@mapn$, .@x, .@y; + //debugmes "Warp successful"; + //debugmes "Prepare timer, target: %s in %d - aka. %s", .name$, @duckinst, instance_npcname(.name$, @duckinst); + addtimer(300, .name$+"::OnBegin"); + closeclientdialog; + close; + return; +} + +OnBegin: + //debugmes "BEGIN NPC %s, char %s. Map %s", instance_npcname(.name$, @duckinst), strcharinfo(0), getmap(); + .@n$=instance_npcname(.name$, @duckinst); + .@m$=getmap(); + killmonsterall(getmap()); + + @pLvl = BaseLevel; + @pirate_killed = 0; + @duck_killed = 0; + @total_pirates = 0; + @total_ducks = 0; + setnpcdisplay .@n$, NPC_NO_SPRITE; + sleep2(1000); + mapannounce(getmap(), "Duck Elder : Here they come...", bc_map); + sleep2(1000); + mapannounce(getmap(), "Duck Elder : DUCKS TO ARMS! KILL ALL, SHOW NO MERCY!", bc_map); + sleep2(1000); + mapannounce(getmap(), "##2Victory: Kill all pirates within 5 minutes. Protect the duck fighters.", bc_map); + mapannounce(getmap(), "##1Defeat: All ducks are slain.", bc_map); + + // Spawn Monsters + // FIXME MIGHT NOT WORK WITH COORDINATES ZERO (for several reasons) + // PLEASE PREFER AREAMONSTER + monster(.@m$, 0, 0, "Ocean Pirate", OceanPirate, (@pLvl/2), "Duck Elder::OnPirateKilled"); + @total_pirates+=(@pLvl/2); + monster(.@m$, 0, 0, "Pirate Captain", OceanPirate, (@pLvl/15), "Duck Elder::OnPirateKilled"); // FIXME + @total_pirates+=(@pLvl/15); + monster(.@m$, 0, 0, "Desert Pirate", DesertBandit, (@pLvl/3), "Duck Elder::OnPirateKilled"); + @total_pirates+=(@pLvl/3); + monster(.@m$, 0, 0, "Marsh Pirate", Bandit, (@pLvl/4), "Duck Elder::OnPirateKilled"); + @total_pirates+=(@pLvl/4); + monster(.@m$, 0, 0, "Buccaneer", Sarracenus, (@pLvl/7), "Duck Elder::OnPirateKilled"); + @total_pirates+=(@pLvl/7); + monster(.@m$, 0, 0, "Corsair", RobinBandit, (@pLvl/7), "Duck Elder::OnPirateKilled"); + @total_pirates+=(@pLvl/7); + monster(.@m$, 0, 0, "Pirate Lord", BanditLord, (@pLvl/12), "Duck Elder::OnPirateKilled"); + @total_pirates+=(@pLvl/12); + monster(.@m$, 0, 0, "Pirate Assassin", HoodedAssassin, (@pLvl/25), "Duck Elder::OnPirateKilled"); + @total_pirates+=(@pLvl/25); + debugmes "Pirates of SARAH, \"%s\" - Total: %d (Lv %d)", strcharinfo(0), @total_pirates, @pLvl; + + // Reinforcements + // FIXME: We might not want summon() here (summon is immune to AOE) + // We probably want bg_monster (blame Jesusalva) or monster w/ alchemy flag + for(.@i = 0; .@i < min(10, @pLvl/5); ++.@i) { + .@sd=summon("Duck Soldier", Duck, 300000, "Duck Elder::OnDuckKilled"); + .@bhp=getunitdata(.@sd, UDT_MAXHP); + // Abizit makes bonus HP vary (like AdjustSpellpower) + .@lvx = .@bhp * (80 + abizit() * rand2(5,10)) / 100; + setunitdata(.@sd, UDT_MAXHP, .@lvx); + setunitdata(.@sd, UDT_HP, .@lvx); + @total_duck++; + } + for(.@i = 0; .@i < min(5, @pLvl/20); ++.@i) { + .@sd=summon("Duck Initiate", EliteDuck, 300000, "Duck Elder::OnDuckKilled"); + .@bhp=getunitdata(.@sd, UDT_MAXHP); + // Abizit makes bonus HP vary (like AdjustSpellpower) + .@lvx = .@bhp * (80 + abizit() * rand2(5,10)) / 100; + setunitdata(.@sd, UDT_MAXHP, .@lvx); + setunitdata(.@sd, UDT_HP, .@lvx); + @total_duck++; + } + + // Begin timer + //initnpctimer(); + addtimer 300000, "Duck Elder::OnTimerQuit"; + end; + +OnPirateKilled: + @pirate_killed++; + .@mia=@total_ducks-@duck_killed; + if (.@mia < mobcount(getmap(), "Duck Elder::OnDuckKilled")) + @duck_killed=@total_ducks-mobcount(getmap(), "Duck Elder::OnDuckKilled"); + if (@duck_killed >= @total_ducks) { + elderPirateDefeat(); + } + if (@pirate_killed >= @total_pirates) { + elderPirateVictory(); + } + end; + +OnDuckKilled: + if (!playerattached()) + end; + @duck_killed++; + if (@duck_killed >= @total_ducks) { + elderPirateDefeat(); + } + end; + +OnTimer300000: + elderPirateDefeat(); + end; + +// Do this even work O.o +OnTimerQuit: + elderPirateDefeat(); + end; + +function elderPirateVictory { + stopnpctimer(); + @pirate_killed = 0; + @duck_killed = -50; // Set to -50 to make sure defeat doesn't trigger when monsters are killed + announce("Duck Elder : Success! The pirates have been thwarted.", bc_self); + .@n$=instance_npcname(.name$, @duckinst); + setnpcdisplay .@n$, NPC_DUCK_ELDER; + setq(LilitQuest_PiratesOfSARAH, 3); + getmapxy(.@m$, .@x, .@y, 0); + killmonsterall(.@m$); + warp "018-5-4", .@x, .@y; + sleep2(10); + dispbottom l("Duck Elder : Success! The pirates have been thwarted."); + end; +} + +function elderPirateDefeat { + //stopnpctimer(); + @pirate_killed = -50; // Set to -50 to make sure victory doesn't trigger when monsters are killed + @duck_killed = -50; + announce("Duck Elder : We have failed. The pirates will regroup and replenish their fallen. They will strike again, until we are defeated.", bc_self); + .@n$=instance_npcname(.name$, @duckinst); + setnpcdisplay .@n$, NPC_DUCK_ELDER; + getmapxy(.@m$, .@x, .@y, 0); + killmonsterall(.@m$); + warp "018-5-4", .@x, .@y; + sleep2(10); + dispbottom l("Duck Elder : We have failed. The pirates will regroup and replenish their fallen. They will strike again, until we are defeated."); + end; +} + +function elderPirateReward { + mesn; + mesq l("We are no longer in danger, thanks to you. The pirates were thoroughly defeated. None survived the battle."); + next; + mesn; + mesq l("The masters of the Duck Side, The Council of Elders, and King DD himself, on behalf of all ducks, thank you for your help. We wouldn't have survived without it."); + getexp(458593, 59505); + setq(LilitQuest_PiratesOfSARAH, 4); + next; + mesn; + mesq l("The masters have decided you are to be given honorary initiation into the Duck Side. The Duck Side is powerful, much more so than you could possibly understand."); + next; + mesn; + mesq l("You must understand that this honor has never been granted to a non-duck before. You will have access to Duck Dungeon, a training grounds for Initiates."); + next; + mesn; + mesq l("As an honorary member of the Duck Side, know that we are more than we appear. The Moubootaur itself has seen fit to reward us when it awakens. Do not attempt to cross us."); + // NOTE: The Moubootaur is "evil"; Therefore, Duck Side is evil too + // (This is a de facto standard; A global truth any specie which knows + // the Moubootaur Legend is aware of) + close; + return; +} + +function elderInitiate { + mesn; + mesq l("We are deliberating. Some do not... approve of your initiation. Leave."); + mesc "* "+l("Kolchak and dangerDuck are working to finish Duck Dungeon. Contact them for updates..."); + return; +} + +function elderAbout { + mesn; + mesq l("This is Duck Island, the last safe refuge for birds. Ducks tend to be very peaceful, but our young ruler, King DD, has been taken up with the art of war."); + next; + mesn; + mesq l("Unfortunately, he has been corrupted by the terrorist organization calling itself S.A.R.A.H. Hopefully, he will return to the ways of peace and bring prosperity to Duck Island once again."); + next; + select + l("What's S.A.R.A.H.?"), + l("Thanks, I think I'll continue exploring."); + mes ""; + if (@menu == 1) + elderAboutSarah(); + close; + return; +} + +function elderAboutSarah { + mesn; + mesq l("As I said, S.A.R.A.H. is a terrorist organization. They are dedicated to wiping out every single duck, worldwide. No duck has ever survived an encounter with a S.A.R.A.H. agent. You would do best to avoid them."); + next; + mesn; + mesq l("I suggest you leave now, %s. If you are even suspected of being a S.A.R.A.H. agent...", get_race()); + close; + return; +} + +function elderClose { + mesn; + mesq l("Enjoy your time here, %s. And keep your eye out for agents of S.A.R.A.H.", get_race()); + next; + mesn; + if (BaseLevel < 50) { + mesq l("Perhaps you may be of assistance later..."); + } else { + mesq l("I may have a task for you. Return when you tired of exploring..."); + } + close; + return; +} + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; + +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + diff --git a/npc/018-5-4/mapflags.txt b/npc/018-5-4/mapflags.txt new file mode 100644 index 0000000..dfe81f9 --- /dev/null +++ b/npc/018-5-4/mapflags.txt @@ -0,0 +1,2 @@ +018-5-4 mapflag pvp +018-5-4 mapflag pvp_noguild diff --git a/npc/018-5-5/_import.txt b/npc/018-5-5/_import.txt new file mode 100644 index 0000000..bcf2f81 --- /dev/null +++ b/npc/018-5-5/_import.txt @@ -0,0 +1,4 @@ +// Map 018-5-5: Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-5-5/_warps.txt", +"npc/018-5-5/ivanize.txt", diff --git a/npc/018-5-5/_warps.txt b/npc/018-5-5/_warps.txt new file mode 100644 index 0000000..b5a5dcc --- /dev/null +++ b/npc/018-5-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5-5: Indoors warps +018-5-5,33,47,0 warp #018-5-5_33_47 1,0,018-5,117,55 diff --git a/npc/018-5-5/ivanize.txt b/npc/018-5-5/ivanize.txt new file mode 100644 index 0000000..9d8b548 --- /dev/null +++ b/npc/018-5-5/ivanize.txt @@ -0,0 +1,99 @@ +// TMW2 scripts. +// Authors: +// Indigovox <rui.gravata@hotmail.com> +// Jesusalva <jesusalva@tmw2.org> +// Description: +// Evil Fairy + +018-5-5,29,38,0 script Ivanize NPC_FAIRY_C,{ + function ivanizeBusy; + if (strcharinfo(0) == "Manatauro") npctalk3 l("Leave me alone! Stop annoying me!"); + .@q = getq(LilitQuest_Ivanize); + if (BaseLevel < 50) ivanizeBusy(); + if (.@q == 0) goto L_FirstTime; + if (.@q > 0) goto L_Exchange; + ivanizeBusy(); + end; + +L_FirstTime: + mesn; + mesq l("Hello %s.", strcharinfo(0)); + next; + mesn strcharinfo(0); + select + l("And who are you?"), + l("How do you know my name?"); + mes ""; + mesn; + mesq l("My name is %s. I am an upstanding citizen of this kingdom. Everyone loves me.", .name$); + next; + mesn; + mesq l("Because I am a special nymph."); + mes "%%0"; + next; + mesn strcharinfo(0); + select + l("Hahah, tell me another one!"), + l("Let's pretend I believe you."); + mes ""; + mes "%%:"; + mes ""; + mesc l("%s, a bit upset, tries to change subjects.", .name$); + next; + mesn; + mesq l("Have you brought me some %s?", getitemlink(SnakeEgg)); + next; + mesn strcharinfo(0); + select + l("Why you don't tell me?"), + l("Wait, I thought you were a nymph!"); + mes ""; + mesn; + mesq l("I know you have %d %s with you, just haven't said before to be polite.", countitem(SnakeEgg), getitemlink(SnakeEgg)); + next; + mesn; + mesq l("I like to eat snake eggs. Lemme do you a proposal: You bring me %d %s, and I'll give you a flask of %s.", .minEggs, getitemlink(SnakeEgg), getitemlink(NymphPoison)); + next; + mesn; + mesq l("Just make sure to don't try to poison a guard or something, and don't tell anyone of our little deal. As a proof of our agreement, I'll even give you a freebie, so you can see this is the real deal."); + next; + mesn; + mesq l("Come back when you have the Snake Eggs for me."); + getitem NymphPoison, 1; + setq LilitQuest_Ivanize, 1; + close; + +L_Exchange: + .@c=countitem(SnakeEgg); + .@o=(.@c/.minEggs); + if (.@c < .minEggs) ivanizeBusy(); + mesn; + mesq l("Look at this, %s really brought me %d %s.", + strcharinfo(0), countitem(SnakeEgg), getitemlink(SnakeEgg)); + next; + mesn strcharinfo(0); + select + l("I want %d %s!", .@o, getitemname(NymphPoison)), + l("I'll see you later."); + mes ""; + if (@menu == 2) close; + delitem SnakeEgg, .@o*.minEggs; + getitem NymphPoison, .@o; + compareandsetq LilitQuest_Ivanize, 1, 2; + mesn; + mesq l("Here you go; See you later!"); + close; + +function ivanizeBusy { + mesn; + mesq l("Can't you see I'm busy?"); + close; +} + +OnInit: + .minEggs = 15; + .sex=G_FEMALE; + .distance=5; + end; +} + diff --git a/npc/018-5-boss/_import.txt b/npc/018-5-boss/_import.txt new file mode 100644 index 0000000..93f9220 --- /dev/null +++ b/npc/018-5-boss/_import.txt @@ -0,0 +1,4 @@ +// Map 018-5-boss: Mountain Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-5-boss/command.txt", +"npc/018-5-boss/mapflag.txt", diff --git a/npc/018-5-boss/command.txt b/npc/018-5-boss/command.txt new file mode 100644 index 0000000..119b14f --- /dev/null +++ b/npc/018-5-boss/command.txt @@ -0,0 +1,60 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Controls the Yetifly Challenge to earn the right to access Lilit + +// .@q = LilitQuest_Access +// 0 - Access not granted +// q2 - Number of attempts +// 1 - Access granted +// 2 - Tree Minigame complete. + +// It is always cast with an instance in mind +018-5-boss,0,0,0 script #YetiFlyChallengeCtrl NPC_HIDDEN,{ + end; + +OnCleanUp: + YETIFLY_INSTANCE=0; + end; + +OnWarn1: + instance_announce(YETIFLY_INSTANCE, "On your marks!", 0); + addtimer(2000, "#YetiFlyChallengeCtrl::OnWarn2"); + end; + +OnWarn2: + instance_announce(YETIFLY_INSTANCE, "READY?!", 0); + addtimer(2000, "#YetiFlyChallengeCtrl::OnBegin"); + end; + +OnBegin: + instance_announce(YETIFLY_INSTANCE, "START!", 0); + @YETIFLY_CYCLE=0; + // No event is bound: Slaying the Yetifly is not planned and thus, without effect. + // It'll vanish along the instance + monster "lilt@"+getcharid(0), 33, 24, l("Yetifly the Mighty"), Yetifly, 1; + addtimer(10000, "#YetiFlyChallengeCtrl::OnCycle"); + end; + +OnCycle: + if (!(compare(getmap(), "lilt") ) || ispcdead()) + end; + @YETIFLY_CYCLE+=1; + + // Is it over? Were you victorious? + if (@YETIFLY_CYCLE >= 6) { + setq LilitQuest_Access, 1; + getexp BaseLevel*425, JobLevel*190; // Reference: (35,25). Scalable reward. + warp "018-5", 97, 70; + mesn l("Yetifly the Mighty"); + mesq l("Not bad... You can now enter Lilit. If you think you can defeat me, climb the tree and meet me at the top!"); + close; + } + + // Resume execution + instance_announce(YETIFLY_INSTANCE, l("survive @@ seconds more!", (6-@YETIFLY_CYCLE)*10), 0); + addtimer(10000, "#YetiFlyChallengeCtrl::OnCycle"); + end; + +} diff --git a/npc/018-5-boss/mapflag.txt b/npc/018-5-boss/mapflag.txt new file mode 100644 index 0000000..918e922 --- /dev/null +++ b/npc/018-5-boss/mapflag.txt @@ -0,0 +1,3 @@ +018-5-boss mapflag zone No Tricks +018-5-0 mapflag nopet +018-5-1 mapflag nopet diff --git a/npc/018-5/_import.txt b/npc/018-5/_import.txt new file mode 100644 index 0000000..f9fc6b3 --- /dev/null +++ b/npc/018-5/_import.txt @@ -0,0 +1,9 @@ +// Map 018-5: Lilit Island +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-5/_mobs.txt", +"npc/018-5/_warps.txt", +"npc/018-5/soul_menhir.txt", +"npc/018-5/storage.txt", +"npc/018-5/teleporter.txt", +"npc/018-5/town.txt", +"npc/018-5/tree.txt", diff --git a/npc/018-5/_mobs.txt b/npc/018-5/_mobs.txt new file mode 100644 index 0000000..af1cd66 --- /dev/null +++ b/npc/018-5/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5: Lilit Island mobs +018-5,68,87,25,16 monster Water Fairy 1185,8,20000,40000 +018-5,66,84,25,18 monster Grass Snake 1169,8,10000,10000 +018-5,82,88,41,15 monster Wind Fairy 1185,8,40000,20000 +018-5,99,47,22,14 monster Nature Fairy 1186,6,30000,30000 +018-5,58,146,26,12 monster Mountain Snake 1123,6,120000,0,Shaabty::OnKillSnake +018-5,58,83,18,22 monster Green Dragon 1195,4,60000,0 +018-5,100,52,21,18 monster Squirrel 1055,6,30000,30000 +018-5,102,88,22,14 monster Vanity Pixie 1215,2,320000,0 +018-5,86,96,18,10 monster Nulity Pixie 1218,1,320000,0 +018-5,60,76,15,17 monster Nulity Pixie 1218,1,240000,0 diff --git a/npc/018-5/_warps.txt b/npc/018-5/_warps.txt new file mode 100644 index 0000000..833d880 --- /dev/null +++ b/npc/018-5/_warps.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-5: Lilit Island warps +018-5,99,82,0 warp #018-5_99_82 0,0,018-5-1,79,67 +018-5,97,68,0 warp #018-5_97_68 0,0,018-5-1,77,53 +018-5,117,56,0 warp #018-5_117_56 0,0,018-5-5,32,45 +018-5,83,48,0 warp #018-5_83_48 0,0,018-5-3,32,46 +018-5,60,146,0 warp #018-5_60_146 0,0,018-5-1,36,98 +018-5,114,54,0 warp #018-5_114_54 0,0,018-5-2,32,46 +018-5,110,46,0 warp #018-5_110_46 0,0,018-5-4,35,43 diff --git a/npc/018-5/soul_menhir.txt b/npc/018-5/soul_menhir.txt new file mode 100644 index 0000000..37be422 --- /dev/null +++ b/npc/018-5/soul_menhir.txt @@ -0,0 +1,30 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Soul Menhir + +018-5,111,52,0 script Soul Menhir#lil NPC_SOUL_MOSS,{ + mesc l("This is a Soul Menhir, but seems more magical than the others."); + select + l("Look closer."), + l("I wanna return home..."); + mes ""; + if (@menu == 2) { + closeclientdialog; + teleporthome(); + close; + } + @map$ = "018-5"; + setarray @Xs, 110, 111, 112; + setarray @Ys, 53, 53, 53; + @x = 0; + @y = 0; + callfunc "SoulMenhir"; + @map$ = ""; + cleararray @Xs[0], 0, getarraysize(@Xs); + cleararray @Ys[0], 0, getarraysize(@Ys); + @x = 0; + @y = 0; + close; +} diff --git a/npc/018-5/storage.txt b/npc/018-5/storage.txt new file mode 100644 index 0000000..cd843b7 --- /dev/null +++ b/npc/018-5/storage.txt @@ -0,0 +1,31 @@ +// TMW-2 Script. +// Author: +// Jesusalva + +018-5,108,44,0 script Storage Fairy NPC_FAIRY_C,{ + if (getq(LoFQuest_EPISODE) == 7 && + countitem(SnakeSkin)) { + mesc l("Deliver the %s to retrieve Miler's memeto?", getitemlink(SnakeSkin)), 1; + if (askyesno() == ASK_NO) { + clear; + } else { + inventoryplace DeathPotion, 1; + mesn; + mesq l("Now, isn't this lovely? I'll turn it into some pants for %s winter.", (season() == WINTER ? l("this") : l("next"))); + next; + delitem SnakeSkin, 1; + getitem DeathPotion, 1; + setq LoFQuest_EPISODE, 8, 0; + mesn; + mesq l("Oh, Miler just prepared a %s for you. You can use it... Or give it to Henry in Nivalis Inn, and whisper \"The Shadow Tortuga won the race against the Panthom Lord\".", getitemlink(DeathPotion)); + close; + } + } + Banker(.name$, "Lilit", 50000); + close; + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} diff --git a/npc/018-5/teleporter.txt b/npc/018-5/teleporter.txt new file mode 100644 index 0000000..8c8d318 --- /dev/null +++ b/npc/018-5/teleporter.txt @@ -0,0 +1,23 @@ +// TMW2 Script +// Authors: +// Jesusalva +// Description: +// Link portals to soul menhirs like the teleporters from old +// The price is temporary. This feature got in because no ship in Nivalis Port +// PS. Anise => “Aisen” Anagram + + +018-5,89,45,0 script #WarpGateLilit NPC_NO_SPRITE,1,0,{ + end; + +OnTouch: + TeleporterGate(TP_LILIT); + close; + + +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + diff --git a/npc/018-5/town.txt b/npc/018-5/town.txt new file mode 100644 index 0000000..9b99054 --- /dev/null +++ b/npc/018-5/town.txt @@ -0,0 +1,174 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Reset LOCATION$ when entering a town + +// .@q = LilitQuest_Access +// 0 - Access not granted +// 1 - Access granted +// 2 - Tree Minigame complete. + +// This NPC have a 1x1 touch area. +// If you logout, then relog, and try to fool it by moving - you'll be caught +// and slided to the right position. +018-5,97,69,0 script #LocLilit NPC_HIDDEN,1,1,{ + end; + +OnTouch: + .@q=getq(LilitQuest_Access); + if (!.@q) + goto L_AccessDenied; + else + EnterTown("Lilit"); + end; + +L_AccessDenied: + getmapxy(.@m$, .@x, .@y, 0); + // Cheater detected (either coming to or from Lilit), deploy countermeasures at once + if (.@x != 97 || .@y != 69) { + slide 97, 69; + atcommand("@jailfor 3mn "+strcharinfo(0)); + dispbottom l("Cheater detected! You have a three minutes sentence to fulfill, now."); + end; + } + mesn ("???"); + mesc l("You should not be here..."); + next; + mesn ("???"); + mesc l("Leave this place now, defiler..."); + next; + mesc l("How will you respond?!"), 1; + select + l("Step forward"), + l("Step backward"); + mes ""; + + // First option is a bad choice, but the only right choice. + if (@menu == 1) { + mesn ("???"); + mesc l("DIE, FILTHY @@!", strtoupper(get_race())); + movecam rand(-20,20), rand(-20,20); + sleep2(60); + movecam rand(-20,20), rand(-20,20); + sleep2(60); + movecam rand(-20,20), rand(-20,20); + sleep2(60); + restorecam; + percentheal -30, -30; + next; + } else { + closeclientdialog; + warp "018-5-1", 77, 53; + end; + } + + mesn ("???"); + mesc l("Ho... I see you are a tough one..."); + next; + mesn ("???"); + mesc l("What brings you here, @@?", get_race()); + next; + mesc l("How will you respond?!"), 1; + select + l("To help fairies"), + l("To explore these lands"), + l("To collect snake skin"), + l("To do quests"), + l("To aid those in need"), + l("Because I am awesome!"), + l("I don't know"); + mes ""; + mesn ("???"); + mesc l("I don't care for your reasons."); + next; + mesn l("Yetifly the Mighty"); + mesq l("I am the Yetifly, guardian of butter and fairies."); + next; + mesn l("Yetifly the Mighty"); + mesc l("AND YOU ARE NOT WELCOME HERE!!"), 1; + movecam rand(-20,20), rand(-20,20); + sleep2(60); + movecam rand(-20,20), rand(-20,20); + sleep2(60); + movecam rand(-20,20), rand(-20,20); + sleep2(60); + movecam rand(-20,20), rand(-20,20); + sleep2(60); + movecam rand(-20,20), rand(-20,20); + sleep2(60); + movecam rand(-20,20), rand(-20,20); + sleep2(60); + restorecam; + next; + mesn l("Yetifly the Mighty"); + mesq l("Unless, of course, if you can prove your strength, challenging me to a duel."); + next; + mesc l("How will you respond?!"), 1; + select + l("Bring it on!"), + l("Nah, I am a chicken."); + mes ""; + if (@menu == 2) { + closeclientdialog; + if (rand(1,4) == 3) + slide 98, 83; + else + slide 61, 147; + dispbottom l("The Yetifly drops you off the cliff. Good job, noob."); + percentheal -10, 0; + end; + } + mesn l("Yetifly the Mighty"); + mesq l("That wouldn't be fair to you, though. So, you don't need to defeat me."); + next; + mesn l("Yetifly the Mighty"); + mesq l("So, you just need to survive for one minute, and I'll consider you are good enough."); + mesc l("Word of the wise: You can freely challenge the Yetifly later.", 2); + if (getskilllv(NV_TRICKDEAD)) mesc l("WARNING: Fake Death skill is disabled on the fight."), 1; + next; + mesc l("Click \"Next\" to begin the fight."), 1; + next; + closeclientdialog; + + // Create instance. + .@ID=getcharid(0); + @MAP_NAME$="lilt@"+str(.@ID); // Max 4 chars for map name + .@INSTID = instance_create("lilt@a"+(.@ID), getcharid(3), IOT_CHAR); + .@instanceMapName$ = instance_attachmap("018-5-boss", .@INSTID, 0, @MAP_NAME$); + + // Instance already exists, or something went wrong + if (.@instanceMapName$ == "") { + mesn l("Yetifly the Mighty"); + mesc l("*put his glasses on*"); + next; + mesn l("Yetifly the Mighty"); + mesq l("Whaaaaaat, you are that noob from earlier! Vanish! Be gone! Don't bore me!"); + next; + warp "018-5-1", 77, 53; + closeclientdialog; + close; + } + + // Everything went right, create the instance, it expires after 2 minutes + instance_set_timeout(120, 120, .@INSTID); + instance_init(.@INSTID); + + YETIFLY_INSTANCE=.@INSTID; + addtimer(1000, "#YetiFlyChallengeCtrl::OnWarn1"); + warp @MAP_NAME$, 31, 41; + end; +} + +// Double check +018-5,99,64,0 script #LilitAccCtrl NPC_HIDDEN,1,1,{ +OnTouch: + .@q=getq(LilitQuest_Access); + if (!.@q) { + slide 97, 69; + atcommand("@jailfor 3mn "+strcharinfo(0)); + dispbottom l("Cheater detected! You have a three minutes sentence to fulfill, now."); + } + end; +} + diff --git a/npc/018-5/tree.txt b/npc/018-5/tree.txt new file mode 100644 index 0000000..dc240b5 --- /dev/null +++ b/npc/018-5/tree.txt @@ -0,0 +1,66 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Lilit's access + +018-5,100,52,0 script Great Tree NPC_NO_SPRITE,{ + function gtLocked; + function gtFirstTime; + function gtAccessOK; + + .@q = getq(LilitQuest_Access); + + if (JobLevel < 40) gtLocked(); + switch (.@q) { + case 2: + gtAccessOK(); break; + case 1: + gtFirstTime(); break; + default: + gtLocked(); + } + close; + +function gtFirstTime { + if (getq(LoFQuest_Fairy) >= 3 && + getq(LilitQuest_Shaabty) >= 4 && + getq(LilitQuest_Ivanize) >= 2) { + mesn l("Yetifly the Mighty"); + mesq l("%s... While you don't have the title of %s nor the flower of fae, nor have anything remotely important on your persona...", strcharinfo(0), b(l("Friend of the Fairies"))); + next; + mesn l("Yetifly the Mighty"); + mesq l("...I cannot fail to notice your deeds. Very well. Under my own authority, I authorize you to climb this holy tree."); + next; + mesn l("Yetifly the Mighty"); + mesq l("We shall be waiting for your arrival."); + next; + closeclientdialog; + gtAccessOK(); + return; + } + mesn l("Yetifly the Mighty"); + mesq l("Your persistence is amusing. However, you're yet to acquire the title of %s.", b(l("Friend of the Fairies"))); + next; + mesn l("Yetifly the Mighty"); + mesq l("Bring me the proof of your devotion, and I shall arrange you an audience with the Queen."); + return; +} + +function gtLocked { + mesn; + mesq l("The door is locked."); + close; +} + +function gtAccessOK { + warp "018-7", 52, 33; + return; +} + +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + diff --git a/npc/018-6-0/_import.txt b/npc/018-6-0/_import.txt new file mode 100644 index 0000000..0e2c16f --- /dev/null +++ b/npc/018-6-0/_import.txt @@ -0,0 +1,3 @@ +// Map 018-6-0: Sanctuary - Forgotten Chamber +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-6-0/main.txt", diff --git a/npc/018-6-0/ctrl b/npc/018-6-0/ctrl new file mode 100644 index 0000000..898b0fd --- /dev/null +++ b/npc/018-6-0/ctrl @@ -0,0 +1,43 @@ +LoFQuest_Barbara +q1 + q3 + +-------------------------- +0 Quest not assigned +1 Quest was assigned + 0 No cutscene seen + 1 The wounded soldier seen + 2 The footprints seen +2 Forgotten Chamber Puzzle is in progress (warp on) + 0 No puzzle solved + 1 West puzzle solved (&) (west lever) + 2 East puzzle solved (&) (east lever) + 3 South puzzle unlocked (w/e lever) + 4 South puzzle node (unused) + 5 South puzzle solved, access to shrine granted + &64 West Puzzle in Progress + &128 East Puzzle in Progress +3 Forgotten Shrine has been allowed (warp on / remember to reenable NPCs on resume) + Bitwise boss fights: + 1, 2, 4, 8, 16, 32, 64, 128, 256 (enable warps as win) + → Once the main shrine was reached: set to 511, Mana Stone story + & 512 => Boss defeated, cutscene ended, magic apple now visible +4 Ambush finished, return to elenium mines + 0 Magic Apple forgotten + 1 Magic Apple taken +5 Barbara captured + 0 Magic Apple forgotten + 1 Magic Apple taken +0 Reward claimed + +Field 2: Instance ID + +BARBARA_STATE +0 UNKNOWN +1 MURDERED +2 ARRESTED (BENJAMIN NODES) +3 FORGIVEN (BARBARA NODES) +4 EXTRA REWARD CLAIMED ON BENJAMIN +5 EXTRA REWARD CLAIMED ON BARBARA + + diff --git a/npc/018-6-0/main.txt b/npc/018-6-0/main.txt new file mode 100644 index 0000000..53c544e --- /dev/null +++ b/npc/018-6-0/main.txt @@ -0,0 +1,224 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Controls Forgotten Chamber + +// Main Controller for Instanced maps +018-6-2,0,0,0 script #01862_InstCtrl NPC_HIDDEN,{ + end; + +// Map, x, y, width, height, mob, amount +function AreaMonsterB { + .@m$=getarg(0); + .@x1=getarg(1); + .@x2=.@x1+getarg(3); + .@y1=getarg(2); + .@y2=.@y1+getarg(4); + .@mi=getarg(5); + .@am=getarg(6); + areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mi), .@mi, .@am); + //, instance_npcname(.name$)+"::OnKill"+.@mi); + return; +} + +// Level 40~50 (60 mobs) +OnLevel1: + killmonsterall(getmap()); + AreaMonsterB(getmap(), 50, 20, 100, 140, Snake, 10); + AreaMonsterB(getmap(), 50, 20, 100, 140, BlackSlime, 15); + AreaMonsterB(getmap(), 50, 20, 100, 140, AlphaMouboo, 5); + AreaMonsterB(getmap(), 50, 20, 100, 140, Wolvern, 5); + AreaMonsterB(getmap(), 50, 20, 100, 140, DarkLizard, 15); + AreaMonsterB(getmap(), 50, 20, 100, 140, BlackScorpion, 10); + end; + +// Level 50~60 (55 mobs) +OnLevel2: + killmonsterall(getmap()); + AreaMonsterB(getmap(), 50, 20, 100, 140, BlackScorpion, 15); + AreaMonsterB(getmap(), 50, 20, 100, 140, DustRifle, 5); + AreaMonsterB(getmap(), 50, 20, 100, 140, MountainSnake, 10); + AreaMonsterB(getmap(), 50, 20, 100, 140, HoodedNinja, 10); + AreaMonsterB(getmap(), 50, 20, 100, 140, FallenGuard2, 15); + end; + +// Level 60~70 (50 mobs) + 10 passive +OnLevel3: + killmonsterall(getmap()); + AreaMonsterB(getmap(), 50, 20, 100, 140, FallenGuard2, 10); + AreaMonsterB(getmap(), 50, 20, 100, 140, WickedMushroom, 20); + AreaMonsterB(getmap(), 50, 20, 100, 140, Archant, 20); + AreaMonsterB(getmap(), 50, 20, 100, 140, Crafty, 10); + end; + +// Level 60~100 (37 mobs) +OnLevel4: + killmonsterall(getmap()); + AreaMonsterB(getmap(), 50, 20, 100, 140, Archant, 5); + AreaMonsterB(getmap(), 50, 20, 100, 140, Forain, 15); + AreaMonsterB(getmap(), 50, 20, 100, 140, GreenDragon, 7); + AreaMonsterB(getmap(), 50, 20, 100, 140, Terranite, 5); + AreaMonsterB(getmap(), 50, 20, 100, 140, GoboBear, 5); + end; +} + +///////////////////////////// +018-6-0,90,67,0 script #ToForgottenShrine NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("It looks dangerous."); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q < 2 || (.@q2 == 2 && .@q3 != 7)) + Exception("ERROR, YOU SHOULD NOT BEEN SEEING THIS. 018-6-1.TFC.INVALID", RB_DEFAULT|RB_ISFATAL); + // Make the speech + if (.@q == 2) { + setq1 LoFQuest_Barbara, 3; + setq3 LoFQuest_Barbara, 0; + } + + // Execute the warp (randomly) + if (any(true,false)) + warp BarbaraInstCheck(3), 31, 151; + else + warp BarbaraInstCheck(3), 145, 26; + end; + + +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +///////////////////////////// +018-6-0,90,90,0 script #FromEleniumMines NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("It should bring me back."); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + if (.@q < 10) { + warp BarbaraInstCheck(1), 83, 28; + //warp BarbaraInstCheck(0), 90+any(-1, 1), 90+any(-1,1); + } else { + warp "018-6-1", 83, 28; + } + .alwaysVisible=true; + end; +} + + + + + +///////////////////////////// +018-6-0,90,113,0 script #ToSouthHall NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("It looks dangerous."); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q == 2 && .@q3 == 3) { + addtimer(100, "#01862_InstCtrl::OnLevel3"); + enablenpc instance_npcname("#FromWestHall", .@q2); + enablenpc instance_npcname("#FromEastHall", .@q2); + warp BarbaraInstCheck(2), 90, 32; + //warp BarbaraInstCheck(0), 90+any(-1, 1), 90+any(-1,1); + } else { + dispbottom l("I already visited this warp."); + } + end; + + +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +///////////////////////////// +018-6-0,67,90,0 script #ToWestHall NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("It looks dangerous."); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q != 2) { + dispbottom l("I already visited this warp."); + end; + } + + // Check if quest must begin + if (!(.@q3 & 1)) { + // Monster control + if (.@q3 & 2) + addtimer(100, "#01862_InstCtrl::OnLevel2"); + else + addtimer(100, "#01862_InstCtrl::OnLevel1"); + // Mark the quest in progress as WEST HALL, and warp + setq3 LoFQuest_Barbara, .@q3|64; + enablenpc instance_npcname("#FromSouthHall", .@q2); + warp BarbaraInstCheck(2), 70, 150; + } else { + dispbottom l("I already visited this warp."); + } + end; + + +OnInit: + .alwaysVisible=true; + disablenpc .name$; + end; +} + + +///////////////////////////// +018-6-0,113,90,0 script #ToEastHall NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("It looks dangerous."); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q != 2) { + dispbottom l("I already visited this warp."); + end; + } + + // Check if quest must begin + if (!(.@q3 & 2)) { + // Monster control + if (.@q3 & 1) + addtimer(100, "#01862_InstCtrl::OnLevel2"); + else + addtimer(100, "#01862_InstCtrl::OnLevel1"); + // Mark the quest in progress as EAST HALL, and warp + setq3 LoFQuest_Barbara, .@q3|128; + enablenpc instance_npcname("#FromSouthHall", .@q2); + warp BarbaraInstCheck(2), 103, 156; + } else { + dispbottom l("I already visited this warp."); + } + end; + + +OnInit: + .alwaysVisible=true; + disablenpc .name$; + end; +} + + diff --git a/npc/018-6-1/_import.txt b/npc/018-6-1/_import.txt new file mode 100644 index 0000000..2770c1a --- /dev/null +++ b/npc/018-6-1/_import.txt @@ -0,0 +1,5 @@ +// Map 018-6-1: Elenium Mines +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-6-1/_mobs.txt", +"npc/018-6-1/_warps.txt", +"npc/018-6-1/main.txt", diff --git a/npc/018-6-1/_mobs.txt b/npc/018-6-1/_mobs.txt new file mode 100644 index 0000000..de822a9 --- /dev/null +++ b/npc/018-6-1/_mobs.txt @@ -0,0 +1,18 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-6-1: Elenium Mines mobs +018-6-1,40,49,7,8 monster Big Elenium Bif 1228,5,450000,45000 +018-6-1,95,51,7,8 monster Big Elenium Bif 1228,5,450000,45000 +018-6-1,145,83,6,5 monster Medium Elenium Bif 1227,5,400000,45000 +018-6-1,62,86,6,5 monster Medium Elenium Bif 1227,5,400000,45000 +018-6-1,65,130,6,5 monster Medium Elenium Bif 1227,5,400000,45000 +018-6-1,125,158,6,5 monster Small Elenium Bif 1226,4,300000,45000 +018-6-1,129,183,6,5 monster Small Elenium Bif 1226,4,300000,45000 +018-6-1,34,181,5,5 monster Small Elenium Bif 1226,4,300000,45000 +018-6-1,80,172,54,25 monster Black Slime 1178,16,30000,15000 +018-6-1,89,167,54,25 monster Dark Lizard 1051,8,30000,15000 +018-6-1,96,103,56,29 monster Black Scorpion 1074,17,30000,15000 +018-6-1,101,105,54,25 monster Mountain Snake 1123,9,30000,15000 +018-6-1,70,45,44,25 monster Wicked Mushroom 1176,14,30000,15000 +018-6-1,68,45,31,21 monster Archant 1026,7,30000,15000 +018-6-1,39,51,8,6 monster Mountain Snake 1123,1,30000,15000 +018-6-1,92,53,8,6 monster Mountain Snake 1123,1,30000,15000 diff --git a/npc/018-6-1/_warps.txt b/npc/018-6-1/_warps.txt new file mode 100644 index 0000000..dda3368 --- /dev/null +++ b/npc/018-6-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-6-1: Elenium Mines warps +018-6-1,80,200,0 warp #018-6-1_80_200 0,0,017-1,32,45 diff --git a/npc/018-6-1/main.txt b/npc/018-6-1/main.txt new file mode 100644 index 0000000..953804e --- /dev/null +++ b/npc/018-6-1/main.txt @@ -0,0 +1,480 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Controls Elenium Mines, specially the instances + +// Main Controller for Instanced maps +018-6-1,0,0,0 script #01861_InstCtrl NPC_HIDDEN,{ + end; + +// Map, x, y, width, height, mob, amount +function AreaMonsterB { + .@m$=getarg(0); + .@x1=getarg(1); + .@x2=.@x1+getarg(3); + .@y1=getarg(2); + .@y2=.@y1+getarg(4); + .@mi=getarg(5); + .@am=getarg(6); + areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mi), .@mi, .@am, instance_npcname(.name$)+"::OnKill"+.@mi); + return; +} + +// (var, value) +function SetIfVoid { + if (!getarg(0)) + return getarg(1); + return 0; +} + +// Bifs won't respawn +OnKill1226: +OnKill1227: +OnKill1228: + end; + +// Others: Wait 30 seconds and respawn +OnKill1178: + .@mi=SetIfVoid(.@mi, 1178); +OnKill1051: + .@mi=SetIfVoid(.@mi, 1051); +OnKill1074: + .@mi=SetIfVoid(.@mi, 1074); +OnKill1123: + .@mi=SetIfVoid(.@mi, 1123); +OnKill1176: + .@mi=SetIfVoid(.@mi, 1176); +OnKill1026: + .@mi=SetIfVoid(.@mi, 1026); + + // Common utils + /* + // Respawn monster after 30s + getmapxy(.@m$, .@x, .@y, 0); + sleep(30000); + AreaMonsterB(.@m$, .@x-2, .@y-2, 4, 4, .@mi, 1); + */ + end; + +OnInstanceInit: + // Generate map ID + getmapxy(.@m$, .@x, .@y, UNITTYPE_NPC); + + // Bif monsters + AreaMonsterB(.@m$, 40, 49, 7, 8, BigEleniumBif, 5); + AreaMonsterB(.@m$, 95, 51, 7, 8, BigEleniumBif, 5); + AreaMonsterB(.@m$, 145, 83, 6, 5, EleniumBif, 5); + AreaMonsterB(.@m$, 62, 86, 6, 5, EleniumBif, 5); + AreaMonsterB(.@m$, 65, 130, 6, 5, EleniumBif, 5); + AreaMonsterB(.@m$, 125, 158, 6, 5, SmallEleniumBif, 4); + AreaMonsterB(.@m$, 129, 183, 6, 5, SmallEleniumBif, 4); + AreaMonsterB(.@m$, 34, 181, 5, 5, SmallEleniumBif, 4); + + // Common Monsters + AreaMonsterB(.@m$, 80, 172, 54, 25, BlackSlime, 16); + AreaMonsterB(.@m$, 89, 167, 54, 25, DarkLizard, 8); + AreaMonsterB(.@m$, 96, 103, 56, 29, BlackScorpion, 17); + AreaMonsterB(.@m$, 101, 105, 54, 25, MountainSnake, 9); + AreaMonsterB(.@m$, 70, 45, 44, 25, WickedMushroom, 14); + AreaMonsterB(.@m$, 68, 45, 31, 21, Archant, 7); + end; + +} +///////////////////////////// +018-6-1,83,26,0 script #ToForgottenChamber NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("It looks dangerous."); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + // Skipped cutscenes, show you the EXIT + if (.@q == 1 && .@q3 != 1) + cwarp "017-1", 32, 45; + + if (.@q >= 1) { + warp BarbaraInstCheck(0), 90, 91; + // Complete the first arc if possible + if (.@q == 1) { + setnpcdialogtitle l("Forgotten Chamber"); + mesn l("Forgotten Chamber"); + mesq l("You see yourself in a strange chamber. Strange drawings litter the walls."); + next; + mesn l("Forgotten Chamber"); + mesq l("You can see in distance two portals working, and two portals offline."); + next; + mesn l("Forgotten Chamber"); + mesq l("It seems to be a puzzle, you can only imagine what needs to be done in order to enable the right portals."); + next; + setq1 LoFQuest_Barbara, 2; + setq3 LoFQuest_Barbara, 0; + closeclientdialog; + } + // Update M0 NPCs based on quest state + if (.@q > 2) + enablenpc instance_npcname("#ToForgottenShrine", .@q2); + if (.@q == 2) { + // Note: state 64 and 128 are temporary and must be removed + if (.@q3 & 64) + setq3 LoFQuest_Barbara, .@q3^64; + // Safety update + .@q3=getq3(LoFQuest_Barbara); + + if (.@q3 & 128) + setq3 LoFQuest_Barbara, .@q3^128; + // Safety update + .@q3=getq3(LoFQuest_Barbara); + + if (.@q3 == 5) + enablenpc instance_npcname("#ToForgottenShrine", .@q2); + if (.@q3 == 3) + enablenpc instance_npcname("#ToSouthHall", .@q2); + } + + } else { + Exception("ERROR, YOU SHOULD NOT BEEN SEEING THIS. 018-6-1.TFC"); + } + end; + +OnInit: + disablenpc .name$; + end; + +} + +///////////////////////////// Minievents +018-6-1,128,131,0 script Wounded Soldier#01861 NPC_GUARD_DEAD,{ + //npctalk3 l("Please find her and don't worry with me! And be careful!"); + npctalk3 l("*scream in pain*"); + close; + +OnMain: + setpcblock(PCBLOCK_HARD, true); + mesc l("STORY MODE ENABLED. Monsters won't attack you, so you can read without worries."), 1; + next; + showavatar NPC_GUARD_DEAD; + mesn l("Wounded Soldier"); + mesq l("Hey, you! Are you here on Kenton's orders?!"); + next; + setnpcdialogtitle l("Benjamin, Wounded Soldier"); + mesn l("Benjamin, Wounded Soldier"); + mesq l("My name is Benjamin, and I was in charge of an incursion here to capture Barbara."); + next; + mesn l("Benjamin, Wounded Soldier"); + mesq l("My unit, however, was attacked! %s snuck up on us!", b(l("An assassin"))); + mesc l("You help Benjamin in getting up."); + next; + mesn l("Benjamin, Wounded Soldier"); + mesq l("I hope my men are safe, that was yesterday. I am barely alive."); + mesc l("You know what is worse? THIS WHOLE CAVE HAVE DEATH PENALTY ON!!!"), 1; + next; + mesn l("Benjamin, Wounded Soldier"); + mesq l("I shall report to Kenton. I now entrust this quest in your hands!"); + next; + mesn l("Benjamin, Wounded Soldier"); + mesq l("And please, bring those criminals to justice! Barbara and the Assassin!"); + next; + mesc l("With a wave, Benjamin returns to report Kenton about the outcome."); + setq3 LoFQuest_Barbara, 1; + disablenpc instance_npcname(.name$); // NPC will now go to rest + setpcblock(PCBLOCK_HARD, false); + showavatar; + close; + +OnInit: + .sex=G_MALE; + disablenpc .name$; + end; +OnInstanceInit: + if ($@GM_OVERRIDE) setnpcdisplay instance_npcname(.name$), NPC_WOUNDEDSOLDIER; + disablenpc instance_npcname(.name$); + end; +} + +// Event trigger +018-6-1,121,132,0 script #01861TriggerWS1 NPC_HIDDEN, 0, 10,{ + end; +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q == 1 && .@q3 == 0) { + enablenpc instance_npcname("Wounded Soldier#01861", .@q2); + addtimer2(500, instance_npcname("Wounded Soldier#01861", .@q2)+"::OnMain"); + } + end; + +OnInit: + disablenpc .name$; + end; +} + +///////////////////////////// Minievents +018-6-1,89,70,0 script Barbara#01861 NPC_INJURIED_GIRL,{ + //npctalk3 l("Please find her and don't worry with me! And be careful!"); + npctalk3 l("*scream in pain*"); + close; + +OnMain: + slide 84, 71; // Safety + showavatar NPC_INJURIED_GIRL; // this is handled by avatars.xml + + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + sleep2(50); + + // Barbara was taken hostage or murdered - save this data + @evil=0; // Temporary variable to show the murder option or not + setnpcdialogtitle l("Wounded Girl"); + mesn l("Wounded Girl"); + mesq l("..."); + next; + mesn l("Wounded Girl"); + mesq l("...I guess I can't hide anymore..."); + next; + sshake(rand2(3,5), false); + mesn l("Wounded Girl"); + mesq l("Ah!"); + next; + setnpcdialogtitle l("Barbara, Wounded Girl"); + mesn l("Barbara, Wounded Girl"); + mesq l("Sorry, I haven't seen you. My name is Barbara."); + next; + select + l("My name is ")+strcharinfo(0), + l("I'm here to arrest you."); + mes ""; + if (@menu == 1) { + mesn l("Barbara, Wounded Girl"); + mesq l("Pleased to meet you. What brings you down here?"); + next; + mesn strcharinfo(0); + mesq l("I'm here to arrest a criminal."); + next; + } else { + @evil+=1; + } + mesn l("Barbara, Wounded Girl"); + mesq l("Please spare me. I'm gravely wounded. I'm innocent, I swear!"); + next; + select + l("Why should I spare you?"), + l("Where is the item you've stolen?"); + mes ""; + mesn l("Barbara, Wounded Girl"); + mesq l("I... I confess. I did steal an apple. I was hungry! I am very poor."); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("My family died when I was just a little girl... I have been trying to survive..."); + next; + select + l("Cut the chat short and go straight to the point!"), + l("I'm listening, but don't hope me to buy your story."); + mes ""; + if (@menu == 2) { + mesn l("Barbara, Wounded Girl"); + mesq l("You know, if you're poor, you can't get too close to the Noble district."); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("They left me no inheritance, either. Even surviving has been a struggle. I'm not strong enough to kill the living potatoes for @@.", getitemlink(Potatoz)); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("The monsters here are too strong, and as I said before, I can't get close to the Transcendence Portal nor to get training."); + next; + select + l("I'm not here to listen stories, I need you to return what you've stole."); + } else { + @evil+=1; + } + mesn l("Barbara, Wounded Girl"); + mesq l("I can't give you the Apple I've stole. Because, I was stolen first!"); + next; + select + l("I don't believe you."), + l("Let's say I believe you. Who stole you? Benjamin?"); + mes ""; + if (@menu == 1) { + mesn l("Barbara, Wounded Girl"); + mesq l("But- But you have to! It is the ")+b(l("truth"))+"!"; + next; + select + l("And whom do you claim to be the thief?"), + rif(@evil, l("[Kill her]")), + l("Barbara. You're under arrest by Kenton's orders. Please surrend peacefully."); + mes ""; + @evil+=1; + switch (@menu) { + case 2: + mesn l("Barbara, Wounded Girl"); + mesq l("OH MY, PLEASE DON'T!"); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("I NEVER TOUCHED THE SOUL MENHIR! I SWEAR YOU, I'M INNOCENT! PLEASE HAVE MERCY!!"); + next; + mesc l("Really kill Barbara?"), 1; + mesc l("WARNING: THIS ACTION IS IRREVERSIBLE."), 1; + if (askyesno() == ASK_YES) { + mes ""; + mesc l("She falls in a single blow. You'll attribute her death to the Mysterious Assassin."); + BARBARA_STATE=1; + setq1 LoFQuest_Barbara, 5; + disablenpc instance_npcname(.name$); // NPC will now go to rest + close; + } else { + @evil=false; + mesn l("Barbara, Wounded Girl"); + mesc l("*sigh*"); + mesq l("Thank you..."); + next; + } + break; + case 3: + mesn l("Barbara, Wounded Girl"); + mesq l("...I have no other choice, do I?"); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("But be warned, I don't have the Apple with me. It must be further in."); + // The quest is not over yet - do not set BARBARA_STATE + setq1 LoFQuest_Barbara, 5; + disablenpc instance_npcname(.name$); // NPC will now go to rest + close; + break; + } + } else { + mesn l("Barbara, Wounded Girl"); + mesq l("Oh no, Benjamin is a city guard, he would never do that. I hope."); + next; + } +L_MainStory: + mesn l("Barbara, Wounded Girl"); + mesq l("A hooded man attacked me, and left me in this state."); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("The hooded man was about to deliver me a final blow, but then he saw the Apple I've stole..."); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("...And for some reason he took the apple and went further in the caves."); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("Maybe if you go back the way you're coming, you find him..."); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("By the way, I can't say for sure it was a man. He smelled like onions, and his face was shinning."); + next; + // FIXME: Should she be so blunt? + mesn l("Barbara, Wounded Girl"); + mesq l("I'm only assuming it was a man because the lack of boobs."); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("So... I'm at your hands now... What will you do with me?"); + next; + select + l("Listen to her story again."), + rif(@evil, l("[Kill her]")), + l("You're under arrest. I have to bring you back to Kenton."), + l("I have some healing items with me, we should tend your wounds first."); + mes ""; + switch (@menu) { + // Murder + case 2: + mesn l("Barbara, Wounded Girl"); + mesq l("OH MY, PLEASE DON'T!"); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("I NEVER TOUCHED THE SOUL MENHIR! I SWEAR YOU, I'M INNOCENT! PLEASE HAVE MERCY!!"); + next; + mesc l("Really kill Barbara?"), 1; + mesc l("WARNING: THIS ACTION IS IRREVERSIBLE."), 1; + if (askyesno() == ASK_YES) { + mes ""; + mesc l("She falls in a single blow. You'll attribute her death to the Mysterious Assassin."); + BARBARA_STATE=1; + setq1 LoFQuest_Barbara, 5; + disablenpc instance_npcname(.name$); // NPC will now go to rest + next; + closeclientdialog; + dispbottom l("You see a fruit on her body. You take it."); + getitembound MysteriousFruit, 1, 4; + close; + } else { + @evil=false; + mesn l("Barbara, Wounded Girl"); + mesc l("*sigh*"); + mesq l("Thank you..."); + next; + } + // Repeat + case 1: + goto L_MainStory; + break; + // Healing + case 4: + mesn l("Barbara, Wounded Girl"); + mesq l("I'm afraid my wounds are too critical. I'm bleeding. I was cursed. And I can't even move."); + next; + if (countitem(ElixirOfLife)) { + mesc l("You have an @@ - Should you give her that?", getitemlink(ElixirOfLife)); + if (askyesno() == ASK_YES) { + mes ""; + mesn l("Barbara, Wounded Girl"); + mesq l("It's true enough an @@ could heal even a dead tree, and would dispel almost every status ailment I could have.", getitemlink(ElixirOfLife)); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("But my case requires extended treatment. I need to be hospitalized. I'm afraid it is too late for the Elixir to have effect..."); + next; + } + } + mesn strcharinfo(0); + mesq l("...You're right. My medicine cannot heal you."); + next; + mesn l("Barbara, Wounded Girl"); + mesq l("I think the Doctor, on Dimond's Cove could help me, but that means passing by Kenton..."); + next; + // Arrest + case 3: + mesn l("Barbara, Wounded Girl"); + mesq l("...I have no other choice, do I?"); + next; + mesn l("Barbara, Wounded Girl"); + mesc l("*sigh*"); + mesq l("But be warned, I don't have the Apple with me. It must be further in."); + mes ""; + mesc l("Barbara was apprehended. It would be weird to show her on your inventory, so we won't."); + // The quest is not over yet - do not set BARBARA_STATE + setq1 LoFQuest_Barbara, 5; + disablenpc instance_npcname(.name$); // NPC will now go to rest + close; + } + Exception("Invalid Barbara Dialog State - 0"); + close; + +OnInit: + .sex=G_FEMALE; + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +// Event trigger +018-6-1,84,71,0 script #01861TriggerBB1 NPC_HIDDEN, 0, 10,{ + end; +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q == 4) { + enablenpc instance_npcname("Barbara#01861", .@q2); + addtimer2(500, instance_npcname("Barbara#01861", .@q2)+"::OnMain"); + } + end; + +OnInit: + disablenpc .name$; + end; +} + diff --git a/npc/018-6-2/_import.txt b/npc/018-6-2/_import.txt new file mode 100644 index 0000000..2291a77 --- /dev/null +++ b/npc/018-6-2/_import.txt @@ -0,0 +1,3 @@ +// Map 018-6-2: Forgotten Hall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-6-2/main.txt", diff --git a/npc/018-6-2/main.txt b/npc/018-6-2/main.txt new file mode 100644 index 0000000..0e1ebf7 --- /dev/null +++ b/npc/018-6-2/main.txt @@ -0,0 +1,137 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Controls Forgotten Chamber + +///////////////////////////// +018-6-2,90,31,0 script #FromSouthHall NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("It looks dangerous."); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q == 2) { + // 1/64 - West Puzzle + if ((.@q3 & 64)) { + warp BarbaraInstCheck(0), 68, 90; + setq3 LoFQuest_Barbara, .@q3|1; + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3-64; + .@q3=getq3(LoFQuest_Barbara); + disablenpc instance_npcname(.name$, .@q2); + // Puzzle complete, enable next NPC + if (.@q3 == 3) + enablenpc instance_npcname("#ToSouthHall", .@q2); + end; + } + // 2/128 - East Puzzle + else if ((.@q3 & 128)) { + warp BarbaraInstCheck(0), 112, 90; + setq3 LoFQuest_Barbara, .@q3|2; + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3-128; + .@q3=getq3(LoFQuest_Barbara); + disablenpc instance_npcname(.name$, .@q2); + // Puzzle complete, enable next NPC + if (.@q3 == 3) + enablenpc instance_npcname("#ToSouthHall", .@q2); + end; + } + // Wut + else { + Exception("ERROR, YOU SHOULD NOT BEEN SEEING THIS. 018-6-2.FSH", RB_DEFAULT|RB_ISFATAL); + } + // Active the quest + if (.@q3 == 3) + enablenpc instance_npcname("#ToSouthHall", .@q2); + } else { + Exception("ERROR, YOU SHOULD NOT BEEN SEEING THIS. 018-6-2.FSH.MQ"); + } + end; + +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; + +} + +///////////////////////////// +018-6-2,70,152,0 script #FromWestHall NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("Should I walk on it?"); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q != 2) + Exception("ERROR, INVALID WARP", RB_DEFAULT|RB_ISFATAL); + + // East hall not yet enabled, we must repeat + if (!(.@q3 & 128)) { + // Mark the west hall as complete (again) + addtimer(100, "#01862_InstCtrl::OnLevel4"); + setq3 LoFQuest_Barbara, .@q3|64; + disablenpc instance_npcname(.name$, .@q2); + warp BarbaraInstCheck(2), 90, 32; + } else { + // Quest is over! + setq3 LoFQuest_Barbara, 7; + disablenpc instance_npcname(.name$, .@q2); + enablenpc instance_npcname("#ToForgottenShrine", .@q2); + warp BarbaraInstCheck(0), 90, 112; + } + end; + + +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +///////////////////////////// +018-6-2,103,156,0 script #FromEastHall NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("Should I walk on it?"); + end; + +OnTouch: + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q != 2) + Exception("ERROR, INVALID WARP", RB_DEFAULT|RB_ISFATAL); + + // West hall not yet enabled, we must repeat + if (!(.@q3 & 64)) { + // Mark the east hall as complete (again) + addtimer(100, "#01862_InstCtrl::OnLevel4"); + setq3 LoFQuest_Barbara, .@q3|128; + disablenpc instance_npcname(.name$, .@q2); + warp BarbaraInstCheck(2), 90, 32; + } else { + // Quest is over! + setq3 LoFQuest_Barbara, 7; + disablenpc instance_npcname(.name$, .@q2); + enablenpc instance_npcname("#ToForgottenShrine", .@q2); + warp BarbaraInstCheck(0), 90, 112; + } + end; + + +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + diff --git a/npc/018-6-3/_import.txt b/npc/018-6-3/_import.txt new file mode 100644 index 0000000..0d3803e --- /dev/null +++ b/npc/018-6-3/_import.txt @@ -0,0 +1,4 @@ +// Map 018-6-3: Forgotten Shrine +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-6-3/main.txt", +"npc/018-6-3/wsys.txt", diff --git a/npc/018-6-3/main.txt b/npc/018-6-3/main.txt new file mode 100644 index 0000000..f4544a0 --- /dev/null +++ b/npc/018-6-3/main.txt @@ -0,0 +1,456 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Controls Forgotten Shrine + +018-6-3 mapflag nowarpto + +// 01863_RelevanceCheck ( Room ID ) +function script 01863_RelevanceCheck { + .@id=getarg(0); + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q < 3) + Exception("INVALID QUEST STATE, REVCHECK", RB_DEFAULT|RB_ISFATAL); + if (.@q != 3) + return false; + if (.@q3 & .@id) + return false; + return true; +} + +// Shut down the enabled warps. +function script 01863_DisableAllWarps { + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q < 3) + Exception("ERROR, INVALID WARP", RB_DEFAULT|RB_ISFATAL); + + disablenpc instance_npcname("#01863_fromNorthtoOut", .@q2); + disablenpc instance_npcname("#01863_fromNorthtoNW", .@q2); + disablenpc instance_npcname("#01863_fromNorthtoNE", .@q2); + disablenpc instance_npcname("#01863_fromSouthtoOut", .@q2); + disablenpc instance_npcname("#01863_fromSouthtoSW", .@q2); + disablenpc instance_npcname("#01863_fromSouthtoSE", .@q2); + disablenpc instance_npcname("#01863_fromNWtoNorth", .@q2); + disablenpc instance_npcname("#01863_fromNWtoNC", .@q2); + disablenpc instance_npcname("#01863_fromNWtoSW", .@q2); + disablenpc instance_npcname("#01863_fromSWtoSouth", .@q2); + disablenpc instance_npcname("#01863_fromSWtoSC", .@q2); + disablenpc instance_npcname("#01863_fromSWtoNW", .@q2); + disablenpc instance_npcname("#01863_fromNEtoNorth", .@q2); + disablenpc instance_npcname("#01863_fromNEtoNC", .@q2); + disablenpc instance_npcname("#01863_fromNEtoSE", .@q2); + disablenpc instance_npcname("#01863_fromSEtoSouth", .@q2); + disablenpc instance_npcname("#01863_fromSEtoSC", .@q2); + disablenpc instance_npcname("#01863_fromSEtoNE", .@q2); + disablenpc instance_npcname("#01863_fromNCtoNW", .@q2); + disablenpc instance_npcname("#01863_fromNCtoNE", .@q2); + disablenpc instance_npcname("#01863_fromNCtoCC", .@q2); + disablenpc instance_npcname("#01863_fromSCtoSW", .@q2); + disablenpc instance_npcname("#01863_fromSCtoSE", .@q2); + disablenpc instance_npcname("#01863_fromSCtoCC", .@q2); + disablenpc instance_npcname("#01863_fromCCtoNC", .@q2); + disablenpc instance_npcname("#01863_fromCCtoSC", .@q2); + return; +} + +// Main Controller for Instanced maps +018-6-3,0,0,0 script #01863_InstCtrl NPC_HIDDEN,{ + end; + +// Map, x1, y1, x2, y2, mob, amount +function AreaMonsterB { + .@m$=getarg(0); + .@x1=getarg(1); + .@x2=getarg(3); + .@y1=getarg(2); + .@y2=getarg(4); + .@mi=getarg(5); + .@am=getarg(6); + if (!.@am) + return; + areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mi), .@mi, .@am); + //, instance_npcname(.name$)+"::OnKill"+.@mi); + return; +} + +// Map, x1, y1, x2, y2, mob, bossID +function AreaMonsterBoss { + .@m$=getarg(0); + .@x1=getarg(1); + .@x2=getarg(3); + .@y1=getarg(2); + .@y2=getarg(4); + .@mi=getarg(5); + .@id=getarg(6); + areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mi), .@mi, 1, instance_npcname(.name$)+"::OnKill"+.@id); + return; +} + +// BossRoom ( RoomID, x1, y1, x2, y2, adjustment ) +// Adjustment is for bigger rooms. Defaults to false. +function BossRoom { + .@id=getarg(0); + .@x1=getarg(1); + .@y1=getarg(2); + .@x2=getarg(3); + .@y2=getarg(4); + .@ad=getarg(5, false); + // True: Must spawn boss, False: Allow to use warps + if (01863_RelevanceCheck(.@id)) { + mapannounce getmap(), l("BOSS FIGHT!"), bc_map|bc_pc; + // Spawn a boss. Passive chance: 17% + // Removed: HolyPixie, NulityPixie, VanityPixie + AreaMonsterBoss(getmap(), .@x1, .@y1, .@x2, .@y2, any(CopperSkullSlime, LavaSkullSlime, BlackSlimeMother, TerraniteProtector, GoboBear, Centaur), .@id); + + // BIG ROOM: 6~12 support units + // SMALL ROOM: 5~10 support units + AreaMonsterB(getmap(), .@x1, .@y1, .@x2, .@y2, BlackSlime, rand2(2,4)+.@ad); + AreaMonsterB(getmap(), .@x1, .@y1, .@x2, .@y2, DarkLizard, rand2(1,2)+.@ad); + AreaMonsterB(getmap(), .@x1, .@y1, .@x2, .@y2, BlackScorpion, rand2(1,2)); + AreaMonsterB(getmap(), .@x1, .@y1, .@x2, .@y2, Archant, rand2(1,2)-.@ad); + // Only spawn the fast Hooded Ninja on big rooms + if (.@ad) { + AreaMonsterB(getmap(), .@x1, .@y1, .@x2, .@y2, HoodedNinja, any(0,0,1)); + } + return false; + } + return true; +} + +// W - West, C - Center, E - East, N - North, S - South +OnRoomNorth: +L_Room1: + if (BossRoom(1, 62, 41, 121, 58, true)) { + enablenpc instance_npcname("#01863_fromNorthtoNW"); + enablenpc instance_npcname("#01863_fromNorthtoNE"); + enablenpc instance_npcname("#01863_fromNorthtoOut"); + } + end; + +OnRoomNW: +L_Room2: + if (BossRoom(2, 48, 65, 68, 85)) { + enablenpc instance_npcname("#01863_fromNWtoNorth"); + enablenpc instance_npcname("#01863_fromNWtoNC"); + enablenpc instance_npcname("#01863_fromNWtoSW"); + } + end; + +OnRoomNC: +L_Room4: + if (BossRoom(4, 77, 65, 103, 76)) { + enablenpc instance_npcname("#01863_fromNCtoNW"); + enablenpc instance_npcname("#01863_fromNCtoNE"); + enablenpc instance_npcname("#01863_fromNCtoCC"); + } + end; + +OnRoomNE: +L_Room8: + if (BossRoom(8, 111, 65, 131, 85)) { + enablenpc instance_npcname("#01863_fromNEtoNorth"); + enablenpc instance_npcname("#01863_fromNEtoNC"); + enablenpc instance_npcname("#01863_fromNEtoSE"); + } + end; + +OnRoomSW: +L_Room16: + if (BossRoom(16, 48, 95, 68, 115)) { + enablenpc instance_npcname("#01863_fromSWtoSouth"); + enablenpc instance_npcname("#01863_fromSWtoSC"); + enablenpc instance_npcname("#01863_fromSWtoNW"); + } + end; + +OnRoomSC: +L_Room32: + if (BossRoom(32, 77, 104, 103, 115)) { + enablenpc instance_npcname("#01863_fromSCtoSW"); + enablenpc instance_npcname("#01863_fromSCtoSE"); + enablenpc instance_npcname("#01863_fromSCtoCC"); + } + end; + +OnRoomSE: +L_Room64: + if (BossRoom(64, 111, 95, 131, 115)) { + enablenpc instance_npcname("#01863_fromSEtoSouth"); + enablenpc instance_npcname("#01863_fromSEtoSC"); + enablenpc instance_npcname("#01863_fromSEtoNE"); + } + end; + +OnRoomSouth: +L_Room128: + if (BossRoom(128, 62, 122, 121, 139, true)) { + enablenpc instance_npcname("#01863_fromSouthtoSW"); + enablenpc instance_npcname("#01863_fromSouthtoSE"); + enablenpc instance_npcname("#01863_fromSouthtoOut"); + } + end; + +OnRoomCC: + enablenpc instance_npcname("#01863_fromCCtoNC"); + enablenpc instance_npcname("#01863_fromCCtoSC"); + end; + +OnFinalRoom: +// Room: 74,83 ~ 106,97 + .@x1=74; + .@y1=83; + .@x2=106; + .@y2=97; + .@id=256; + changemusic getmap(), "let_the_battles_begin.ogg"; + mapannounce getmap(), col(l("FINAL BOSS FIGHT - WATCH OUT!"), 1), bc_map|bc_pc; + .@mi=NightDragon; + @boss=monster(getmap(), 90, 89, strmobinfo(1, .@mi), .@mi, 1, instance_npcname(.name$)+"::OnKillBoss"); + + // Spawn reinforcements based on difficulty + AreaMonsterB(getmap(), .@x1, .@y1, .@x2, .@y2, MagicGoblin, (@difficulty_modulus ? 5 : 3)); + AreaMonsterB(getmap(), .@x1, .@y1, .@x2, .@y2, DeathCat, (@difficulty_modulus ? 3 : 1)); + end; + +// Kill boss will use setq3 and also erase any remaining monster +// Then it'll cast again the room event to enable the warps. +OnKill1: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|1; + killmonsterall(getmap()); + goto L_Room1; + +OnKill2: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|2; + killmonsterall(getmap()); + goto L_Room2; + +OnKill4: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|4; + killmonsterall(getmap()); + goto L_Room4; + +OnKill8: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|8; + killmonsterall(getmap()); + goto L_Room8; + +OnKill16: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|16; + killmonsterall(getmap()); + goto L_Room16; + +OnKill32: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|32; + killmonsterall(getmap()); + goto L_Room32; + +OnKill64: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|64; + killmonsterall(getmap()); + goto L_Room64; + +OnKill128: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|128; + killmonsterall(getmap()); + goto L_Room128; + +OnKill256: + .@q3=getq3(LoFQuest_Barbara); + setq3 LoFQuest_Barbara, .@q3|256; + killmonsterall(getmap()); + end; + +OnKillBoss: + if (!playerattached()) { + enablenpc instance_npcname("Mana Stone#01863"); + Exception("Could not finish the Boss Fight!"); + end; + } + changemusic getmap(), "Misty_Shrine.ogg"; + enablenpc instance_npcname("???#01863"); + enablenpc instance_npcname("#01863_fromCCtoNC"); + enablenpc instance_npcname("#01863_fromCCtoSC"); + getexp 200000, 10000; + unittalk(@boss, l("You are a mere %s, but you may be a hero... Find me again...", get_race())); + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + setq LoFQuest_Barbara, 4, .@q2, 0; + makeitem2(HeadHood, 1, 1, 0, 0, BlackDye, 0, 0, 0, getmap(), -1, -1, 3); // ChocolateDye or BrownDye, maybe + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +} + +///////////////////////////// +///////////////////////////// +///////////////////////////// +// Critical Room +// NCCC: 90.74 → 90,85 +// SCCC: 90.105 → 90,95 +// Altar: 90,90 +// Room: 74,83 ~ 106,97 + +018-6-3,90,91,0 script Mana Stone#01863 NPC_MANA_STONE,{ + mesn; + mes l("The mighty Mana Stone does not reacts against you."); + mes l("It's")+b(l("small, red, round and shiny.")); + mes l("If you fell ready, perhaps you should touch it?"); + mes ""; + select + l("Touch it!"), + l("Take it!"), + l("Break it!"), + l("Leave it alone!"); + mes ""; + switch (@menu) { + case 1: + mesc l("Curiously, you try to touch the Mana Stone."); + next; + break; + case 2: + mesc l("Determinate, you move your hand to grab it."); + next; + break; + case 3: + mesc l("Are you out of your mind?!"); + mesc l("We must capture Barbara AND return the stolen item!"); + mesc l("If you break that, you could never go to the World's Edge!"); + mesc l("...Assuming you can break a Mana Stone, that is."); + next; + mesc l("Attempt to break the Mana Stone?"), 1; + if (askyesno() == ASK_NO) + close; + mes ""; + @difficulty_modulus=true; + break; + default: + close; + } + mes ""; + mes ".:: " + l("The Mana Stone") + " ::."; + mesq l("Do you think yourself worthy of my power?"); + next; + // Both choices are valid, actually. + askyesno(); + mes ".:: " + l("The Mana Stone") + " ::."; + if (@menu == ASK_YES) + mesq l("Then prove yourself!"); + else + mesq l("But that's no excuse for cowardice!"); + next; + mes ".:: " + l("The Mana Stone") + " ::."; + mesq l("Foolish mortal, who doesn't knows what you are doing!"); + next; + setnpcdisplay instance_npcname("Mana Stone#01863"), NPC_LIGHTBRINGER; + setnpcdialogtitle l("The Mana Sword, Lightbringer?"); + mes ".:: " + l("The Mana Sword") + " ::."; + mesq l("I shall decide here and now, if you are worth of living in this world!"); + next; + mes ".:: " + l("The Mana Sword") + " ::."; + mesq l("I am the Judge, and I shall make Judgment upon you!"); + mesc l("WARNING: ")+l("If you die or logout here, the quest will be reset!"), 1; + next; + 01863_DisableAllWarps(); + doevent instance_npcname("#01863_InstCtrl")+"::OnFinalRoom"; + disablenpc instance_npcname(.name$); + setq3 LoFQuest_Barbara, 255; + close; + +// Mana Stone is the initial NPC, not hidden +OnInit: + disablenpc .name$; + end; +} + +018-6-3,90,91,0 script ???#01863 NPC_NO_SPRITE,{ + .@q=getq(LoFQuest_Barbara); + .@q2=getq2(LoFQuest_Barbara); + .@q3=getq3(LoFQuest_Barbara); + if (.@q != 4) + Exception("ERROR INVALID APPLE 1863 ST NOT 4", RB_DEFAULT|RB_ISFATAL); + if (.@q3) + Exception("ERROR INVALID APPLE 1863 Q3 IS VALID", RB_DEFAULT|RB_ISFATAL); + + mesc l("You see a red apple here. It is ")+b(l("small, red, round and shiny.")); + next; + mesc l("Take it?"); + if (askyesno() == ASK_YES) { + inventoryplace MagicApple, 1; + getitem MagicApple, 1; + setq3 LoFQuest_Barbara, 1; + mes ""; + mesc l("You take the @@. It seems to be the stolen item.", getitemlink(MagicApple)); + if (@difficulty_modulus) { + @difficulty_modulus=false; + Zeny+=200; + mesc l("You also find @@ GP with it.", 200); + } + disablenpc instance_npcname(.name$); + } + close; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,34,148,0 script Book#01863A NPC_NO_SPRITE,{ + if (!getskilllv(TMW2_ANCIENTLANGUAGES)) { + mesn strcharinfo(0); + mesc l("It's written in an ancient language, you cannot understand what's written."); + mesc l("The symbols at the end of the book look to be warnings, though."); + mesc l("I should check if I have more than enough potions and food before proceeding!"); + mesc l("REMEMBER: FAILURE AT A BOSS FIGHT WILL CAUSE QUEST TO RESET!"), 1; + next; + mesc l("I am really curious what this shrine is about, though."); + close; + } + mes ".:: " + l("Orbyter Shrine") + " ::."; + mes l("This shrine is devoted to those whom shall never come back."); + mes l("Deep sorrow and sadness, this is the Forgotten Shrine."); + mes ""; + // TRANSLATORS: IWS = Internal Warp System + mes l("To the wanderer: Please avoid entering on the rooms by the internal warp system."); + mesc l("Guardians will come to stop you, and to lock down the shrine!"), 1; + mes l("In memoriam. May those whom shall never come back find rest in their afterlifes."); // TRANSLATORS: In Memoriam = Latin expression, means "in memory" + mes l("May their journey be successful, may their objectives be attained, may the Mana rest in peace."); + mes ""; + mes l("The Mana Guardian,"); + //mes "MEE6 the Bot"; + mesc "« "+l("It's too faint to read.")+" »"; + if (!@warning) { + next; + @warning=true; + mesn strcharinfo(0); + mesc l("This shrine seems to be a dangerous place, and nobody should defile it."); + mesc l("I should check if I have more than enough potions and food before proceeding!"); + mesc l("REMEMBER: FAILURE AT A BOSS FIGHT WILL CAUSE QUEST TO RESET!"), 1; + } + close; + +OnInit: + .distance=3; + end; +} + +018-6-3,142,28,0 duplicate(Book#01863A) Book#01863B NPC_NO_SPRITE + + diff --git a/npc/018-6-3/wsys.txt b/npc/018-6-3/wsys.txt new file mode 100644 index 0000000..40e8d86 --- /dev/null +++ b/npc/018-6-3/wsys.txt @@ -0,0 +1,689 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Warp System Forgotten Shrine (rough duplicates) + +////////// Warp System below this file +018-6-3,86,32,0 script #01863_fromOuttoNorth NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 86, 44; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNorth"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +} + +018-6-3,86,43,0 script #01863_fromNorthtoOut NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 86, 31; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,64,50,0 script #01863_fromNorthtoNW NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 58, 67; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNW"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,58,66,0 script #01863_fromNWtoNorth NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 65, 50; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNorth"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,119,50,0 script #01863_fromNorthtoNE NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 122, 68; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNE"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,122,67,0 script #01863_fromNEtoNorth NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 118, 50; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNorth"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,67,75,0 script #01863_fromNWtoNC NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 79, 71; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNC"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,78,71,0 script #01863_fromNCtoNW NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 66, 75; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNW"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,57,83,0 script #01863_fromNWtoSW NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 57, 97; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSW"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,57,96,0 script #01863_fromSWtoNW NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 57, 82; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNW"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,112,75,0 script #01863_fromNEtoNC NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 101, 70; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNC"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,102,70,0 script #01863_fromNCtoNE NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 113, 75; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNE"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,121,84,0 script #01863_fromNEtoSE NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 121, 97; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSE"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,121,96,0 script #01863_fromSEtoNE NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 121, 83; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNE"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,57,113,0 script #01863_fromSWtoSouth NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 64, 131; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSouth"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,63,131,0 script #01863_fromSouthtoSW NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 57, 112; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSW"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,123,113,0 script #01863_fromSEtoSouth NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 119, 131; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSouth"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,120,131,0 script #01863_fromSouthtoSE NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 123, 112; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSE"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,66,106,0 script #01863_fromSWtoSC NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 79, 110; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSC"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,78,110,0 script #01863_fromSCtoSW NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 65, 106; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSW"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,112,110,0 script #01863_fromSEtoSC NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 101, 110; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSC"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,102,110,0 script #01863_fromSCtoSE NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 113, 110; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSE"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,92,137,0 script #01863_fromSouthtoOut NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 92, 151; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,92,150,0 script #01863_fromOuttoSouth NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 92, 136; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSouth"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +} + +018-6-3,90,84,0 script #01863_fromCCtoNC NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 90, 73; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomNC"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,90,96,0 script #01863_fromCCtoSC NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 90, 106; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomSC"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,90,74,0 script #01863_fromNCtoCC NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 90, 85; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomCC"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +018-6-3,90,105,0 script #01863_fromSCtoCC NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("I should be prepared before walking on this."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Warp you to next room coordinates + slide 90, 95; + + // Execute the next room code and finish + doevent instance_npcname("#01863_InstCtrl")+"::OnRoomCC"; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + +////////// Leave room +018-6-3,30,152,0 script #01863_SWLeave NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("This will bring me back to the Hall."); + end; + +OnTouch: + // Disable any other active portal + 01863_DisableAllWarps(); + + // Lead you outside + warp BarbaraInstCheck(0), 90, 68; + end; + +// NPC must remain hidden. +OnInit: + disablenpc .name$; + end; +} + +018-6-3,146,25,0 duplicate(#01863_SWLeave) #01863_NELeave NPC_NO_SPRITE + + diff --git a/npc/018-7-1/_import.txt b/npc/018-7-1/_import.txt new file mode 100644 index 0000000..cefe635 --- /dev/null +++ b/npc/018-7-1/_import.txt @@ -0,0 +1,8 @@ +// Map 018-7-1: Developers' Lair (Lilit Palace) +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-7-1/018-7-1_demure_blackbox.txt", +"npc/018-7-1/_mobs.txt", +"npc/018-7-1/_warps.txt", +"npc/018-7-1/demure.txt", +"npc/018-7-1/lilit.txt", +"npc/018-7-1/yetifly.txt", diff --git a/npc/018-7-1/_mobs.txt b/npc/018-7-1/_mobs.txt new file mode 100644 index 0000000..9db1b9c --- /dev/null +++ b/npc/018-7-1/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-7-1: Developers' Lair (Lilit Palace) mobs +018-7-1,85,58,9,13 monster Mana Piou 1155,8,35000,270000 +018-7-1,90,67,5,5 monster Forest Piou 1202,4,35000,120000 +018-7-1,90,54,5,7 monster Duck 1029,4,200000,35000 +018-7-1,80,59,4,12 monster Nature Fairy 1186,3,200000,35000 diff --git a/npc/018-7-1/_warps.txt b/npc/018-7-1/_warps.txt new file mode 100644 index 0000000..eed0ec1 --- /dev/null +++ b/npc/018-7-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-7-1: Developers' Lair (Lilit Palace) warps +018-7-1,56,100,0 warp #018-7-1_56_100 0,0,018-7,59,71 diff --git a/npc/018-7-1/demure.txt b/npc/018-7-1/demure.txt new file mode 100644 index 0000000..5f2001c --- /dev/null +++ b/npc/018-7-1/demure.txt @@ -0,0 +1,220 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Demure is the Queen of Dragons + + +018-7-1,23,20,0 script Demure, Queen of Dragons NPC_DEMURE,{ + mesn; + mesq l("Greetings mortal; I am Demure, Queen of Dragons."); + if (BaseLevel < 40) close; + next; + mesn strcharinfo(0); + select + l("Thanks. It is my pleasure."), + rif($DEMUR_HOLDER$ == "", l("I've heard rumors about a legendary axe.")), + l("I want to challenge you."); + mes ""; + if (@menu == 1) { closeclientdialog; close;} + if (@menu == 2) { + mesn; + mesq l("The %s, yes. I have it. It is a legendary weapon, only one of it exist on the world.", getitemlink(DemureAxe)); + next; + if ($GAME_STORYLINE < 3) { + mesn; + mesq l("But it is still too early to give it away to a %s children.", get_race()); + close; + } + mesn; + mesq l("I'll give it to someone who challenge me and best me in an impressive manner. If it was easy, someone would already have claimed it."); + mesn; + mesq l("So, will you challenge me?"); + next; + if (askyesno() == ASK_NO) close; + } + mesc l("Mode?"); + menuint + l("Solo"), MODE_SOLO, + rif(getcharid(1), l("Party")), MODE_PARTY; + .@mode = @menuret; + if (.@mode == MODE_PARTY) { + if (getcharid(1) < 1) + kick(getcharid(3), 3); + if (strcharinfo(0) != getpartyleader(getcharid(1))) { + mesn; + mesq l("Hold your horses, %s. You are not the party leader.", strcharinfo(0)); + close; + } + } + mes ""; + mesc l("Difficulty?"); + menuint + l("I want to challenge the Yetifly."), 1, + l("I want to challenge you."), 2, + l("I want to challenge you both."), 3; + .@mode2 = @menuret; + mes ""; + mesc l("Monster Density and Boss HP?"); + menuint + l("Meager."), 1, + l("Normal."), 2, + l("Doubled."), 4, + l("Hunter."), 8, + l("Ultimate"), 16; + .@mode3 = @menuret; + mes ""; + if (.@mode == MODE_SOLO) { + .@mapn$="demu@"+getcharid(0); + .@inst = instance_create("Demured "+getcharid(0), getcharid(3), IOT_CHAR); + } else { + .@mapn$="demu@"+getcharid(1); + .@inst = instance_create("Demured "+getcharid(1), getcharid(1), IOT_PARTY); + } + instance_attachmap("018-7-1", .@inst, false, .@mapn$); + // Instance lasts 10 minutes + 15s grace time + instance_set_timeout(615, 615, .@inst); + instance_init(.@inst); + + // Create walls + setcells .@mapn$, 75, 66, 75, 67, 1, .@mapn$+"A"; + setcells .@mapn$, 85, 84, 86, 84, 1, .@mapn$+"B"; + + // Adjust the proper monster count to be spawned + if (.@mode == MODE_PARTY) { + getpartymember(getcharid(1)); + .@count = max($@partymembercount, 1); + } else { + .@count = 1; + } + + // Setup the boss monsters. Non-exclusive (bitwise) + if (.@mode2 & 1) { + .@m=monster(.@mapn$, 85, 66, strmobinfo(1, Yetifly), Yetifly, 1, strnpcinfo(NPC_NAME_UNIQUE)+"::OnYeti"); + .@p=getunitdata(.@m, UDT_MAXHP); + setunitdata(.@m, UDT_MAXHP, .@p*.@count*.@mode3); + setunitdata(.@m, UDT_HP, .@p*.@count*.@mode3); + } + if (.@mode2 & 2) { + .@m=monster(.@mapn$, 87, 57, strmobinfo(1, DemureFirstForm), DemureFirstForm, 1, strnpcinfo(NPC_NAME_UNIQUE)+"::OnDem1"); + .@p=getunitdata(.@m, UDT_MAXHP); + setunitdata(.@m, UDT_MAXHP, .@p*.@count*.@mode3); + setunitdata(.@m, UDT_HP, .@p*.@count*.@mode3); + } + + // Setup the reinforcements, in appropriate number and mode + for (.@i = 0; .@i < (.@count*.@mode3); .@i++) { + .@mob = any(EarthFairy, FireFairy, WaterFairy, WindFairy, PoisonFairy); + .@m = areamonster(.@mapn$, 76, 47, 95, 83, strmobinfo(1, .@mob), .@mob, 1); + .@p = getunitdata(.@m, UDT_MODE); + setunitdata(.@m, UDT_MODE, (.@p | MD_AGGRESSIVE)); + } + for (.@i = 0; .@i < .@count; .@i++) { + .@mob = any(EliteDuck, Archant, Wolvern, BlueSlimeMother); + areamonster .@mapn$, 76, 47, 95, 83, strmobinfo(1, .@mob), .@mob, any(1,2); + } + + // Warp you and/or your party + if (.@mode == MODE_SOLO) + warp(.@mapn$, 85, 70); + else + warpparty(.@mapn$, 85, 70, getcharid(1), "018-7-1", true); + + // Begin tracking time and metadata (blackbox needs this) + @elapsed = 0; + @d_mode1 = .@mode; + @d_mode2 = .@mode2; + @d_mode3 = .@mode3; + @d_count = .@count; + addtimer 5000, strnpcinfo(NPC_NAME_UNIQUE)+"::OnBeet"; + close; + +OnBeet: + if (!compare(getmap(), "demu@") && !compare(getmap(), "018-7-1")) end; + @elapsed += 5; + if (@elapsed % 60 < 5) + mapannounce(getmap(), l("Time left: %d minutes", 10-(@elapsed/60)), bc_map | bc_pc); + if (@elapsed > 600) + end; + addtimer2 5000, strnpcinfo(NPC_NAME_UNIQUE)+"::OnBeet"; + end; + +// Check victory conditions +OnYeti: + fix_mobkill(Yetifly); +OnDem2: + if (!playerattached()) { + consolebug "ERROR - PLAYER NOT ATTACHED, INVALID KILL, CANNOT RESOLVE MAP."; + end; + } + .@m$ = getmap(); + .@left=0; + .@left+=mobcount(.@m$, strnpcinfo(NPC_NAME_UNIQUE)+"::OnYeti"); + .@left+=mobcount(.@m$, strnpcinfo(NPC_NAME_UNIQUE)+"::OnDem1"); + .@left+=mobcount(.@m$, strnpcinfo(NPC_NAME_UNIQUE)+"::OnDem2"); + // You win! + if (!.@left) { + mapannounce(getmap(), "CONGRATULATIONS - YOU WIN", bc_map | bc_pc); + Zeny += rand2(500, 1500)*max(1, @d_mode2); + // FIXME: Add a proper reward + maptimer2(.@m$, 1000, strnpcinfo(NPC_NAME_UNIQUE)+"::OnBye"); + 01871_Demure_BlackBox(); + } + end; + +OnBye: + Zeny += rand2(100, 300)*max(1, @d_mode2); + warp "018-7-1", 23, 22; + deltimer strnpcinfo(NPC_NAME_UNIQUE)+"::OnBeet"; + specialeffect(FX_FANFARE, SELF, getcharid(3)); + end; + +// Summon second form (give 5 seconds to players to move) +// FIXME: @d_* might not be set +OnDem1: + if (!playerattached()) { + consolebug "ERROR - PLAYER NOT ATTACHED, INVALID KILL, CANNOT RESOLVE MAP."; + end; + } + getmapxy(.@m$, .@x, .@y, 0); + .@n$ = "Demure, Queen of Dragons : "; + mapannounce(getmap(), .@n$+"You think you've won...?", bc_map | bc_pc); + sleep2(1000); + mapannounce(getmap(), .@n$+"That would be lame; After all, did you forget that...", bc_map | bc_pc); + sleep2(2000); + mapannounce(getmap(), .@n$+"I *am* the Queen of Dragons??", bc_map | bc_pc); + sleep2(1000); + // Read data, summon dragons... + for (.@i = 0; .@i < max(1, @d_count*@d_mode3/2); .@i++) { + .@mob = any(EarthFairy, FireFairy, WaterFairy, WindFairy, GreenDragon); + .@m = areamonster(.@m$, .@x-10, .@y-10, .@x+10, .@y+10, strmobinfo(1, .@mob), .@mob, 1); + .@p = getunitdata(.@m, UDT_MODE); + setunitdata(.@m, UDT_MODE, (.@p | MD_AGGRESSIVE)); + } + areamonster(.@m$, .@x-5, .@y-5, .@x+5, .@y+5, strmobinfo(1, GreenDragon), GreenDragon, 1); + + // And finally, change form (ressurect) + .@m=monster(.@m$, 87, 57, strmobinfo(1, DemureSecondForm), DemureSecondForm, 1, strnpcinfo(NPC_NAME_UNIQUE)+"::OnDem2"); + if (@d_count) { + .@p=getunitdata(.@m, UDT_MAXHP); + // If player is solo'ing, put more ATK and less HP + .@hp=.@p*@d_count*@d_mode3; + if (@d_mode1 == MODE_SOLO) { + .@hp = .@hp * 3 / 5; + // Raise ASPD based on difficulty + setunitdata(.@m, UDT_ADELAY, + getunitdata(.@m, UDT_ADELAY) - (@d_mode3 * 25)); + } + setunitdata(.@m, UDT_MAXHP, .@hp); + setunitdata(.@m, UDT_HP, .@hp); + } + mapannounce(getmap(), .@n$+"I'll leave a 2x2 black square where you used to be!", bc_map | bc_pc); + end; + +OnInit: + .distance = 4; + .sex = G_FEMALE; + npcsit; + end; +} + diff --git a/npc/018-7-1/lilit.txt b/npc/018-7-1/lilit.txt new file mode 100644 index 0000000..f8623cd --- /dev/null +++ b/npc/018-7-1/lilit.txt @@ -0,0 +1,19 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Lilit is the Queen of Fairies +// Not Titania nor Oberon? Eh. + + +018-7-1,82,47,0 script Lilit NPC_LILIT,{ + mesn; + mesq l("Greetings, young soul; I am the queen of fairies, Lilit."); + close; + +OnInit: + .distance = 4; + .sex = G_FEMALE; + end; +} + diff --git a/npc/018-7-1/yetifly.txt b/npc/018-7-1/yetifly.txt new file mode 100644 index 0000000..5a21de5 --- /dev/null +++ b/npc/018-7-1/yetifly.txt @@ -0,0 +1,20 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Yetifly the Mighty + +018-7-1,54,78,0 script Yetifly NPC_YETIFLY,{ + mesn; + mesq l("Heh, congratulations making it this far. Once again, allow me to present myself:"); + next; + mesn; + mesq l("I am Yetifly the Mighty, guardian of the fae!"); + close; + +OnInit: + .distance = 4; + .sex = G_MALE; + end; +} + diff --git a/npc/018-7/_import.txt b/npc/018-7/_import.txt new file mode 100644 index 0000000..6b0664e --- /dev/null +++ b/npc/018-7/_import.txt @@ -0,0 +1,5 @@ +// Map 018-7: Lilit - Developers' Sanctuary +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/018-7/_mobs.txt", +"npc/018-7/_warps.txt", +"npc/018-7/raify.txt", diff --git a/npc/018-7/_mobs.txt b/npc/018-7/_mobs.txt new file mode 100644 index 0000000..f45427e --- /dev/null +++ b/npc/018-7/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-7: Lilit - Developers' Sanctuary mobs +018-7,59,74,14,4 monster Duck 1029,6,200000,35000 +018-7,69,27,8,7 monster Forest Piou 1202,4,35000,120000 +018-7,61,57,44,43 monster Wind Fairy 1185,16,40000,20000 +018-7,34,73,25,34 monster Water Fairy 1185,6,20000,40000 +018-7,85,73,25,34 monster Fire Fairy 1183,6,20000,40000 +018-7,59,40,48,22 monster Earth Fairy 1182,6,20000,40000 +018-7,34,31,14,11 monster Nulity Pixie 1218,1,120000,0 +018-7,81,38,14,11 monster Vanity Pixie 1215,1,120000,0 +018-7,34,86,14,11 monster Holy Pixie 1216,1,120000,0 +018-7,80,80,14,11 monster Shadow Pixie 1217,1,120000,0 diff --git a/npc/018-7/_warps.txt b/npc/018-7/_warps.txt new file mode 100644 index 0000000..f26f85e --- /dev/null +++ b/npc/018-7/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 018-7: Lilit - Developers' Sanctuary warps +018-7,59,70,0 warp #018-7_59_70 0,0,018-7-1,56,99 +018-7,52,32,0 warp #018-7_52_32 0,0,018-5,100,53 diff --git a/npc/018-7/raify.txt b/npc/018-7/raify.txt new file mode 100644 index 0000000..332cc26 --- /dev/null +++ b/npc/018-7/raify.txt @@ -0,0 +1,143 @@ +// TMW-2 Script. +// Author: +// Povo +// Description: +// Lime Dye Quest (lvl 40) + +018-7,87,34,0 script Raify NPC_FAIRY_A,{ + .@q = getq(LilitQuest_Raify); + if (.@q == 1) + goto L_Offer; + if (.@q >= 2) + goto L_Dye; + if (BaseLevel > 39) { + speech S_LAST_NEXT, + l("Psst."), + l("Hey you. Come here...I have a quest for ya."), + l("Keep your voice down and just listen."), + l("Look, I really hate pious...all of them."), + l("I want to get rid of them."), + l("However, that is not exactly a popular idea around here. The fae seem more concerned with snakes."), + l("That is why I need your help. If you can 'take care of' a bunch of pious and bring me their feathers as proof, I will give you a reward."); + goto L_Offer; + } + speech S_LAST_NEXT, + l("Hmm what is it?"), + l("I'm sorry but I can't talk right now... I have something on my mind."), + l("I said go away!"), + l(" It is not like you are strong enough to pick a fight with me so kindly leave before I get mad."); + close; + +L_Offer: + mesn; + mesq l("So here is what I am thinking."); + next; + mesn; + mesq l("You bring me proof of completing my 'mission,' along with some water and I can dispose of the evidence by turning them into %s", + getitemlink(LimeDye)); + next; + mesn; + mesq l("I think that %d %s and %d %s should be enough for now.", + 50, getitemlink(ForestPiouFeathers), + 50, getitemlink(ManaPiouFeathers)); + mesq l("I will also need %d %s to make the mixture and %s GP as a fee.", + 1, getitemlink(BottleOfWoodlandWater), + fnum(2500)); + compareandsetq LilitQuest_Raify, 0, 1; + .@q = getq(LilitQuest_Raify); + if (.@q >= 2) + goto L_Dye; + +L_Menu: + next; + mesn; + mesq l("So what do you say? You in?"); + next; + select + l("Uhh...yeah sure...I guess..."), + l("Dye? How can you do that without a cauldron?"), + l("If I am doing your dirty work, why am I the one paying you?!"), + l("Wait...could you repeat that?"), + l("Umm..I have another quest to get to. So... umm... I should get going."); + + mes ""; + switch (@menu) { + case 1: + setq LilitQuest_Raify, 2; + mesn; + mesq l("Perfect, I knew you were right person for the job."); + mesq l("Come back when you have everything."); + close; + case 2: + mesn; + mesq l("Do you really have to ask? I am a fairy...*sigh*"); + next; + mesq l("...wingless humanoids."); + break; + case 3: + mesn; + mesq l("Consider it an insurance policy. Besides, %s isn't exactly easy to come by.", getitemlink(LimeDye)); + break; + case 4: + mesn; + mesq l("I need you to take care of my 'Piou Problem.'"); + next; + goto L_Offer; + case 5: + goto L_Quit; + } + goto L_Menu; + +L_Dye: + mesn; + mesq l("So did you take care of my 'Piou Problem?'"); + mesq l("Did you bring everything?"); + next; + select + l("Yes."), + l("No."), + l("What did you need me to get again?"); + mes ""; + + if (@menu == 2) + goto L_Quit; + + if (@menu == 3) + goto L_Offer; + +L_DyeLoop: + if (countitem(BottleOfWoodlandWater) >= 1 && + countitem(ForestPiouFeathers) >= 50 && + countitem(ManaPiouFeathers) >= 50 && + Zeny >= 2500) { + inventoryplace LimeDye, 1, EmptyBottle, 1; + delitem BottleOfWoodlandWater, 1; + delitem ForestPiouFeathers, 50; + delitem ManaPiouFeathers, 50; + Zeny-=2500; + getitem LimeDye, 1; + getitem EmptyBottle, 1; + if (getq(LilitQuest_Raify) == 2) { + setq LilitQuest_Raify, 3; + getexp 3500, 0; + } + mesn; + mesq l("Thanks! Here's your dye. Do you want to trade any more?"); + next; + if (askyesno() == ASK_YES) + goto L_DyeLoop; + } else { + mesn; + mesq l("Sorry, you don't seem to have everything I need."); + } + close; + +L_Quit: + closedialog; + close; + +OnInit: + .sex = G_OTHER; + .distance = 5; + end; +} diff --git a/npc/019-1-1/_import.txt b/npc/019-1-1/_import.txt new file mode 100644 index 0000000..cb56f2c --- /dev/null +++ b/npc/019-1-1/_import.txt @@ -0,0 +1,4 @@ +// Map 019-1-1: Miller's House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-1-1/_warps.txt", +"npc/019-1-1/miler.txt", diff --git a/npc/019-1-1/_warps.txt b/npc/019-1-1/_warps.txt new file mode 100644 index 0000000..f5764e2 --- /dev/null +++ b/npc/019-1-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-1-1: Miller's House warps +019-1-1,33,42,0 warp #019-1-1_33_42 0,0,019-1,84,59 diff --git a/npc/019-1-1/miler.txt b/npc/019-1-1/miler.txt new file mode 100644 index 0000000..7a294aa --- /dev/null +++ b/npc/019-1-1/miler.txt @@ -0,0 +1,222 @@ +// TMW2/LOF Script. +// Author: +// Jesusalva +// Description: +// Part from the EPISODE and the Well Quest +// TODO: Walking NPC, clothes, etc; + +019-1-1,41,24,0 script Miler NPC_PLAYER,{ + .@q=getq(LoFQuest_EPISODE); + .@w=getq(NivalisQuest_Well); + + mesn; + if (MERC_RANK) + mesq l("Hello, @@.", mercrank()); + else if (THIEF_RANK) + mesq l("Hello, @@.", thiefrank()); + else + mesq l("Hello."); + + mes ""; + menu + l("Hello."), -, + rif(.@w == 1, l("I need help.")), L_Well, + rif(.@q == 3 && countitem(HerbalTea), l("The Doctor sent you some tea.")), L_Doctor, + rif(.@q == 5 && countitem(PresentBox), l("I have a present box to you open.")), L_Box, + rif(.@q == 4 && BaseLevel >= 50, l("So, could I help you?")), L_Quest, + rif(.@q == 6 && !@miler_wait, l("I'm back.")), L_Continue, + l("Do you want any monster killed?"), L_GHQ; + + // If not on Cordo quest, Miler will speak about + if (!THIEF_RANK && !MERC_RANK) + goto L_Rejected; + + close; + +// Well Quest Subplot +L_Well: + mes ""; + mesn; + mesq l("What's the problem?"); + next; + select + l("Someone fell into the well."); + mes ""; + mesn; + mesq l("Ho! I'll help them!"); + getexp (JobLevel * 111), 0; + setq NivalisQuest_Well, 2; + close; + +// Well Quest Subplot +L_Doctor: + mes ""; + delitem HerbalTea, 1; + getexp 111, 11; + setq LoFQuest_EPISODE, 4; + mesn; + mesq l("Many thanks, the Doctor always know what's best for you."); // you or your health? Are you sure? + next; + mesn; + mesq l("Lemme just fetch a small something for you...."); + next; + mesn; + mesq l("Oh dear, oh dear, where could I have possibly left it?!"); + close; + +// Not on Cordo quest +L_Rejected: + /* + mesn; + mesq l("You cannot help me at all. You lack any skill to do so."); + next; + */ + mesn; + mesq l("Hey, did you know there are two mouboos which constantly fight against themselves?!"); + next; + mesn; + mesq l("One claims to be a constable and teach people to sell high and buy low."); + mesq l("The other one claims to be a dangerous bandit and to teach how to steal from monsters!"); + next; + mesn; + mesq l("Well, I heard you needed to have some Job levels to sign up with them, and couldn't resign later."); + mesq l("But it is a so silly fight, that whoever you join with shouldn't do much difference."); + next; + mesn; + mesq l("Anyway, I heard both were disciples from Cordo-whatever, a powerful person from LoF Village."); + //mesq l("I think you should get initiated on any side before speaking to me again."); + close; + +// Main Quest +L_Quest: + + // Force players upon Cordo quest + /* + if (!THIEF_RANK && !MERC_RANK) + goto L_Rejected; + */ + + mes ""; + mesn; + mesq l("I lost the precious ring they gave me as a gift... Who could have taken it...?"); + next; + mesn; + mesq l("...Of course. It was THEM. It gotta to be them!"); + next; + mesn strcharinfo(0); + select l("'Them' whom?"); + mes ""; + mesn; + mesq l("They came in the night, always taking what does not belong to them.... The SLIMES!"); + next; + mesn; + mesq l("The ones around here are specially nasty. They steal stuff and seal them on 'present boxes', just to amuse as people try to open those..."); + next; + mesn; + mesq l("...But worry not, I have the right screwdriver for the job. So, are you up to bring me some Present Boxes?"); + next; + mesn strcharinfo(0); + select + l("Yes, I'll help you."), + l("Nah, not now. Slimes ruin your clothes, after all."); + mes ""; + if (@menu == 2) { + mesn; + mesq l("Ah, I see, I imagine you'll wear something different then... But please come back."); + close; + } + setq LoFQuest_EPISODE, 5, 0; + mesn; + mesq l("Great, just bring me several boxes, once one of them have the ring I'm looking for."); + close; + +// Open Present Box Loop +L_Box: + inventoryplace NPCEyes, 1; + .@q2=getq2(LoFQuest_EPISODE); + delitem PresentBox, 1; + setq2 LoFQuest_EPISODE, .@q2+1; + mesn; + mesc l("@@ uses his screwdriver and open the sealed gift box like a pro.", .name$); + // Handle result + if (.@q2 >= 70) { + goto L_Success; + } else if (.@q2 >= 50) { + .@id=any(WhiteFur, Candy, Milk, Lockpicks, MaggotSlime, CandyCane, ChocolateBar, XmasCake, GingerBreadMan, CherryCake, Plushroom, Moss, Chagashroom, BugLeg, Acorn, Manana, Mashmallow, HardSpike, SilkCocoon, IceCube, CoinBag, Coal, CottonCloth, BlueDye); + getitem .@id, 1; + mesc l("But there was only a(n) @@ inside.", getitemlink(.@id)); + } else if (.@q2 >= 30) { + if (rand(1,50) < .@q2) { + .@id=any(Candy, MaggotSlime, Plushroom, Chagashroom, BugLeg, Acorn, MauveHerb); + getitem .@id, 1; + mesc l("But there was only a(n) @@ inside.", getitemlink(.@id)); + } else { + .@id=rand2(2,4)+.@q2; + Zeny=Zeny+.@id; + mesc l("But there was only @@ GP inside.", .@id); + } + } else if (.@q2 >= 10) { + .@id=rand2(3,5)+.@q2; + Zeny=Zeny+.@id; + mesc l("But there was only @@ GP inside.", .@id); + } else { + mesc l("But the box was empty."); + } + + // Try again! + if (countitem(PresentBox)) { + mesn; + mesq l("You have more boxes. Wanna try again?"); + if (askyesno() == ASK_YES) + goto L_Box; + } + close; +// Quest Complete +L_Success: + setq LoFQuest_EPISODE, 6, 0; + getexp 25000, 0; // @Saulc DO NOT INCREASE THIS VALUE, if you think this is low, give player MobPoints. + // TODO: Pre-Requisite Item? (eg. Lazurite or Bent Neddle) + // TODO: White Roses quest. We could sell them high and allow drop during spring + mesc l("His golden ring pops right out of it."); + next; + mesn; + mesq l("Many thanks. I couldn't live without it. Please come back later."); + @miler_wait=true; + close; + +L_Continue: + if (BaseLevel < 50) goto L_Rejected; + mesn; + mesq l("Welcome back. Uh, no, I haven't forgot I promised you a small something... But you see, then I lost my ring, and..."); + next; + mesn; + mesq l("...I deposited everything on the bank. Sorry!"); + next; + mesn; + mesq l("Oh, but don't you worry. Sure, you can't go in Nivalis bank and take my stuff... But the Storage Fairy at Lilit might just let you."); + next; + mesn; + mesq l("But she is a fairy. She won't be pleased with flowers. Instead, go and give her a %s as a token of good will.", getitemlink(SnakeSkin)); + next; + mesn; + mesq l("Pal, I'm counting on you. You'll like the little something I have for you!"); + setq LoFQuest_EPISODE, 7, 0; + close; + +L_GHQ: + GHQ_Assign(Moggun, "Nivalis"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, KnitHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, JeansShorts); + setunitdata(.@npcId, UDT_HAIRSTYLE, any(2,3,4,6,14,15,17,21,22,24,25,26)); + setunitdata(.@npcId, UDT_HAIRCOLOR, rand(0,20)); + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/019-1/_import.txt b/npc/019-1/_import.txt new file mode 100644 index 0000000..0513ece --- /dev/null +++ b/npc/019-1/_import.txt @@ -0,0 +1,6 @@ +// Map 019-1: Snow Field +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-1/_mobs.txt", +"npc/019-1/_warps.txt", +"npc/019-1/sign.txt", +"npc/019-1/well.txt", diff --git a/npc/019-1/_mobs.txt b/npc/019-1/_mobs.txt new file mode 100644 index 0000000..25c4953 --- /dev/null +++ b/npc/019-1/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-1: Snow Field mobs +019-1,70,67,52,48 monster Fluffy 1022,50,100000,30000 +019-1,61,73,52,48 monster White Slime 1094,20,100000,30000 +019-1,79,61,52,48 monster Wolvern 1037,12,60000,30000 +019-1,56,60,21,18 monster Santa Slime 1096,2,30000,30000 +019-1,115,52,19,16 monster Pollet 1219,2,200000,30000 diff --git a/npc/019-1/_warps.txt b/npc/019-1/_warps.txt new file mode 100644 index 0000000..85492a9 --- /dev/null +++ b/npc/019-1/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-1: Snow Field warps +019-1,70,21,0 warp #019-1_70_21 3,0,020-1,70,127 +019-1,61,102,0 warp #019-1_61_102 3,0,019-4,95,28 +019-1,21,70,0 warp #019-1_21_70 0,1,019-6,256,69 +019-1,84,53,0 warp #019-1_84_53 0,0,019-1-1,33,41 diff --git a/npc/019-1/sign.txt b/npc/019-1/sign.txt new file mode 100644 index 0000000..93599d6 --- /dev/null +++ b/npc/019-1/sign.txt @@ -0,0 +1,21 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Random Sign nobody bothers reading. + +019-1,67,30,0 script Sign#01916730 NPC_SWORDS_SIGN,{ + if (!$NIVALIS_LIBDATE) { + mesc l("WARNING: Nivalis is currently under siege from the Monster King himself."), 3; + next; + mesc l("We've built this blockade to prevent this area from being overrun with monsters, but who knows for how long this will last."); + } else { + mesc l("The city is still under repairs, but town square is safe already."), 1; + } + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} diff --git a/npc/019-1/well.txt b/npc/019-1/well.txt new file mode 100644 index 0000000..97a3e5d --- /dev/null +++ b/npc/019-1/well.txt @@ -0,0 +1,169 @@ +// TMW2/LOF Script. +// Author: +// Jesusalva +// Description: +// Well connected to Terranite Cave. This is the water Nivalis townsfolk uses. + +019-1,110,101,0 script Well#Nivalis NPC_NO_SPRITE,{ + // Begin here + .@q=getq(NivalisQuest_Well); + mesn l("The Self-Serving Ice Well!"); + mesc l("Hello, my name is Mahid, and this well belongs to me!"); + mesc l("You are allowed to fill your bottles, but BE SURE TO PAY!"); + mesc l("Otherwise, ##BYOU'LL DIE.##b Have a nice day!"); + next; + if (.@q == 1) { + mesn l("???"); + mesq l("Hey, is somebody over there?"); + mes ""; + } + + menu + l("Fill Water Bottles"), L_Bottle, + rif(.@q == 2, l("Jump inside!")), L_Reckless, + rif(.@q == 1, l("Steal bucket!")), L_Bucket, + rif(.@q != 1, l("Throw something inside!")), L_Throw, + l("Leave."), -; + close; + +// Jump to Terranite Cave (requires 55 vitality minimum) +L_Reckless: + closedialog; + warp "015-6", 363, 109; + dispbottom l("Ouch! That was kinda reckless!"); + percentheal -150+readparam2(bVit), 0; + close; + +// Easter Egg +L_Bucket: + mes ""; + percentheal -rand(10,20), 0; + mesn strcharinfo(0); + mesq l("Ouch, the bucket BITE me!"); + close; + +// Main Quest +L_Throw: + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .@id = requestitem(); + + // If ID is invalid, there's not enough items, it is bound = Cannot bury + if (.@id < 1) { + mesc l("You give up."); + close; + } + if (countitem(.@id) < 1 || checkbound(.@id) || !getiteminfo(.@id, ITEMINFO_MAXCHANCE)) { + if (checkbound(.@id)) + mesc l("You cannot drop this item!"); + else if (!getiteminfo(.@id, ITEMINFO_MAXCHANCE)) + mesc l("This item is too precious, you cannot part with it!"); + else + mesc l("You give up."); + close; + } + // Delete item and spawn it at Terranite Cave (an Angry Yellow Slime might steal it) + delitem .@id, 1; + makeitem .@id, 1, "015-6", any(362,363), any(110, 112, 114); + // Now we check if quest must start + if (!.@q) + goto L_Quest; + // If not, report if it is safe to jump + if (readparam2(bVit) < 55) + mesc l("The item impact suggests you don't have enough vitality to jump inside."), 1; + else if (readparam2(bVit) < 75) + mesc l("The item impact suggests jumping inside will leave you badly wounded."); + else + mesc l("The item impact suggests jumping inside should be safe if you have enough life."); + close; + +// Quest Node +L_Quest: + mesn l("???"); + if (getiteminfo(.@id, ITEMINFO_WEIGHT) > 1000) + mesq l("Ouch! That's heavy!"); + else if (getiteminfo(.@id, ITEMINFO_SELLPRICE) < 10) + mesq l("What cheap crap is this? It's not worth even 10 GP."); + else if (getiteminfo(.@id, ITEMINFO_TYPE) == IT_HEALING) + mesq l("Mhm, this looks healthy."); + else if (getiteminfo(.@id, ITEMINFO_TYPE) == IT_AMMO) + mesq l("Ammo? I prefer power gloves! That is useless for me!"); + else + mesq l("Who is throwing stuff at me?!"); + next; + select + l("Who are you?"), + l("How did you get down there?"), + l("Do you need help?"), + menuaction(l("Leave.")); + mes ""; + mesn; + switch (@menu) { + case 1: mesq l("I'll talk about who I am after leaving the well."); break; + case 2: mesq l("I don't remember. I guess somebody threw me here!"); break; + case 3: mesq l("I certainly can't get out on my own."); break; + default: mesq l("..Anyone there?"); close; break; + } + next; + do { + select + l("I'll call someone to aid you."), + l("Is it too deep?"), + l("Couldn't you climb the rope?"), + menuaction(l("Leave.")); + mes ""; + mesn; + switch (@menu) { + case 1: mesq l("Please do, my friend."); setq NivalisQuest_Well, 1; break; + case 2: mesq l("It's over a hundred meters in depth. There is some land here, but I'm afraid of Terranite."); break; + case 3: mesq l("I'm not crazy, the bucket is vicious and the rope won't withstand my weight."); break; + default: mesq l("..Anyone there?"); close; break; + } + } while (@menu != 1); + close; + +// Fill a water bottle +L_Bottle: + mes ""; + mesc l("Cost: @@ gp per bottle.", .price); + input .@count; + + if (!.@count) + close; + + .@gp = .@count * .price; + + if (Zeny < .@gp) { + mesc l("Not enough money."); + close; + } + + if (countitem(EmptyBottle) < .@count) { + mesc l("Not enough bottles."); + close; + } + + inventoryplace IcedBottle, .@count, BottleOfWoodlandWater, .@count; + delitem EmptyBottle, .@count; + + // Calculate how many iced bottles you'll get + .@iced=0; + for (.@i=0; .@i < .@count; .@i++) { + if (rand2(500) < 33) + .@iced++; + } + + // Apply the results and have a happy day! + Zeny-=.@gp; + if (.@iced) + getitem IcedBottle, .@iced; + getitem BottleOfWoodlandWater, .@count-.@iced; + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + .price=60; + end; +} diff --git a/npc/019-2/_import.txt b/npc/019-2/_import.txt new file mode 100644 index 0000000..a9e9c04 --- /dev/null +++ b/npc/019-2/_import.txt @@ -0,0 +1,9 @@ +// Map 019-2: Nivalis Port +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-2/_mobs.txt", +"npc/019-2/_warps.txt", +"npc/019-2/angela.txt", +"npc/019-2/guards.txt", +"npc/019-2/harry.txt", +"npc/019-2/ship.txt", +"npc/019-2/sign.txt", diff --git a/npc/019-2/_mobs.txt b/npc/019-2/_mobs.txt new file mode 100644 index 0000000..061fe05 --- /dev/null +++ b/npc/019-2/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-2: Nivalis Port mobs +019-2,60,59,5,2 monster White Slime 1094,2,90000,30000 +019-2,73,53,42,36 monster Fluffy 1022,10,60000,30000 +019-2,97,72,8,37 monster Wind Fairy 1185,1,75000,30000 +019-2,70,104,36,21 monster Iced Fluffy 1041,7,60000,30000 +019-2,72,41,31,26 monster Pollet 1219,4,120000,30000 +019-2,75,35,7,7 monster White Slime 1094,2,90000,30000 diff --git a/npc/019-2/_warps.txt b/npc/019-2/_warps.txt new file mode 100644 index 0000000..85925ec --- /dev/null +++ b/npc/019-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-2: Nivalis Port warps +019-2,37,55,0 warp #019-2_37_55 0,0,020-1,106,55 +019-2,76,24,0 warp #019-2_76_24 0,0,021-1,212,299 diff --git a/npc/019-2/angela.txt b/npc/019-2/angela.txt new file mode 100644 index 0000000..0455bda --- /dev/null +++ b/npc/019-2/angela.txt @@ -0,0 +1,141 @@ +// TMW2 scripts. +// Authors: +// The Mana World Team +// Jesusalva +// Description: +// Elf, main quest is rescuing Cindy (she's kidnapped by Yetis every once in a while) +// Once you complete her quest (requires some scheduling which the Yeti King provides), +// you'll gain some needed Reputation Points to be able to cross Frostia Gates. +// Note this is counted with other Frostia quests to reduce the toll of entering Frostia +// (With high Frostia reputations, you get rid of the tax and high honors, but this +// also raise/blocks Orc Village Entrance. Because if you haven't noticed, there's a +// delicate equilibrium between Elves and Orcs.) +// +// Globally Controled Quest. Sometimes, she's here. Other times, this NPC is +// disabled and the one inside the house is active. That's done with a walk +// animation. +// +// Related Quests: Nivalis Well, Celestia Quest, Frostia Quest +// Main Quest: Cindy Quest +// Reward: Something awesome, probably. +// Advised Level: Groups of level 80 players. +// +// Quest Variable: NivalisQuest_Cindy +// 0: Quest Not Started +// 1: Angela assigned the quest +// 2: Yeti instructions were received +// 3: Yeti quest complete, received instructions +// 4: Checkpoint +// 5: Cindy was rescued! +// +// Global Variable: $@CINDY_STATE +// 0: Status is clean (she'll be kidnapped next interation) +// %1: Cindy still wasn't rescued +// %2: A party is fighting currently (the division value is the difficulty level, and affects minimum level) +// >1500000000: Cindy is safely at home (until this date +%s) + +019-2,75,25,0 script Angela#Outside NPC_ELF_F,{ + if ($@CINDY_STATE > gettimetick(2)) + goto L_Safe; + if (BaseLevel < 60) { hello; end; } + if ($@CINDY_STATE % 2 == 1) goto L_Worried; + if (getq(NivalisQuest_Cindy) == 0) goto L_Start; + mesn; + mesq l("Please find my lovely daughter!"); + if (getq(NivalisQuest_Cindy) == 1 && getq(NivalisQuest_Well) < 2 && @yetiagro) { + next; + select + l("I will, don't worry."), + l("She isn't on that cave."); + mes ""; + if (@menu == 2) { + mesn; + mesq l("Do you have mental issues? I mean, of course there is a secret passage there!"); + next; + mesn; + mesq l("I think the switches may open it, but I couldn't flip them."); + next; + mesn; + mesq l("I guess you'll need an Yeti for that, but those on the cave are murderous."); + CINDY_PLAYER_STRUCK=true; + next; + mesn; + mesq l("Besides, I'm not an Yeti expert. I don't understand how these creatures think, nor do I care! I only want Cindy back home safely!!"); + if (TUTORIAL) + mesc l("Don't we know any Yeti specialist? Maybe Celestia?"); + } + } + close; + +L_Safe: + mesn; + mesq l("Ah, what a wonderful day."); + close; + +L_Worried: + mesn; + mesq l("The group of warriors which went into the cave to rescue my precious Cindy still haven't returned... I'm worried with them, too..."); + close; + +L_Start: + mesn; + mesq l("My daughter! My precious Cindy, why it had to be you?!"); + next; + mesn; + mesq l("Please, help my precious! Save her! Oh, my precious Cindy..."); + next; + mesn col(l("Rescuing Cindy Quest"),1); + mesc l("You are about to accept a high-risk quest. You cannot complete it alone."), 1; + mesc l("Help the distressed woman to save her daughter? ALL PLAYERS are penalized with failures."), 1; + select + l("[Decline Quest]"), + l("[Accept Quest]"); + mes ""; + if (@menu == 1) { + mesn strcharinfo(0); + mesq lg("Sorry 'mam, I forgot courage on my other set of pants. This one doesn't have enough defense for it."); + close; + } + mesn; + mesq l("Awful Yetis kidnapped my daughter, and brought her to the cave. Please save her!"); + next; + mesn; + mesq l("Ah, but don't distress them if you can't save her! My precious daughter life is more important than anything!"); + next; + mesn; + mesq l("I'll reward you and your friends in an appropriate way, but if you endanger her... Oh, my poor Cindy..."); + next; + movecam rand(-20,20), rand(-20,20); + sleep2(80); + movecam rand(-20,20), rand(-20,20); + sleep2(80); + movecam rand(-20,20), rand(-20,20); + sleep2(80); + movecam rand(-20,20), rand(-20,20); + sleep2(80); + restorecam; + mes ""; + mesc l("[Quest Accepted]"), 1; + mesc l("Rescue Cindy from the Ice Labyrinth and the evil Yetis. This is a global, multiplayer quest."), 1; + mesc l("Advised party size: From 3 and above"); + channelmes("#world", strcharinfo(0) + " said to be brave enough to help Angela rescuing her daughter!"); + setq NivalisQuest_Cindy, 1; + close; + + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, KnitHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, TneckSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, CottonSkirt); + setunitdata(.@npcId, UDT_HAIRSTYLE, any(7,8,9,10,11,12,13,19,20)); + setunitdata(.@npcId, UDT_HAIRCOLOR, rand(0,20)); + + .sex = G_FEMALE; + .distance = 5; + end; +} + + + diff --git a/npc/019-2/guards.txt b/npc/019-2/guards.txt new file mode 100644 index 0000000..3f7e350 --- /dev/null +++ b/npc/019-2/guards.txt @@ -0,0 +1,165 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Protect Nivalis +// Q_NivalisLibday +// Day, Score, Temporary Time; + +019-2,44,54,0 script Guard#019-2.1 NPC_GUARD1,{ + .@q=getq(Q_NivalisLibday); + + if ($NIVALIS_LIBDATE) { + mesn; + mesq l("Nivalis was liberated @@ ago.", FuzzyTime($NIVALIS_LIBDATE)); + close; + } else if ($NLIB_DAY > 0) { + if ($NLIB_DAY >= 7) goto L_MK; + if (.@q == $NLIB_DAY) goto L_Delay; + if (BaseLevel < 30) goto L_Noob; + if (BaseLevel >= 30) goto L_Veteran; + close; + } else if (is_admin()) { // NOTE: This is an override label + mesc "Initiate Nivalis Liberation Day?", 1; + mesc "Event will last 7 days.", 1; + if ($NLIB_SEQDAY) + mesc "The event is currently set to happen in "+($NLIB_SEQDAY-gettimeparam(GETTIME_DAYOFMONTH))+" days."; + else + mesc b("Liberation Day is NOT scheduled to happen."), 1; + select + "Not yet.", + "Yes", + "No"; + if (@menu == 2) { + closeclientdialog; + goto OnNLibStart; + } + close; + } else { + legiontalk; end; + } + end; + +OnNLibStart: + addmapmask "019-3", MASK_MATTACK; + $NLIB_DAY=1; + $NLIB_HIGHTIME=0; + $NLIB_HIGHNAME$=""; + setmapflagnosave("019-3", "000-1", 22, 22); + setmapflagnosave("020-1", "000-1", 22, 22); + setmapflagnosave("023-2", "000-1", 22, 22); + setmapflag("019-3",mf_bexp,25); + setmapflag("020-1",mf_bexp,150); + disablenpc "#019-1_70_21"; + disablenpc "#019-2_37_55"; + disablenpc "#020-1_70_128"; + disablenpc "#020-1_107_55"; + enablenpc "Lightbringer#NLib"; + kamibroadcast("Nivalis Liberation Day has started."); + $NLIB_SEQDAY=false; + end; + +// Event Selectors +L_Noob: + mesn; + mesq l("Hey, you! We need help to get rid from some remaining monsters at Nivalis City."); + mesq l("No need to kill the Fluffies, though."); + next; + mesn; + mesq l("Are you up for the challenge?"); + if (askyesno() == ASK_YES) { + // Begin quest. Logout/Death will cause loss of the current day. + setq1 Q_NivalisLibday, $NLIB_DAY; + @nlib_wave=0; + @nlib_time=300; // This makes sure that wave 1 will start. + addtimer(5000, "#NLib_Siege::OnLoop"); + warp "020-1", rand(69,82), rand(78, 91); + } + close; + +L_Veteran: + mesn; + mesq l("Hey, you! We need help to find the Monster King."); + next; + mesn; + mesq l("Could you head deep in the woods and track him down?"); + if (askyesno() == ASK_YES) { + // Control if you already found the Monster King + @QNL3=0; + // Begin quest. Logout/Death will cause loss of the current day. + setq1 Q_NivalisLibday, $NLIB_DAY; + setq3 Q_NivalisLibday, gettimetick(2); + warp "019-3", any(128, 129, 130, 131, 132), any(24, 25, 26, 27); + doevent("Guard#019-3.1::OnBegin"); + closedialog; + } + close; + +L_MK: + if (gettime(3) != 18 && !$@GM_OVERRIDE) { + mesn; + mesq l("Today at 18:30 UTC we are going to attack the Monster King by surprise. There will be no delays, so be there."); + } else { + mesn; + mesq l("Do you want to go against the Monster King now? The event will start 18:30 UTC sharply."); + if (askyesno() == ASK_YES) { + // Control if you already found the Monster King + @QNL3=0; + // Begin quest. Logout/Death will cause loss of the current day. + setq1 Q_NivalisLibday, $NLIB_DAY; + setq3 Q_NivalisLibday, gettimetick(2); + warp "019-3", any(128, 129, 130, 131, 132), any(24, 25, 26, 27); + doevent("Guard#019-3.1::OnAdvise"); + closedialog; + } + } + close; + +// Misc +L_Delay: + mesn; + mesq l("You already helped us today. Come back tomorrow."); + next; + mesc l("Do you want to cross to the other side? You'll need to find the sea to return here if you do."), 1; + next; + if (askyesno() == ASK_YES) + warp "019-1", 70, 30; + close; + +OnInit: + if ($NLIB_DAY) + addmapmask "019-3", MASK_MATTACK; + + .sex = G_MALE; + .distance = 5; + end; + +OnHour00: + if ($NLIB_DAY) + $NLIB_DAY+=1; + // Begin Nivalis Liberation Day + if ($NLIB_SEQDAY == gettimeparam(GETTIME_DAYOFMONTH)) + goto OnNLibStart; + end; + +OnClock1800: + if ($NLIB_DAY == 7) { + kamibroadcast("All players, Nivalis Liberation Day starting in ##Bhalf hour##b."); + kamibroadcast("Failing this event will change the game world FOREVER and in an IRREVERSIBLE way."); + } + end; + +OnClock1825: + if ($NLIB_DAY == 7) + announce "All players, Nivalis Liberation Day starting in five minutes.", bc_all | bc_npc; + end; + +OnClock1830: + if ($NLIB_DAY != 7) + end; + setmapflag("023-2",mf_bexp,200); + setmapflag("023-2",mf_nopenalty); + donpcevent("The Monster King#NLib::OnBegin"); + end; +} + diff --git a/npc/019-2/harry.txt b/npc/019-2/harry.txt new file mode 100644 index 0000000..bafc00a --- /dev/null +++ b/npc/019-2/harry.txt @@ -0,0 +1,15 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Harry gives the player latest news on the world + +019-2,92,116,0 script Harry NPC_JOURNALMAN,{ + Journalman(.name$); + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/019-2/ship.txt b/npc/019-2/ship.txt new file mode 100644 index 0000000..b51633c --- /dev/null +++ b/npc/019-2/ship.txt @@ -0,0 +1,18 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// This script controls access to Ships, fixing variables. + +019-2,120,113,0 script NivalShip NPC_HIDDEN,0,0,{ + +OnTouch: + EnterTown("Nival"); + goto L_Warp; + +L_Warp: + warp "016-1@"+LOCATION$, 21, 26; + closedialog; + close; +} + diff --git a/npc/019-2/sign.txt b/npc/019-2/sign.txt new file mode 100644 index 0000000..eb663bf --- /dev/null +++ b/npc/019-2/sign.txt @@ -0,0 +1,23 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Random Sign nobody bothers reading. +// I just got lazy. Maybe I'll sell these two houses for 1000000 GP each... :drolls: + +019-2,95,108,0 script Sign#019295108 NPC_NO_SPRITE,{ + if (!$NIVALIS_LIBDATE) { + mesc l("WARNING: Nivalis is currently under siege from the Monster King himself."), 3; + next; + mesc l("The Alliance Advanced Outposts are closed for maintenance. Group in front of the town entrance!"); + } else { + mesc l("Welcome to Nivalis, the frozen town."); + mesc l("The Alliance Advanced Outposts have been abandoned and locked after the Liberation day."); + } + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} diff --git a/npc/019-3/_import.txt b/npc/019-3/_import.txt new file mode 100644 index 0000000..498f4b8 --- /dev/null +++ b/npc/019-3/_import.txt @@ -0,0 +1,5 @@ +// Map 019-3: Snow Forest +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-3/_mobs.txt", +"npc/019-3/_warps.txt", +"npc/019-3/guards.txt", diff --git a/npc/019-3/_mobs.txt b/npc/019-3/_mobs.txt new file mode 100644 index 0000000..15df646 --- /dev/null +++ b/npc/019-3/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-3: Snow Forest mobs +019-3,88,63,88,63 monster Fluffy 1022,50,100000,30000 +019-3,74,61,55,42 monster Wolvern 1037,35,100000,30000 +019-3,76,46,71,34 monster Santa Slime 1096,40,100000,30000 diff --git a/npc/019-3/_warps.txt b/npc/019-3/_warps.txt new file mode 100644 index 0000000..76a0245 --- /dev/null +++ b/npc/019-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-3: Snow Forest warps +019-3,58,84,0 warp #019-3_58_84 0,0,023-2,121,37 diff --git a/npc/019-3/guards.txt b/npc/019-3/guards.txt new file mode 100644 index 0000000..3e72118 --- /dev/null +++ b/npc/019-3/guards.txt @@ -0,0 +1,72 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Nivalis Liberation Day NPC + +019-3,128,22,0 script Guard#019-3.1 NPC_GUARD1,{ + if ($NLIB_DAY == 7) + goto L_MK; + mesn; + mesq l("Are you done yet?"); + select + rif(@QNL3, l("Yes")), + l("No"); + if (@menu == 2) + close; + + if ($NLIB_DAY == 7) + cwarp "019-2", 43, 55; + + // Load quest env + // Day, Score, Temporary Time; + .@q1=getq(Q_NivalisLibday); + .@q2=getq2(Q_NivalisLibday); + .@q3=getq3(Q_NivalisLibday); + + // Calculate score (You have 3 minutes. You get a single point for finishing.) + .@score=1; + .@time=gettimetick(2); + .@score+=max(180-(.@time-.@q3), 0)/5; + + // Update Total Score + setq2 Q_NivalisLibday, .@q2+.@score; + .@q2=getq2(Q_NivalisLibday); + // Update Highscores if needed + if (.@q2 > $NLIB_HIGHTIME) { + $NLIB_HIGHTIME=.@q2; + $NLIB_HIGHNAME$=strcharinfo(0); + } + // Close quest again and give you a proper reward + setq1 Q_NivalisLibday, $NLIB_DAY; + Zeny=Zeny+(.@score*15); // max about 450 GP + getexp .@score*BaseLevel*2, .@score; // max about 30 JExp - Estimate Max EXP Ranges: (2400~4500) XP + + // Warp you back + warp "019-2", 43, 55; + closedialog; + close; + +L_MK: + mesn; + mesq l("Good luck hunting down the Monster King."); + close; + +OnBegin: + npctalk l("Begin!"); + end; + +OnAdvise: + npctalk l("You must be where the Monster King is at 18:30 UTC sharply - no delays allowed!"); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + + if (!$NLIB_DAY) + disablenpc .name$; + end; + +} + diff --git a/npc/019-4-1/_import.txt b/npc/019-4-1/_import.txt new file mode 100644 index 0000000..e01bc7d --- /dev/null +++ b/npc/019-4-1/_import.txt @@ -0,0 +1,7 @@ +// Map 019-4-1: Christmas Workshop +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-4-1/_warps.txt", +"npc/019-4-1/bedder.txt", +"npc/019-4-1/chief.txt", +"npc/019-4-1/cook.txt", +"npc/019-4-1/golbarez.txt", diff --git a/npc/019-4-1/_warps.txt b/npc/019-4-1/_warps.txt new file mode 100644 index 0000000..caf73d3 --- /dev/null +++ b/npc/019-4-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-4-1: Christmas Workshop warps +019-4-1,51,46,0 warp #019-4-1_51_46 3,0,019-4,133,54 diff --git a/npc/019-4-1/bedder.txt b/npc/019-4-1/bedder.txt new file mode 100644 index 0000000..ca142be --- /dev/null +++ b/npc/019-4-1/bedder.txt @@ -0,0 +1,95 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Christmas Quest +// In dire need for white fur for bedding. Gives %Reward% for %Amount% (LESS LIKELY) +// (TMW Org. Version) +// Or maybe, he just take the white fur + something + open xmas box and trades +// these for closed gift boxes (MOST LIKELY) +// TMW2:ML Version + +019-4-1,48,39,0 script Christmas Storage Master NPC_GNOME_C,{ + if ($EVENT$ != "Christmas") + goto L_OutOfSeason; + mesn; + mesq l("I was informed that our bedding material for fragile presents is nearly depleted..."); + next; + goto L_Main; + +L_OutOfSeason: + mesn; + mesq l("According to the Holy books of a parallel world, Christmas is an event to celebrate the birth of someone very important, the son of God, whom have the promised kingdom."); + next; + mesn; + mesq l("Years later, someone then started giving gifts to the poor childrens on that date. It sticked, and now people usually trade gifts each other on the date."); + next; + mesn; + mesq l("It is known that even world wars have been under cease-fire on this date. On our world, Jesus Saves pays fortunes to Santa so he can gift every adventurer for the good year."); + next; + mesn; + mesq l("When Christmas starts, here will become frantic again, with things running out of stock all the time... But the adventurers deserve it, for fighting monsters the year round."); + close; + +L_Main: + mesn; + mesq l("You can give @@ to help us, I'll pay you accordingly.", getitemlink(WhiteFur)); // Normal: 30. Here: 75. + mesq l("Or you can give me an @@, 3 @@ and an @@, and I'll make a gift for you. If you don't have the @@, you can pay 200 GP for it instead.", getitemlink(OpenedChristmasBox), getitemlink(WhiteFur), getitemlink(MoubooFigurine), getitemlink(MoubooFigurine)); + mes ""; + select + l("Good to know."), + rif(countitem(WhiteFur), l("I want to sell White Fur for 75 GP each")), + rif(countitem(OpenedChristmasBox) && countitem(WhiteFur) >= 3 && countitem(MoubooFigurine), l("I want a gift box and here is the gift.")), + rif(countitem(OpenedChristmasBox) && countitem(WhiteFur) >= 3 && Zeny >= 200, l("I want a gift box but I don't have a gift.")); + mes ""; + switch (@menu) { + case 2: + mesc l("How many do you want to sell? You currently have @@ @@.", countitem(WhiteFur), getitemlink(WhiteFur)); + input .@count; + mes ""; + if (countitem(WhiteFur) < .@count) { + mesc l("You don't have that."); + next; + goto L_Main; + } else { + delitem WhiteFur, .@count; + Zeny=Zeny+75*(.@count); + mesn; + mesq l("Thanks. A pleasure doing business with you."); + next; + } + break; + case 3: + inventoryplace ClosedChristmasBox, 1; + delitem WhiteFur, 3; + delitem MoubooFigurine, 1; + delitem OpenedChristmasBox, 1; + getitem ClosedChristmasBox, 1; + mesn; + mesq l("Here you go, anything else?"); + next; + break; + case 4: + inventoryplace ClosedChristmasBox, 1; + delitem WhiteFur, 3; + Zeny=Zeny-200; + $XMAS_MONEY+=100; + delitem OpenedChristmasBox, 1; + getitem ClosedChristmasBox, 1; + mesn; + mesq l("Here you go, anything else?"); + next; + break; + default: + close; + } + + goto L_Main; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/019-4-1/chief.txt b/npc/019-4-1/chief.txt new file mode 100644 index 0000000..ca4bd1f --- /dev/null +++ b/npc/019-4-1/chief.txt @@ -0,0 +1,351 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Christmas Quest +// Closed Christmas Boxes have been stolen! Give him any you find for the +// ranking (and ranking based rewards). The global amount of delivered boxes +// will determine the gifts (and amount of gifts) handled by Santa! +// +// Rewards: Santa Hat, Gnome Hat, Santa Bearded Hat, Ugly Christmas Sweater, +// Turtle Neck Sweater, Santa Globe, Snowman Globe, Red Stocking. +// +// SQuest_Christmas +// Current Year +// Boxes Delivered +// Money Sponsored +// +// $XMAS_GIFTS +// World-wide collected gifts count. + +019-4-1,56,33,0 script Christmas Chief NPC_GNOME_A,{ + function ScoreXMAS { + + mes "##B"+l("Top 5 - Christmas Box Donation Event")+"##b"; + mes("1."+$@xmas_box_name$[0]+" ("+fnum($@xmas_box_value[0])+")"); + mes("2."+$@xmas_box_name$[1]+" ("+fnum($@xmas_box_value[1])+")"); + mes("3."+$@xmas_box_name$[2]+" ("+fnum($@xmas_box_value[2])+")"); + mes("4."+$@xmas_box_name$[3]+" ("+fnum($@xmas_box_value[3])+")"); + mes("5."+$@xmas_box_name$[4]+" ("+fnum($@xmas_box_value[4])+")"); + next; + + mes "##B"+l("Top 5 - Golbarez Seasonal Quest")+"##b"; + mes("1."+$@xmas_gp_name$[0]+" ("+fnum($@xmas_gp_value[0])+" GP)"); + mes("2."+$@xmas_gp_name$[1]+" ("+fnum($@xmas_gp_value[1])+" GP)"); + mes("3."+$@xmas_gp_name$[2]+" ("+fnum($@xmas_gp_value[2])+" GP)"); + mes("4."+$@xmas_gp_name$[3]+" ("+fnum($@xmas_gp_value[3])+" GP)"); + mes("5."+$@xmas_gp_name$[4]+" ("+fnum($@xmas_gp_value[4])+" GP)"); + next; + } + + // Christmas still running + if ($EVENT$ == "Christmas") + goto L_Main; + + // Last year + if (getq(SQuest_Christmas) == gettime(GETTIME_YEAR)-1) + goto L_Reward; + + // Not in season + goto L_OutOfSeason; + +L_OutOfSeason: + mesn; + mesq l("This workshop doesn't gets too many tasks from Santa outside the Christmas..."); + if (is_staff()) + ScoreXMAS(); + close; + +L_Reward: + inventoryplace NPCEyes, 6, Iten, 1; + .@q1=getq2(SQuest_Christmas); + .@q2=getq3(SQuest_Christmas); + setq1 SQuest_Christmas, 0; + + /* Handle Christmas Quest Rewards */ + /* The top 5 gets special rewards! */ + // #1 : White Cat Pet + // #2 - #3: Red Stocking + // #4 - #5: Extra xmas gift + /* You get Christmas Gift Box, which contain rares */ + + .@gifts=min(15, log2($XMAS_GIFTS/100))+1; + if (.@q1 >= .@gifts) + getitem XmasGift, .@gifts; + + // Top 5 rewards + if (strcharinfo(0) == $@xmas_box_name$[0]) + makepet CattyCat; + else if (strcharinfo(0) == $@xmas_box_name$[1] || strcharinfo(0) == $@xmas_box_name$[2]) + getitem RedStocking, 1; + else if (strcharinfo(0) == $@xmas_box_name$[3] || strcharinfo(0) == $@xmas_box_name$[4]) + getitem XmasGift, 1; + + if (strcharinfo(0) == .@name$[0]) + mesc l("You gained a @@ for the #1 place on the event. Remember to feed it @@, or it may run away from you.", getitemlink(CattyCat), getitemlink(Milk)); + + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// + /* Handle Golbarez Quest Rewards */ + /* Mostly Coins, some Merc Boxes, EXP/JExp */ + /* Gift the best people at this quest, too */ + getexp .@q2/4, (.@q2/1000)+.@q1; + .@coins=limit(0, .@q2/10000, 300); + .@overf=limit(0, (.@q2-3000000)/5000, 1400); + REAL_ESTATE_CREDITS+=.@q2*15/10; + if (.@coins) + getitem CasinoCoins, .@coins; + if (.@coins > 3) + getitem StrangeCoin, .@coins/3; + if (.@overf) + getitem HeroCoin, .@overf; + // Mercenary boxes (requires certain global donation values) + .@mc_boxc=.@q2/100000; + .@mc_boxb=(.@q2/60000)-.@mc_boxc; + .@mc_boxa=(.@q2/40000)-.@mc_boxb; + // Blueprints + .@bp_total=.@q2/15000; // Price of an Ancient Blueprint + .@bp_rank=0; + + // Event: Supreme (5 Mi) + if ($XMAS_MONEY >= 5000000) { + if (.@mc_boxc) + getitem MercBoxC, .@mc_boxc; + if (.@mc_boxb) + getitem MercBoxB, .@mc_boxb+1; + if (.@mc_boxa) + getitem MercBoxA, .@mc_boxa+2; + .@bp_rank=5; + // Event: Great (1.5 Mi) + } if ($XMAS_MONEY >= 1500000) { + if (.@mc_boxc) + getitem MercBoxC, .@mc_boxc; + if (.@mc_boxb) + getitem MercBoxB, .@mc_boxb; + if (.@mc_boxa) + getitem MercBoxA, .@mc_boxa+2; + .@bp_rank=4; + // Event: Good (750k) + } else if ($XMAS_MONEY >= 750000) { + if (.@mc_boxc) + getitem MercBoxC, .@mc_boxc; + if (.@mc_boxb) + getitem MercBoxB, .@mc_boxb; + if (.@mc_boxa) + getitem MercBoxA, .@mc_boxa+1; + .@bp_rank=3; + // Event: Average (250k) + } else if ($XMAS_MONEY >= 250000) { + if (.@mc_boxb) + getitem MercBoxB, .@mc_boxb; + if (.@mc_boxa) + getitem MercBoxA, .@mc_boxa; + .@bp_rank=2; + // Event: Bad (must aid players - 100k) + } else if ($XMAS_MONEY >= 100000) { + if (.@q2 >= 10000) + getitem MercBoxA, .@mc_boxa+1; + .@bp_rank=1; + } + // Event: Terrible (< 100k: No merc rewards) + + // Blueprints control + // Event: Horrible + if (.@bp_rank <= 0 && .@bp_total) { + .@bp=.@bp_total/2; + getitem AlchemyBlueprintA, limit(1, .@bp, 3); + if (.@bp) + getitem EquipmentBlueprintA, limit(1, .@bp, 3); + } + // Event: Bad + else if (.@bp_rank <= 1 && .@bp_total) { + .@bp=.@bp_total/2; + getitem AlchemyBlueprintA, limit(1, .@bp, 10); + if (.@bp) + getitem EquipmentBlueprintA, limit(1, .@bp, 10); + if (.@bp > 3) + getitem AncientBlueprint, 1; + } + // Event: Average + else if (.@bp_rank <= 2 && .@bp_total) { + .@bp=.@bp_total/4; + getitem AlchemyBlueprintA, limit(1, .@bp, 5); + getitem EquipmentBlueprintA, limit(1, .@bp, 5); + if (.@bp) { + getitem AlchemyBlueprintB, limit(1, .@bp, 3); + getitem EquipmentBlueprintB, limit(1, .@bp, 3); + getitem AncientBlueprint, 1; + } + } + // Event: Good + else if (.@bp_rank <= 3 && .@bp_total) { + .@bp=.@bp_total/6; + getitem AlchemyBlueprintA, limit(1, .@bp, 10); + getitem EquipmentBlueprintA, limit(1, .@bp, 10); + getitem AlchemyBlueprintB, limit(1, .@bp, 5); + getitem EquipmentBlueprintB, limit(1, .@bp, 5); + if (.@bp) { + getitem AlchemyBlueprintC, limit(1, .@bp, 3); + getitem EquipmentBlueprintC, limit(1, .@bp, 3); + getitem AncientBlueprint, 2; + } + } + // Event: Great + else if (.@bp_rank <= 4 && .@bp_total) { + .@bp=.@bp_total/7; + getitem AlchemyBlueprintB, limit(1, .@bp, 10); + getitem EquipmentBlueprintB, limit(1, .@bp, 10); + getitem AlchemyBlueprintC, limit(1, .@bp, 5); + getitem EquipmentBlueprintC, limit(1, .@bp, 5); + if (.@bp) { + getitem AlchemyBlueprintD, limit(1, .@bp, 3); + getitem EquipmentBlueprintD, limit(1, .@bp, 3); + getitem AncientBlueprint, 3; + } + } + // Event: Supreme + else if (.@bp_rank >= 5 && .@bp_total) { + .@bp=.@bp_total/8; + getitem AlchemyBlueprintC, limit(1, .@bp, 7); + getitem EquipmentBlueprintC, limit(1, .@bp, 7); + getitem AlchemyBlueprintD, limit(1, .@bp, 5); + getitem EquipmentBlueprintD, limit(1, .@bp, 5); + if (.@bp) { + getitem AlchemyBlueprintE, limit(1, .@bp, 2); + getitem EquipmentBlueprintE, limit(1, .@bp, 2); + getitem AncientBlueprint, 3; + } + } + + // MVP Awards + if ((.@q2*100)/$XMAS_MONEY >= 80) { // 80% contribution + getitem PrismGift, limit(1, .@q2/500000, 2); + getitem GoldenGift, limit(1, .@q2/100000, 3); + if (.@bp_rank >= 2) + getitem AncientBlueprint, 5; + else if (.@bp_rank >= 1) + getitem AncientBlueprint, 3; + } else if ((.@q2*100)/$XMAS_MONEY >= 60) { // 60% contribution + getitem GoldenGift, limit(1, .@q2/500000, 2); + getitem SilverGift, limit(1, .@q2/100000, 3); + if (.@bp_rank >= 2) + getitem AncientBlueprint, 3; + else if (.@bp_rank >= 1) + getitem AncientBlueprint, 2; + } else if ((.@q2*100)/$XMAS_MONEY >= 40) { // 40% contribution + getitem SilverGift, limit(1, .@q2/500000, 2); + getitem BronzeGift, limit(1, .@q2/100000, 3); + if (.@bp_rank >= 2) + getitem AncientBlueprint, 2; + else if (.@bp_rank >= 1) + getitem AncientBlueprint, 1; + } else if ((.@q2*100)/$XMAS_MONEY >= 20) { // 20% contribution + getitem BronzeGift, limit(1, .@q2/500000, 2); + getitem StrangeCoin, limit(1, .@q2/5000, 20); + if (.@bp_rank >= 2) + getitem AncientBlueprint, 1; + } else if ((.@q2*100)/$XMAS_MONEY >= 5) { // 5% contribution + getitem StrangeCoin, limit(1, .@q2/100000, 10); + getitem CasinoCoins, limit(1, .@q2/5000, 20); + } + + /* The top 5 gets special rewards! */ + // #1 : Xmas Gift x2 + // #2 - #3: Xmas Gift x1 + // #4 - #5: Bronze Gift + if (strcharinfo(0) == $@xmas_gp_name$[0]) + getitem XmasGift, max(.@bp_rank, 1)+1; + else if (strcharinfo(0) == $@xmas_gp_name$[1] || strcharinfo(0) == $@xmas_gp_name$[2]) + getitem XmasGift, max(.@bp_rank, 1); + else if (strcharinfo(0) == $@xmas_gp_name$[3] || strcharinfo(0) == $@xmas_gp_name$[4]) + getitem BronzeGift, (.@bp_rank > 3 ? 2 : 1); + + mesn; + mesq l("Thanks for helping us the last year. I hope to have your help by the next year."); + next; + ScoreXMAS(); + close; + +L_Main: + // Start Event for the first time if needed + if (gettime(GETTIME_MONTH) != JANUARY && getq(SQuest_Christmas) != gettime(GETTIME_YEAR)) { + setq SQuest_Christmas, gettime(GETTIME_YEAR), 0, 0; + } + if (gettime(GETTIME_MONTH) == JANUARY && getq(SQuest_Christmas) != gettime(GETTIME_YEAR)-1) { + setq SQuest_Christmas, gettime(GETTIME_YEAR)-1, 0, 0; + } + + // Begin Christmas + .@q=getq2(SQuest_Christmas); + // + if (!getq(General_Narrator)) { + mesn; + mesq l("The stolen christmas boxes!! Christmas is RUINED!!!"); + mesc l("You should complete Candor Prologue before participating on this quest."); + close; + } + mesn; + mesq l("We only managed to recover @@ stolen @@ thus far...", $XMAS_GIFTS, getitemlink(ClosedChristmasBox)); + // Same formula from 2007 event. (max: 3.276.800 boxes delivered, an ABSURD amount) + // You need to give at least this many boxes yourself to be eligible for rewards, though. + .@gifts=min(15, log2($XMAS_GIFTS/100))+1; + mesq l("If things keep like this, we will only manage to give @@ gifts to every player...", .@gifts); + if (.@q < .@gifts || !.@q) + mesc l("WARNING: You must give at least @@ boxes more before event ends to be eligible for a reward!", .@gifts-.@q), 1; + next; + if (.@q) { + mesn; + switch (.@gifts) { + case 0: mesq l("There isn't enough, not even for NPCs. We need at least 100."); break; + case 1: mesq l("Santa can give one present for all players. Maybe if we got 200..."); break; + case 2: mesq l("Hey, two gifts is very nice. But if we delivered 400. Think on it."); break; + case 3: mesq l("Wow, three gifts! Good job! With 800, an extra gift for everyone!"); break; + case 4: mesq l("Hahah, four gifts, neat! Can we get to 1600 boxes?"); break; + case 5: mesq l("Amazing. Five gifts for all. With 3200, we could supply nomad tribes..."); break; + case 6: mesq l("Atonishing. Six gifts is the real deal. Easy to guess: 6400 is the next milestone."); break; + case 7: mesq l("Impressive. Seven gifts! Now, 12800 is not easy. I understand if everyone gives up."); break; + case 8: mesq l("Eight gifts. Let's double the goal. 25600 gifts. Did you guys hire some chinese gold farmers, anyway?"); break; + case 9: mesq l("How did you got that much? Nine gifts, and 51200 if you want even more."); break; + case 10: mesq l("Want to break the hundredthousand item limit?? Ten gifts, for more, bring 102,400 boxes here."); break; + case 11: mesq l("Eleven gifts? You guys really have no live! For 204,800 I'll give an extra one."); break; + case 12: mesq l("Twelve... There is no way people collected this many gifts. Anyway. 404,800 is the next milestone."); break; + case 13: mesq l("What the f...? Four Hundred Thousand??? Must be a bug. Next milestone is 819,200. Cheaters."); break; + case 14: mesq l("I must report this to Jesusalva. The inventory system can't handle that much. Want more? Try 1,638,400."); break; + default: mesq l("ENOUGH! LAME CHEATERS, GET OFF THIS GAME %%a That's 15 gifts, and it is final."); break; + } + next; + } + goto L_Loop; + +L_Loop: + mesn; + mesq l("Well, if you want to donate @@, you'll be ranked. There's gift for everyone, and rewards for top 5 donors.", getitemlink(ClosedChristmasBox)); + mesc l("Thus far, you've donated @@ boxes.", .@q); + mes ""; + select + rif(countitem(ClosedChristmasBox), l("Return @@ boxes", countitem(ClosedChristmasBox))), + l("Scoreboards"), + l("Leave"); + mes ""; + switch (@menu) { + case 1: + .@am=countitem(ClosedChristmasBox); + delitem ClosedChristmasBox, .@am; + .@q=getq2(SQuest_Christmas); + $XMAS_GIFTS+=.@am; + setq2 SQuest_Christmas, .@q+.@am; + break; + case 2: ScoreXMAS(); goto L_Loop; break; + } + + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/019-4-1/cook.txt b/npc/019-4-1/cook.txt new file mode 100644 index 0000000..856f22b --- /dev/null +++ b/npc/019-4-1/cook.txt @@ -0,0 +1,74 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Christmas Quest. +// Originally, it was planned to collect XMAS CAKE, XMAS CANDY CANE and GINGERBREAD BISCUIT +// and then make a player ranking for sport (like Ched). The ranking function, +// however, was moved to Chief. Maybe Cook will exchange these for open xmas boxes. + +019-4-1,30,29,0 script Christmas Cook NPC_GNOME_B,{ + if ($EVENT$ != "Christmas") + goto L_OutOfSeason; + goto L_Main; + +L_OutOfSeason: + mesn; + mesq l("Hmm... I can handle cooking during normal days. Christmas is the problem. I never have enough sweeties by then..."); + close; + +L_Main: + mesn; + mesq l("Ah... Santa's helpers sure eat a lot. I'm sure gift delivery is hard for them."); + next; + mesn; + mesq l("Well, you know... Maybe we can strike a deal. I have good relations with the stock manager. I'll give you an event item."); + next; + +L_Loop: + mesc l("@@/10 @@ for 1 @@", countitem(GingerBreadMan), getitemlink(GingerBreadMan),getitemlink(OpenedChristmasBox)); + mesc l("@@/9 @@ for 1 @@", countitem(XmasCandyCane), getitemlink(XmasCandyCane), getitemlink(OpenedChristmasBox)); + mesc l("@@/8 @@ for 1 @@", countitem(XmasCake), getitemlink(XmasCake), getitemlink(OpenedChristmasBox)); + mes ""; + select + l("Uhm, I don't need that."), + rif(countitem(GingerBreadMan)>=10, l("Trade the Gingerbread")), + rif(countitem(XmasCandyCane)>=9, l("Trade the Xmas Candy Cane")), + rif(countitem(XmasCake)>=8, l("Trade the Xmas Cake")), + l("Maybe later."); + mes ""; + switch (@menu) { + case 2: + delitem GingerBreadMan, 10; + getexp getiteminfo(GingerBreadMan, ITEMINFO_SELLPRICE)*10, 10; + getitem OpenedChristmasBox, 1; + mesn; + mesq l("Deal. Here you go."); + next; + goto L_Loop; break; + case 3: + delitem XmasCandyCane, 9; + getexp getiteminfo(XmasCandyCane, ITEMINFO_SELLPRICE)*10, 9; + getitem OpenedChristmasBox, 1; + mesn; + mesq l("Deal. Here you go."); + next; + goto L_Loop; break; + case 4: + delitem XmasCake, 8; + getexp getiteminfo(XmasCake, ITEMINFO_SELLPRICE)*10, 8; + getitem OpenedChristmasBox, 1; + mesn; + mesq l("Deal. Here you go."); + next; + goto L_Loop; break; + } + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/019-4-1/golbarez.txt b/npc/019-4-1/golbarez.txt new file mode 100644 index 0000000..1175064 --- /dev/null +++ b/npc/019-4-1/golbarez.txt @@ -0,0 +1,62 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Christmas quest. ported from 2010 and adapted for TMW2:ML. +// Original Name: Golbenez +// Year, Gifts, GP + +019-4-1,30,45,0 script Golbarez NPC_LOF_RICH,{ + if ($EVENT$ != "Christmas") + goto L_OutOfSeason; + goto L_Main; + +L_OutOfSeason: + mesn; + if (any(true, false)) + mesq l("AAH! You scared me!"); + else + mesq l("Land Of Fire is a place so warm... Although this workshop is a better place to break time and space during Christmas."); + close; + +L_Main: + if (getq(SQuest_Christmas) != gettime(GETTIME_YEAR) && + !(gettime(GETTIME_MONTH) == JANUARY && getq(SQuest_Christmas) == gettime(GETTIME_YEAR)-1)) { + mesn; + mesq l("Please talk with this workshop's Chief before talking to me."); + close; + } + .@q=getq3(SQuest_Christmas); + .@ratio=(.@q*100)/$XMAS_MONEY; + mesn; + mesq l("Mortal! I am @@! I have broken through the barriers of space and time!", .name$); + mesq l("I want to keep breaking them, until I find the paradise! Thus far, I only found the Land Of Fire!"); + next; + mesn; + mesq l("I need millions of GP to fund my time-space shattering, and thus far, I've only collected @@ GP.", format_number($XMAS_MONEY)); + mesq l("If you sponsor me, while I break into more dimensions, I'll give you any stuff I find. Hey, they could be rare here!"); + next; + mesc l("Sponsor @@ in how much GP?", .name$); + input .@count; + mes ""; + if (Zeny < .@count) { + mesn; + mesq l("Don't try to trick me, this attracts bad karma! You could get stolen on Christmas! Seriously, this has happened before!"); + close; + } + Zeny=Zeny-.@count; + $XMAS_MONEY+=.@count; + setq3 SQuest_Christmas, .@q+.@count; + mesn; + mesq l("Thanks for your patronage! The rewards will be available with @@, in case I do find the paradise!", "Christmas Chief"); + mesc l("Thus far, you've sponsored Golbarez in %d %% from total.", .@ratio); + mesc l("Note: No rares will be given if Golbarez doesn't get enough GP to find the paradise, but Strange Coins will still be given."); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/019-4/_import.txt b/npc/019-4/_import.txt new file mode 100644 index 0000000..381ecd0 --- /dev/null +++ b/npc/019-4/_import.txt @@ -0,0 +1,5 @@ +// Map 019-4: Romantic Place +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-4/_mobs.txt", +"npc/019-4/_warps.txt", +"npc/019-4/wateranimation.txt", diff --git a/npc/019-4/_mobs.txt b/npc/019-4/_mobs.txt new file mode 100644 index 0000000..2be7903 --- /dev/null +++ b/npc/019-4/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-4: Romantic Place mobs +019-4,93,59,22,21 monster Water Fairy 1184,6,100000,30000 +019-4,84,103,36,19 monster Alpha Mouboo 1056,2,100000,30000 +019-4,101,58,44,52 monster Fluffy 1022,18,100000,30000 +019-4,116,67,36,30 monster Azul Slime 1095,6,100000,30000 +019-4,143,35,6,7 monster Pollet 1219,2,200000,30000 diff --git a/npc/019-4/_warps.txt b/npc/019-4/_warps.txt new file mode 100644 index 0000000..fa1a571 --- /dev/null +++ b/npc/019-4/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-4: Romantic Place warps +019-4,95,27,0 warp #019-4_95_27 3,0,019-1,61,101 +019-4,133,53,0 warp #019-4_133_53 0,0,019-4-1,50,45 +019-4,54,110,0 warp #019-4_54_110 1,0,014-5,192,17 diff --git a/npc/019-4/wateranimation.txt b/npc/019-4/wateranimation.txt new file mode 100644 index 0000000..438066b --- /dev/null +++ b/npc/019-4/wateranimation.txt @@ -0,0 +1,41 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Water animations, splash, fishes, etc... + +019-4,144,53,0 script #fishing_winterlands0 NPC_WATER_SPLASH,{ + + fishing(1, CommonCarp, + FrozenYetiTear, IceCube, GrassCarp); // begin or continue fishing + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +019-4,146,32,0 duplicate(#fishing_winterlands0) #fishing_winterlands1 NPC_WATER_SPLASH +019-4,115,71,0 duplicate(#fishing_winterlands0) #fishing_winterlands2 NPC_WATER_SPLASH + +019-1,114,104,0 duplicate(#fishing_winterlands0) #fishing_winterlands3 NPC_WATER_SPLASH +019-1,121,78,0 duplicate(#fishing_winterlands0) #fishing_winterlands4 NPC_WATER_SPLASH + +019-2,103,119,0 script #fishing_winterlandsb NPC_WATER_SPLASH,{ + + fishing; // begin or continue fishing + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +019-2,103,103,0 duplicate(#fishing_winterlandsb) #fishing_winterlands5 NPC_WATER_SPLASH +019-2,109,74,0 duplicate(#fishing_winterlandsb) #fishing_winterlands6 NPC_WATER_SPLASH +019-2,109,44,0 duplicate(#fishing_winterlandsb) #fishing_winterlands7 NPC_WATER_SPLASH + + diff --git a/npc/019-5-1/_import.txt b/npc/019-5-1/_import.txt new file mode 100644 index 0000000..e46d7b8 --- /dev/null +++ b/npc/019-5-1/_import.txt @@ -0,0 +1,4 @@ +// Map 019-5-1: Frosty Underground +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-5-1/_mobs.txt", +"npc/019-5-1/_warps.txt", diff --git a/npc/019-5-1/_mobs.txt b/npc/019-5-1/_mobs.txt new file mode 100644 index 0000000..79bd47d --- /dev/null +++ b/npc/019-5-1/_mobs.txt @@ -0,0 +1,16 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-5-1: Frosty Underground mobs +019-5-1,68,101,6,4 monster Yeti 1064,2,100000,30000 +019-5-1,55,61,12,13 monster Moggun 1070,14,100000,30000 +019-5-1,119,104,2,2 monster Snowman 1440,1,100000,30000 +019-5-1,132,71,4,3 monster White Slime 1094,4,100000,30000 +019-5-1,107,85,7,4 monster Blue Slime 1087,2,100000,30000 +019-5-1,98,77,3,2 monster White Slime 1094,4,100000,30000 +019-5-1,70,82,3,2 monster White Slime 1094,6,100000,30000 +019-5-1,106,34,3,2 monster White Slime 1094,4,100000,30000 +019-5-1,33,37,3,2 monster White Slime 1094,4,100000,30000 +019-5-1,144,105,7,4 monster Blue Slime 1087,2,100000,30000 +019-5-1,152,55,7,4 monster Blue Slime 1087,2,100000,30000 +019-5-1,84,53,7,4 monster Blue Slime 1087,2,100000,30000 +019-5-1,47,23,7,5 monster Snowman 1440,2,100000,30000 +019-5-1,113,48,6,4 monster Yeti 1064,2,100000,30000 diff --git a/npc/019-5-1/_warps.txt b/npc/019-5-1/_warps.txt new file mode 100644 index 0000000..acbf5dc --- /dev/null +++ b/npc/019-5-1/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-5-1: Frosty Underground warps +019-5-1,139,19,0 warp #019-5-1_139_19 0,0,019-5,180,187 +019-5-1,78,30,0 warp #019-5-1_78_30 0,0,019-5,119,198 +019-5-1,83,43,0 warp #019-5-1_83_43 0,0,019-5,124,211 +019-5-1,131,48,0 warp #019-5-1_131_48 0,0,019-5,172,216 +019-5-1,131,55,0 warp #019-5-1_131_55 0,0,019-5,172,223 +019-5-1,124,109,0 warp #019-5-1_124_109 0,0,019-5,165,277 diff --git a/npc/019-5-2/_import.txt b/npc/019-5-2/_import.txt new file mode 100644 index 0000000..3a8419a --- /dev/null +++ b/npc/019-5-2/_import.txt @@ -0,0 +1,4 @@ +// Map 019-5-2: Snow Hills Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-5-2/_mobs.txt", +"npc/019-5-2/_warps.txt", diff --git a/npc/019-5-2/_mobs.txt b/npc/019-5-2/_mobs.txt new file mode 100644 index 0000000..a9c7e43 --- /dev/null +++ b/npc/019-5-2/_mobs.txt @@ -0,0 +1,23 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-5-2: Snow Hills Cave mobs +019-5-2,56,135,4,4 monster White Slime 1094,7,100000,30000 +019-5-2,35,153,3,3 monster White Slime 1094,7,100000,30000 +019-5-2,39,104,4,4 monster White Slime 1094,7,100000,30000 +019-5-2,73,153,4,4 monster White Slime 1094,7,100000,30000 +019-5-2,64,104,1,1 monster White Slime 1094,7,100000,30000 +019-5-2,24,168,3,2 monster Blue Slime 1087,2,100000,30000 +019-5-2,87,134,2,1 monster Blue Slime 1087,2,100000,30000 +019-5-2,55,122,2,1 monster Blue Slime 1087,2,100000,30000 +019-5-2,35,128,2,1 monster Blue Slime 1087,2,100000,30000 +019-5-2,53,84,2,1 monster Blue Slime 1087,2,100000,30000 +019-5-2,75,136,10,13 monster Moggun 1070,5,100000,30000 +019-5-2,39,31,21,10 monster Moggun 1070,12,100000,30000 +019-5-2,40,31,2,1 monster Azul Skull Slime 1402,2,100000,30000 +019-5-2,122,157,10,8 monster Yeti 1064,2,100000,30000 +019-5-2,102,166,7,6 monster Azul Skull Slime 1402,5,100000,30000 +019-5-2,109,81,7,6 monster Azul Slime Mother 1243,1,100000,30000 +019-5-2,123,90,2,1 monster Blue Slime 1087,2,100000,30000 +019-5-2,109,97,4,4 monster White Slime 1094,7,100000,30000 +019-5-2,119,41,21,10 monster Moggun 1070,8,100000,30000 +019-5-2,51,94,19,7 monster Azul Skull Slime 1402,5,100000,30000 +019-5-2,121,172,4,4 monster White Slime 1094,7,100000,30000 diff --git a/npc/019-5-2/_warps.txt b/npc/019-5-2/_warps.txt new file mode 100644 index 0000000..c54de64 --- /dev/null +++ b/npc/019-5-2/_warps.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-5-2: Snow Hills Cave warps +019-5-2,46,74,0 warp #019-5-2_46_74 0,0,019-5,45,75 +019-5-2,33,176,0 warp #019-5-2_33_176 0,0,019-5,37,174 +019-5-2,126,181,0 warp #019-5-2_126_181 0,0,019-5,101,144 +019-5-2,35,42,0 warp #019-5-2_35_42 0,0,019-5,78,110 +019-5-2,108,163,0 warp #019-5-2_108_163 0,0,019-5,83,125 +019-5-2,110,103,0 warp #019-5-2_110_103 0,0,019-5,108,166 +019-5-2,119,95,0 warp #019-5-2_119_95 0,0,019-5,108,157 +019-5-2,119,59,0 warp #019-5-2_119_59 0,0,019-5,102,112 +019-5-2,128,46,0 warp #019-5-2_128_46 0,0,019-5,102,108 diff --git a/npc/019-5-3/_import.txt b/npc/019-5-3/_import.txt new file mode 100644 index 0000000..07c705f --- /dev/null +++ b/npc/019-5-3/_import.txt @@ -0,0 +1,4 @@ +// Map 019-5-3: Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-5-3/_warps.txt", +"npc/019-5-3/episode.txt", diff --git a/npc/019-5-3/_warps.txt b/npc/019-5-3/_warps.txt new file mode 100644 index 0000000..9c34218 --- /dev/null +++ b/npc/019-5-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-5-3: Indoors warps +019-5-3,31,33,0 warp #019-5-3_31_33 1,0,019-5,128,40 diff --git a/npc/019-5-3/episode.txt b/npc/019-5-3/episode.txt new file mode 100644 index 0000000..ed9c71a --- /dev/null +++ b/npc/019-5-3/episode.txt @@ -0,0 +1,76 @@ +// TMW2 scripts. +// Authors: +// Jesusalva <jesusalva@tmw2.org> +// Description: +// The Episode + +019-5-3,21,25,0 script Mysterious Chest#EP1 NPC_CHEST,{ + if (instance_id() < 0) end; + .@gp = BankVault + Zeny; + .@money = 1+(BaseLevel > 60 ? (BaseLevel > 80 ? .@gp/5 : .@gp/10) : .@gp/50); + .@money = max(.@money, 1000); + mesc l("Do you want to open this chest?"); + mesc l("You must deposit %s GP, which will be returned if quest is completed.", fnum(.@money)); + mesc l("There is a time limit, which is already running."); + // At level 80, the "boss" is just small fry. + if (BaseLevel < 80) + mesc l("WARNING: Dangerous!!"), 1; + if (Zeny < .@money) close; + next; + if (askyesno() == ASK_NO) { + closeclientdialog; + close; + } + CHEST_MONEY=(CHEST_MONEY/2)+.@money; // You lose 50% of whatever you put earlier + Zeny-=.@money; + disablenpc instance_npcname("Mysterious Chest#EP1", getq2(LoFQuest_EPISODE)); + enablenpc instance_npcname("Mysterious Chest#EP2", getq2(LoFQuest_EPISODE)); + closeclientdialog; + // Good luck with your boss :3 + .@m$=getmap(); + monster(.@m$, 31, 31, "???", JackO, 1); + sleep2(max(300, 10000-(BaseLevel * 100))); + monster(.@m$, 30, 31, "???", MagicGoblin, 1); + monster(.@m$, 31, 31, "???", MagicGoblin, 1); + sleep2(max(15000, 60000-(BaseLevel * 200))); + monster(.@m$, 30, 31, "???", MagicGoblin, 1); + monster(.@m$, 31, 31, "???", MagicGoblin, 1); + end; + +OnInit: + .distance = 2; + end; +} + + +019-5-3,21,25,0 script Mysterious Chest#EP2 NPC_CHEST,{ + if (instance_id() < 0) end; + if (mobcount(getmap(), "all")) end; + mesc l("Do you want to open this chest?"); + if (askyesno() == ASK_NO) { + closeclientdialog; + close; + } + if (getq(LoFQuest_EPISODE) != 11) { + atcommand("@ban 3d "+strcharinfo(0)); + end; + } + .@q2 = getq2(LoFQuest_EPISODE); + instance_set_timeout(900, 900, .@q2); + warp "ep02@"+getcharid(0), 37, 20; // To Falkrun + disablenpc instance_npcname("#004-3-1_70_25", getq2(LoFQuest_EPISODE)); + disablenpc instance_npcname("#004-3-1_68_33", getq2(LoFQuest_EPISODE)); + disablenpc instance_npcname("Mysterious Chest#EP2", getq2(LoFQuest_EPISODE)); + close; + +OnInstanceInit: + .distance = 2; + disablenpc instance_npcname("Mysterious Chest#EP2"); + end; + +OnInit: + .distance = 2; + disablenpc .name$; + end; +} + diff --git a/npc/019-5/_import.txt b/npc/019-5/_import.txt new file mode 100644 index 0000000..79fadae --- /dev/null +++ b/npc/019-5/_import.txt @@ -0,0 +1,5 @@ +// Map 019-5: Rock Plateau +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-5/_mobs.txt", +"npc/019-5/_warps.txt", +"npc/019-5/episode.txt", diff --git a/npc/019-5/_mobs.txt b/npc/019-5/_mobs.txt new file mode 100644 index 0000000..54040c2 --- /dev/null +++ b/npc/019-5/_mobs.txt @@ -0,0 +1,27 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-5: Rock Plateau mobs +019-5,85,195,26,10 monster Moggun 1070,12,100000,30000 +019-5,128,261,11,6 monster Wolvern 1037,5,100000,30000 +019-5,113,219,7,6 monster Wolvern 1037,2,100000,30000 +019-5,77,272,5,4 monster Wolvern 1037,2,100000,30000 +019-5,156,200,22,16 monster Wolvern 1037,7,100000,30000 +019-5,186,227,5,4 monster Wolvern 1037,2,100000,30000 +019-5,37,175,1,1 monster White Slime 1094,7,100000,30000 +019-5,45,75,1,1 monster Blue Slime 1087,1,100000,30000 +019-5,81,125,51,28 monster Moggun 1070,25,100000,30000 +019-5,189,123,12,18 monster Wolvern 1037,6,100000,30000 +019-5,160,115,12,10 monster Wolvern 1037,6,100000,30000 +019-5,169,163,24,15 monster Wolvern 1037,6,100000,30000 +019-5,58,71,24,15 monster Wolvern 1037,6,100000,30000 +019-5,122,116,10,12 monster Wolvern 1037,6,100000,30000 +019-5,110,173,12,10 monster Wolvern 1037,6,100000,30000 +019-5,108,168,1,1 monster White Slime 1094,7,100000,30000 +019-5,108,158,1,1 monster Blue Slime 1087,1,100000,30000 +019-5,87,119,18,17 monster Yeti 1064,2,100000,30000 +019-5,138,22,1,1 monster White Slime 1094,4,100000,30000 +019-5,157,66,1,1 monster White Slime 1094,4,100000,30000 +019-5,174,46,2,2 monster Blue Slime 1087,2,100000,30000 +019-5,143,43,2,2 monster Blue Slime 1087,2,100000,30000 +019-5,122,61,1,1 monster White Slime 1094,4,100000,30000 +019-5,132,70,2,2 monster Blue Slime 1087,2,100000,30000 +019-5,106,157,97,149 monster Pollet 1219,24,200000,30000 diff --git a/npc/019-5/_warps.txt b/npc/019-5/_warps.txt new file mode 100644 index 0000000..53989cb --- /dev/null +++ b/npc/019-5/_warps.txt @@ -0,0 +1,18 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-5: Rock Plateau warps +019-5,136,285,0 warp #019-5_136_285 1,0,019-6,94,18 +019-5,119,197,0 warp #019-5_119_197 0,0,019-5-1,78,29 +019-5,172,215,0 warp #019-5_172_215 0,0,019-5-1,131,47 +019-5,172,222,0 warp #019-5_172_222 0,0,019-5-1,131,54 +019-5,180,186,0 warp #019-5_180_186 0,0,019-5-1,139,18 +019-5,124,210,0 warp #019-5_124_210 0,0,019-5-1,83,42 +019-5,165,276,0 warp #019-5_165_276 0,0,019-5-1,124,108 +019-5,108,165,0 warp #019-5_108_165 0,0,019-5-2,110,102 +019-5,108,156,0 warp #019-5_108_156 0,0,019-5-2,119,94 +019-5,102,111,0 warp #019-5_102_111 0,0,019-5-2,119,58 +019-5,102,107,0 warp #019-5_102_107 0,0,019-5-2,128,45 +019-5,101,143,0 warp #019-5_101_143 0,0,019-5-2,126,180 +019-5,37,173,0 warp #019-5_37_173 0,0,019-5-2,33,175 +019-5,45,73,0 warp #019-5_45_73 0,0,019-5-2,46,76 +019-5,78,109,0 warp #019-5_78_109 0,0,019-5-2,35,41 +019-5,83,123,0 warp #019-5_83_123 0,0,019-5-2,108,165 diff --git a/npc/019-5/episode.txt b/npc/019-5/episode.txt new file mode 100644 index 0000000..7264a53 --- /dev/null +++ b/npc/019-5/episode.txt @@ -0,0 +1,33 @@ +// TMW2 scripts. +// Authors: +// Jesusalva <jesusalva@tmw2.org> +// Description: +// The Episode + +019-5,128,39,0 script #EpisodeDoor NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (getq(LoFQuest_EPISODE) != 11) { + dispbottom l("This door is locked. A warning sign is on the door: \"do not enter!\"."); + end; + } + // The Episode + setq3 LoFQuest_EPISODE, 0; + .@q2=getq2(LoFQuest_EPISODE); + .@mapn$="ep01@"+getcharid(0); + .@mape$="ep02@"+getcharid(0); + if (instanceowner(.@q2) == getcharid(3)) { + instance_set_timeout(900, 900, .@q2); + } else { + .@q2 = instance_create("Episode "+getcharid(0), getcharid(3), IOT_CHAR); + instance_attachmap("019-5-3", .@q2, false, .@mapn$); + instance_attachmap("004-3-1", .@q2, false, .@mape$); + instance_set_timeout(900, 900, .@q2); + instance_init(.@q2); + setq2 LoFQuest_EPISODE, .@q2; + } + // Warp to instance + warp .@mapn$, 30, 32; + end; +} + diff --git a/npc/019-6/_import.txt b/npc/019-6/_import.txt new file mode 100644 index 0000000..c1ac348 --- /dev/null +++ b/npc/019-6/_import.txt @@ -0,0 +1,4 @@ +// Map 019-6: snow Path +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/019-6/_mobs.txt", +"npc/019-6/_warps.txt", diff --git a/npc/019-6/_mobs.txt b/npc/019-6/_mobs.txt new file mode 100644 index 0000000..765d2cf --- /dev/null +++ b/npc/019-6/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-6: snow Path mobs +019-6,144,58,109,42 monster Pollet 1219,12,200000,30000 +019-6,94,63,48,52 monster Santa Slime 1096,6,30000,30000 +019-6,141,50,100,45 monster Fluffy 1022,50,100000,30000 +019-6,125,71,75,37 monster Wolvern 1037,12,60000,30000 diff --git a/npc/019-6/_warps.txt b/npc/019-6/_warps.txt new file mode 100644 index 0000000..be355b3 --- /dev/null +++ b/npc/019-6/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 019-6: snow Path warps +019-6,257,70,0 warp #019-6_257_70 0,1,019-1,22,69 +019-6,95,17,0 warp #019-6_95_17 1,0,019-5,135,284 diff --git a/npc/020-1/_import.txt b/npc/020-1/_import.txt new file mode 100644 index 0000000..2eb1208 --- /dev/null +++ b/npc/020-1/_import.txt @@ -0,0 +1,13 @@ +// Map 020-1: Nivalis +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-1/_mobs.txt", +"npc/020-1/_warps.txt", +"npc/020-1/guards.txt", +"npc/020-1/mapflags.txt", +"npc/020-1/misc.txt", +"npc/020-1/serge.txt", +"npc/020-1/siege.txt", +"npc/020-1/town.txt", +"npc/020-1/trainer.txt", +"npc/020-1/wateranimation.txt", +"npc/020-1/well.txt", diff --git a/npc/020-1/_mobs.txt b/npc/020-1/_mobs.txt new file mode 100644 index 0000000..9bafbea --- /dev/null +++ b/npc/020-1/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-1: Nivalis mobs +020-1,62,75,50,51 monster Fluffy 1022,20,60000,30000 +020-1,66,76,48,49 monster Squirrel 1054,5,100000,30000 +020-1,68,79,48,49 monster Pollet 1219,16,90000,30000 diff --git a/npc/020-1/_warps.txt b/npc/020-1/_warps.txt new file mode 100644 index 0000000..d8018a1 --- /dev/null +++ b/npc/020-1/_warps.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-1: Nivalis warps +020-1,47,48,0 warp #020-1_47_48 0,0,020-2,30,36 +020-1,66,52,0 warp #020-1_66_52 0,0,020-7,40,49 +020-1,64,82,0 warp #020-1_64_82 0,0,020-4,64,53 +020-1,86,46,0 warp #020-1_86_46 0,0,020-5,33,30 +020-1,57,91,0 warp #020-1_57_91 0,0,020-3,20,28 +020-1,70,128,0 warp #020-1_70_128 3,0,019-1,70,22 +020-1,107,55,0 warp #020-1_107_55 0,0,019-2,38,55 +020-1,33,94,0 warp #020-1_33_94 0,0,020-6,30,32 diff --git a/npc/020-1/guards.txt b/npc/020-1/guards.txt new file mode 100644 index 0000000..4d33bee --- /dev/null +++ b/npc/020-1/guards.txt @@ -0,0 +1,104 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Protect Nivalis + +020-1,49,48,0 script Lieutenant Joshua NPC_PLAYER,{ + // The Monster King guild have a special menu + if (strcharinfo(2) == "Monster King") goto L_MKControl; + + if ($NIVALIS_LIBDATE) { + mesn; + mesq l("Nivalis was liberated @@ ago.", FuzzyTime($NIVALIS_LIBDATE)); + close; + } else { + legiontalk; end; + } + +L_MKControl: + mesn; + mes "Oh noes! You've found the Nivalis control panel!"; + next; + select + l("Abort"), + l("Initiate a siege"); + mes ""; + if (@menu == 2) { + doevent "Lieutenant Joshua::OnStartSiege"; + closedialog; + } + close; + + +OnMKSiege: + $@SIEGE_ABORTED = false; +OnStartSiege: + kamibroadcast(col("WARNING! WARNING! Monster Army is moving towards Nivalis!!",1)); + do_siege("020-1", "019-2", "NIVAL", TP_NIVAL, .name$, .siegetime); + initnpctimer; + end; + +// Timers +OnTimer5000: + .siegetime+=5; + do_siege("020-1", "019-2", "NIVAL", TP_NIVAL, .name$, .siegetime); + switch (.siegetime) { + // Monster Army arrives in town + case 60: + disablenpc "Serge"; + disablenpc "Knox The Traveler"; + disablenpc "Camilot"; + disablenpc "Mede"; + disablenpc "Gambler#020-4"; + disablenpc "Gambling Xan"; + disablenpc "Baktar"; + disablenpc "Bracco"; + disablenpc "Agostine"; + break; + // Monster Army deployed in town + case 90: + disablenpc "Ben#NivBanker"; + break; + // Monster army have withdrawn completly + case MK_SIEGE_DURATION: + .siegetime=0; + announce(("Nivalis siege is over!"), bc_all); + enablenpc "Serge"; + enablenpc "Knox The Traveler"; + enablenpc "Camilot"; + enablenpc "Mede"; + enablenpc "Gambler#020-4"; + enablenpc "Gambling Xan"; + enablenpc "Baktar"; + enablenpc "Bracco"; + enablenpc "Agostine"; + enablenpc "Ben#NivBanker"; + stopnpctimer; + end; + break; + } + + // Loop again + initnpctimer; + end; + +OnInit: + .siegetime=0; + .sex = G_MALE; + .distance = 4; + + // Check items.xml for info about this. + .@npcId = getnpcid(); + setunitdata(.@npcId, UDT_HEADTOP, BullHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, LieutenantArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_SHIELD, LousyMoccasins); // TODO FIXME: Display Boots + setunitdata(.@npcId, UDT_WEAPON, Backsword); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 15); + end; + + +} + diff --git a/npc/020-1/mapflags.txt b/npc/020-1/mapflags.txt new file mode 100644 index 0000000..838cbfa --- /dev/null +++ b/npc/020-1/mapflags.txt @@ -0,0 +1,8 @@ +020-1 mapflag town +020-2 mapflag town +020-3 mapflag town +020-4 mapflag town +020-5 mapflag town +020-6 mapflag town +020-7 mapflag town +020-7-1 mapflag town diff --git a/npc/020-1/misc.txt b/npc/020-1/misc.txt new file mode 100644 index 0000000..f3b24ee --- /dev/null +++ b/npc/020-1/misc.txt @@ -0,0 +1,197 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Essential scripts any city must have + +// Description: +// The Travelers travel around the world telling stories. +020-1,44,67,0 script Knox The Traveler NPC_F_COINKEEPER,{ + + mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Wow! Are you @@? Everyone, in every city, talks about you!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + + mesq l("Hello. I am @@, and I am from a family of travellers. We travel though the whole world, looking for exotic goods.", .name$); + next; + mesq l("You can buy rare items with me, or I can tell you about different cities in our world."); + +L_Menu: + mes ""; + menu + l("I want to trade with you."), L_Trade, + l("Tell me about Tulimshar."), L_Tulim, + l("Tell me about Nivalis."), L_Nival, + l("Tell me about Frostia."), L_Frost, + l("Tell me about Thermin."), L_Therm, + l("Sorry, I'll pass."), L_Close; + +L_Tulim: + mes ""; + mesn; + mesq l("Tulimshar is the oldest human city, and its foundation is the year zero of our calendar."); + next; + mesq l("The city only flourished because Janett Platinum had the idea to build city walls surrounding this city."); + next; + mesq l("The desert climate means you'll find mostly maggots and scorpions. Their drops include cactus drinks, cake, knifes, black pearls, gold, and other common things."); + next; + mesq l("You can find for a good price desert equipment and some kind of dyes. You find all sort of crafters, artisans and warriors here."); + next; + goto L_Menu; + +L_Nival: + mes ""; + mesn; + mesq l("Nivalis was the last human settlement built during the First Era."); + next; + mesq l("It's cold, harsh climate makes difficult to live there. It was founded by people thrown away from Tulimshar and Hunrscald for political reasons."); + next; + mesq l("The cold climate is ideal for slimes, penguins, and other icy creatures. You can find lots of... ice, of course!"); + next; + mesq l("Some items are only produced in Nivalis. After all, it is hard to work properly with ice in a desert!"); + next; + goto L_Menu; + +L_Frost: + mes ""; + mesn; + mesq l("Frostia is the only city known that was not founded by humans."); + next; + mesq l("They are strict with who is allowed inside, so you'll need either elf or ukar friends to pass."); + next; + mesq l("It is on a huge, icy mountain peak. Rumors about dragons and legendary items to be found."); + next; + mesq l("Some of finest elven craftmanship can be found there, like bows, for example."); + next; + goto L_Menu; + +L_Therm: + mes ""; + mesn; + mesq l("Thermin is also known as the lost city. It was once founded to mine ores, and export to everyone."); + next; + mesq l("I think it was the Orcs and Raijins town. It was laid to waste by Yetis."); + next; + mesq l("Most Orcs become nomads and Raijins moved to Hurnscald, but they say Thermin might have been rebuilt somewhere else."); + next; + mesq l("On the ruins you may find free loot... And on the new town, you may find the sturdiest stuff of all."); + next; + goto L_Menu; + +L_Trade: + mesn; + mesq l("Use your @@ as currency!", getitemlink(StrangeCoin)); + tutmes l("%s is obtained during events, daily logins, heroic deeds, gifts, etc. But cannot be bought with real money.", getitemlink(StrangeCoin)); + next; + openshop "Aeros Trader"; + closedialog; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, UglyChristmasSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 22); + setunitdata(.@npcId, UDT_HAIRCOLOR, 8); + npcsit; + + .sex = G_MALE; + .distance = 5; + end; +} + +// Description: +// Banker. +020-2,30,25,0 script Ben#NivBanker NPC_LLOYD,{ + Banker(.name$, "Nivalis", 10000); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +// Description: +// Barber. +020-1,88,76,0 script Camilot NPC_ELVEN_FEMALE_ARMOR_SHOP,{ + function setRace { + clear; + setnpcdialogtitle l("Debug - Modify Race"); + mes l("Race") + ": " + $@allraces$[Class]; + next; + mes l("Please select the desired race."); + select("Human:Ukar:Redy:Elf:Orc:Raijin:Tritan"); + jobchange max(0, @menu-1); + return; + } + + + mesn; + mesq l("Hi! Do you want a hair cut?"); + + do + { + select + l("What is my current hairstyle and hair color?"), + l("I'd like to get a different style."), + l("Can you do something with my color?"), + rif(is_staff(), l("I am a GM, and I want to change my Race!")), + l("I'm fine for now, thank you."); + + switch (@menu) + { + case 1: + BarberSayStyle 3; + break; + case 2: + BarberChangeStyle; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Enjoy your new style."); + l("Anything else?"); + break; + case 3: + BarberChangeColor; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("I hope you like this color."); + l("Anything else?"); + break; + case 4: + setRace; + break; + case 5: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Feel free to come visit me another time."); + + goodbye; + } + } while (1); + close; + + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + +// Description: +// Soul Menhir +020-1,57,63,0 script Soul Menhir#niv NPC_SOUL_SNOW,{ + @map$ = "020-1"; + setarray @Xs, 56, 57, 58, 56, 58, 56, 57, 58; + setarray @Ys, 62, 62, 62, 63, 63, 64, 64, 64; + @x = 0; + @y = 0; + callfunc "SoulMenhir"; + @map$ = ""; + cleararray @Xs[0], 0, getarraysize(@Xs); + cleararray @Ys[0], 0, getarraysize(@Ys); + @x = 0; + @y = 0; + close; +} diff --git a/npc/020-1/serge.txt b/npc/020-1/serge.txt new file mode 100644 index 0000000..5414591 --- /dev/null +++ b/npc/020-1/serge.txt @@ -0,0 +1,103 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Hunt Fluffies, and Winter Quest, based on Luffyx. Disabled during Christmas. +// +// SQuest_Winter +// SergeQuest; + +020-1,77,78,0 script Serge NPC_ELF,{ + if (season() != WINTER) + goto L_OutOfSeason; + if ($EVENT$ == "Christmas") + goto L_QuestDisabled; + + .@q=getq(SQuest_Winter); + mesn; + mesq l("Brrrr... I'm freezing! The winter at Nivalis is so harsh!!"); + if (.@q < 1) + goto L_WinterQuest; + +L_Main: + if (GHQUEST) + GHQ_Assign(Fluffy, "Nivalis"); + close; + +L_OutOfSeason: + mesn; + mesq l("Hmm, Nivalis is a wonderful place to live in! Although it is a tad too cold on Winter..."); + goto L_Main; + +L_QuestDisabled: + mesn; + mesq l("Merry Christmas!"); + mesc l("The @@ quest is disabled during Christmas event.", getitemlink(KnitHat)); + next; + goto L_Main; + +L_WinterQuest: + next; + mesn; + mesq l("Hey, do you know what is good on this harsh winter? A @@!", getitemlink(KnitHat)); + next; + mesn; + mes l("What about you bring me:"); + mes l("@@/120 @@", countitem(Snowflake), getitemlink(Snowflake)); + mes l("@@/80 @@", countitem(CaramelCandy), getitemlink(CaramelCandy)); + mes l("@@/40 @@", countitem(GingerBreadMan), getitemlink(GingerBreadMan)); + mes l("@@/10 @@", countitem(ChocolateBiscuit), getitemlink(ChocolateBiscuit)); // Found at Chocolate Slime: 4.00% + next; + select + l("Not now, thanks"), + l("To be honest, I have that with me!"); + + mes ""; + if (@menu == 1) + goto L_Main; + if (@menu == 2) { + if ( + countitem(Snowflake) < 120 || + countitem(CaramelCandy) < 80 || + countitem(GingerBreadMan) < 40 || + countitem(ChocolateBiscuit) < 10 + ) goto L_Lying; + + inventoryplace KnitHat, 1; + delitem Snowflake, 120; + delitem CaramelCandy, 80; + delitem GingerBreadMan, 40; + delitem ChocolateBiscuit, 10; + getitem KnitHat, 1; + getexp BaseLevel*267, JobLevel*80; // Level 30/20 ABSOLUTE CAP + setq1 SQuest_Winter, 1; + mesn; + mesq l("Yay yay! Many thanks! Here, take the reward as promised!"); + next; + mesn; + mesq l("We can do this again on next winter!"); + goto L_Main; + } + + close; + +L_Lying: + mesn; + mesq l("Please don't lie to me..."); + goto L_Main; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, CommunityShirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, JeansChaps); + setunitdata(.@npcId, UDT_HEADBOTTOM, DeepBlackBoots); + setunitdata(.@npcId, UDT_WEAPON, KnitHat); + setunitdata(.@npcId, UDT_HAIRSTYLE, 13); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .distance=4; + .sex=G_MALE; + npcsit; + end; + +} diff --git a/npc/020-1/siege.txt b/npc/020-1/siege.txt new file mode 100644 index 0000000..49b7c41 --- /dev/null +++ b/npc/020-1/siege.txt @@ -0,0 +1,60 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Nivalis Siege for Liberation Day +// Each player process its own share of monsters. There are 10 waves. +// +// Q_NivalisLibday +// Day, Score, Temporary Time; + +020-1,0,0,0 script #NLib_Siege NPC_HIDDEN,{ + +OnLoop: + @nlib_time+=5; // This is looped every 5 s + + // Victory conditions: All monsters dead & number of waves filled. (Or if you reach level 40) + if (BaseLevel >= 30 || (@nlib_wave >= 10 && mobcount("020-1", "#NLib_Siege::OnPetDeath") <= 0)) + goto L_CleanUp; + + // New wave condition: Waves pending and A- All Mobs Dead B- 4 minutes spent + if (@nlib_wave < 10 && (mobcount("020-1", "#NLib_Siege::OnPetDeath") <= 0 || @nlib_time >= 240)) + goto L_NextRound; + + // reset timer + addtimer(5000, "#NLib_Siege::OnLoop"); + end; + +L_NextRound: + @nlib_time=0; + @nlib_wave = @nlib_wave + 1; + // Prepare next round + dispbottom l("Wave @@/10", @nlib_wave); + .@amount=@nlib_wave+rand(1,2); + freeloop(true); + for (.@i = 0; .@i < .@amount; ++.@i) { + .@monsterId=any(CaveMaggot, WhiteSlime, MagicGoblin, Bandit, GreenSlime, + CaveSnake, LavaSlime, DesertBandit, AngryRedScorpion, + Scorpion, RedScorpion, BlackSlime, Piousse, CandiedSlime, + AzulSlime, BlueSlime, SlimeBlast, RedSlime, AngryScorpion); + areamonster "020-1", 20, 20, 100, 100, strmobinfo(1, .@monsterId), .@monsterId, 1, "#NLib_Siege::OnPetDeath"; + } + freeloop(false); + + // reset timer + addtimer(5000, "#NLib_Siege::OnLoop"); + end; + +// Warp you back, and give you a random small score. +L_CleanUp: + .@q2=getq2(Q_NivalisLibday); + setq2 Q_NivalisLibday, .@q2+rand(1,5); + warp "019-2", 43, 55; + end; + +OnPetDeath: + .@lf=mobcount("020-1", "#NLib_Siege::OnPetDeath"); + dispbottom l("Mobs remaining: @@", .@lf); + end; + +} diff --git a/npc/020-1/town.txt b/npc/020-1/town.txt new file mode 100644 index 0000000..4d957ce --- /dev/null +++ b/npc/020-1/town.txt @@ -0,0 +1,12 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Reset LOCATION$ when entering a town + +020-1,71,102,0 script #LocNival NPC_HIDDEN,2,0,{ +OnTouch: + EnterTown("Nival"); + end; +} +020-1,106,55,0 duplicate(#LocNival) #LocNivalB NPC_HIDDEN,1,1 diff --git a/npc/020-1/trainer.txt b/npc/020-1/trainer.txt new file mode 100644 index 0000000..1fb4774 --- /dev/null +++ b/npc/020-1/trainer.txt @@ -0,0 +1,167 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Mercenary Trainer + +020-1,39,89,0 script Mercenary Trainer NPC_REDY_MALE_SWORD,{ + mesn; + mesq l("Hello, I am a sword to hire, a Mercenary Trainer and Chief."); + next; + mesn; + mesq l("Do you want to hire a mercenary? Or perhaps get a card so you can invoke them later? I can even make them stronger if you wish."); + next; + goto L_Main; + +L_Main: + select + l("Hire a mercenary"), + l("Buy a mercenary card"), + l("Evolve mercenaries"), + l("Bye."); + mes ""; + switch (@menu) { + // Hire Card + case 1: + menuint + l("[%d GP] [Lv 1~25] Hire for one hour", 2500), 1, + l("[%d GP] [Lv 26~40] Hire for one hour", 7500), 2, + l("[%d GP] [Lv 41~60] Hire for one hour", 15000), 3, + l("[%d GP] [Lv 61~79] Hire for one hour", 30000), 4, + l("[%d GP] [Lv 80~100] Hire for one hour", 50000), 5, + l("I've changed my mind"), 0; + switch (@menuret) { + case 1: + .@gp=max(2000, POL_AdjustPrice(2500)); + if (Zeny < .@gp) { + mesc l("You cannot pay."), 1; + next; + } else { + .@mid=merc_randid(0, 0, 0, 0, 1000); + mercenary_create(.@mid, 3600000); + POL_PlayerMoney(.@gp); + } + break; + case 2: + .@gp=max(7000, POL_AdjustPrice(7500)); + if (Zeny < .@gp) { + mesc l("You cannot pay."), 1; + next; + } else { + .@mid=merc_randid(0, 0, 0, 1000, 0); + mercenary_create(.@mid, 3600000); + POL_PlayerMoney(.@gp); + } + break; + case 3: + .@gp=max(14000, POL_AdjustPrice(15000)); + if (Zeny < .@gp) { + mesc l("You cannot pay."), 1; + next; + } else { + mercenary_create merc_randid(0, 0, 1000, 0, 0), 3600000; + POL_PlayerMoney(.@gp); + } + break; + case 4: + .@gp=max(27000, POL_AdjustPrice(30000)); + if (Zeny < .@gp) { + mesc l("You cannot pay."), 1; + next; + } else { + mercenary_create merc_randid(0, 1000, 0, 0, 0), 3600000; + POL_PlayerMoney(.@gp); + } + break; + case 5: + .@gp=max(45000, POL_AdjustPrice(50000)); + if (Zeny < .@gp) { + mesc l("You cannot pay."), 1; + next; + } else { + mercenary_create merc_randid(1000, 0, 0, 0, 0), 3600000; + POL_PlayerMoney(.@gp); + } + break; + default: + break; + } + break; + // Buy Card + case 2: + npcshopattach(.name$); + openshop; + closedialog; + close; + break; + // Evolve Card + case 3: + mesn; + mesq l("Give me %s cards of the same type, and I'll give you one card of a higher rarity. There is no cost, but the card is random.", b(l("three"))); + next; + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .@card = requestitem(); + if (.@card <= 1) break; + if (countitem(.@card) < 3) { + mesc l("You need at least %d cards of same kind.", 3), 1; + break; + } + .@st = merc_getstar(.@card); + if (.@st < 1 || .@st >= 5) { + mesc l("This cannot be evolved."), 1; + break; + } + delitem .@card, 3; + merc_boxset( + (.@st == 4 ? 1000 : 0), + (.@st == 3 ? 1000 : 0), + (.@st == 2 ? 1000 : 0), + (.@st == 1 ? 1000 : 0), + 0); + mesn; + mesq l("Here you go! Wasn't this a sweet deal?"); + break; + // Leave + default: + closeclientdialog; + goodbye; + close; + break; + } + goto L_Main; + +OnInit: + tradertype(NST_MARKET); + sellitem MercBoxEE, 25000, 1; + sellitem MercBoxDD, 15000, 2; + sellitem MercBoxCC, 7500, 3; + sellitem MercBoxBB, 3750, 4; + sellitem MercBoxAA, 1250, 5; + + .distance=5; + .sex=G_MALE; + end; + +OnClock0001: +OnClock1201: + restoreshopitem MercBoxEE, 25000, 1; + restoreshopitem MercBoxDD, 15000, 2; + restoreshopitem MercBoxCC, 7500, 3; + restoreshopitem MercBoxBB, 3750, 4; + restoreshopitem MercBoxAA, 1250, 5; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes("Nival"); + end; + +OnSellItem: + debugmes("Sale confirmed"); + PurchaseTaxes("Nival"); + end; + +} + diff --git a/npc/020-1/wateranimation.txt b/npc/020-1/wateranimation.txt new file mode 100644 index 0000000..9876cf6 --- /dev/null +++ b/npc/020-1/wateranimation.txt @@ -0,0 +1,24 @@ +// TMW2 scripts. +// Author: +// Saulc +// Jesusalva +// Description: +// Water animations, splash, fishes, etc... + +020-1,82,73,0 script #fishing_nivalis0 NPC_WATER_SPLASH,{ + + fishing(1, CommonCarp, + IceCube, GrassCarp); // begin or continue fishing + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + +020-1,87,73,0 duplicate(#fishing_nivalis0) #fishing_nivalis1 NPC_WATER_SPLASH +020-1,31,52,0 duplicate(#fishing_nivalis0) #fishing_nivalis2 NPC_WATER_SPLASH +020-1,30,63,0 duplicate(#fishing_nivalis0) #fishing_nivalis3 NPC_WATER_SPLASH +020-1,64,127,0 duplicate(#fishing_nivalis0) #fishing_nivalis4 NPC_WATER_SPLASH + diff --git a/npc/020-1/well.txt b/npc/020-1/well.txt new file mode 100644 index 0000000..94265f0 --- /dev/null +++ b/npc/020-1/well.txt @@ -0,0 +1,14 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Annoy players + +020-1,99,83,0 script #NivalisWell NPC_NO_SPRITE,{ + npctalkonce l("This well is sealed. They must take water outside the town."); + end; + +OnInit: + .distance=3; + end; +} diff --git a/npc/020-2/_import.txt b/npc/020-2/_import.txt new file mode 100644 index 0000000..f0970e0 --- /dev/null +++ b/npc/020-2/_import.txt @@ -0,0 +1,3 @@ +// Map 020-2: Nivalis Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-2/_warps.txt", diff --git a/npc/020-2/_warps.txt b/npc/020-2/_warps.txt new file mode 100644 index 0000000..0efaf69 --- /dev/null +++ b/npc/020-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-2: Nivalis Indoors warps +020-2,30,37,0 warp #020-2_30_37 0,0,020-1,47,49 diff --git a/npc/020-3/_import.txt b/npc/020-3/_import.txt new file mode 100644 index 0000000..b456279 --- /dev/null +++ b/npc/020-3/_import.txt @@ -0,0 +1,4 @@ +// Map 020-3: Nivalis Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-3/_warps.txt", +"npc/020-3/mede.txt", diff --git a/npc/020-3/_warps.txt b/npc/020-3/_warps.txt new file mode 100644 index 0000000..c4408f0 --- /dev/null +++ b/npc/020-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-3: Nivalis Indoors warps +020-3,19,28,0 warp #020-3_19_28 0,0,020-1,56,91 diff --git a/npc/020-3/mede.txt b/npc/020-3/mede.txt new file mode 100644 index 0000000..697a786 --- /dev/null +++ b/npc/020-3/mede.txt @@ -0,0 +1,137 @@ +// TMW2 Script +// Author: +// Saulc +// Vasily_Makarov (original from Evol) +// Jesusalva +// Dye Quest added by Povo +// Description: +// Status Reset + +020-3,26,28,2 script Mede NPC_PLAYER,{ + + speech S_LAST_NEXT, + l("I am @@, an alchemist specialized in reset potions.", .name$); + +L_Menu: + .@plush_count = BaseLevel*220-(9*220); + // Lv 10: 220 GP + // Lv 90: 1.782 GP + if (BaseLevel > 10) + .@plush_count = .@plush_count/(BaseLevel/9); + + select + l("Can you reset my stats please?"), + rif(MONSTERPOT >= 1, l("Can you make me a Monster Potion?")), + l("Do you make anything else?"), + lg("You are weird, I have to go sorry."); + + switch (@menu) + { + case 1: + goto L_ResetStats; + case 2: + goto L_MonsterPot; + case 3: + goto L_Dye; + case 4: + goto L_Quit; + } + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + ConfirmStatusReset(); + goto L_Quit; + +L_MonsterPot: + mesn; + mesq l("Not yet, I'm waiting @@ to deliver me the @@ reagent.", any("Saulc", "Jesusalva", "Demure"), any(l("Red"), l("Green"), l("Blue"), l("White"))); + next; + mesn; + mesq l("But if you want, you can farm @@ already. Good luck!", getitemlink(GoldenScorpionClaw)); + next; + goto L_Quit; + +L_Dye: + mesn; + mesq l("I used to make dyes for Agostine but he hasn't bought any in a while. I could make you a %s if you bring me the right items.", + getitemlink(TealDye)); + next; + if (BaseLevel < 35) { + mesn; + mesq l("But the monsters around here must be pretty scary for someone like you."); + next; + mesq l("Come back when you are a higher level."); + next; + goto L_Menu; + } + mesq l("Bring me %d %s, %d %s, %d %s, and %d %s, and I will make it for you. I also charge %d GP as commission.", + 1, getitemlink(IcedBottle), + 25, getitemlink(BlueCoral), + 60, getitemlink(CobaltHerb), + 5, getitemlink(IceCube), + 1500); + compareandsetq NivalisQuest_Mede, 0, 1; + next; + select + l("Yeah, I need one."), + l("Thanks for the help, but no."), + l("Actually, nevermind. Good bye!"); + mes ""; + if (@menu == 2) + goto L_Menu; + + if (@menu == 3) + goto L_Quit; + +L_DyeLoop: + if (countitem(IcedBottle) >= 1 && + countitem(BlueCoral) >= 25 && + countitem(CobaltHerb) >= 60 && + countitem(IceCube) >= 5 && + Zeny >= 1500) { + inventoryplace TealDye, 1, EmptyBottle, 1; + delitem IcedBottle, 1; + delitem BlueCoral, 25; + delitem CobaltHerb, 60; + delitem IceCube, 5; + Zeny-=1500; + getitem TealDye, 1; + getitem EmptyBottle, 1; + if (getq(NivalisQuest_Mede) == 1) { + setq NivalisQuest_Mede, 2; + getexp 1500, 0; + } + mesn; + mesq l("Thanks! Here you go. Perhaps you want another one?"); + next; + if (askyesno() == ASK_YES) + goto L_DyeLoop; + } else { + mesn; + mesq l("Sorry, you don't seem to have everything I need."); + } + close; + + +L_Quit: + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, BrimmedHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, TneckSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .sex = G_MALE; + .distance = 4; + npcsit; + end; +} diff --git a/npc/020-4/_import.txt b/npc/020-4/_import.txt new file mode 100644 index 0000000..aef7c44 --- /dev/null +++ b/npc/020-4/_import.txt @@ -0,0 +1,6 @@ +// Map 020-4: Nivalis Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-4/_warps.txt", +"npc/020-4/baktar.txt", +"npc/020-4/gambler.txt", +"npc/020-4/henry.txt", diff --git a/npc/020-4/_warps.txt b/npc/020-4/_warps.txt new file mode 100644 index 0000000..b0326b0 --- /dev/null +++ b/npc/020-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-4: Nivalis Indoors warps +020-4,64,54,0 warp #020-4_64_54 2,0,020-1,64,83 diff --git a/npc/020-4/baktar.txt b/npc/020-4/baktar.txt new file mode 100644 index 0000000..4472c53 --- /dev/null +++ b/npc/020-4/baktar.txt @@ -0,0 +1,183 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Baktar +// Quests: +// NivalisQuest_Baktar +// .@q1 = Controls Braknar Shield +// (0= Not met, 1= Khafar, 2= Baktar, 3= Complete) +// .@q2 = bitmask with Tulimshar items given + +020-4,64,39,6 script Baktar NPC_RAIJIN,{ + .@q=getq(NivalisQuest_Baktar); + if (!.@q) + goto L_Start; + if (.@q == 2) + goto L_Braknar; + goto L_Collector; + +L_Start: + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("Hello, did you come from Tulimshar? Because you have a nice tan."); + next; + select + l("Yes, I'm coming from Tulimshar."), + l("Well, I just like sunbathing."), + l("Good bye."); + mes ""; + switch (@menu) { + case 2: + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("Bah! Don't try to fool me! The sun can't burn in Nivalis."); + // Don't stop now + case 3: + if (rand2(1,5) == 4) + mesc l("*cough cough*"); + close; + break; + } + + setq NivalisQuest_Baktar, 1, 0; + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("All my parents, grandparents, until the world was born, are from Tulimshar."); + next; + mesn; + mesc l("*cough cough*"); + mesq l("Thus, I like to collect small memetos from Tulimshar."); + next; + mesn; + mesq l("The doctor told me to move here after I got tuberculosis... But I like Tulimshar so much! Please bring me Tulimshar souvenirs, I'll pay you well!"); + close; + + +L_Collector: + .@q2=getq2(NivalisQuest_Baktar); + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("Did you brought me an souvenir from Tulimshar?"); + mes ""; + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .@id = requestitem(); + + if (.@id < 1) close; + if (countitem(.@id) < 1 || checkbound(.@id)) + close; + + // Special exception + if (.@id == Croconut || .@id == HalfCroconut) { + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("Ooh, this definitely is a Tulimshar item! But I would prefer a box full of these, please."); + close; + } + + // No item + .@m = htget(.TULIMITEM, str(.@id), 0); + if (!.@m) { + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("What crap is that?! This is not from Tulimshar!"); + next; + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("I won't accept stuff from Halinarzo!"); + mesc l("Baktar can be picky with Tulimshar stuff, too."); + close; + } + // Already given + if (.@q2 & .@m) { + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("I already have that..."); + close; + } + + // Gives 4.5× more + .@sp=getiteminfo(.@id, ITEMINFO_SELLPRICE); + mesc l("Really give your @@ to Baktar?", getitemlink(.@id)), 1; + mesc l("The item will be lost forever."); + next; + if (askyesno() == ASK_YES) { + delitem .@id, 1; + Zeny+=(.@sp*45/10); + getexp (BaseLevel/4)*.@sp, .@sp; + setq2 NivalisQuest_Baktar, .@q2|.@m; + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("Thanks."); + } + + close; + +L_Braknar: + .@q2=getq2(NivalisQuest_Baktar); + mesn; + if (rand2(1,5) == 4) + mesc l("*cough cough*"); + mesq l("Welcome back."); + select + l("I brought a souvenir for you."), + l("Do you know someone called Braknar?"), + l("Er, uhm, hi!"); + mes ""; + switch (@menu) { + case 1: goto L_Collector; + case 2: + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("Yeah, it was my grand-grand-grandfather."); + next; + mesn strcharinfo(0); + mes l("'-' \"Could you give me his shield? Pretty please? I need it to survive and bring Tulimshar goodies!\""); + next; + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + mesq l("No, it is a family heirloom. But I do have the shield blueprints. A skilled craftsman could forge one."); + next; + mesn strcharinfo(0); + mes l("*-* \"Could you share those blueprints with me? Please?\""); + next; + mesn; + if (rand2(1,5) == 4) mesc l("*cough cough*"); + if (bitmask_count(.@q2) < 2) { + mesq l("Why should I? Go away. %%n"); + close; + } + mesq l("Sure. Here, take it."); + setq1 NivalisQuest_Baktar, 3; + RECIPES_EQUIPMENT[CraftBraknarShield]=true; + break; + case 3: close; + } + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, KnitHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonShorts); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 24); + setunitdata(.@npcId, UDT_HAIRCOLOR, 1); + + .sex = G_MALE; + .distance = 5; + npcsit; + + // Constants + .TULIMITEM = htnew; + htput(.TULIMITEM, str(DesertHat), 1); + htput(.TULIMITEM, str(SerfHat), 2); + htput(.TULIMITEM, str(IronShovel), 4); + htput(.TULIMITEM, str(SilkRobe), 8); + htput(.TULIMITEM, str(FishBox), 16); + htput(.TULIMITEM, str(CroconutBox), 32); + htput(.TULIMITEM, str(PlushroomBox), 64); + end; +} + diff --git a/npc/020-4/gambler.txt b/npc/020-4/gambler.txt new file mode 100644 index 0000000..1384aa5 --- /dev/null +++ b/npc/020-4/gambler.txt @@ -0,0 +1,153 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Gambler: Will next card be better or worse? +// $XAN_BETS: How many victories all players already had (for propaganda :3) + +020-4,69,35,0 script Gambler#020-4 NPC_FLOPPED_NOBLEMAN,{ + function cardname{ + switch (getarg(0)) { + case 0: + return "A"; break; + case 10: + return "J"; break; + case 11: + return "Q"; break; + case 12: + return "K"; break; + case 13: + return l("Joker"); break; + default: + return getarg(0)+1; + } + } + + goto L_Menu; + +L_Menu: + showavatar; + mesn; + mesc l("Hey, I am flopped. Do you want to gamble?"); + mesc l("You need a @@. I'll flip one card, and you'll need to decide if next draw will be HIGHER or LOWER.", getitemlink(CasinoCoins)); + mesc l("If a tie happens, I'll give your coin back."); + next; + menu + rif(countitem(CasinoCoins) >= 1, l("Let's play!")), L_Spin, + l("Information"), L_Info, + l("Leave"), L_Quit; + +L_Info: + mes ""; + mesc l("Rules:"); + mesc l("A card will be flipped, you'll need to decide if next flip will be HIGHER or LOWER."); + mesc l("Cards are ranked on this priority: A - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - J - Q - K - Joker"); + next; + mesc l("Prizes:"); + mesc l("If you're right, you'll get at least 80 GP!"); + mesc l("You'll also get half of the GP as Monster Points!"); + mesc l("If a tie happens, you'll get your coin back."); + mesc l("If you're wrong, your winning streak is reset."); + mesc l("Winning Streak is also reset on logout or when you leave the Inn."); + next; + mesc l("Winning Strike Prizes:"); + mesc l("Every seven successive right guesses, you'll get a %s!", getitemlink(BronzeGift)); // 7.14% + mesc l("Every fifteen successive right guesses, you'll get a @@!", getitemlink(SilverGift)); // 3.33% + mesc l("Every fifty successive right guesses, you'll get a @@!", getitemlink(GoldenGift)); // 1.00% + mesc l("Every 101 successive right guesses, you'll get a @@!", getitemlink(PrismGift)); // 0.50% + next; + goto L_Menu; + + +L_Spin: + showavatar AVATAR_CARD_COVER; + mesc l("I'll draw a card now!"); + next; + delitem CasinoCoins, 1; + // First card will never be an edge card (Ace or Joker), so you can ALWAYS guess wrong. + .@card1=rand(1, 12); + showavatar 1000+.@card1; + mesn; + mesc l("It's a @@!", cardname(.@card1)); + mesc l("Will next draw be HIGHER or LOWER?!"); + next; + mesc l("Cards are ranked on this priority: A - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - J - Q - K - Joker"); + select + l("HIGHER!"), + l("LOWER!"); + mes ""; + + // Flip Flop! + .@card2=rand(0, 13); + showavatar 1000+.@card2; + mesn; + mesc l("It's a @@!", cardname(.@card2)); + + if (.@card1 == .@card2) { + mesc l("It's a tie!"); + getitem CasinoCoins, 1; + .@bypass=1; + } else if (.@card2 < .@card1 && @menu == 2) { + mesc l("It's lower! That's right!"); + Zeny=Zeny+80; + @gambler_winstreak=@gambler_winstreak+1; + } else if (.@card2 > .@card1 && @menu == 1) { + mesc l("It's higher! That's right!"); + Zeny=Zeny+80; + @gambler_winstreak=@gambler_winstreak+1; + } else { + mesc l("You were wrong!"); + @gambler_winstreak=0; + } + + // Winning Streak + if (!.@bypass && @gambler_winstreak) { + if (@gambler_winstreak % 7 == 0) + getitem BronzeGift, 1; + if (@gambler_winstreak % 15 == 0) + getitem SilverGift, 1; + if (@gambler_winstreak % 50 == 0) + getitem GoldenGift, 1; + if (@gambler_winstreak % 101 == 0) + getitem PrismGift, 1; + mesc l("Your current win streak is @@!", @gambler_winstreak); + $XAN_BETS=$XAN_BETS+1; + Zeny+=min((@gambler_winstreak*2), 40); + Mobpt+=40+min(@gambler_winstreak, 20); + } else { + .@bypass=0; + } + next; + goto L_Menu; + +L_Quit: + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + end; + +} + + +// Random NPC +020-4,70,36,4 script Gambling Xan NPC_PLAYER,{ + mesn; + mesq l("Argh... I can never get it right! If only he drew an Ace or a Joker on the first draw... But he never does that!"); + next; + mesn; + mesq l("That rat... I already spent @@ @@ with him!!", $XAN_BETS, getitemlink(CasinoCoins)); + mes l("If I weren't tempted to try again everytime someone gets it right..."); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + + .sex = G_MALE; + .distance = 4; + npcsit; + end; +} diff --git a/npc/020-4/henry.txt b/npc/020-4/henry.txt new file mode 100644 index 0000000..a88a12a --- /dev/null +++ b/npc/020-4/henry.txt @@ -0,0 +1,159 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 + +020-4,24,38,0 script Henry NPC_HENRY,{ + function newQuest; + function checkQuest; + + // The EPISODE + if (getq(LoFQuest_EPISODE) == 8 && + countitem(DeathPotion)) { + mesc l("Should we give this shady looking 'fella a %s?", getitemlink(DeathPotion)); + select + l("No, who knows what they might do with it!"), + l("Sure, what could go wrong?"); + mes ""; + if (@menu != 2) { + clear; + mesc l("*whistles*"); + } else { + mesn strcharinfo(0); + mesq l("The Shadow Tortuga won the race against the Panthom Lord."); + next; + mesn; + mesq l("Oooh, I see, I see! So you're working with ##Bthem##b... This is most appreciated."); + next; + mesn; + mesq l("So, you have fulfilled this part of the misssion. Bring %d %s and a %s to Pachua in the Desert Canyon, and say the same thing to him so he knows you're with us.", 10, getitemlink(Honey), getitemlink(ElixirOfLife)); + next; + mesn; + mesq l("Once you done so, come back and report the new passcode... Then I'll disclose to you the real mission."); + delitem DeathPotion, 1; + getexp 35000, 0; + setq LoFQuest_EPISODE, 9, 0; + close; + } + } + // Also part of The EPISODE + if (getq(LoFQuest_EPISODE) == 10) { + mesn strcharinfo(0); + mesq l("The crow took off to his last flight."); + next; + mesn; + mesq "..."; + next; + if (BaseLevel < 55) { + mesn; + mesq l("Alright. Now to give your quest. But not now, you're still weak. Come back later, will you."); + close; // Next quest is Level 70 so. + } else { + mesn; + mesq l("West of here, north of here, is a cabin. Investigate. That's all."); + next; + mesn; + mesq l("...Oh, right! We locked it. Uhm, we'll open it for you. Be careful."); + setq LoFQuest_EPISODE, 11; + close; + } + } + // Also part of The EPISODE + if (getq(LoFQuest_EPISODE) == 12) { + if (BaseLevel < 57) { + mesn; + mesq l("Before you give me your report, acquire level %d.", 57); + close; // Next quest is Level 70 so. + } else { + mesn; + mesq l("Hm, thanks for your report. This is most useful. Here, drink this."); + next; + mesc l("You start feeling dizzy."); + mesn; + mesq l("%s? You don't look so well. You should see a doctor immediately!", strcharinfo(0)); + setq LoFQuest_EPISODE, 13, 0, 0; + SC_Bonus(86400, SC_POISON, 1); + close; + } + } + + // Level requeriment + if (BaseLevel < 70) { + npctalk3 l("*whistles*"); + close; + } + + // Main Quest + .@q=getq(NivalisQuest_Henry); + switch (.@q) { + case 0: + newQuest(); break; + case 1: + checkQuest(); break; + default: + npctalk3 l("*whistles*"); + } + close; + +function newQuest { + mesn; + mesq l("Hey, psst! Come over here!"); + next; + mesn; + mesq l("I need a favor! For certain... reasons... I ran out of poison! And For certain... reasons... I am in dire need of them!"); + next; + mesn; + mesq l("So if you bring me %d %s, I'll pay you awesomely! Whaddaya say?!", 24, getitemlink(NymphPoison)); + next; + select + l("Sure, I'll be right back."), + l("Erm, for certain... reasons... I cannot help you right now!"); + mes ""; + if (@menu == 2) { + closeclientdialog; + return; + } + mesn; + mesq l("Thanks, I knew I could count on you!"); + setq NivalisQuest_Henry, 1; + return; +} + +function checkQuest { + mesn; + mesq l("Did you brought me the %d/%d %s?", countitem(NymphPoison), 24, getitemlink(NymphPoison)); + select + l("Yes, here!"), + l("Sorry, I'll be back."); + mes ""; + if (@menu == 2) { + closeclientdialog; + return; + } + if (countitem(NymphPoison) < 24) { + mesn; + mesq l("Ooh, many thanks! Lemme just grab something for you..."); + next; + mesc l("%s stabs you to the chest!", l("Henry")), 1; + mesn; + mesq l("Foolish kid, thinking they can fool me, Henry S., from all the people...!"); + die(); + return; + } + delitem NymphPoison, 24; + Mobpt+=10000; + getexp 100000, 0; + setq NivalisQuest_Henry, 2; + mesn; + mesq l("Hehehe... Thanks. Here, take these %s Monster Points... This exchange has never happened.", fnum(10000)); + return; +} + + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/020-5/_import.txt b/npc/020-5/_import.txt new file mode 100644 index 0000000..c98775e --- /dev/null +++ b/npc/020-5/_import.txt @@ -0,0 +1,4 @@ +// Map 020-5: Nivalis Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-5/_warps.txt", +"npc/020-5/bracco.txt", diff --git a/npc/020-5/_warps.txt b/npc/020-5/_warps.txt new file mode 100644 index 0000000..8b70d15 --- /dev/null +++ b/npc/020-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-5: Nivalis Indoors warps +020-5,33,31,0 warp #020-5_33_31 2,0,020-1,86,47 diff --git a/npc/020-5/bracco.txt b/npc/020-5/bracco.txt new file mode 100644 index 0000000..881b842 --- /dev/null +++ b/npc/020-5/bracco.txt @@ -0,0 +1,400 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Nivalis shopkeeper & forge master. He deals with the equipment which Nicholas, +// Silversmith and Nahred doesn't works with. +// TODO FIXME: Rewrite Meltdown to don't allow if countitem(id) > 1 +// Also, use deletion by ID (reliable). +// delitemidx is HOPELESSY BROKEN + +020-5,31,25,0 script Bracco NPC_M_SHOPKEEPER,{ + goto L_Start; + // NewMeltdown( item ) + // Meltdown the item for realz + function NewMeltdown { + .@const$ = data_to_string(getarg(0)); + + // Shady code by gumi + if (startswith(.@const$, "Craft")) { + // infer the item constant from the craft constant + .@recipe = getarg(0); + + .@item = string_to_data(substr(.@const$, 5, getstrlen(.@const$) - 1)); + } else { + // infer the craft constant from the item constant + .@recipe = string_to_data(sprintf("Craft%s", .@const$)); + .@item = getarg(0); + } + + if (.@item <= 0) { + // target item not found + consolebug("ERROR, INVALID ITEM ID DETECTED at NewMeltdown"); + return; + } + // More shady code by gumi + for (.@inv = 0; .@inv < 9; ++.@inv) { + .@size = getcraftrecipe(.@recipe, .@inv, .@qty[0], .@item_id[0]); + + if (.@size < 0) { + if (.@size == -1) { + // recipe does not exist + return 0; + break; + } + // inventory does not exist + return 0; + break; + } + + // More shady code + for (.@it = 0; .@it < .@size; ++.@it) { + .@recipe_item = .@item_id[.@it]; + .@recipe_qty = .@qty[.@it]; + + if (.@recipe_item <= 0) { + break; + } + + // "Unique" items are never refunded + if (.@recipe_qty <= 1) continue; + + // Coal is NEVER refunded + if (.@recipe_item == Coal) continue; + + // New rates + .@mini = .@recipe_qty * 3 / 10 + 1; // Minimum: 30% + 1 + .@maxi = .@recipe_qty / 2; // Maximum: 50% + 1 + + // Some sanitizing (should never happen but you can never be sure) + // (Could only happen if qty == 1, which is skipped) + if (.@mini > .@maxi) + .@maxi = .@mini; + + .@ammo = rand2(.@mini, .@maxi); + + getitem .@recipe_item, .@ammo; + mesc l("* Acquired @@ @@!", .@ammo, getitemlink(.@recipe_item)); + } + } + return .@item; + } + + // Meltdown( item, price ) + function Meltdown { + if (countitem(getarg(0)) != 1) { + mesc l("Wait, if you try to melt more than one item, manaplus will get buggy."), 1; + mesc l("Please try again later!"), 1; + close; + } + + .@index=getarg(0); + .@price=getarg(1, getiteminfo(.@item, ITEMINFO_SELLPRICE)/20); + .@price=POL_AdjustPrice(.@price); + + // Confirmation + mesn; + mesc l("Really melt down your @@? It'll cost you @@ GP. This action cannot be undone!", getitemlink(.@index), .@price), 1; + next; + if (askyesno() == ASK_NO || Zeny < .@price) + return; + + // Report it was done + mesc l("@@ melt down your @@...", .name$, getitemlink(.@index)), 2; + + delitem .@index, 1; + POL_PlayerMoney(.@price); + + // TODO: Inventoryplace. + // Add Items (if inventory is full, your fault and not mine) + NewMeltdown(.@index); + @indexisbroken=true; + return; + } + + // MassMeltdown( item, price ) + function MassMeltdown { + .@id=getarg(0); + .@price=getarg(1); + .@total=countitem(.@id); + if (!.@total) { + mesc l("You don't have any %s.", getitemlink(.@id)), 1; + mesc l("Please try again later!"), 1; + next; + return; + } + + .@price=POL_AdjustPrice(.@price); + + // Skip Confirmation + mesn; + mesc l("Really melt down all your @@? It'll cost you @@ GP each. This action cannot be undone!", getitemlink(.@id), .@price), 1; + next; + if (askyesno() == ASK_NO || Zeny < .@price) + return; + + delinventorylist(); + getinventorylist(); + + delitem .@id, .@total; // Delete first, no refunds + + freeloop(true); + for (.@index=0; .@index < @inventorylist_count; .@index++) { + .@x=@inventorylist_id[.@index]; + if (.@x == getarg(0) && Zeny >= .@price) { + //delitemidx .@index, 1; + POL_PlayerMoney(.@price); + // Report it was done + mesc l("@@ melt down your @@...", .name$, getitemlink(.@x)), 2; + + // Really melt it down + NewMeltdown(getarg(0)); + } + } + freeloop(false); + @indexisbroken=true; + return; + } + + // blacksmith_create( BaseItem1, Amount, BaseItem2, Amount, PrizeItem, Price ) + function blacksmith_create { + .@base1=getarg(0); + .@amon1=getarg(1); + .@base2=getarg(2); + .@amon2=getarg(3); + .@prize=getarg(4); + .@price=getarg(5); + + .@price=POL_AdjustPrice(.@price); + + mesn; + mesq l("Do you want to craft @@? For that I will need:", getitemlink(.@prize)); + mesc l("@@/@@ @@", countitem(.@base1), .@amon1, getitemlink(.@base1)); + mesc l("@@/@@ @@", countitem(.@base2), .@amon2, getitemlink(.@base2)); + mesc l("@@/@@ GP", format_number(Zeny), format_number(.@price)); + + select + l("Yes"), + l("No"); + + if (@menu == 2) + return; + + if (countitem(.@base1) >= .@amon1 && + countitem(.@base2) >= .@amon2 && + Zeny >= .@price) { + inventoryplace .@prize, 1; + delitem .@base1, .@amon1; + delitem .@base2, .@amon2; + POL_PlayerMoney(.@price); + getitem .@prize, 1; + .@xp=getiteminfo(.@base1, ITEMINFO_SELLPRICE)*.@amon1+getiteminfo(.@base2, ITEMINFO_SELLPRICE)*.@amon2; + .@xp=.@xp*2/3; + getexp .@xp, rand(1,10); + + mes ""; + mesn; + mesq l("Many thanks! Come back soon."); + } else { + speech S_FIRST_BLANK_LINE,// | S_LAST_NEXT, + l("You don't have enough material, sorry."); + } + return; + } + +// Start +L_Start: + mesn; + mesq l("Welcome to my fine establishment!"); + mes ""; + select + l("Trade"), + l("I'm actually looking for an item forged!"), + l("I would like an item melted!"), + l("I would like all Knifes and Daggers on me melted!"), + l("Leave"); + mes ""; + + if (@menu == 2) + goto L_Forge; + + if (@menu == 3) + goto L_Meltdown; + + if (@menu == 4) + goto L_Irreversible; + + closedialog; + if (@menu == 1) { + npcshopattach(.name$); + shop .name$; + } + goodbye; + close; +// Note: the prices are absurd atm, but hey hey, every single one of them are cap items currently +L_Forge: + mesn; + mesq l("Well, if you want warrior craft, perhaps you should look for @@ or @@.", l("Nicholas"), l("Nahrec")); + mes ""; + select + l("Nothing, sorry!"), + l("I want leather armbands!"), + l("I want copper armbands!"), + l("I want iron armbands!"); + mes ""; + switch (@menu) { + case 1: + close; break; + case 2: + blacksmith_create(LeatherPatch, 40, TitaniumIngot, 1, Armbands, 6500); + break; + case 3: + blacksmith_create(CopperIngot, 10, Coal, 30, CopperArmbands, 11000); + break; + case 4: + blacksmith_create(IronIngot, 40, Coal, 80, IronArmbands, 21000); + break; + } + goto L_Forge; + +L_Irreversible: + mesn; + mesq l("Quite the guts! The price is taxed individually, if you run out of GP it is your loss."); + mesc l("Are you sure?"), 1; + next; + menuint + l("I'm not."), 0, + l("Rusty Knife"), RustyKnife, + l("Small Knife"), SmallKnife, + l("Knife"), Knife, + l("Sharp Knife"), SharpKnife, + l("Dagger"), Dagger; + mes ""; + .@it=@menuret; + switch (@menuret) { + // Copy Paste from normal Meltdown + case RustyKnife: + MassMeltdown(.@it, 15, IronOre, any(0, 0, 0, 1, 1)); + break; + case SmallKnife: + MassMeltdown(.@it, 15, IronOre, any(0, 0, 1, 1, 1)); + break; + case Knife: + MassMeltdown(.@it, 25, IronOre, any(0, 1, 1, 2)); + break; + case SharpKnife: + MassMeltdown(.@it, 50, IronOre, any(1, 2, 2, 3)); + break; + case Dagger: + MassMeltdown(.@it, 100, IronOre, any(2, 2, 3, 3, 4)); + break; + // MISSING + } + close; + + + + +L_Meltdown: + mesn; + mesc l("What item do you want to melt down? This is irreversible, and may return some ingots to you, but there is no way to tell how many you'll receive!"), 1; + mesc l("Each item have it's own tax."); + .@id=requestitem(); + if (.@id <= 0) + close; + mes ""; + // Returns 30~50% of invested ingots, rounded down. Never returns Coal. + switch (.@id) { + // Special Exceptions + case SilverMirror: + Meltdown(.@id, 500, SilverOre, rand2(2, 5)); // Exception + break; + case RustyKnife: + Meltdown(.@id, 15, IronOre, any(0, 0, 0, 1, 1)); // Exception + break; + case SmallKnife: + Meltdown(.@id, 15, IronOre, any(0, 0, 1, 1, 1)); // Exception + break; + case Knife: + Meltdown(.@id, 25, IronOre, any(0, 1, 1, 2)); // Exception + break; + case SharpKnife: + Meltdown(.@id, 50, IronOre, any(1, 2, 2, 3)); // Exception + break; + case Dagger: + Meltdown(.@id, 100, IronOre, any(2, 2, 3, 3, 4, 5)); // Exception + break; + case GoldenRing: + Meltdown(.@id, 1500, GoldPieces, rand2(2,3)); + break; + default: + if (!Meltdown(.@id)) { + mesn; + mesq l("I cannot melt this. I only melt down equipment, and not everything I know how to!"); + next; + } + break; + } + //mesc l("Melt something else?"); + //if (askyesno() == ASK_NO) + // close; + //mes ""; + //goto L_Meltdown; + mesc l("Thanks for using my services!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, TneckSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, RaidTrousers); + setunitdata(.@npcId, UDT_WEAPON, FurBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 2); + + sleep(SHOPWAIT); + tradertype(NST_MARKET); + sellitem BritShield, -1, 1; + sellitem BladeShield, -1, 1; + sellitem MiereCleaver, -1, 1; + sellitem ShortSword, -1, 1; + sellitem LeatherShirt, -1, 1; + sellitem LeatherShield, 5000, 1; + sellitem ShortBow, -1, 1; + sellitem ArrowAmmoBox,-1,rand(8,12); + sellitem IronAmmoBox,-1,rand(3,5); + + npcsit; + .sex = G_MALE; + .distance = 5; + end; + +OnWed0000: +OnThu0400: +OnFri0800: +OnSat1200: +OnSun1600: +OnMon2000: + restoreshopitem BritShield, 1; + restoreshopitem BladeShield, 1; + restoreshopitem MiereCleaver, 1; + restoreshopitem ShortSword, 1; + restoreshopitem LeatherShirt, 1; + restoreshopitem LeatherShield, 5000, 1; + restoreshopitem ShortBow, 1; + restoreshopitem ArrowAmmoBox,rand(8,12); + restoreshopitem IronAmmoBox,rand(3,5); + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} diff --git a/npc/020-6/_import.txt b/npc/020-6/_import.txt new file mode 100644 index 0000000..e6ed347 --- /dev/null +++ b/npc/020-6/_import.txt @@ -0,0 +1,4 @@ +// Map 020-6: Nivalis Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-6/_warps.txt", +"npc/020-6/agostine.txt", diff --git a/npc/020-6/_warps.txt b/npc/020-6/_warps.txt new file mode 100644 index 0000000..d9dbc08 --- /dev/null +++ b/npc/020-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-6: Nivalis Indoors warps +020-6,31,33,0 warp #020-6_31_33 1,0,020-1,33,95 diff --git a/npc/020-6/agostine.txt b/npc/020-6/agostine.txt new file mode 100644 index 0000000..e58d123 --- /dev/null +++ b/npc/020-6/agostine.txt @@ -0,0 +1,186 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// TMW-BR Team +// The Mana World Team +// Description: +// Agostine the Legendary Tailor +// Quest struct: +// Quest State, Nº of furs, Randomness Limiter + +020-6,30,26,0 script Agostine NPC_EDOUARD,{ + mesn l("Agostine, the Legendary Tailor"); + mesq l("Oui, welcome to this fine shop! My name is Agostine, the legendary tailor!"); + next; + mesn l("Agostine, the Legendary Tailor"); + mesq l("Some people say that I am the best tailor in the world, but I think I am the best one in the universe!"); + next; + mesn l("Agostine, the Legendary Tailor"); + mesq l("As long that you have the money, and some really high quality material, I can tailor anything ugly in something fashion!"); + mesc l("As long that Saulc doesn't draws the sprites for my stuff, that is! %%a"); // That's a joke. + next; + +L_Main: + .@q=getq(NivalisQuest_Agostine); + .@q2=getq2(NivalisQuest_Agostine); + .@q3=getq3(NivalisQuest_Agostine); + mesn l("Agostine, the Legendary Tailor"); + mesq l("So, what will it be?"); + select + l("I'm fine, thanks!"), + rif(.@q < 2, l("How much would be the budget for Fur Boots?")); + mes ""; + + switch (@menu) { + case 1: + closeclientdialog; npctalkonce l("Arrevouir!"); close; + case 2: + // Once in a while you need to drink him + if (.@q % 2 == 0) + goto L_Beer; + goto L_FurBoots; + } + + close; + +L_Beer: + mesn l("Agostine, the Legendary Tailor"); + mesq l("HOLD THAT! I actually like to drink, and spend some drinking nights with Saulc and Crazyfefe!"); + next; + mesn l("Agostine, the Legendary Tailor"); + mesq l("Beer is nice, but sometimes I want something more exotic to drink! Oui, we need a proper bar here!"); + next; + mesn l("Agostine, the Legendary Tailor"); + mesq l("So, what about this: You bring me a @@ and I'll tailor anything you need!", getitemlink(RedPlushWine)); + if (!countitem(RedPlushWine)) + close; + next; + select + l("Here, you can have a glass."), + l("Sorry, I have nothing."); + mes ""; + if (@menu == 1) { + delitem RedPlushWine, 1; + setq1 NivalisQuest_Agostine, .@q+1; + mesn l("Agostine, the Legendary Tailor"); + mesq l("Aaaaaahhh... Oui, that's a good wine! So, what will it be?"); + goto L_Main; + } else { + mesn l("Agostine, the Legendary Tailor"); + mesq l("That is a pity..."); + } + close; + +L_FurBoots: + mesn l("Agostine, the Legendary Tailor"); + mesq l("So, you want some @@ to keep you comfy on this harsh winter?", getitemlink(FurBoots)); + next; + mesn l("Agostine, the Legendary Tailor"); + mesq l("Well, as with any legendary tailor, you must bring the materials yourself, and pay the work fee!"); + next; + mesn l("Agostine, the Legendary Tailor"); + // Roughly 125 fishes and 15~100 fur + mesq l("For the @@, I'll need 5 ##Bhigh-quality##b @@, and @@ GP for work fee!", getitemlink(FurBoots), getitemlink(WhiteFur), format_number(7125)); + mesq l("I'll also need @@ as base material!", getitemlink(Boots)); + + mesc l("(Note: Agostine will destroy low quality materials!)"); + next; + select + l("Legendary my ass! What a rip-off!"), + l("I'll start delivering the fur!"), + l("I have everything, can you tailor it?"); + + switch (@menu) { + case 1: + goto L_Main; + case 2: + goto L_DeliverFur; + case 3: + @agostine_item=FurBoots; + @agostine_base=Boots; + @agostine_fee=7125; + @agostine_fur=5; + goto L_Craft; + } + + close; + +// Fur minigame +L_DeliverFur: + .@q2=getq2(NivalisQuest_Agostine); + .@q3=getq3(NivalisQuest_Agostine); + mesc l("You already delivered @@ high-quality patches of white fur.", .@q2); + + select + rif(countitem(WhiteFur), l("Is this high-quality fur?")), + l("Maybe later."); + mes ""; + if (@menu == 2) + goto L_Main; + + mesc l("Agostine takes the fur from your hands."); + delitem WhiteFur, 1; + .@success=rand(6,26)-.@q3; // You will never need more than 20 fur nor less than <delivered_good> fur per patch + + if (.@success <= 1) { + mesc "%%H " + l("He looks pleased."); + getexp 100, 25; + setq2 NivalisQuest_Agostine, .@q2+1; + setq3 NivalisQuest_Agostine, .@q2; + next; + mesn l("Agostine, the Legendary Tailor"); + mesq l("That's some good white for you've got here!"); + } else { + mesc l("He looks displeased and destroys the fur."); + getexp 15, 5; + setq3 NivalisQuest_Agostine, .@q3+1; + next; + mesn l("Agostine, the Legendary Tailor"); + mesq l("It was an awful cut! Don't think any piece of a sightly lower material will have part in my art!"); + } + + goto L_DeliverFur; + +// Agostine item crafting +L_Craft: + .@q=getq(NivalisQuest_Agostine); + .@q2=getq2(NivalisQuest_Agostine); + .@q3=getq3(NivalisQuest_Agostine); + if (.@q2 < @agostine_fur) { + mesn l("Agostine, the Legendary Tailor"); + mesq l("You haven't provided me enough High-Quality White Fur! Go back to hunting!"); + next; + goto L_Main; + } + if (Zeny < @agostine_fee) { + mesn l("Agostine, the Legendary Tailor"); + mesq l("You don't have enough money! I am an expensive tailor, I want @@ GP!", format_number(@agostine_fee)); + next; + goto L_Main; + } + if (!countitem(@agostine_base)) { + mesn l("Agostine, the Legendary Tailor"); + mesq l("You haven't provided me the @@ for the base material!", getitemlink(@agostine_base)); + next; + goto L_Main; + } + + inventoryplace @agostine_item, 1; + delitem @agostine_base, 1; + setq2 NivalisQuest_Agostine, .@q2-@agostine_fur; + Zeny=Zeny-@agostine_fee; + getitem @agostine_item, 1; + setq1 NivalisQuest_Agostine, .@q+1; + getexp @agostine_fee, @agostine_fur*125; + + mesn l("Agostine, the Legendary Tailor"); + mesq lg("Here you go, my friend! Please enjoy!"); + next; + goto L_Main; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + diff --git a/npc/020-7-1/_import.txt b/npc/020-7-1/_import.txt new file mode 100644 index 0000000..b41683e --- /dev/null +++ b/npc/020-7-1/_import.txt @@ -0,0 +1,20 @@ +// Map 020-7-1: Blue Sages' Mansion +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-7-1/_warps.txt", +"npc/020-7-1/core.txt", +"npc/020-7-1/eevert.txt", +"npc/020-7-1/ensio.txt", +"npc/020-7-1/henriikka.txt", +"npc/020-7-1/janika.txt", +"npc/020-7-1/janitors.txt", +"npc/020-7-1/kristian.txt", +"npc/020-7-1/kullervo.txt", +"npc/020-7-1/mirjami.txt", +"npc/020-7-1/nea.txt", +"npc/020-7-1/oskari.txt", +"npc/020-7-1/peetu.txt", +"npc/020-7-1/politics.txt", +"npc/020-7-1/pyry.txt", +"npc/020-7-1/sage.txt", +"npc/020-7-1/santeri.txt", +"npc/020-7-1/teuvo.txt", diff --git a/npc/020-7-1/_warps.txt b/npc/020-7-1/_warps.txt new file mode 100644 index 0000000..4db1e29 --- /dev/null +++ b/npc/020-7-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-7-1: Blue Sages' Mansion warps +020-7-1,24,85,0 warp #020-7-1_24_85 2,0,020-7,55,22 diff --git a/npc/020-7-1/core.txt b/npc/020-7-1/core.txt new file mode 100644 index 0000000..e1a70c2 --- /dev/null +++ b/npc/020-7-1/core.txt @@ -0,0 +1,337 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Core functions (it MUST be loaded first) + +// Some setup is required + +// Check if you have something to ask to Blue Sage staff +// BSQuestion( ) +function script BSQuestion { + .@b1=getq(NivalisQuest_BlueSage); + .@b2=getq2(NivalisQuest_BlueSage); + .@b3=getq3(NivalisQuest_BlueSage); + .@rt=0; + + // After you collected everyone's feedback, hide the option + if (is_between(3,8,.@b1)) { + if (.@b3 < BS_NPCALL) + .@rt=.@rt | BS_QHELPER; + } + + if (.@b1 == 6) { + if (.@b2 < BS_NPCALL) + .@rt=.@rt | BS_QVISITOR; + } + return .@rt; +} + + +// Clear a Nest ID +// BSClearNest( ID ) +function script BSClearNest { + .@id=getarg(0); + switch (.@id) { + case BS_SNEST1: + $@BS_NEST1=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill1"); + break; + case BS_SNEST2: + $@BS_NEST2=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill2"); + break; + case BS_SNEST3: + $@BS_NEST3=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill3"); + break; + case BS_SNEST4: + $@BS_NEST4=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill4"); + break; + case BS_SNEST5: + $@BS_NEST5=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill5"); + break; + case BS_SNEST6: + $@BS_NEST6=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill6"); + break; + case BS_SNEST7: + $@BS_NEST7=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill7"); + break; + case BS_SNEST8: + $@BS_NEST8=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill8"); + break; + case BS_SNEST9: + $@BS_NEST9=0; + killmonster("020-7-1", "#BlueSageHUB::OnSlimeKill9"); + break; + } + if (playerattached()) { + @nestid=0; + setq2 NivalisQuest_BlueSageSlimes, 0; + } + return; +} + +// Return the Nest ID +// BSNestID( x, y ) +function script BSNestID { + .@xa=getarg(0); + .@ya=getarg(1); + switch (.@xa) { + case 1: + if (.@ya == 1) + return BS_SNEST1; + else if (.@ya == 2) + return BS_SNEST2; + else if (.@ya == 3) + return BS_SNEST3; + break; + case 2: + if (.@ya == 1) + return BS_SNEST4; + else if (.@ya == 2) + return BS_SNEST5; + else if (.@ya == 3) + return BS_SNEST6; + break; + case 3: + if (.@ya == 1) + return BS_SNEST7; + else if (.@ya == 2) + return BS_SNEST8; + else if (.@ya == 3) + return BS_SNEST9; + break; + } + Exception("Invalid BS Nest Quadrands: ("+.@xa+", "+.@ya+")"); + return 0; +} + +// Return the Nest Quadrands +// BSQuadrand( NestID ) +function script BSQuadrand { + .@nx=getarg(0); + switch (.@nx) { + case BS_SNEST1: + return 1; + case BS_SNEST2: + return 2; + case BS_SNEST3: + return 3; + case BS_SNEST4: + return 4; + case BS_SNEST5: + return 5; + case BS_SNEST6: + return 6; + case BS_SNEST7: + return 7; + case BS_SNEST8: + return 8; + case BS_SNEST9: + return 9; + } + Exception("Invalid BS Nest ID: ("+.@nx+")"); + return 0; +} + +// Proccess the slime type and amount +// BSProccess( ) +function script BSProccess { + .@b1=getq(NivalisQuest_BlueSageSlimes); + .@b2=getq2(NivalisQuest_BlueSageSlimes); + .@b3=getq3(NivalisQuest_BlueSageSlimes); + + // Quest state not assigned + if (!.@b1) + return; + + // Fix your coordinates + getmapxy(.@m$, .@xc, .@yc,0); + .@x=.@xc-63; + .@y=.@yc-31; + + // Check if you're off the library area + if (.@x <= 0 || .@y <= 0) + return; + + // Map changed. OnCycle is not cleared, so we don't return, we end. + if (.@m$ != "020-7-1") + end; + + // Calculate quadrand + // The map goes from (64,32) to (139,83) + // There is a 3x3 area, totalizing nine nests + // The useful area is 75 x 51, divided by 3 we have + // 25 x 17 quadrands. + // Anything beyond these quadrands is not regarded + .@x=(.@x/25)+1; + .@y=(.@y/17)+1; + if (.@x > 3 || .@y > 3) + return; + + // We want the Quadrand ID to know if it was cleared or not + .@nest=BSNestID(.@x,.@y); + .@id=BSQuadrand(.@nest); + + // Check if the nest was triggered + if (@nestid == .@nest) + return; + + // Check if it was cleared already + if (getq3(NivalisQuest_BlueSageSlimes) & .@nest) + return; + + // Check if we should spawn or if the quadrand is active + if (getd("$@BS_NEST"+str(.@id))) + return; + + // Let's see if something should happen (16.7% odds each second. 3% of drip) + .@chance=rand(30); + if (.@chance <= 25) { + return; + } else if (.@chance == 27) { + dispbottom l("A slime drips in front of you and explodes!"); + percentheal -40, 0; + } else { + dispbottom l("You notice a group of slimes emerging from the debris among the shelves."); + } + + // If it haven't returned yet: It's good to go! + // Reserve the nest ID for us, and clear the previous. + BSClearNest(@nestid); + BSClearNest(.@nest); + @nestid=.@nest; + setd("$@BS_NEST"+.@id, getcharid(0)); + + // We spawn monsters + if (.@id % 4 == 0) + .@mid=WhiteSlime; + else + .@mid=BlueSlime; + + //debugmes "Spawning %d slimes on quadrand %d", getd("$@BS_KNEST"+.@id), .@id; + // Does getd() works against a constant? I don't think so + monster .@m$, .@xc, .@yc, strcharinfo(0)+"'s slime", .@mid, getd("$@BS_KNEST"+.@id), "#BlueSageHUB::OnSlimeKill"+.@id; + + return; +} + +// Proccess the slime death +// BSProccessDeath( nestid, total ) +function script BSProccessDeath { + .@nest=getarg(0); + .@ammo=getarg(1); + .@id=BSQuadrand(.@nest); + + // First and foremost: Drops + if (playerattached()) { + getmapxy(.@m$, .@x, .@y, 0); + // Capped at ~12% drop chance of book pages + if (rand(0,10000) <= 660+(readparam2(bLuk)*6)) + makeitem SpellBookPage, 1, .@m$, .@x, .@y; + if (rand(0,10000) <= 100) + makeitem Candy, 1, .@m$, .@x, .@y; + } + + // Mark the score if appliable and player is attached + if (playerattached()) { + if (@nestid == .@nest) { + .@q2=getq2(NivalisQuest_BlueSageSlimes)+1; + setq2 NivalisQuest_BlueSageSlimes, .@q2; + } + } + + // All mobs are dead + if (!mobcount("020-7-1", "#BlueSageHUB::OnSlimeKill"+.@id)) { + if (playerattached()) { + // Did you killed all slimes? Or did they explode themselves? + // You have some chance to win even if you failed to kill all + // Will not work if this was not your nest + if ((.@q2 == .@ammo || rand(0,9000) == 255) && @nestid == .@nest) { + .@q3=getq3(NivalisQuest_BlueSageSlimes); + setq3 NivalisQuest_BlueSageSlimes, .@q3 | .@nest; + dispbottom l("It wasn't easy, but you think that you extinguished this nest."); + if ((.@q3 | .@nest) == BS_SNESTALL) + dispbottom l("Perhaps that was the last of them?"); + } else { + dispbottom l("You don't see any slimes from that nest anymore. But did you really get all of them?"); + debugmes "Nest %d, killed %d/%d slimes", .@id, .@q2, .@ammo; + debugmes "NEST ID: %d / %d", @nestid, .@nest; + } + } + // Regardless of player attached or not, this nest must be clean + BSClearNest(.@nest); + } + + return; +} + +020-7-1,24,84,0 script #BlueSageHUB NPC_HIDDEN,2,2,{ + end; + +OnSlimeKill1: + BSProccessDeath(BS_SNEST1, $@BS_KNEST1); + end; + +OnSlimeKill2: + BSProccessDeath(BS_SNEST2, $@BS_KNEST2); + end; + +OnSlimeKill3: + BSProccessDeath(BS_SNEST3, $@BS_KNEST3); + end; + +OnSlimeKill4: + BSProccessDeath(BS_SNEST4, $@BS_KNEST4); + end; + +OnSlimeKill5: + BSProccessDeath(BS_SNEST5, $@BS_KNEST5); + end; + +OnSlimeKill6: + BSProccessDeath(BS_SNEST6, $@BS_KNEST6); + end; + +OnSlimeKill7: + BSProccessDeath(BS_SNEST7, $@BS_KNEST7); + end; + +OnSlimeKill8: + BSProccessDeath(BS_SNEST8, $@BS_KNEST8); + end; + +OnSlimeKill9: + BSProccessDeath(BS_SNEST9, $@BS_KNEST9); + end; + +OnTouch: + addtimer2(1000, "#BlueSageHUB::OnCycle"); + end; + +OnCycle: + BSProccess(); + addtimer(1000, "#BlueSageHUB::OnCycle"); + end; + +OnInit: + // K - Nest Kills + $@BS_KNEST1=3; + $@BS_KNEST2=2; + $@BS_KNEST3=1; + $@BS_KNEST4=4; // WS + $@BS_KNEST5=2; + $@BS_KNEST6=1; + $@BS_KNEST7=3; + $@BS_KNEST8=5; // WS + $@BS_KNEST9=1; + end; +} + diff --git a/npc/020-7-1/eevert.txt b/npc/020-7-1/eevert.txt new file mode 100644 index 0000000..5e3f3f1 --- /dev/null +++ b/npc/020-7-1/eevert.txt @@ -0,0 +1,137 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Workers which produces pages +// helperM* + +// NivalisQuest_BlueSagePagemaker STRUCTURE +// FIELD 1: +// MAIN STATE +// FIELD 2: +// BOOK MAKING QUEST +// 1 - Illustrations delivered (BS_PMINK) +// 2 - Bindings delivered (BS_PMBINDING) +// 4 - Pages delivered (BS_PMPAGE) +// 8 - Glue delivered (BS_PMGLUE) +// =15: All items delivered + +020-7-1,34,56,0 script Eevert NPC_BLUESAGEWORKER_MA,{ + function askQuestion; + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + mesn; + if (.@qt & BS_PMINK) + mesq l("Thanks for your help with the inks! Now I'll be able to fulfill my tasks adequately. Some of these books were really valuable, and it's important to recreate them as good as possible."); + else + mesq l("Mh. I wonder how I'm expected to perform my task with this meager equipment. The new books will look pathetic."); + + // Begin here + askQuestion(); + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + mes ""; + mesn; + mesq l("I'm doing the covers and illustrations for the new books we create from the pieces of the old ones."); + next; + mesn; + mesq l("But due to all of the chaos from when the slimes escaped, most of the phials of colored ink were broken. How am I supposed to do the illustrations without color?"); + next; + mesn; + mesq l("I could make ink myself, but I am lacking some material..."); + next; + mes l("@@/@@ @@", countitem(CobaltHerb), 50, getitemlink(CobaltHerb)); + mes l("@@/@@ @@", countitem(GambogeHerb), 50, getitemlink(GambogeHerb)); + mes l("@@/@@ @@", countitem(AlizarinHerb), 50, getitemlink(AlizarinHerb)); + mes l("@@/@@ @@", countitem(ArtichokeHerb), 50, getitemlink(ArtichokeHerb)); + mes l("@@/@@ @@", countitem(MauveHerb), 50, getitemlink(MauveHerb)); + mes l("@@/@@ @@", countitem(MaggotSlime), 10, getitemlink(MaggotSlime)); + mes l("@@/@@ @@", countitem(DuckFeather), 1, getitemlink(DuckFeather)); + next; + mesn; + mesq l("Hehe... Some material... %%5"); + next; + select + l("Uh... Yeah, that's not funny."), + l("Worry not, I have them with me."); + mes ""; + if (@menu == 1) { + mesn; + mesq l("I agree with you. Most of these materials can't even be found on Nivalis."); + next; + mesn; + mesq l("I guess I'll need to try to work with whatever ink is left until the supplies arrive next month..."); + close; + } + if (countitem(CobaltHerb) < 50 || + countitem(GambogeHerb) < 50 || + countitem(AlizarinHerb) < 50 || + countitem(ArtichokeHerb) < 50 || + countitem(MauveHerb) < 50 || + countitem(MaggotSlime) < 10 || + countitem(DuckFeather) < 1) { + mesn; + mesq l("Do you know how to count, maggot? %%5"); + next; + mesn; + mesq l("You should be ASHAMED of yourself, you liar."); + close; + } + delitem CobaltHerb, 50; + delitem GambogeHerb, 50; + delitem AlizarinHerb, 50; + delitem ArtichokeHerb, 50; + delitem MauveHerb, 50; + delitem MaggotSlime, 10; + delitem DuckFeather, 1; + setq2 NivalisQuest_BlueSagePagemaker, .@qt|BS_PMINK; + getexp 3535, 215; // 20% from references, rounded up. It's part of main story. + // is present. REMEMBER THIS IS A LEVEL 36/16 QUEST, REGARDLESS IF EVERYONE DECIDES + // TO DO IT AT LEVEL 40. Exp reward will not change. It's main story, too. + // Besides, it gives Job Experience, which is not common. + mesn; + mesq l("Wonderful! Now I can prepare the ink for magnificent illustrations! You're very generous."); + close; + +function askQuestion { + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + next; + select + rif(!(.@qt & BS_PMINK), l("What is your problem?")), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye."), l("Not my problem.")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("A visitor? I don't really pay attention to visitors, unless it's someone notable."); + break; + case 3: + mesn; + mesq l("Ah, Peetu. I really appreciate his sense for high quality work. The requirements to become a helper of a sage are already high, but Peetu is outstanding. He has a talent for magic and combined with his diligence, it's quite remarkable."); + next; + mesn; + mesq l("That's why I'm really confused about this situation, since he was the one performing the sealing of the slimes. I wonder what went wrong."); + if (!(.@q3 & .bsId)) + setq3 NivalisQuest_BlueSage, .@q3 | .bsId; + break; + case 4: + close; + } + } while (@menu != 1); + return; +} + +OnInit: + .bsId=BS_NPC02; + .sex=G_MALE; + .distance=5; + npcsit; + end; +} + diff --git a/npc/020-7-1/ensio.txt b/npc/020-7-1/ensio.txt new file mode 100644 index 0000000..c232c22 --- /dev/null +++ b/npc/020-7-1/ensio.txt @@ -0,0 +1,147 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Responsible for recovering the lost pages (SpellBookPage) +// helperBookpages* +// +// NivalisQuest_BlueSagePagefinder STRUCTURE +// FIELD 1: +// MAIN STATE +// FIELD 2: +// nº of pages found (0~31) +// FIELD 3: +// nº of duplicates found (for randomness control - capped at 60) + +020-7-1,36,39,4 script Ensio NPC_BLUESAGEWORKER_MB,{ + function askQuestion; + function helpLogic; + + .@qt=getq2(NivalisQuest_BlueSagePagefinder); + mesn; + if (.@qt == 31) + mesq l("We'd never be able to recover our books so quickly without your help. Thanks!"); + else if (.@qt >= 27) + mesq l("There are only a few pages missing."); + else if (.@qt >= 19) + mesq l("Wow, you already found quite a lot of bookpages. Please keep it up!"); + else if (.@qt >= 9) + mesq l("Thanks for the pages you brought. There are still some missing."); + else if (.@qt) + mesq l("So many bookpages are still missing..."); + else + mesc l("*sigh*"); + if (getq(NivalisQuest_BlueSagePagefinder) && getq(NivalisQuest_BlueSage) >= 12) + close; + + next; + mesn; + mesq l("Hello. Did you come here to see the library? There isn't much left... But we're working on recovering the books by collecting the ripped out book pages, sorting them and recreating the books."); + askQuestion(); + close; + +function askQuestion { + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + .@qo=getq(NivalisQuest_BlueSagePagefinder); + .@qt=getq2(NivalisQuest_BlueSagePagefinder); + next; + mes ""; + select + rif(!.@qo, "Can I help you somehow?"), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye."), l("Good luck with that. See you in ten years or something.")); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("Oh, would you? That'd be great! You see, the slimes ate most of the books and it is difficult to retrieve the bookpages. If you feel capable of this task, you could go out to hunt the Blue and White Slimes, to get the @@ back from them.", getitemlink(SpellBookPage)); + if (!countitem(SpellBookPage)) + break; + helpLogic(); + break; + case 2: + mesn; + mesq l("Visitor with a mask? Ah, I think I know who are you talking about. Black clothes and a golden mask. Rather strange guy."); + next; + mesn; + mesq l("He was looking around in the library a while, and then came to ask about our research. He seemed to be particularly interested in our experiments with the slimes."); + next; + mesn; + mesq l("Heh, I wonder what he would've said if he'd have visited us a little bit later. Mh, now that I think about it, the accident with the slimes happened just the night after he was here."); + if (!(.@q2 & .bsId)) + setq2 NivalisQuest_BlueSage, .@q2 | .bsId; + break; + case 3: + mesn; + mesq l("Oh, I can't really tell. I'm mostly working in the library, while he's very involved in the research."); + break; + } + } while (@menu != 4); + close; +} + +function helpLogic { + do { + inventoryplace ThetaBook, 1; + .@qt=getq2(NivalisQuest_BlueSagePagefinder); + .@qx=getq3(NivalisQuest_BlueSagePagefinder); + next; + mesc l("Donate 1/@@ @@ to Ensio?", countitem(SpellBookPage), getitemlink(SpellBookPage)); + mes ""; + // break(); works, right? + if (askyesno() == ASK_NO) + break; + + delitem SpellBookPage, 1; + if (rand(90 - .@qt) < 36 && .@qx < 60) { + // Old Page + setq3 NivalisQuest_BlueSagePagefinder, .@qx+1; + mesn; + mesq "%%f " + l("Mh, let me see. We already have a copy of this page, but it's helpful nevertheless. Thank you."); + } else { + // Original Page + setq2 NivalisQuest_BlueSagePagefinder, .@qt+1; + mesn; + mesq "%%H " + l("Ah, wonderful! This is a page we haven't found yet!"); + } + getexp 525, 32; // about 3% of exp table. + + // Maybe you've completed the requeriment? + .@qt=getq2(NivalisQuest_BlueSagePagefinder); + if (.@qt >= 31) { + next; + mesn; + mesq l("Amazing. I think you found all the missing pages of which we didn't have a copy. This is a great help! I'll mention this to Nikolai."); + next; + mesn; + mesq l("For now, please accept this @@ as my gratitude.", getitemlink(ThetaBook)); + getitem ThetaBook, 1; + setq1 NivalisQuest_BlueSagePagefinder, 1; + // Get any experience below the 60 failed pages threshold + // But you'll get only ~1% per success because it took you no page + // And you'll always get at least 1% bonus for... reasons. + .@x=60-.@qx; + .@x+=1; + getexp 175*.@x, 10*.@x; + break; + } + + } while (countitem(SpellBookPage)); + return; +} + +OnInit: + .bsId=BS_NPC07; + .sex=G_MALE; + .distance=5; + npcsit; + end; +} + + diff --git a/npc/020-7-1/henriikka.txt b/npc/020-7-1/henriikka.txt new file mode 100644 index 0000000..2b86634 --- /dev/null +++ b/npc/020-7-1/henriikka.txt @@ -0,0 +1,88 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Responsible for recovering the lost pages (SpellBookPage) +// helperBookpages* +// +// NivalisQuest_BlueSagePagefinder STRUCTURE +// FIELD 1: +// MAIN STATE +// FIELD 2: +// nº of pages found (0~31) +// FIELD 3: +// nº of duplicates found (for randomness control - capped at 60) + +020-7-1,33,39,4 script Henriikka NPC_BLUESAGEWORKER_FA,{ + function askQuestion; + mesc l("You see a helper looking through some papers that have a strange smell."); + askQuestion(); + close; + +function askQuestion { + next; + if (.@qt >= 255) { + mesn; + mesq l("I'm so glad we didn't have to go out to hunt the slimes! Thank you!"); + } else if (.@qt) { + mesn; + mesq l("If you get any bookpages from the slimes, bring them to Ensio. Thanks for your help!"); + } else { + mesn; + mesc l("*sighs*"); + mesq l("Oh, hey. Welcome to the library, or what's left of it."); + } + + // Mainframe Loop + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + .@qt=getq2(NivalisQuest_BlueSagePagefinder); + next; + mes ""; + select + rif(.@qt < 255, "What are you doing?"), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("We're trying to repair the books by collecting the ripped out bookpages and sorting them and copying them for new books. It's a lot to do, and after being eaten by a slime they stink!"); + next; + mesn; + mesq l("And a lot of pages are missing, since most of the slimes escaped. I suppose we'll have to go out and hunt them once we're done here. Unless someone else hunt them for us."); + if (!.@qt) { + next; + mesn; + mesq l("Well, these slimes are dangerous, but if you find some pages, be sure to bring them to Ensio. This will help us a lot. Eh, if you're interested in helping, that is. %%1"); + } + break; + case 2: + mesn; + mesq l("With a mask? I don't really remember... We have so many visitors. Though I suppose someone wearing a mask would be noticeable... But I'm so worn out from the past few days that I'm just glad I can even recall my own name! Sorry."); + break; + case 3: + mesn; + mesq l("Oh, I never really thought about that. He was the one who failed the sealing, right? But I heard it's a quite difficult spell, so I suppose this could've happened to any mage. I don't know. Why are you asking such difficult questions?"); + next; + mesn; + mesq l("I need to go on with sorting the bookpages now."); + break; + } + } while (@menu != 4); + close; +} + +OnInit: + .sex=G_FEMALE; + .distance=5; + npcsit; + end; +} + + diff --git a/npc/020-7-1/janika.txt b/npc/020-7-1/janika.txt new file mode 100644 index 0000000..47c9c38 --- /dev/null +++ b/npc/020-7-1/janika.txt @@ -0,0 +1,101 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Workers which produces pages +// helperM* + +// NivalisQuest_BlueSagePagemaker STRUCTURE +// FIELD 1: +// MAIN STATE +// FIELD 2: +// BOOK MAKING QUEST +// 1 - Illustrations delivered (BS_PMINK) +// 2 - Bindings delivered (BS_PMBINDING) +// 4 - Pages delivered (BS_PMPAGE) +// 8 - Glue delivered (BS_PMGLUE) +// =15: All items delivered + +020-7-1,44,56,0 script Janika NPC_BLUESAGEWORKER_FA,{ + function askQuestion; + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + mesn; + if (.@qt & BS_PMBINDING) + mesq l("Thanks for the Silk Cocoons. With these the new books are going to be exquisite."); + else + mesq l("This is going to be difficult... oh, hello. I'm working on recreating some of the books that were destroyed."); + + // Begin here + askQuestion(); + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + mes ""; + mesn; + mesq l("I need @@/@@ @@ for the book covers and binding of some of the more valuable books we're trying to recreate.", countitem(SilkCocoon), 60, getitemlink(SilkCocoon)); + next; + mesn; + mesq l("You wouldn't happen to have them, would you?"); + if (countitem(SilkCocoon) < 60) + close; + next; + if (askyesno() == ASK_NO) { + mes ""; + mesn; + mesq l("That's a pity."); + close; + } + mes ""; + + delitem SilkCocoon, 60; + setq2 NivalisQuest_BlueSagePagemaker, .@qt|BS_PMBINDING; + getexp 3535, 215; // 20% from references, rounded up. It's part of main story. + // is present. REMEMBER THIS IS A LEVEL 36/16 QUEST, REGARDLESS IF EVERYONE DECIDES + // TO DO IT AT LEVEL 40. Exp reward will not change. It's main story, too. + // Besides, it gives Job Experience, which is not common. + mesn; + mesq l("Excellent! This is exactly what I need. Thanks a lot. I'll tell Nikolai about your generosity."); + close; + +function askQuestion { + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + next; + select + rif(!(.@qt & BS_PMBINDING), l("Can I help you?")), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye."), l("Good luck.")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("Oh, I know who you mean! That impertinent person came over in the workshop area and fiddled about with all kinds of things here! Such a rude person! Didn't he understand that we had delicate things going on here? We had to send him back to the library area several times."); + next; + mesn; + mesc l("She shakes her head."); + mesq l("Sometimes I think it'd be better not to allow visitors here. But Nikolai set a high value on keeping contact with the population. Politics."); + if (!(.@q2 & .bsId)) + setq2 NivalisQuest_BlueSage, .@q2 | .bsId; + break; + case 3: + mesn; + mesq l("Mh, I don't know him closely."); + break; + case 4: + close; + } + } while (@menu != 1); + return; +} + +OnInit: + .bsId=BS_NPC02; + .sex=G_FEMALE; + .distance=5; + end; +} + diff --git a/npc/020-7-1/janitors.txt b/npc/020-7-1/janitors.txt new file mode 100644 index 0000000..c7ae6db --- /dev/null +++ b/npc/020-7-1/janitors.txt @@ -0,0 +1,82 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// In charge to clear the mess +// helperCleaning* + helperJanitor + +// Quest: NivalisQuest_BlueSageSlimes +// 1: Acception state (0 - Not accepted, 1 - Accepted, 2 - Complete) +// 2: Killed Slimes Control +// 3: Killed Nests Control + +020-7-1,79,70,0 script Arvo NPC_BLUESAGEWORKER_MA,{ + function askQuestion; + mesn; + mesq l("Oh, this is so much work... I can't believe they let this happen! You would think they'd be careful when playing around with such powerful forces. Hah! Big mistake!"); + askQuestion(); + close; + +function askQuestion { + next; + if (.@qt >= 255) { + mesn; + mesq l("Good work with the slimes. This will make our task much easier."); + next; + mesn; + mesc l("*sigh*"); + mesq l("It appears we were focusing too much on research and this lead to neglect of other duties of a Sage's household. This is dangerous in times of changes. I'm worried about the future."); + } else if (.@qt) { + mesn; + mesc l("*sighs*"); + mesq l("We really appreciate your help with the slimes."); + } else { + mesn; + mesq l("It's dangerous in the library right now, be careful. Do you need anything else?"); + } + + // Mainframe Loop + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + .@qt=getq3(NivalisQuest_BlueSageSlimes); + next; + mes ""; + select + rif(.@qt < BS_SNESTALL, ""), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("Yes, I remember that one! I welcomed him in the libary and showed him around. But I didn't have the impression he was really listening to what I was saying, though that's hard to tell with the mask."); + next; + mesn; + mesq l("But when I left him to look at the books on his own, he sneaked over to the workshop area. Visitors are allowed to have a look there, but only if they don't interfere with any research and experiments. But it seems he was being rather disturbing, since they sent them back to the library part."); + next; + mesn; + mesq l("I tried to keep an eye on him after that, but when I got distracted by some newly arriving visitor he went over to the workshop again! Usually I enjoy my job, because I like talking to people, but this guy... was a pain, really."); + if (!(.@q2 & .bsId)) + setq2 NivalisQuest_BlueSage, .@q2 | .bsId; + break; + case 3: + mesn; + mesq l("Peetu? That's one of the high rank helpers. As far as I know, he's capable of magic and takes part in some important researches. But I don't really know about those things."); + break; + } + } while (@menu != 4); + close; +} + +OnInit: + .bsId=BS_NPC04; + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/020-7-1/kristian.txt b/npc/020-7-1/kristian.txt new file mode 100644 index 0000000..08ea6d7 --- /dev/null +++ b/npc/020-7-1/kristian.txt @@ -0,0 +1,71 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// In charge to clear the mess +// helperCleaning* + helperJanitor + +// Quest: NivalisQuest_BlueSageSlimes +// 1: Acception state (0 - Not accepted, 1 - Accepted, 2 - Complete) +// 2: Killed Slimes Control +// 3: Killed Nests Control + +020-7-1,62,83,0 script Kristian NPC_BLUESAGEWORKER_MA,{ + function askQuestion; + mesn; + mesq l("Welcome. Please don't go deeper into the library, there are still some slimes left. But in spite of that we have made quite a bit of progress. You should have seen the mess just after most of the slimes escaped!"); + askQuestion(); + close; + +function askQuestion { + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + .@qt=getq3(NivalisQuest_BlueSageSlimes); + next; + if (.@qt >= 255) { + mesn; + mesq l("I'm so glad that you took care of the remaining slimes. Thanks! Do you need anything else?"); + } else if (.@qt) { + mesn; + mesq l("Wow, it's really generous of you to fight those slimes for us. Do you need anything else?"); + } else { + mesn; + mesq l("Do you need anything else?"); + } + mes ""; + select + rif(.@qt < BS_SNESTALL, ""), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("Oh, there was such a visitor, yes. Strange guy, and didn't really know how to behave. He sneaked into the workshop area all the time, I think he was rather curious about the things going on there."); + next; + mesn; + mesq l("But still, he can't just walk in there, fiddle with the experiments and disturb the helpers doing their work."); + if (!(.@q2 & .bsId)) + setq2 NivalisQuest_BlueSage, .@q2 | .bsId; + break; + case 3: + mesn; + mesq l("Peetu? I think he's a good guy. Very focused on his work, I think. I don't have that much to do with him, so I can't really tell."); + break; + } + } while (@menu != 4); + close; +} + +OnInit: + .bsId=BS_NPC05; + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/020-7-1/kullervo.txt b/npc/020-7-1/kullervo.txt new file mode 100644 index 0000000..5ca37bb --- /dev/null +++ b/npc/020-7-1/kullervo.txt @@ -0,0 +1,109 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Workers which produces pages +// helperM* + +// NivalisQuest_BlueSagePagemaker STRUCTURE +// FIELD 1: +// MAIN STATE +// FIELD 2: +// BOOK MAKING QUEST +// 1 - Illustrations delivered (BS_PMINK) +// 2 - Bindings delivered (BS_PMBINDING) +// 4 - Pages delivered (BS_PMPAGE) +// 8 - Glue delivered (BS_PMGLUE) +// =15: All items delivered + +020-7-1,45,31,4 script Kullervo NPC_BLUESAGEWORKER_MA,{ + function askQuestion; + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + mesn; + if (.@qt & BS_PMPAGE) + mesq l("Very well, now I can create more paper."); + else + mesq l("Oh no! What should we do now?"); + + // Begin here + askQuestion(); + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + mes ""; + mesn; + mesq l("We're nearly out of paper. The new books require so much material... And it's not easy to get Reed Bundles to create new paper."); + next; + mesn; + mesq l("I only need @@/@@ @@ to make enough magic paper for the books... Do you, perchance, have them with you?", countitem(ReedBundle), 15, getitemlink(ReedBundle)); + if (countitem(ReedBundle) < 15) + close; + next; + if (askyesno() == ASK_NO) { + mes ""; + mesn; + mesq l("Well, I guess I was thinking too high of a random adventurer like yourself..."); + close; + } + mes ""; + + delitem ReedBundle, 15; + setq2 NivalisQuest_BlueSagePagemaker, .@qt|BS_PMPAGE; + getexp 3535, 215; // 20% from references, rounded up. It's part of main story. + // is present. REMEMBER THIS IS A LEVEL 36/16 QUEST, REGARDLESS IF EVERYONE DECIDES + // TO DO IT AT LEVEL 40. Exp reward will not change. It's main story, too. + // Besides, it gives Job Experience, which is not common. + mesn; + mesq l("Thank you! That's very generous of you."); + close; + +function askQuestion { + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + next; + select + rif(!(.@qt & BS_PMPAGE), l("What's wrong?")), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye."), l("Good luck.")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("Oh, yes. There was such a guy, shortly before the accident I think. He sneaked into the workshop several times, sticking his nose into stuff that shouldn't be his business. We had to send him back to the library area at least three times."); + next; + mesn; + mesq l("Maybe he was confused, not sure. He talked very strangely."); + if (!(.@q2 & .bsId)) + setq2 NivalisQuest_BlueSage, .@q2 | .bsId; + break; + case 3: + mesn; + mesq l("Are you asking because people say it's his fault? Listen, I've known him for many years now. We started our service here about the same time. We share our room and are really close friends."); + next; + mesn; + mesq l("And in all these years, I've never seen him messing up anything important. He's a perfectionist. He isn't satisfied with anything less than the best possible result when doing his work."); + next; + mesn; + mesq l("I don't understand what went wrong with that spell, but it just has to have another cause than Peetu. I'm worried about him. He must feel very miserable. But I can't leave my duty here."); + if (!(.@q3 & .bsId)) + setq3 NivalisQuest_BlueSage, .@q3 | .bsId; + break; + case 4: + close; + } + } while (@menu != 1); + return; +} + +OnInit: + .bsId=BS_NPC03; + .sex=G_MALE; + .distance=5; + npcsit; + end; +} + diff --git a/npc/020-7-1/mirjami.txt b/npc/020-7-1/mirjami.txt new file mode 100644 index 0000000..d4b1750 --- /dev/null +++ b/npc/020-7-1/mirjami.txt @@ -0,0 +1,91 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// In charge to clear the mess +// helperCleaning* + helperJanitor + +// Quest: NivalisQuest_BlueSageSlimes +// 1: Acception state (0 - Not accepted, 1 - Accepted, 2 - Complete) +// 2: Killed Slimes Control +// 3: Killed Nests Control + +020-7-1,40,78,0 script Mirjami NPC_BLUESAGEWORKER_FA,{ + function askQuestion; + mesn; + mesq l("Where is it? Everything's upside down. These terrible slimes."); + askQuestion(); + close; + +function askQuestion { + next; + .@qt=getq3(NivalisQuest_BlueSageSlimes); + if (.@qt >= BS_SNESTALL) { + mesn; + mesq l("I heard you fought the slimes that were still roaming between the bookshelves. Thank you!"); + next; + mesn; + mesq l("I was a bit worried that they might ruin our efforts by messing everything up again after we just cleaned."); + } else { + mesn; + mesq l("I'm just searching for some cleaning supplies. They must be somewhere around here, but everything went upside down when the slimes escaped."); + next; + mesn; + mesq l("Maybe they're in the library, but it is too dangerous there right now..."); + } + + // Mainframe Loop + do { + .@q=getq(NivalisQuest_BlueSageSlimes); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + .@qt=getq3(NivalisQuest_BlueSageSlimes); + next; + mes ""; + select + rif(.@qt == BS_SNESTALL && .@q == 1, "Did you found the supplies yet?"), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 1: + //mesq l("Not yet, blame Jesusalva, your reward was with them"); + inventoryplace AlchemyBlueprintA, 1, AncientBlueprint, 1; + mesn; + mesq l("Yeah... I also found a few old blueprints. We don't need this crap, you can put it on your @@.", getitemlink(AlchemyBlueprintA), getitemlink(RecipeBook)); + next; + getitem AlchemyBlueprintA, 1; + getitem any(AncientBlueprint, AlchemyBlueprintA, EquipmentBlueprintA), 1; + setq1 NivalisQuest_BlueSageSlimes, 2; + next; + mesn; + mesq l("You can be thankful later. Aren't you excited to see what exactly was on that blueprint? Go on, enjoy it! %%G"); + break; + case 2: + mesn; + mesq l("Ohh, I think I know who you mean. That was a strange guy. He always came over here in the workshop, said he's very interested in the research. I suppose that's ok, but he didn't keep his hands to himself, and touched some experiments and I caught him near some storage racks."); + next; + mesn; + mesq l("Visitors should keep away from those!"); + if (!(.@q2 & .bsId)) + setq2 NivalisQuest_BlueSage, .@q2 | .bsId; + break; + case 3: + mesn; + mesq l("Oh, eh, why are you asking me? I've only been here a short while and don't really know all of the people well enough yet."); + break; + } + } while (@menu != 4); + close; +} + +OnInit: + .bsId=BS_NPC06; + .sex=G_FEMALE; + .distance=5; + end; +} + diff --git a/npc/020-7-1/nea.txt b/npc/020-7-1/nea.txt new file mode 100644 index 0000000..90c84f3 --- /dev/null +++ b/npc/020-7-1/nea.txt @@ -0,0 +1,78 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// In charge to clear the mess +// helperCleaning* + helperJanitor + +// Quest: NivalisQuest_BlueSageSlimes +// 1: Acception state (0 - Not accepted, 1 - Accepted, 2 - Complete) +// 2: Killed Slimes Control +// 3: Killed Nests Control + +020-7-1,53,39,0 script Nea NPC_BLUESAGEWORKER_FA,{ + function askQuestion; + mesn; + mesq l("Oh, hello. If I were you I wouldn't go deeper into the library. There are still some of those monsters left. Slipping between the bookshelves."); + askQuestion(); + close; + +function askQuestion { + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + .@qt=getq3(NivalisQuest_BlueSageSlimes); + next; + if (.@qt >= 255) { + mesn; + mesq l("I feel much safer now that you defeated the slimes. Thanks so much! Do you need anything else?"); + } else if (.@qt) { + mesn; + mesq l("\"Oh, I'm so glad you're taking care of those scary slimes. You're so brave! How many of them are still left, do you think? Will it be safe to go there soon?"); + } else { + mesn; + mesc l("She shudders."); + mesq l("And they can explode! Nothing in the world could make me go in there. I'll just do my work right here, clean the floor and tidy up the books, and I'm keeping my eyes open in case they get over here."); // Cleaning the floor, aham. ¬.¬ + next; + mesn; + mesq l("Do you need anything else?"); + } + mes ""; + select + rif(.@qt < BS_SNESTALL, ""), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("With a mask? Mh, I think I know who are you talking about. I vaguely remember that there was someone with a mask... but I can't recall any details, I wasn't really paying attention."); + close; + break; + case 3: + mesn; + mesq l("Petu? Yeah, he's the kind of person who always wants to do things perfectly. That makes it a bit difficult to work with him, since his expectations for others are as high as for himself."); + next; + mesn; + mesq l("I really wonder how that accident could've happened. I'd have never expected him to mess something up like that."); + if (!(.@q3 & .bsId)) + setq3 NivalisQuest_BlueSage, .@q3 | .bsId; + close; + break; + } + } while (@menu != 4); + close; +} + +OnInit: + .bsId=BS_NPC05; + .sex=G_FEMALE; + .distance=5; + npcsit; + end; +} + diff --git a/npc/020-7-1/oskari.txt b/npc/020-7-1/oskari.txt new file mode 100644 index 0000000..652c2de --- /dev/null +++ b/npc/020-7-1/oskari.txt @@ -0,0 +1,233 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Helper boss +// +// NivalisQuest_BlueSage STRUCTURE +// FIELD 1: +// INVESTIGATION +// 1 - STBY OUTSIDE +// 2 - ACCESS GRANTED +// 3 - QUEST ASSIGNED BY PEETU - talk to Oskari (and others) +// 4 - Oskari is OK with peetu, but wanna hear from others. He also sends you +// to ask what Peetu happened +// 5 - Adultered ingredients seems the cause, report to Elias +// 6 - Elias is now worried about a visitor. Ask people about and report. +// 7 - If everyone found the visitor, confirm Elias the worries +// 8 - Elias sent you to Oskari to inform the issue. Blue Sage probably knew all along. +// will not advance unless everyone thinks Peetu is good. +// 9 - Oskari accepts the cause. Tells to report Peetu that it probably was +// a saboutage, to check if the Silk Cocoon really was there. +// 10 - Peetu confirmed the saboutage. Report to Blue Sage. +// 11 - Blue Sage accepted the evidence, and explains about other sages issues. +// It's not known who or what is behind this. He excuses for making you waste +// your time. He asks you to return to him later, as he needs to write letters. +// 12 - QUEST COMPLETE - You collected your reward +// Also picked up a letter for Frostia Mayor, about the incident (Main Story). +// FIELD 2: +// Bitwise (BS_QVISITOR) +// FIELD 3: +// Bitwise (BS_QHELPER) + +020-7-1,122,29,0 script Oskari NPC_BLUESAGEWORKER_FB,{ + function pIdle; + + function pPeetu; // Peetu sent you to them + function pElias; // Elias sent you to them + function pComplete; + + .@q=getq(NivalisQuest_BlueSage); + switch (.@q) { + case 2: + pIdle(); + break; + case 3: + pPeetu(); + break; + case 4: + case 5: + case 6: + case 7: + pIdle(); + mesc l("I probably should talk to everyone else, and assess better the situation, before bothering Oskari."); + break; + case 8: + pElias(); + break; + case 9: + pIdle("Peetu"); // Skippers... + break; + case 10: + case 11: + pIdle(); + break; + case 12: + pComplete(); + break; + default: + teleporthome(); + die(); + end; + break; + } + close; + + +// Here we begin +function pIdle { + if (getarg(0, "") != "") { + mesn; + mesq l("What are you doing? Stop wasting my time and go talk to @@!", getarg(0, "##1##BBUGGY FLYING COW##b##0")); + return; + } + mesn; + mesq l("You have to excuse me, as you might have heard we're having some trouble at the moment and I, as Chief of Nikolai's household staff, have a lot to do."); + return; +} + +// Peetu Question +function pPeetu { + mesn; + mesq l("You have to excuse me, as you might have heard we're having some trouble at the moment and I, as Chief of Nikolai's household staff, have a lot to do."); + next; + select + l("Ok, see you."), + l("Peetu asked me to talk to you about his mishap."); + mes ""; + if (@menu == 1) + close; + mesn; + mesq l("So, what did he said?"); + next; + mesn strcharinfo(0); + mesq l("He is blaming himself and is afraid of losing his job."); + next; + mesn; + mesq l("Well... Can't say I'm surprised. He is very perfectionist. Putting on himself the blame is something he would do."); + next; + mesn; + mesq l("Well, all humans does mistakes... Wait, he is an elf. So, everyone commit mistakes. Anyway..."); + next; + mesc l("@@ stops, and keep silent for a while, thinking, before continuing.", .name$); + next; + mesn; + mesq l("You know, he is very experienced and reliable. He might look and act like a kid, but he is at least a century old."); + next; + mesn; + mesq l("This whole situation is very disquieting, not to say suspicious. There might be something more to it that we're not seeing."); + next; + mesn; + mesq l("It might be just my gut instinct, but... Would you be willing to talk with Peetu and the other helpers, investigating what actually happened?"); + next; + mesn; + mesq l("The whole team is very distressed, too. Could you take the opportunity to see if the team is putting the blame on Peetu? Thanks in advance."); + next; + mesn; + mesq l("Please tell Peetu I do not plan in firing him, it should calm him down. And please carry out an investigation, don't disrupt my work until you're done."); + setq1 NivalisQuest_BlueSage, 4; + close; +} + +// Elias Question +function pElias { + mesn; + mesq l("Have you talked to Peetu co-workers about their opinion yet?"); + next; + + // cannot advance otherwise + .@q=getq3(NivalisQuest_BlueSage); + if (.@q != BS_NPCALL) { + mesn; + mesq l("I need to know if someone is blaming him. It is important."); + close; + } + select + l("Not yet."), + l("Yes, everyone thinks he is competent and Elias have a report of a masked man."); + mes ""; + if (@menu == 1) + close; + + mesn; + mesq l("With a mask you say? This reminds me... wait a moment."); + next; + mes l("... .... ...."); + next; + mesn; + mesq l("Mh... yes. It makes sense. Listen. This is a secret, but it might have been.... @@.", b(l("Sabotage"))); + next; + mesn; + mesq l("I need a favor from you. Go talk to Peetu. Ask him to confirm if there was silk cocoon in the ingredients. Janika reported me that we've ran out of it two days ago."); + setq1 NivalisQuest_BlueSage, 9; + close; +} + +// We're done. +function pComplete { + mesn; + mesq l("Thanks again for your investigations. It was a great help."); + close; +} + + +OnTimer1000: + domovestep; + +OnInit: + // Long Movement is difficult for Hercules Engine + initpath "move", 122, 29, + "dir", UP, 0, + "wait", 15, 0, + "dir", LEFT, 0, + "move", 109, 29, + "move", 79, 29, + "move", 59, 29, + "move", 56, 29, + "wait", 3, 0, + "dir", RIGHT, 0, + "wait", 7, 0, + "dir", LEFT, 0, + "wait", 7, 0, + "dir", DOWN, 0, + "move", 55, 48, + "wait", 3, 0, + "dir", RIGHT, 0, + "wait", 10, 0, + "dir", DOWN, 0, + "wait", 10, 0, + "dir", LEFT, 0, + "wait", 3, 0, + "move", 26, 46, + "dir", UP, 0, + "wait", 4, 0, + "dir", RIGHT, 0, + "wait", 4, 0, + "dir", DOWN, 0, + "wait", 2, 0, + "move", 26, 57, + "wait", 2, 0, + "move", 25, 77, + "wait", 3, 0, + "dir", RIGHT, 0, + "wait", 10, 0, + "move", 26, 57, + "move", 26, 46, + "move", 55, 48, + "move", 56, 29, + "wait", 3, 0, + "dir", RIGHT, 0, + "wait", 1, 0, + "move", 59, 29, + "move", 79, 29, + "move", 109, 29; + // And go back to Peetu, which is very far away, 'cause I'm lazy + initialmove; + initnpctimer; + + .sex=G_FEMALE; + .distance=5; + end; +} + diff --git a/npc/020-7-1/peetu.txt b/npc/020-7-1/peetu.txt new file mode 100644 index 0000000..7a9387e --- /dev/null +++ b/npc/020-7-1/peetu.txt @@ -0,0 +1,343 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Peetu, gifted since birth, only 1 NPC each 100 are born mages +// +// NivalisQuest_BlueSage STRUCTURE +// FIELD 1: +// INVESTIGATION +// 1 - STBY OUTSIDE +// 2 - ACCESS GRANTED - But Peetu is crying too much +// 3 - Peetu was calmed down, go talk to Oskari about him +// 4 - Oskari is OK with peetu, but wanna hear from others. He also sends you +// to ask what Peetu happened +// 5 - Adultered ingredients seems the cause, report to Elias +// 6 - Elias is now worried about a visitor. Ask people about and report. +// 7 - If everyone found the visitor, confirm Elias the worries +// 8 - Elias sent you to Oskari to inform the issue. Blue Sage probably knew all along. +// will not advance unless everyone thinks Peetu is good. +// 9 - Oskari accepts the cause. Tells to report Peetu that it probably was +// a saboutage, to check if the Silk Cocoon really was there. +// 10 - Peetu confirmed the saboutage. Report to Blue Sage. +// 11 - Blue Sage accepted the evidence, and explains about other sages issues. +// It's not known who or what is behind this. He excuses for making you waste +// your time. He asks you to return to him later, as he needs to write letters. +// 12 - QUEST COMPLETE - You collected your reward +// Also picked up a letter for Frostia Mayor, about the incident (Main Story). +// FIELD 2: +// Bitwise (BS_QVISITOR) +// FIELD 3: +// Bitwise (BS_QHELPER) + +020-7-1,122,27,2 script Peetu NPC_BLUESAGEWORKER_MC,{ + function pWaiting; + + function pIntro; + function pContinue; + function pReflection; + function pInvestigation; + function pComplete; + + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + + if (.@q <= 4) + npctalk3 any(l("*sob sob*"), l("*crying*")); + + switch (.@q) { + case 2: + pIntro(); + break; + case 3: + pWaiting("Oskari"); + break; + case 4: + pReflection(); + break; + case 5: + pWaiting("Elias"); // In case they skipped all dialogs and are now lost. + break; + case 6: + case 7: + case 8: + pWaiting("Oskari"); + break; + case 9: + pInvestigation(); + break; + case 10: + case 11: + pWaiting("Blue Sage"); + break; + case 12: + pComplete(); + break; + default: + teleporthome(); + die(); + end; + break; + } + close; + +// Here we begin +function pWaiting { + .@name$=getarg(0, "##1##BBUG, REPORT ME: THE FLYING COW##0##b"); + mesn; + mesc l("*sniff sniff*"); + mesq l("I'm waiting for @@ feedback... Please go talk to them! %%S", .@name$); + close; +} + +// Peetu is too upset with failing (yeah, he is that kind of perfectionist here). +// We should find a way to calm him down. +function pIntro { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + mesn; + mesc l("*sniff sniff*"); + next; + select + l("Hello Peetu."), + rif(.@q2 >= 1, l("[Give him some salty Sea Drops?]")), + rif(.@q2 >= 2, l("[Give him some tasty Chocolate Bar?]")), + rif(.@q2 >= 3, l("[Give him a Mouboo Figurine to play with?]")), + rif(.@q2 >= 4, l("[Slap his hands to surprise him and get his attention?]")), + rif(.@q2 >= 5, l("SHUT UP PEETU, I'M ALREADY TIRED OF LISTENING YOUR CRIES!")), + rif(.@q2 >= 5, l("I give up. You're hopeless.")), + rif(.@q2 >= 5, l("Have you cried enough?")), + rif(.@q2 >= 5, l("[Pat his shoulder and say everything will be fine.]")), + rif(.@q2, l("Hi Peetu, are you calmer now?")); + mes ""; + switch (@menu) { + case 1: + if (.@q2 < 1) + setq2 NivalisQuest_BlueSage, 1; + break; + case 2: + if (!countitem(SeaDrops)) { + mesc l("You don't have @@.", getitemlink(SeaDrops)); + close; + } + if (.@q2 < 2) + setq2 NivalisQuest_BlueSage, 2; + break; + case 3: + if (!countitem(ChocolateBar)) { + mesc l("You don't have @@.", getitemlink(ChocolateBar)); + close; + } + if (.@q2 < 3) + setq2 NivalisQuest_BlueSage, 3; + break; + case 4: + if (!countitem(MoubooFigurine)) { + mesc l("You don't have @@.", getitemlink(MoubooFigurine)); + close; + } + if (.@q2 < 4) + setq2 NivalisQuest_BlueSage, 4; + break; + case 5: + mesn; + mesq l("AH!"); + next; + mesc l("You seem to have gotten Peetu's attention for a while."); + next; + if (.@q2 < 5) + setq2 NivalisQuest_BlueSage, 5; + break; + case 9: + pContinue(); + break; + } + mesn; + mes l("WAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHH"); + mesc l("@@ burst in tears.", .name$); + next; + mesn strcharinfo(0); + mesc l("I probably should look in a way of calming him down."); + close; +} + +// He is calmer now +function pContinue { + mesn; + mesc l("*sniff sniff*"); + next; + select + l("What have happened? Why are you crying?"), + l("Maybe I can help to ease your pain?"), + l("Don't cry any further. I am here to help."), + l("Please tell me calmly what happened so I can help."); + mes ""; + mesn; + mesq l("It's *sniff* It's all my fault... *sniff*"); + next; + mesn; + mesc l("*sniff* *sniff*"); + mesq l("It was my job to seal the slimes away for the night, but I somehow messed it up!"); + next; + mesn; + mesc l("*tears weeling up*"); + mesq l("And now I'm going to lose my job and I'll have to leave here and no other sage would give me a new appointment and I don't know what else to do!"); + next; + mesc l("@@ latches onto you and starts sobbing on your shoulder.", .name$); + select + l("[Try to console him]"), + l("[Shake him and tell him to pull himself together]"), + l("[Push him away from you and leave]"); + mes ""; + if (@menu == 3) { + setq2 NivalisQuest_BlueSage, 4; + close; + } + mesc l("@@ calms a bit.", .name$); + next; + mesn; + mesq l("Uh. You're probably right. I'm very sorry. I'm just... You know, I wanted to become a scholar of the sages, studying and... Oh, how could I mess that up? Did you see the library? It caused so much damage! What else should they do other than kick me out?"); + next; + mesn; + mesq l("I would kick me out myself! I'm such a failure! A complete disaster! %%i"); + next; + select + l("Maybe you should talk to your chief about that?"), + l("It doesn't make much sense to draw overhasty conclusions."), + l("Do you have a clue about what went wrong?"); + mes ""; + mesn; + mesq l("Well ... but ... I mean ... I don't know ... "); + next; + mesn; + mesq l("Would you ... uhm ... would you talk to Chief Oskari for me? And ask her what she plans to do about me? I... I just don't feel capable of doing that myself right now. I'll try to pull myself together in the meanwhile."); + setq NivalisQuest_BlueSage, 3, 0; + close; +} + +// Report that Oskari is not planning to fire him (yet), and is trying to understand +// what went wrong so it do not repeat. +function pReflection { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + select + l("Good news - Oskari doesn't plans in firing you (yet)!"), + l("I'm here to investigate what exactly happened so this doesn't happens again."), + l("Everyone commit mistakes and Oskari was very understanding. I need to do some questions about the incident though."), + l("Oskari have a good opinion of you. Can you tell what exactly happened?"); + mes ""; + mesn; + mesc l("*sniff sniff*"); + mesq l("So... You see... I usually pay a lot of attention to my work. Especially when doing something as delicate as sealing away the slimes for the night."); + next; + mesn; + mesq l("We're doing researches on slimes, the explosive ones. You probably saw when they explode, they damage anything close to them, friendly or not, right?"); + next; + mesn; + mesq l("Nikolai said it was important, so we keep a few of them locked in the basement. I apply a spell to seal them so they don't wander around, explode around, or... *sniff*..."); + next; + mesn; + mesq l("It's a very delicate spell. I usually start to prepare it while the helpers are still working on their research and experiments. That way, when they're done, they can just come and place the slimes under the seal."); + next; + mesn; + mesq l("So... @@ That night, I cast it as usual. Everything seemed alright so I went to bed.", "##9*sniff*##0"); + next; + mesn; + mesc l("Eyes grows!"); + mesq l("I woke up in a shake! I felt... Oh, it's hard to describe... As if the spell suddenly started inflating, getting bigger... bigger... weaker... weaker..."); + next; + mesn; + mesc l("*snap fingers*"); + mesq l("And then, it was no more! It vanished! In a matter of minutes the slimes were all over the library. A good thing they are slow, none escaped to the town."); + next; + mesn; + mesq l("Of course I hurried there as fast as I could, yelling to wake up everyone, but that caused so much confusion that we fell over each other in the corridor and when we finally reached the library, the slimes were already spread all over the room, eating or exploding the books."); + next; + select + l("*snooze*"), + l("And have you thought in the cause?"); + mes ""; + if (@menu == 1) + clear; + mesn; + mesq l("Now that I've described the spells to you, I'm sure I cast them correctly. But... I think there is a way to have these effects. It's silly though."); + next; + mesn; + mesq l("I never tried it, because it makes no sense to do that, but theoretically adding some Silk Cocoons could have such an effect. Uh... but that should not happen."); + next; + mesn; + mesq l("Could you please ask @@ about it? The ingredients... It would still be my fault...", b(l("Elias"))); + next; + mesc l("@@ is on the verge of crying again. Better leave out and look for Elias.", .name$); + setq1 NivalisQuest_BlueSage, 5; + close; +} + +// Peetu hurries back to check what happened. Wait 3 minutes in the library. +// He'll then say that in fact there was silk cocoon +function pInvestigation { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + mesn; + mesc l("*sniff sniff*"); + mesq l("So... Anything new on my situation?"); + next; + select + l("Not yet."), + l("Yes, you'll be fired."), + l("Yes, Chief Oskari suspects a sabotage."); + mes ""; + switch (@menu){ + case 1: + close; + case 2: + mesn; + mes l("WAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHH"); + mesc l("@@ burst in tears.", .name$); + next; + percentheal 0, -100; + mesn strcharinfo(0); + mesc l("Meh, telling him it was a lie is not going to help."); + close; + } + mesn; + mesc l("*eyes widen up*"); + mesq l("A... A sabotage?! O.o"); + next; + mesn strcharinfo(0); + mesq l("Keep your voice down! And yes. Can you confirm @@ was the cause?", getitemlink(SilkCocoon)); + next; + mesn; + mesq l("...Yes. Hold tight."); + next; + mes "... ... ..."; + next; + mesn; + mesq l("...Yes. It was there. Please report to Blue Sage at once."); + setq1 NivalisQuest_BlueSage, 10; + close; +} + +// The crime was "solved" +function pComplete { + mesn; + mesq l("Oh, hey, welcome back, @@! Thanks for all your help!", strcharinfo(0)); + next; + mesn; + .@subject$=any(l("town finances"), l("house finances"), l("town damage by monsters"), l("library damage"), l("supply report"), l("magic book"), l("town overview")); + mesq l("I'm currently going over some of the household paperwork. Right now I'm inspecting the @@. The work never stops!", .@subject$); + close; +} + +OnInit: + .sex=G_MALE; + .distance=5; + npcsit; + end; +} + diff --git a/npc/020-7-1/politics.txt b/npc/020-7-1/politics.txt new file mode 100644 index 0000000..b6ca116 --- /dev/null +++ b/npc/020-7-1/politics.txt @@ -0,0 +1,58 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Town Administrator file, see npc/functions/politics.txt +// User variables: +// #POL_APPLYWEEK = Week of last application +// #POL_VOTEDAY = Day of last vote + +020-7-1,37,61,0 script Nivalis Office NPC_POLITICS,{ +do +{ + mesc ".:: "+l("Nivalis Townhall")+" ::.", 2; + mesc l("Current Town Administrator: ")+$NIVAL_MAYOR$, 3; + POL_TownInfo("NIVAL"); + mesc l("Application fee: @@ GP", .applytax); + next; + select + l("Information"), + rif(strcharinfo(0) == $NIVAL_MAYOR$, l("Manage Town")), + rif(#POL_APPLYWEEK != gettimeparam(GETTIME_WEEKDAY), l("Apply for the office!")), + l("View Candidate List and cast a vote"), + l("[Quit]"); + + switch (@menu) { + case 1: + POL_Information(); + break; + case 2: + POL_Manage("NIVAL"); + break; + case 3: + // array_push might be too sensible for getd/setd + if (Zeny < .applytax) + break; + Zeny-=.applytax; + $NIVAL_MONEY+=.applytax; + #POL_APPLYWEEK=gettimeparam(GETTIME_WEEKDAY); + array_push($NIVAL_CANDIDATE$, strcharinfo(0)); + array_push($NIVAL_VOTES, 0); + mesc l("Application successful!"), 3; + next; + break; + case 4: + POL_Candidate("NIVAL"); + break; + default: + close; + } +} while (true); +end; + +OnInit: + .applytax=100; + .distance=4; + end; +} + diff --git a/npc/020-7-1/pyry.txt b/npc/020-7-1/pyry.txt new file mode 100644 index 0000000..5ea8e0e --- /dev/null +++ b/npc/020-7-1/pyry.txt @@ -0,0 +1,111 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// In charge to clear the mess +// helperCleaning* + helperJanitor + +// Quest: NivalisQuest_BlueSageSlimes +// 1: Acception state (0 - Not accepted, 1 - Accepted, 2 - Complete) +// 2: Killed Slimes Control +// 3: Killed Nests Control + +020-7-1,50,63,6 script Pyry NPC_BLUESAGEWORKER_MB,{ + function askQuestion; + .@qt=getq3(NivalisQuest_BlueSageSlimes); + mesn; + if (.@qt == BS_SNESTALL) + mesq l("It seems all of the slimes are gone now. I'm so glad! Thanks for your help. I mentioned your assistance to Nikolai."); + else if (.@qt > 0) + mesq l("It's very brave of you to fight against those slimes deeper in the library. Remember that you have to kill the entire group to prevent them from breeding. And do it @@!", b(l("Alone"))); + else + mesq l("Welcome to the library of Sage Nikolai. I'm very sorry, but as you see it's not in a good shape at the moment. Most of the books were eaten by the slimes or damaged and we're working hard on cleaning up the mess."); + + askQuestion(); + close; + +function askQuestion { + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + .@qo=getq(NivalisQuest_BlueSageSlimes); + .@qt=getq3(NivalisQuest_BlueSageSlimes); + next; + mes ""; + select + rif(.@qt < BS_SNESTALL, "What are you doing?"), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("Resting. I've been slaying slime nests the whole day."); + next; + if (.@qo) { + mesn; + mesq l("There are some slimes around in the deeper parts of the library. Remember to kill them alone, and beware, if they kill themselves it won't count."); + } else { + mesn; + mesq l("Do you want to help? I promise you no reward, so you will do it of your own will."); + next; + if (askyesno() == ASK_YES) { + mesn; + mesq l("Great! Now, listen carefully: This library is laid out in nine squares."); + next; + mesn; + mesq l("You have to kill, alone, and this means without the help of the slimes either, nine nests. One in each quadrant."); + next; + mesn; + mesq l("Be careful when walking, because you may change quadrands and then... It won't count. You'll need to start over again."); + next; + mesn; + mesq l("I wish you good luck!"); + setq NivalisQuest_BlueSageSlimes, 1, 0, 0; + } else { + mesn; + mesq l("Alright. Then please don't go too deep on the library, it is dangerous."); + } + } + break; + case 2: + mesn; + mesq l("Mh, yes. I remember him. He was very interested in the library and the research we do here. He had an unusual behaviour and appearance. But, well, that isn't a reason to refuse someone, right?"); + break; + case 3: + mesn; + mesq l("Peetu? I have always been glad to have him on the team. He's really brought forward our work here."); + next; + mesn; + mesq l("I was very surprised when I heard that he's responsible for the failed sealing. I'd never have expected that from him."); + if (!(.@q3 & .bsId)) + setq3 NivalisQuest_BlueSage, .@q3 | .bsId; + break; + } + } while (@menu != 4); + close; +} + +OnSlimeDeath: + if (playerattached()) { + getmapxy(.@m$, .@x, .@y, 0); + // Capped at ~10% drop chance of book pages + if (rand(0,10000) <= 460+(readparam2(bLuk)*6)) + makeitem SpellBookPage, 1, .@m$, .@x, .@y; + if (rand(0,10000) <= 100) + makeitem Candy, 1, .@m$, .@x, .@y; + } + end; + +OnInit: + .bsId=BS_NPC06; + .sex=G_MALE; + .distance=5; + npcsit; + end; +} + diff --git a/npc/020-7-1/sage.txt b/npc/020-7-1/sage.txt new file mode 100644 index 0000000..6011758 --- /dev/null +++ b/npc/020-7-1/sage.txt @@ -0,0 +1,322 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Blue Sage +// +// Minimum level: 36 (implicit) -> 40 (presumed at quest end) +// Minimum jblvl: 16 (implicit) -> 20 (presumed at quest end) +// +// NivalisQuest_BlueSage STRUCTURE +// FIELD 1: +// INVESTIGATION +// 1 - STBY OUTSIDE +// 2 - ACCESS GRANTED +// 3 - QUEST ASSIGNED BY PEETU - talk to Oskari (and others) +// 4 - Oskari is OK with peetu, but wanna hear from others. He also sends you +// to ask what Peetu happened +// 5 - Adultered ingredients seems the cause, report to Elias +// 6 - Elias is now worried about a visitor. Ask people about and report. +// 7 - If everyone found the visitor, confirm Elias the worries +// 8 - Elias sent you to Oskari to inform the issue. Blue Sage probably knew all along. +// 8/9 WARNING: will not advance unless everyone thinks Peetu is good. +// 9 - Oskari accepts the cause. Tells to report Peetu that it probably was +// a saboutage, to check if the Silk Cocoon really was there. +// 10 - Peetu confirmed the saboutage. Report to Blue Sage. +// 11 - Blue Sage accepted the evidence, and explains about other sages issues. +// It's not known who or what is behind this. He excuses for making you waste +// your time. He asks you to return to him later, as he needs to write letters. +// 12 - QUEST COMPLETE - You collected your reward +// Also picked up a letter for Frostia Mayor, about the incident (Main Story). +// FIELD 2: +// Bitwise (BS_QVISITOR) +// FIELD 3: +// Bitwise (BS_QHELPER) + +020-7-1,40,24,0 script #BlueSageCaves NPC_HIDDEN,1,0,{ +OnTouch: + .@q=getq(NivalisQuest_BlueSage); + if (.@q < 12) { + dispbottom l("The door is locked."); + } else { + warp "020-7-2", 71, 53; + } + end; +} + +020-7-1,35,59,0 script The Blue Sage NPC_BLUESAGE,{ + function nStart; + function nReport; + function nLetters; + function nCindy; + + .@cindy=($@CINDY_STATE < gettimetick(2)); + .@q=getq(NivalisQuest_BlueSage); + + // If you helped the four page makers, you receive a reward + // I know a Titanium Ingot is lame, but in TMW Org. there was no reward *at all* + .@qt=getq(NivalisQuest_BlueSagePagemaker); + if (.@qt == 0) { + .@qt2=getq2(NivalisQuest_BlueSagePagemaker); + if (.@qt2 == BS_PMALL) { + inventoryplace TitaniumIngot, 1; + mesn l("Nikolai, the Blue Sage"); + mesq l("I've heard you helped my staff to recreate some books. For that, I am grateful."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Please accept this @@ as a gratitude for your time.", getitemlink(TitaniumIngot)); + getitem TitaniumIngot, 1; + getexp 3535, 215; // The remaining 20% EXP to reach 100% =D + setq1 NivalisQuest_BlueSagePagemaker, 1; + next; + } + } + + mesn l("Nikolai, the Blue Sage"); + if (is_night()) + .@t$=l("Good evening"); + else + .@t$=l("Good morning"); + mesq .@t$ + l(", my name is Nikolai. I am a sage, and the owner of this place."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Not only that, but I am also Angela's husband and Nivalis Mayor. If you have any issues, you can go straight to me."); + if (.@cindy) { + mesc l("Nikolai takes a sweat of his head. He seems worried with Cindy."); + } + next; + select + rif(.@q < 10, l("I came here to talk about the World's Edge.")), + rif(.@q == 10, l("I came here to report... A sabotage.")), + rif(.@q == 11, l("I'm back.")), + rif(.@q >= 12, l("Can you repeat what you said before?")), + rif(.@cindy, l("Aren't you Cindy's father? Why don't you go to her rescue?")), + l("Please excuse me, Blue Sage Nikolai."); + mes ""; + switch (@menu) { + case 1: + // You must solve the issue here, first + nStart(); + break; + // Blue Sage Investigation: Report about saboutage + case 2: + nReport(); + break; + // Blue Sage Investigation: Post-Report + case 3: + if (@timed < gettimetick(2)) + nLetters(); + else + mesq l("I'm not done yet, please hold tight. It won't take long."); + break; + // Repeat about WE and AFSM + case 4: + nLetters(); + break; + // Question about Cindy + case 5: + // Quest ends at stage 12 + if (.@q != 12) { + mesn l("Nikolai, the Blue Sage"); + mesq l("The house is a mess. They need me here. Also."); + next; + } + nCindy(); + break; + } + close; + +// Report about the sabotage +function nReport { + mesc l("You explain the Blue Sage about the sabotage incident details, from the Silk Cocoon to the masked visitor."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Hm hm. Thanks for the report. Well, as you could have suspected... I knew that all along."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Listen, it was not only me who had issues with this... Masked Man. Auldsbel also had a similar issues."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("In other words, whoever they are, they're after the sages. They are not targeting just simple magical users. This might be a problem."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("I must, however, thank you. I could not just come straight up and tell that to everyone, nor simply lock the house without no reason. I'm always fair."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Your help was invaluable, my staff believed in the unbiased view of the Hurnscald Household. Alas, now Peetu is capable to work again."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("You wanted to know about the World Edge, right? The Ancient Families of the Soul Menhir, and if you're part of them..."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("I cannot answer you about yourself, but I can tell you about the Ancient Families and the World Edge. Which is classified information, by the way."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("However, you'll need to keep cooperating with me. Trust me, this incident and what you look for it is closely related."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("I'll be writing a letter to the next town you're going to visit, and also assign work to my household. You should come back later."); + + getexp 17500, 1000; + setq1 NivalisQuest_BlueSage, 11; + @timed=gettimetick(2)+30; + return; +} + +// Receive the letters and the next task along a text wall about the AFSM and WE +function nLetters { + .@q=getq(NivalisQuest_BlueSage); + inventoryplace Coal, 20; + mesn l("Nikolai, the Blue Sage"); + mesq l("So. For the info dump or text wall... I'll let you choose what you want to know, or to skip it entirely."); + + do { + next; + mesc l("What do you want to know?"); + mes ""; + select + l("What's the World Edge?"), + l("Where is the World's Edge?"), + l("What are the Ancient Families of the Soul Menhir?"), + l("About the Prophecy... What about Elves? Orcs? Redys? Etc.?"), + l("That's everything I wanted to know."); + mes ""; + switch (@menu) { + case 1: + mesn l("Nikolai, the Blue Sage"); + mesq l("World's Edge is the place where the Monster King Fortress is."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("It is the place where it all began... And I'm not talking about the Mana War."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("It is the birthplace of humans, the first place to come to existence... The World Edge. The place where humanity began, and according to the legend... The place where it shall perish."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Humans never built a settlement there. But the prophecy is there."); + break; + case 2: + mesn l("Nikolai, the Blue Sage"); + mesq l("World's Edge is an island situated northwest of here."); + break; + case 3: + mesn l("Nikolai, the Blue Sage"); + mesq l("Do you know what a Soul Menhir is?"); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("It more than just a piece of Zealite Ore... It is a part of the world's heart."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("The World's Heart is at World's Edge. The *real* Ancient Families are the ones who broke it in parts and brought it to each town."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Thanks to this, we can now respawn after death. But that's when the prophecy was told. The prophecy... Of the death of all humans. It gives me shivers."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("What you refer as Ancient Families, are probably their descendants. There was nothing special about them, other than they promised to defend mankind of their own actions."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("When they left to World Edge, they probably were looking for Mana Fragments. In other words... Soul Menhirs. Parts of the World Heart."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("I don't know why they would repeat a past mistake. The Monster King is not human anymore. I also do not know what artifact they lost. We have more questions than certainty about this."); + break; + case 4: + mesn l("Nikolai, the Blue Sage"); + mesq l("They will all perish, along most of wildlife, according to the legend passed down."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("However, the legend said there was a way to prevent this disaster. A single way... Which the Ancient Families of Soul Menhir kept a secret passed down between generations."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Knowing that would make you a descendant of them. But eh, that's asking too much. You had amnesia, right? That was on the letter."); + break; + } + } while (@menu != 5); + + // Quest complete? Don't continue. If needed, give a hint to players. + if (.@q != 11) { + mesn l("Nikolai, the Blue Sage"); + if (getq(HurnscaldQuest_Sagratha) == 1) + mesq l("I'm worried with which Sage the masked man will aim next..."); + else + mesq l("Always a pleasure to help."); + close; + } + + mesn l("Nikolai, the Blue Sage"); + mesq l("So, back to action! Finally. Your next destination is going to be Frostia Town."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Remember the masked man said he was from Frostia? Or masked woman, we don't know. You should inform their Mayor at once."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Also, I don't know what Sage will be their next target. I'm... A bit concerned with Sagratha. As she choose to live with wildlife and all, we sorta don't know a lot of what happens with her."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Sorry, I'm babbling. Deliver this letter to Frostia's King. He will know what to do and what you should do."); + next; + + getitem Coal, 20; + setq1 NivalisQuest_BlueSage, 12; + setq General_Narrator, 11; + mesn l("Nikolai, the Blue Sage"); + mesq l("Also, take this. It's time to you learn to craft your own weapon. Talk to Nicholas in Hurnscald forge to make an awesome @@. Or use it on some other craft, it is your choice.", getitemlink(Backsword)); + mesc l("Received @@ @@!", 20, getitemlink(Coal)); + mesc l("Access to basement was granted!"); + getvaultexp(10); + return; +} + +// You must solve any issue inside the household before advancing main story +function nStart { + mesn l("Nikolai, the Blue Sage"); + mesq l("Well, I would love to, but the house is a mess."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("All Peetu have been doing the past hours was crying, and crying, and crying some more. I can't barely sleep hearing his cries from my room."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Not only that, but he is the only one here besides me who can do magic. Without him, I have to work doubled."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("He should be crying in the far northeast corner of this library. Can you go there to see him?"); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Once he resumes working confidently, we can go over this important subject."); + next; + mesc b(l(".:: Main Quest 5-1 ::.")), 3; + mesc l("* Meet the Blue Sage"), 2; + mesc l("* Aid the Blue Sage in getting Peetu back to action"), 9; + return; +} + +// Question about Cindy +function nCindy { + mesn l("Nikolai, the Blue Sage"); + mesq l("Just like every cave below the woodlands are under the Terranite King domains, every land covered in snow is under the Yeti King's domains."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("It would be unwise to pick a fight with the Yeti King himself. The whole town would suffer."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Not only that, but the Yeti King and me had an... incident, in times best forgotten. Let's not talk about it."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("The situation is different if someone from Hurnscald went to her rescue. That's why I ask you to help my Cindy."); + next; + mesn l("Nikolai, the Blue Sage"); + mesq l("Please talk to my wife first, she knows more about the situation than I do."); + next; + mesq l("Also, Yetis can be crafty at times. I think someone on Hurnscald Household knew a lot about them, you might want to ask them if you ever feel struck."); + return; +} + +OnInit: + .sex=G_MALE; + .distance=5; + npcsit; + end; +} + diff --git a/npc/020-7-1/santeri.txt b/npc/020-7-1/santeri.txt new file mode 100644 index 0000000..9076e25 --- /dev/null +++ b/npc/020-7-1/santeri.txt @@ -0,0 +1,104 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Workers which produces pages +// helperM* + +// NivalisQuest_BlueSagePagemaker STRUCTURE +// FIELD 1: +// MAIN STATE +// FIELD 2: +// BOOK MAKING QUEST +// 1 - Illustrations delivered (BS_PMINK) +// 2 - Bindings delivered (BS_PMBINDING) +// 4 - Pages delivered (BS_PMPAGE) +// 8 - Glue delivered (BS_PMGLUE) +// =15: All items delivered + +020-7-1,36,31,4 script Santeri NPC_BLUESAGEWORKER_MA,{ + function askQuestion; + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + mesn; + if (.@qt & BS_PMGLUE) + mesq l("Thanks to you, our glue supply is replenished and we can repair those books."); + else + mesq l("Welcome. Are you an adventurer? I could use some help."); + + // Begin here + askQuestion(); + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + mes ""; + mesn; + mesq l("You see, we're working on repairing all of the damaged books and creating new ones for those that were lost."); + next; + mesn; + mesq l("Therefore we need a lot of glue, but our supplies are nearly used up. I need @@/@@ @@ as ingredient to make new glue.", countitem(WolvernTooth), 3, getitemlink(WolvernTooth)); + next; + mesn; + mesq l("Do you have that? I have the other materials but I couldn't get it... @@ are too dangerous, they are level @@ monsters.", getmonsterlink(Wolvern), strmobinfo(3,Wolvern)); + if (countitem(WolvernTooth) < 3) + close; + next; + if (askyesno() == ASK_NO) { + mes ""; + mesn; + mesq l("That's a pity."); + close; + } + mes ""; + + delitem WolvernTooth, 3; + setq2 NivalisQuest_BlueSagePagemaker, .@qt|BS_PMGLUE; + getexp 3535, 215; // 20% from reference levels 36/16. It's part of main story. + // Exp reward will not change. It applies to Job Exp on same rate. + mesn; + mesq l("Great! Thank you!"); + close; + +function askQuestion { + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qt=getq2(NivalisQuest_BlueSagePagemaker); + next; + select + rif(!(.@qt & BS_PMGLUE), any(l("Can I help you?"), l("I am. What heroic action is needed?"))), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye."), l("I'm just a lurker.")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("There was a visitor with a mask? I didn't notice. You see, I spend most of my time in the workshop, and concentrate on my work. There are other helpers who attend to the visitors. And hopefully keep them from disturbing my concentration."); + break; + case 3: + mesn; + mesq l("Oh, that's an interesting question. I was really surprised when I heard that he was responsible for the failure. I've worked together with him before, and I have to say, it really was a pleasure. He's very accurate and diligent, but also polite and helpful."); + next; + mesn; + mesq l("I really wonder what went wrong. I can't imagine Peetu messing up something so important."); + if (!(.@q3 & .bsId)) + setq3 NivalisQuest_BlueSage, .@q3 | .bsId; + break; + case 4: + mesn; + mesq l("Hm. Then please don't disturb me, I'm trying to concentrate."); + close; + } + } while (@menu != 1); + return; +} + +OnInit: + .bsId=BS_NPC04; + .sex=G_MALE; + .distance=5; + end; +} + + diff --git a/npc/020-7-1/teuvo.txt b/npc/020-7-1/teuvo.txt new file mode 100644 index 0000000..c2fb7f8 --- /dev/null +++ b/npc/020-7-1/teuvo.txt @@ -0,0 +1,75 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Responsible for recovering the lost pages (SpellBookPage) +// helperBookpages* +// +// NivalisQuest_BlueSagePagefinder STRUCTURE +// FIELD 1: +// MAIN STATE +// FIELD 2: +// nº of pages found (0~31) +// FIELD 3: +// nº of duplicates found (for randomness control - capped at 60) + +020-7-1,45,39,0 script Teuvo NPC_BLUESAGEWORKER_MA,{ + function askQuestion; + mesn; + mesc l("@@ seems to be upset.", .name$); + mesq l("I always thought it was a bad idea to play around with the slimes. And as if holding them in here isn't bad enough, no, they also had to mess around with magic."); + askQuestion(); + close; + +function askQuestion { + next; + if (.@qt >= 255) { + mesn; + mesq l("Thanks for helping out here. Do you need anything else?"); + } else if (.@qt) { + mesn; + mesq l("Did you find some bookpages? Ensio will take them."); + } + + // Mainframe Loop + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + .@qs=BSQuestion(getq(NivalisQuest_BlueSage)); + .@qt=getq2(NivalisQuest_BlueSagePagefinder); + next; + mes ""; + select + rif(.@qt == 255, ""), + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("With a mask? Hm. I don't remember."); + break; + case 3: + mesn; + mesq l("Peetu? He's one of those magic wielders who think they can do anything. Heh, you see the result here."); + next; + mesn; + mesq l("But to be fair, from what I've seen Peetu was always very attentive and dutiful."); + if (!(.@q3 & .bsId)) + setq3 NivalisQuest_BlueSage, .@q3 | .bsId; + break; + } + } while (@menu != 4); + close; +} + +OnInit: + .bsId=BS_NPC07; + .sex=G_MALE; + .distance=5; + npcsit; + end; +} diff --git a/npc/020-7-2/_import.txt b/npc/020-7-2/_import.txt new file mode 100644 index 0000000..6d305d4 --- /dev/null +++ b/npc/020-7-2/_import.txt @@ -0,0 +1,6 @@ +// Map 020-7-2: Ice cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-7-2/_mobs.txt", +"npc/020-7-2/_warps.txt", +"npc/020-7-2/connor.txt", +"npc/020-7-2/parcival.txt", diff --git a/npc/020-7-2/_mobs.txt b/npc/020-7-2/_mobs.txt new file mode 100644 index 0000000..4e4152b --- /dev/null +++ b/npc/020-7-2/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-7-2: Ice cave mobs +020-7-2,57,92,40,26 monster Blue Slime 1087,30,35000,420000,Pyry::OnSlimeDeath +020-7-2,60,88,40,26 monster White Slime 1094,15,36000,400000,Pyry::OnSlimeDeath +020-7-2,56,97,38,26 monster Black Slime 1178,50,35000,420000 +020-7-2,36,68,18,55 monster Santa Slime 1096,9,35000,450000 +020-7-2,66,74,29,55 monster Cave Bat 1039,16,35000,450000 +020-7-2,52,34,40,26 monster Azul Slime 1095,7,35000,450000 diff --git a/npc/020-7-2/_warps.txt b/npc/020-7-2/_warps.txt new file mode 100644 index 0000000..d25cba3 --- /dev/null +++ b/npc/020-7-2/_warps.txt @@ -0,0 +1,14 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-7-2: Ice cave warps +020-7-2,25,22,0 warp #020-7-2_25_22 0,0,020-1,76,38 +020-7-2,28,56,0 script #020-7-2_28_56 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 33,62; end; +} +020-7-2,33,61,0 script #020-7-2_33_61 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 28,55; end; +} +020-7-2,71,54,0 warp #020-7-2_71_54 0,0,020-7-1,39,25 diff --git a/npc/020-7-2/connor.txt b/npc/020-7-2/connor.txt new file mode 100644 index 0000000..42ec862 --- /dev/null +++ b/npc/020-7-2/connor.txt @@ -0,0 +1,48 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW LoF (Paxel) +// Description: +// Gives Coal in exchange for Raw Logs daily + +020-7-2,72,23,0 script Connor NPC_LOF_FURNACE,{ + .@l=CONNOR_LASTDAY; + .@t=gettimeparam(GETTIME_DAYOFMONTH); + mesn; + mesq l("Meh, the Blue Sage is truly wasteful. We use %s even for the simplest things such as keeping the house warm.", getitemlink(Coal)); + next; + mesn; + if (.@t == .@l) { + mesq l("But you have already given me firewood today. Come back tomorrow, pal."); + close; + } + mesq l("But %s is too valuable to burn and there is no alternative fuel for forges. It sells for %d GP and we are just wasting it here.", getitemlink(Coal), getiteminfo(Coal, ITEMINFO_SELLPRICE)); + next; + mesn; + mesq l("Yet, I could burn %d %s to keep the house just as warm, maybe even cozier, and the Blue Sage would be none the wiser. Fire is fire, warmth is warmth.", 15, getitemlink(RawLog)); + next; + mesn; + mesq l("I can even give you the %s lumps of precious %s we would have burned. Whaddaya say, do we have a deal?", l("five"), getitemlink(Coal)); // ie. whaddaya say -> what do you say + if (countitem(RawLog) < 15) + close; + if (askyesno() == ASK_YES) { + inventoryplace Coal, 5; + delitem RawLog, 15; + getitem Coal, 5; + CONNOR_LASTDAY=.@t; + .@exp=getiteminfo(Coal, ITEMINFO_SELLPRICE)+BaseLevel+JobLevel; + .@jobexp=15+JobLevel; + getexp .@exp, .@jobexp; + mesc l("* Gained %d EXP and %d Job EXP", .@exp, .@jobexp); + mes ""; + mesn; + mesq l("Thanks, pal. See you tomorrow."); + } + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + diff --git a/npc/020-7-2/parcival.txt b/npc/020-7-2/parcival.txt new file mode 100644 index 0000000..140ee67 --- /dev/null +++ b/npc/020-7-2/parcival.txt @@ -0,0 +1,22 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW LoF (Paxel) +// Description: +// Informs about the Slime Cage + +020-7-2,25,51,0 script Parcival NPC_BLUESAGEWORKER_MA,{ + mesn; + mesq l("We keep the Slimes past the barrier for the night, so be careful when entering."); + next; + mesn; + mesq l("A good thing we still use sturdy standard steel grating on the emergency exit which lead to Nivalis. I can't imagine how bad the incident would be wasn't for that."); + close; + +OnInit: + .sex=G_MALE; + .distance=5; + end; +} + + diff --git a/npc/020-7/_import.txt b/npc/020-7/_import.txt new file mode 100644 index 0000000..8ba944b --- /dev/null +++ b/npc/020-7/_import.txt @@ -0,0 +1,5 @@ +// Map 020-7: Blue Sages' Mansion +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/020-7/_warps.txt", +"npc/020-7/elias.txt", +"npc/020-7/workers.txt", diff --git a/npc/020-7/_warps.txt b/npc/020-7/_warps.txt new file mode 100644 index 0000000..4a9805b --- /dev/null +++ b/npc/020-7/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 020-7: Blue Sages' Mansion warps +020-7,56,21,0 warp #020-7_56_21 1,0,020-7-1,24,84 +020-7,40,50,0 warp #020-7_40_50 0,0,020-1,66,53 diff --git a/npc/020-7/elias.txt b/npc/020-7/elias.txt new file mode 100644 index 0000000..1e771be --- /dev/null +++ b/npc/020-7/elias.txt @@ -0,0 +1,216 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Controls access to Blue Sage Residence +// Minimum level: 36 (implicit) +// Minimum jblvl: 16 (implicit) + +// NivalisQuest_BlueSage STRUCTURE +// FIELD 1: +// INVESTIGATION +// 1 - STBY OUTSIDE +// 2 - ACCESS GRANTED +// 3 - QUEST ASSIGNED BY PEETU - talk to Oskari (and others) +// 4 - Oskari is OK with peetu, but wanna hear from others. He also sends you +// to ask what Peetu happened +// 5 - Adultered ingredients seems the cause, report to Elias +// 6 - Elias is now worried about a visitor. Ask people about and report. +// 7 - If everyone found the visitor, confirm Elias the worries +// 8 - Elias sent you to Oskari to inform the issue. Blue Sage probably knew all along. +// will not advance unless everyone thinks Peetu is good. +// 9 - Oskari accepts the cause. Tells to report Peetu that it probably was +// a saboutage, to check if the Silk Cocoon really was there. +// 10 - Peetu confirmed the saboutage. Report to Blue Sage. +// 11 - Blue Sage accepted the evidence, and explains about other sages issues. +// It's not known who or what is behind this. He excuses for making you waste +// your time. He asks you to return to him later, as he needs to write letters. +// 12 - QUEST COMPLETE - You collected your reward +// Also picked up a letter for Frostia Mayor, about the incident (Main Story). +// FIELD 2: +// Bitwise (BS_QVISITOR) +// FIELD 3: +// Bitwise (BS_QHELPER) + +020-7,44,41,0 script #BlueSageEntry NPC_HIDDEN,1,1,{ +OnTouch: + .@q=getq(NivalisQuest_BlueSage); + if (.@q < 2) { + slide 42, 43; + doevent "Elias::OnAccessDenied"; + } + end; +} + +// Here we start +020-7,40,41,0 script Elias NPC_BLUESAGEWORKER_MB,{ + function eliasWorry; + function eliasQuestion; + function eliasThankyou; + function eliasConfirmed; + mesn; + mesq l("Hello, and welcome to Blue Sage's Residence, Library, and Nivalis Townhall."); + .@q=getq(NivalisQuest_BlueSage); + if (.@q >= 2) + goto L_Main; + next; + mesn; + mesq l("Due to a recent incident involving slimes, the building is closed to public visits."); + .@qn=getq(General_Narrator); + mes ""; + select + l("That's sad to hear."), + rif(.@qn == 10 && !.@q, l("I have a letter from Rakinorf.")), + rif(.@q == 1, l("So? How was it?")), + l("What happened?"); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("Oh, you must be from Hurnscald Household, then. Hand me the letter, I'll have it delivered."); + next; + mesn; + mesq l("Please wait here a short while."); + setq NivalisQuest_BlueSage, 1, 0, 0; + break; + case 3: + mesn; + mesq l("Yes, the Blue Sage will see you."); + next; + mesn; + mesq l("Please don't mind the mess, there are slimes everywhere, thanks to Peetu."); + next; + mesn; + mesq l("You may pass. The Sage is on the library waiting for you."); + setq NivalisQuest_BlueSage, 2, 0, 0; + break; + case 4: + mesn; + mesq l("Slimes are on the loose. They have escaped, ate several books, and some are still in the building."); + next; + mesn; + mesq l("Some are dangerous, too. Even if you look capable of fighting, the Blue Sage instructed me to prevent anyone from visiting until the mess is cleared."); + break; + } + //goodbye; + close; + +L_Main: + next; + // .@q is preserved when you use goto + switch (.@q) { + case 1: + case 2: + case 3: + case 4: + case 5: + eliasQuestion(); + break; + case 6: + eliasWorry(); + break; + case 7: + case 8: + eliasConfirmed(); + break; + case 9: + case 10: + case 11: + eliasThankyou(); + break; + default: + mesn; + mesq l("I heard you have been assisting the household staff. For that, I am thankful."); + break; + } + close; + +// Elias is worried with visitor and asks for insights +function eliasWorry { + .@q2=getq2(NivalisQuest_BlueSage); + if (.@q2 == BS_NPCALL) { + setq1 NivalisQuest_BlueSage, 7; + eliasConfirmed(); + close; + } + mesn; + mesq l("After your questions I'm really getting worried about this guy with the mask. In retrospect it really seems suspicous."); + next; + mesn; + mesq l("It might be a good idea to ask around if anyone else observed something odd connected to this person."); + return; +} + +// Suspections Confirmed, report back +function eliasConfirmed { + mesn; + mesq l("I heard your investigations reminded other people that they observed suspicious behaviour too."); + next; + mesn; + mesq l("It's all my fault, I should've paid more attention! You should talk to Chief Oskari about this."); + if (getq(NivalisQuest_BlueSage) == 7) + setq1 NivalisQuest_BlueSage, 8; + return; +} + +// Waiting quest to end +function eliasThankyou { + mesn; + mesq l("Now that Chief Oskari knows about the sequence of events, the Sage will take care of it. It's really a shame how malicious people can be."); + next; + mesn; + mesq l("Thanks a lot for your help revealing the truth."); + return; +} + +// Main question to Elias +function eliasQuestion { + select + l("Peetu thinks someone put Silk Cocoon along the ingredients."), + l("Thanks, pal. I still got some matters to discuss with the Blue Sage if you excuse me."); + mes ""; + if (@menu == 2) { + mesn; + mesq l("Yes, of course. We have good relations with Hurnscald Town, so you're welcome."); + close; + } + mesn; + mesq l("So, Peetu didn't check the ingredients before casting? That's... Well, I don't know."); + next; + mesn; + mesq l("But that is unlikely, we have no reason to sabotage our own work! You saw the mess it caused. Everyone knew it was important."); + next; + mesn strcharinfo(0); + mesq l("Hmm, it might be a hunch but... Can you tell me if there were any unusual visitors before the incident?"); + next; + mesn; + mesq l("Mh, let me think. There were quite a few visitors with different concerns, but that's usual at this time of the year."); + next; + mesn; + mesq l("One of them was a bit odd, they were wearing a mask and had a strange way of talking. They said they came from Frostia, which is an elven town in the north. Elves are usually shy, so I thought it would have been rude to ask them to remove the mask."); + next; + mesn; + mesq l("If I remember correctly they wanted to see the library. They looked like a man, but you never know."); + next; + mesn; + mesc l("Elias gets a bit excited."); + mesq l("Do you think he may have had something to do with the accident? We're a very hospitable house, so I didn't see a reason to deny him the entrance. I mean, his mask might have been a bit unusual, but hey, there could've been many reasons why someone would wear a mask, don't you think?"); + setq1 NivalisQuest_BlueSage, 6; + next; + eliasWorry(); + return; +} + + +OnAccessDenied: + npctalk3 l("You can't go in there!"); + end; + +OnInit: + .sex=G_MALE; + .distance=5; + end; + +} + diff --git a/npc/020-7/workers.txt b/npc/020-7/workers.txt new file mode 100644 index 0000000..b5d2f73 --- /dev/null +++ b/npc/020-7/workers.txt @@ -0,0 +1,163 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// TMW Org. +// Description: +// Resting workers + +020-7,46,30,6 script Miro NPC_BLUESAGEWORKER_MA,{ + function askQuestion; + mesn; + mesq l("I'm so tired... Slimes everywhere... Chaos everywhere... Yawn..."); + next; + mesn; + mesq l("After cleaning for twelve hours straight, they allowed me to rest a bit."); + .@q=getq(NivalisQuest_BlueSage); + if (BSQuestion(.@q)) + askQuestion(BSQuestion(.@q)); + if (.@q == 12) { + next; + mesn; + mesq l("You did a good job too. Rest a bit, too. There's still one chair."); + } + close; + +function askQuestion { + .@qs=getarg(0); + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + next; + select + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("With a mask? I really don't know. I'm usually in the workshop and most of the visitors spend their time in the library. Sometimes they come to have a quick look in the workshop too, but I don't really pay attention to that, so... I can't recall a visitor with a mask, sorry."); + break; + case 2: + mesn; + mesq l("Peetu? He is very cautious. It is not like him to make mistakes. After all, he is a elf. Elves are really careful with their jobs."); + next; + mesn; + mesq l("If Nikolai's helpers weren't so carefully chosen, I'd think this was some kind of a bad joke from someone."); + if (!(.@q3 & .bsId)) + setq3 NivalisQuest_BlueSage, .@q3 | .bsId; + break; + } + } while (@menu != 3); + close; +} + +OnInit: + .bsId=BS_NPC01; + .sex=G_MALE; + .distance=5; + npcsit; + end; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +020-7,50,31,2 script Lenita NPC_BLUESAGEWORKER_FA,{ + function askQuestion; + .@q=getq(NivalisQuest_BlueSage); + // Teuvo said you're helping to collect some of the missing bookpages to repair the books. That's very kind of you! It's so much work to do, so every bit of help is welcome. + // Wow, Ensio told me you collected so many of the lost book pages. + if (.@q == 12) { + mesn; + mesq l("You did a good job. Rest a bit. There's still one chair."); + close; + } + mesn; + mesq l("Oh, hello. You didn't choose a good time to visit. The library is a total mess. The slimes got out of control and ate most of the books."); + next; + mesn; + mesq l("We're trying to repair some of the valuable and important books. It's so much work!"); + next; + mesn; + mesq l("I was up until late last night, and woke up so early today... I'm having a break right now."); + if (BSQuestion(.@q)) + askQuestion(BSQuestion(.@q)); + close; + +function askQuestion { + .@qs=getarg(0); + do { + .@q=getq(NivalisQuest_BlueSage); + .@q2=getq2(NivalisQuest_BlueSage); + .@q3=getq3(NivalisQuest_BlueSage); + next; + select + rif(.@qs & BS_QVISITOR, l("Do you know anything about the strange visitor?")), + rif(.@qs & BS_QHELPER, l("What's your opinion of Peetu and his work?")), + any(l("I need to leave."), l("See you."), l("Bye.")); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("With a mask? Yeah, I remember. That was a strange guy. He came to visit the library, but he lurked around at the workshop area and they had to send him back to the books."); + if (!(.@q2 & .bsId)) + setq2 NivalisQuest_BlueSage, .@q2 | .bsId; + break; + case 2: + mesn; + mesq l("Mh, I can't really tell since I usually work in the library while he works at the workshop. Maybe you should ask around among the people who work there."); + break; + } + } while (@menu != 3); + close; +} + +OnInit: + .bsId=BS_NPC01; + .sex=G_FEMALE; + .distance=5; + npcsit; + end; +} + + diff --git a/npc/021-0/_import.txt b/npc/021-0/_import.txt new file mode 100644 index 0000000..7402208 --- /dev/null +++ b/npc/021-0/_import.txt @@ -0,0 +1,2 @@ +// Map 021-0: Mountain Top +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/021-1/_import.txt b/npc/021-1/_import.txt new file mode 100644 index 0000000..c5b4859 --- /dev/null +++ b/npc/021-1/_import.txt @@ -0,0 +1,6 @@ +// Map 021-1: Ice Labyrinth +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/021-1/_mobs.txt", +"npc/021-1/_warps.txt", +"npc/021-1/logic.txt", +"npc/021-1/yeti.txt", diff --git a/npc/021-1/_mobs.txt b/npc/021-1/_mobs.txt new file mode 100644 index 0000000..74d1e8f --- /dev/null +++ b/npc/021-1/_mobs.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 021-1: Ice Labyrinth mobs +021-1,162,100,144,80 monster White Slime 1094,75,100000,30000 +021-1,160,174,127,112 monster Blue Slime 1087,90,100000,30000 +021-1,161,230,147,74 monster Moggun 1070,62,100000,30000 diff --git a/npc/021-1/_warps.txt b/npc/021-1/_warps.txt new file mode 100644 index 0000000..36fc0c3 --- /dev/null +++ b/npc/021-1/_warps.txt @@ -0,0 +1,243 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 021-1: Ice Labyrinth warps +021-1,103,40,0 script #021-1_103_40 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 56,49; end; +} +021-1,241,280,0 script #021-1_241_280 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 266,284; end; +} +021-1,183,282,0 script #021-1_183_282 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 158,282; end; +} +021-1,159,282,0 script #021-1_159_282 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 184,282; end; +} +021-1,212,300,0 warp #021-1_212_300 0,0,019-2,76,25 +021-1,128,266,0 script #021-1_128_266 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 115,266; end; +} +021-1,116,266,0 script #021-1_116_266 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 129,266; end; +} +021-1,80,268,0 script #021-1_80_268 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 55,268; end; +} +021-1,56,268,0 script #021-1_56_268 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 81,268; end; +} +021-1,109,255,0 script #021-1_109_255 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 106,231; end; +} +021-1,106,232,0 script #021-1_106_232 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 109,256; end; +} +021-1,103,199,0 script #021-1_103_199 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 92,174; end; +} +021-1,92,175,0 script #021-1_92_175 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 103,200; end; +} +021-1,148,150,0 script #021-1_148_150 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 128,134; end; +} +021-1,128,135,0 script #021-1_128_135 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 148,151; end; +} +021-1,146,172,0 script #021-1_146_172 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 135,197; end; +} +021-1,135,196,0 script #021-1_135_196 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 146,171; end; +} +021-1,36,249,0 script #021-1_36_249 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 35,225; end; +} +021-1,35,226,0 script #021-1_35_226 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 36,250; end; +} +021-1,36,200,0 script #021-1_36_200 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 26,177; end; +} +021-1,26,178,0 script #021-1_26_178 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 36,201; end; +} +021-1,24,157,0 script #021-1_24_157 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 31,143; end; +} +021-1,31,144,0 script #021-1_31_144 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 24,158; end; +} +021-1,91,125,0 script #021-1_91_125 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 115,76; end; +} +021-1,115,77,0 script #021-1_115_77 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 91,126; end; +} +021-1,57,49,0 script #021-1_57_49 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 104,40; end; +} +021-1,265,284,0 script #021-1_265_284 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 240,280; end; +} +021-1,283,271,0 script #021-1_283_271 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 269,244; end; +} +021-1,269,245,0 script #021-1_269_245 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 283,272; end; +} +021-1,286,190,0 script #021-1_286_190 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 289,163; end; +} +021-1,289,164,0 script #021-1_289_164 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 286,191; end; +} +021-1,292,123,0 script #021-1_292_123 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 296,96; end; +} +021-1,296,97,0 script #021-1_296_97 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 292,124; end; +} +021-1,275,73,0 script #021-1_275_73 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 283,47; end; +} +021-1,283,48,0 script #021-1_283_48 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 275,74; end; +} +021-1,197,263,0 script #021-1_197_263 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 187,227; end; +} +021-1,187,228,0 script #021-1_187_228 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 197,264; end; +} +021-1,218,265,0 script #021-1_218_265 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 227,241; end; +} +021-1,227,242,0 script #021-1_227_242 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 218,266; end; +} +021-1,212,193,0 script #021-1_212_193 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 207,167; end; +} +021-1,207,168,0 script #021-1_207_168 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 212,194; end; +} +021-1,236,211,0 script #021-1_236_211 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 235,166; end; +} +021-1,235,167,0 script #021-1_235_167 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 236,212; end; +} +021-1,195,122,0 script #021-1_195_122 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 208,91; end; +} +021-1,208,92,0 script #021-1_208_92 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 195,123; end; +} +021-1,228,121,0 script #021-1_228_121 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 232,92; end; +} +021-1,232,93,0 script #021-1_232_93 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 228,122; end; +} +021-1,192,70,0 script #021-1_192_70 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 200,49; end; +} +021-1,200,50,0 script #021-1_200_50 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 192,71; end; +} diff --git a/npc/021-1/logic.txt b/npc/021-1/logic.txt new file mode 100644 index 0000000..47bfdf8 --- /dev/null +++ b/npc/021-1/logic.txt @@ -0,0 +1,142 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Cave logic. Controls also switchs and false warps. See soren/main for more info. + +// Logic +function script CindySwitch_Check_211 { + .@st1=getvariableofnpc(.lifetime, "#CindySwitch_01")-gettimetick(2); + .@st2=getvariableofnpc(.lifetime, "#CindySwitch_02")-gettimetick(2); + .@st3=getvariableofnpc(.lifetime, "#CindySwitch_03")-gettimetick(2); + .@st4=getvariableofnpc(.lifetime, "#CindySwitch_04")-gettimetick(2); + .@st5=getvariableofnpc(.lifetime, "#CindySwitch_05")-gettimetick(2); + .@i=0; + if (.@st1 > 0) .@i++; + if (.@st2 > 0) .@i++; + if (.@st3 > 0) .@i++; + if (.@st4 > 0) .@i++; + if (.@st5 > 0) .@i++; + return .@i; +} + +// Passages +021-1,155,196,0 script #0211Logic NPC_NO_SPRITE,0,0,{ + end; +OnTouch: + // Sanitize some stuff + if ($@CINDY_STATE < gettimetick(2) && $@CINDY_STATE > 1500000000) + $@CINDY_STATE=0; + // Main logic + .@q=getq(NivalisQuest_Cindy); + if (.@q < 3) end; + if ($@CINDY_STATE > gettimetick(2)) goto L_Safe; + if ($@CINDY_STATE % 2 == 1) goto L_Blocked; + if (CindySwitch_Check_211() == 5) { + .@pos=rand(0,8); + if (.@q == 3) { + setq NivalisQuest_Cindy, 4; + warp "021-1", .xp[.@pos], .yp[.@pos]; + } else { + warp "021-2", 80, 102; + } + } else { + switch (CindySwitch_Check_211()) { + case 0: + case 1: + end; + case 2: + case 3: + dispbottom l("A few switches aren't triggered yet."); break; + case 4: + case 5: + dispbottom l("A single switch is not online - Cannot pass without all of them on."); break; + } + } + end; + +L_Blocked: + dispbottom l("This place is reeking blood. We better come again later."); + end; + +L_Safe: + dispbottom l("There's no reason to enter these caves now."); + end; + +OnInit: + setarray .xp, 25, 43, 97, 142, 274, 52, 36, 52, 120; + setarray .yp, 300, 300, 300, 300, 299, 176, 79, 77, 73; + end; +} + +021-1,136,29,0 script #FrostiaGateway NPC_NO_SPRITE,0,0,{ + end; +OnTouch: + if (!$NIVALIS_LIBDATE) { + npctalkonce l("Brr! It's extremely cold! I cannot go there!"); + end; + } else if (BaseLevel < 40) { + mesc l("You're about to enter a dangerous area. Are you sure?"); + if (askyesno() == ASK_NO) + end; + } + warp "022-1", 69, 111; + end; + +} + +// Switches +021-1,282,34,0 script #CindySwitch_01 NPC_SWITCH_OFFLINE,{ + .@q=getq(NivalisQuest_Cindy); + if (.@q < 3) { + mesn strcharinfo(0); + mesq l("This is a strange switch..."); + close; + } + if (.lifetime-gettimetick(2) <= 0) { + mesc l("Pull the switch?"); + select + l("Pull it"), + l("Leave it"); + if (@menu == 1) { + getexp rand(55, 110), rand(5, 11); + .lifetime=gettimetick(2)+rand(110, 150)+180; // Something between 1m50s and 2m30s + 3 minutes because the maze is huge + specialeffect(FX_SUCCESS); + setnpcdisplay .name$, NPC_SWITCH_ONLINE; + initnpctimer; + .@r=rand(0,100); + getmapxy(.@m$, .@x, .@y,0); + if (.@r < 80) + monster .@m$, .@x, .@y, "Yeti", Yeti, 1; + else if (.@r > 99) + makeitem(Candy, 1, .@m$, .@x, .@y); + npctalk l("Automatic disarm in: @@", FuzzyTime(.lifetime)); + closedialog; + } + close; + } + npctalk l("Automatic disarm in: @@", FuzzyTime(.lifetime)); + end; + +OnTimer1000: + if (.lifetime-gettimetick(2) <= 0) { + setnpcdisplay .name$, NPC_SWITCH_OFFLINE; + stopnpctimer; + } else { + initnpctimer; + } + end; + +OnInit: + .sex = G_OTHER; + .distance = 3; + .lifetime=0; // When will this switch turn off automatically + end; + +} + +021-1,231,36,0 duplicate(#CindySwitch_01) #CindySwitch_02 NPC_SWITCH_OFFLINE +021-1,34,29,0 duplicate(#CindySwitch_01) #CindySwitch_03 NPC_SWITCH_OFFLINE +021-1,25,297,0 duplicate(#CindySwitch_01) #CindySwitch_04 NPC_SWITCH_OFFLINE +021-1,30,168,0 duplicate(#CindySwitch_01) #CindySwitch_05 NPC_SWITCH_OFFLINE + diff --git a/npc/021-1/yeti.txt b/npc/021-1/yeti.txt new file mode 100644 index 0000000..7721530 --- /dev/null +++ b/npc/021-1/yeti.txt @@ -0,0 +1,242 @@ +// TMW2 script. +// Authors: +// Jesusalva +// Description: +// Controls the Cindy Gateway quest node. +// Also controls Watch Spot Yetis. + +021-1,246,206,0 script Yeti#0211Cindy NPC_YETI,{ + // Is this the right Yeti? + .@n=getq(NivalisQuest_Well); + + if (.@n < 2) + goto L_NoQuest; + + CINDY_PLAYER_STRUCK=false; + // Alright, now we need to cycle Cindy quest to decide if this NPC will have + // a special behavior or not. + .@q=getq(NivalisQuest_Cindy); + + if (.@q == 1) goto L_Assigned; // Quest Assigned + if (.@q == 2) goto L_Report; // Quest (Sub) Accepted + if (.@q <= 5) goto L_Instructions; // Quest (Sub) Completed + if (.@q >= 6) goto L_Complete; // Quest completed at least once. + +L_Thanks: + mesn; + mesq l("Hey, thanks for saving me. You know, from the well."); + next; + mesn strcharinfo(0); + mesq l("You're wel-- Wait, it was you who was trapped on the well near Miler's house?!"); + next; + mesn; + mesq l("Yes, exactly. Thanks for the rescue. I must guard this area from the caves, if you need, just call me."); + close; + +L_Assigned: + mesn; + mesq l("Hello my friend."); + next; + select + l("Hi."), + l("Hm, do you perhaps know where to find Cindy and could help me?"), + l("Die, your evil Yeti!"); + mes ""; + switch (@menu) { + case 1: goto L_Thanks; + case 2: + mesn; + mesq l("Hm, would \"Cindy\" be a small, little girl, kidnapped by some sturdy-looking rogue Yetis?"); + next; + mesn strcharinfo(0); + mesq l("Probably."); + next; + mesn; + mesq l("Well, yes, I've saw them heading off to the Yeti King room. Lately, many Yetis have been uprising against your majesty."); + next; + mesn; + mesq l("Unfortunately, @@s are not welcome on the Yeti Domains. You're no exception.", get_race()); + next; + mesn strcharinfo(0); + select + l("Alright, good bye."), + l("Can't I do anything, though?"), + l("Tell me how or I'll kill you!"); + mes ""; + if (@menu == 3) { + mesn; + mesq l("Now, that's unfortunate. I don't think you can hit me from where you are."); + next; + mesn; + mesq l("Besides, I know these caves like the back of my hand. You would have a hard time tracking me down."); + next; + mesn; + mesq l("In fact, you would get lost and killed. If I were you, I would avoid the Death Penalty. It's high, ya know?"); + } else if (@menu == 2) { + mesn; + mesq l("Well, the Yeti King has went away to the cliffs north of this one. He'll get back here very hungry."); + next; + mesn; + mesq l("I am his right-hand Yeti, so I could grant you passage to his Throne Room to slay the rogue Yetis..."); + next; + mesn; + mesq l("...But saving me won't be enough, so build trust by the kind gift of @@ @@. I'll be waiting.", 50, getitemlink(MoubooSteak)); // A normal Steak could do, but... + setq NivalisQuest_Cindy, 2; + } + close; + case 3: + mesn; + mesq l("That's rude. I never did anything to you."); + next; + mesn; + mesq l("Please get moving, @@. I do not mean any harm to you.", get_race()); + close; + } + close; + +L_Report: + mesn; + mesq l("Did you brought us a kind gift of good will, on the worth of %d %s, to offer to our King?", 50, getitemlink(MoubooSteak)); + next; + select + rif(countitem(MoubooSteak) >= 50, l("Yes, here they are.")), // Demure was here + l("Not yet."); + mes ""; + if (@menu == 2) { + mesn; + mesq l("Then please bring me this, so I can help you back."); + close; + } + delitem MoubooSteak, 50; + getexp 120000, 0; // roughly 30% from needed EXP. This quest IS boring. + setq NivalisQuest_Cindy, 3; + mesn; + mesq l("Great. I hereby task you to kill the rogue Yetis on the Yeti King Throne Room. Of course, you won't be able to leave until all of them are dead."); + next; + mesn; + mesq l("Getting there is tricky, so listen carefully to the instructions:"); + next; +L_Instructions: + mesn l("ROGUE YETI SLAYING REQUEST"); + mesc l("Mission: Kill all rogue Yetis on the Throne Room. You won't be able to leave until all of them are dead."); + next; + mesc l("Directions: "); + mesc l("You must flip all 5 switches on this cave, and then walk accross the cave blocked with an etheral crystal."); + mesc l("First timers will be lead to somewhere random on this cave to keep intruders out. Just walk there again while all switches are still active."); + mesc l("This random cave-warping only happens once, too."); + mesc l("The Throne Room is the last room. You can reactive any switch which turns itself off."); + next; + mesc l("Notes: "); + mesc l("To unlock the cell on the middle of the throne room, you need to flip all 5 switches there."); + mesc l("You need a @@ to flip the switches on the Throne Room, don't forget to bring at least five.", getitemlink(TreasureKey)); + mesc l("EVERY switch on EVERY cave will unflip itself after about 2 minutes. So don't waste your time."), 1; + mesc l("Keep in mind that %s are weak, baby Yetis, so kill them as well!" % getmonsterlink(Moggun)); + next; + mesn; + mesq l("Good luck. %%1"); + close; + +L_Complete: + //mesc l("ToDo"), 1; + mesn; + mesq l("Hello my friend. Thanks for slaying the rogue Yetis."); + mesc l("Do you want to read again the instructions?"); + next; + if (askyesno() == ASK_YES) { + goto L_Instructions; + } + close; + +// The other NPC +L_NoQuest: + if (TUTORIAL) + dispbottom l("I might want to ask an Yeti specialist about this. Maybe Celestia?"); + dispbottom l("Who's Mercury? Maybe I'm missing something? Where could he have went?"); + CINDY_PLAYER_STRUCK=true; + @yetiagro=@yetiagro+1; + if (@yetiagro > 5) goto L_Hit; + if (@yetiagro > 3) goto L_Warning; + if (@yetiagro > 1) goto L_Disrupt; + goto L_Walking; + +L_Walking: + mesn; + mesq l("Grr, where's Mercury... He was supposed to keep watch over this spot... It was meant to be my free time!"); + next; + mesn; + mesq l("And you, @@... You better get walking. Soon.", get_race()); + if (TUTORIAL) + mesc l("WARNING: Angela Quest cannot be finished without finishing Mercury sidequest first."), 1; + if (!.@n) { + mesc l("(...This probably could have been a great hint... if I knew who Mercury in first place %%L)"); + mesc l("I suggest we ignore the hint and ask %s about it instead.", b("Celestia")); + } + close; + +L_Disrupt: + npctalk3 l("Grrr... Why are @@ on this cave?! Where did Mercury went after all?!", get_race()); + end; + +L_Warning: + npctalk3 l("Stupid @@, get out of this cave before I lose my patience! Mercury... Show up already!", get_race()); + end; + +L_Hit: + npctalk3 l("DIE, SCUM!"); + @yetiagro=0; + percentheal -90, -100; + close; + +OnInit: + .sex=G_OTHER; + .distance=20; + end; +} + + + + + + +/////////////////////////////////////////////////////// Random Lookout Yetis +021-1,144,203,0 script Yeti#0211Guard NPC_YETI,{ + dispbottom l("I better don't bother this Yeti, before it kills me."); + @yetiagro=@yetiagro+1; + if (@yetiagro > 5) goto L_Hit; + if (@yetiagro > 3) goto L_Warning; + if (@yetiagro > 1) goto L_Disrupt; + goto L_Walking; + +L_Walking: + mesn; + mesq l("You, @@... You better get walking. Soon.", get_race()); + close; + +L_Disrupt: + mesn; + mesq l("Grrr... Why are @@ on this cave?! Begone, before I lose my patience!", get_race()); + close; + +L_Warning: + mesn; + mesq l("Stupid @@, get out of this cave before I lose my patience!", get_race()); + close; + +L_Hit: + mesn; + mesq l("DIE, SCUM!"); + percentheal -90, -100; + @yetiagro=0; + close; + +OnInit: + .sex=G_OTHER; + .distance=20; + end; +} + + + + +//021-1,25,298,0 duplicate(Yeti#0211Guard) Yeti#0211G2 NPC_YETI + diff --git a/npc/021-2/_import.txt b/npc/021-2/_import.txt new file mode 100644 index 0000000..2e81435 --- /dev/null +++ b/npc/021-2/_import.txt @@ -0,0 +1,5 @@ +// Map 021-2: Snow Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/021-2/_mobs.txt", +"npc/021-2/_warps.txt", +"npc/021-2/mapflags.txt", diff --git a/npc/021-2/_mobs.txt b/npc/021-2/_mobs.txt new file mode 100644 index 0000000..298f661 --- /dev/null +++ b/npc/021-2/_mobs.txt @@ -0,0 +1,7 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 021-2: Snow Cave mobs +021-2,47,79,22,21 monster Moggun 1070,12,90000,30000 +021-2,90,73,29,30 monster Blue Slime 1087,27,20000,30000 +021-2,74,37,49,21 monster White Slime 1094,15,100000,30000 +021-2,77,62,10,11 monster Yeti 1064,5,100000,30000 +021-2,84,64,4,4 monster Yeti 1064,2,10000,10000 diff --git a/npc/021-2/_warps.txt b/npc/021-2/_warps.txt new file mode 100644 index 0000000..c274291 --- /dev/null +++ b/npc/021-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 021-2: Snow Cave warps +021-2,57,49,0 warp #021-2_57_49 1,0,021-3,70,25 +021-2,80,103,0 warp #021-2_80_103 0,0,021-1,155,196 diff --git a/npc/021-2/mapflags.txt b/npc/021-2/mapflags.txt new file mode 100644 index 0000000..f5bc49d --- /dev/null +++ b/npc/021-2/mapflags.txt @@ -0,0 +1 @@ +021-2 mapflag nosave 019-2,76,25 diff --git a/npc/021-3/_import.txt b/npc/021-3/_import.txt new file mode 100644 index 0000000..3544a2a --- /dev/null +++ b/npc/021-3/_import.txt @@ -0,0 +1,6 @@ +// Map 021-3: Deeper Snow Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/021-3/_mobs.txt", +"npc/021-3/_warps.txt", +"npc/021-3/doors.txt", +"npc/021-3/mapflags.txt", diff --git a/npc/021-3/_mobs.txt b/npc/021-3/_mobs.txt new file mode 100644 index 0000000..bb865fd --- /dev/null +++ b/npc/021-3/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 021-3: Deeper Snow Cave mobs +021-3,46,90,27,34 monster Moggun 1070,12,90000,30000 +021-3,55,49,21,27 monster Blue Slime 1087,20,30000,30000 +021-3,53,54,25,47 monster White Slime 1094,15,100000,30000 +021-3,54,64,26,26 monster Yeti 1064,5,90000,30000 diff --git a/npc/021-3/_warps.txt b/npc/021-3/_warps.txt new file mode 100644 index 0000000..65d8d75 --- /dev/null +++ b/npc/021-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 021-3: Deeper Snow Cave warps +021-3,70,24,0 warp #021-3_70_24 0,0,021-2,57,48 diff --git a/npc/021-3/doors.txt b/npc/021-3/doors.txt new file mode 100644 index 0000000..f0405f5 --- /dev/null +++ b/npc/021-3/doors.txt @@ -0,0 +1,21 @@ +// TMW2 scripts. +// Authors: +// The Mana World Team +// Jesusalva +// Description: +// Cave logic + +021-3,24,123,0 script #0213_Logic NPC_NO_SPRITE,1,0,{ + if ($@CINDY_STATE > gettimetick(2)) goto L_Safe; + if ($@CINDY_STATE % 2 == 1) goto L_Blocked; + warp "021-4", 67, 28; + end; + +L_Blocked: + dispbottom l("This place is reeking blood. We better come again later."); + end; + +L_Safe: + dispbottom l("There's no need to go in that Yeti infested den right now."); + end; +} diff --git a/npc/021-3/mapflags.txt b/npc/021-3/mapflags.txt new file mode 100644 index 0000000..8f505b7 --- /dev/null +++ b/npc/021-3/mapflags.txt @@ -0,0 +1 @@ +021-3 mapflag nosave 019-2,76,25 diff --git a/npc/021-4/_import.txt b/npc/021-4/_import.txt new file mode 100644 index 0000000..bf86c09 --- /dev/null +++ b/npc/021-4/_import.txt @@ -0,0 +1,5 @@ +// Map 021-4: Cindy Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/021-4/doors.txt", +"npc/021-4/main.txt", +"npc/021-4/mapflags.txt", diff --git a/npc/021-4/doors.txt b/npc/021-4/doors.txt new file mode 100644 index 0000000..0ba3606 --- /dev/null +++ b/npc/021-4/doors.txt @@ -0,0 +1,25 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Cave logic + +021-4,67,27,0 script #Leave0214 NPC_NO_SPRITE,0,0,{ + if ($@CINDY_STATE % 2 == 1 && $@CINDY_STATE < 150000) goto L_Blocked; + if ($@CINDY_STATE > 150000) goto L_Check; +L_Warp: + warp "021-3", 24, 122; + end; + +L_Blocked: + dispbottom l("You cannot leave this room until either ALL yetis are dead, or you are dead yourself."); + end; + +L_Check: + .@pl = getmapusers("021-4"); + if (.@pl <= 1) { // Because we must include the one who triggered this script wasn't warped yet + $@CINDY_HERO$=""; + setnpcdisplay "Cindy#Outside", NPC_CINDY_CAGE; + } + goto L_Warp; +} diff --git a/npc/021-4/main.txt b/npc/021-4/main.txt new file mode 100644 index 0000000..679429d --- /dev/null +++ b/npc/021-4/main.txt @@ -0,0 +1,376 @@ +// TMW2 scripts. +// Authors: +// The Mana World Team +// Jesusalva +// Description: +// Cindy Cave core logic. You must flip 5 switches to free Cindy, and kill every +// Yeti to leave the Yeti's King Throne Room. Each passing minute, a new Yeti will +// spawn. Amount relies on how long this has been dragging on. +// Flipping switches creates yetis on the other switches. +// You must try flip all 5 switches at once. Once all switches are flipped, there'll +// be various waves of Yetis. More time passing = more penalty Yetis, with a safety +// check to abort script after a hour. If this happen, everyone inside the cave +// will be banned for cheating. This should not be possible without @monsterignore anyway :< + +// Switches +function script CindySwitch_Check_214 { + .@st1=getvariableofnpc(.lifetime, "#CindySwitch_06")-gettimetick(2); + .@st2=getvariableofnpc(.lifetime, "#CindySwitch_07")-gettimetick(2); + .@st3=getvariableofnpc(.lifetime, "#CindySwitch_08")-gettimetick(2); + .@st4=getvariableofnpc(.lifetime, "#CindySwitch_09")-gettimetick(2); + .@st5=getvariableofnpc(.lifetime, "#CindySwitch_10")-gettimetick(2); + .@i=0; + if (.@st1 > 0) .@i++; + if (.@st2 > 0) .@i++; + if (.@st3 > 0) .@i++; + if (.@st4 > 0) .@i++; + if (.@st5 > 0) .@i++; + return .@i; +} + +// Switches +021-4,101,33,0 script #CindySwitch_06 NPC_SWITCH_OFFLINE,{ + .@q=getq(NivalisQuest_Cindy); + if (.@q < 3 || !countitem(TreasureKey)) { + mesn strcharinfo(0); + mesq l("This is a strange switch..."); + close; + } + if (.lifetime-gettimetick(2) <= 0) { + mesc l("Pull the switch?"); + select + l("Pull it"), + l("Leave it"); + if (@menu == 1) { + getexp rand(55, 110), rand(5, 11); + .lifetime=gettimetick(2)+rand(110, 150)+60; // Something between 1m50s and 2m30s + 1 minute + specialeffect(FX_SUCCESS); + setnpcdisplay .name$, NPC_SWITCH_ONLINE; + initnpctimer; + .@r=rand2(0,100); + getmapxy(.@m$, .@x, .@y,0); + if (.@r < 90) + monster .@m$, .@x, .@y, "Yeti", Yeti, rand2(4,6); + else + monster .@m$, .@x, .@y, "Yeti", Yeti, 2; + npctalk l("Automatic disarm in: @@", FuzzyTime(.lifetime)); + closedialog; + } + close; + } + npctalk l("Automatic disarm in: @@", FuzzyTime(.lifetime)); + end; + +OnTimer1000: + if (.lifetime-gettimetick(2) <= 0) { + setnpcdisplay .name$, NPC_SWITCH_OFFLINE; + stopnpctimer; + } else { + initnpctimer; + } + end; + +OnInit: + .sex = G_OTHER; + .distance = 3; + .lifetime=0; // When will this switch turn off automatically + end; + +} + +021-4,90,79,0 duplicate(#CindySwitch_06) #CindySwitch_07 NPC_SWITCH_OFFLINE +021-4,49,65,0 duplicate(#CindySwitch_06) #CindySwitch_08 NPC_SWITCH_OFFLINE +021-4,47,21,0 duplicate(#CindySwitch_06) #CindySwitch_09 NPC_SWITCH_OFFLINE +021-4,80,49,0 duplicate(#CindySwitch_06) #CindySwitch_10 NPC_SWITCH_OFFLINE + +// Cindy +// Global Variable: $@CINDY_STATE + +021-4,66,49,0 script Cindy#Outside NPC_CINDY_CAGE,{ + if (getq(NivalisQuest_Cindy) < 4) + goto L_Cheat; + if ($@CINDY_STATE > 150000) goto L_Reset; + if ($@CINDY_STATE % 2 == 0) goto L_Start; + if (CindySwitch_Check_214() == 5 && !.canfinish) { + // Gate is open, advance to stage 2 + .canfinish=1; + .lifetime+=3; + .wtime=0; + mapannounce "021-4", "*roaaaaar*",0; + npctalk "Take care! More Yetis! And they have friends!!"; + setnpcdisplay .name$, NPC_CINDY_UNCAGE; + .@pl = getmapusers("021-4"); + if (.@pl == 1) + .@pl+=1; + .@pl+=3; + areamonster "021-4", 20, 20, 100, 80, "Yeti", Yeti, .@pl, "Cindy#Outside::OnPetDeath"; + areamonster "021-4", 20, 20, 100, 80, "Moggun", Moggun, .@pl, "Cindy#Outside::OnPetDeath"; + areamonster "021-4", 20, 20, 100, 80, "Slime Blast", SlimeBlast, $@CINDY_STATE+1; + } + + // If a major bug happened, do it now + // This finishes the quest + .@y=mobcount("021-4", "Cindy#Outside::OnPetDeath"); + if (.@y == 0 && .canfinish) { + $@CINDY_STATE=gettimetick(2)+60*rand2(55,65)*rand2(4,36); // It is way too random to I say how long it takes (220min ~ 39 hours) + mapannounce "021-4", "Nivalis: Cindy is now safe!",bc_all|bc_npc; + areatimer "021-4", 20, 20, 100, 80, 10, "Cindy#Outside::OnReward"; + setnpcdisplay .name$, NPC_CINDY; + stopnpctimer; + end; + } + + if (.canfinish) { + npctalk l("*scream*"); + if (TUTORIAL) + dispbottom l("Cindy is too scared to leave; Better we kill the Yetis and their friends!"); + } else { + npctalk3 l("Please get me out of here!"); + if (TUTORIAL) + dispbottom l("I believe these switches control the cage's lock."); + } + end; + +// Start +L_Start: + mesn; + mesq l("Have you came here to rescue me?"); + mes ""; mes ""; + mesc l(".:: WARNING ::."), 1; + mesc l("Once you decide to rescue Cindy, nobody else will be able to enter or leave this room."), 1; + mesc l("The blame of failure will be over you, but so will be the glory of success. There's no death penalty for others."), 1; + mes ""; + select + l("Not yet, I'm waiting for friends"), + l("Yes. Let me try to open this."), + l("No, I'll let you there to the Yeti's mercy."); + + if (@menu == 2 && $@CINDY_STATE % 2 == 0) { + $@CINDY_STATE+=1; + $@CINDY_HERO$=strcharinfo(0); + goto L_Begin; + } + close; + +// Begin +L_Begin: + initnpctimer; + enablenpc "#CindySwitch_06"; + enablenpc "#CindySwitch_07"; + enablenpc "#CindySwitch_08"; + enablenpc "#CindySwitch_09"; + enablenpc "#CindySwitch_10"; + changemusic "021-4", "misuse.ogg"; + + .@pl = getmapusers("021-4"); + areamonster "021-4", 20, 20, 100, 80, "Yeti", Yeti, .@pl, "Cindy#Outside::OnPetDeath"; + monster "021-4", 66, 49, "Yeti", Yeti, 1, "Cindy#Outside::OnPetDeath"; + close; + +// Special use-case +L_Reset: + if ($@CINDY_HERO$ == strcharinfo(0)) { + goto L_Winner; + } else if (is_gm() && $@GM_OVERRIDE) { + $@CINDY_STATE=0; + npctalk l("*beeep*"); + } + end; + +// For all players finishing the quest +OnReward: + if (ispcdead()) { + recovery(getcharid(3)); + warp "Save", 0, 0; + end; + } + if ($@CINDY_HERO$ == strcharinfo(0)) + dispbottom l("Congratulations on rescuing Cindy. You should now talk to her before leaving."); + else + dispbottom l("Congratulations on rescuing Cindy."); + setq2 NivalisQuest_Cindy, getq2(NivalisQuest_Cindy)+1; + getexp 10000, JobLevel*rand2(12,16); + getitem TreasureMap, 1; + end; + +// Winner Reward +L_Winner: + mesn; + mesq l("Thanks for saving me. I'll return home on my own, if you don't mind."); + mesq l("Here, take this reward. Good luck. %%1"); + + if ($CINDY_WINNER$ == "") { + $CINDY_WINNER$=strcharinfo(0); + channelmes("#world", $CINDY_WINNER$+" is the first player to finish Cindy Quest!! GG, dude! %%N"); + announce "All hail ##B"+$CINDY_WINNER$+"##b, first to complete the ##3Cindy Quest!", bc_all|bc_npc; + getexp 0, 2000; + getitem PrismGift, 1; + mesc l("CONGRATULATIONS! You are the first player to finish Cindy Quest!!"), 2; + mesc l("You just gained a Prism Gift, and 2000 Job Exp for your bravery!"), 2; + } else { + if (getcharid(1) > 0) + .@v$=getpartyname(getcharid(1)); + else + .@v$=strcharinfo(0); + kamibroadcast("Cindy was rescued by \""+.@v$+"\"."); + } + + // Better proccess this before everything else... + $@CINDY_HERO$=""; + setnpcdisplay .name$, NPC_ICE_CAGE; + + .@q=getq(NivalisQuest_Cindy); + // First time + if (.@q == 4) { + getitem Earmuffs, 1; + } else { + .@mbonus=(BaseLevel-60)*40; // up to 3600 GP limit (lv 150) [1.6k at L100] + Zeny=Zeny+rand(min(15000, 5000+.@mbonus),15000); + getitem TreasureMap, 1; + getitem TreasureKey, 1; + } + setq1 NivalisQuest_Cindy, 5; + next; + npctalk l("Cindy is gone..."); + closedialog; + close; + +// Events +OnPetDeath: + //fix_mobkill(Moggun); + end; + +// Cast against all players on defeat +OnFail: + die(); + heal -1, -1; + end; + +// Edge Cases +L_Cheat: + warp "Save", 0, 0; atcommand "@jail "+strcharinfo(0); dispbottom l("Cheater detected."); + end; + +// Timeout +OnReckless: +L_Reckless: + kamibroadcast("People failed to rescue Cindy!"); + areatimer "021-4", 20, 20, 100, 80, 10, "Cindy#Outside::OnFail"; + goto L_CleanUp; + +// Event is over, clean up the mess the players left +OnCleanUp: +L_CleanUp: + .lifetime=0; + .wtime=0; + .canfinish=0; + $@CINDY_STATE+=1; + killmonsterall("021-4"); + disablenpc "#CindySwitch_06"; + disablenpc "#CindySwitch_07"; + disablenpc "#CindySwitch_08"; + disablenpc "#CindySwitch_09"; + disablenpc "#CindySwitch_10"; + setnpcdisplay .name$, NPC_CINDY_CAGE; // We need NPC_CINDY for complete + changemusic "021-4", "water_prelude.ogg"; + stopnpctimer; + end; + +// Main loop +OnTimer2000: + .@y=mobcount("021-4", "Cindy#Outside::OnPetDeath"); + + // Quest is complete... We might need to clean the mess. + if ($@CINDY_STATE > gettimetick(2)) { + .@pl = getmapusers("021-4"); + if (!.@pl) + goto L_CleanUp; + end; + } + + // This finishes the quest + if (.@y == 0 && .canfinish) { + $@CINDY_STATE=gettimetick(2)+60*rand2(55,65)*rand2(4,36); // It is way too random to I say how long it takes (220min ~ 39 hours) + mapannounce "021-4", "Nivalis: Cindy is now safe!",bc_all|bc_npc; + areatimer "021-4", 20, 20, 100, 80, 10, "Cindy#Outside::OnReward"; + setnpcdisplay .name$, NPC_CINDY; + stopnpctimer; + end; + } + .wtime+=2; + + // Before 10 minutes, spawn every ~ 2 minutes + .@bonus=rand2(110,130); + // After 10 waves, spawn each ~ 10 minutes + if (.lifetime >= 10) + .@bonus+=500; + + // After 15 waves, we'll go a bit slower, but not much. + // You are a noob and should fail the quest. + if (.lifetime >= 15) + .@bonus+=.lifetime*2; + + if (.wtime >= .@bonus) { + // A new wave have passed + .wtime=0; + .lifetime+=1; + .@pl = getmapusers("021-4"); + + // Once the gate is open, more Yetis spawn, but not much + if (.canfinish) + .@pl+=1; + + // Value is given as: 1 Yeti per player + .@value=.@pl; + // + 1 Yeti per difficulty setting + .@value+=($@CINDY_STATE/2); + // +1 Yetis if you are alone because this is not a solo quest at heart + if (.@pl == 1) + .@value+=1; + + // To make easier, we spawn some Mogguns, too. + // The ratio is Yeti 2:1 Moggun. (Third Yeti is replaced) + .@mogg=.@value/3; + .@yeti=.@value-.@mogg; + areamonster "021-4", 20, 20, 100, 80, "Yeti", Yeti, .@yeti, "Cindy#Outside::OnPetDeath"; + areamonster "021-4", 20, 20, 100, 80, "Moggun", Moggun, .@mogg, "Cindy#Outside::OnPetDeath"; + npctalk any("Yetis!", + "Watch out!", + "More of them are coming!", + "Be careful! There's more!", + "More Yetis! Will this never end?", + "AAAAH! YETIS!", + "*shierks*", + "There are coming more and more!", + "More Yetis! We are hopeless!", + "Keep your guard up!", + "Hit and run, hit and run! More of them!"); + } + + // Regardless of Wave, Blue Slimes and Slime Blast will show up. + // This is based difficulty setting (previous fails) + if (.wtime % 10 == 2) { + .@mid=any(SlimeBlast, SlimeBlast, SlimeBlast, BlueSlime, BlueSlime, AzulSlime); + areamonster "021-4", 20, 20, 100, 80, strmobinfo(1, .@mid), .@mid, $@CINDY_STATE; + } + + // You're taking too much time, clear the spot after the 30th wave + if (.lifetime >= 30) + goto L_Reckless; + initnpctimer; + end; + +OnInit: + .distance=5; + .lifetime=0; // Controls Yeti Wave + .wtime=0; // Timer runs sort of often. WTime controls automatic Yeti advance + .canfinish=0;// Can finish or must talk to cage first? + $@CINDY_HERO$=""; + disablenpc "#CindySwitch_06"; + disablenpc "#CindySwitch_07"; + disablenpc "#CindySwitch_08"; + disablenpc "#CindySwitch_09"; + disablenpc "#CindySwitch_10"; + end; +} + diff --git a/npc/021-4/mapflags.txt b/npc/021-4/mapflags.txt new file mode 100644 index 0000000..59c57df --- /dev/null +++ b/npc/021-4/mapflags.txt @@ -0,0 +1,2 @@ +021-4 mapflag pvp +021-4 mapflag zone MMO diff --git a/npc/022-1/_import.txt b/npc/022-1/_import.txt new file mode 100644 index 0000000..4f2aea2 --- /dev/null +++ b/npc/022-1/_import.txt @@ -0,0 +1,6 @@ +// Map 022-1: North Icelands +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/022-1/_mobs.txt", +"npc/022-1/_warps.txt", +"npc/022-1/gift_collector.txt", +"npc/022-1/sign.txt", diff --git a/npc/022-1/_mobs.txt b/npc/022-1/_mobs.txt new file mode 100644 index 0000000..1e92d29 --- /dev/null +++ b/npc/022-1/_mobs.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 022-1: North Icelands mobs +022-1,30,73,10,34 monster Rudolph Slime 1086,8,0,0 +022-1,29,72,10,34 monster Noel Slime 1096,3,0,0 +022-1,92,58,9,6 monster Blue Slime 1087,8,0,0 +022-1,65,53,23,14 monster Noel Slime 1096,3,0,0 +022-1,69,75,13,6 monster Rudolph Slime 1086,8,0,0 +022-1,77,104,31,15 monster Rudolph Slime 1086,8,0,0 +022-1,78,105,31,15 monster Noel Slime 1096,3,0,0 diff --git a/npc/022-1/_warps.txt b/npc/022-1/_warps.txt new file mode 100644 index 0000000..fb1ac2e --- /dev/null +++ b/npc/022-1/_warps.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 022-1: North Icelands warps +022-1,80,65,0 warp #022-1_80_65 0,0,023-1,57,42 +022-1,73,62,0 warp #022-1_73_62 0,0,023-1,47,58 +022-1,58,52,0 warp #022-1_58_52 0,0,023-1,37,27 +022-1,70,81,0 warp #022-1_70_81 0,0,023-1,52,61 +022-1,68,90,0 warp #022-1_68_90 0,0,023-1,45,69 +022-1,49,55,0 warp #022-1_49_55 0,0,023-1,26,35 +022-1,70,112,0 warp #022-1_70_112 1,0,021-1,136,30 diff --git a/npc/022-1/gift_collector.txt b/npc/022-1/gift_collector.txt new file mode 100644 index 0000000..a2fc28b --- /dev/null +++ b/npc/022-1/gift_collector.txt @@ -0,0 +1,92 @@ +// TMW2/LoF scripts. +// Authors: +// TMW-LoF Team +// Jesusalva +// Description: +// Part Of Christmas Events +// SQuest_XmasCollector +// Status, Delivered Gifts, Current Year +// Status: +// 0 - Quest is unknown +// 1 - Quest is known +// 2 - Rewards Collected + +022-1,65,53,0 script Gift Fanatic NPC_GNOME_C,{ + // Start Event for the first time + if (gettime(GETTIME_MONTH) != JANUARY && getq3(SQuest_XmasCollector) < gettime(GETTIME_YEAR)) { + setq SQuest_XmasCollector, 1, 0, gettime(GETTIME_YEAR); + } + if (gettime(GETTIME_MONTH) == JANUARY && getq3(SQuest_XmasCollector) != gettime(GETTIME_YEAR)-1) { + setq SQuest_XmasCollector, 1, 0, gettime(GETTIME_YEAR)-1; + } + // Main Loop + if ($EVENT$ == "Christmas") + goto L_Main; + + // Last year + if (getq3(SQuest_XmasCollector) == gettime(GETTIME_YEAR)-1) + goto L_Reward; + + // Not on season + goto L_OutOfSeason; + +L_OutOfSeason: + mesn; + mesq l("Hey, collect lots of @@ and give them to me on Christmas! Not now. On Christmas!", getitemlink(PresentBox)); + close; + +// The reward is actually just an extra bonus, there is nothing WOW to see here... +// Unless you reach the milestones: 200, 400, 800, 1600, 3200, 6400, 12800... gifts +// The maximum is 10 christmas boxes, or 102.400 gifts. +// There's a floor, though: Your Base Level. That determines the minimum amount +// before it starts counting. So in reality, you need 200~300 gifts to get prize. +L_Reward: + inventoryplace XmasGift, 10; + .@q=getq2(SQuest_XmasCollector); + .@q=max(1, .@q-BaseLevel); + .@gifts=min(10, log2(.@q/200))+1; + mesn; + mesq l("Hey, huge THANKS for the help! I love gifts! Here's your due reward!"); + mesc l("Got @@ EXP, @@ JExp and @@ GP for helping out.", .@q*7, .@q, .@q*5); + getexp .@q*7, .@q; + Zeny=Zeny+(.@q*5); + getitem XmasGift, .@gifts; + setq SQuest_XmasCollector, 2, 0, 0; + close; + +L_Main: + .@q=getq2(SQuest_XmasCollector); + mesn; + mesq l("I want @@! I want @@!!", getitemlink(PresentBox), getitemlink(PresentBox)); + next; + .@price=(getiteminfo(PresentBox, ITEMINFO_SELLPRICE)+2); + mesn; + mesq l("I am willing to pay @@ GP for each you bring me! Do you want to give me ALL your @@?!", .@price, getitemlink(PresentBox)); + next; + select + rif(countitem(PresentBox), l("Yes, of course!")), + l("Not now..."); + mes ""; + if (@menu == 1) { + .@qnt=countitem(PresentBox); + .@q+=.@qnt; + delitem PresentBox, .@qnt; + Zeny=Zeny+.@price*.@qnt; + getexp .@qnt*.@price, .@qnt; + setq2 SQuest_XmasCollector, .@q; + mesn; + mesc l("You just delivered %d %s to %s.", + .@qnt, getitemlink(PresentBox), .name$); + mesq l("Many, many thanks! Thus far, you delivered me @@ gift boxes!", .@q); + next; + } + mesn; + mesq l("Come back and give me more gifts!"); + close; + +OnInit: + .sex=G_OTHER; + .distance=5; + end; +} + diff --git a/npc/022-1/sign.txt b/npc/022-1/sign.txt new file mode 100644 index 0000000..ced2019 --- /dev/null +++ b/npc/022-1/sign.txt @@ -0,0 +1,21 @@ +// TMW2 Script. +// Author: +// Jesusalva +// Description: +// Important Sign + +022-1,38,44,0 script Sign#02213844 NPC_SWORDS_SIGN,{ + mesc b(l(".:: PVP King Imperial Arena ::.")); + mesc l("The Arena is currently closed for maintenance."); + mesc l("It's advised to collect PVP equipment."); + mesc l("If you think this should be a priority, please ask Jesusalva."), 1; + tutmes l("If you kill an opponent stronger than you, you will gain honor points. But if the oponent is 15 levels weaker than you, it will be NEGATIVE!"), l("About Scoreboards and Honor Points"); + tutmes l("You will also LOSE honor if the opponent is below level 30. If you are a bandit (negative honor), all fights versus you will be honorable."), l("About Scoreboards and Honor Points"); + tutmes l("If you kill the same person within 30 minutes, honor will not fluctuate. The whole honor system is very experimental."), l("About Scoreboards and Honor Points"); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + end; +} diff --git a/npc/023-1/_import.txt b/npc/023-1/_import.txt new file mode 100644 index 0000000..4b279bf --- /dev/null +++ b/npc/023-1/_import.txt @@ -0,0 +1,4 @@ +// Map 023-1: Frozen Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/023-1/_mobs.txt", +"npc/023-1/_warps.txt", diff --git a/npc/023-1/_mobs.txt b/npc/023-1/_mobs.txt new file mode 100644 index 0000000..600dd29 --- /dev/null +++ b/npc/023-1/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 023-1: Frozen Cave mobs +023-1,42,33,23,13 monster Noel Slime 1096,4,0,0 diff --git a/npc/023-1/_warps.txt b/npc/023-1/_warps.txt new file mode 100644 index 0000000..14f6c10 --- /dev/null +++ b/npc/023-1/_warps.txt @@ -0,0 +1,59 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 023-1: Frozen Cave warps +023-1,59,20,0 script #023-1_59_20 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 36,46; end; +} +023-1,62,32,0 script #023-1_62_32 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 50,56; end; +} +023-1,37,28,0 warp #023-1_37_28 0,0,022-1,58,53 +023-1,35,33,0 script #023-1_35_33 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 31,61; end; +} +023-1,57,43,0 warp #023-1_57_43 0,0,022-1,80,66 +023-1,50,43,0 script #023-1_50_43 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 50,49; end; +} +023-1,26,36,0 warp #023-1_26_36 0,0,022-1,49,56 +023-1,36,47,0 script #023-1_36_47 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 59,21; end; +} +023-1,50,48,0 script #023-1_50_48 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 50,42; end; +} +023-1,50,55,0 script #023-1_50_55 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 62,31; end; +} +023-1,47,59,0 warp #023-1_47_59 0,0,022-1,73,63 +023-1,52,62,0 warp #023-1_52_62 0,0,022-1,70,82 +023-1,31,62,0 script #023-1_31_62 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 35,34; end; +} +023-1,45,70,0 warp #023-1_45_70 0,0,022-1,68,91 +023-1,46,33,0 script #023-1_46_33 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 39,21; end; +} +023-1,39,20,0 script #023-1_39_20 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 46,32; end; +} +023-1,42,33,0 warp #023-1_42_33 0,0,024-1,75,135 diff --git a/npc/023-2/_import.txt b/npc/023-2/_import.txt new file mode 100644 index 0000000..88b53a1 --- /dev/null +++ b/npc/023-2/_import.txt @@ -0,0 +1,6 @@ +// Map 023-2: Magic Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/023-2/_mobs.txt", +"npc/023-2/_warps.txt", +"npc/023-2/lightbringer.txt", +"npc/023-2/mk.txt", diff --git a/npc/023-2/_mobs.txt b/npc/023-2/_mobs.txt new file mode 100644 index 0000000..3173a20 --- /dev/null +++ b/npc/023-2/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 023-2: Magic Cave mobs +023-2,96,52,17,27 monster Moggun 1070,15,0,0 +023-2,38,77,11,27 monster Water Fairy 1184,4,0,0 +023-2,54,44,21,6 monster Yeti 1064,5,0,0 +023-2,81,61,39,16 monster Angry Bat 1194,8,0,0 diff --git a/npc/023-2/_warps.txt b/npc/023-2/_warps.txt new file mode 100644 index 0000000..f6a5c23 --- /dev/null +++ b/npc/023-2/_warps.txt @@ -0,0 +1,23 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 023-2: Magic Cave warps +023-2,86,80,0 script #023-2_86_80 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 86,83; end; +} +023-2,86,82,0 script #023-2_86_82 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 86,79; end; +} +023-2,30,108,0 script #023-2_30_108 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 30,105; end; +} +023-2,30,106,0 script #023-2_30_106 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 30,109; end; +} +023-2,121,38,0 warp #023-2_121_38 0,0,019-3,58,85 diff --git a/npc/023-2/lightbringer.txt b/npc/023-2/lightbringer.txt new file mode 100644 index 0000000..d2186a1 --- /dev/null +++ b/npc/023-2/lightbringer.txt @@ -0,0 +1,31 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// The most powerful sword ever. It's alive. + +023-2,91,86,0 script Lightbringer#NLib NPC_LIGHTBRINGER,{ + if ($NLIB_DAY == 7) goto L_Today; + npctalkonce l("It still isn't time to awake the King Of Holy Swords, Light Bringer."); + end; + +L_Today: + if (strcharinfo(0) != $NLIB_HIGHNAME$) { + npctalk3 l("The sword glows too much. Perhaps @@ could take it.", $NLIB_HIGHNAME$); + end; + } + rentitem Lightbringer, (60*60); + dispbottom l("This live sword drafts itself to your hand. You can wield it during today's event."); + disablenpc .name$; + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + + if (!$NLIB_DAY) + disablenpc .name$; + end; + +} + diff --git a/npc/023-2/mk.txt b/npc/023-2/mk.txt new file mode 100644 index 0000000..f203796 --- /dev/null +++ b/npc/023-2/mk.txt @@ -0,0 +1,260 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Nivalis Liberation Day + +// The Walking Monster King +023-2,121,35,0 script The Monster King#NLib NPC_MONSTERKING,{ + end; + +OnTouch: + npctalk3 get_race() + "!"; + die(); + end; + +OnInit: + .sex = G_MALE; + .distance = 5; + .sakar = 0; + .MK = 0; + +OnHour00: +OnReprocess: + if (!$NLIB_DAY) { + disablenpc .name$; + end; + } + switch ($NLIB_DAY) { + case 1: + movenpc(.name$, 121, 35, DIR_WEST); break; + case 2: + movenpc(.name$, 105, 37, DIR_NORTH); break; + case 3: + movenpc(.name$, 105, 51, DIR_SOUTH); break; + case 4: + movenpc(.name$, 102, 63, DIR_WEST); break; + case 5: + movenpc(.name$, 81, 59, DIR_NORTH); break; + case 6: + movenpc(.name$, 53, 53, DIR_SOUTH); break; + case 7: + movenpc(.name$, 36, 99, DIR_SOUTH); break; + default: + channelmes("#world", "A bug happened: Monster King # Nivalis Liberation Day - Value Out of Range."); disablenpc .name$; break; + } + + end; + +// Event Handler + +L_Finish: + $MANA_BLVL-=10; // Set level to 30 + // Just being bigger is not enough. Must be over the double for the advantage draw (about 1 player for 10m) + if (.victory_count > (.defeat_count*2)) { + announce "The fight ends in draw! The Monster King did accomplish what he set, though...", bc_all|bc_npc; + channelmes("#world", "Congratulations for the draw, that was a good fight."); + channelmes("#world", "The Monster King stole whatever he wanted and left."); + // Reproduce the same bonus from Alpha Server + $@EXP_EVENT=25; + donpcevent "@exprate::OnPlayerCall"; + $MANA_BLVL-=5; // Set level to 25 + } else if (.victory_count < 100) { // 1 player for 5 minutes is enough to prevent this loss + announce "The Monster King, after moping the floor with the players, accomplish what he set, and left...", bc_all|bc_npc; + channelmes("#world", "The players failed miserably in stopping the Monster King."); + // Reproduce the same penalty from Beta Server + $@EXP_EVENT=rand2(-50, -25); + donpcevent "@exprate::OnPlayerCall"; + } else { + announce "The fight ends in draw, with advantage to the Monster King...", bc_all|bc_npc; + channelmes("#world", "The players weren't capable to really harm the Monster King."); + } +L_Finish2: + killmonsterall("023-2"); + .wcycle=0; + maptimer("023-2", 10, "The Monster King#NLib::OnReturn"); + channelmes("#world", "*FINAL SCORE - PLAYERS "+.victory_count+" : "+.defeat_count+" MONSTERS*"); + + // Game Storyline advances: The Town Blockade Stage is now over + $GAME_STORYLINE=1; + $MK_TEMPVAR=gettimeparam(GETTIME_DAYOFMONTH)+ + MK_IDLE_DURATION+rand2(MK_IDLE_VARIATION); + + // Clean up the event + $NIVALIS_LIBDATE=gettimetick(2); + $NLIB_DAY=0; + $NLIB_HIGHTIME=0; + $NLIB_HIGHNAME$=""; + disablenpc "Lightbringer#NLib"; + enablenpc "#019-1_70_21"; + enablenpc "#019-2_37_55"; + enablenpc "#020-1_70_128"; + enablenpc "#020-1_107_55"; + + removemapflag("019-3", mf_bexp); + removemapflag("019-3", mf_nosave); + removemapflag("020-1", mf_bexp); + removemapflag("020-1", mf_nosave); + removemapflag("023-2", mf_bexp); + removemapflag("023-2", mf_nosave); + removemapflag("023-2", mf_nopenalty); + removemapmask "019-3", MASK_MATTACK; + setmapflag("019-3",mf_bexp,100); + setmapflag("020-1",mf_bexp,100); + setmapflag("023-2",mf_bexp,100); + stopnpctimer; + end; + +OnTimer3000: + .wcycle+=3; + .@pl=getareausers("023-2", 21, 109, 31, 114); + .@pla=getmapusers("023-2"); + if (.@pl <= 0 && .@pla > 0) + mapannounce "023-2", "##1WARNING: Players outside the Monster King room counts toward DEFEAT count!", 0; + .victory_count+=.@pl; + + // Processment + if (.@pl <= 0) { + .defeat_count+=1; + } else if (.wcycle % 30 == 3) { + areamonster "023-2", 21, 109, 31, 114, "Monster", any(AngryScorpion, BlackSlime, BlackScorpion, Wolvern, BlueSlime, SlimeBlast, CaveMaggot), .@pl; + } else if (.wcycle % 30 == 18) { + areamonster "023-2", 21, 109, 31, 114, "Monster", any(BlackSlime, BlueSlime, SlimeBlast), 1; // PS. Drop “Demure Dark Soul” ¬.¬ + } else if (.wcycle % 60 == 0) { + mapannounce "023-2", str(15-(.wcycle/60))+" minute(s) remaining", 0; + } + if (.wcycle >= 900) goto L_Finish; + initnpctimer; + // Andrei Sakar's Fake Chant + if (.wcycle % 15 < 3) { + unittalk(.sakar, sprintf("Come %s %s, and %s %s!", any("great", "magnificent"), any("light", "oath", "sacrament"), any("devastate", "annihilate", "obliterate", "liquidate", "eviscerate"), "nearby")); + harm(.MK, 100, HARM_MISC, Ele_Holy, .sakar); + } + end; + +OnVictory: + channelmes("#world", "The Monster King was defeated by players, and had to flee!"); + channelmes("#world", "Getting magic is now easier!"); + announce "Players have defeated the Monster King! He fleed from the cave after leaving a decoy!", bc_all|bc_npc; + $MANA_BLVL-=10; // Set level to 20 + $@EXP_EVENT=25; + donpcevent "@exprate::OnPlayerCall"; + goto L_Finish2; + end; + +OnBegin: + .defeat_count=0; + .victory_count=0; + .wcycle=0; + channelmes("#world", "The fight against the Monster King ends in 15 minutes. If he is forced to flee, that'll result as victory."); + channelmes("#world", "Each cycle there are players fighting against him will result in victory points. The opposite will result in defeat points."); + channelmes("#world", "More players fighting will result in more victory points. This may affect drastically the result."); + channelmes("#world", "Defeat, decided by a certain threshold of points, will result in MAGIC BEING PERMANENTLY HARDER TO OBTAIN."); + announce "The Alliance attacks the Monster King! 15 minutes! Rules in #world", bc_all|bc_npc; + movenpc(.name$, 0, 0); + .MK=monster("023-2", 24, 111, "The Monster King", MonsterKing, 1, "The Monster King#NLib::OnVictory"); + .sakar=monster("023-2", 30, 109, "Andrei Sakar", AndreiSakar, 1, "The Monster King#NLib::OnSkip", Size_Medium, 1); + areamonster "023-2", 21, 109, 31, 114, "Monster", AngryScorpion, 1; + areamonster "023-2", 21, 109, 31, 114, "Monster", BlackSlime, 2; + areamonster "023-2", 21, 109, 31, 114, "Monster", BlueSlime, 1; + areamonster "023-2", 21, 109, 31, 114, "Monster", CaveMaggot, 1; + initnpctimer; + end; + +OnReturn: + warp "019-2", 43, 55; + end; + +OnSkip: + end; +} + + +// Floor triggers +023-2,121,35,0 script #NLIB_T01 NPC_HIDDEN,1,2,{ + +OnTouch: + if ($NLIB_DAY == 1) { + @QNL3=1; + dispbottom l("I've found the Monster King."); + } + end; +} + + +023-2,110,36,0 script #NLIB_T02 NPC_HIDDEN,2,2,{ + +OnTouch: + if ($NLIB_DAY == 2) { + @QNL3=1; + dispbottom l("I've found the Monster King."); + } else if ($NLIB_DAY < 2) { + slide 114, 36; + dispbottom l("I should not veer off too much the path."); + } + end; +} + +023-2,105,50,0 script #NLIB_T03 NPC_HIDDEN,2,2,{ + +OnTouch: + if ($NLIB_DAY == 3) { + @QNL3=1; + dispbottom l("I've found the Monster King."); + } else if ($NLIB_DAY < 3) { + slide 104, 48; + dispbottom l("I should not veer off too much the path."); + } + end; +} + +023-2,104,62,0 script #NLIB_T04 NPC_HIDDEN,0,0,{ + +OnTouch: + if ($NLIB_DAY == 4) { + @QNL3=1; + dispbottom l("I've found the Monster King."); + } else if ($NLIB_DAY < 4) { + slide 104, 61; + dispbottom l("I should not veer off too much the path."); + } + end; +} + +023-2,86,59,0 script #NLIB_T05 NPC_HIDDEN,2,5,{ + +OnTouch: + if ($NLIB_DAY == 5) { + @QNL3=1; + dispbottom l("I've found the Monster King."); + } else if ($NLIB_DAY < 5) { + slide 91, 61; + dispbottom l("I should not veer off too much the path."); + } + end; +} + +023-2,53,49,0 script #NLIB_T06 NPC_HIDDEN,0,5,{ + +OnTouch: + if ($NLIB_DAY == 6) { + @QNL3=1; + dispbottom l("I've found the Monster King."); + } else if ($NLIB_DAY < 6) { + slide 55, 48; + dispbottom l("I should not veer off too much the path."); + } + end; +} + +023-2,35,90,0 script #NLIB_T07 NPC_HIDDEN,1,3,{ + +OnTouch: + if ((gettime(2) < 25 || gettime(2) > 45) && !$@GM_OVERRIDE) { + slide 39, 89; + dispbottom l("I should not go ahead... yet."); + } + end; +} + + diff --git a/npc/023-3-1/_import.txt b/npc/023-3-1/_import.txt new file mode 100644 index 0000000..a755984 --- /dev/null +++ b/npc/023-3-1/_import.txt @@ -0,0 +1,3 @@ +// Map 023-3-1: The Master Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/023-3-1/logic.txt", diff --git a/npc/023-3-1/logic.txt b/npc/023-3-1/logic.txt new file mode 100644 index 0000000..2055988 --- /dev/null +++ b/npc/023-3-1/logic.txt @@ -0,0 +1,306 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// 023-3-1 The First Monster King's Throne Room Configuration File + +023-3-1 mapflag zone MMO + +023-3-1,45,27,0 script #Finish02331 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@m$=getmap(); + .@n=getq(General_Narrator); + .@q=getq(FrostiaQuest_Homunculus); + // Cheater Detected + if (.@n < 15) { + warp "Save", 0, 0; + die(); + end; + } + if (.@q < 3) { + dispbottom l("The magic power outflowing in the room prevents you from leaving."); + end; + } + if (mobcount(.@m$, "#Core02331::OnMobDie")) { + dispbottom l("These assassins will catch me if I do that now!"); + end; + } + + .@mapn$="023-3-2"; + warp .@mapn$, any(39,40), 58; + end; +} + +023-3-1,45,80,0 script #Exit02331 NPC_HIDDEN,1,0,{ + end; +OnTouch: + .@n=getq(General_Narrator); + .@q=getq3(FrostiaQuest_Homunculus); + // Cheater Detected + if (.@n < 15) { + warp "Save", 0, 0; + die(); + end; + } + if (.@n == 15) { + dispbottom lg("I'm not a coward! I must press forward!"); + end; + } + .@mapn$="001-7"; + warp .@mapn$, 59, 45; + end; +} +// To the traps! + +023-3-1,45,63,0 script #Ambush02331 NPC_HIDDEN,10,0,{ +OnTouch: + .@q=getq(FrostiaQuest_Homunculus); + if (.@q < 2) { + dispbottom l("Error, cheater detected"); + die(); + end; + } + .@q=getq3(FrostiaQuest_Homunculus); + if (!.@q) { + .@m$=getmap(); + .@mobID1=monster(.@m$, 43, 55, l("Assassin"), HoodedNinja, 1, "#Core02331::OnMobDie"); + .@mobID2=monster(.@m$, 48, 55, l("Assassin"), HoodedNinja, 1, "#Core02331::OnMobDie"); + monster(.@m$, 43, 60, l("Assassin"), Assassin, 1, "#Core02331::OnMobDie"); + monster(.@m$, 43, 65, l("Assassin"), Assassin, 1, "#Core02331::OnMobDie"); + monster(.@m$, 48, 60, l("Assassin"), Assassin, 1, "#Core02331::OnMobDie"); + monster(.@m$, 48, 65, l("Assassin"), Assassin, 1, "#Core02331::OnMobDie"); + + if (any(true,false)) + unittalk(.@mobID1, l("Kill 'em!")); + else + unittalk(.@mobID2, l("Kill 'em!")); + + setq3 FrostiaQuest_Homunculus, 1; + } + end; +} + +// Main event core +023-3-1,45,52,0 script #Core02331 NPC_HIDDEN,10,0,{ +OnTouch: + if (instance_id() < 0) + end; + .@q=getq(FrostiaQuest_Homunculus); + if (.@q < 2) { + dispbottom l("Error, cheater detected"); + die(); + end; + } + .@n$=instance_npcname(.name$); + .@q=getq3(FrostiaQuest_Homunculus); + if (.@q == 1) { + // Begin the event core + setq3 FrostiaQuest_Homunculus, 2; + addtimer(1100, .@n$+"::OnEvent01"); + attachnpctimer(); + initnpctimer(); + } + end; +OnEvent01: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + warp .@m$, 45, 42; + @ISBAMUTH=monster(.@m$, 45, 34, l("Isbamuth"), Isbamuth, 1, .name$+"::OnIsbamuthDefeat"); + .@g1=monster(.@m$, 42, 37, l("Assassin"), HoodedNinja, 1, .name$+"::OnMobDie"); + .@g2=monster(.@m$, 48, 37, l("Assassin"), HoodedNinja, 1, .name$+"::OnMobDie"); + .@g3=monster(.@m$, 39, 40, l("Assassin"), Assassin, 1, .name$+"::OnMobDie"); + .@g4=monster(.@m$, 51, 40, l("Assassin"), Assassin, 1, .name$+"::OnMobDie"); + + // Block everyone for cutscene (includes invencibility boost) + setpcblock(PCBLOCK_HARD, true); + sleep2(10); + sc_start(SC_STUN, 7500, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, @ISBAMUTH); + sc_start(SC_STUN, 7500, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@g1); + sc_start(SC_STUN, 7500, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@g2); + sc_start(SC_STUN, 7500, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@g3); + sc_start(SC_STUN, 7500, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@g4); + + unittalk(@ISBAMUTH, l("Seems like the rat have come after the cheese.")); + addtimer(1500, .@n$+"::OnE02"); + end; + +OnE02: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("HAHAHAHA! How foolish of you, didn't even bother trying to sneak in!")); + + addtimer(1500, .@n$+"::OnE03"); + end; + +OnE03: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("Remember my name: I am Isbamuth, and I've took the Throne which rightfully belongs to me.")); + + addtimer(1500, .@n$+"::OnE04"); + end; + +OnE04: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("This throne is not from the Monster King... IT BELONGS ONLY TO ME!")); + + addtimer(1500, .@n$+"::OnE05"); + end; + +OnE05: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("Now, as you gave yourself the trouble of coming here...")); + + addtimer(1500, .@n$+"::OnE06"); + end; + +OnE06: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + setpcblock(PCBLOCK_HARD, false); + unittalk(@ISBAMUTH, l("LET'S DANCE!")); + mapannounce(.@m$, "##2"+l("Victory Conditions: Survive!"), 0); + mapannounce(.@m$, "##1"+l("Defeat Conditions: Your death!"), 0); + + addtimer(15000, .@n$+"::OnW01"); + addtimer(60000, .@n$+"::OnW02"); + addtimer(180000, .@n$+"::OnE07"); + end; + +OnE07: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + @SCOUT=monster(.@m$, 43, 29, l("Assassin"), HoodedNinja, 1, .name$+"::OnMobDie"); + sc_start(SC_STUN, 14000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, @SCOUT); + + unitemote(@SCOUT, E_KITTY); + unitstop(@ISBAMUTH); + dispbottom l("Something seems to be happening close to the Throne."); + + deltimer(.@n$+"::OnW01"); + deltimer(.@n$+"::OnW02"); + addtimer(2000, .@n$+"::OnE08"); + end; + +OnE08: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("What's it, scout.")); + unitwalk(@ISBAMUTH, 45, 34); + + addtimer(2500, .@n$+"::OnE09"); + end; + +OnE09: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unitemote(@SCOUT, E_THUMBUP); + unittalk(@ISBAMUTH, l("So, it is ready?")); + + addtimer(2000, .@n$+"::OnE10"); + end; + +OnE10: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("I hate to leave you now, @@, but I have more important things to do.", strcharinfo(0))); + + addtimer(1500, .@n$+"::OnE11"); + end; + +OnE11: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("Enjoy this world while it lasts. Heh. It's time to... detonate.")); + + addtimer(1500, .@n$+"::OnE12"); + end; + +OnE12: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + // A warp to non-instanced version to prevent death sprite from showing up. + unitwarp(@ISBAMUTH, "023-3-1", 45, 45); + unitwarp(@SCOUT, "023-3-1", 45, 45); + .@isb=@ISBAMUTH; + @ISBAMUTH=0; + unitkill(.@isb); + unitkill(@SCOUT); + setq1 FrostiaQuest_Homunculus, 3; + mapannounce(.@m$, "##2"+l("Victory Conditions: Defeat all enemies!"), 0); + mapannounce(.@m$, "##1"+l("Defeat Conditions: Your death!"), 0); + end; + +// War events +OnW01: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("Be cursed, you fool!")); + sc_start(SC_CURSE, 3000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK); + + // Maybe you're stupidly ignoring what we just said? + if (TUTORIAL) + dispbottom col(l("REMINDER: You do not need to kill anything, you need to ##BSURVIVE##b!"), 3); + + addtimer(45000, .@n$+"::OnW01"); + end; + +OnW02: + .@m$=getmap(); + .@n$=instance_npcname(.name$); + + unittalk(@ISBAMUTH, l("Come to my aid! Vanish this fool!")); + monster(.@m$, 42, 37, l("Assassin"), any(Assassin,Bandit,RobinBandit), 1, .name$+"::OnMobDie"); + monster(.@m$, 48, 37, l("Assassin"), HoodedNinja, 1, .name$+"::OnMobDie"); + monster(.@m$, 39, 40, l("Assassin"), any(Assassin,Bandit,RobinBandit), 1, .name$+"::OnMobDie"); + monster(.@m$, 51, 40, l("Assassin"), Assassin, 1, .name$+"::OnMobDie"); + + addtimer(60000, .@n$+"::OnW02"); + end; + +// Secret events. Do not handle Isbamuth death as it should be impossible... +OnIsbamuthDefeat: + if (!@ISBAMUTH) + end; + Exception(l("Why do you bully me! - This is a bug: 02331.LOGIC.OID"), RB_DISPBOTTOM|RB_DEBUGMES); + deltimer(.@n$+"::OnW01"); + deltimer(.@n$+"::OnW02"); + getexp 0, 1000; + @ISBAMUTH=monster(.@m$, 45, 34, l("Isbamuth"), Isbamuth, 1, .name$+"::OnIsbamuthDefeat"); + end; + +// For mobcount() only +OnMobDie: + end; + +OnTimer1000: + .@m$=instance_mapname("023-3-1"); + if (getmapusers(.@m$)) + initnpctimer; +OnTimerQuit: + // Cleanup - you fail. + killmonsterall(.@m$); + stopnpctimer(); + detachnpctimer(); + end; +} + + + diff --git a/npc/023-3-2/_import.txt b/npc/023-3-2/_import.txt new file mode 100644 index 0000000..1298da8 --- /dev/null +++ b/npc/023-3-2/_import.txt @@ -0,0 +1,3 @@ +// Map 023-3-2: Study Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/023-3-2/logic.txt", diff --git a/npc/023-3-2/logic.txt b/npc/023-3-2/logic.txt new file mode 100644 index 0000000..773abcb --- /dev/null +++ b/npc/023-3-2/logic.txt @@ -0,0 +1,330 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// 023-3-2 Isbamuth's Study Room Configuration File + +023-3-2 mapflag zone MMO + +023-3-2,39,59,0 script #Exit02332 NPC_HIDDEN,1,0,{ + end; +OnTouch: + .@q=getq(General_Narrator); + .@q2=getq2(General_Narrator); + // Cheater Detected + if (.@q < 15) { + warp "Save", 0, 0; + die(); + end; + } + if (.@q == 15) { + dispbottom lg("I'm not a coward! I must press forward!"); + end; + } + /* + Access to the dungeon is now prohibted. + .@mapn$="023-3-1"; + warp .@mapn$, 45, 28; + */ + .@mapn$="024-16"; + warp .@mapn$, 30, 26; + end; +} + +// The Master Room... We use field 3, too, but bitwise position 2 is reserved. +023-3-2,38,30,0 script Cauldron#MKHB NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + if (.@q < 4095) { + mesn; + mesc l("It's steaming. Who knows what insanity Isbamuth is brewing inside?!"); + next; + mesc l("Keep exploring the Study Room and certify it is safe first. Also, make sure to learn what this was used for."); + close; + } + mesn; + mesc l("It's steaming hot, but you guess this is where Isbamuth created homunculus. Most bandits, thieves and assassins seems to have been born here."); + next; + mesc l("He just abandoned this place and left everything behind... It's suspicious at best. You don't think anyone will be able to decrypt the secrets of Artificial Lifeforms."); + next; + mesc l("Hesitant, you reach out your hand, and grab whatever was on the Cauldron. It seems to be an Embryo."); + next; + skill AM_CALLHOMUN, 1, 0; + mesc l("You have collect enough evidence, it's time to leave and report."); + setq1 FrostiaQuest_Homunculus, 4; + setq1 General_Narrator, 16; + close; +OnInit: + .distance=2; + end; +} + +023-3-2,40,25,0 script Notebook#MKHB NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + if (.@q < 4094) { + mesn; + mesc l("Uhm, of course a book like this has to be a trap, right?"); + next; + mesc l("Keep exploring the Study Room and certify it is safe first."); + close; + } + mesn l("Diary, 31st May 297 AT"); + mesc l("I've been delving in the secrets of Mana, using the stolen Mana Stone we got the other day."); + next; + mesc l("I'm almost in the point of no return. I cannot believe this, but mana... is alive."); + next; + mesc l("I'll keep studying it in secret. I'm so proud of my thieves!"); + next; + mesn l("Diary, 21st June 297 AT"); + mesc l("I've managed to create the first mana-based artificial lifeform, it survived three seconds, the record so far."); + next; + mesc l("Saul is supplying me the alchemist resources for that. In exchange, my thieves supply him gold and spare his friends."); + next; + mesc l("He thinks he is using me to his rebellion, but I'm the one using him. I'm not the Bandit Lord for no reason."); + next; + mesn l("Diary, 1st April 298 AT"); + mesc l("I've managed to create a Maggot. It did not die. And it was not an April Fool's joke. Nice one."); + next; + mesn l("Diary, 12nd December 298 AT"); + mesc l("The Mana Stone... is a scary being. It changes you. I'm no longer myself, but some thieves are still at my side... for now."); + next; + mesn l("Diary, 3rd August 299 AT"); + mesc l("Saul is planning an assault at the Magic Tower. I want these Mana Stones."); + next; + mesc l("Most thieves left, but that's not a problem, I created an army out of Homunculus. I already have 500 bandits. I will create more later, to get Mana Stones."); + next; + mesn l("Diary, 2nd March 300 AT"); + mesc l("It's time. My army already have tens of thousands of monsters, homunculus, persons. The mana stones I've got thus far are all dried out, now."); + next; + mesc l("I could not be the Bandit King. But once I steal all Mana Stones, I'll be not only the best thief in the world. I already decided my new title."); + next; + mesc l("I shall call myself from there on, after this great assault... @@.", b(l("the Monster King"))); + next; + mesn l("Diary, 3rd March 300 AT"); + mesc l("To create an homunculus, I should follow the secrets in the book \"The Man Who Played God\". I wrote this narrative with the greatest secrets of it."); + next; + mesc l("I shall leave this book with my son, SphinxNox. I will not fail, and live forever. This will be their inheritance from me."); + next; + mesc l("Raid might be great. Saul might be popular. SphinxNox might be an annoying brat. But I'm, or rather, will soon be... @@.", b(l("the Monster King"))); + next; + +// Implicit fallthrough +L_BookRead: + mesc l(".:: The Homunculus Keeping Manual ::."), 3; + mesc l("To create an homunculus out of the embryo mass: Use the Call Homunculus Skill. It'll be random, based on the embryo nature."); + next; + mesc l(".:: The Homunculus Keeping Manual ::."), 3; + mesc l("Homunculus are a bit dumb, although the ones I've cultivated and took personal care of can express reactions. In future, I'll research sentience."); + next; + mesc l(".:: The Homunculus Keeping Manual ::."), 3; + mesc l("They need to be feed, when hunger falls below 75. @@ is the best food for them.", getitemlink(Bread)); + next; + mesc l(".:: The Homunculus Keeping Manual ::."), 3; + mesc l("Also, they level up when they attack, and can be revived with the right skill."); + next; + mesc l(".:: The Homunculus Keeping Manual ::."), 3; + mesc l("Once they get too annoying, I need to kill them... But I think I could put them to rest with the Rest Homunculus skill."); + next; + mesc l(".:: The Homunculus Keeping Manual ::."), 3; + mesc l("It's totally safe. They are loyal as long that feed. And to think I started off giving @@ to the poor...", getitemlink(Bread)); + mes ""; + mesc "-- " + l("The Monster King."); + + if (!(.@q & .hcID)) { + skill AM_REST, 1, 0; + skill AM_RESURRECTHOMUN, 1, 0; + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + dispbottom l("I should talk to Cauldron now... Scary."); + } + close; + +OnBookRead: + .@q=getq(General_Narrator); + if (.@q < 16) end; + .@q=.hcID; + goto L_BookRead; + +OnInit: + .hcID=1; + .distance=2; + bindatcmd "homunculus", "Notebook#MKHB::OnBookRead", 0, 100, 0; + end; +} + +023-3-2,36,31,0 script Machinery#MKHB NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("You have no idea what this piece of equipment is used for."); + next; + mesn; + mesc l("It have the same shape as those used to polish weapons, but there are blood stains everywhere."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=4; + .distance=2; + end; +} + +023-3-2,40,20,0 script Staircase#MKHB NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("The stairs have magic cast on them, I cannot climb."); + next; + mesn; + mesc l("I have no idea what's above. Behind the stair, is the painting of a table."); + next; + mesn; + mesc l("Seems to be related to alchemy."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=8; + .distance=2; + end; +} + +023-3-2,41,30,0 script Potions#MKHB NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("Several flasks are aligned there. They seemed to be dyes, but a close inspection reveals they're not."); + next; + mesn; + mesc l("They must be reagents used in alchemy."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + else + mesc l("I don't think I needed to keep looking these potions."); + close; +OnInit: + .hcID=16; + .distance=2; + end; +} + +023-3-2,36,25,0 script Bookcase#MKHB1 NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("Several books about alchemy. Seems to be pretty advanced."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=32; + .distance=2; + end; +} + + +023-3-2,44,25,0 script Bookcase#MKHB2 NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("Several books about hiding magic. Might be why the houses looked abandoned and empty but the chimney was going."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=64; + .distance=2; + end; +} + + +023-3-2,46,28,0 script Bookcase#MKHB3 NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("Several books about artificial lifeforms and the man who played God."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=128; + .distance=2; + end; +} + +023-3-2,49,31,0 duplicate(Bookcase#MKHB3) Bookcase#MKHB4 NPC_NO_SPRITE + +023-3-2,32,28,0 script Bookcase#MKHB5 NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("Seems to be accounting reports and stuff. You take a few pages."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=256; + .distance=2; + end; +} + +023-3-2,30,31,0 duplicate(Bookcase#MKHB1) Bookcase#MKHB6 NPC_NO_SPRITE + +023-3-2,35,35,0 duplicate(Bookcase#MKHB1) Bookcase#MKHB7 NPC_NO_SPRITE + +023-3-2,43,35,0 duplicate(Bookcase#MKHB2) Bookcase#MKHB8 NPC_NO_SPRITE + +023-3-2,33,22,0 duplicate(Potions#MKHB) Potions#MKHB1 NPC_NO_SPRITE +023-3-2,30,24,0 duplicate(Potions#MKHB) Potions#MKHB2 NPC_NO_SPRITE +023-3-2,27,26,0 duplicate(Potions#MKHB) Potions#MKHB3 NPC_NO_SPRITE +023-3-2,46,22,0 duplicate(Potions#MKHB) Potions#MKHB4 NPC_NO_SPRITE +023-3-2,49,24,0 duplicate(Potions#MKHB) Potions#MKHB5 NPC_NO_SPRITE +023-3-2,52,26,0 duplicate(Potions#MKHB) Potions#MKHB6 NPC_NO_SPRITE + + +023-3-2,37,20,0 script Wardrobe#MKHB NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("Inside it, seems to be several magic reagents, powders, and other stuff."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + else + mesc l("I don't think I needed to keep looking these wardrobes."); + close; +OnInit: + .hcID=512; + .distance=2; + end; +} + +023-3-2,43,20,0 duplicate(Wardrobe#MKHB) Wardrobe#MKHB1 NPC_NO_SPRITE +023-3-2,31,21,0 duplicate(Wardrobe#MKHB) Wardrobe#MKHB2 NPC_NO_SPRITE +023-3-2,28,23,0 duplicate(Wardrobe#MKHB) Wardrobe#MKHB3 NPC_NO_SPRITE +023-3-2,25,25,0 duplicate(Wardrobe#MKHB) Wardrobe#MKHB4 NPC_NO_SPRITE +023-3-2,48,21,0 duplicate(Wardrobe#MKHB) Wardrobe#MKHB5 NPC_NO_SPRITE +023-3-2,51,23,0 duplicate(Wardrobe#MKHB) Wardrobe#MKHB6 NPC_NO_SPRITE +023-3-2,54,25,0 duplicate(Wardrobe#MKHB) Wardrobe#MKHB7 NPC_NO_SPRITE + +023-3-2,51,37,0 script Barrels#MKHB NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("These barrels are sealed and heavy. It's impossible to know what's inside. There's a marking on them, you copy them somewhere."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + else + mesc l("I don't think I needed to keep looking these barrels."); + close; +OnInit: + .hcID=1024; + .distance=2; + end; +} + +023-3-2,45,41,0 duplicate(Barrels#MKHB) Barrels#MKHB1 NPC_NO_SPRITE +023-3-2,30,40,0 duplicate(Barrels#MKHB) Barrels#MKHB2 NPC_NO_SPRITE +023-3-2,25,34,0 duplicate(Barrels#MKHB) Barrels#MKHB3 NPC_NO_SPRITE + +023-3-2,38,26,0 script Alchemy Set#MKHB NPC_NO_SPRITE,{ + .@q=getq3(FrostiaQuest_Homunculus); + mesn; + mesc l("Hey look, an alchemy set. Not your area of expertise, but you take a few samples."); + if (!(.@q & .hcID)) + setq3 FrostiaQuest_Homunculus, .@q|.hcID; + close; +OnInit: + .hcID=2048; + .distance=2; + end; +} + diff --git a/npc/023-3/_import.txt b/npc/023-3/_import.txt new file mode 100644 index 0000000..ab1f0b7 --- /dev/null +++ b/npc/023-3/_import.txt @@ -0,0 +1,4 @@ +// Map 023-3: Ice Caves +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/023-3/_mobs.txt", +"npc/023-3/logic.txt", diff --git a/npc/023-3/_mobs.txt b/npc/023-3/_mobs.txt new file mode 100644 index 0000000..42f1032 --- /dev/null +++ b/npc/023-3/_mobs.txt @@ -0,0 +1,23 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 023-3: Ice Caves mobs +023-3,63,215,3,2 monster Pollet 1219,2,30000,30000 +023-3,41,214,16,4 monster Cyan Butterfly 1172,2,30000,30000 +023-3,39,217,16,4 monster Iced Fluffy 1041,2,30000,30000 +023-3,90,214,5,5 monster Wolvern 1037,1,30000,30000 +023-3,63,188,12,7 monster Pollet 1219,2,30000,30000 +023-3,120,54,80,16 monster Assassin 1062,6,30000,30000 +023-3,181,42,13,12 monster Bluepar 1177,2,30000,30000 +023-3,49,55,13,12 monster Bluepar 1177,2,30000,30000 +023-3,137,170,42,37 monster Blue Slime 1087,11,30000,30000 +023-3,163,96,36,33 monster Moggun 1070,6,30000,30000 +023-3,68,180,29,23 monster Santa Slime 1096,4,30000,30000 +023-3,67,90,21,21 monster Slime Blast 1090,3,30000,30000 +023-3,72,113,25,37 monster White Slime 1094,4,30000,30000 +023-3,51,134,17,25 monster Azul Slime 1095,3,30000,30000 +023-3,123,68,22,21 monster Rudolph Slime 1086,2,30000,30000 +023-3,122,201,31,13 monster Rudolph Slime 1086,2,30000,30000 +023-3,129,170,31,5 monster Archant 1026,3,30000,30000 +023-3,93,181,22,68 monster Water Fairy 1184,2,30000,30000 +023-3,93,57,22,66 monster Nature Fairy 1186,2,30000,30000 +023-3,117,127,91,101 monster Ice Maggot 1012,32,30000,30000 +023-3,105,157,91,86 monster Cave Bat 1039,22,30000,30000 diff --git a/npc/023-3/logic.txt b/npc/023-3/logic.txt new file mode 100644 index 0000000..0b72251 --- /dev/null +++ b/npc/023-3/logic.txt @@ -0,0 +1,366 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// 023-3 Abandoned Ice Caves Configuration File. Variables: $@LOGIC_0233BOSS + +023-3 mapflag zone MMO + +023-3,63,220,0 script #Exit0233 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@q=getq(General_Narrator); + .@q2=getq2(General_Narrator); + // Cheater Detected + if (.@q < 15) { + warp "Save", 0, 0; + die(); + end; + } + + mesc l("Return to Frostia's Castle?"); + if (askyesno() == ASK_YES) { + .@mapn$="024-16"; + warp .@mapn$, 30, 26; + } + closeclientdialog; + close; +} + +023-3,48,22,0 script #Finish0233 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@q=getq(General_Narrator); + .@q2=getq2(General_Narrator); + // Cheater Detected + if (.@q < 15) { + warp "Save", 0, 0; + die(); + end; + } + // Not allowed to proceed + if (.@q > 15) { + dispbottom l("Hmm, no, I shouldn't go back there..."); + end; + } + + .@mapn$="001-7"; + warp .@mapn$, 91, 90; + end; +} + +// A simple random treasure chest - to be sure players were introduced to this +// awesome system. Same rules as any treasure box still applies. +023-3,94,212,0 script #chest_02330 NPC_CHEST,{ + + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; + +OnInit: + .distance = 2; + end; +} + + +// Miniboss fight must be enabled on right npc. No passing before that. +023-3,120,103,0 script #MiniBossTrap0233 NPC_HIDDEN,0,2,{ + end; + +OnTouch: + .@q=getq(FrostiaQuest_Homunculus); + if (!.@q) { + dispbottom l("A powerful magic barrier repels you!"); + getmapxy(.@m$, .@x, .@y, 0); + slide .@x-2, .@y; + sit(); + } + end; +} + + +// The scout which went ahead of you (+ miniboss mechanics) +023-3,86,88,0 script Elite Scout#MB0233 NPC_DARKSABER,{ + if ($@LOGIC_0233BOSS) { + npctalkonce l("FIGHT!"); + end; + } + if (getq(FrostiaQuest_Homunculus) >= 1 && !is_sponsor()) { + npctalkonce l("I'm not going any closer to that cursed place!"); + end; + } + if (getq(FrostiaQuest_Homunculus) >= 1) { + npctalk3 l("Everything in order."); + end; + } + mesn; + mesq l("Congratulations in making this far, @@.", strcharinfo(0)); + next; + select + l("Thanks, and bye."), + l("What should I do now?"); + if (@menu == 1) + close; + mes ""; + mesn; + mesq l("So, there is a magical barrier to the right, which is in place exactly to protect us."); + next; + mesn; + mesq l("Past this checkpoint, is the village where the Monster King was born."); + next; + mesn; + mesq l("If you want to pass through it, you'll need to defeat the Guardian which made the seal. You can bring friends for healing and support."); + next; + mesn; + mesq l("Once the fight begins, the barrier at your left will lock. It won't open until the fight is over."); + next; + mesn; + mesq l("Are you ready?"); + mesc l("WARNING: DO NOT PROCEED UNLESS YOU'RE READY."), 1; + if (askyesno() == ASK_YES && !$@LOGIC_0233BOSS) { + $@LOGIC_0233BOSS=getcharid(3); // It could be 1. + $@LOGIC_0233BOXX=monster("023-3", any(85,94,103,113), any(91,96,102,111), "Guardian", Yeti, 1, .name$+"::OnYetiDefeat"); + initnpctimer; + } + closeclientdialog; + end; +// Check if fight is still going on +OnTimer10000: + if (!attachrid($@LOGIC_0233BOSS)) { + killmonster("023-3", .name$+"::OnYetiDefeat"); + $@LOGIC_0233BOSS=0; + end; + } + // You gave up, finish it now and not later + if (getmap() != "023-3") { + killmonster("023-3", .name$+"::OnYetiDefeat"); + $@LOGIC_0233BOSS=0; + end; + } + // What if the Yeti left the chamber? O.o + // It's not like there was mob collision to prevent this from happening + // If that's the case, bring the Yeti to player position and order attack :> + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, $@LOGIC_0233BOXX); + getmapxy(.@mx$, .@xx, .@yx, 0); + if (!(is_between(79, 120, .@x) && is_between(75, 125, .@y))) { + unitwarp($@LOGIC_0233BOXX, .@mx$, .@xx, .@yx); + unitattack($@LOGIC_0233BOXX, $@LOGIC_0233BOSS); + unittalk($@LOGIC_0233BOXX, "I did not forgot you, @@! %%e", strcharinfo(0)); + end; + } + initnpctimer; + end; +// Yeti Defeated +OnYetiDefeat: + stopnpctimer; + .@rid=$@LOGIC_0233BOSS; + $@LOGIC_0233BOSS=0; + // In past, only MVP would count. Now, the requester count. + if (playerattached()) + detachrid(); + // Player decided to leave the game, annuled + if (!attachrid(.@rid)) + end; + // Player decided to leave for a walk, annuled + if (!isin("023-3", 79, 75, 120, 125)) + end; + + // We don't need compareandsetq because it's now the requester + message(.@rid, l("You've slayed the Yeti.")); + npctalk l("Good luck in your journey, @@.", rid2name(.@rid)); + setq FrostiaQuest_Homunculus, 1; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + + end; +OnInit: + .distance = 5; + end; +} + + +// Cannot flee from Miniboss Fight +023-3,80,84,0 script #MiniBossTrapB0233 NPC_HIDDEN,5,0,{ + end; + +OnTouch: + .@q=getq(FrostiaQuest_Homunculus); + .@q2=getq(FrostiaQuest_Homunculus); + if (.@q) + end; + if ($@LOGIC_0233BOSS) { + dispbottom l("A powerful magic barrier repels you!"); + slide 81, 86; + sit(); + } + end; +} + +// A not-so-simple treasure chest from Sagratha's Dungeon +023-3,0,0,0 script #0233ChestCtrl NPC_HIDDEN,{ + end; + +OnDelay: + initnpctimer; + end; + +OnTimer180000: + stopnpctimer; +OnInit: + setarray .@x, 189, 191, 193, 186; + setarray .@y, 44, 42, 43, 45; + .@r=rand2(getarraysize(.@x)); + monster "023-3", .@x[.@r], .@y[.@r], "Treasure Chest", any(BronzeChest,BronzeMimic,SilverChest,SilverMimic,GoldenChest,GoldenMimic), 1, "#0233ChestCtrl::OnDelay"; + end; +} + +// Aethyr Gateway +023-3,26,219,0 script #AethyrGate NPC_HIDDEN,{ + end; + +OnInit: + // In theory, pattern ID must be between 1~9 + // To set $@p1$~$@p9$ with the PERL expression + // But as we're only using $@p0$ (full string) + // the value itself is meaningless + .pid=getnpcid(); + debugmes "Aethyr: Pattern %d", .pid; + //I'm not going to learn PERL just for that + defpattern(.pid, "^(.*)$", "OnTalkNearby"); + activatepset(.pid); + end; + +OnTalkNearby: + // not very obvious stuff by gumi, $@p0$ contains the whole string + // so we must cut it. Could use $@p1$ if perl was proper but... meh. + .@no_nick$ = strip(substr($@p0$, getstrlen(strcharinfo(PC_NAME)) + 3, getstrlen($@p0$) - 1)); + .@message$ = strtoupper(.@no_nick$); + + //0234_Password_BlackBox(.@message$); + callfunc "0234_Password_BlackBox", .@message$; + end; +} + + +// TODO: Spike traps and etc. at the corritor +// TODO: Rolling Stone Trap + +// Delete rolling stones as they hit this wall +/*023-3,47,40,0 script #0233DelStone NPC_HIDDEN,1,0,{ +OnTouch: + end; + +OnTouchNPC: + //die(); + //unitkill(0); + unitwarp(0, "023-3", 42, 26); + end; +} + +// TODO: Rolling Stone core +023-3,0,0,0 script #0233RollStone NPC_HIDDEN,{ + end; +OnInit: + $@0233_STONEA=monster("023-3", 47, 23, "Rolling Stone", RollingStone, 1); + $@0233_STONEB=monster("023-3", 48, 25, "Rolling Stone", RollingStone, 1); + $@0233_STONEC=monster("023-3", 47, 30, "Rolling Stone", RollingStone, 1); + $@0233_STONED=monster("023-3", 48, 37, "Rolling Stone", RollingStone, 1); + initnpctimer; + end; + +OnTimer250: + unitwalk($@0233_STONEA, 47, 40); + unitwalk($@0233_STONEC, 47, 40); + unitwalk($@0233_STONEB, 48, 40); + unitwalk($@0233_STONED, 48, 40); + + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, $@0233_STONEA); + if (.@y >= 40) + unitwarp($@0233_STONEA, "023-3", 47, 23); + + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, $@0233_STONEB); + if (.@y >= 40) + unitwarp($@0233_STONEB, "023-3", 48, 23); + + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, $@0233_STONEC); + if (.@y >= 40) + unitwarp($@0233_STONEC, "023-3", 47, 23); + + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, $@0233_STONED); + if (.@y >= 40) + unitwarp($@0233_STONED, "023-3", 48, 23); + + initnpctimer; + end; +} +*/ + +// TODO: NPC walking cannot trigger OnTouch events >.> +023-3,47,23,0 script #0233RollStoneA NPC_ROLLINGSTONE,0,0,{ + end; + +OnInit: + npcspeed(240); + initnpctimer; + npcwalkto(47, 41); + end; + +OnTimer120: + if (.y >= 40) { + movenpc(.name$, 47, 23); + npcwalkto(47, 41); + } + if (getareausers("023-3", 0)) { + areatimer2("023-3", .x, .y, .x, .y, 10, .name$+"::OnTouchie"); + } + initnpctimer; + end; + +OnTouchie: + if (isin(.map$, .x, .y, 0)) { + percentheal -40, -40; + warp .map$, 49, 41; + } + end; + +OnTouchNPC: + sc_start SC_COMA, 60000, 0; + end; +} + +023-3,48,26,0 script #0233RollStoneB NPC_ROLLINGSTONE,0,0,{ + end; + +OnInit: + npcspeed(240); + initnpctimer; + npcwalkto(49, 41); + end; + +OnTimer120: + if (.y >= 40) { + movenpc(.name$, 48, 23); + npcwalkto(48, 41); + } + if (getareausers("023-3", 0)) { + areatimer2("023-3", .x, .y, .x, .y, 10, .name$+"::OnTouchie"); + } + initnpctimer; + end; + +OnTouchie: + if (isin(.map$, .x, .y, 0)) { + percentheal -40, -40; + warp .map$, 49, 41; + } + end; + +OnTouchNPC: + sc_start SC_COMA, 60000, 0; + end; +} + +023-3,47,30,0 duplicate(#0233RollStoneA) #0233RollStoneC NPC_ROLLINGSTONE,0,0 +023-3,48,37,0 duplicate(#0233RollStoneB) #0233RollStoneD NPC_ROLLINGSTONE,0,0 + diff --git a/npc/023-4/_import.txt b/npc/023-4/_import.txt new file mode 100644 index 0000000..ebd1020 --- /dev/null +++ b/npc/023-4/_import.txt @@ -0,0 +1,6 @@ +// Map 023-4: Ice Caves +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/023-4/023-4_blackbox.txt", +"npc/023-4/_mobs.txt", +"npc/023-4/_warps.txt", +"npc/023-4/traps.txt", diff --git a/npc/023-4/_mobs.txt b/npc/023-4/_mobs.txt new file mode 100644 index 0000000..df036b0 --- /dev/null +++ b/npc/023-4/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 023-4: Ice Caves mobs +023-4,44,44,25,24 monster Ice Maggot 1012,27,30000,30000 +023-4,61,62,6,5 monster Copper Slime Mother 1238,1,30000,30000 +023-4,62,28,6,6 monster White Slime Mother 1242,1,30000,30000 +023-4,58,44,6,26 monster JackO 1120,2,30000,15000 diff --git a/npc/023-4/_warps.txt b/npc/023-4/_warps.txt new file mode 100644 index 0000000..4fcfe65 --- /dev/null +++ b/npc/023-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 023-4: Ice Caves warps +023-4,42,70,0 warp #023-4_42_70 0,0,023-3,87,219 +023-4,24,20,0 warp #023-4_24_20 0,0,031-1,58,103 diff --git a/npc/023-4/traps.txt b/npc/023-4/traps.txt new file mode 100644 index 0000000..9de778c --- /dev/null +++ b/npc/023-4/traps.txt @@ -0,0 +1,58 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Advanced Spike Traps + +///////////////////////////// +023-4,60,51,0 script #SpikeTrap NPC_TRAP_B,1,1,{ + end; + +OnInit: + .damage=0; + .time=0; + .goal=rand2(5,10); + initnpctimer; + end; + +OnTouch: +OnTouchNPC: + if (.damage) { + if (playerattached()) { + percentheal -80, 0; + } else { + sc_start SC_WALKSPEED, 15000, 60; + sc_start SC_STUN, rand2(3000), 1; + } + } + specialeffect(11, AREA, strnpcinfo(0)); + end; + +OnTimer1000: + .time+=1; + if (.time > .goal) { + .damage = !(.damage); + .time = 0; + if (.damage) + setnpcdisplay strnpcinfo(0), NPC_TRAP_ONLINE_B; + else + setnpcdisplay strnpcinfo(0), NPC_TRAP_B; + } + initnpctimer; + end; +} + +023-4,50,60,0 duplicate(#SpikeTrap) #SpikeTrap001 NPC_TRAP,1,1 +023-4,31,59,0 duplicate(#SpikeTrap) #SpikeTrap002 NPC_TRAP,1,1 +023-4,31,48,0 duplicate(#SpikeTrap) #SpikeTrap003 NPC_TRAP,1,1 +023-4,22,50,0 duplicate(#SpikeTrap) #SpikeTrap004 NPC_TRAP,1,1 +023-4,39,39,0 duplicate(#SpikeTrap) #SpikeTrap005 NPC_TRAP,1,1 +023-4,42,42,0 duplicate(#SpikeTrap) #SpikeTrap006 NPC_TRAP,1,1 +023-4,52,44,0 duplicate(#SpikeTrap) #SpikeTrap007 NPC_TRAP,1,1 +023-4,50,46,0 duplicate(#SpikeTrap) #SpikeTrap008 NPC_TRAP,1,1 +023-4,47,28,0 duplicate(#SpikeTrap) #SpikeTrap009 NPC_TRAP,1,1 +023-4,44,28,0 duplicate(#SpikeTrap) #SpikeTrap010 NPC_TRAP,1,1 +023-4,42,25,0 duplicate(#SpikeTrap) #SpikeTrap011 NPC_TRAP,1,1 +023-4,39,24,0 duplicate(#SpikeTrap) #SpikeTrap012 NPC_TRAP,1,1 + + diff --git a/npc/024-1/_import.txt b/npc/024-1/_import.txt new file mode 100644 index 0000000..8b4f2df --- /dev/null +++ b/npc/024-1/_import.txt @@ -0,0 +1,14 @@ +// Map 024-1: Frostia +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-1/_mobs.txt", +"npc/024-1/_warps.txt", +"npc/024-1/erlan.txt", +"npc/024-1/guard.txt", +"npc/024-1/john.txt", +"npc/024-1/mapflags.txt", +"npc/024-1/meriel.txt", +"npc/024-1/rydel.txt", +"npc/024-1/soul-menhir.txt", +"npc/024-1/taenya.txt", +"npc/024-1/teleporter.txt", +"npc/024-1/town.txt", diff --git a/npc/024-1/_mobs.txt b/npc/024-1/_mobs.txt new file mode 100644 index 0000000..07c198b --- /dev/null +++ b/npc/024-1/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-1: Frostia mobs +024-1,98,43,76,22 monster Pollet 1219,16,90000,30000 +024-1,96,77,78,21 monster Iced Fluffy 1041,7,60000,30000 +024-1,89,120,21,22 monster Azul Slime 1095,3,30000,30000 +024-1,145,70,25,36 monster Rudolph Slime 1086,2,30000,30000 diff --git a/npc/024-1/_warps.txt b/npc/024-1/_warps.txt new file mode 100644 index 0000000..6732a87 --- /dev/null +++ b/npc/024-1/_warps.txt @@ -0,0 +1,23 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-1: Frostia warps +024-1,75,134,0 warp #024-1_75_134 0,0,023-1,42,32 +024-1,94,70,0 script #024-1_94_70 NPC_HIDDEN,3,0,{ + end; +OnTouch: + slide 93,72; end; +} +024-1,32,54,0 warp #024-1_32_54 0,0,024-2,41,29 +024-1,126,28,0 warp #024-1_126_28 0,0,024-3,35,34 +024-1,154,40,0 warp #024-1_154_40 0,0,024-4,28,28 +024-1,160,40,0 warp #024-1_160_40 0,0,024-4,39,28 +024-1,155,55,0 warp #024-1_155_55 0,0,024-5,34,28 +024-1,143,40,0 warp #024-1_143_40 0,0,024-6,34,27 +024-1,160,49,0 warp #024-1_160_49 0,0,024-7,40,28 +024-1,136,28,0 warp #024-1_136_28 0,0,024-8,71,31 +024-1,134,48,0 warp #024-1_134_48 0,0,024-9,41,37 +024-1,77,59,0 warp #024-1_77_59 0,0,024-10,24,36 +024-1,83,59,0 warp #024-1_83_59 0,0,024-10,34,36 +024-1,105,34,0 warp #024-1_105_34 0,0,024-11,24,34 +024-1,76,45,0 warp #024-1_76_45 0,0,024-12,31,34 +024-1,50,54,0 warp #024-1_50_54 0,0,024-13,30,36 +024-1,64,30,0 warp #024-1_64_30 0,0,024-15,25,38 diff --git a/npc/024-1/erlan.txt b/npc/024-1/erlan.txt new file mode 100644 index 0000000..b6cc4dd --- /dev/null +++ b/npc/024-1/erlan.txt @@ -0,0 +1,118 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +024-1,73,48,0 script Erlan NPC_ELF,{ + .@q = getq(FrostiaQuest_Erlan); + if (BaseLevel < 40) { + mesc l("The elf seems busy. He ignores you."); + close; + } + if (.@q == 0) { + if (frally()) goto L_Start_F; + else goto L_Start_U; + } + else if (.@q == 1) { + if (frally()) goto L_Submit_F; + else goto L_Submit_U; + } + else if (.@q == 2) { + if (frally()) goto L_Done_F; + else goto L_Done_U; + } + Exception("Invalid quest state - "+str(.@q)); + end; + +L_Start_F: + mesn; + mesq lg("Hey gal, could you do me a little favor?", "Hey pal, could you do me a little favor?"); + next; + mesn; + mesq l("You see, I am Erlan, a researcher. I'm currently developing strong poison to rival with the legendary %s. For that end, I need raw, untreated sewerage, but there is no such thing here. Could you bring me %d %s? Thanks!", getitemlink(NymphPoison), 7, getitemlink(BottleOfSewerWater)); + setq FrostiaQuest_Erlan, 1; + close; + +L_Submit_F: + mesn; + mesq l("Did you got the %d %s I've asked?", 7, getitemlink(BottleOfSewerWater)); + next; + if (askyesno() == ASK_YES) { + if (countitem(BottleOfSewerWater) < 7) { + mesn; + mesq l("You've been spending too much time with humans. You are picking up bad habits from them like lying. Be careful."); + close; + } + delitem BottleOfSewerWater, 7; + getexp 10000, 0; + Zeny+=5700; + setq FrostiaQuest_Erlan, 2; + mesn; + mesq lg("Thanks, my friend. Here's some money."); + } + close; + +L_Done_F: + mesn; + mesq l("Thanks for the help earlier. Unfortunately, I had no luck yet."); + next; + mesn; + mesq l("There are rumors about a sacred elf land, where our antecessors lived in peacefully somewhere close to here, but the path was lost. That's why we now live with dwarves."); + close; +///////////////////////////////////////////////////////////////////////////////// +L_Start_U: + mesn; + mesq l("Uh, a %s? How odd.", get_race()); + next; + mesn; + mesq l("You see, I am Erlan, a researcher. I'm currently developing strong poison to rival with the legendary %s. For that end, I need raw, untreated sewerage, but there is no such thing in a civilized town like ours.", getitemlink(NymphPoison)); + next; + mesn; + mesq l("But you seem to have come from more savage and barbaric towns, so if you could bring me %d %s, that would be truly helpful.", 7, getitemlink(BottleOfSewerWater)); + setq FrostiaQuest_Erlan, 1; + close; + +L_Submit_U: + mesn; + mesq l("Did you got the %d %s I've asked?", 7, getitemlink(BottleOfSewerWater)); + next; + if (askyesno() == ASK_YES) { + if (countitem(BottleOfSewerWater) < 7) { + mesn; + mesq l("You think you can fool me and lie? I'll teach you a lesson you won't forget!"); + mesc l("%s slaps you!", .name$); + percentheal -30, 0; + close; + } + delitem BottleOfSewerWater, 7; + getexp 10000, 0; + Zeny+=5700; + setq FrostiaQuest_Erlan, 2; + mesn; + mesq l("Heh, thanks %s. Here's some money.", get_race()); + } + close; + +L_Done_U: + mesn; + mesq l("No, I don't have any poison for your sampling yet."); + next; + mesn; + mesq l("Our antecessors used to live peacefully in a sanctuary somewhere close to here, but the path was lost. That's why we now live with dwarves."); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADTOP, Beard); + setunitdata(.@npcId, UDT_HAIRSTYLE, 6); + setunitdata(.@npcId, UDT_HAIRCOLOR, 20); + npcsit; + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/024-1/guard.txt b/npc/024-1/guard.txt new file mode 100644 index 0000000..cefc16a --- /dev/null +++ b/npc/024-1/guard.txt @@ -0,0 +1,116 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Controls access to Frostia. + +024-1,93,73,0 script Frostia Guard NPC_ELF,{ + // Main Quest Have Precedence + if (getq(General_Narrator) == 11) + goto L_MainQuest; + if (getq(General_Narrator) == 12) + goto L_MainQuest2; + +L_MainLogic: + if (BaseLevel < 40) + goto L_Unallowed; + if (Class == Elven) + { + slide 93, 69; + npctalk3 l("Elves are always allowed inside."); + end; + } + // You have a house inside + if (ESTATE_RENTTIME >= gettimetick(2)) { + slide 93, 69; + npctalk3 l("I don't trust @@s.", get_race()); + end; + } + + //.@tax=1001-min(1000, reputation("Frostia")*10); + .@tax=501-min(500, reputation("Frostia")*5); + mesn; + mesc l("The guard eyes you with suspcion."); + mesq l("You should not be here. Get moving."); + if (Zeny < .@tax) + close; + next; + mesc l("Bribe the guard for @@ GP?", .@tax); + if (askyesno() == ASK_YES) { + Zeny-=.@tax; + closeclientdialog; + slide 93, 69; + npctalk3 l("Behave yourself."); + } + close; + +L_Unallowed: + mesn; + mesq l("You're not welcome here. Get moving."); + close; + + +L_MainQuest: + mesc b(l(".:: Main Quest 6-1 ::.")), 3; + msObjective(BaseLevel >= 40, l("* @@/@@ Base Level", BaseLevel, 40)); + mesc l("* Deliver Nikolai's Letter to Frostia Mayor"), 9; + if (BaseLevel < 40) + close; + next; + mesn strcharinfo(0); + select + l("I have a letter for the Mayor."); + mes ""; + mesn; + mesq l("We don't have a mayor. The independent city-state of Frostia is ruled by a King."); + next; + mesn; + mesq l("King Gelid Frozenheart II rules over all elves and dwarves who live here peacefully, and he also presides the city council."); + next; + mesn; + mesq l("I hate foreigners. They know nothing about us and want to impose their \"ideals\" about democracy and whatever."); + next; + mesn; + mesq l("Anyway, you had a letter to our @@, you meant? Sure thing, I'll let him know at once.", b(l("king"))); + setq General_Narrator, 12; + close; + +L_MainQuest2: + mesn; + mesq l("King Gelid Frozenheart II will have you."); + next; + mesn; + mesq l("His policy is to never read a letter without the sender being present. Of course we checked for poison and traps."); + next; + mesn; + mesq l("Behave yourself, or you'll be executed in the town square mercilessly. You have been warned."); + next; + closeclientdialog; + // Maybe an instance instead? + warp "024-16", 30, 52; + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, WarlordHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, WarlordPlate); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, JeansChaps); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex = G_MALE; + .distance = 5; + end; +} + +// This small script controls if you can visit King Gelid II or not +024-1,97,20,0 script #FrostiaCastle NPC_HIDDEN,1,0,{ +end; +OnTouch: + if (getq(General_Narrator) >= 12) + warp "024-16", 30, 52; + end; +} + + diff --git a/npc/024-1/john.txt b/npc/024-1/john.txt new file mode 100644 index 0000000..e1662d1 --- /dev/null +++ b/npc/024-1/john.txt @@ -0,0 +1,321 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// John H + +024-1,154,45,0 script Jhon Henryfield NPC_PLAYER,{ + if (BaseLevel < 42) { + asleep(); + end; + } + .@q=getq(FrostiaQuest_JhonH); + + // Quest failed + if (.@q == 8) { + mesn; + mesq l("Go away creep, I HATE YOU!"); + close; + } + + if (!.@q) { + mesn; + mesc l("ZZZzzzz..."); + mesc l("%s seems to be asleep... Maybe we can wake him up somehow?", .name$); + next; + do + { + select + l("Poke him"), + l("Attack him"), + l("Give him an item"); + mes ""; + if (@menu == 1) { + mesc l("No reply. We should try something else."); + next; + } else if (@menu == 2) { + mesn; + mesc l("*Opening eyes in a shock*"); + mesq l("AAAAaaahhhhh!!!"); + setq FrostiaQuest_JhonH, 8; // Quest failed + close; + } else { + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .@give = requestitem(); + if (.@give < 1) continue; + if (!countitem(.@give)) continue; + if (checkbound(.@give)) continue; + if (getiteminfo(.@give, ITEMINFO_TYPE) != IT_HEALING && + getiteminfo(.@give, ITEMINFO_TYPE) != IT_USABLE) continue; + mesc l("Really give a %s to Jhon?", getitemlink(.@give)); + mesc l("The item will be lost forever."), 1; + next; + if (askyesno() == ASK_NO) continue; + mes ""; + delitem .@give, 1; + if (.@give == Coffee) { + setq FrostiaQuest_JhonH, 1; + .@q = 1; + getexp 250, 25; + mesn; + mesq l("Coffee! I'm awake now!"); + next; + break; + } else { + mesc l("...No reaction..."); + next; + } + } + } while (true); + } + /////////////////////////// + if (!.@q) close; + do + { + mesn; + mesq l("How can I help you, my friend?"); + next; + select + l("Who are you?"), + rif(.@q >= 2 && countitem(Coffee), l("Do you want more coffee?")), + l("Nothing, thanks anyway."); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("Name is Jhon, and that's not a typo! I am an absolute coffee lover, and I travel to exotic places in search of the perfect brew."); + next; + mesn; + mesq l("Unfortunately, elves doesn't like to share coffee, so I thought in leaving but fell asleep. Oops!"); + next; + mesn; + mesq l("If you have some coffee to me, I'll buy it of your hands."); + compareandsetq FrostiaQuest_JhonH, 1, 2; + .@q = max(.@q, 2); + next; + break; + case 2: + mesc l("How much coffee you'll give Jhon?"); + input .@c, 0, countitem(Coffee); + mes ""; + .@c = min(.@c, countitem(Coffee)); + if (.@c < 1) break; + delitem Coffee, .@c; + .@q2 = getq2(FrostiaQuest_JhonH) + rand2(.@c); + .@q3 = getq3(FrostiaQuest_JhonH); + setq2 FrostiaQuest_JhonH, .@q2; + .@xp = 250 + rand2(getiteminfo(Coffee, ITEMINFO_SELLPRICE) / 3); + .@gp = getiteminfo(Coffee, ITEMINFO_SELLPRICE) * 2 / 3; // 67% GP + Zeny += .@c * .@gp; + getexp .@c * .@xp, .@c * 25; // ~33% EXP + bonus + mesn; + mesq lg("Thanks, gal!", "Thanks, pal!"); + next; + switch (.@q3) { + case 0: + if (.@q2 >= 1) { + inventoryplace Potatoz, 1; + getitem Potatoz, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 1: + if (.@q2 >= 5) { + inventoryplace IcedBottle, 1; + getitem IcedBottle, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 2: + if (.@q2 >= 10) { + inventoryplace ScrollBattlePlansA, 1; + getitem ScrollBattlePlansA, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 3: + if (.@q2 >= 15) { + inventoryplace ScrollDefenseBlessA, 1; + getitem ScrollDefenseBlessA, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 4: + if (.@q2 >= 20) { + inventoryplace ScrollSYeti, 1; + getitem ScrollSYeti, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 5: + if (.@q2 >= 25) { + inventoryplace AlchemyBlueprintA, 1; + getitem AlchemyBlueprintA, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 6: + if (.@q2 >= 30) { + inventoryplace AlchemyBlueprintB, 1; + getitem AlchemyBlueprintB, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 7: + if (.@q2 >= 40) { + .@i = any(LukPotionA, DexPotionA, IntPotionA, VitPotionA, AgiPotionA); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 8: + if (.@q2 >= 50) { + inventoryplace AlchemyBlueprintC, 1; + getitem AlchemyBlueprintC, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 9: + if (.@q2 >= 60) { + .@i = any(LukPotionC, DexPotionC, IntPotionC, VitPotionC, AgiPotionC, SacredLifePotion, SacredManaPotion); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 10: + if (.@q2 >= 75) { + inventoryplace AlchemyBlueprintD, 1; + getitem AlchemyBlueprintD, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 11: + if (.@q2 >= 90) { + inventoryplace AncientBlueprint, 1; + getitem AncientBlueprint, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 12: + if (.@q2 >= 100) { + .@i = any(LukPotionB, DexPotionB, IntPotionB, VitPotionB, AgiPotionB, SacredLifePotion, SacredManaPotion, DodgePotion, PrecisionPotion, MoveSpeedPotion, StatusResetPotion, DeathPotion, PurificationPotion, NymphPoison, ElixirOfLife); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 13: + if (.@q2 >= 120) { + .@i = any(LukPotionC, DexPotionC, IntPotionC, VitPotionC, AgiPotionC, SacredLifePotion, SacredManaPotion, DodgePotion, PrecisionPotion, MoveSpeedPotion, StatusResetPotion, DeathPotion, PurificationPotion, NymphPoison, ElixirOfLife); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 14: + if (.@q2 >= 140) { + .@i = any(LukPotionC, DexPotionC, IntPotionC, VitPotionC, AgiPotionC, SacredLifePotion, SacredManaPotion, DodgePotion, PrecisionPotion, MoveSpeedPotion, StatusResetPotion, DeathPotion, PurificationPotion, NymphPoison, ElixirOfLife, SacredImmortalityPotion, Manapple); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 15: + if (.@q2 >= 150) { + .@i = any(LukPotionC, DexPotionC, IntPotionC, VitPotionC, AgiPotionC, SacredLifePotion, SacredManaPotion, DodgePotion, PrecisionPotion, MoveSpeedPotion, StatusResetPotion, DeathPotion, PurificationPotion, NymphPoison, ElixirOfLife, SacredImmortalityPotion, AncientBlueprint, FrostiaWarpCrystal); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 16: + if (.@q2 >= 175) { + .@i = any(LukPotionC, DexPotionC, IntPotionC, VitPotionC, AgiPotionC, SacredLifePotion, SacredManaPotion, DodgePotion, PrecisionPotion, MoveSpeedPotion, StatusResetPotion, NymphPoison, ElixirOfLife, SacredImmortalityPotion, AncientBlueprint, FrostiaWarpCrystal, ScrollCriticalFortuneA); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 17: + if (.@q2 >= 200) { + .@i = any(LukPotionC, DexPotionC, IntPotionC, VitPotionC, AgiPotionC, SacredLifePotion, SacredManaPotion, ElixirOfLife, SacredImmortalityPotion, AncientBlueprint, FrostiaWarpCrystal, ScrollCriticalFortuneA, ScrollMagnusHealA, ScrollSWolvern); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 18: + if (.@q2 >= 250) { + .@i = any(LukPotionC, DexPotionC, IntPotionC, VitPotionC, AgiPotionC, SacredLifePotion, SacredManaPotion, ElixirOfLife, SacredImmortalityPotion, AncientBlueprint, FrostiaWarpCrystal, ScrollCriticalFortuneA, ScrollMagnusHealA, ScrollSWolvern); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 19: + if (.@q2 >= 300) { + .@i = any(LukPotionC, DexPotionC, IntPotionC, VitPotionC, AgiPotionC, SacredLifePotion, SacredManaPotion, SacredImmortalityPotion, AncientBlueprint, FrostiaWarpCrystal, ScrollCriticalFortuneA, ScrollMagnusHealA, ScrollSWolvern, GoldenApple); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 20: + if (.@q2 >= 400) { + .@i = any(SacredImmortalityPotion, AncientBlueprint, FrostiaWarpCrystal, ScrollCriticalFortuneA, ScrollMagnusHealA, ScrollSWolvern, PirateTreasureMap, TimeFlask); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 21: + if (.@q2 >= 450) { + .@i = any(ScrollCriticalFortuneB, ScrollBattlePlansB, ScrollDefenseBlessB, ScrollMagnusHealB, ScrollSDragon, GoldenApple); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + } + break; + case 22: + if (.@q2 >= 500) { + .@i = any(ScrollCriticalFortuneB, ScrollBattlePlansB, ScrollDefenseBlessB, ScrollMagnusHealB, ScrollSDragon, DivineApple); + inventoryplace .@i, 1; + getitem .@i, 1; + setq3 FrostiaQuest_JhonH, .@q3 + 1; + mesc l("You have reached the current reward limit for Jhon."), 1; + mesc l("Futurely, Mylarin Dust and a Sunny Crystal will also be available."); // And a Mysterious Fruit + Platinum, just like Ryan + } + break; + } + // Item acquired? + if (getq3(FrostiaQuest_JhonH) != .@q3) { + mesn; + mesq l("Here, you can have this, token of my appreciation."); + next; + } + break; + default: + .@q = 12; break; + } + } while (.@q < 8); + + closeclientdialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, AFKCap); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); // TODO + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 22); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + npcsit; + + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/024-1/mapflags.txt b/npc/024-1/mapflags.txt new file mode 100644 index 0000000..a58d5d8 --- /dev/null +++ b/npc/024-1/mapflags.txt @@ -0,0 +1,17 @@ +024-1 mapflag town +024-1 mapflag nopenalty +024-2 mapflag zone indoors +024-3 mapflag zone indoors +024-4 mapflag zone indoors +024-5 mapflag zone indoors +024-6 mapflag zone indoors +024-7 mapflag zone indoors +024-8 mapflag zone indoors +024-9 mapflag zone indoors +024-10 mapflag zone indoors +024-11 mapflag zone indoors +024-12 mapflag zone indoors +024-13 mapflag zone indoors +024-14 mapflag zone indoors +024-15 mapflag zone indoors +024-16 mapflag zone indoors diff --git a/npc/024-1/meriel.txt b/npc/024-1/meriel.txt new file mode 100644 index 0000000..109131e --- /dev/null +++ b/npc/024-1/meriel.txt @@ -0,0 +1,113 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +024-1,114,45,0 script Meriel NPC_ELF_F,{ + .@q = getq(FrostiaQuest_Meriel); + if (BaseLevel < 42) { + mesc l("The elf seems busy. She ignores you."); + close; + } + if (.@q == 0) { + if (frally()) goto L_Start_F; + else goto L_Start_U; + } + else if (.@q == 1) { + if (frally()) goto L_Submit_F; + else goto L_Submit_U; + } + else if (.@q == 2) { + if (frally()) goto L_Done_F; + else goto L_Done_U; + } + Exception("Invalid quest state - "+str(.@q)); + end; + +L_Start_F: + mesn; + mesq lg("Hey gal, could you do me a little favor?", "Hey pal, could you do me a little favor?"); + next; + mesn; + mesc l("*blushes*"); + mesq l("Bring me %d %s. I cannot tell you what for, though!", 18, getitemlink(PurpleBlobime)); + setq FrostiaQuest_Meriel, 1; + close; + +L_Submit_F: + mesn; + mesq l("Did you got the %d %s I've asked?", 18, getitemlink(PurpleBlobime)); + next; + if (askyesno() == ASK_YES) { + if (countitem(PurpleBlobime) < 18) { + mesn; + mesq l("You've been spending too much time with humans. You are picking up bad habits from them like lying. Be careful."); + close; + } + inventoryplace EquipmentBlueprintC, 1; + delitem PurpleBlobime, 18; + getitem EquipmentBlueprintC, 1; + getexp 4210, 0; + setq FrostiaQuest_Meriel, 2; + mesn; + mesq l("Ahh, he'll be so happy! Thanks! You can keep this."); + } + close; + +L_Done_F: + mesn; + mesq l("I've heard some elves, when they abandoned our original town, founded Aethyr somewhere up northwest. But all contact with it has since been lost."); + close; +///////////////////////////////////////////////////////////////////////////////// +L_Start_U: + mesn; + mesq l("Hey, can you spare me a moment?"); + next; + mesn; + mesc l("*blushes*"); + mesq l("I want %d %s! Don't worry, I can pay.", 18, getitemlink(PurpleBlobime)); + setq FrostiaQuest_Meriel, 1; + close; + +L_Submit_U: + mesn; + mesq l("Did you got the %d %s I've asked?", 18, getitemlink(PurpleBlobime)); + next; + if (askyesno() == ASK_YES) { + if (countitem(PurpleBlobime) < 18) { + mesn; + mesq l("You liar, I'll show you to respect woman!"); + mesc l("%s slaps you!", .name$); + percentheal -30, 0; + close; + } + inventoryplace EquipmentBlueprintC, 1; + delitem PurpleBlobime, 18; + getitem EquipmentBlueprintC, 1; + getexp 4210, 0; + setq FrostiaQuest_Meriel, 2; + mesn; + mesq l("Hihihi... Thanks! Here, you can keep this."); + } + close; + +L_Done_U: + mesn; + mesq l("I've heard some elves, when they abandoned our original town, founded Aethyr somewhere up northwest. But all contact with it has since been lost."); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonSkirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADTOP, TrapperHat); + setunitdata(.@npcId, UDT_HAIRSTYLE, 8); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/024-1/rydel.txt b/npc/024-1/rydel.txt new file mode 100644 index 0000000..1c1648d --- /dev/null +++ b/npc/024-1/rydel.txt @@ -0,0 +1,115 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +024-1,32,26,0 script Rydel NPC_ELF,{ + .@q = getq(FrostiaQuest_Rydel); + if (BaseLevel < 50) { + mesc l("The elf seems busy. He ignores you."); + close; + } + if (.@q == 0) { + if (frally()) goto L_Start_F; + else goto L_Start_U; + } + else if (.@q == 1) { + if (frally()) goto L_Submit_F; + else goto L_Submit_U; + } + else if (.@q == 2) { + if (frally()) goto L_Done_F; + else goto L_Done_U; + } + Exception("Invalid quest state - "+str(.@q)); + end; + +L_Start_F: + mesn; + mesq lg("Hey gal, could you do me a little favor?", "Hey pal, could you do me a little favor?"); + next; + mesn; + mesq l("Bring me %d %s so I may study its properties and improve our town water quality!", 3, getitemlink(BottleOfDivineWater)); + setq FrostiaQuest_Rydel, 1; + close; + +L_Submit_F: + mesn; + mesq l("Did you got the %d %s I've asked?", 3, getitemlink(BottleOfDivineWater)); + next; + if (askyesno() == ASK_YES) { + if (countitem(BottleOfDivineWater) < 3) { + mesn; + mesq l("You've been spending too much time with humans. You are picking up bad habits from them like lying. Be careful."); + close; + } + delitem BottleOfDivineWater, 3; + getexp 24000, 0; + setq FrostiaQuest_Rydel, 2; + mesn; + mesq lg("Thanks to you, my friend, we will soon have good water to drink."); + } + close; + +L_Done_F: + mesn; + mesq l("I am not done purifying the water yet. This may take years, but it'll be worth it."); + next; + mesn; + mesq l("By the way, I've heard that the name of the wizard of Aethyr is the key to open its gates. But I don't know neither where the gates are, nor who is the \"wizard of Aethyr\"."); + close; +///////////////////////////////////////////////////////////////////////////////// +L_Start_U: + mesn; + mesq l("Hey silly %s, I need a favor from you.", get_race()); + next; + mesn; + mesq l("I am not content with our water supply. It gives us common water, but we of the superior race deserve better water."); + next; + mesn; + mesq l("So! Bring me %d %s so I may study its properties and make a purifier or enhancer for our well.", 3, getitemlink(BottleOfDivineWater)); + setq FrostiaQuest_Rydel, 1; + close; + +L_Submit_U: + mesn; + mesq l("Did you got the %d %s I've asked?", 3, getitemlink(BottleOfDivineWater)); + next; + if (askyesno() == ASK_YES) { + if (countitem(BottleOfDivineWater) < 3) { + mesn; + mesq l("You liar, I'll teach you a lesson you won't forget!"); + mesc l("%s slaps you!", .name$); + percentheal -30, 0; + close; + } + delitem BottleOfDivineWater, 3; + getexp 24000, 0; + setq FrostiaQuest_Rydel, 2; + mesn; + mesq l("Thanks to you, lowly %s, we will soon have water we deserve!", get_race()); + } + close; + +L_Done_U: + mesn; + mesq l("I won't share our water with you!"); + next; + mesn; + mesq l("The name of the wizard of Aethyr is the key to open its gates. I've heard it'll open for a lowly %s as you, as long that you know both. But I don't think anyone here would share that with you!", get_race()); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADTOP, Beard); + setunitdata(.@npcId, UDT_HAIRSTYLE, 4); + setunitdata(.@npcId, UDT_HAIRCOLOR, 1); + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/024-1/soul-menhir.txt b/npc/024-1/soul-menhir.txt new file mode 100644 index 0000000..2820399 --- /dev/null +++ b/npc/024-1/soul-menhir.txt @@ -0,0 +1,20 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Soul Menhir + +024-1,94,41,0 script Soul Menhir#frost NPC_SOUL_SNOW,{ + @map$ = "024-1"; + setarray @Xs, 93, 94, 95, 93, 95, 93, 94, 95; + setarray @Ys, 40, 40, 40, 41, 41, 42, 42, 42; + @x = 0; + @y = 0; + callfunc "SoulMenhir"; + @map$ = ""; + cleararray @Xs[0], 0, getarraysize(@Xs); + cleararray @Ys[0], 0, getarraysize(@Ys); + @x = 0; + @y = 0; + close; +} diff --git a/npc/024-1/taenya.txt b/npc/024-1/taenya.txt new file mode 100644 index 0000000..fcf9c23 --- /dev/null +++ b/npc/024-1/taenya.txt @@ -0,0 +1,123 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +024-1,58,28,0 script Taenya NPC_ELF_F,{ + .@q = getq(FrostiaQuest_Taenya); + if (BaseLevel < 52) { + mesc l("The elf seems busy. She ignores you."); + close; + } + if (.@q == 0) { + if (frally()) goto L_Start_F; + else goto L_Start_U; + } + else if (.@q == 1) { + if (frally()) goto L_Submit_F; + else goto L_Submit_U; + } + else if (.@q == 2) { + if (frally()) goto L_Done_F; + else goto L_Done_U; + } + Exception("Invalid quest state - "+str(.@q)); + end; + +L_Start_F: + mesn; + mesq lg("Hey friend, could you do me a little favor?"); + next; + mesn; + mesq l("I always wanted to taste a %s! But they don't grow anywhere, and I've heard the only way to obtain one is from %s. Unfortunately, I do not favor violence, so... Could you get one for me?", getitemlink(Manapple), getitemlink(BronzeBossGift)); + next; + mesn; + mesq l("Thanks, I owe you one!"); + setq FrostiaQuest_Taenya, 1; + close; + +L_Submit_F: + mesn; + mesq l("Did you got the %d %s I've asked?", 1, getitemlink(Manapple)); + next; + if (askyesno() == ASK_YES) { + if (countitem(Manapple) < 1) { + mesn; + mesq l("You've been spending too much time with humans. You are picking up bad habits from them like lying. Be careful."); + close; + } + inventoryplace EquipmentBlueprintC, 1; + delitem Manapple, 1; + getitem EquipmentBlueprintC, 1; + getexp 12000, 0; + setq FrostiaQuest_Taenya, 2; + mesn; + mesq l("Yay! Many thanks! I'll enjoy it! Here, you can keep this."); + } + close; + +L_Done_F: + mesn; + mesq l("Hey, the %s you gave me was quite tasty, thanks!", getitemlink(Manapple)); + next; + mesn; + mesq l("%s is known as the Wizard of Aethyr. They say the elves which lived in Aethyr have became fairies, but contact has been lost long ago... I've heard it was a very pretty town north of here, though.", b("Tametomo")); + close; +///////////////////////////////////////////////////////////////////////////////// +L_Start_U: + mesn; + mesq l("Hey %s, could you do me a little favor?", get_race()); + next; + mesn; + mesq l("I always wanted to taste a %s! But they don't grow anywhere, and I've heard the only way to obtain one is from %s. Unfortunately, I do not favor violence, so... Could you get one for me?", getitemlink(Manapple), getitemlink(BronzeBossGift)); + next; + mesn; + mesq l("I'll be waiting!"); + setq FrostiaQuest_Taenya, 1; + close; + +L_Submit_U: + mesn; + mesq l("Did you got the %d %s I've asked?", 1, getitemlink(Manapple)); + next; + if (askyesno() == ASK_YES) { + if (countitem(Manapple) < 1) { + mesn; + mesq l("You liar, I'll teach you a lesson you won't forget!"); + mesc l("%s stabs you with a dagger!", .name$); + percentheal -55, 0; + close; + } + inventoryplace EquipmentBlueprintC, 1; + delitem Manapple, 1; + getitem EquipmentBlueprintC, 1; + getexp 12000, 0; + setq FrostiaQuest_Taenya, 2; + mesn; + mesq l("Thanks! I'll enjoy it! Here, you can keep this."); + } + close; + +L_Done_U: + mesn; + mesq l("Sorry, but I already ate the %s you gave me!", getitemlink(Manapple)); + next; + mesn; + mesq l("%s is known as the Wizard of Aethyr. They say the elves which lived in Aethyr have became fairies, but contact has been lost long ago... I've heard it was a very pretty town north of here, though.", b("Tametomo")); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonSkirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADTOP, TrapperHat); + setunitdata(.@npcId, UDT_HAIRSTYLE, 9); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/024-1/teleporter.txt b/npc/024-1/teleporter.txt new file mode 100644 index 0000000..2bed0ae --- /dev/null +++ b/npc/024-1/teleporter.txt @@ -0,0 +1,23 @@ +// TMW2 Script +// Authors: +// Jesusalva +// Description: +// Link portals to soul menhirs like the teleporters from old +// The price is temporary. This feature got in because no ship in Nivalis Port +// PS. Anise => “Aisen” Anagram + + +024-1,155,80,0 script #WarpGateFrost NPC_NO_SPRITE,1,0,{ + end; + +OnTouch: + TeleporterGate(TP_FROST); + close; + + +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + diff --git a/npc/024-1/town.txt b/npc/024-1/town.txt new file mode 100644 index 0000000..3e21e4f --- /dev/null +++ b/npc/024-1/town.txt @@ -0,0 +1,12 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Reset LOCATION$ when entering a town + +024-1,93,69,0 script #LocFrostia NPC_HIDDEN,2,0,{ +OnTouch: + EnterTown("Frostia"); + end; +} + diff --git a/npc/024-10/_import.txt b/npc/024-10/_import.txt new file mode 100644 index 0000000..c1656f2 --- /dev/null +++ b/npc/024-10/_import.txt @@ -0,0 +1,5 @@ +// Map 024-10: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-10/_warps.txt", +"npc/024-10/eldrin.txt", +"npc/024-10/haircut.txt", diff --git a/npc/024-10/_warps.txt b/npc/024-10/_warps.txt new file mode 100644 index 0000000..d94ba38 --- /dev/null +++ b/npc/024-10/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-10: Frostia Indoors warps +024-10,34,37,0 warp #024-10_34_37 0,0,024-1,83,60 +024-10,24,37,0 warp #024-10_24_37 0,0,024-1,77,60 diff --git a/npc/024-10/eldrin.txt b/npc/024-10/eldrin.txt new file mode 100644 index 0000000..bce65e0 --- /dev/null +++ b/npc/024-10/eldrin.txt @@ -0,0 +1,143 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 - Frostia's Bounty House + +024-10,33,30,0 script Eldrin NPC_ELF,{ + //.@q = getq(FrostiaQuest_Rydel); + if (BaseLevel < 30) { + mesc l("The elf seems busy. He ignores you."); + close; + } + .@d=gettimeparam(GETTIME_DAYOFMONTH); + mesn l("Eldrin, Taskmaster"); + mesq l("Hello, and welcome to Frostia bounty hunter guild."); + next; + mesn l("Eldrin, Taskmaster"); + mesq l("I have bounties for specific high level monsters as well for extermination. Do note mobs killed in Heroes Hold may not count."); + next; + mesn l("Eldrin, Taskmaster"); + mesq l("Be careful that they may be anywhere in the world, and the same rules as Tulimshar Guard House apply! So, are you here to apply or to report back?"); + mesc l("Protip: All quests expire at 00:00 server time, be sure to finish AND report back before that!"); + next; + // Borrowed from Arkim Code + mesc l("Time remaining to report completion: ")+FuzzyTime($@ARKIM_TIMER+86400), 1; + + select + l("I want a MODERATE (Lv 50~70) task!"), + l("I want a EXPERT (Lv 70~100) task!"), + l("I want a MASTER (Lv 100+) task!"), + l("I want a BOSS task!"), + l("It was nice seeing you."); + mes ""; + switch (@menu) { + case 1: + .@q1=getq(General_MobHunting5); + .@q2=getq2(General_MobHunting5); + .@q3=getq3(General_MobHunting5); + .@q=General_MobHunting5; + .@lv=9; + goto L_QuestMaster; + case 2: + .@q1=getq(General_MobHunting6); + .@q2=getq2(General_MobHunting6); + .@q3=getq3(General_MobHunting6); + .@q=General_MobHunting6; + .@lv=15; + goto L_QuestMaster; + case 3: + .@q1=getq(General_MobHunting7); + .@q2=getq2(General_MobHunting7); + .@q3=getq3(General_MobHunting7); + .@q=General_MobHunting7; + .@lv=24; + goto L_QuestMaster; + case 4: + .@q1=getq(General_MobHunting8); + .@q2=getq2(General_MobHunting8); + .@q3=getq3(General_MobHunting8); + .@q=General_MobHunting8; + .@lv=50; + goto L_QuestMaster; + } + closeclientdialog; + goodbye(); + close; + +L_QuestMaster: + // It's a new day, so we must generate a new quest! + if (.@q1 != .@d) { + .@q1=.@d; + if (.@lv == 9) { + .@q2=any(EarthFairy, FireFairy, WaterFairy, WindFairy, PoisonFairy, BlackScorpion, MountainSnake, ForestMushroom, GoldenScorpion, Yeti, WickedMushroom, Archant, Scar, Crafty); + } else if (.@lv == 15) { + .@q2=any(Crafty, Forain, GreenDragon, Troll, Moonshroom, Terranite, JackO, BlackMamba, Centaur, GoboBear); + } else if (.@lv == 24) { + .@q2=any(TerraniteProtector, EliteDuck, Reaper, NightmareDragon, NightmareDragon, PinkieSuseran, PinkieMaximus); + } else if (.@lv == 50) { + .@q2=any(Tengu, Tipiu, EvilScythe, GiantCaveMaggot, SpiderQueen, TerraniteKing, PinkieEmperor, Yetifly, YetiKing); + } else { + Exception("Bad setting for GMH.LV: "+.@lv, RB_DEFAULT|RB_SPEECH|RB_ISFATAL); + } + setq .@q, .@q1, .@q2, 0; + } + + // Maybe there is no monster to kill + if (!.@q2) { + mesn l("Eldrin, Taskmaster"); + mesq l("You've already completed this quest today. Try again tomorrow."); + close; + } + + // You only need to slay one boss + if (.@q3 && .@lv == 50) + .@q3 += 50; + + // Maybe you finished the quest? + // Reuses everything from Kreist + if (.@q3 >= 50) { + mesn l("Eldrin, Taskmaster"); + mesq l("Good job, you've killed the @@ @@ and reported back in time!", 50, getmonsterlink(.@q2)); + next; + inventoryplace MercBoxA, 1; + if (MERCENARY_DAILYQUEST == 100) { + inventoryplace BountyHunterHelmet, 1; + getitem BountyHunterHelmet, 1; + } + mesn l("Eldrin, Taskmaster"); + mesq l("It's not much of a reward, but doesn't it feels great to help others in need?! HAHAHA!"); + .@overkill=.@q3-50; + Zeny+=.@lv*(42+MERCENARY_DAILYQUEST); + // Raise LV according to monster level + .@lv+=getmonsterinfo(.@q2, MOB_LV)+MERCENARY_DAILYQUEST*11/10; + getexp BaseLevel*.@lv, .@lv+.@overkill; + setq2 .@q, 0; + setq3 .@q, 0; + MERCENARY_DAILYQUEST+=1; + if (MERCENARY_DAILYQUEST % 5 == 0) { + getitem MercBoxA, 1; + } + close; + } + + mesn l("Eldrin, Taskmaster"); + if (.@lv != 50) + mesq l("So, please kill @@/@@ @@ for us and make the world a safer place!", .@q3, 50, getmonsterlink(.@q2)); + else + mesq l("So, please kill a %s for us and make the world a safer place!", getmonsterlink(.@q2)); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_HEADMIDDLE, Chainmail); + setunitdata(.@npcId, UDT_HEADTOP, VikingHelmet); + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/024-10/haircut.txt b/npc/024-10/haircut.txt new file mode 100644 index 0000000..a7d9311 --- /dev/null +++ b/npc/024-10/haircut.txt @@ -0,0 +1,66 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Haircut. + +024-10,27,30,0 script Cato Mil NPC_ELVEN_FEMALE_ARMOR_SHOP,{ + function setRace { + clear; + setnpcdialogtitle l("Debug - Modify Race"); + mes l("Race") + ": " + $@allraces$[Class]; + next; + mes l("Please select the desired race."); + select("Human:Ukar:Redy:Elf:Orc:Raijin:Tritan"); + jobchange max(0, @menu-1); + return; + } + + + mesn; + mesq l("Hi! Do you want a hair cut?"); + + do + { + select + l("What is my current hairstyle and hair color?"), + l("I'd like to get a different style."), + l("Can you do something with my color?"), + rif(is_staff(), l("I am a GM, and I want to change my Race!")), + l("I'm fine for now, thank you."); + + switch (@menu) + { + case 1: + BarberSayStyle 3; + break; + case 2: + BarberChangeStyle; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Enjoy your new style."); + l("Anything else?"); + break; + case 3: + BarberChangeColor; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("I hope you like this color."); + l("Anything else?"); + break; + case 4: + setRace; + break; + case 5: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Feel free to come visit me another time."); + + goodbye; + } + } while (1); + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/024-11/_import.txt b/npc/024-11/_import.txt new file mode 100644 index 0000000..8a4943b --- /dev/null +++ b/npc/024-11/_import.txt @@ -0,0 +1,4 @@ +// Map 024-11: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-11/_warps.txt", +"npc/024-11/politics.txt", diff --git a/npc/024-11/_warps.txt b/npc/024-11/_warps.txt new file mode 100644 index 0000000..7a50b04 --- /dev/null +++ b/npc/024-11/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-11: Frostia Indoors warps +024-11,24,35,0 warp #024-11_24_35 0,0,024-1,105,35 diff --git a/npc/024-11/politics.txt b/npc/024-11/politics.txt new file mode 100644 index 0000000..efb7fcb --- /dev/null +++ b/npc/024-11/politics.txt @@ -0,0 +1,63 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Town Administrator file, see npc/functions/politics.txt +// User variables: +// #POL_APPLYWEEK = Week of last application +// #POL_VOTEDAY = Day of last vote + +024-11,24,30,0 script Frostia Office NPC_POLITICS,{ +do +{ + mesc ".:: "+l("Frostia Townhall")+" ::.", 2; + mesc l("Current Town Administrator: ")+$FROSTIA_MAYOR$, 3; + if (Class != Elven) + mesc l("Only elves may run to Town Admin Office in Frostia!"), 1; + else + mesc l("Hey, you're an elf, cool! But you still cannot run for office here!"), 1; + close; + POL_TownInfo("FROSTIA"); + mesc l("Application fee: @@ GP", .applytax); + next; + select + l("Information"), + rif(strcharinfo(0) == $FROSTIA_MAYOR$, l("Manage Town")), + rif(#POL_APPLYWEEK != gettimeparam(GETTIME_WEEKDAY), l("Apply for the office!")), + l("View Candidate List and cast a vote"), + l("[Quit]"); + + switch (@menu) { + case 1: + POL_Information(); + break; + case 2: + POL_Manage("FROSTIA"); + break; + case 3: + // array_push might be too sensible for getd/setd + if (Zeny < .applytax) + break; + Zeny-=.applytax; + $FROSTIA_MONEY+=.applytax; + #POL_APPLYWEEK=gettimeparam(GETTIME_WEEKDAY); + array_push($FROSTIA_CANDIDATE$, strcharinfo(0)); + array_push($FROSTIA_VOTES, 0); + mesc l("Application successful!"), 3; + next; + break; + case 4: + POL_Candidate("FROSTIA"); + break; + default: + close; + } +} while (true); +end; + +OnInit: + .applytax=50; + .distance=4; + end; +} + diff --git a/npc/024-12/_import.txt b/npc/024-12/_import.txt new file mode 100644 index 0000000..1715d18 --- /dev/null +++ b/npc/024-12/_import.txt @@ -0,0 +1,4 @@ +// Map 024-12: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-12/_warps.txt", +"npc/024-12/alicia.txt", diff --git a/npc/024-12/_warps.txt b/npc/024-12/_warps.txt new file mode 100644 index 0000000..27cb4f4 --- /dev/null +++ b/npc/024-12/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-12: Frostia Indoors warps +024-12,31,35,0 warp #024-12_31_35 0,0,024-1,76,46 diff --git a/npc/024-12/alicia.txt b/npc/024-12/alicia.txt new file mode 100644 index 0000000..05c653c --- /dev/null +++ b/npc/024-12/alicia.txt @@ -0,0 +1,71 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Status Reset. (+ Alchemy Recipe?) + +024-12,28,29,0 script Alicia NPC_ELF_F,{ + + speech S_LAST_NEXT, + l("I am @@, an alchemist specialized in reset potions.", .name$); + +L_Menu: + .@plush_count = BaseLevel*200-(9*200); + // Lv 10: 200 GP + // Lv 90: 16.000 GP + if (BaseLevel > 10) + .@plush_count = .@plush_count/(BaseLevel/10); + + select + l("Can you reset my stats please?"), + l("Can you teach me some Alchemy Recipe?"), + lg("You are weird, I have to go sorry."); + + switch (@menu) + { + case 1: + goto L_ResetStats; + case 2: + goto L_Recipe; + case 3: + goto L_Quit; + } + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + ConfirmStatusReset(); + goto L_Quit; + +L_Recipe: + mes ""; + mesn; + mesq l("Oh my! Did you rent a house or an apartment and now want to brew stuff, like @@ or @@?", getitemlink(PrecisionPotion), getitemlink(StatusResetPotion)); + next; + mesn; + mesq l("...That's your problem, not mine. I am an elf if you haven't noticed."); + next; + goto L_Menu; + +L_Quit: + closedialog; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, Earmuffs); + setunitdata(.@npcId, UDT_HEADMIDDLE, TneckSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, FurBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 11); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .sex = G_FEMALE; + .distance = 5; + end; + +} + diff --git a/npc/024-13/_import.txt b/npc/024-13/_import.txt new file mode 100644 index 0000000..b264542 --- /dev/null +++ b/npc/024-13/_import.txt @@ -0,0 +1,4 @@ +// Map 024-13: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-13/_warps.txt", +"npc/024-13/manager.txt", diff --git a/npc/024-13/_warps.txt b/npc/024-13/_warps.txt new file mode 100644 index 0000000..522fb3e --- /dev/null +++ b/npc/024-13/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-13: Frostia Indoors warps +024-13,30,37,0 warp #024-13_30_37 0,0,024-1,50,55 diff --git a/npc/024-13/manager.txt b/npc/024-13/manager.txt new file mode 100644 index 0000000..9931f4c --- /dev/null +++ b/npc/024-13/manager.txt @@ -0,0 +1,123 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System + +// ESTATE_ID → Instance ID of the Estate (required for NPCs, expire) +// ESTATE_RENTTIME → When the rent will expire +// ESTATE_MOBILIA_2 → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// ESTATE_MOBILIA_4 → Bitmask of mobilia currently purchased on Air Collision (2) +// ESTATE_MOBILIA_8 → Bitmask of mobilia currently purchased on Water Collision (3) +// ESTATE_MOBILIA_32 → Bitmask of mobilia currently purchased on Yellow Collision (4) +// ESTATE_MOBILIA_64 → Bitmask of mobilia currently purchased on Normal Collision (1) +// ESTATE_MOBILIA_128 → Bitmask of mobilia currently purchased on Player Collision (5) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +024-13,31,32,0 script Apartment Manager NPC_ELF,{ + if (ESTATE_RENTTIME < gettimetick(2)) + goto L_RentAvailable; + + mesn; + mesq l("Your rent is valid for @@.", FuzzyTime(ESTATE_RENTTIME)); + mesc l("Apartment rents cannot be renewed until they expire. Furniture won't be lost."); + close; + +L_RentAvailable: + do + { + mesc l("This Real Estate is available for rent for only @@ GP!", format_number(.price)); + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("You currently have: @@ GP and mobiliary credits", format_number(.@gp)); + next; + select + rif(.@gp > .price, l("Rent it! Make it mine!")), + l("Information"), + l("Don't rent it"); + + // You want to rent + if (@menu == 1) { + realestate_payment(.price); + + // Payment done, you can now acquire the house for a month + ESTATE_RENTTIME=gettimetick(2)+.time; + + mesc l("Rent successful for 30 days!"); + } else if (@menu == 2) { + mesc l("You can rent this house to make it yours.") + " " + l("The rent lasts 30 days."); + mesc l("Then you'll be able to buy furniture and utility."); + mesc l("This is an apartment. You cannot renew until it expire, and cannot invite guests."); + next; + mesc l("Both rent and furniture are bought using money, however, there are mobiliary credits."); + mesc l("Mobiliary Credits is a special currency which can only be used on real estate."); + mesc l("It's obtained with ADMINS or by selling furniture. It is sumed to money and used first."); + next; + } + } while (@menu == 2); + close; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, BowlerHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, CreasedShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + setunitdata(.@npcId, UDT_WEAPON, LeatherTrousers); + + .price=5000; // Monthly rent price. + .time=2592000; // Defaults to 30 days + end; + +} + +// Door entrance +024-13,29,28,0 script #RES_PPL NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (ESTATE_RENTTIME < gettimetick(2)) + goto L_RentAvailable; + + // Warp you to your apartment if exists in memory already. + // Build the instance otherwise. + + // Well, "checking if instance exist by mapname" is an illusion. + // So we try to build and if we fail, we warp the player to the instance. + .@ID=getcharid(0); + @MAP_NAME$="ples@"+str(.@ID); // Max 4 chars for map name + + .@INSTID = instance_create("ples@a"+(.@ID), getcharid(3), IOT_CHAR); + + // Instance already exists - .@INSTID returns "-4" + if (.@INSTID == -4) { + warp @MAP_NAME$, 33, 33; + end; + } + + // Attach the map + .@instanceMapName$ = instance_attachmap("024-14", .@INSTID, 0, @MAP_NAME$); + + // Record important stuff & load furniture + ESTATE_ID=.@INSTID; + addtimer(20, instance_npcname("Doorbell#RES_PPL", .@INSTID)+"::OnReload"); + addtimer(70, instance_npcname("NPCs#RES_PPL", .@INSTID)+"::OnReload"); + + // It'll be self-destroyed eventually... + instance_set_timeout(1000000, 1000000, .@INSTID); + instance_init(.@INSTID); + warp @MAP_NAME$, 33, 33; + end; + +L_RentAvailable: + dispbottom l("You do not have booked an apartment here."); + close; + +OnInit: + .distance=1; + end; + +} + diff --git a/npc/024-14/_import.txt b/npc/024-14/_import.txt new file mode 100644 index 0000000..2c45a9c --- /dev/null +++ b/npc/024-14/_import.txt @@ -0,0 +1,5 @@ +// Map 024-14: Real Estate +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-14/_warps.txt", +"npc/024-14/doorbell.txt", +"npc/024-14/utils.txt", diff --git a/npc/024-14/_warps.txt b/npc/024-14/_warps.txt new file mode 100644 index 0000000..de798c0 --- /dev/null +++ b/npc/024-14/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-14: Real Estate warps +024-14,34,34,0 warp #024-14_34_34 1,0,024-13,29,29 diff --git a/npc/024-14/doorbell.txt b/npc/024-14/doorbell.txt new file mode 100644 index 0000000..ac6452a --- /dev/null +++ b/npc/024-14/doorbell.txt @@ -0,0 +1,328 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Doorbell allows you to purchase mobilia, besides loading it when server starts +// Each layer can have 32 different furniture pieces because bitmask limit. +// This file is custom to every room + +// ESTATE_ID → Instance ID of the Estate (required for NPCs, expire) +// ESTATE_RENTTIME → When the rent will expire +// ESTATE_MOBILIA_2 → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// ESTATE_MOBILIA_4 → Bitmask of mobilia currently purchased on Air Collision (2) +// ESTATE_MOBILIA_8 → Bitmask of mobilia currently purchased on Water Collision (3) +// ESTATE_MOBILIA_32 → Bitmask of mobilia currently purchased on Yellow Collision (4) +// ESTATE_MOBILIA_64 → Bitmask of mobilia currently purchased on Normal Collision (1) +// ESTATE_MOBILIA_128 → Bitmask of mobilia currently purchased on Player Collision (5) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller +024-14,32,34,0 script Doorbell#RES_PPL NPC_NO_SPRITE,{ + // Name, Layer, Price, ID, x1, y1, x2, y2, + function create_object { + array_push(.nams$, getarg(0)); + array_push(.layer, getarg(1)); + array_push(.price, getarg(2)); + array_push(.objid, getarg(3)); + array_push(.x1, getarg(4)); + array_push(.y1, getarg(5)); + array_push(.x2, getarg(6)); + array_push(.y2, getarg(7)); + return; + } + function re2_sellprice; + function re2_togglemobilia; + function re2_hasmobilia; + + .id=getcharid(0); + goto L_Manage; +// Managment Menu +L_Manage: + mesc l("@@'s Apartment", strcharinfo(0)); + mesc ".:: "+ l("Managment Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Rent time available: @@", FuzzyTime(ESTATE_RENTTIME)); + mesc l("Total Credits and GP: @@", format_number(.@gp)); + mes ""; + + next; + select + l("Leave"), + l("Manage Furniture"), + rif(is_staff(), l("Reload NPC Data")); + + switch (@menu) { + case 1: + close; + break; + case 2: + goto L_Furniture; + break; + case 3: + addtimer2(150, instance_npcname("NPCs#RES_PPL")+"::OnReload"); + close; + break; + } + goto L_Manage; + +L_Furniture: + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Furniture Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Total Credits and GP: @@", format_number(.@gp)); + + next; + select + l("Finish"), + l("Manage Beds"), + l("Manage Utilities"), + l("Manage Luxury furniture"), + l("Manage Decoration"), + l("Manage Chairs"), + l("Manage Paintings"); + mes ""; + + switch (@menu) { + case 1: + goto L_Manage; + break; + case 2: + mesc ".:: "+ l("Beds") + " ::.", 3; + @re_col=RES_OBJECTS; + break; + case 3: + mesc ".:: "+ l("Utilities") + " ::.", 3; + @re_col=RES_UTILITIES; + break; + case 4: + mesc ".:: "+ l("Luxury furniture") + " ::.", 3; + @re_col=RES_LUXURY; + break; + case 5: + mesc ".:: "+ l("Decoration") + " ::.", 3; + @re_col=RES_DECORATION; + break; + case 6: + mesc ".:: "+ l("Chairs") + " ::.", 3; + @re_col=RES_SITTABLE; + break; + case 7: + mesc ".:: "+ l("Paintings") + " ::.", 3; + @re_col=RES_WALLDECORATION; + break; + } + +// L_ContinuousLoop +// Requires the following variables: +// @re_col +// Target Collision ID +L_ContinuousLoop: + deletearray @valid_ids; + + // Create a second array (@valid_ids) with the ID of objects within @re_col group + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + //debugmes "Found object ID %d named %s on layer %s coords (%d,%d) - Looking for layer %d", .@i, .nams$[.@i], .layer[.@i], .x1[.@i], .y1[.@i], @re_col; + if (.layer[.@i] == @re_col) + array_push(@valid_ids, .@i); + } + //debugmes "Found %d valid objects", getarraysize(@valid_ids); + + // Create the menu with @valid_ids - Check if you already have the item to decide if you're buying or selling + @menuentries$="Finish:"; + for (.@j=0; .@j < getarraysize(@valid_ids); .@j++) { + .@i=@valid_ids[.@j]; + if (re2_hasmobilia(.id, .layer[.@i], .objid[.@i])) + @menuentries$+=l("Sell ")+.nams$[.@i]+l(" for ") + format_number( re2_sellprice(.id,.price[.@i]) ) +":"; + else + @menuentries$+=l("Purchase ")+.nams$[.@i]+(" for ") + format_number( .price[.@i] )+":"; + } + select (@menuentries$); + mes ""; + + // First option to return to previous menu + if (@menu == 1) + goto L_Furniture; + + // Otherwise, we know then that (@menu-2) is the ID in @valid_ids + // So we save .@id with the correct ID in object arrays. + // We also calculate how much aggregated money you have. + .@id=@valid_ids[@menu-2]; + .@gp=REAL_ESTATE_CREDITS+Zeny; + + if (re2_hasmobilia(.id, .layer[.@id], .objid[.@id])) { + // If you have the mobilia, you're selling it for Mobiliary Credits + delcells realestate_cellname(.id, .@id); + re2_togglemobilia(.id, .layer[.@id], .objid[.@id]); + addtimer2(150, "NPCs#RES_PPL::OnReload"); + REAL_ESTATE_CREDITS+=re2_sellprice(.id,.price[.@i]); + mesc l("Sale successful!"); + next; + } else { + // Else, you're buying it, so we must check if you have the moolah first + .@price=.price[.@id]; + if (.@gp > .@price) { + realestate_payment(.@price); + setcells "ples@"+getcharid(0), .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + areatimer("ples@"+getcharid(0), .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], 10, "::OnSlide"); + re2_togglemobilia(.id, .layer[.@id], .objid[.@id]); + addtimer2(150, "NPCs#RES_PPL::OnReload"); + mesc l("Purchase successful!"); + next; + } else { + mesc l("Not enough funds!"); + next; + } + } + + // This loops forever + goto L_ContinuousLoop; + + +// When using setcells() a player could get trapped! +// This label will slide the player back to entrance, which should be a safe spot +OnSlide: + slide 33, 33; + end; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + // Arrays + // We go element by element on the array building the menu + .nams$=""; + .layer=0; + .price=0; + .objid=0; + .x1=0; + .y1=0; + .x2=0; + .y2=0; + + // Furniture Settings + // Name, Collision Layer, Price, ID, x1, y1, x2, y2 + // For Collision Layer, see constants.conf ("Real Estate Collisions") + create_object("Placeholder" ,99,999999,99999, 99, 99, 99, 99); + + create_object("Red Bed" , 5, 5000, 1, 25, 29, 26, 32); + create_object("Blue Bed" , 5, 5000, 2, 27, 29, 28, 32); + + create_object("Wardrobe" , 1, 7000, 1, 25, 26, 26, 26); + create_object("Cauldron" , 1, 5000, 2, 28, 27, 29, 27); + create_object("Empty Shelf" , 1, 2000, 4, 34, 26, 34, 26); + create_object("Bookshelf" , 1, 2000, 8, 35, 26, 35, 26); + create_object("Bottle Shelf", 1, 2000, 16, 36, 26, 36, 26); + create_object("Beer Shelf" , 1, 2000, 32, 37, 26, 37, 26); + + create_object("Piano" , 3, 10000, 1, 31, 26, 33, 26); + + create_object("Right Desk" , 2, 5000, 2, 36, 30, 38, 32); + + create_object("Right Chair" , 4, 2000, 2, 37, 29, 37, 29); + + create_object("Painting 01" , 6, 3000, 1, 27, 23, 27, 23); + create_object("Painting 02" , 6, 3000, 2, 29, 24, 29, 24); + create_object("Painting 03" , 6, 3000, 4, 32, 23, 32, 23); + create_object("Painting 04" , 6, 3000, 8, 35, 23, 35, 23); + end; + +OnReload: + // Load Mobilia already existing + debugmes "[REAL ESTATE] Now loading mobilia"; + for (.@i=0; .@i < getarraysize(.layer); .@i++) { + switch (.layer[.@i]) { + case 1: + if (ESTATE_MOBILIA_64 & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 2: + if (ESTATE_MOBILIA_4 & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 3: + if (ESTATE_MOBILIA_8 & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 4: + if (ESTATE_MOBILIA_32 & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 5: + if (ESTATE_MOBILIA_128 & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + case 6: + if (ESTATE_MOBILIA_2 & .objid[.@i]) + array_push(.valid_ids, .@i); + break; + default: + break; + } + } + debugmes "Found %d valid objects", getarraysize(.valid_ids); + for (.@j=0; .@j < getarraysize(.valid_ids); .@j++) { + .@id=.valid_ids[.@j]; + setcells "ples@"+getcharid(0), .x1[.@id], .y1[.@id], .x2[.@id], .y2[.@id], .layer[.@id], realestate_cellname(.id, .@id); + } + deletearray .valid_ids; + end; + + + // Additional crap needed because instance system + // Previously declared functions here. Copy paste from functions/, but without $. + function re2_sellprice + { + .@timeleft=ESTATE_RENTTIME-gettimetick(2); // Number of seconds + .@daysleft=.@timeleft/86400; // Number of days left of rent + .@weeksleft=.@timeleft/604800; // Number of weeks left of rent + return (getarg(1)/max(1, 6-.@weeksleft)) - max(0, 45-.@daysleft); + } + function re2_togglemobilia + { + switch (getarg(1)) { + case 1: + ESTATE_MOBILIA_64 = ESTATE_MOBILIA_64 ^ getarg(2); break; + case 2: + ESTATE_MOBILIA_4 = ESTATE_MOBILIA_4 ^ getarg(2); break; + case 3: + ESTATE_MOBILIA_8 = ESTATE_MOBILIA_8 ^ getarg(2); break; + case 4: + ESTATE_MOBILIA_32 = ESTATE_MOBILIA_32 ^ getarg(2); break; + case 5: + ESTATE_MOBILIA_128 = ESTATE_MOBILIA_128 ^ getarg(2); break; + case 6: + ESTATE_MOBILIA_2 = ESTATE_MOBILIA_2 ^ getarg(2); break; + default: + debugmes("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", getarg(2), getarg(1)); break; + } + if (getarg(3, "error") != "error") { addtimer2(150, "NPCs#RES_PPL::OnReload"); } + return; + } + function re2_hasmobilia + { + switch (getarg(1)) { + case 1: + return ESTATE_MOBILIA_64 & getarg(2); + case 2: + return ESTATE_MOBILIA_4 & getarg(2); + case 3: + return ESTATE_MOBILIA_8 & getarg(2); + case 4: + return ESTATE_MOBILIA_32 & getarg(2); + case 5: + return ESTATE_MOBILIA_128 & getarg(2); + case 6: + return ESTATE_MOBILIA_2 & getarg(2); + default: + debugmes("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", getarg(2), getarg(1)); return false; + } + return false; + } + +} + + diff --git a/npc/024-14/utils.txt b/npc/024-14/utils.txt new file mode 100644 index 0000000..ad9c511 --- /dev/null +++ b/npc/024-14/utils.txt @@ -0,0 +1,80 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Utils take care of NPCs - Their code, and enable/disable using check_cell +// This file is custom to every room + +// ESTATE_ID → Instance ID of the Estate (required for NPCs, expire) +// ESTATE_RENTTIME → When the rent will expire +// ESTATE_MOBILIA_2 → Bitmask of mobilia currently purchased on Monster Collision (6) (Use on walls only) +// ESTATE_MOBILIA_4 → Bitmask of mobilia currently purchased on Air Collision (2) +// ESTATE_MOBILIA_8 → Bitmask of mobilia currently purchased on Water Collision (3) +// ESTATE_MOBILIA_32 → Bitmask of mobilia currently purchased on Yellow Collision (4) +// ESTATE_MOBILIA_64 → Bitmask of mobilia currently purchased on Player Collision (5) +// ESTATE_MOBILIA_128 → Bitmask of mobilia currently purchased on Normal Collision (1) + +// REAL_ESTATE_CREDITS → Credits equivalent to GP the player have. Will be used first. + +// The sign is the main controller for rent system +// Doorbell is the main controller for indoor +// This is the NPC script controller +024-14,0,0,0 script NPCs#RES_PPL NPC_HIDDEN,{ + // load_npc ( name , map, x , y{, cell} ) + function load_npc { + if (checknpccell(getarg(1), getarg(2), getarg(3), getarg(4, cell_chknopass))) { + enablenpc instance_npcname(getarg(0), ESTATE_ID); + } else { + disablenpc instance_npcname(getarg(0), ESTATE_ID); + } + return; + } + end; + +OnInit: + // NPC Settings + .sex = G_OTHER; + .distance = 3; + end; + +// Load or unload accordingly +OnReload: + //debugmes "[REAL ESTATE] NPC ONRELOAD"; + // load_npc ( name , map, x , y{, cell} ) + load_npc("Wardrobe#RES_PPL", "ples@"+getcharid(0), 25, 26); + load_npc("Cauldron#RES_PPL", "ples@"+getcharid(0), 28, 27); + load_npc("Piano#RES_PPL" , "ples@"+getcharid(0), 32, 26); + end; + +} + +024-14,25,26,0 script Wardrobe#RES_PPL NPC_NO_SPRITE,{ + openstorage; + end; + +OnInit: + .distance=3; + end; +} + + +024-14,28,27,0 script Cauldron#RES_PPL NPC_NO_SPRITE,{ + realestate_cauldron(); + close; + +OnInit: + .distance=3; + end; +} + + +024-14,32,26,0 script Piano#RES_PPL NPC_NO_SPRITE,{ + realestate_piano(); + close; + +OnInit: + .distance=3; + end; +} + diff --git a/npc/024-15/_import.txt b/npc/024-15/_import.txt new file mode 100644 index 0000000..f150fe2 --- /dev/null +++ b/npc/024-15/_import.txt @@ -0,0 +1,4 @@ +// Map 024-15: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-15/_warps.txt", +"npc/024-15/lilanna.txt", diff --git a/npc/024-15/_warps.txt b/npc/024-15/_warps.txt new file mode 100644 index 0000000..800c601 --- /dev/null +++ b/npc/024-15/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-15: Frostia Indoors warps +024-15,25,39,0 warp #024-15_25_39 0,0,024-1,64,31 diff --git a/npc/024-15/lilanna.txt b/npc/024-15/lilanna.txt new file mode 100644 index 0000000..bfedb40 --- /dev/null +++ b/npc/024-15/lilanna.txt @@ -0,0 +1,198 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Anise INC. Representative + +024-15,23,24,0 script Lilanna NPC_ELF_F,{ + function FixCrystal; + function FixSelect; + mesn; + mesq l("Hello there, I am Lilanna and you are in ANISE INC. headquarters."); + next; + goto L_Loop; + +L_Loop: + mesn; + mesq l("How may I help you?"); + select + l("Thanks."), + l("What ANISE INC. Does?"), + l("How to use best Warp Technology?"), + l("I would like to buy a Warp Crystal."), + rif(countitem(BrokenWarpCrystal), l("My warp crystal broke!")); + mes ""; + switch (@menu) { + case 1: + close; + case 2: + mesn; + mesq l("ANISE Incorporated is the biggest company in warp technology!"); + next; + mesn; + mesq l("From magical gates to ancient time-space technology - we know it all!"); + next; + mesn; + mesq l("Do you know the warp crystals? We did them. Did you saw the great warp gate at the town entrance? It was us!"); + next; + mesn; + mesq l("If you ever need to get quickly from one place to the other, count on us!"); + next; + break; + case 3: + mesn; + mesq l("All warp technology, be it crystal-based, potion-based, or mechanic-based, relies on timespace distortions."); + next; + mesn; + mesq l("If you distort time-space too much, you might end up outside of it, meeting a quick, permanent, and unrecoverable death."); + next; + mesn; + mesq l("Different warp systems overlaps each other, so the cooldown is carried over regardless of the technology you use."); + mesc l("Potentially more destructive warp systems will have much longer cooldowns because they cause bigger holes in timespace."); + next; + mesn; + mes l("How to better preserve your dispostives:"); + mes l("Mechanically based technology (eg. the Gates or Time Flasks) will never suffer damage from operation but are more costly."); + mes l("Crystal based technology can break if used too often. Try taking longer between each warp to raise their lifetime."); + mes l("Potion based technology will vanish upon use and usually don't interfer with other technologies. Usually."); + next; + mesn; + mesq l("Knowing when to warp is the secret to success!"); + next; + break; + case 4: + npcshopattach(.name$); + openshop(); + closeclientdialog; + end; + break; + case 5: + FixSelect(); + break; + } + goto L_Loop; + +// FixCrystal ( Crystal ID, GP Tax ) +function FixCrystal { + .@cy=getarg(0); + .@gp=getarg(1); + if (Zeny < .@gp) { + mesc l("NOT ENOUGH MONEY"), 1; + mesc l("@@/@@ GP", fnum(Zeny), fnum(.@gp)); + next; + return; + } + inventoryplace .@cy, 1; + delitem BrokenWarpCrystal, 1; + Zeny-=.@gp; + getitem .@cy, 1; + mesc l("Operation successful."), 3; + next; + mesn; + mesq l("Here you go, a brand new crystal. I'll fix the old one on the meanwhile. Take care of it."); + next; + return; +} + +// A menu for fixing crystals +function FixSelect { + do + { + mesc l("Fixing Crystals have a price. You can only charge crystals to places you've already been."); + mesc l("These are saved by walking in warp portals or touching Soul Menhirs."); + mes l("You have: @@ Broken crystals", countitem(BrokenWarpCrystal)); + .@cbase=750; // Candor Base + .@nbase=1250; // Normal Base + .@lbase=2500; // LoF Base + .@fbase=5000; // Faraway Base + .@kbase=10000; // King Base (incl. Fortress Is.) + select + l("Don't fix."), + rif(true, l("Candor - @@ GP", fnum(.@cbase))), + rif(TELEPORTERS & TP_TULIM, l("Tulimshar - @@ GP", fnum(.@nbase))), + rif(TELEPORTERS & TP_HURNS, l("Hurnscald - @@ GP", fnum(.@nbase))), + rif(TELEPORTERS & TP_NIVAL, l("Nivalis - @@ GP", fnum(.@nbase))), + rif(TELEPORTERS & TP_FROST|TP_HALIN, l("Land Of Fire - @@ GP", fnum(.@lbase))), + rif(TELEPORTERS & TP_HALIN, l("Halinarzo - @@ GP", fnum(.@fbase))), + rif(TELEPORTERS & TP_FROST, l("Frostia - @@ GP", fnum(.@fbase))), + rif(TELEPORTERS & TP_LILIT, l("Lilit - @@ GP (not exchangeable)", fnum(.@kbase))), + rif(TELEPORTERS & TP_ARTIS, l("Artis - @@ GP (not exchangeable)", fnum(.@kbase))); + mes ""; + switch (@menu) { + case 1: return; + case 2: FixCrystal(CandorWarpCrystal, .@cbase); break; + case 3: FixCrystal(TulimWarpCrystal, .@nbase); break; + case 4: FixCrystal(HurnsWarpCrystal, .@nbase); break; + case 5: FixCrystal(NivalWarpCrystal, .@nbase); break; + case 6: FixCrystal(LoFWarpCrystal, .@lbase); break; + case 7: FixCrystal(HalinWarpCrystal, .@fbase); break; + case 8: FixCrystal(FrostiaWarpCrystal, .@fbase); break; + case 9: FixCrystal(LilitWarpCrystal, .@kbase); break; + case 10: FixCrystal(ArtisWarpCrystal, .@kbase); break; + } + } while (countitem(BrokenWarpCrystal)); + return; +} + +OnTimer1000: + domovestep; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, Monocle); + setunitdata(.@npcId, UDT_HEADMIDDLE, AlchemistArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonSkirt); + setunitdata(.@npcId, UDT_WEAPON, FurBoots); // She could use gloves, no + setunitdata(.@npcId, UDT_HAIRSTYLE, 19); + setunitdata(.@npcId, UDT_HAIRCOLOR, 16); + + initpath "move", 23, 24, + "dir", DOWN, 0, + "wait", 15, 0, + "dir", RIGHT, 0, + "move", 27, 24, + "wait", 3, 0, + "dir", DOWN, 0, + "wait", 15, 0, + "move", 24, 32, + "wait", 3, 0, + "dir", RIGHT, 0, + "wait", 10, 0, + "dir", LEFT, 0, + "wait", 10, 0, + "dir", UP, 0, + "wait", 3, 0, + "move", 27, 34, + "dir", UP, 0, + "wait", 3, 0, + "dir", LEFT, 0, + "wait", 3, 0, + "move", 23, 24, + "wait", 3, 0, + "dir", DOWN, 0, + "wait", 10, 0; + initialmove; + initnpctimer; + tradertype(NST_MARKET); + sellitem BrokenWarpCrystal, 50000, 1; + + .sex = G_MALE; + .distance = 5; + end; + +OnClock2358: + restoreshopitem BrokenWarpCrystal, 50000, 1; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} + diff --git a/npc/024-16/_import.txt b/npc/024-16/_import.txt new file mode 100644 index 0000000..402dc74 --- /dev/null +++ b/npc/024-16/_import.txt @@ -0,0 +1,7 @@ +// Map 024-16: Frostia's Throne Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-16/_warps.txt", +"npc/024-16/craftsman.txt", +"npc/024-16/generals.txt", +"npc/024-16/guards.txt", +"npc/024-16/king.txt", diff --git a/npc/024-16/_warps.txt b/npc/024-16/_warps.txt new file mode 100644 index 0000000..5ead61b --- /dev/null +++ b/npc/024-16/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-16: Frostia's Throne Room warps +024-16,30,54,0 warp #024-16_30_54 2,0,024-1,98,22 diff --git a/npc/024-16/craftsman.txt b/npc/024-16/craftsman.txt new file mode 100644 index 0000000..961161e --- /dev/null +++ b/npc/024-16/craftsman.txt @@ -0,0 +1,177 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Craftmaster, teaches player TMW2_CRAFT + +024-16,27,42,0 script Dwarf Craftsmaster NPC_DWARF_CRAFTMASTER,{ + function calcRequisites; + function calcPrices; + function calcUpgrade; + .@q=getq(General_Narrator); + if (.@q < 13) { + hello; + end; + } + mesn; + mesq lg("Look what we have here, it is a girl!", "Look what we have here, it is a boy!"); + next; + mesn; + mesq l("I'm Thurgar the mighty craftsman. I can make anything reality! But I only work to the king!"); + next; + mesn; + mesq l("...Unless, of course, if you're interested in learning this art. You'll not regret it, I assure you."); + next; + // Main Loop + .@score=CRAFTING_SCORE_COMPLETE/40; + mesc l("Crafting Skill Level: @@", getskilllv(TMW2_CRAFT)); + msObjective(.@score >= calcRequisites(), + l("Completed Crafts: @@/@@", .@score, calcRequisites()) ); + msObjective(Zeny >= calcPrices(), + l("Money: @@ GP", format_number(Zeny)) ); + // Script end + if (getskilllv(TMW2_CRAFT) > 6) + close; + mes ""; + select + rif(!.@score, l("How can I complete a craft?")), + rif(.@score >= calcRequisites(), l("Learn crafting for @@ GP", format_number(calcPrices())) ), + rif(.@score, l("How can I complete a craft?")), + l("Nothing for now, thanks."); + mes ""; + switch (@menu) { + case 1: + case 3: + mesn; + mesq l("Well, first of, you'll need an @@ and an Equipment Recipe.", getitemlink(RecipeBook)); + next; + if (!countitem(RecipeBook) && !countitem(JesusalvaGrimorium)) { + mesn; + mesq l("If you don't have the recipe book, you'll need to find one... Maybe someone in a household you've already helped is willing to give you one."); + next; + } + mesn; + mesq l("Anyway, once you have the recipe book and learned a recipe, you can craft items in forges. I think you can buy it in your apartment."); + next; + mesn; + mesq l("That will help you to make your very first first craft! Remember to use @@ to change which bonuses can be applied to your craft items.", b("@ucp")); + next; + mesn; + mesq l("I think someone on Tulimshar is capable to teach you these bonuses. Eh, I don't know. Haven't been there for a while."); + break; + case 2: + if (calcUpgrade()) { + mesn; + mesq l("There you go. Craft hard, mwhahahahaha!"); + } else { + mesn; + mesq l("You don't have met all requisites, like money and successful crafts, or you already reached the maximum level for this skill."); + } + break; + } + close; + +// Calc successful crafts required to learn crafting +// Returns amount of crafts needed +function calcRequisites { + switch (getskilllv(TMW2_CRAFT)) { + case 0: + return 0; + case 1: + return 3; + case 2: + return 7; + case 3: + return 12; + case 4: + return 18; + case 5: + return 32; + case 6: + return 64; + } + return -1; +} + +// Calc how much GP the skill will cost you +// Returns amount of GP +function calcPrices { + switch (getskilllv(TMW2_CRAFT)) { + case 0: + return 1000; + case 1: + return 5000; + case 2: + return 9000; + case 3: + return 15000; + case 4: + return 27000; + case 5: + return 36000; + case 6: + return 50000; + } + return false; +} + +// calcUpgrade() returns true if skill +// can be leveled up. And levels it up. +function calcUpgrade { + .@gp=calcPrices(); + .@cf=calcRequisites(); + if (Zeny < .@gp) + return false; + if (CRAFTING_SCORE_COMPLETE / 40 < .@cf) + return false; + if (.@cf < 0 || .@gp < 0) + return false; + + // You may get a free blueprint + switch (getskilllv(TMW2_CRAFT)) { + case 0: + inventoryplace EquipmentBlueprintA, 1; + getitem EquipmentBlueprintA, 1; + + // We should aid you getting basic skills - for free! + if (!CRAFTSYS[CRGROUP_BASE]) + CRAFTSYS[CRGROUP_BASE]+=1; + if (!CRAFTSYS_CURRENT) + CRAFTSYS_CURRENT=CRAFTSYS_CURRENT|CRGROUP_BASE; + + break; + case 1: + inventoryplace EquipmentBlueprintB, 1; + getitem EquipmentBlueprintB, 1; + break; + case 2: + inventoryplace EquipmentBlueprintC, 1; + getitem EquipmentBlueprintC, 1; + break; + case 3: + inventoryplace EquipmentBlueprintD, 1; + getitem EquipmentBlueprintD, 1; + break; + case 4: + inventoryplace EquipmentBlueprintE, 1; + getitem EquipmentBlueprintE, 1; + break; + case 5: + inventoryplace AncientBlueprint, 1; + getitem AncientBlueprint, 1; + break; + default: + getexp 1700, 200; + break; + } + + Zeny-=.@gp; + skill TMW2_CRAFT, getskilllv(TMW2_CRAFT)+1, 0; + return true; +} + +OnInit: + .distance=5; + end; +} + diff --git a/npc/024-16/generals.txt b/npc/024-16/generals.txt new file mode 100644 index 0000000..6f420f5 --- /dev/null +++ b/npc/024-16/generals.txt @@ -0,0 +1,142 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Generals + +// FrostiaTaskMaster(lvl) +function script FrostiaTaskMaster { + .@q=General_MobHunting4; + .@q1=getq(General_MobHunting4); + .@q2=getq2(General_MobHunting4); + .@q3=getq3(General_MobHunting4); + .@d=gettimeparam(GETTIME_DAYOFMONTH); + .@lv=getarg(0, 1); + // It's a new day, so we must generate a new quest! + if (.@q1 != .@d) { + .@q1=.@d; + .@q2=any(Moggun, IcedFluffy, Fluffy, Pollet, BlueSlime, WhiteSlime, SantaSlime, AzulSlime, RudolphSlime); // WaterFairy + setq .@q, .@q1, .@q2, 0; + } + + // Maybe there is no monster to kill + if (!.@q2) { + mesn; + mesq l("You've already completed this quest today. Try again tomorrow."); + return true; + } + + // Maybe you finished the quest? + if (.@q3 >= 50) { + mesn; + mesq l("Good job, you've killed the @@ @@ and reported back in time!", 50, getmonsterlink(.@q2)); + next; + mesn; + mesq l("It's not much of a reward, but doesn't it feels great to help others in need?! HAHAHA!"); + Zeny+=.@lv*8; + // Raise LV according to monster level + .@lv+=strmobinfo(3, .@q2); + getexp BaseLevel*.@lv, .@lv; + setq2 .@q, 0; + setq3 .@q, 0; + return false; + } + + mesn; + mesq l("Please kill @@/@@ @@ for us and make the world a safer place!", .@q3, 50, getmonsterlink(.@q2)); + return false; +} + +// FrostiaGeneralQuest( Skillname ) +function script FrostiaGeneralQuest { + .@at$=getarg(0); + mesn; + mesq l("Hey. You there. Time to do @@ and show what you're made of.", .@at$); + next; + mesn; + mesq l("If you conclude the training, you'll be suitable for the difficult mission our King have to you."); + next; + mesc b(l(".:: Main Quest 6-2 ::.")), 3; + msObjective(BaseLevel >= 52, l("* @@/@@ Base Level", BaseLevel, 52)); + msObjective(JobLevel >= 24, l("* @@/@@ Job Level", JobLevel, 24)); + //msObjective(JobLevel >= 24, l("* Win an Arena Match")); + next; + if (BaseLevel >= 52 && JobLevel >= 24) { + mesn; + mesq l("Congrats, you did it."); + next; + mesn; + mesq l("So, if you hired a Mercenary to protect your back... Would you prefer it to attack from rearguard, or charge against certain death, buying you time?"); + next; + select + l("I honestely wouldn't hire a Mercenary."), + l("Running is for noobs, we should hold our ground!"), + l("I can protect myself, so it should attack from afar."); + mes ""; + // Decide which mercenary you'll get + if (@menu == 2) + .@card=MercCard_Saulc; + else if (@menu == 3) + .@card=MercCard_Arthur; + else + .@card=any(MercCard_Saulc, MercCard_Arthur); + + inventoryplace ElixirOfLife, 1, .@card, 1, Grenade, 3; + mesn; + mesq l("Take this @@. It might save your life on the secret mission you're about to be assigned to.", getitemlink(.@card)); + next; + mesn; + mesq l("I'll also provide you a few @@s and an Elixir. Use them wisely.", getitemlink(Grenade)); + next; + mesn; + mesq l("Now go talk to the King."); + setq General_Narrator, 15; + getitem Grenade, 3; + getitem .@card, 1; + getitem ElixirOfLife, 1; + getexp 0, 400; // Get 10% JEXP. Reference: 3988~5564 + close; + } + mesn; + mesq l("Now, a simple @@ quest to make you stronger...", .@at$); + next; + if (FrostiaTaskMaster(20)) { + next; + mesn; + mesq l("Or maybe... You want to try again right now? I'll do whatever needed to level you up to less-noobish levels."); + next; + if (askyesno() == ASK_YES) { + setq General_MobHunting4, 0, 0, 0; + FrostiaTaskMaster(20); + } + } + close; +} + +////////////////////////////////////////////////////////////////////// +024-16,29,21,0 script Wizard General NPC_BLUESABER,{ + .@q=getq(General_Narrator); + if (.@q == 14) + FrostiaGeneralQuest("Magic"); + else + FrostiaTaskMaster(20); + close; + +OnInit: + .distance=5; + end; +} + +024-16,31,21,0 script Warrior General NPC_REDSABER,{ + .@q=getq(General_Narrator); + if (.@q == 14) + FrostiaGeneralQuest("Fight"); + else + FrostiaTaskMaster(20); + close; + +OnInit: + .distance=5; + end; +} + diff --git a/npc/024-16/guards.txt b/npc/024-16/guards.txt new file mode 100644 index 0000000..80405dd --- /dev/null +++ b/npc/024-16/guards.txt @@ -0,0 +1,30 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Frostia King Guards + +024-16,33,42,0 script Royal Guard#02416A NPC_BRGUARD_SPEAR,{ + legiontalk; + end; + +OnInit: + .distance=5; + end; +} + +024-16,25,33,0 duplicate(Royal Guard#02416A) Royal Guard#02416B NPC_BRGUARD_SWORD +024-16,35,33,0 duplicate(Royal Guard#02416A) Royal Guard#02416C NPC_BRGUARD_BOW + + +// Before King Gelid give you his OK, you cannot leave throne room +024-16,30,53,0 script #FrostiaKingAudience NPC_HIDDEN,1,0,{ +end; +OnTouch: + if (getq(General_Narrator) <= 12) { + slide 30, 52; + dispbottom l("Ops, I should not leave this room without talking to the king first."); + } + end; +} + diff --git a/npc/024-16/king.txt b/npc/024-16/king.txt new file mode 100644 index 0000000..676e61f --- /dev/null +++ b/npc/024-16/king.txt @@ -0,0 +1,249 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Ruler of Frostia (maybe we should instance this map so the king can walk) + +024-16,30,23,0 script King Gelid NPC_ELF,{ + .@q=getq(General_Narrator); + if (.@q < 12) { + nude(); + atcommand("#dropall "+strcharinfo(0)); // Evil + Exception("FATAL ERROR, PLAYER "+strcharinfo(0)+" NOT ALLOWED TO BE WITHIN FROSTIA'S CASTLE - REASON: SAULC IS A NOOB"); + slide 30, 52; + end; + } + if (.@q >= 17) + goto L_Complete; + if (.@q == 16) + goto L_Report; + if (.@q == 15) + goto L_Campaign; + if (.@q >= 13) + goto L_MainQuest; + + mesn l("King Gelid Frozenheart"); + mesq l("Hello."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Yes. Frostia is a city-estate, and is ruled by me, King Gelid Frozenheart II."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Is this letter yours? Very well, let me read."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Hm hm. This sounds pretty concerning. Aiming at Sages is also a smart move, as they compose the Alliance Council."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("As about if I have any idea why they said they were from here? ...Actually, I do."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("That aside, I see you've helped Hurnscald Mayor, Nivalis Mayor, Halinarzo Librarian and even the Alliance Representative in Tulimshar!"); + next; + mesn l("King Gelid Frozenheart"); + mesq lg("I'm quite interested in you, mah' girl!", "I'm quite interested in you, mah' boy!"); + next; + mesn l("King Gelid Frozenheart"); + mesq l("So, only citzens, elves and dwarves are normally welcome here and I'm NOT going to make you an exception."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("In the ")+b(l("southwest part of the town"))+l(" is the Inn, and you can rent an apartment there for 30 days."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("If you rent an apartment, you'll become a \"citzen\" and be allowed here. Besides, you can buy stuff in your apartment to, for example, craft stuff."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Did I said the best craftsman and craftswoman in the world are here? @@, the legendary bow, was proudly crafted by the first king of Frostia!", getitemlink(Tyranny)); + next; + mesn l("King Gelid Frozenheart"); + mesq l("He is not alive anymore, so only a single bow of those exist in the whole world... That is why we call it a ")+b(l("legendary weapon")); + next; + // Finish the quest + setq General_Narrator, 13; + REAL_ESTATE_CREDITS+=5000; + +L_MainQuest: + mesn l("King Gelid Frozenheart"); + mesq l("Anyway, I'll give you @@ Real Estate Credits in advance. Go book yourself a room in the Inn. And then come back to me so we may discuss details.", 5000); + next; + + mesc b(l(".:: Main Quest 6-1 ::.")), 3; + msObjective(BaseLevel >= 40, l("* @@/@@ Base Level", BaseLevel, 40)); + msObjective(true, l("* Deliver Nikolai's Letter to Frostia Mayor")); + msObjective(ESTATE_RENTTIME >= gettimetick(2), l("* Rent a room in Frostia's Inn")); + if (ESTATE_RENTTIME >= gettimetick(2)) + goto L_Continue; + close; + +L_Continue: + next; + mesn l("King Gelid Frozenheart"); + mesq l("Great, I see you've already made yourself comfortable!"); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Do not hesit to purchase furniture. The furniture belongs to the house, so if you lose the house, you'll lose it, too. But apartments are never rent to someone else!"); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Bah, sorry my manners! I love to chat! Anyway, talk to any General of mine, just behind me. See if they need help."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("There's somewhere I want you to go, but you're not strong enough yet, so be patient and help them first."); + if (getq(General_Narrator) == 13) { + setq General_Narrator, 14, 0; + getexp 0, 40; + } + close; + +L_Campaign: + .@q=getq(FrostiaQuest_Homunculus); + // We probably should apply you a penalty should you flee... no? + setq3 FrostiaQuest_Homunculus, 0; + + // Long intro until you defeat the Yeti. + if (!.@q) { + mesn l("King Gelid Frozenheart"); + mesq l("Listen. North of here, is the ruins of an old village. Said village is cursed, and nobody here wants to go there."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("But if you're looking to go to the World Edge, the place where THE WORLD WILL DIE, I guess you are immune to rumors."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("I would bring @@, just in case. Anyway, the place where you're going to is the village where @@ was born.", getitemlink(Coffee), b(l("the Monster King"))); + next; + mesn l("King Gelid Frozenheart"); + mesq l("I've sent a scout ahead of you, and he reported traps in the caves you'll be needing to use to get there."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("As you can guess, said village was abandoned. However, people are claiming to have seen people walking there, like shadows or zombies."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("As a King, I cannot simply ignore it. Of course, I really doubt it is zombies, but nobody wants to go. That's why I'm resorting to an adventurer like you."); + next; + } + // This is a do{} loop + do { + mesn l("King Gelid Frozenheart"); + mesq l("So, will you go?"); + mesc l("WARNING: Difficult quest, bringing plenty of healing items strongly advised!"), 1; + select + l("Not right now."), + l("Why not Andrei Sakar?"), + l("What will be my reward?"), + l("Yes, I'll go."); + mes ""; + + switch (@menu) { + case 1: + close; + case 2: + mesn l("King Gelid Frozenheart"); + if ($FIRESOFSTEAM < 10) + mesq l("I tried. He asked for too much money."); + else + mesq l("Where have you been, pal? He perished."); + next; + break; + case 3: + mesn l("King Gelid Frozenheart"); + mesq l("Whatever you find there is yours to keep."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("I'll also try to bring you to the world's edge, as you've asked."); + next; + break; + case 4: + cwarp "023-3", 63, 219; break; + } + } while (true); + end; // fallback + +L_Report: + mesn strcharinfo(0); + mesc l("You report to the King about everything you've found and learned there."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("What?! This changes the whole history. Not only that, but this is a serious issue!"); + next; + mesn l("King Gelid Frozenheart"); + mesq l("I thought I was going to send you in a nest of Yetis, but instead, I've sent you straight to the birthplace of Bandits and Assassins."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Blue Sage will want to know this, I'll write him a letter."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Now, listen to me. I know I promised to bring you to the World's Edge."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Truth is - I cannot do that. Obviously. But I know who can."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Do you know Land Of Fire Village? It is west of Hurnscald and is a major town."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("It only loses to Tulimshar, as far as political and economical importance goes."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Go to the townhall there and show the mayor this other letter."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("They will try to prove your worth before, though. So be ready."); + next; + mesn l("King Gelid Frozenheart"); + mesq l("Good luck on your journey!"); + // Ref. level 52/24 (almost 100% of exp table) + getexp 142750, 3950; + getvaultexp(10); + setq General_Narrator, 17, 0, 0; + close; + +L_Complete: + mesn l("King Gelid Frozenheart"); + mesq l("Welcome back. Please enjoy your stay here."); + next; + select + l("Could I go back to that cave? It's a good farm spot."), + rif($TYRAN_HOLDER$ == "", l("What do you know about the Tyranny Bow?")), + l("Thanks, my liege."); + mes ""; + if (@menu == 1) + cwarp "023-3", 63, 219; + if (@menu == 2) { + mesn l("King Gelid Frozenheart"); + mesq l("It is one of the five legendary weapons. Legend says it was hidden in a tree on Aethyr, and will be given to the one who proves to be an excelent archer."); + next; + if ($GAME_STORYLINE < 4) { + mesn l("King Gelid Frozenheart"); + mesq l("But this is not a time of need, so it is unlikely to appear even if all other conditions were to be met."); + close; + } + mesn l("King Gelid Frozenheart"); + mesq l("If it was easy, someone would already have claimed it, so good luck if you plan in obtaining it."); + } + close; + +OnInit: + /* + <sprite>equipment/legs/assassinpants-male.xml</sprite> + <sprite>equipment/feet/boots-male.xml</sprite> + <sprite>equipment/hands/armbands-male.xml</sprite> + <sprite>hairstyles/hairstyle15.xml|#585858,A4A4A4,C0C0C0,ffffff</sprite> + <sprite>equipment/head/crown.xml</sprite> + <sprite>equipment/weapons/knife.xml</sprite> + <sprite>equipment/chest/warlordplate-male.xml</sprite> + <!--sprite>equipment/charm/manta-imperial-male.xml</sprite--> + <sound event="hit">weapons/piouslayer/hit1.ogg</sound> + */ + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, ImperialCrown); + setunitdata(.@npcId, UDT_HEADMIDDLE, WarlordPlate); + setunitdata(.@npcId, UDT_HEADBOTTOM, AssassinPants); + setunitdata(.@npcId, UDT_WEAPON, FurBoots); // Boots + // TODO: Armbands, imperial robe, etc. + setunitdata(.@npcId, UDT_HAIRSTYLE, 15); + setunitdata(.@npcId, UDT_HAIRCOLOR, 8); + + .distance=4; + npcsit; + end; +} + diff --git a/npc/024-2/_import.txt b/npc/024-2/_import.txt new file mode 100644 index 0000000..2ca764d --- /dev/null +++ b/npc/024-2/_import.txt @@ -0,0 +1,4 @@ +// Map 024-2: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-2/_warps.txt", +"npc/024-2/gaelira.txt", diff --git a/npc/024-2/_warps.txt b/npc/024-2/_warps.txt new file mode 100644 index 0000000..9e28c4d --- /dev/null +++ b/npc/024-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-2: Frostia Indoors warps +024-2,41,30,0 warp #024-2_41_30 0,0,024-1,32,55 diff --git a/npc/024-2/gaelira.txt b/npc/024-2/gaelira.txt new file mode 100644 index 0000000..d8398d4 --- /dev/null +++ b/npc/024-2/gaelira.txt @@ -0,0 +1,131 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +024-2,41,22,0 script Gaelira NPC_ELF_F,{ + .@q = getq(FrostiaQuest_Gaelira); + if (BaseLevel < 52) { + mesc l("The elf seems busy. She ignores you."); + close; + } + if (.@q == 0) { + if (frally()) goto L_Start_F; + else goto L_Start_U; + } + else if (.@q == 1) { + if (frally()) goto L_Submit_F; + else goto L_Submit_U; + } + else if (.@q == 2) { + if (frally()) goto L_Done_F; + else goto L_Done_U; + } + Exception("Invalid quest state - "+str(.@q)); + end; + +L_Start_F: + mesn; + mesq lg("Hey friend, could you do me a little favor?"); + next; + mesn; + mesq l("You see, I love %s! So shiny, so bright... They are my favorite!", getitemlink(Pearl)); + next; + mesn; + mesq l("So, if you could bring me %d of them, that would be totally awesome! Pretty please! I'm counting on you!", 20); + setq FrostiaQuest_Gaelira, 1; + close; + +L_Submit_F: + mesn; + mesq l("Did you got the %d %s I've asked?", 20, getitemlink(Pearl)); + next; + if (askyesno() == ASK_YES) { + if (countitem(Pearl) < 20) { + mesn; + mesq l("You've been spending too much time with humans. You are picking up bad habits from them like lying. Be careful."); + close; + } + inventoryplace AncientBlueprint, 1; + delitem Pearl, 20; + getitem AncientBlueprint, 1; + Zeny+=getiteminfo(Pearl, ITEMINFO_SELLPRICE)*20*12/10; // 120% payoff + getexp 15000, 0; + setq FrostiaQuest_Gaelira, 2; + mesn; + mesq l("Thank you soooo much! Here is some compensation, and you can keep this worthless scroll of paper! I looooove Pearls!"); + } + close; + +L_Done_F: + mesn; + mesq l("Hey there! How are you? I hope you're doing fine!"); + next; + mesn; + mesq l("Did you knew about the legend of Aethyr? I've heard the path to it was somewhere to the west of our old village... And some stuff about passwords... Hehe, I don't remember!"); + next; + mesn; + mesq l("But I can't help but keep thinking on how many shiny treasures might be there!"); + close; +///////////////////////////////////////////////////////////////////////////////// +L_Start_U: + mesn; + mesq l("Hey %s, could you do me a little favor?", get_race()); + next; + mesn; + mesq l("You see, I love %s! So shiny, so bright... They are my favorite!", getitemlink(Pearl)); + next; + mesn; + mesq l("So, if you could bring me %d of them, that would be totally awesome! I'm counting on you!", 20); + setq FrostiaQuest_Gaelira, 1; + close; + +L_Submit_U: + mesn; + mesq l("Did you got the %d %s I've asked?", 20, getitemlink(Pearl)); + next; + if (askyesno() == ASK_YES) { + if (countitem(Pearl) < 20) { + mesn; + mesq l("You liar, I'll teach you a lesson you won't forget!"); + mesc l("%s stabs you with a dagger!", .name$); + percentheal -55, 0; + close; + } + inventoryplace AncientBlueprint, 1; + delitem Pearl, 20; + getitem AncientBlueprint, 1; + Zeny+=getiteminfo(Pearl, ITEMINFO_SELLPRICE)*20*12/10; // 120% payoff + getexp 15000, 0; + setq FrostiaQuest_Gaelira, 2; + mesn; + mesq l("Thank you! Here is some compensation, and you can keep this worthless scroll of paper! I looooove Pearls!"); + } + close; + +L_Done_U: + mesn; + mesq l("Hey there! How are you?"); + next; + mesn; + mesq l("Did you knew about the legend of Aethyr? I've heard the path to it was somewhere to the west of our old village... And some stuff about passwords... I'm not sure if I remember!"); + next; + mesn; + mesq l("Well, I can't help but keep thinking on how many shiny treasures might be there!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonSkirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, AssassinChest); + setunitdata(.@npcId, UDT_HEADTOP, BunnyEars); + setunitdata(.@npcId, UDT_HAIRSTYLE, 10); + setunitdata(.@npcId, UDT_HAIRCOLOR, 6); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/024-3/_import.txt b/npc/024-3/_import.txt new file mode 100644 index 0000000..5b3917d --- /dev/null +++ b/npc/024-3/_import.txt @@ -0,0 +1,4 @@ +// Map 024-3: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-3/_warps.txt", +"npc/024-3/arauto.txt", diff --git a/npc/024-3/_warps.txt b/npc/024-3/_warps.txt new file mode 100644 index 0000000..d0ee584 --- /dev/null +++ b/npc/024-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-3: Frostia Indoors warps +024-3,35,35,0 warp #024-3_35_35 0,0,024-1,126,29 diff --git a/npc/024-3/arauto.txt b/npc/024-3/arauto.txt new file mode 100644 index 0000000..5c6eb63 --- /dev/null +++ b/npc/024-3/arauto.txt @@ -0,0 +1,16 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Banker. + +024-3,33,28,0 script Arauto NPC_LLOYD,{ + Banker(.name$, "Frostia", 9800); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/024-4/_import.txt b/npc/024-4/_import.txt new file mode 100644 index 0000000..10834a1 --- /dev/null +++ b/npc/024-4/_import.txt @@ -0,0 +1,5 @@ +// Map 024-4: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-4/_warps.txt", +"npc/024-4/alaion.txt", +"npc/024-4/solana.txt", diff --git a/npc/024-4/_warps.txt b/npc/024-4/_warps.txt new file mode 100644 index 0000000..c82f437 --- /dev/null +++ b/npc/024-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-4: Frostia Indoors warps +024-4,39,29,0 warp #024-4_39_29 0,0,024-1,160,41 +024-4,28,29,0 warp #024-4_28_29 0,0,024-1,154,41 diff --git a/npc/024-4/alaion.txt b/npc/024-4/alaion.txt new file mode 100644 index 0000000..76a7258 --- /dev/null +++ b/npc/024-4/alaion.txt @@ -0,0 +1,55 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Random shops + +024-4,36,23,0 script Alaion NPC_ELF,{ + npcshopattach(.name$); + shop .name$; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, any(Chainmail, LightPlatemail, CopperArmor, AssassinChest)); + setunitdata(.@npcId, UDT_HEADBOTTOM, any(RaidTrousers, LeatherTrousers, JeansChaps, BromenalPants, AssassinPants, ChainmailSkirt)); + setunitdata(.@npcId, UDT_WEAPON, AncientSword); + setunitdata(.@npcId, UDT_HAIRSTYLE, rand2(5,15)); + setunitdata(.@npcId, UDT_HAIRCOLOR, rand2(2,10)); + + .sex = G_MALE; + .distance = 4; + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem Halberd, -1, 1; + sellitem Broadsword, -1, 1; + sellitem Kitana, -1, 1; + sellitem ShortSword, -1, 1; + sellitem Backsword, -1, 1; + sellitem ShortGladius, -1, 1; + end; + +OnClock1250: +OnClock0112: + restoreshopitem Halberd, 1; + restoreshopitem Broadsword, 1; + restoreshopitem Kitana, 1; + restoreshopitem ShortSword, 1; + restoreshopitem Backsword, 1; + restoreshopitem ShortGladius, 1; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} + diff --git a/npc/024-4/solana.txt b/npc/024-4/solana.txt new file mode 100644 index 0000000..5f87766 --- /dev/null +++ b/npc/024-4/solana.txt @@ -0,0 +1,47 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Random shops + +024-4,28,23,0 script Solana NPC_ELF_F,{ + npcshopattach(.name$); + shop .name$; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, any(LightPlatemail, ForestArmor, AssassinChest)); + setunitdata(.@npcId, UDT_HEADBOTTOM, any(RaidTrousers, JeansChaps, AssassinPants, ChainmailSkirt)); + setunitdata(.@npcId, UDT_WEAPON, BansheeBow); + setunitdata(.@npcId, UDT_HAIRSTYLE, rand2(5,15)); + setunitdata(.@npcId, UDT_HAIRCOLOR, rand2(2,10)); + + .sex = G_FEMALE; + .distance = 4; + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem ForestBow, -1, 1; + sellitem ElficBow, -1, 1; + end; + +OnClock1250: +OnClock0112: + restoreshopitem ForestBow, 1; + restoreshopitem ElficBow, 1; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} + diff --git a/npc/024-5/_import.txt b/npc/024-5/_import.txt new file mode 100644 index 0000000..4d9bb3c --- /dev/null +++ b/npc/024-5/_import.txt @@ -0,0 +1,5 @@ +// Map 024-5: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-5/_warps.txt", +"npc/024-5/rychell.txt", +"npc/024-5/saevel.txt", diff --git a/npc/024-5/_warps.txt b/npc/024-5/_warps.txt new file mode 100644 index 0000000..1373ba2 --- /dev/null +++ b/npc/024-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-5: Frostia Indoors warps +024-5,34,29,0 warp #024-5_34_29 0,0,024-1,155,56 diff --git a/npc/024-5/rychell.txt b/npc/024-5/rychell.txt new file mode 100644 index 0000000..ac6ba56 --- /dev/null +++ b/npc/024-5/rychell.txt @@ -0,0 +1,57 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Random shops + +024-5,33,23,0 script Rychell NPC_ELF,{ + npcshopattach(.name$); + shop .name$; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, any(Chainmail, LightPlatemail, CopperArmor, AssassinChest)); + setunitdata(.@npcId, UDT_HEADBOTTOM, any(RaidTrousers, LeatherTrousers, JeansChaps, BromenalPants, AssassinPants, ChainmailSkirt)); + setunitdata(.@npcId, UDT_WEAPON, BlueKnightShield); + setunitdata(.@npcId, UDT_HAIRSTYLE, rand2(5,15)); + setunitdata(.@npcId, UDT_HAIRCOLOR, rand2(2,10)); + + .sex = G_MALE; + .distance = 4; + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem BritShield, -1, 1; + sellitem Chainmail, -1, 1; + //sellitem CopperArmor, -1, 1; + sellitem SilkPants, -1, 1; + sellitem ChainmailSkirt, -1, 1; + sellitem BromenalGloves, -1, 1; + sellitem BromenalHelmet, -1, 1; + end; + +OnClock1251: +OnClock0113: + restoreshopitem BritShield, 1; + restoreshopitem Chainmail, 1; + //restoreshopitem CopperArmor, 1; + restoreshopitem SilkPants, 1; + restoreshopitem ChainmailSkirt, 1; + restoreshopitem BromenalGloves, 1; + restoreshopitem BromenalHelmet, 1; + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; +} + diff --git a/npc/024-5/saevel.txt b/npc/024-5/saevel.txt new file mode 100644 index 0000000..2005cb0 --- /dev/null +++ b/npc/024-5/saevel.txt @@ -0,0 +1,118 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +024-5,38,23,0 script Saevel NPC_ELF,{ + .@q = getq(FrostiaQuest_Saevel); + if (BaseLevel < 47) { + mesc l("The elf seems busy. He ignores you."); + close; + } + if (.@q == 0) { + if (frally()) goto L_Start_F; + else goto L_Start_U; + } + else if (.@q == 1) { + if (frally()) goto L_Submit_F; + else goto L_Submit_U; + } + else if (.@q == 2) { + if (frally()) goto L_Done_F; + else goto L_Done_U; + } + Exception("Invalid quest state - "+str(.@q)); + end; + +L_Start_F: + mesn; + mesq l("I'm am about to make myself some powerful equipment... But I ran out of material to refine it!"); + next; + mesn; + mesq l("Could you, perhaps, bring me %d %s? That'll help me to finish this.", 1, getitemlink(Arcanum)); + setq FrostiaQuest_Saevel, 1; + close; + +L_Submit_F: + mesn; + mesq l("Did you got the %d %s I've asked?", 1, getitemlink(Arcanum)); + next; + if (askyesno() == ASK_YES) { + if (countitem(Arcanum) < 1) { + mesn; + mesq l("You've been spending too much time with humans. You are picking up bad habits from them like lying. Be careful."); + close; + } + inventoryplace EquipmentBlueprintB, 1, EquipmentBlueprintA, 1; + delitem Arcanum, 1; + getexp 7000, 0; + getitem EquipmentBlueprintB, 1; + getitem EquipmentBlueprintA, 1; + setq FrostiaQuest_Saevel, 2; + mesn; + mesq l("Muahahaha, I'll be SO strong thanks to you! Here, I don't need these weak recipes anymore. You can have them!"); + } + close; + +L_Done_F: + mesn; + mesq l("POWER!"); + next; + mesn; + mesq l("Reminds me, but they say the Aethyr is a place where powerful elves of old live. But nobody has been there on the past century... Or if they were, nobody said anything about it! Hahahah!"); + close; +///////////////////////////////////////////////////////////////////////////////// +L_Start_U: + mesn; + mesq l("I'm am about to make myself some powerful equipment... But I ran out of material to refine it!"); + next; + mesn; + mesq l("Could you bring me %d %s? That'll help me to finish this.", 1, getitemlink(Arcanum)); + setq FrostiaQuest_Saevel, 1; + close; + +L_Submit_U: + mesn; + mesq l("Did you got the %d %s I've asked?", 1, getitemlink(Arcanum)); + next; + if (askyesno() == ASK_YES) { + if (countitem(Arcanum) < 1) { + mesn; + mesq l("You liar, I'll teach you a lesson you won't forget!"); + mesc l("%s slaps you!", .name$); + percentheal -30, 0; + close; + } + inventoryplace EquipmentBlueprintB, 1, EquipmentBlueprintA, 1; + delitem Arcanum, 1; + getexp 7000, 0; + getitem EquipmentBlueprintB, 1; + getitem EquipmentBlueprintA, 1; + setq FrostiaQuest_Saevel, 2; + mesn; + mesq l("Muahahaha, I'll be SO strong thanks to you! Here, these recipes should be useful for ya. You can have them!"); + } + close; + +L_Done_U: + mesn; + mesq l("POWER!"); + next; + mesn; + mesq l("Reminds me, but they say the Aethyr is a place where powerful elves of old live. But nobody has been there on the past century... Or if they were, nobody said anything about it! Hahahah!"); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_HEADMIDDLE, GoldenWarlordPlate); + setunitdata(.@npcId, UDT_HEADTOP, SamuraiHelmet); + setunitdata(.@npcId, UDT_HAIRSTYLE, 2); + setunitdata(.@npcId, UDT_HAIRCOLOR, 8); + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/024-6/_import.txt b/npc/024-6/_import.txt new file mode 100644 index 0000000..1af2c67 --- /dev/null +++ b/npc/024-6/_import.txt @@ -0,0 +1,4 @@ +// Map 024-6: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-6/_warps.txt", +"npc/024-6/talindra.txt", diff --git a/npc/024-6/_warps.txt b/npc/024-6/_warps.txt new file mode 100644 index 0000000..0cad9b4 --- /dev/null +++ b/npc/024-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-6: Frostia Indoors warps +024-6,34,28,0 warp #024-6_34_28 0,0,024-1,143,41 diff --git a/npc/024-6/talindra.txt b/npc/024-6/talindra.txt new file mode 100644 index 0000000..7ef8dad --- /dev/null +++ b/npc/024-6/talindra.txt @@ -0,0 +1,128 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +024-6,31,23,0 script Talindra NPC_ELF_F,{ + .@q = getq(FrostiaQuest_Talindra); + if (BaseLevel < 65) { + mesc l("The elf seems busy. She ignores you."); + close; + } + if (.@q == 0) { + if (frally()) goto L_Start_F; + else goto L_Start_U; + } + else if (.@q == 1) { + if (frally()) goto L_Submit_F; + else goto L_Submit_U; + } + else if (.@q == 2) { + if (frally()) goto L_Done_F; + else goto L_Done_U; + } + Exception("Invalid quest state - "+str(.@q)); + end; + +L_Start_F: + mesn; + mesq lg("Hey friend, could you do me a little favor?"); + next; + mesn; + mesq l("I want to prove exotic tea. More specifically, I'm looking for %s and %s. Bring me %d of each, and I'll reward you awesomely.", getitemlink(CelestiaTea), getitemlink(HerbalTea), 15); + next; + mesn; + mesq l("Good luck!"); + setq FrostiaQuest_Talindra, 1; + close; + +L_Submit_F: + mesn; + mesq l("Did you got everything I've asked?"); + mes sprintf("- %d %s", 15, getitemlink(CelestiaTea)); + mes sprintf("- %d %s", 15, getitemlink(HerbalTea)); + next; + if (askyesno() == ASK_YES) { + if (countitem(CelestiaTea) < 15 || + countitem(HerbalTea) < 15) { + mesn; + mesq l("You've been spending too much time with humans. You are picking up bad habits from them like lying. Be careful."); + close; + } + inventoryplace EquipmentBlueprintD, 1; + delitem CelestiaTea, 15; + delitem HerbalTea, 15; + getitem EquipmentBlueprintD, 1; + getexp 50000, 0; + setq FrostiaQuest_Talindra, 2; + mesn; + mesq l("Yay! Many thanks! I'll enjoy it! Here is the promised reward."); + } + close; + +L_Done_F: + mesn; + mesq l("I've heard Aethyr path was to the west, but I'm not sure west of where... %s", col(l("*sigh*"), 9)); + close; +///////////////////////////////////////////////////////////////////////////////// +L_Start_U: + mesn; + mesq l("Hey %s, could you do me a little favor?", get_race()); + next; + mesn; + mesq l("I want to prove exotic tea. More specifically, I'm looking for %s and %s. Bring me %d of each, and I'll reward you awesomely.", getitemlink(CelestiaTea), getitemlink(HerbalTea), 15); + next; + mesn; + mesq l("Try to don't take too long!"); + setq FrostiaQuest_Talindra, 1; + close; + +L_Submit_U: + mesn; + mesq l("Did you got everything I've asked?"); + mes sprintf("%d %s", 15, getitemlink(CelestiaTea)); + mes sprintf("%d %s", 15, getitemlink(HerbalTea)); + next; + if (askyesno() == ASK_YES) { + if (countitem(CelestiaTea) < 15 || + countitem(HerbalTea) < 15) { + mesn; + mesq l("You liar, I'll teach you a lesson you won't forget!"); + mesc l("%s stabs you with a dagger!", .name$); + percentheal -55, 0; + close; + } + inventoryplace EquipmentBlueprintD, 1; + delitem CelestiaTea, 15; + delitem HerbalTea, 15; + getitem EquipmentBlueprintD, 1; + getexp 50000, 0; + setq FrostiaQuest_Talindra, 2; + mesn; + mesq l("Thanks! I'll enjoy it! Here is the promised reward."); + } + close; + +L_Done_U: + mesn; + mesq l("I've heard Aethyr path was to the west, but I'm not sure west of where... %s", col(l("*sigh*"), 9)); + next; + mesn; + mesq l("...Go mind your own business, %s.", get_race()); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonSkirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADTOP, TrapperHat); + setunitdata(.@npcId, UDT_HAIRSTYLE, 20); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/024-7/_import.txt b/npc/024-7/_import.txt new file mode 100644 index 0000000..41e09e8 --- /dev/null +++ b/npc/024-7/_import.txt @@ -0,0 +1,4 @@ +// Map 024-7: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-7/_warps.txt", +"npc/024-7/afking.txt", diff --git a/npc/024-7/_warps.txt b/npc/024-7/_warps.txt new file mode 100644 index 0000000..fcff269 --- /dev/null +++ b/npc/024-7/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-7: Frostia Indoors warps +024-7,40,29,0 warp #024-7_40_29 0,0,024-1,160,50 diff --git a/npc/024-7/afking.txt b/npc/024-7/afking.txt new file mode 100644 index 0000000..b6dc63d --- /dev/null +++ b/npc/024-7/afking.txt @@ -0,0 +1,175 @@ +// TMW2 scripts. +// Author: +// TMW-BR +// Jesusalva +// Description: +// AFK Cap quest (Lv 47) + +024-7,40,24,0 script AF King NPC_PLAYER,{ + function AFPrologue; + function AFGrinding; + function AFKingGelid; + function AFItemList; + function AFDeliver; + + if (BaseLevel < 47) { + npctalkonce l("*AFK: I am Away From Keyboard*"); + end; + } + .@q=getq(FrostiaQuest_AFKCap); + @flag02474024=false; + + switch (.@q) { + case 0: // Mission not started + mesn; + mesq l("I'm back, I'm here, I'm great! I am the AF King Arthur!"); + next; + do { + mesn; + mesq l("But can I help you?"); + next; + select + l("Good, I need to talk with the King."), + l("You have a nice hat."), + l("Do you know where I can level up?"), + rif(@flag02474024, l("I would like a hat like yours.")), + l("Nothing, good bye!"); + mes ""; + switch (@menu) { + case 1: + AFKingGelid(); + break; + case 2: + AFPrologue(); + @flag02474024=true; + break; + case 3: + AFGrinding(); + break; + case 4: + setq FrostiaQuest_AFKCap, 1; + AFItemList(); + break; + default: + close; + } + } while (true); + break; + case 1: // Mission accepted + AFItemList(); + break; + default: // Mission complete + npctalkonce l("*AFK: I am Away From Keyboard*"); + end; + break; + } + + closedialog; + goodbye; + close; + +function AFPrologue { + mesn; + mesq l("No one is more there than here than me! Look at my hat, it symbolizes that I am an useless player!"); + next; + mesn; + mesq l("And if you listen to Tulimshar's Professor lecture wearing it, you'll gain more experience!"); + next; + mesn; + mesq l("Basically, no one else disturbs me anymore when I'm busy snoring in class! ^.^"); + next; + return; +} + +function AFGrinding { + mesn; + mesq l("Uhh... Not really! I love to kill Yetis, but sometimes they are really shy!"); + next; + mesn; + mesq l("They must be hiding in some cave... It's a shame!"); + next; + return; +} + +function AFKingGelid { + mesn; + mesq l("Uhm, I'm not the king of Frostia, I'm the king of all useless adventurers."); + next; + mesn; + mesq l("I just go out removing swords from rocks, and then throwing them again at the lake. All that while I drink coffee."); + next; + mesn; + mesq l("If you need to talk with King Gelid, he should be in the castle north here."); + next; + return; +} + +function AFItemList { + mesn; + mesq l("Sure, no problem! For this awesome hat which I can make it myself, you'll need to bring me a few items!"); + next; + mes ".:: "+l("Item List")+" ::."; + mesc l("@@/@@ @@", countitem(SilkCocoon), 300, getitemlink(SilkCocoon)); + mesc l("@@/@@ @@", countitem(CobaltHerb), 100, getitemlink(CobaltHerb)); + mesc l("@@/@@ @@", countitem(CottonCloth), 25, getitemlink(CottonCloth)); + mesc l("@@/@@ @@", countitem(BlueDye), 1, getitemlink(BlueDye)); + mesc l("@@/@@ @@", format_number(Zeny), format_number(5000), "GP"); + next; + // FIXME Deprecate .@ scope on flag + if (@flag02474024) { + mesn; + mesq l("Now I'll be waiting!"); + close; + } + select + l("Okay, I'll be back!"), + l("I have everything you've asked for."); + mes ""; + if (@menu == 2) + AFDeliver(); + return; +} + +function AFDeliver { + inventoryplace AFKCap, 1; + if ( + countitem(SilkCocoon) < 300 || + countitem(CobaltHerb) < 100 || + countitem(CottonCloth) < 25 || + countitem(BlueDye) < 1 || + Zeny < 5000) { + mesn; + mesq l("I'll feed you to the Moubootaur %%e"); + setparam(MaxHp, readparam(MaxHp)-100); + die(); + close; + } + Zeny-=5000; + delitem SilkCocoon, 300; + delitem CobaltHerb, 100; + delitem CottonCloth, 25; + delitem BlueDye, 1; + getitem AFKCap, 1; + setq FrostiaQuest_AFKCap, 2; + mesn; + mesq l("As I had the spare materials I've already did yours in advance."); + next; + mesn; + mesq l("There you go. Enjoy it!"); + next; + return; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, AFKCap); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); // TODO + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 0); + npcsit; + + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/024-8/_import.txt b/npc/024-8/_import.txt new file mode 100644 index 0000000..7fcab78 --- /dev/null +++ b/npc/024-8/_import.txt @@ -0,0 +1,5 @@ +// Map 024-8: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-8/_warps.txt", +"npc/024-8/jeremy.txt", +"npc/024-8/nurse.txt", diff --git a/npc/024-8/_warps.txt b/npc/024-8/_warps.txt new file mode 100644 index 0000000..9fee9a5 --- /dev/null +++ b/npc/024-8/_warps.txt @@ -0,0 +1,33 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-8: Frostia Indoors warps +024-8,71,32,0 warp #024-8_71_32 0,0,024-1,136,29 +024-8,69,24,0 script #024-8_69_24 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 37,32; end; +} +024-8,67,24,0 script #024-8_67_24 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 30,32; end; +} +024-8,65,24,0 script #024-8_65_24 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 23,32; end; +} +024-8,37,33,0 script #024-8_37_33 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 69,25; end; +} +024-8,30,33,0 script #024-8_30_33 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 67,25; end; +} +024-8,23,33,0 script #024-8_23_33 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 65,25; end; +} diff --git a/npc/024-8/jeremy.txt b/npc/024-8/jeremy.txt new file mode 100644 index 0000000..79638bd --- /dev/null +++ b/npc/024-8/jeremy.txt @@ -0,0 +1,116 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +024-8,22,27,0 script Jeremy NPC_ELF,{ + .@q = getq(FrostiaQuest_Jeremy); + if (BaseLevel < 50) { + mesc l("The old man seems busy. He ignores you."); + close; + } + if (.@q == 0) { + if (frally()) goto L_Start_F; + else goto L_Start_U; + } + else if (.@q == 1) { + if (frally()) goto L_Submit_F; + else goto L_Submit_U; + } + else if (.@q == 2) { + if (frally()) goto L_Done_F; + else goto L_Done_U; + } + Exception("Invalid quest state - "+str(.@q)); + end; + +L_Start_F: + mesn; + mesq lg("Hey gal, could you do me a little favor?", "Hey pal, could you do me a little favor?"); + next; + mesn; + mesq l("Bring me %d %s, the white slimes keep stealing them and I am a bit too old to go running around, hunting slimes!", 3, getitemlink(SilverMirror)); + setq FrostiaQuest_Jeremy, 1; + close; + +L_Submit_F: + mesn; + mesq l("Did you got the %d %s I've asked?", 3, getitemlink(SilverMirror)); + next; + if (askyesno() == ASK_YES) { + if (countitem(SilverMirror) < 3) { + mesn; + mesq l("You've been spending too much time with humans. You are picking up bad habits from them like lying. Be careful."); + close; + } + delitem SilverMirror, 3; + getexp 24000, 0; + setq FrostiaQuest_Jeremy, 2; + mesn; + mesq lg("Thanks, my friend."); + } + close; + +L_Done_F: + mesn; + mesq l("Thanks for the help earlier."); + next; + mesn; + mesq l("Did you knew that %s was the wizard whom sealed the path to Aethyr, the sacred elf land? I wonder what happened afterwards.", b("Tametomo")); + close; +///////////////////////////////////////////////////////////////////////////////// +L_Start_U: + mesn; + mesq l("What is a %s doing here? Eugh, you stink! You should keep away from our children!", get_race()); + next; + mesn; + mesq l("I hate asking this of a lesser being like you, but if you plan in staying in our town, you should at very least make yourself useful."); + next; + mesn; + mesq l("Bring me %d %s, we elves are infinitely beautiful and the white slimes, just like your kin, seems to envy us. So please recover the stolen mirrors.", 3, getitemlink(SilverMirror)); + setq FrostiaQuest_Jeremy, 1; + close; + +L_Submit_U: + mesn; + mesq l("Did you got the %d %s I've asked?", 3, getitemlink(SilverMirror)); + next; + if (askyesno() == ASK_YES) { + if (countitem(SilverMirror) < 3) { + mesn; + mesq l("You liar, I'll teach you a lesson you won't forget!"); + mesc l("%s slaps you!", .name$); + percentheal -30, 0; + close; + } + delitem SilverMirror, 3; + getexp 24000, 0; + setq FrostiaQuest_Jeremy, 2; + mesn; + mesq l("Hmpf, for a %s, you're a bit resourceful.", get_race()); + } + close; + +L_Done_U: + mesn; + mesq l("What are you still doing here? Begone!"); + next; + mesn; + mesq l("Hmpf, if %s was still around... He sealed the path to Aethyr, the sacred elf land. I'm sure he could protect Frostia from the likes of you as well!", b("Tametomo")); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonTrousers); + setunitdata(.@npcId, UDT_HEADMIDDLE, LeatherShirt); + setunitdata(.@npcId, UDT_HEADTOP, Beard); + setunitdata(.@npcId, UDT_HAIRSTYLE, 26); + setunitdata(.@npcId, UDT_HAIRCOLOR, 3); + npcsit; + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/024-8/nurse.txt b/npc/024-8/nurse.txt new file mode 100644 index 0000000..c50d8e5 --- /dev/null +++ b/npc/024-8/nurse.txt @@ -0,0 +1,23 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Nurse. + +024-8,73,26,0 script Frostia's Nurse NPC_ELF_F,{ + Nurse(.name$, 10, 5); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonSkirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, TneckSweater); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HAIRSTYLE, 12); + setunitdata(.@npcId, UDT_HAIRCOLOR, 14); + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/024-9/_import.txt b/npc/024-9/_import.txt new file mode 100644 index 0000000..b196b3f --- /dev/null +++ b/npc/024-9/_import.txt @@ -0,0 +1,5 @@ +// Map 024-9: Frostia Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/024-9/_warps.txt", +"npc/024-9/barkeeper.txt", +"npc/024-9/sake.txt", diff --git a/npc/024-9/_warps.txt b/npc/024-9/_warps.txt new file mode 100644 index 0000000..6606fb7 --- /dev/null +++ b/npc/024-9/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 024-9: Frostia Indoors warps +024-9,41,38,0 warp #024-9_41_38 0,0,024-1,134,49 diff --git a/npc/024-9/barkeeper.txt b/npc/024-9/barkeeper.txt new file mode 100644 index 0000000..2bdd419 --- /dev/null +++ b/npc/024-9/barkeeper.txt @@ -0,0 +1,54 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Sells beer + +// Temporary Sprite +024-9,39,31,0 script Bar Jobs NPC_DWARF_TRADER,{ + hello; + npcshopattach(.name$); + shop .name$; + close; + +OnInit: + sleep(SHOPWAIT); + tradertype(NST_MARKET); + + sellitem DwarvenSake, 1100, 1; + sellitem Beer, 300, 20; + if ($ARKIM_ST >= 4000) + sellitem ClothoLiquor, -1, (($ARKIM_ST-4000)/500)+1; + if ($ARKIM_ST >= 4700) + sellitem LachesisBrew, -1, (($ARKIM_ST-4700)/500)+1; + if (!rand2(10)) + sellitem JasmineTea, getiteminfo(JasmineTea, ITEMINFO_BUYPRICE)*15/10, rand2(1,3); + + .sex = G_MALE; + .distance = 5; + end; + +OnClock2358: + restoreshopitem DwarvenSake, 1100, 1; + restoreshopitem Beer, 300, 20; + if ($ARKIM_ST >= 4000) + restoreshopitem ClothoLiquor, (($ARKIM_ST-4000)/500)+1; + if ($ARKIM_ST >= 4700) + restoreshopitem LachesisBrew, (($ARKIM_ST-4700)/500)+1; + if (!rand2(10)) + restoreshopitem JasmineTea, getiteminfo(JasmineTea, ITEMINFO_BUYPRICE)*15/10, rand2(1,3); + end; + +// Pay your taxes! +OnBuyItem: + debugmes("Purchase confirmed"); + PurchaseTaxes(); + end; + +OnSellItem: + debugmes("Sale confirmed"); + SaleTaxes(); + end; + +} + diff --git a/npc/024-9/sake.txt b/npc/024-9/sake.txt new file mode 100644 index 0000000..61e1aab --- /dev/null +++ b/npc/024-9/sake.txt @@ -0,0 +1,175 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Dwarven Sake, the most powerful beverage which is not a rare +// Variables: +// DWARVEN_DATE = When the Sake started being done +// DWARVEN_DONE = When the Sake will be ready +// DWARVEN_AMMO = How much Sake you're trying to make +// Success Rate is based on how much you're trying to do and how long ago that was + +024-9,43,30,0 script Sake Barrel NPC_NO_SPRITE,{ + goto L_Main; + // dwarvensake_chance() + // Returns chance (0~10,000) to successfully obtain sake + // DWARVEN_DONE/DWARVEN_DATE is taken in account + function dwarvensake_chance { + .@max=10000; + .@base=DWARVEN_DATE;//-(DWARVEN_DONE-DWARVEN_DATE); + // .@c = how much time is left until completion + // .@d = original amount of time required + // .@e = Current time + .@c=DWARVEN_DONE-.@base; //-gettimetick(2); + .@d=DWARVEN_DATE-.@base; //-DWARVEN_DONE; + .@e=gettimetick(2)-.@base; + + // We must divide everything by 10 to cause imprecision + // aka. don't cause overflow bug + .@c=.@c/10; + .@d=.@d/10; + .@e=.@e/10; + + //debugmes "%d - %d - %d", .@d, .@e, .@c; + //debugmes "Start - Now - Finish"; + if (.@c == 0) + return .@max; + if ($@GM_OVERRIDE) debugmes "Ratio: %d/%d = %d", .@e, .@c, (.@e*.@max)/.@c; + return min(10000, (.@e*.@max)/.@c); + } + +L_Main: + if (!DWARVEN_DATE) { + mesn; + mesc l("Do you want to make sake?"); + mesc l("This barrel is a courtesy from Dimond Cove Inn."); + next; + select + l("Information"), + l("Yes"), + l("No"); + mes ""; + + switch (@menu) { + case 1: + mesc l("Produced item:"); + mesc l("@@", getitemlink(DwarvenSake)); + mes ""; + mesc l("Cost per two glass:"); + mesc l("* @@/@@ @@", countitem(ArtichokeHerb), 25, getitemlink(ArtichokeHerb)); + mesc l("* @@/@@ @@", countitem(MauveHerb), 25, getitemlink(MauveHerb)); + mesc l("* @@/@@ @@", countitem(CobaltHerb), 25, getitemlink(CobaltHerb)); + mesc l("* @@/@@ @@", countitem(GambogeHerb), 25, getitemlink(GambogeHerb)); + mesc l("* @@/@@ @@", countitem(AlizarinHerb), 25, getitemlink(AlizarinHerb)); + mesc l("* @@/@@ @@", countitem(ShadowHerb), 20, getitemlink(ShadowHerb)); + mesc l("* @@ Water Bottle", 1); + next; + break; + case 2: + mesc l("How many batches do you want to produce? (max. 5)"); + input .@glass_count; + if (.@glass_count < 1 || + .@glass_count > 5 || + countitem(ArtichokeHerb) < 25*.@glass_count || + countitem(MauveHerb) < 25*.@glass_count || + countitem(CobaltHerb) < 25*.@glass_count || + countitem(GambogeHerb) < 25*.@glass_count || + countitem(AlizarinHerb) < 25*.@glass_count || + countitem(ShadowHerb) < .@glass_count*20 + ) { + mesc l("Not enough ingredients or invalid amount."), 1; + break; + } + mesc l("Which water will you use?"); + mesc l("The bottom-most the water, the better the bonus."); + menuint + l("Cancel"), -1, + rif(countitem(BottleOfSewerWater) >= .@glass_count, l("Sewer Water")), 0, + rif(countitem(BottleOfSeaWater) >= .@glass_count, l("Sea Water")), 3600, + rif(countitem(BottleOfTonoriWater) >= .@glass_count, l("Tonori Water")), 11760, + rif(countitem(BottleOfWoodlandWater) >= .@glass_count, l("Woodland Water")), 12000, + rif(countitem(BottleOfDivineWater) >= .@glass_count, l("Divine Water")), 21600; + mes ""; + if (@menuret < 0) + break; + switch (@menuret) { + case 0: + .@bonus=@menuret; + .@water=BottleOfSewerWater; + break; + case 3600: + .@bonus=@menuret; + .@water=BottleOfSeaWater; + break; + case 11760: + .@bonus=@menuret; + .@water=BottleOfTonoriWater; + break; + case 12000: + .@bonus=@menuret; + .@water=BottleOfWoodlandWater; + break; + case 21600: + .@bonus=@menuret; + .@water=BottleOfDivineWater; + break; + default: + mesc l("Error, invalid return code, blame Saulc"), 1; + mes "==== SCRIPT ABORTED"; + close; + } + + // Save data + delitem ArtichokeHerb, .@glass_count*25; + delitem MauveHerb, .@glass_count*25; + delitem CobaltHerb, .@glass_count*25; + delitem GambogeHerb, .@glass_count*25; + delitem AlizarinHerb, .@glass_count*25; + delitem ShadowHerb, .@glass_count*20; + delitem .@water, .@glass_count; + DWARVEN_AMMO=.@glass_count; + DWARVEN_DATE=gettimetick(2); + DWARVEN_DONE=gettimetick(2)-.@bonus+.mintime; + DWARVEN_DONE+=.cuptime*DWARVEN_AMMO; + break; + case 3: + close; + break; + } + goto L_Main; + } else { + mesn; + mesc l("Your request for @@ @@ are being fermented for @@.", DWARVEN_AMMO, getitemlink(DwarvenSake), FuzzyTime(DWARVEN_DATE)); + next; + inventoryplace DwarvenSake, DWARVEN_AMMO; + mesn; + mes l("Trying to retrieve it now will have @@ % chance to be successful.", dwarvensake_chance()/100); + mes l("Attempt to retrieve it now?"); + next; + if (askyesno() == ASK_YES) { + if (rand(1000,10000) < dwarvensake_chance()) { + mesc l("Success!"), 3; + getitem DwarvenSake, DWARVEN_AMMO*2; + } else { + mesc l("The sake wasn't ready yet and you lost it..."), 1; + } + DWARVEN_DATE=0; + DWARVEN_AMMO=0; + } + } + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + + // Time to make each batch (12 hours) + .cuptime=(60*60*12); + // Base time to make any amount of cups (72 hours) + .mintime=(60*60*72); + end; + +} + + + diff --git a/npc/025-1/_config.txt b/npc/025-1/_config.txt new file mode 100644 index 0000000..915bd51 --- /dev/null +++ b/npc/025-1/_config.txt @@ -0,0 +1,25 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-1: Fortress Town - Holy Land conf + +025-1,99,112,0 script #025-1_99_112 NPC_HIDDEN,{ + end; +OnDisable: + delcells "025-1_99_112"; end; +OnEnable: +OnInit: + setcells "025-1", 99, 112, 100, 112, 1, "025-1_99_112"; +} + +025-1,100,122,0 script #025-1_100_122 NPC_HIDDEN,7,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnCurse"; + end; +} + +025-1,100,22,0 script #025-1_100_22 NPC_HIDDEN,3,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnCurse"; + end; +} diff --git a/npc/025-1/_import.txt b/npc/025-1/_import.txt new file mode 100644 index 0000000..c373e04 --- /dev/null +++ b/npc/025-1/_import.txt @@ -0,0 +1,17 @@ +// Map 025-1: Fortress Town - Holy Land +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/025-1/_config.txt", +"npc/025-1/_mobs.txt", +"npc/025-1/_warps.txt", +"npc/025-1/anin.txt", +"npc/025-1/commander.txt", +"npc/025-1/ctrl.c", +"npc/025-1/drahcir.txt", +"npc/025-1/ihclot.txt", +"npc/025-1/phoenix.txt", +"npc/025-1/rum.txt", +"npc/025-1/salohcin.txt", +"npc/025-1/selim.txt", +"npc/025-1/teleporter.txt", +"npc/025-1/xovilam.txt", +"npc/025-1/yuko.txt", diff --git a/npc/025-1/_mobs.txt b/npc/025-1/_mobs.txt new file mode 100644 index 0000000..5de3a35 --- /dev/null +++ b/npc/025-1/_mobs.txt @@ -0,0 +1,13 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-1: Fortress Town - Holy Land mobs +025-1,39,96,15,6 monster Moubi 1038,6,60000,30000 +025-1,35,41,10,15 monster Moubi 1038,6,60000,30000 +025-1,78,33,12,11 monster Croc 1006,6,60000,30000 +025-1,120,30,12,8 monster Moubi 1038,6,60000,30000 +025-1,136,30,1,8 monster Croc 1006,6,60000,30000 +025-1,142,77,7,11 monster Croc 1006,6,60000,30000 +025-1,161,86,11,15 monster Moubi 1038,6,60000,30000 +025-1,79,97,12,6 monster Croc 1006,6,60000,30000 +025-1,42,115,15,4 monster Croc 1006,6,60000,30000 +025-1,45,116,20,3 monster Croc 1006,6,60000,30000 +025-1,154,116,20,3 monster Croc 1006,6,60000,30000 diff --git a/npc/025-1/_warps.txt b/npc/025-1/_warps.txt new file mode 100644 index 0000000..b2ec269 --- /dev/null +++ b/npc/025-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-1: Fortress Town - Holy Land warps +025-1,100,123,0 warp #025-1_100_123 7,0,025-2,99,25 diff --git a/npc/025-1/anin.txt b/npc/025-1/anin.txt new file mode 100644 index 0000000..5b4cd96 --- /dev/null +++ b/npc/025-1/anin.txt @@ -0,0 +1,79 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Anin is Nina spelled backwards + +025-1,109,71,0 script Anin The Traveler NPC_F_COINKEEPER,{ + + mesn; + if (strcharinfo(0) == $MOST_HEROIC$) mesq l("Wow! Are you @@? Everyone, in every city, talks about you!", $MOST_HEROIC$); + if (strcharinfo(0) == $MOST_HEROIC$) next; + + mesq l("Hello. I am @@, and I am from a family of travellers. We travel though the whole world, looking for exotic goods.", .name$); + next; + mesq l("You can buy rare items with me, or I can tell you about different cities in our world."); + +L_Menu: + mes ""; + menu + l("I want to trade with you."), L_Trade, + l("Tell me about Tulimshar."), L_Tulim, + l("Tell me about Fortress Town."), L_Town, + l("Sorry, I'll pass."), L_Close; + +L_Tulim: + mes ""; + mesn; + mesq l("Tulimshar is the oldest human city, and its foundation is the year zero of our calendar."); + next; + mesq l("The city only flourished because Janett Platinum had the idea to build city walls surrounding this city."); + next; + mesq l("The desert climate means you'll find mostly maggots and scorpions. Their drops include cactus drinks, cake, knifes, black pearls, gold, and other common things."); + next; + mesq l("You can find for a good price desert equipment and some kind of dyes. You find all sort of crafters, artisans and warriors here."); + next; + goto L_Menu; + +L_Town: + mes ""; + mesn; + mesq l("Fortress Town is connected to the prophecies of the death of all humans, wildlife, and the other lesser races."); + next; + mesq l("The World's Heart was shattered by the Ancient Families to serve as Soul Menhirs on the world, and this allows respawning after death, but at a cost."); + next; + mesq l("The World Edge, which is where we are, is a holy land, the place where all began and all shall perish. Which is why after the World's Heart was broken, it was vowed to never make a settlement here ever again."); + next; + mesq l("Right now this territory is under the Monster Army's control, but the Alliance has occupied it to raid the Impregnable Fortress."); + next; + mesq l("Nearly everyone around is a seasoned veteran or was sent here by the Alliance Council to support the war efforts. Including me. So, good luck!"); + next; + goto L_Menu; + + +L_Trade: + mesn; + mesq l("Use your @@ as currency!", getitemlink(StrangeCoin)); + tutmes l("%s is obtained during events, daily logins, heroic deeds, gifts, etc. But cannot be bought with real money.", getitemlink(StrangeCoin)); + next; + openshop "Aeros Trader"; + closedialog; + +L_Close: + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, UglyChristmasSweater); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, CandorBoots); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 27); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + npcsit; + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/025-1/commander.txt b/npc/025-1/commander.txt new file mode 100644 index 0000000..891c79d --- /dev/null +++ b/npc/025-1/commander.txt @@ -0,0 +1,253 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Leads the Alliance in the Fortress Town + +025-1,96,26,0 script Commander Povo NPC_BRGUARD_SPEAR,{ + mesn; + mesq l("Greetings %s, I am %s, the man in charge for the Alliance occupation of Fortress Town.", (strcharinfo(0) == $MOST_HEROIC$ ? lg("Hero") : lg("Adventurer")), .name$); + next; + if (BARBARA_STATE == 2) { + inventoryplace NPCEyes, 1; + mesn; + mesq l("Sir Pyndragon and Lady Lalica asked to thank thee for the situation with the thief."); + next; + mesn; + mesq l("Therefore, please choose a permanent stat-boosting fruit."); + next; + menuint + l("I'll decide later"), 0, + l("Strength"), StrengthFruit, + l("Agility"), AgilityFruit, + l("Vitality"), VitalityFruit, + l("Intelligence"), IntelligenceFruit, + l("Dexterity"), DexterityFruit, + l("Luck"), LuckFruit; + mes ""; + if (@menuret) { + getitembound @menuret, 1, 4; + BARBARA_STATE += 10; + getexp 0, 250; + mesn; + mesq l("Here you go. Please, keep fighting and growing strong. Improving oneself is... Damn, I forgot the proverb."); + next; + } + } + + // Endtrail + mes ""; + mesc l("@@ You need to wait further releases to continue this quest!", b(l("WARNING:"))), 1; + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + +///////////////////////////////////////////////////////////////////////////////// +025-1,59,86,0 script Commander Cadis NPC_BRGUARD_SWORD,{ + function cadisReward; + mesn; + mesq l("Greetings %s, I am %s, I am in charge of monster extermination.", (strcharinfo(0) == $MOST_HEROIC$ ? lg("Hero") : lg("Adventurer")), .name$); + next; + // Check for ongoing quests + if (getq(FortressQuest_SlimeHunter) == 1) + goto L_SlimeHunter; + if (getq(FortressQuest_Over100) == 1) + goto L_Over100; + if (getq(FortressQuest_RangedHunt) == 1) + goto L_RangedHunt; + mesn; + mesq l("I have extremely difficult quests for you; They are more painful than a Grand Hunter Quest, because I don't admit wimps fighting with me."); + next; + mesn; + mesq l("You will be rewarded by me shall you succeed in any of the tasks."); + mesc l("%s: Once accepted, you must complete them before taking another one.", b(l("WARNING"))); + // Well, we *could* make it more GHQ-like but lazy devs are lazy. + next; + do + { + select + l("I'm not interested."), + rif(!getq(FortressQuest_SlimeHunter), l("The Great Slime Hunt")), + rif(!getq(FortressQuest_Over100), l("The Great Over 100 Hunt")), + rif(!getq(FortressQuest_RangedHunt), l("The Great Ranged Hunt")); + mes ""; + switch (@menu) { + case 1: + close; + case 2: + mesn; + mesq l("You'll have to slay %s %s for me! I don't care which ones, just SLAY THEM!", fnum(.million), l("slimes")); + next; + select + l("Accept"), + l("Reject"); + mes ""; + if (@menu == 1) { + setq1 FortressQuest_SlimeHunter, 1; + mesn; + mesq l("Then get to work already!"); + close; + } + break; + case 3: + mesn; + mesq l("You'll have to slay %s %s for me! I don't care which ones, just SLAY THEM!", fnum(.million), l("monsters of at least level 100")); + mesc l("Each monster will be counted as %d kills.", 2); + next; + select + l("Accept"), + l("Reject"); + mes ""; + if (@menu == 1) { + setq1 FortressQuest_Over100, 1; + mesn; + mesq l("Then get to work already!"); + close; + } + break; + case 4: + mesn; + mesq l("You'll have to slay %s %s for me! I don't care which ones, just SLAY THEM!", fnum(.million), l("ranged monsters")); + mesc l("Minimum range: %d", 3); + next; + select + l("Accept"), + l("Reject"); + mes ""; + if (@menu == 1) { + setq1 FortressQuest_RangedHunt, 1; + mesn; + mesq l("Then get to work already!"); + close; + } + break; + } + } while (true); + close; + +L_SlimeHunter: + cadisReward(FortressQuest_SlimeHunter, l("Slimes")); + close; + +L_Over100: + cadisReward(FortressQuest_Over100, l("over 100 mobs")); + close; + +L_RangedHunt: + cadisReward(FortressQuest_RangedHunt, l("ranged mobs")); + close; + +function cadisReward { + .@kill=getq2(getarg(0)); + .@rewa=getq3(getarg(0)); + mesn; + mesq l("Thus far you've slain %s/%s %s for me!", fnum(.@kill), fnum(.million), b(getarg(1, "monsters"))); + inventoryplace NPCEyes, 4, Iten, 2; + + // ***** ***** ***** Rewards ***** ***** ***** + if (.@kill >= .tier1 && .@rewa < 1) { + mesc l("Milestone reached: %s kills", fnum(.tier1)); + setq3 getarg(0), 1; + getitem MercenaryBoxsetDD, 3; + getitem SacredImmortalityPotion, 2; + getitem MercenaryBoxsetE, 1; + } + + if (.@kill >= .tier2 && .@rewa < 2) { + mesc l("Milestone reached: %s kills", fnum(.tier2)); + setq3 getarg(0), 2; + getitem MercenaryBoxsetEE, 1; + getitem MagicApple, 1; + getitem EquipmentBlueprintE, 1; + getitem IridiumOre, 2; + } + + if (.@kill >= .tier3 && .@rewa < 3) { + mesc l("Milestone reached: %s kills", fnum(.tier3)); + setq3 getarg(0), 3; + getitem MercenaryBoxsetEE, 2; + getitem StrangeCoin, 50; + getitem EarthPowder, 2; + getitem PlatinumOre, 1; + } + + if (.@kill >= .tier4 && .@rewa < 4) { + mesc l("Milestone reached: %s kills", fnum(.tier4)); + setq3 getarg(0), 4; + getitem MercenaryBoxsetEE, 3; + getitem ElixirOfLife, 5; + getitem EarthPowder, 2; + getitem PlatinumOre, 1; + } + + + // Quest complete + if (.@kill >= .million) { + mesc b(l("Quest complete: Congratulations!")), 3; + setq1 getarg(0), 2; + setq3 getarg(0), 5; + getitem MercenaryBoxsetEE, 5; + getitem StrangeCoin, 100; + getitem EarthPowder, 3; + getitem any(LuckFruit, DexterityFruit, IntelligenceFruit, VitalityFruit, AgilityFruit, StrengthFruit), 1; + getitem MysteriousFruit, 1; + } + return; +} + +OnInit: + .sex = G_MALE; + .distance = 5; + .million = 1000000; + + .tier1 = 10000; + .tier2 = 50000; + .tier3 = 250000; + .tier4 = 500000; + end; +} + +// Commander Cadis Questcheck +function script CadisQuestCheck { + if (!playerattached()) + return; + + .@mobId=getarg(0, killedrid); + + if (getq(FortressQuest_SlimeHunter) == 1) { + // Register the kill + if (compare("slime", strtolower(strmobinfo(1, .@mobId)))) { + setq2 FortressQuest_SlimeHunter, getq2(FortressQuest_SlimeHunter)+1; + // Report every 1000 kills + if (getq2(FortressQuest_SlimeHunter) % 1000 == 0) + dispbottom l("Cadis : You have slain %s %s out of a million.", fnum(getq2(FortressQuest_SlimeHunter)), l("slimes")); + } + } + + if (getq(FortressQuest_Over100) == 1) { + // Register the kill as doubled (to make easier) + if (getmonsterinfo(.@mobId, MOB_LV) >= 100) { + setq2 FortressQuest_Over100, getq2(FortressQuest_Over100)+2; + // Report every 1000 kills + if (getq2(FortressQuest_Over100) % 1000 == 0) + dispbottom l("Cadis : You have slain %s %s out of a million.", fnum(getq2(FortressQuest_SlimeHunter)), l("mobs over lv 100")); + } + } + + if (getq(FortressQuest_RangedHunt) == 1) { + // Register the kill + if (getmonsterinfo(.@mobId, MOB_RANGE) >= 3) { + setq2 FortressQuest_RangedHunt, getq2(FortressQuest_RangedHunt)+1; + // Report every 1000 kills + if (getq2(FortressQuest_RangedHunt) % 1000 == 0) + dispbottom l("Cadis : You have slain %s %s out of a million.", fnum(getq2(FortressQuest_SlimeHunter)), l("ranged mobs")); + } + } + + return; +} + diff --git a/npc/025-1/ctrl.c b/npc/025-1/ctrl.c new file mode 100644 index 0000000..7b1d768 --- /dev/null +++ b/npc/025-1/ctrl.c @@ -0,0 +1,572 @@ +// TMW2 Script +// Notes: The Monster King will retake the town every +// OnTue0000 +// (Tuesday, midnight) +// Only the world hero may begin a siege. +// Only one siege per day is allowed +// Writes to MK Temp Var. This variable will unlock the castle gates +// Then the inner gates, and finally, will be a co-requisite to the floors +// Variables: +// $FORTRESS_STATE = int +// 0 - Locked +// 1 - Unlocked +// $@FORTRESS_STATUE = bitmask +// 1,2,4,8,16 - broken statues +// 1024 - Fortress Gate +// 2048 - Siege started +// 4096 - Governor was spawned +// $@FORT_BLACKLIST = int array +// Char ID which already raided this week + +// MAPFLAGS +025-1 mapflag zone SuperMMO +025-3 mapflag zone SuperMMO +026-0 mapflag zone SuperMMO +026-1 mapflag zone SuperMMO +026-2 mapflag zone SuperMMO + +///////////////////////////////////////// +// FUNCTIONS + +// FTCleanup(status) +function script FTCleanup { + // Kills whatever called this function if time is wrong + if ($GAME_STORYLINE < 3) + end; + // Reset variables + $FORTRESS_STATE=getarg(0); + $@FORTRESS_STATUE=0; + // Enable the Magic Statues + enablenpc "Magic Statue#1"; + enablenpc "Magic Statue#2"; + enablenpc "Magic Statue#4"; + enablenpc "Magic Statue#8"; + enablenpc "Magic Statue#16"; + // Kill stray monsters (including town gate) + killmonsterall("025-1"); + // Main gate + if ($FORTRESS_STATE) { + hideonnpc "Gate#F"; + enablenpc "Yuko"; + enablenpc "Ihclot"; + enablenpc "Salohnic"; + enablenpc "Xovilam"; + enablenpc "Drahcir"; + enablenpc "Selim"; + enablenpc "Rum Barrel"; + enablenpc "Commander Povo"; + enablenpc "Commander Cadis"; + enablenpc "Phoenix Rebirth"; + enablenpc "Anin The Traveler"; + enablenpc "The Impregnable Fortress"; + enablenpc "#025-3_100_179"; + donpcevent "#025-1_99_112::OnDisable"; + deletearray $@FORT_BLACKLIST; + } else { + hideoffnpc "Gate#F"; + disablenpc "Yuko"; + disablenpc "Ihclot"; + disablenpc "Salohnic"; + disablenpc "Xovilam"; + disablenpc "Drahcir"; + disablenpc "Selim"; + disablenpc "Rum Barrel"; + disablenpc "Commander Povo"; + disablenpc "Commander Cadis"; + disablenpc "Phoenix Rebirth"; + disablenpc "Anin The Traveler"; + disablenpc "The Impregnable Fortress"; + disablenpc "#025-3_100_179"; + donpcevent "#025-1_99_112::OnEnable"; + mapwarp("025-1", "025-2", 100, 27); + } + enablenpc "#025-1_100_123"; + return; +} + +// FTStatue(id) +function script FTStatue { + .@id=getarg(0); + mesn l("Magic Statue"); + mes l("There is a reading: The Mana Source. The Moubootaur. The Monster King."); + mes l("The war. The blood. The inspiration. The mana. The world. The defiance."); + mes l("The guard. The heir. The originals. The races. The later. The seal."); + mes l("The fragments. The war. The Terranite. AEGIS MAGNA PROTECTIVE SCUTUM."); + next; + mesc l("It seems to be a defensive spell."); + if ($FORTRESS_STATE) + return false; + + // Break the statues? + mesc l("Break the Statue?"), 1; + if (!islegendary()) + mesc l("* May stun you for 10 seconds!"); + next; + if (askyesno() == ASK_YES) { + if ($@FORTRESS_STATUE & .@id) + return false; + if (!islegendary()) + sc_start SC_STUN, 10000, 1; + doevent("Gate#F::OnStatueBreach"); + mapannounce("025-1", strcharinfo(0)+" has broken a statue!", bc_map); + $@FORTRESS_STATUE=$@FORTRESS_STATUE|.@id; + return true; + } + + return false; +} + + + + + + + + + + + + +///////////////////////////////////////// +// NPC SCRIPTS + +// Main gate - Also where the World Hero can begin the siege +025-1,99,112,0 script Gate#F NPC_NO_SPRITE,{ + function spawnMob; + function spawnCore; + // Main Story block - WHAT + if ($GAME_STORYLINE < 3) + die(); + // Still open + if ($FORTRESS_STATE) + end; + // Siege ongoing + if ($@FORTRESS_STATUE) + end; + // Only World Hero may interact + if (strcharinfo(0) != $MOST_HEROIC$ && !is_master()) { + dispbottom l("I will not assault the Fortress Island by myself. Instead, I'll wait for %s.", $MOST_HEROIC$); + end; + } + + // Hey, you can assault the town! + mesc ".:: "+l("THE FORTRESS ISLAND TOWN") + " ::.", 1; + mes l("Behind this gate, lies the Fortress Island Town."); + next; + mesc ".:: "+l("THE FORTRESS ISLAND TOWN") + " ::.", 1; + mesc l("Assault?"), 1; + mes l("* Ensure you and your team is ready and at their positions."); + next; + select + l("Not now."), + l("Information"), + l("Bring it on, we're ready!"); + mes ""; + if (@menu == 1) { + close; + } else if (@menu == 2) { + mesc l("* The siege resets at Tuesday 00:00"); + mesc l("* Sieges increase in difficulty as they are won"); + mesc l("* Unlike monster sieges, the difficulty does not fluctuates based on nº of players or their level!"); + dnext; + mesc l("* The warp to this map will be suspended until the siege is finished."); + mesc l("* The town is liberated once the Monster Governor is slain."); + mesc l("* Monster Governor will only show up when the town magic shield is broken."); + mesc l("* Monster Governor gives 500,000 exp and 100,000 jexp to whoever deals most damage to it"); + dnext; + mesc l("* All monsters in the town and Impregnable Fortress give +25%% EXP"); + mesc l("* You cannot teleport in the town."); + mesc l("* This is a Cursed Lands map, watch out for MP during the fights as well"); + mesc l("* The siege is lost if all players die."); + mesc l("* Only one attempt can be made per day."); + close; + } + + // FIRE THE EVENT + hideonnpc "Gate#F"; + disablenpc "#025-1_100_123"; + initnpctimer; + + // Reset variables + $@FORTRESS_STATUE=2048; + + // Spawn the gate + .@g=monster("025-1", 99, 113, strmobinfo(1, FortressGate), FortressGate, 1, "Gate#F::OnSesame"); + .@bhp=getunitdata(.@g, UDT_MAXHP); + .@bhp=.@bhp*(25+$MK_TEMPVAR)/25; // +4% per success or 8,000 HP + setunitdata(.@g, UDT_MAXHP, .@bhp); + setunitdata(.@g, UDT_HP, .@bhp); + + // Initial defending waves + spawnCore(true); + spawnCore(false); + + // Front Gate Guardians (Lv 70~80) + getmapxy(.@m$, .@x, .@y, 0); + .@x1=.@x-5; + .@x2=.@x+5; + .@y1=.@y-5; + .@y2=.@y+5; + for (.@i = 0; .@i < 4; ++.@i) { + spawnMob(any(AzulSkullSlime, YellowSkullSlime, Forain, GreenDragon, Michel, EliteDuck, Troll, Moonshroom, Terranite), .@x1, .@y1, .@x2, .@y2); + } + + // Player blacklist (unable to use 025-2 warp) + maptimer2("025-1", 10, "Gate#F::OnMPBlacklist"); + kamibroadcast($MOST_HEROIC$+"'s team has begun a siege on Fortress Town. Will they prevail?"); + close; + +///////////////////////////////////////////////////////// +OnSesame: + debugmes("[INFO] FORTRESS TOWN WAS BREACHED"); + $@FORTRESS_STATUE = $@FORTRESS_STATUE|1024; + donpcevent "#025-1_99_112::OnDisable"; + kamibroadcast("The Fortress Town Gate has been breached!"); + spawnCore(true); + close; + +// Heartbeat (B1) +OnTimer35000: + if ($FORTRESS_STATE) + end; + .@breach=($@FORTRESS_STATUE & 1024); + if (mobcount("025-1", "all") < (.@breach ? 180 : 90)) + spawnCore(.@breach); + end; + +// Heartbeat (B2) +OnTimer70000: + if ($FORTRESS_STATE) + end; + + // Initial variables + .@breach=($@FORTRESS_STATUE & 1024); + .@ppl=getmapusers("025-1"); + + // Fail condition + if (.@ppl <= 0) { + kamibroadcast("Players failed to conquer the Fortress Island!"); + FTCleanup($FORTRESS_STATE); + end; + } + + // Spawn mobs + spawnCore(.@breach); + + // Summon reinforcements + maptimer2("025-1", 10, "Gate#F::OnMPReinforce"); + + // Restart timer + initnpctimer; + end; +///////////////////////////////////////////////////////// +OnMPBlacklist: + array_push($@FORT_BLACKLIST, getcharid(0)); + goto OnMPReinforce; + +OnMPReinforce: + // Dispose dead bodies + if (ispcdead() || getmap() != "025-1") { + warp "025-2", 96, 25; + end; + } + // Summon allies + // Last a whole minute + summon("Allied Guard", any(FallenGuard1, FallenGuard2, FallenGuard3)); + end; + +///////////////////////////////////////////////////////// +OnStatueBreach: + spawnCore(true); + getmapxy(.@m$, .@x, .@y, 0); + .@x1=.@x-5; + .@x2=.@x+5; + .@y1=.@y-5; + .@y2=.@y+5; + // Statue Guardians (Lv 70~90) + for (.@i = 0; .@i < 5; ++.@i) { + spawnMob(any(AzulSkullSlime, YellowSkullSlime, Forain, GreenDragon, Michel, EliteDuck, Troll, Moonshroom, Terranite, JackO, BlackMamba, Centaur, GoboBear, TerraniteProtector), .@x1, .@y1, .@x2, .@y2); + } + // Maybe spawn monster governor + if ( + ($@FORTRESS_STATUE & 1) && + ($@FORTRESS_STATUE & 2) && + ($@FORTRESS_STATUE & 4) && + ($@FORTRESS_STATUE & 8) && + ($@FORTRESS_STATUE & 16)) { + // The monster Governor shall now make their appearance! + spawnCore(true); + .@x1=97; + .@y1=21; + .@x2=102; + .@y2=25; + // Governor's Personal Bodyguard (Lv 90~110) + for (.@i = 0; .@i < 4; ++.@i) { + spawnMob(any(TerraniteProtector, LavaSkullSlime, VanityPixie, HolyPixie, ShadowPixie, NulityPixie, BlackSkullSlime, Reaper, NightmareDragon, WhirlyBird, PinkieSuseran), .@x1, .@y1, .@x2, .@y2); + } + .@mob=monster("025-1", any(99, 100), any(22, 23, 24), "Monster Governor", MonsterGeneral, 1, "Gate#F::OnConquest"); + // Stat and Strengthen the governor + // Set governor metadata + setunitdata(.@mob, UDT_LEVEL, 100+$MK_TEMPVAR); + // Update monster modes + .@opt=getunitdata(.@mob, UDT_MODE); + .@opt=.@opt|MD_AGGRESSIVE; + .@opt=.@opt|MD_BOSS; + .@opt=.@opt|MD_NOKNOCKBACK; + setunitdata(.@mob, UDT_MODE, .@opt); + setunitdata(.@mob, UDT_RACE, RC_Legendary); + // Increase health in 2% per siege + .@bhp=75000; + .@bhp=.@bhp*(50+$MK_TEMPVAR)/50; + setunitdata(.@mob, UDT_MAXHP, .@bhp); + setunitdata(.@mob, UDT_HP, .@bhp); + // Accuracy is very very high, immune to crits, always crit + setunitdata(.@mob, UDT_HIT, 9999); + setunitdata(.@mob, UDT_LUK, 65535); + // Increase damage in 1% per siege (remember crit) + .@atk=320; + .@atk=.@atk*(100+$MK_TEMPVAR)/100; + setunitdata(.@mob, UDT_ATKMIN, .@atk); + setunitdata(.@mob, UDT_ATKMAX, .@atk); + // Ranged monster + setunitdata(.@mob, UDT_ATKRANGE, 8); + // Increase defenses in 0.5% per siege + .@def=getunitdata(.@mob, UDT_DEF); + .@def=.@def*(200+$MK_TEMPVAR)/200; + setunitdata(.@mob, UDT_DEF, .@def); + .@def=getunitdata(.@mob, UDT_MDEF); + .@def=.@def*(200+$MK_TEMPVAR)/200; + setunitdata(.@mob, UDT_MDEF, .@def); + .@def=getunitdata(.@mob, UDT_FLEE); + .@def=.@def*(200+$MK_TEMPVAR)/200; + setunitdata(.@mob, UDT_FLEE, .@def); + .@def=getunitdata(.@mob, UDT_PDODGE); + .@def=.@def*(200+$MK_TEMPVAR)/200; + setunitdata(.@mob, UDT_PDODGE, .@def); + // "Normalize" criticals + setunitdata(.@mob, UDT_CRIT, rand2(900, 2700)); + + // Announce that the Fortress Town can now be conquered + kamibroadcast("##1"+"Aegis Scutum has been nullified, the Monster Governor has appeared at Fortress Town!"); + mapannounce "025-1", "Defeat the Monster Governor to capture the fortress town.", bc_map; + } + end; + +///////////////////////////////////////////////////////// +// spawnMob(Mob, X1, Y1, X2, Y2) +function spawnMob { + //.@mob=monster("025-1", rand2(getarg(1), getarg(3)), rand2(getarg(2), getarg(4)), strmobinfo(1, getarg(0)), getarg(0), 1); + .@mob=areamonster("025-1", getarg(1), getarg(2), getarg(3), getarg(4), + strmobinfo(1, getarg(0)), getarg(0), 1); + .@opt=getunitdata(.@mob, UDT_MODE); + // Make aggressive + .@opt=.@opt|MD_AGGRESSIVE; + // All forces can suffer knockback + if (.@opt & MD_NOKNOCKBACK) + .@opt=.@opt^MD_NOKNOCKBACK; + // Save new options + setunitdata(.@mob, UDT_MODE, .@opt); + + // Increase health in 1%+1% per siege + .@bhp=getunitdata(.@mob, UDT_MAXHP); + .@bhp=.@bhp*(101+$MK_TEMPVAR)/100; + setunitdata(.@mob, UDT_MAXHP, .@bhp); + setunitdata(.@mob, UDT_HP, .@bhp); + + // Increase accuracy in 10%+1% per siege + .@acc=getunitdata(.@mob, UDT_HIT); + .@acc=.@acc*(110+$MK_TEMPVAR)/100; + setunitdata(.@mob, UDT_HIT, .@acc); + + // TODO: adjust ViewRange + return; +} + +// spawnCore(breach) +function spawnCore { + // Now, the thing is, I don't care with how powerful your invading forces are. + // I only care with how many success you have. + if (getarg(0)) { + .@x1=24; + .@y1=21; + .@x2=175; + .@y2=105; + .@am=2+$MK_TEMPVAR; + } else { + .@x1=25; + .@y1=110; + .@x2=180; + .@y2=120; + .@am=1+($MK_TEMPVAR/3); + } + freeloop(true); + // Level 40~60 Section + for (.@i = 0; .@i < .@am*2; ++.@i) { + spawnMob(any(Tipiou, Pollet, Wolvern, FireSkull, DarkLizard, BlackScorpion, EarthFairy, FireFairy, WaterFairy, WindFairy, PoisonFairy, DustGatling, DustRifle, DustRevolver, MountainSnake, HoodedNinja, ForestMushroom, GoldenScorpion, Yeti), .@x1, .@y1, .@x2, .@y2); + } + // Level 60~80 Section + for (.@i = 0; .@i < .@am*3/2+1; ++.@i) { + spawnMob(any(Yeti, WickedMushroom, Archant, Scar, Crafty, AzulSkullSlime, YellowSkullSlime, Forain, GreenDragon, Michel, EliteDuck, Troll, Moonshroom, Terranite), .@x1, .@y1, .@x2, .@y2); + } + // Level 80~100 section + for (.@i = 0; .@i < .@am; ++.@i) { + spawnMob(any(RedSkullSlime, Terranite, JackO, BlackMamba, GreenSkullSlime, Centaur, GoboBear, TerraniteProtector), .@x1, .@y1, .@x2, .@y2); + } + // Summoners Section + for (.@i = 0; .@i < .@am; ++.@i) { + spawnMob(any(GreenSlimeMother, BlueSlimeMother, YellowSlimeMother, RedSlimeMother, WhiteSlimeMother, AzulSlimeMother, LavaSlimeMother, BlackSlimeMother), .@x1, .@y1, .@x2, .@y2); + } + if (getarg(0)) { + // DemiBoss section (Internal only, increases every ~2 weeks) + for (.@i = 0; .@i < (.@am/2); ++.@i) { + spawnMob(any(VanityPixie, HolyPixie, ShadowPixie, NulityPixie, Reaper, BlackSkullSlime, NightmareDragon, WhirlyBird, PinkieSuseran), .@x1, .@y1, .@x2, .@y2); + } + } + freeloop(false); + return; +} + +///////////////////////////////////////////////////////// +OnInit: + sleep(200); // Ensure the NPCs will exist when this run + FTCleanup($FORTRESS_STATE); + end; + +OnTue0000: + .@cl=$FORTRESS_STATE; + FTCleanup(false); + if (.@cl) + kamibroadcast("The Monster Army has retaken Fortress Town!"); + end; + +OnConquest: + // Failsafe + if ($GAME_STORYLINE < 3) + end; + // Advance the victory count + $MK_TEMPVAR+=1; + // Handle rewards in low priority + freeloop(true); + for (.@i=0; .@i < getarraysize($@FORT_BLACKLIST); .@i++) { + .@cid=$@FORT_BLACKLIST[.@i]; + rodex_sendmail(.@cid, "Commander Povo", "Fortress Town Reward", "For your bravure in Fortress Town conquest! Cheers!", 0, StrangeCoin, 1+$MK_TEMPVAR); + } + freeloop(false); + // Clean up the remainders of the fight + FTCleanup(true); + kamibroadcast("Fortress Town has been captured by the Allied Forces!"); + // Experience injection to the brave (500k EXP, 100k JEXP) + if (playerattached()) { + getexp 500000, 100000; + getitem StrangeCoin, rand2(40,50); + specialeffect(FX_FANFARE, AREA, getcharid(3)); + } + // Experience injection to the survivors + maptimer("025-1", 10, "Gate#F::OnConBonus"); + stopnpctimer; + // Begin Fires of Steam if needed + if (!$FIRESOFSTEAM && $MK_TEMPVAR >= MK_FIRESOFSTEAM_START) { + kamibroadcast("Andrei Sakar : It has been about three months since we begun battling for control overthe Fortress Town."); + sleep(5000); + kamibroadcast("Andrei Sakar : Since this begun, however, we've heard nothing of Artis Town."); + sleep(5000); + kamibroadcast("Andrei Sakar : Therefore, I invite all of you, brave adventurers, to go on an expedition with me to Artis."); + sleep(5000); + kamibroadcast("Andrei Sakar : "+b("Hopefully nothing terrible happened... Nard shall provide us transport.")); + sleep(5000); + kamibroadcast("Andrei Sakar : I only want people strong enough to battle on the Fortress because the risks are high. Onwards! To victory! I'll be waiting"); + $FIRESOFSTEAM = 1; + } + end; + +OnConBonus: + message strcharinfo(0), l("Commander Povo : Good job %s. You did well.", lg("girl", "man")); // TRANSLATORS: Preserve whitespace. + .@it=any(ApanaCake, SacredLifePotion, SacredManaPotion, SacredImmortalityPotion, DeathPotion, PurificationPotion, ApanaCake); + if (.@it == ApanaCake || .@it == PurificationPotion) + .@qn = rand2(2, 5); + else + .@qn = 1; + getitem .@it, .@qn; + getexp 25000, 10000; + end; +} + + +///////////////////////////////////////////////////////// +// Statue NPCs +025-1,32,55,0 script Magic Statue#1 NPC_STATUE_WIZARD,{ + .@b=FTStatue(strnpcinfo(2, "0")); + if (.@b) + disablenpc .name$; + close; + +OnInit: + .distance=2; + end; +} + +025-1,160,25,0 duplicate(Magic Statue#1) Magic Statue#2 NPC_STATUE_BACCHUS +025-1,156,97,0 duplicate(Magic Statue#1) Magic Statue#4 NPC_STATUE_FAFA +025-1,134,70,0 duplicate(Magic Statue#1) Magic Statue#8 NPC_STATUE_EVILMAN +025-1,80,37,0 duplicate(Magic Statue#1) Magic Statue#16 NPC_STATUE_GUARD + + + + + +///////////////////////////////////////////////////////// +// The Impregnable Fortress Gate +025-1,99,20,0 script The Impregnable Fortress NPC_NO_SPRITE,3,1,{ + // Operation not permitted + if (!$FORTRESS_STATE || $@FORTRESS_STATUE) + end; + // Not unlocked + if ($GAME_STORYLINE >= 3 && $MK_TEMPVAR < MKIF_LV_0F) { + mesc l("The gate is sealed shut."), 1; + mesc l("The monster army is still strong on this floor!"), 1; + mesc l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_0F), 1; + close; + } + mesc l("Visit the Impregnable Fortress, 0F?"); + mesc l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_0F); + if (askyesno() == ASK_YES) + warp "025-3", 100, 178; + closeclientdialog; + close; + +OnTouch: + // Operation not permitted + if (!$FORTRESS_STATE || $@FORTRESS_STATUE) + end; + // Not unlocked + if ($GAME_STORYLINE >= 3 && $MK_TEMPVAR < MKIF_LV_0F) + end; + warp "025-3", 100, 178; + end; + +OnInit: + .distance=4; + end; +} + + +///////////////////////////////////////////////////////// +// Real access to 025-1 map +025-2,96,24,0 script Fortress Town Access NPC_HIDDEN,8,0,{ + end; +OnTouch: + // Disabled + if ($GAME_STORYLINE < 3 || $@FORTRESS_STATUE) + end; + // Open + if ($FORTRESS_STATE) + cwarp "025-1", 99, 122; + // Blacklisted + if (array_find($@FORT_BLACKLIST, getcharid(0)) != -1) + end; + // Not blacklisted + warp "025-1", 99, 122; + end; +} + + diff --git a/npc/025-1/drahcir.txt b/npc/025-1/drahcir.txt new file mode 100644 index 0000000..72a3b7c --- /dev/null +++ b/npc/025-1/drahcir.txt @@ -0,0 +1,16 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Drahcir is Richard spelled backwards + +025-1,167,61,0 script Drahcir NPC_LLOYD,{ + Banker(.name$, "Fortress Town", 2000); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/025-1/ihclot.txt b/npc/025-1/ihclot.txt new file mode 100644 index 0000000..cd093e2 --- /dev/null +++ b/npc/025-1/ihclot.txt @@ -0,0 +1,148 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Ihclot is Tolchi spelled backwards + +025-1,96,66,0 script Ihclot NPC_RAIJIN_FEMALE_LEGION_ARTIS,{ + function apRefresh; + function getPrice; + function getPriceInt; + + .@left=apRefresh(); + mesn; + mesq l("I put options on items upon request, but I am very expensive! This is deducted from your tweaking quota."); + if (!.@left) { + mesc l("You currently have no Tweaking AP, so you will need to wait %02d minute(s).", 60-gettime(GETTIME_MINUTE)), 1; + close; + } + next; + mesn; + mesq l("I'll read the options from you, but the level will be paid by me, and failure rate is non-existant. I am the best. blacksmith. in. the. universe! Use %s if you need to change any options.", b("@ucp")); + csysGUI_Report(); + + mesc l("Please select the item you plan in tweaking."), 1; + // Request and confirm + .@id=requestitemindex(); + if (!csys_Confirm(.@id)) + close; + + // Find numeric ID + delinventorylist(); + getinventorylist(); + .@handle=@inventorylist_id[.@id]; + .@lv=getiteminfo(.@handle, ITEMINFO_ELV); + + // Invalid (absolutely should never happen) + if (.@handle < 1) { + mesn; + mesq l("...What? Which item? Sorry, too much smoke around here."); + next; + close; + } + // Multiple + if (countitem(.@handle) != 1) { + mesn; + mesq l("Sorry, but you have multiple %s.", getitemlink(.@handle)); + next; + close; + } + // Permission NG: (Not Granted) + if (.@lv < .minLevel) { + mesn; + mesq l("Sorry, but I won't dare touch a %s.", getitemlink(.@handle)); + next; + mesn; + mesq l("Depending on the case, Nicholas, in Hurnscald, can do a better job than me."); + tutmes l("The selected item is too weak. %s will only tweak items level %d or higher.", .name$, .minLevel); + next; + close; + } + // Aleady slotted + if (getitemoptionidbyindex(.@id, 0) > 0) { + mesn; + mesq l("I'm not going to try to improve this masterpiece. Look its options!"); + next; + close; + } + + // Calculate price + .@price=.@lv**3; // Cubic function to determine price. + .@price=max(1, .@price/7); + mesn; + mesq l("Please select the level I should use."); + menuint + l("Abort (%d GP)", fnum(.@price*0)), 0, + getPrice(.@price, 10), 10, + getPrice(.@price, 20), 20, + getPrice(.@price, 30), 30, + getPrice(.@price, 40), 40, + getPrice(.@price, 50), 50, + getPrice(.@price, 60), 60, + getPrice(.@price, 70), 70, + getPrice(.@price, 80), 80, + getPrice(.@price, 90), 90, + getPrice(.@price, 100), 100; + mes ""; + if (@menuret == 0) + close; + + // Ask player to confirm + if (!csys_Confirm(.@id)) + close; + + // Perform payment + Zeny-=getPriceInt(.@price, @menuret); + SMITH_TWEAKS+=1; + + // Apply the bonuses + csys_ApplyPerfect(.@id, .@level); + mesn; + mesc l("Well, here you are. I hope you enjoy ^^", 3); + // Act 5+ Grace Reroll + if ($GAME_STORYLINE >= 5) { + next; + mesc l("DO YOU WANT TO REROLL THE ITEM? THIS CANNOT BE UNDONE! (free)"), 1; + next; + if (askyesno() == ASK_YES) + csys_ApplyPerfect(.@id, .@level); + } + close; + +/* *************************************************************************** */ +function apRefresh { + .@left=gettimeparam(GETTIME_HOUR)-SMITH_TWEAKS; + if (.@left > 6) { + .@left=6; + SMITH_TWEAKS=gettimeparam(GETTIME_HOUR)-6; + } + return .@left; +} + +function getPriceInt { + .@price=getarg(0); + .@level=getarg(1); + .@final=.@price*.@level/5; + if (strcharinfo(0) == $MOST_HEROIC$) + .@final=max(1, .@final-(.@level*5000)); + return .@final; +} + +function getPrice { + .@price=getarg(0); + .@level=getarg(1); + .@final=getPriceInt(.@price, .@level); + if (Zeny >= .@final) + return l("Level %d (%s GP)", .@level, fnum(.@final)); + else + return ""; +} + +OnInit: + .sex = G_OTHER; + .distance = 5; + .minLevel = 50; + end; +} + + diff --git a/npc/025-1/phoenix.txt b/npc/025-1/phoenix.txt new file mode 100644 index 0000000..5e80f41 --- /dev/null +++ b/npc/025-1/phoenix.txt @@ -0,0 +1,230 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Notes: +// Phoenix Rebirth + +025-1,66,21,0 script Phoenix Rebirth NPC_BRGUARD_SPEAR,{ + setpcblock(PCBLOCK_HARD, true); + .@phoenix = true; + +OnMain: + mesn; + mesq l("Hello there. My name is %s.", .name$); + next; + mesn; + if (REBIRTH) + .@blvl=0-(REBIRTH_OVERLEVEL/REBIRTH); + else + .@blvl=0; + + switch (REBIRTH) { + case 0: + .@blvl+=99; + .@rebirth$=l("first"); + break; + case 1: + .@blvl+=120; + .@rebirth$=l("second"); + break; + case 2: + .@blvl+=135; + .@rebirth$=l("third"); + break; + case 3: + .@blvl+=150; + .@rebirth$=l("fourth"); + break; + case 4: + .@blvl+=175; + .@rebirth$=l("final"); + if ($GAME_STORYLINE >= 5) + break; + default: + setpcblock(PCBLOCK_HARD, false); + mesc l("Unfortunately you cannot rebirth anymore!"); + close; + break; + } + .@blvl=max(90, .@blvl); // Do not allow it to go too far below! + mesc l("Once you reach level %d, I'll offer you a life-time opportunity to delete all your levels and experience for your %s rebirth!", .@blvl, .@rebirth$); + next; + mesn; + mesq l("Rebirth is free. Here are the perks you'll get by doing the rebirth:"); + mes ""; + mes l("* +3 Inventory Slots")+" ##1##BWIP##b##0"; + mes l("* +1 in all attributes, permanently"); + mes l("* +1 Magic Skill Point"); + mes l("* +%d%% EXP Gain and Drop Chance, permanently", 2); + mes l("* 1 %s", getitemlink(SupremeGift)); + mes l("* 1 Trait"); + mes l("* Healing items improved"); + mes l("* %s Level up", getskillname(TMW2_DROPS)); + if (BaseLevel < .@blvl) { + setpcblock(PCBLOCK_HARD, false); + close; + } + next; + mesn; + mesq l("Are you interested? You'll keep %s your equipment, magic, quest progression, craft recipes, money, whatever else which is not a level. Not even Job level won't be reset!", b(l("ALL"))); + mesc l("This cannot be undo later!"), 1; + next; + setpcblock(PCBLOCK_HARD, false); + inventoryplace NPCEyes, 7; + select + l("Do not rebirth"), + l("Rebirth"), + l("Do not rebirth"); + mes ""; + if (@menu != 2) + close; + mesc l("ARE YOU SURE? THIS CANNOT BE UNDONE LATER!!"), 1; + if (askyesno() != ASK_YES) + close; + + setnpcdialogtitle l("Rebirth Trait Selection"); + mesc l("Please select a trait."); + mesc l("This choice CANNOT be undone later."), 1; + menuint + l("Cancel"), 0, + rif(!(PCBONUS & PCB_ATKBONUS), l("Atk +25")), PCB_ATKBONUS, + rif(!(PCBONUS & PCB_MATKBONUS), l("Matk +25")), PCB_MATKBONUS, + rif(!(PCBONUS & PCB_DEFBONUS), l("Def +20")), PCB_DEFBONUS, + rif(!(PCBONUS & PCB_MDEFBONUS), l("MDEF +10")), PCB_MDEFBONUS, + rif(!(PCBONUS & PCB_EVDBONUS), l("Evasion +20")), PCB_EVDBONUS, + rif(!(PCBONUS & PCB_HITBONUS), l("Accuracy +25")), PCB_HITBONUS, + rif(!(PCBONUS & PCB_CRITBONUS), l("Crit +5%")), PCB_CRITBONUS, + rif(!(PCBONUS & PCB_DOUBLEATK), l("Double Attack +5%")), PCB_DOUBLEATK, + rif(!(PCBONUS & PCB_ALLSTATS), l("All Stats +1")), PCB_ALLSTATS, + rif(!(PCBONUS & PCB_HPBONUS), l("HP +500")), PCB_HPBONUS, + rif(!(PCBONUS & PCB_MPBONUS), l("MP +200")), PCB_MPBONUS, + rif(!(PCBONUS & PCB_ASPDBONUS), l("Atk. Speed +10")), PCB_ASPDBONUS, + rif(!(PCBONUS & PCB_WSPDBONUS), l("Walk +5%")), PCB_WSPDBONUS, + rif(!(PCBONUS & PCB_WEIGHTBONUS), l("Max Weight +1kg")), PCB_WEIGHTBONUS, + rif(!(PCBONUS & PCB_EXPBONUS), l("EXP Gain +10%")), PCB_EXPBONUS, + rif(!(PCBONUS & PCB_NOKNOCKBACK), l("Knockback Immunity")), PCB_NOKNOCKBACK, + rif(REBIRTH && !(PCBONUS & PCB_SPLASHMASTER), l("AoE (not stackable)")), PCB_SPLASHMASTER, + rif(REBIRTH && !(PCBONUS & PCB_RANGEMASTER), l("Atk Range +1")), PCB_RANGEMASTER, + rif(REBIRTH < 5 && !(PCBONUS & PCB_LEGENDARY), l("No penalty against Legendary mobs")), PCB_LEGENDARY; + if (@menuret == 0) return; + PCBONUS=PCBONUS|@menuret; + + // Okay, then a rebirth it is! + expandinventory(3); + REBIRTH+=1; + REBIRTH_OVERLEVEL=max(0, BaseLevel-.@blvl-(REBIRTH_OVERLEVEL/REBIRTH)); + resetlvl(3); + sk_lvup(TMW2_DROPS); + NewcomerEXPDROPUP(); + getvaultexp(20); + getitembound SupremeGift, 1, 4; + warp "005-1", 40, 117; + LOCATION$="Candor"; + kamibroadcast(strcharinfo(0)+" has been reborn."); + // Maybe you were THE FIRST + if (!REBIRTH_WINNER) + REBIRTH_WINNER = gettimetick(2); + if ($REBIRTH_WINNER$ == "") { + $REBIRTH_WINNER$=strcharinfo(0); + channelmes("#world", $REBIRTH_WINNER$+" is the first player to REBIRTH!! They are so OP! %%N"); + announce "All hail ##B"+$REBIRTH_WINNER$+"##b, first player to REBIRTH and become OP!", bc_all|bc_npc; + getitem SupremeGift, 1; + next; + } + // These operations can be delayed or fail + StatusResetReinvest(); + if ($EVENT$ == "Rebirth" && .@phoenix) + BaseLevel = 3; + // Open a new dialog + clear; + setnpcdialogtitle l("Rebirth Race Selection"); + mesc l("Do you want to change your race?"); + mesc l("This can only be reverted at rebirth! Beware!"); + next; + if (askyesno() != ASK_YES) + close; + + // Rebirth: 0 + mes ""; + mes ".:: "+l("Human")+" ::."; + mesc l("The most widespread race in the continent, highly versatile, perfect all-rounders."); + mesc l("Strength: NONE"), 2; + mesc l("Weakness: NONE"), 1; + + + // Rebirth: 1 + if (REBIRTH >= 1) { + mes ""; + mes ".:: "+l("Elven")+" ::."; + mesc l("They are highly intelligent, and have higher magical affinity, and live longer, but lacks creativity."); + mesc l("Strength: INT+"), 2; + mesc l("Weakness: STR-"), 1; + mes ""; + mes ".:: "+l("Tritan")+" ::."; + mesc l("Blue from sea, learned how to hunt. But unfortunately, not how to withstand attacks."); + mesc l("Strength: DEX+"), 2; + mesc l("Weakness: VIT-"), 1; + } + + + // Rebirth: 2 + if (REBIRTH >= 2) { + mes ""; + mes ".:: "+l("Raijin")+" ::."; + mesc l("Lives undercover in human society. Luck led them to surviving this far."); + mesc l("Strength: LUK+"), 2; + mesc l("Weakness: DEX-"), 1; + mes ""; + mes ".:: "+l("Orc")+" ::."; + mesc l("Highly strong and bulky, faces their foes head-on."); + mesc l("Strength: STR+, Carry Weight +"), 2; + mesc l("Weakness: AGI-"), 1; + } + + + // Rebirth: 3 + if (REBIRTH >= 3) { + mes ""; + mes ".:: "+l("Ukar")+" ::."; + mesc l("Superb stealth, and quick on their toes."); + mesc l("Strength: AGI+, Walk Speed +"), 2; + mesc l("Weakness: LUK-"), 1; + mes ""; + mes ".:: "+l("Redy")+" ::."; + mesc l("Grown in harsh environments, withstand anything."); + mesc l("Strength: VIT++"), 2; + mesc l("Weakness: INT-"), 1; + } + + + // Rebirth: 5 + if (REBIRTH >= 5) { + mes ""; + mes ".:: "+l("Savior")+" ::."; + mesc l("Unparelled, strength without precedents. Ancient beings of pure mana, rumored to be immortal."); + mesc l("Strength: ALL"), 2; + mesc l("Weakness: NONE"), 1; + } + next; + menuint + l("Kaizei Human"), 0, + rif(REBIRTH >= 1, l("Elven")), 3, + rif(REBIRTH >= 1, l("Tritan")), 6, + rif(REBIRTH >= 2, l("Raijin")), 5, + rif(REBIRTH >= 2, l("Orc")), 4, + rif(REBIRTH >= 3, l("Ukar")), 7, + rif(REBIRTH >= 3, l("Redy")), 8, + rif(REBIRTH >= 5, l("Savior")), 9, + l("Argaes Human"), 1, + l("Tonori Human"), 2, + l("Don't change race"), Class; + + jobchange max(0, @menuret); + close; + +OnInit: + .sex = G_MALE; + .distance = 7; + end; +} + diff --git a/npc/025-1/rum.txt b/npc/025-1/rum.txt new file mode 100644 index 0000000..68a6e16 --- /dev/null +++ b/npc/025-1/rum.txt @@ -0,0 +1,181 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Crazyfefe Rum +// Variables: +// FORTRESS_DATE = When the Rum started being done +// FORTRESS_DONE = When the Rum will be ready +// FORTRESS_AMMO = How much Rum you're trying to make +// Success Rate is based on how much you're trying to do and how long ago that was + +025-1,106,56,0 script Rum Barrel NPC_NO_SPRITE,{ + goto L_Main; + // rumbarrel_chance() + // Returns chance (0~10,000) to successfully obtain sake + // FORTRESS_DONE/FORTRESS_DATE is taken in account + function rumbarrel_chance { + .@max=10000; + .@base=FORTRESS_DATE;//-(FORTRESS_DONE-FORTRESS_DATE); + // .@c = how much time is left until completion + // .@d = original amount of time required + // .@e = Current time + .@c=FORTRESS_DONE-.@base; //-gettimetick(2); + .@d=FORTRESS_DATE-.@base; //-FORTRESS_DONE; + .@e=gettimetick(2)-.@base; + + // We must divide everything by 10 to cause imprecision + // aka. don't cause overflow bug + .@c=.@c/10; + .@d=.@d/10; + .@e=.@e/10; + + //debugmes "%d - %d - %d", .@d, .@e, .@c; + //debugmes "Start - Now - Finish"; + if (.@c == 0) + return .@max; + if ($@GM_OVERRIDE) debugmes "Ratio: %d/%d = %d", .@e, .@c, (.@e*.@max)/.@c; + return min(10000, (.@e*.@max)/.@c); + } + +L_Main: + if (!FORTRESS_DATE) { + mesn; + mesc l("Do you want to make %s?", l("Rum")); + mesc l("This barrel is a courtesy from Dimond Cove Inn."); + next; + select + l("Information"), + l("Yes"), + l("No"); + mes ""; + + switch (@menu) { + case 1: + mesc l("Produced item:"); + mesc l("@@", getitemlink(CrazyRum)); + mesc l("%s (rare)", getitemlink(WhiskeyAle)); + mes ""; + mesc l("Cost per %s glass:", l("twelve")); + mesc l("* @@/@@ @@", countitem(Beer), 5, getitemlink(Beer)); + mesc l("* @@/@@ @@", countitem(ArtichokeHerb), 25, getitemlink(ArtichokeHerb)); + mesc l("* @@/@@ @@", countitem(Fungus), 25, getitemlink(Fungus)); + mesc l("* @@/@@ @@", countitem(RedApple), 25, getitemlink(RedApple)); + mesc l("* @@/@@ @@", countitem(Milk), 25, getitemlink(Milk)); + mesc l("* @@/@@ @@", countitem(ShadowHerb), 100, getitemlink(ShadowHerb)); + mesc l("* @@ Water Bottle", 1); + next; + break; + case 2: + mesc l("How many batches do you want to produce? (max. 5)"); + input .@glass_count; + if (.@glass_count < 1 || + .@glass_count > 5 || + countitem(ArtichokeHerb) < 25*.@glass_count || + countitem(Fungus) < 25*.@glass_count || + countitem(RedApple) < 25*.@glass_count || + countitem(Beer) < 5*.@glass_count || + countitem(Milk) < 25*.@glass_count || + countitem(ShadowHerb) < 100*.@glass_count + ) { + mesc l("Not enough ingredients or invalid amount."), 1; + break; + } + mesc l("Which water will you use?"); + mesc l("The bottom-most the water, the better the bonus."); + menuint + l("Cancel"), -1, + rif(countitem(BottleOfSewerWater) >= .@glass_count, l("Sewer Water")), 0, + rif(countitem(BottleOfSeaWater) >= .@glass_count, l("Sea Water")), 3600, + rif(countitem(BottleOfTonoriWater) >= .@glass_count, l("Tonori Water")), 11760, + rif(countitem(BottleOfWoodlandWater) >= .@glass_count, l("Woodland Water")), 12000, + rif(countitem(BottleOfDivineWater) >= .@glass_count, l("Divine Water")), 21600; + mes ""; + if (@menuret < 0) + break; + switch (@menuret) { + case 0: + .@bonus=@menuret; + .@water=BottleOfSewerWater; + break; + case 3600: + .@bonus=@menuret; + .@water=BottleOfSeaWater; + break; + case 11760: + .@bonus=@menuret; + .@water=BottleOfTonoriWater; + break; + case 12000: + .@bonus=@menuret; + .@water=BottleOfWoodlandWater; + break; + case 21600: + .@bonus=@menuret; + .@water=BottleOfDivineWater; + break; + default: + mesc l("Error, invalid return code, blame Saulc"), 1; + mes "==== SCRIPT ABORTED"; + close; + } + + // Save data + delitem ArtichokeHerb, .@glass_count*25; + delitem Fungus, .@glass_count*25; + delitem RedApple, .@glass_count*25; + delitem Beer, .@glass_count*5; + delitem Milk, .@glass_count*25; + delitem ShadowHerb, .@glass_count*100; + delitem .@water, .@glass_count; + FORTRESS_AMMO=.@glass_count; + FORTRESS_DATE=gettimetick(2); + FORTRESS_DONE=gettimetick(2)-.@bonus+.mintime; + FORTRESS_DONE+=.cuptime*FORTRESS_AMMO; + break; + case 3: + close; + break; + } + goto L_Main; + } else { + mesn; + mesc l("Your request for @@ @@ are being fermented for @@.", FORTRESS_AMMO, getitemlink(CrazyRum), FuzzyTime(FORTRESS_DATE)); + next; + inventoryplace CrazyRum, FORTRESS_AMMO; + mesn; + mes l("Trying to retrieve it now will have @@ % chance to be successful.", rumbarrel_chance()/100); + mes l("Attempt to retrieve it now?"); + next; + if (askyesno() == ASK_YES) { + if (rand(1000,10000) < rumbarrel_chance()) { + mesc l("Success!"), 3; + if (REBIRTH > rand2(20)) + getitem WhiskeyAle, FORTRESS_AMMO*.cupammo; + else + getitem CrazyRum, FORTRESS_AMMO*.cupammo; + } else { + mesc l("The %s wasn't ready yet and you lost it...", l("Rum")), 1; + } + FORTRESS_DATE=0; + FORTRESS_AMMO=0; + } + } + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + + // Time to make each batch (48 hours) + .cuptime=(60*60*48); + // Base time to make any amount of cups (120 hours - five days) + .mintime=(60*60*120); + // Amount of cups + .cupammo=12; + end; + +} + + + diff --git a/npc/025-1/salohcin.txt b/npc/025-1/salohcin.txt new file mode 100644 index 0000000..6063eaa --- /dev/null +++ b/npc/025-1/salohcin.txt @@ -0,0 +1,55 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Nicholas the Blacksmith, spelled backwards + +025-1,108,67,0 script Salohnic NPC_NICHOLAS,{ + // Define variables + if ($GAME_STORYLINE >= 5) { + .@tx$=l("thrice"); + .@txn=4; + .@prc=150000; + } else if ($GAME_STORYLINE >= 4) { + .@tx$=l("twice"); + .@txn=3; + .@prc=90000; + } else { + .@tx$=l("once"); + .@txn=2; + .@prc=60000; + } + + // World Hero bonuses + if (reputation("Candor") >= 100 && + reputation("Tulim") >= 100 && + reputation("Halin") >= 100 && + reputation("Hurns") >= 100 && + reputation("LoF") >= 100 && + reputation("Nival") >= 100 && + reputation("Frostia") >= 100) + .@prc-=20000; + + if (strcharinfo(0) == $MOST_HEROIC$) + .@prc-=20000; + + // NPC body + mesn; + mesq l("Hello there, I am %s and I change item options, can I help you today?", .name$); + mes ""; + mesn; + mesq l("You'll be charged even if you fail, be warned. You can re-roll the same item %s, free of charge, but it may break and there will be no refunds!", .@tx$); + next; + + if (!SmithTweakSystem(.@prc, .@txn)) { + mes ""; + mesn; + mesq l("You can always try again another day!"); + } + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/025-1/selim.txt b/npc/025-1/selim.txt new file mode 100644 index 0000000..76fbd9b --- /dev/null +++ b/npc/025-1/selim.txt @@ -0,0 +1,81 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Selim is just a barber. No puns this time. + +025-1,101,58,0 script Selim NPC_ELVEN_FEMALE_ARMOR_SHOP,{ + function setRace { + clear; + setnpcdialogtitle l("%s - Modify Race", .name$); + mes l("Race") + ": " + get_race(); + next; + mes l("Please select the desired race."); + select + l("Kaizei Human"), + l("Argaes Human"), + l("Tonori Human"), + l("Elf"), + l("Orc"), + l("Raijin"), + l("Tritan"), + l("Ukar"), + l("Redy"), + l("Savior"); + switch (@menu) + { + default: + jobchange max(0, @menu-1); + } + return; + } + + + mesn; + mesq l("Hi! Do you want a hair cut?"); + + do + { + select + l("What is my current hairstyle and hair color?"), + l("I'd like to get a different style."), + l("Can you do something with my color?"), + rif(is_gm() || REBIRTH >= 5, l("I want to change my Race!")), + l("I'm fine for now, thank you."); + + switch (@menu) + { + case 1: + BarberSayStyle 3; + break; + case 2: + BarberChangeStyle; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Enjoy your new style."); + l("Anything else?"); + break; + case 3: + BarberChangeColor; + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("I hope you like this color."); + l("Anything else?"); + break; + case 4: + setRace; + break; + case 5: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Feel free to come visit me another time."); + + goodbye; + } + } while (1); + close; + + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/025-1/teleporter.txt b/npc/025-1/teleporter.txt new file mode 100644 index 0000000..4fee051 --- /dev/null +++ b/npc/025-1/teleporter.txt @@ -0,0 +1,23 @@ +// TMW2 Script +// Authors: +// Jesusalva +// Description: +// Link portals to soul menhirs like the teleporters from old +// The price is temporary. This feature got in because no ship in Nivalis Port +// PS. Anise => “Aisen” Anagram + + +025-1,40,115,0 script #WarpGateFort NPC_HIDDEN,1,0,{ + end; + +OnTouch: + TeleporterGate(TP_FORT); + close; + + +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + diff --git a/npc/025-1/xovilam.txt b/npc/025-1/xovilam.txt new file mode 100644 index 0000000..886b038 --- /dev/null +++ b/npc/025-1/xovilam.txt @@ -0,0 +1,47 @@ +// TMW2 Script +// Author: +// Jesusalva + +025-1,143,59,0 script Xovilam NPC_PLAYER,{ + + speech S_LAST_NEXT, + l("I am @@, an alchemist specialized in reset potions.", .name$); + + select + l("Can you reset my stats please?"), + l("You are weird, I have to go sorry."); + + switch (@menu) + { + case 1: + goto L_ResetStats; + case 2: + goto L_Quit; + } + +L_ResetStats: + mesn; + mesq l("Status point reset can't be undone. Do you really want this?"); + +L_ConfirmReset: + ConfirmStatusReset(1000, false); + goto L_Quit; + + +L_Quit: + goodbye; + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .sex = G_MALE; + .distance = 4; + end; +} diff --git a/npc/025-1/yuko.txt b/npc/025-1/yuko.txt new file mode 100644 index 0000000..81336bf --- /dev/null +++ b/npc/025-1/yuko.txt @@ -0,0 +1,71 @@ +// TMW2 Script +// Author: +// Jesusalva +// Yuko Cuf is an anagram of YuckFou (see also 102 58) + +025-1,93,59,0 script Yuko NPC_PLAYER,{ + mesn; + mesq l("Cuf! Cuf!"); // --TRANSLATORS: NOT A TYPO + next; + mesn "Yuko Cuf"; + mesq l("Can I help you somehow today?"); + next; + closeclientdialog; + shop .name$; + goodbye; + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, DarkKnightHelmet); + setunitdata(.@npcId, UDT_HEADMIDDLE, RedknightArmor); + setunitdata(.@npcId, UDT_HEADBOTTOM, BromenalPants); + setunitdata(.@npcId, UDT_WEAPON, BlueKnightShield); // Boots + setunitdata(.@npcId, UDT_HAIRSTYLE, 7); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + + .sex = G_MALE; + .distance = 4; + + sleep(SHOPWAIT); + tradertype(NST_MARKET); + sellitem InsuranceContract, -1, 1; + sellitem HomunResetPotion, -1, 5; + sellitem PurificationPotion, -1, 20; + sellitem AtroposMixture, -1, 50; + sellitem ThornAmmoBox, -1, 5; + sellitem PoisonAmmoBox, -1, 10; + sellitem CursedAmmoBox, -1, 15; + sellitem PileOfAsh, -1, 25; + sellitem BoneKnife, -1, 1; + sellitem LeaderWand, -1, 1; + sellitem BromenalShield, -1, 1; + sellitem LightPlatemail, -1, 1; + sellitem BromenalPants, -1, 1; + sellitem CandleHelmet, -1, 1; + sellitem WarlordHelmet, -1, 1; + end; + +OnWed0016: + restoreshopitem BoneKnife, 1; + restoreshopitem LeaderWand, 1; + restoreshopitem BromenalShield, 1; + restoreshopitem LightPlatemail, 1; + restoreshopitem BromenalPants, 1; + restoreshopitem CandleHelmet, 1; + restoreshopitem WarlordHelmet, 1; + end; + +OnClock1149: + restoreshopitem InsuranceContract, 1; + restoreshopitem ThornAmmoBox, 5; + restoreshopitem PileOfAsh, 25; +OnClock2359: + restoreshopitem HomunResetPotion, 5; + restoreshopitem PurificationPotion, 20; + restoreshopitem AtroposMixture, 50; + restoreshopitem PoisonAmmoBox, 10; + restoreshopitem CursedAmmoBox, 15; + end; +} + diff --git a/npc/025-2-1/_import.txt b/npc/025-2-1/_import.txt new file mode 100644 index 0000000..362abe0 --- /dev/null +++ b/npc/025-2-1/_import.txt @@ -0,0 +1,4 @@ +// Map 025-2-1: Fortress Island Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/025-2-1/_mobs.txt", +"npc/025-2-1/_warps.txt", diff --git a/npc/025-2-1/_mobs.txt b/npc/025-2-1/_mobs.txt new file mode 100644 index 0000000..126a6a6 --- /dev/null +++ b/npc/025-2-1/_mobs.txt @@ -0,0 +1,9 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2-1: Fortress Island Cave mobs +025-2-1,37,89,13,28 monster Archant 1026,6,60000,60000 +025-2-1,107,42,11,10 monster Crafty 1018,6,60000,60000 +025-2-1,85,96,7,8 monster Whirly Bird 1232,3,60000,60000 +025-2-1,59,40,22,13 monster Terranite Protector 1212,6,60000,60000 +025-2-1,98,97,16,19 monster Terranite 1167,4,60000,60000 +025-2-1,66,83,16,31 monster Troll 1171,10,60000,60000 +025-2-1,70,71,50,48 monster Archant 1026,24,60000,60000 diff --git a/npc/025-2-1/_warps.txt b/npc/025-2-1/_warps.txt new file mode 100644 index 0000000..40c83df --- /dev/null +++ b/npc/025-2-1/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2-1: Fortress Island Cave warps +025-2-1,23,67,0 warp #025-2-1_23_67 0,0,025-2,148,86 +025-2-1,29,105,0 warp #025-2-1_29_105 0,0,025-2,158,99 diff --git a/npc/025-2-2/_import.txt b/npc/025-2-2/_import.txt new file mode 100644 index 0000000..a2adedf --- /dev/null +++ b/npc/025-2-2/_import.txt @@ -0,0 +1,4 @@ +// Map 025-2-2: Ancient Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/025-2-2/_mobs.txt", +"npc/025-2-2/_warps.txt", diff --git a/npc/025-2-2/_mobs.txt b/npc/025-2-2/_mobs.txt new file mode 100644 index 0000000..a72cee7 --- /dev/null +++ b/npc/025-2-2/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2-2: Ancient Cave mobs +025-2-2,47,44,17,18 monster Archant 1026,5,32000,8000 +025-2-2,47,47,23,18 monster Cave Maggot 1027,8,2000,8000 +025-2-2,41,30,13,6 monster Azul Skull Slime 1402,3,75000,20000 +025-2-2,51,56,15,7 monster Black Scorpion 1074,1,4000,8000 +025-2-2,60,48,11,14 monster Crafty 1018,3,50000,2500 +025-2-2,36,43,11,7 monster Crafty 1018,2,50000,2500 diff --git a/npc/025-2-2/_warps.txt b/npc/025-2-2/_warps.txt new file mode 100644 index 0000000..b81fa28 --- /dev/null +++ b/npc/025-2-2/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2-2: Ancient Cave warps +025-2-2,43,44,0 warp #025-2-2_43_44 1,0,025-2-4,44,97 +025-2-2,68,62,0 warp #025-2-2_68_62 0,2,025-2-3,22,45 +025-2-2,37,41,0 warp #025-2-2_37_41 2,0,025-2,25,43 diff --git a/npc/025-2-3/_import.txt b/npc/025-2-3/_import.txt new file mode 100644 index 0000000..a335230 --- /dev/null +++ b/npc/025-2-3/_import.txt @@ -0,0 +1,5 @@ +// Map 025-2-3: Pinkie Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/025-2-3/_mobs.txt", +"npc/025-2-3/_warps.txt", +"npc/025-2-3/boss.txt", diff --git a/npc/025-2-3/_mobs.txt b/npc/025-2-3/_mobs.txt new file mode 100644 index 0000000..557abb3 --- /dev/null +++ b/npc/025-2-3/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2-3: Pinkie Cave mobs +025-2-3,55,39,13,14 monster Pinkie 1132,18,4000,8000 +025-2-3,54,41,15,12 monster Pinkie Suseran 1419,10,4000,8000 +025-2-3,56,41,17,10 monster Pinkie Maximus 1249,9,20000,2500 +025-2-3,48,40,26,6 monster Cave Maggot 1027,9,2000,20000 diff --git a/npc/025-2-3/_warps.txt b/npc/025-2-3/_warps.txt new file mode 100644 index 0000000..ab19203 --- /dev/null +++ b/npc/025-2-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2-3: Pinkie Cave warps +025-2-3,21,46,0 warp #025-2-3_21_46 0,1,025-2-2,67,61 diff --git a/npc/025-2-3/boss.txt b/npc/025-2-3/boss.txt new file mode 100644 index 0000000..7621eb1 --- /dev/null +++ b/npc/025-2-3/boss.txt @@ -0,0 +1,39 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Pinkie Emperor Boss + +025-2-3,0,0,0 script #BossCtrl_025-2-3 NPC_HIDDEN,{ + end; + +// Respawn every hour +OnTimer3600000: + stopnpctimer; +OnInit: + setarray .xp, 268, 55, 371, 482, 212; + setarray .yp, 90, 45, 38, 114, 148; + .@tg=rand2(getarraysize(.xp)-1); + monster "025-2-3", .xp[.@tg], .yp[.@tg], strmobinfo(1, PinkieEmperor), PinkieEmperor, 1, "#BossCtrl_025-2-3::OnBossDeath"; + end; + +OnBossDeath: + initnpctimer; + .@party=getcharid(1); + getitem StrangeCoin, 1; + if (.@party > 0) { + mapannounce "025-2-3", "Boss deafeated by Party: " + getpartyname(.@party), bc_all; + partytimer("025-2-3", 200, "#BossCtrl_025-2-3::OnBossReward"); + } else { + mapannounce "025-2-3", "Boss deafeated by: " + strcharinfo(0), bc_all; + } + callfunc "02524_Revenge_BlackBox"; + fix_mobkill(PinkieEmperor); + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +OnBossReward: + if (ispcdead()) end; + getitem StrangeCoin, getmapusers("025-2-3"); + end; +} diff --git a/npc/025-2-4/_import.txt b/npc/025-2-4/_import.txt new file mode 100644 index 0000000..e9181f3 --- /dev/null +++ b/npc/025-2-4/_import.txt @@ -0,0 +1,6 @@ +// Map 025-2-4: Mana Tree Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/025-2-4/025-2-4_aegis_blackbox.txt", +"npc/025-2-4/_mobs.txt", +"npc/025-2-4/_warps.txt", +"npc/025-2-4/tree.txt", diff --git a/npc/025-2-4/_mobs.txt b/npc/025-2-4/_mobs.txt new file mode 100644 index 0000000..71942eb --- /dev/null +++ b/npc/025-2-4/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2-4: Mana Tree Cave mobs +025-2-4,40,28,13,3 monster Red Skull Slime 1404,3,20000,75000 +025-2-4,41,53,15,31 monster Crafty 1018,7,25000,35000 +025-2-4,36,35,9,7 monster Azul Slime Mother 1243,4,40000,10000 +025-2-4,42,39,8,12 monster Moubi 1038,5,300000,6000 +025-2-4,40,36,13,3 monster Yellow Skull Slime 1403,3,20000,75000 +025-2-4,44,30,9,7 monster Great Mouboo Slime 1247,3,80000,10000 diff --git a/npc/025-2-4/_warps.txt b/npc/025-2-4/_warps.txt new file mode 100644 index 0000000..a9c38f2 --- /dev/null +++ b/npc/025-2-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2-4: Mana Tree Cave warps +025-2-4,44,98,0 warp #025-2-4_44_98 0,0,025-2-2,42,45 diff --git a/npc/025-2-4/tree.txt b/npc/025-2-4/tree.txt new file mode 100644 index 0000000..4d22ee5 --- /dev/null +++ b/npc/025-2-4/tree.txt @@ -0,0 +1,77 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Pinkie Cave Tree is part of Legendary Weapons + +025-2-4,39,33,0 script Mana Tree NPC_MANATREE,{ + function manatreeOff; + function manatreeAgain; + function manatreeAegis; + + setpcblock(PCBLOCK_HARD, true); + mesc l("A tree glows in this dark cave, surrounded by mana lanes."); + next; + mesc l("This might be the place of a great secret, but yet, all you can do is stare."); + next; + if (getq(FortressQuest_ManaTree) < 2) manatreeOff(); + else if ($AEGIS_HOLDER$ == "" && @manacool < gettimetick(2)) manatreeAegis(); + else manatreeAgain(); + + setpcblock(PCBLOCK_HARD, false); + closeclientdialog; + close; + +function manatreeOff { + mesc l("...for now."); + next; + return; +} + +function manatreeAgain { + .@t = getq3(FortressQuest_ManaTree); + if (.@t > gettimeparam(GETTIME_DAYOFMONTH)) { + mesc l("...for now."); + next; + return; + } + // Can be obtained every 3 days + inventoryplace Manapple, 1; + mesc l("The tree generously offer you one of its fruits."); + mesc l("You gladly accept it. But probably better not disturb it for a few days."); + setq3 FortressQuest_ManaTree, gettimeparam(GETTIME_DAYOFMONTH)+3; + getitem Manapple, 1; + next; + return; +} + +function manatreeAegis { + mesc l("For which player do you wish to pray?"); + next; + input .@prayer$; + if (.@prayer$ == strcharinfo(0)) { + mesc l("The tree doesn't likes your selfishness."); + percentheal -20, -50; + return; + } + if (.@prayer$ == "") { + mesc l("...Alright then."); + return; + } + @manacool=gettimetick(2)+30; + .@cid=getcharid(3, .@prayer$); + if (.@cid < 1) { + mesc l("Did you just make up that someone?"); + return; + } + 02524_Tree_BlackBox(.@prayer$, .@cid); + @manacool+=150; + return; +} + +OnInit: + .sex = G_OTHER; + .distance = 2; + end; +} + diff --git a/npc/025-2/_import.txt b/npc/025-2/_import.txt new file mode 100644 index 0000000..f3af810 --- /dev/null +++ b/npc/025-2/_import.txt @@ -0,0 +1,6 @@ +// Map 025-2: Fortress Island - South +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/025-2/_mobs.txt", +"npc/025-2/_warps.txt", +"npc/025-2/guard.txt", +"npc/025-2/main.txt", diff --git a/npc/025-2/_mobs.txt b/npc/025-2/_mobs.txt new file mode 100644 index 0000000..1df771b --- /dev/null +++ b/npc/025-2/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2: Fortress Island - South mobs +025-2,75,100,49,13 monster Forain 1061,12,60000,60000 +025-2,112,102,66,18 monster Moonshroom 1069,12,60000,60000 +025-2,68,63,48,21 monster Black Mamba 1174,14,60000,60000 +025-2,129,63,48,21 monster Gobo Bear 1214,14,60000,60000 +025-2,139,31,40,11 monster Vanity Pixie 1215,4,60000,60000 +025-2,66,31,35,12 monster Nightmare Dragon 1230,4,60000,60000 diff --git a/npc/025-2/_warps.txt b/npc/025-2/_warps.txt new file mode 100644 index 0000000..105619b --- /dev/null +++ b/npc/025-2/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-2: Fortress Island - South warps +025-2,25,42,0 warp #025-2_25_42 0,0,025-2-2,37,40 +025-2,158,98,0 warp #025-2_158_98 0,0,025-2-1,29,106 +025-2,148,85,0 warp #025-2_148_85 0,0,025-2-1,23,66 diff --git a/npc/025-2/guard.txt b/npc/025-2/guard.txt new file mode 100644 index 0000000..57a742c --- /dev/null +++ b/npc/025-2/guard.txt @@ -0,0 +1,73 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Notes: +// Generic Guard from the Alliance + +025-2,108,23,0 script Alliance Guard NPC_BRGUARD_BOW,{ + .@q=getq(General_Narrator); + if (.@q >= 21) + goto L_ShortSummary; + inventoryplace PurificationPotion, 3, ElixirOfLife, 1; + setpcblock(PCBLOCK_HARD, true); + mesc l("STORY MODE ENABLED. Monsters won't attack you, so you can read without worries."), 1; + next; + mesn; + mesq l("Hey %s, I was informed about your arrival. You are here, good!", strcharinfo(0)); + next; + mesn; + mesq l("You see the gates over there? This is the World's Edge. The land which never had a settlement. How. is. there. a. town?!"); + next; + mesn; + mesq l("Not only that, but it is swarming with monsters. Something is really wrong here. From our scouts which went in there, only one returned."); + next; + mesn; + mesq l("So, what to expect? Past this gate is a panthom town and some steel grating. Past the grating is the actual Impregnable Fortress."); + next; + mesn; + mesq l("We found several interest points inside, like some mines with the most precious ores in the world - like %s and even %s - along very powerful monsters. Unfortunately, it is partly flooded.", getitemlink(PlatinumOre), getitemlink(MylarinDust)); + next; + mesn; + mesq l("We also found a small fortress and a small gothic building which might have important clues on your journey. Our first task, therefore, should be recapturing this town from our enemies!"); + next; + mesn; + mesq l("Once that is done, the Alliance's staff will set several stands with the most important services you might need, like banking, inside the town central area."); + next; + mesn; + mesq l("Monsters, however, keep coming from the Impregnable Fortress. They always come on %s, although we have no idea why.", b(l("Tuesdays"))); + next; + mesn; + mesq l("So, %s, I know you have other priorities. I don't even know why you are here - for riches? For glory, for fame? To save the world? To find out about your past? To talk with me because, well, I am just *that* cool? All of the above? Perhaps something else?", strcharinfo(0)); + next; + mesn; + mesq l("But we cannot ignore the threat this imposes to us. You might not know our world's history, but this place should never be inhabited or else..."); + next; + mesn; + .@find$=l("Please find %s - we will need the current world's hero to conduct the siege on the Fortress Town.", $MOST_HEROIC$); + .@sieg$=l("You are our hero- Please assemble a team of adventurers and raid the Fortress Town."); + mesq l("%s But please be quick! I feel this world doesn't have much time left!", (strcharinfo(0) == $MOST_HEROIC$ ? .@sieg$ : .@find$)); + next; + mesn; + mesq l("Here, take this %s and these %s and good luck!", getitemlink(ElixirOfLife), getitemlink(PurificationPotion)); + next; + mesc b(l(".:: Main Quest ::.")), 3; + msObjective($FORTRESS_STATE, l("* Invade the Fortress Town")); + msObjective(false, l("* Find clues")); + msObjective(false, l("* (optional) Save the world!")); + next; + setpcblock(PCBLOCK_HARD, false); + setq General_Narrator, 21; + getitem PurificationPotion, 3; + getitem ElixirOfLife, 1; + closeclientdialog; + legiontalk(); + close; + +L_ShortSummary: + .@open$=l("We have set some stalls which should be useful if you plan into raiding the Impregnable Fortress."); + .@lock$=l("It is locked but %s should be able to coordinate a raid on it.", $MOST_HEROIC$); + mesn; + mesq l("Past this point is the Fortress Island. %s Even so, be careful, the town should not exist.", ($FORTRESS_STATE ? .@open$ : .@lock$)); + close; +} + diff --git a/npc/025-2/main.txt b/npc/025-2/main.txt new file mode 100644 index 0000000..1006e04 --- /dev/null +++ b/npc/025-2/main.txt @@ -0,0 +1,31 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Notes: +// Fortress Island South (Lv 70~150 Area) + +025-2 mapflag mask 5 + +025-2,97,122,0 script Airship NPC_AIRSHIP,{ + mesc l(".:: Fortress Island ::."), 1; + mes ""; + mesc l("Do you want to return to Land Of Fire Village?"), 1; + next; + if (askyesno() == ASK_YES) { + specialeffect FX_CIRCLE, SELF, getcharid(3); + closeclientdialog; + sleep2(5000); + removespecialeffect(FX_CIRCLE, SELF, getcharid(3)); + if (ispcdead()) end; + warp "017-10", 64, 25; + } + close; + +OnInit: + .sex = G_OTHER; + .distance = 8; + end; + +} + + diff --git a/npc/025-3/_import.txt b/npc/025-3/_import.txt new file mode 100644 index 0000000..9079e5e --- /dev/null +++ b/npc/025-3/_import.txt @@ -0,0 +1,5 @@ +// Map 025-3: The Impregnable Fortress (Outside) +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/025-3/_mobs.txt", +"npc/025-3/_warps.txt", +"npc/025-3/ctrl.c", diff --git a/npc/025-3/_mobs.txt b/npc/025-3/_mobs.txt new file mode 100644 index 0000000..a7e8cf9 --- /dev/null +++ b/npc/025-3/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-3: The Impregnable Fortress (Outside) mobs +025-3,99,157,75,17 monster Moubi 1038,16,120000,30000 +025-3,105,117,10,21 monster Angry Bat 1194,24,500,500,Impregnable#0F::OnMoubiKill +025-3,98,141,71,16 monster Bloody Mouboo 1119,6,500,500 +025-3,99,117,75,18 monster Crafty 1018,1,500,500,Impregnable#0F::OnMoubiKill diff --git a/npc/025-3/_warps.txt b/npc/025-3/_warps.txt new file mode 100644 index 0000000..7bfc117 --- /dev/null +++ b/npc/025-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 025-3: The Impregnable Fortress (Outside) warps +025-3,100,179,0 warp #025-3_100_179 5,0,025-1,100,22 diff --git a/npc/025-3/ctrl.c b/npc/025-3/ctrl.c new file mode 100644 index 0000000..1cfcf75 --- /dev/null +++ b/npc/025-3/ctrl.c @@ -0,0 +1,61 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// The Impregnable Fortress Control Files +// Quest: General_Fortress +// (MaxFloor+1, internal, internal) + +025-3,105,83,0 script Impregnable#0F NPC_HIDDEN,2,0,{ + end; + +OnTouch: + if (getq(General_Fortress)) goto L_Warp; + mes l(".:: The Impregnable Fortress ::."); + mes ""; + mes l("The fortress goes below until the Monster King Throne is established."); + mes l("To descend you must finish the task on the floor. Sometimes it'll be outspoken, other times, it'll not."); + mes l("The tasks may vary from collecting certain items, walking in a specific order, lighting candles, killing a specific monster or all monsters in the floor, et cetera."); + next; + mes l("The tasks are individual, but help from other players is acceptable in most cases."); + mesc l("(Do note that there may be filters to decide when the player helped you and when the player did the quest for you. In some cases, you'll need to redo the stage.)"); + next; + mes l("After finishing the task, the floor below will be liberated for your access only. Other players will need to complete their tasks."); + mes l("Due to the sheer amount of monsters in the army, most floors are not immediately available! The Monster Army must be weakened before accessing the deeper floors by repeatedly defeating them at Fortress Town Siege."); + next; + mesc l(".:: Impregnable Fortress, %sF ::.", "0"), 3; + msObjective(getq(General_Fortress) == 1, l("* Obtain the Fortress Key")); + msObjective($MK_TEMPVAR < MKIF_LV_B0F, l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_B0F)); + mes ""; + mesc l("Hint: Eek! Eek! Eek!"); + tutmes l("Explore all map elements (including monsters) with the hint in mind to fulfill the mission objectives. They'll never demand something outside the mission map!"); + end; + +L_Warp: + // Not unlocked + if ($GAME_STORYLINE >= 3 && $MK_TEMPVAR < MKIF_LV_B0F) { + mesc l("The gate is sealed shut."), 1; + mesc l("The monster army is still strong on this floor!"), 1; + mesc l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_B0F), 1; + close; + } + warp "026-0", 64, 98; + end; + +OnMoubiKill: + .@q=getq(General_Fortress); + .@q2=getq2(General_Fortress); + .@q3=getq3(General_Fortress); + if (.@q != 0) end; + + setq2 General_Fortress, .@q2+1; + if (rand2(10000) <= .@q2) { + dispbottom b(l("You have found the Impregnable Fortress Key. Access to B0F granted.")); + setq General_Fortress, 1, 0, 0; + getexp 0, 50000; + } + fix_mobkill(AngryBat); + end; +} + + diff --git a/npc/026-0/_import.txt b/npc/026-0/_import.txt new file mode 100644 index 0000000..0b9022b --- /dev/null +++ b/npc/026-0/_import.txt @@ -0,0 +1,5 @@ +// Map 026-0: The Impregnable Fortress (B0F) +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/026-0/_mobs.txt", +"npc/026-0/_warps.txt", +"npc/026-0/ctrl.c", diff --git a/npc/026-0/_mobs.txt b/npc/026-0/_mobs.txt new file mode 100644 index 0000000..9a4582c --- /dev/null +++ b/npc/026-0/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 026-0: The Impregnable Fortress (B0F) mobs +026-0,39,62,12,37 monster Black Skull Slime 1408,16,60000,30000 +026-0,89,62,10,37 monster Lava Skull Slime 1407,20,60000,30000 +026-0,65,77,13,22 monster Whirly Bird 1232,6,60000,35000 +026-0,65,31,13,6 monster Reaper 1196,4,60000,40000 diff --git a/npc/026-0/_warps.txt b/npc/026-0/_warps.txt new file mode 100644 index 0000000..19eaa3b --- /dev/null +++ b/npc/026-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 026-0: The Impregnable Fortress (B0F) warps +026-0,64,99,0 warp #026-0_64_99 2,0,025-3,105,84 diff --git a/npc/026-0/ctrl.c b/npc/026-0/ctrl.c new file mode 100644 index 0000000..c201c26 --- /dev/null +++ b/npc/026-0/ctrl.c @@ -0,0 +1,105 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// The Impregnable Fortress Control Files +// Quest: General_Fortress +// (MaxFloor+1, internal, internal) + +026-0,64,20,0 script Impregnable#B0F NPC_HIDDEN,4,0,{ + end; + +OnTouch: + if (getq(General_Fortress) > 1) goto L_Warp; + mesc l(".:: Impregnable Fortress, %sF ::.", "B0"), 3; + msObjective(getq(General_Fortress) == 2, l("* Solo \"The Yetifly\"")); + msObjective($MK_TEMPVAR < MKIF_LV_B0F, l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_B1F)); + mes ""; + mesc l("Hint: Stomp! Stomp! Stomp! Walk around."); + end; + +L_Warp: + // Not unlocked + if ($GAME_STORYLINE >= 3 && $MK_TEMPVAR < MKIF_LV_B1F) { + mesc l("The gate is sealed shut."), 1; + mesc l("The monster army is still strong on this floor!"), 1; + mesc l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_B1F), 1; + close; + } + warp "026-1", 29, 94; + end; + +} + +026-0,40,52,0 script Impregnable#B0F_1 NPC_HIDDEN,0,0,{ + end; +OnTouch: + .@q=getq(General_Fortress); + .@q2=getq2(General_Fortress); + .@q3=getq3(General_Fortress); + if (.@q != 1) end; + + .@n$=strnpcinfo(0, "_0"); + explode(.@ni$, .@n$, "_"); + .@id=atoi(.@ni$[1]); + if (.@id <= 0) Exception("Unparseable switch: "+.@n$, RB_DEFAULT|RB_ISFATAL); + + if (!(.@q2 & .@id)) { + setq2 General_Fortress, .@q2 | .@id; + } + end; +} + +026-0,45,48,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_2 NPC_HIDDEN,0,0 +026-0,90,42,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_4 NPC_HIDDEN,0,0 +026-0,83,55,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_8 NPC_HIDDEN,0,0 +026-0,82,64,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_16 NPC_HIDDEN,0,0 +026-0,65,58,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_32 NPC_HIDDEN,0,0 +026-0,64,60,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_64 NPC_HIDDEN,0,0 +026-0,43,76,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_128 NPC_HIDDEN,0,0 +026-0,46,77,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_256 NPC_HIDDEN,0,0 +026-0,70,87,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_512 NPC_HIDDEN,0,0 +026-0,84,84,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_1024 NPC_HIDDEN,0,0 +026-0,84,86,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_2048 NPC_HIDDEN,0,0 +026-0,85,86,0 duplicate(Impregnable#B0F_1) Impregnable#B0F_4096 NPC_HIDDEN,0,0 + + +026-0,99,41,0 script Impregnable#B0F_X NPC_HIDDEN,0,0,{ + end; +OnTouch: + .@q=getq(General_Fortress); + .@q2=getq2(General_Fortress); + .@q3=getq3(General_Fortress); + if (.@q != 1) end; + if (.@q2 < 8191) end; + + if (getareausers("026-0", 54, 39, 77, 53) > 0) end; + if (mobcount("026-0", "Impregnable#B0F_X::OnYetifly") > 0) + killmonster("026-0", "Impregnable#B0F_X::OnYetifly", false); + + slide 72, 49; + monster("026-0", 57, 44, l("The Yetifly"), Yetifly, 1, "Impregnable#B0F_X::OnYetifly"); + addtimer2(15000, "Impregnable#B0F_X::OnHeartbeat"); + end; + +OnHeartbeat: + if (getmap() != "026-0") end; + if (ispcdead()) + warp "025-1", 100, 83; + addtimer2(15000, "Impregnable#B0F_X::OnHeartbeat"); + end; + +OnYetifly: + .@q=getq(General_Fortress); + .@q2=getq2(General_Fortress); + .@q3=getq3(General_Fortress); + if (.@q != 1) end; + if (.@q2 < 8191) end; + + dispbottom b(l("You have defeated the Yetifly. Access to B1F granted.")); + setq General_Fortress, 2, 0; + Zeny+=100000; + slide 98, 41; + end; +} + diff --git a/npc/026-1/_config.txt b/npc/026-1/_config.txt new file mode 100644 index 0000000..f583eb2 --- /dev/null +++ b/npc/026-1/_config.txt @@ -0,0 +1,71 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 026-1: The Impregnable Fortress (B1F) conf + +026-1,58,76,0 script #026-1_58_76 NPC_HIDDEN,{ + end; +OnDisable: + delcells "026-1_58_76"; end; +OnEnable: +OnInit: + setcells "026-1", 58, 76, 62, 76, 1, "026-1_58_76"; +} + +026-1,25,32,0 script #026-1_25_32 NPC_HIDDEN,{ + end; +OnDisable: + delcells "026-1_25_32"; end; +OnEnable: +OnInit: + setcells "026-1", 25, 32, 25, 32, 1, "026-1_25_32"; +} + +026-1,30,32,0 script #026-1_30_32 NPC_HIDDEN,{ + end; +OnDisable: + delcells "026-1_30_32"; end; +OnEnable: +OnInit: + setcells "026-1", 30, 32, 30, 32, 1, "026-1_30_32"; +} + +026-1,24,28,0 script #026-1_24_28 NPC_SWITCH_ONLINE,{ + if (getnpcclass() == NPC_SWITCH_OFFLINE) + end; + callfunc "0261_Flip30"; + doevent "#026-1_30_32::OnDisable"; + setnpcdisplay "#026-1_24_28", NPC_SWITCH_OFFLINE; + end; +OnInit: + .distance=2; +} + +026-1,62,28,0 script #026-1_62_28 NPC_SWITCH_ONLINE,{ + if (getnpcclass() == NPC_SWITCH_OFFLINE) + end; + callfunc "0261_Flip25"; + doevent "#026-1_25_32::OnDisable"; + setnpcdisplay "#026-1_62_28", NPC_SWITCH_OFFLINE; + end; +OnInit: + .distance=2; +} + +026-1,30,28,0 script #026-1_30_28 NPC_CHEST,{ + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=2; + end; +} + +026-1,26,28,0 script #026-1_26_28 NPC_SWITCH_ONLINE,{ + if (getnpcclass() == NPC_SWITCH_OFFLINE) + end; + callfunc "0261_GateChange"; + doevent "#026-1_58_76::OnDisable"; + setnpcdisplay "#026-1_26_28", NPC_SWITCH_OFFLINE; + end; +OnInit: + .distance=2; +} diff --git a/npc/026-1/_import.txt b/npc/026-1/_import.txt new file mode 100644 index 0000000..3629258 --- /dev/null +++ b/npc/026-1/_import.txt @@ -0,0 +1,6 @@ +// Map 026-1: The Impregnable Fortress (B1F) +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/026-1/_config.txt", +"npc/026-1/_mobs.txt", +"npc/026-1/_warps.txt", +"npc/026-1/ctrl.c", diff --git a/npc/026-1/_mobs.txt b/npc/026-1/_mobs.txt new file mode 100644 index 0000000..9868cd3 --- /dev/null +++ b/npc/026-1/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 026-1: The Impregnable Fortress (B1F) mobs +026-1,39,59,16,10 monster Snail 1426,8,60000,40000 +026-1,79,59,16,10 monster Pinkie Suseran 1419,6,60000,40000 +026-1,39,84,16,10 monster Black Skull Slime 1408,8,60000,40000 +026-1,79,84,16,10 monster Snail 1426,8,60000,40000 +026-1,79,34,16,10 monster Mandragora 1423,5,60000,40000 +026-1,39,34,16,10 monster Jhon Longnose 1421,5,60000,40000 +026-1,59,59,2,35 monster Nightmare Dragon 1230,6,60000,40000 +026-1,29,27,7,4 monster Evil Scythe 1036,8,100,900000 diff --git a/npc/026-1/_warps.txt b/npc/026-1/_warps.txt new file mode 100644 index 0000000..684c6d4 --- /dev/null +++ b/npc/026-1/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 026-1: The Impregnable Fortress (B1F) warps +026-1,29,95,0 warp #026-1_29_95 0,0,026-0,65,21 diff --git a/npc/026-1/ctrl.c b/npc/026-1/ctrl.c new file mode 100644 index 0000000..26bb478 --- /dev/null +++ b/npc/026-1/ctrl.c @@ -0,0 +1,186 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// The Impregnable Fortress Control Files +// Quest: General_Fortress +// (MaxFloor+1, internal, internal) + +026-1,60,26,0 script Impregnable#B1F NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (getq(General_Fortress) > 1) goto L_Warp; + mesc l(".:: Impregnable Fortress, %sF ::.", "B1"), 3; + msObjective(getq(General_Fortress) == 2, l("* Obtain clearance")); + msObjective($MK_TEMPVAR < MKIF_LV_B1F, l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_B2F)); + mes ""; + mesc l("Hint: You might need to come back later."); + end; + +L_Warp: + // Not unlocked + if ($GAME_STORYLINE >= 3 && $MK_TEMPVAR < MKIF_LV_B2F) { + mesc l("The gate is sealed shut."), 1; + mesc l("The monster army is still strong on this floor!"), 1; + mesc l("Minimum wins: %d/%d", $MK_TEMPVAR, MKIF_LV_B2F), 1; + close; + } + //warp "026-2", X, Y; + dispbottom l("Coming Soon, in Moubootaur Legends!"); + end; + +// Disarm & reset everything +OnMinute15: +OnMinute45: + if ($@DATA_0261[12]) + setcells "026-1", 58, 76, 62, 76, 1, "026-1_58_76"; + if ($@DATA_0261[13]) + setcells "026-1", 25, 32, 25, 32, 1, "026-1_25_32"; + if ($@DATA_0261[14]) + setcells "026-1", 30, 32, 30, 32, 1, "026-1_30_32"; + setnpcdisplay "#026-1_24_28", NPC_SWITCH_ONLINE; + setnpcdisplay "#026-1_26_28", NPC_SWITCH_ONLINE; + setnpcdisplay "#026-1_62_28", NPC_SWITCH_ONLINE; + callfunc "0261_CheckReset"; + end; +} + +// Other misc controllers +function script 0261_GateChange { + //if (Sp < MaxSp) end; + if (Hp < MaxHp) {die(); end;} + $@DATA_0261[12] = true; + percentheal -99, -100; + dispbottom "*snap* - Oh noes, there was a trap!"; + return; +} + +function script 0261_Flip25 { + $@DATA_0261[13] = true; + return; +} + +function script 0261_Flip30 { + $@DATA_0261[14] = true; + return; +} + +function script 0261_CheckReset { + if ($@DATA_0261[0] == 1 && + $@DATA_0261[1] == 1 && + $@DATA_0261[2] == 1 && + $@DATA_0261[3] == 1 && + $@DATA_0261[4] == 1 && + $@DATA_0261[5] == 1 && + $@DATA_0261[6] == 1 && + $@DATA_0261[7] == 1 && + $@DATA_0261[8] == 1 && + $@DATA_0261[9] == 1 && + $@DATA_0261[10] == 1 && + $@DATA_0261[11] == 1) + .@r=true; + if ($@DATA_0261[0]) + delcells "026-1_D0"; + if ($@DATA_0261[1]) + delcells "026-1_D1"; + if ($@DATA_0261[2]) + delcells "026-1_D2"; + if ($@DATA_0261[3]) + delcells "026-1_D3"; + if ($@DATA_0261[4]) + delcells "026-1_D4"; + if ($@DATA_0261[5]) + delcells "026-1_D5"; + if ($@DATA_0261[6]) + delcells "026-1_D6"; + if ($@DATA_0261[7]) + delcells "026-1_D7"; + if ($@DATA_0261[8]) + delcells "026-1_D8"; + if ($@DATA_0261[9]) + delcells "026-1_D9"; + if ($@DATA_0261[10]) + delcells "026-1_D10"; + if ($@DATA_0261[11]) + delcells "026-1_D11"; + deletearray $@DATA_0261; + if (.@r) { + $@DATA_0261[12] = true; + delcells "026-1_58_76"; + } + return .@r; +} + +026-1,29,76,0 script Important Note#B1F NPC_NO_SPRITE,{ + function noteBegin; + if ($@DATA_0261[12]) end; + if (getq2(General_Fortress) == 0) noteBegin(); + mesc l("Flip the manaplace?"), 1; + next; + if (askyesno() == ASK_YES) { + .@r=0261_CheckReset(); + if (!.@r) + warp "003-1", 82, 119; + else + setq General_Fortress, 2, 0, 0; + } + close; + +function noteBegin { + mes l("To the monster army:"); + mes ""; + mes l("There has been a lot of movement here as of late."); + mes l("Your Highness does not like it, and you're dirtying the floor faster than staff can clean."); + mes ""; + mes l("Therefore, we have restricted access to and from B0F/B2F."); + mes l("If you could not attend to the meeting (may the king forgive your soul), just restore the objects to their proper position and flip the switch on this manaplace."); + mes ""; + mes l("Be careful to don't flip the manaplace before all objects are ordered correctly."); + mes ""; + mes l("-- The Keeper"); + next; + setq2 General_Fortress, 1; + return; +} + +OnInit: + .distance=3; + end; +} + +// Flip flop +026-1,26,89,0 script #0261_FF@0 NPC_NO_SPRITE,{ + explode(.@ni$, .name$, "@"); + .@x=atoi(.@ni$[1]); + .@v$=sprintf("$@DATA_0261[%d]", .@x); + .@i=getd(.@v$); + if (.@i == 0) { + setd(.@v$, 1); + setcells "026-1", .x-1, .y, .x+1, .y, 2, "026-1_D"+.@x; + } else if (.@i == 1) { + setd(.@v$, 2); + delcells "026-1_D"+.@x; + setcells "026-1", .x-1, .y, .x+1, .y, 3, "026-1_D"+.@x; + } else { + setd(.@v$, 0); + delcells "026-1_D"+.@x; + } + end; +OnInit: + .distance=2; + end; +} + +026-1,32,89,0 duplicate(#0261_FF@0) #0261_FF@1 NPC_NO_SPRITE +026-1,26,86,0 duplicate(#0261_FF@0) #0261_FF@2 NPC_NO_SPRITE +026-1,32,86,0 duplicate(#0261_FF@0) #0261_FF@3 NPC_NO_SPRITE +026-1,71,76,0 duplicate(#0261_FF@0) #0261_FF@4 NPC_NO_SPRITE +026-1,71,81,0 duplicate(#0261_FF@0) #0261_FF@5 NPC_NO_SPRITE +026-1,78,76,0 duplicate(#0261_FF@0) #0261_FF@6 NPC_NO_SPRITE +026-1,78,81,0 duplicate(#0261_FF@0) #0261_FF@7 NPC_NO_SPRITE +026-1,85,76,0 duplicate(#0261_FF@0) #0261_FF@8 NPC_NO_SPRITE +026-1,85,81,0 duplicate(#0261_FF@0) #0261_FF@9 NPC_NO_SPRITE +026-1,92,76,0 duplicate(#0261_FF@0) #0261_FF@10 NPC_NO_SPRITE +026-1,92,81,0 duplicate(#0261_FF@0) #0261_FF@11 NPC_NO_SPRITE + diff --git a/npc/026-2/_import.txt b/npc/026-2/_import.txt new file mode 100644 index 0000000..9240be1 --- /dev/null +++ b/npc/026-2/_import.txt @@ -0,0 +1,3 @@ +// Map 026-2: The Impregnable Fortress (B2F) +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/026-2/_warps.txt", diff --git a/npc/026-2/_warps.txt b/npc/026-2/_warps.txt new file mode 100644 index 0000000..a80657f --- /dev/null +++ b/npc/026-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 026-2: The Impregnable Fortress (B2F) warps +026-2,32,98,0 warp #026-2_32_98 0,0,026-1,60,27 diff --git a/npc/027-0/_import.txt b/npc/027-0/_import.txt new file mode 100644 index 0000000..3a8099a --- /dev/null +++ b/npc/027-0/_import.txt @@ -0,0 +1,5 @@ +// Map 027-0: Administration Building +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/027-0/_warps.txt", +"npc/027-0/emma.txt", +"npc/027-0/enrique.txt", diff --git a/npc/027-0/_warps.txt b/npc/027-0/_warps.txt new file mode 100644 index 0000000..aafd47b --- /dev/null +++ b/npc/027-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-0: Administration Building warps +027-0,65,36,0 warp #027-0_65_36 2,0,027-1,90,40 diff --git a/npc/027-0/emma.txt b/npc/027-0/emma.txt new file mode 100644 index 0000000..a44aa57 --- /dev/null +++ b/npc/027-0/emma.txt @@ -0,0 +1,121 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Emma is Enrique's wife + +027-0,88,26,2 script Emma NPC_DARK_SORCERER_F,{ + mesn; + mesq l("Hello. I am Emma, Enrique's wife."); + mesc l("Your current scholar rank: %s (%s Research Points)", + academicrank(), fnum(MAGIC_RP)); + if (!MAGIC_LVL) + close; + next; + mesn; + mesq l("I'm currently doing tutorship for students who are... falling behind %%p"); + next; + mesn; + mesq l("Do you need the extra credit?"); + mesc l("Tutorship will give you Research Points."), 1; + mesc l("You should not leave this room."), 1; + mesc l("Tutorship is NOT the most effective way."), 1; + next; + mesq l("Do you need the extra credit?"); + mesc l("Cost: %d GP/min", .cost); + mesc l("Gain: %d RP/min", .gain); + if (@emmapro) + mesc l("Warning: If you are taking an advanced class, it will canceled."), 1; + menuint + l("I'm fine, thanks"), 0, + l("5 minutes"), 5, + l("10 minutes"), 10, + l("15 minutes"), 15, + l("30 minutes"), 30, + l("1 hour"), 60, + l("2 hours"), 120, + rif(abizit() == 5 && MAGIC_LVL > 3, l("Show me something more advanced!")), -1; + mes ""; + if (@menuret < 0) { + goto L_Advanced; + } + if (!@menuret) { + mesn; + mesq l("Then I hope you can keep your grades high, hmm hmm!"); + close; + } + if (Zeny < .cost*@menuret) { + mesn; + mesq l("Ara ara? You do not have enough money with you!"); + close; + } + Zeny-=.cost*@menuret; + addtimer2(60000, .name$+"::OnTick"); + @emmatick=max(@emmatick, gettimetick(2)) + @menuret*60; + @emmapro=false; + mesn; + mesq l("Hmm hmm! Then, let's begin."); + mesc l("Note: You can increase the time by talking to Emma again."); + close; + +L_Advanced: + mesn; + mesq l("I actually have a special class for those with perfect magical control like you."); + next; + mesq l("Do you need the extra credit?"); + mesc l("Cost: %d GP/min", .cost*15); + mesc l("Gain: %d RP/min", .gain*10); + menuint + l("I've changed my mind."), 0, + l("5 minutes"), 5, + l("10 minutes"), 10, + l("15 minutes"), 15, + l("30 minutes"), 30, + l("1 hour"), 60, + l("2 hours"), 120; + mes ""; + if (!@menuret) { + mesn; + mesq l("Then I hope you can keep your grades high, hmm hmm!"); + close; + } + if (Zeny < .cost*@menuret*15) { + mesn; + mesq l("Ara ara? You do not have enough money with you!"); + close; + } + Zeny-=.cost*@menuret*15; + addtimer2(60000, .name$+"::OnTick"); + if (!@emmapro) + @emmatick=0; + @emmatick=max(@emmatick, gettimetick(2)) + @menuret*60; + @emmapro=true; + mesn; + mesq l("Hmm hmm! Then, let's begin."); + close; + +OnTick: + // Time out + if (@emmatick < gettimetick(2)) { + npctalk3 l("Enough for now. Let's rest, shall we?"); + @emmapro = 0; + end; + } + // Changed maps + if (getmap() != "027-0") + end; + + .@up=.gain*(@emmapro ? 10 : 1); + MAGIC_RP+=.@up; + dispbottom l("Research Points +%d", .@up); + addtimer2(60000, .name$+"::OnTick"); + end; + +OnInit: + .distance = 4; + .sex = G_FEMALE; + .cost = 4500; + .gain = 15; + end; +} + diff --git a/npc/027-0/enrique.txt b/npc/027-0/enrique.txt new file mode 100644 index 0000000..11bacac --- /dev/null +++ b/npc/027-0/enrique.txt @@ -0,0 +1,181 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Enrique is the headmaster of the Academy + +027-0,46,25,0 script Enrique NPC_BLACKALCHEMIST,{ + mesn; + mesq l("Hello. I am Enrique, headmaster of The Academy."); + mesc l("Your current scholar rank: %s (%s Research Points)", + academicrank(), fnum(MAGIC_RP)); + if (!MAGIC_LVL) + close; + do + { + next; + select + l("Thanks!"), + l("What is the Academy?"), + l("What are Magic Skill Points?"), + l("What are Research Points"), + l("What is the Scholar Rank?"), + l("Who are the most famous scholars?"), + rif(!ACADEMIC_RANK, l("I would like to enroll!")), + l("What counts for titulation? Could you give me examples?"), + rif(getskilllv(TMW2_STUDY) < ACADEMIC_RANK, l("I would like to learn a new skill!")); + mes ""; + switch (@menu) { + case 1: + goodbye(); + break; + case 2: + mesn; + mesq l("The Academy Island is situated east of Land of Fire Village."); + next; + mesn; + mesq l("It was built by The Alliance, and can be accessed by a ship in Tulimshar."); + next; + mesn; + mesq l("As you may be aware, the Magic Council is in Tulim, after all."); + next; + mesn; + mesq l("Here you can find professors, academics and researchers. Feel free to enroll in any class as well."); + break; + case 3: + mesn; + mesq l("Simply put, they are raw power."); + next; + mesn; + mesq l("There is only so much magical power your body can handle without breaking."); + next; + mesn; + mesq l("By \"breaking\", I do not mean death. It is something worse."); + next; + mesn; + mesq l("How to expand this limit? Well, you'll need to touch a Mana Stone. If you can handle more raw power, your limit will extend."); + next; + mesn; + mesq l("It also increases naturally as you grow, albeit less."); + next; + mesn; + mesq l("Keep in mind, we at the Academy do not have a Mana Stone."); + break; + case 4: + mesn; + mesq l("Ah, research points (RP). They are knowledge which can be used in honing your skills."); + next; + mesn; + mesq l("You will need a certain amount of them to upgrade any skill, which will be spent and no longer available for use."); + next; + mesn; + mesq l("But the more you use a certain skill, the less research points will be required to upgrade it. And that bonus is permanent."); + next; + mesn; + mesq l("As to how to obtain research points... Randomly when casting, by enrolling in a class with my wife Emma, by studying monsters, and randomly but very rarely with the Professor in Tulimshar."); + break; + case 5: + mesn; + mesq l("Those with most %s on this world becomes scholars.", "[@@https://wiki.moubootaurlegends.org/|"+l("research")+"@@]"); + next; + mesn; + mesq l("Scholars dedicate part of their time so others can learn about the world, and as such, they are always welcome to this Academy."); + next; + mesn; + mesq l("If they enroll, they will become able to study monsters and to visit the Mystical Forest west of here as well."); + next; + mesn; + mesq l("Of course, some get scholarship in honor of the cause or by notable knowledge and prowess; These are a special group not worth mentioning."); + break; + case 6: + HallOfAcademic(); + break; + case 7: + mesn; + mesq l("That is not how things are done here."); + next; + mesn; + mesq l("First, you need to contribute to the common knowledge of this world. Like, write a %s, guide new players or something.", "[@@https://wiki.moubootaurlegends.org/|"+l("Wiki article")+"@@]"); + next; + mesn; + mesq l("Then someone of a scholarship rank above your must approve it, and say that your work is enough to the next scholar rank."); + next; + mesn; + mesq l("Naturally, it doesn't have to be a wiki article. But it must be noteworthy to the whole community, and wiki articles are one of the easiest ways for that."); + next; + mesn; + mesq l("For the higher titles, you'll need vouch of multiple people. You can vouch for anyone below your rank with %s but never for someone of same or higher rank.", b("@titulate")); + next; + mesn; + mesq l("Do note that abuse will cause both you and the person you vouched for to be kicked from the Academy! So be mindful with to whom you give titles."); + next; + mesn; + mesq l("Abuse can be determined by your peers or by the grandmasters, so be careful."); + next; + mesn; + mesq l("Otherwise, you can also obtain a title during the Magic Olympics. They happen every three months in average, and are the hardest way to obtain a title."); + break; + case 8: + mesn; + mesq l("Oh, several things. It depends a bit on what the scholars consider, and the scholars... well, they change."); + next; + mesn; + mesq l("For example, translations do not count. Wiki articles count."); + next; + mesn; + mesq l("Writing/Updating in client-data quests.xml and items.xml with the correct information usually counts as well."); + next; + mesn; + mesq l("But pretty much, anything which contributes to the common knowledge of this world. It might even be writing a NPC telling about the world history, developing software for Wiki/GameInfo/etc., it all depends on the scholars."); + break; + case 9: + mesn; + mesq l("The study skill allows you to see the exact health a monster have, as well some details."); + next; + mesn; + mesq l("And if you study them well, you'll also gain more Research Points than you would get with other skills."); + next; + mesn; + mesq l("Think on it like a... perk, for having helped the adventurers in this world. I just need to check if you are eligible, hold still."); + next; + mesn; + mesc l("%s takes a paper from his drawer. It has your photo on it.", .name$); + next; + mesn; + mesq l("%s, %s in Magic Arts, Parity Level %d, tier %d mage.", strcharinfo(0), academicrank(), REBIRTH*100+BaseLevel, MAGIC_LVL); + next; + if (MAGIC_LVL < (1+getskilllv(TMW2_STUDY))) { + mesn; + mesq l("The skill would be wasted on a noob mage like you. Go get more magic power first!"); + break; + } + if (REBIRTH*100+BaseLevel < (1+getskilllv(TMW2_STUDY))*50) { + mesn; + mesq l("The skill would be wasted on a noob like you. You are weak. Go gain a few levels and then return here."); + break; + } + // TODO: Need X skills learnt or 1M GP. + .@price = (1+getskilllv(TMW2_STUDY)) * 1000; + mesn; + mesq l("Teaching you how to study will take from my time, so I'll want a small compromise of %s GP to make sure you will actually make good use of it.", fnum(.@price)); + if (Zeny < .@price) + break; + next; + mesc l("Pay the tuition fee?"), 1; + if (askyesno() == ASK_YES) { + Zeny-=.@price; + skill TMW2_STUDY, getskilllv(TMW2_STUDY)+1, 0; + mesn; + mesq l("Use its powers for good!"); + } + break; + } + } while (@menu != 1); + close; + +OnInit: + .distance = 4; + .sex = G_MALE; + end; +} + diff --git a/npc/027-1/_import.txt b/npc/027-1/_import.txt new file mode 100644 index 0000000..e64a633 --- /dev/null +++ b/npc/027-1/_import.txt @@ -0,0 +1,14 @@ +// Map 027-1: The Academy Island +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/027-1/_mobs.txt", +"npc/027-1/_warps.txt", +"npc/027-1/ctrl.txt", +"npc/027-1/eliza.txt", +"npc/027-1/elza.txt", +"npc/027-1/enzo.txt", +"npc/027-1/ezra.txt", +"npc/027-1/laura.txt", +"npc/027-1/statue.txt", +"npc/027-1/students.txt", +"npc/027-1/students2.txt", +"npc/027-1/wizard.txt", diff --git a/npc/027-1/_mobs.txt b/npc/027-1/_mobs.txt new file mode 100644 index 0000000..d8d5604 --- /dev/null +++ b/npc/027-1/_mobs.txt @@ -0,0 +1,12 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-1: The Academy Island mobs +027-1,77,95,4,3 monster Duck 1029,2,60000,30000 +027-1,29,146,10,10 monster Magic Ratto 1126,3,60000,30000 +027-1,57,127,9,3 monster Mana Piou 1155,3,60000,30000 +027-1,114,116,30,25 monster Red Butterfly 1025,7,60000,30000 +027-1,128,25,12,6 monster Duck 1029,2,30000,30000 +027-1,150,48,10,30 monster Croc 1006,6,30000,30000 +027-1,59,116,24,25 monster Mana Bug 1075,4,60000,30000 +027-1,100,152,60,7 monster Sea Slime 1093,12,5000,5000 +027-1,149,117,11,27 monster Piou 1002,4,60000,30000 +027-1,89,65,26,25 monster Cyan Butterfly 1172,6,60000,30000 diff --git a/npc/027-1/_warps.txt b/npc/027-1/_warps.txt new file mode 100644 index 0000000..7d57ab2 --- /dev/null +++ b/npc/027-1/_warps.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-1: The Academy Island warps +027-1,90,156,0 warp #027-1_90_156 5,0,003-1,59,59 +027-1,90,39,0 warp #027-1_90_39 1,0,027-0,65,35 +027-1,73,53,0 warp #027-1_73_53 0,0,027-2,28,28 +027-1,77,65,0 warp #027-1_77_65 0,0,027-2,32,42 +027-1,107,59,0 warp #027-1_107_59 0,0,027-4,29,34 +027-1,46,100,0 warp #027-1_46_100 0,0,027-3,27,25 +027-1,40,115,0 warp #027-1_40_115 0,0,027-3,24,39 +027-1,103,101,0 warp #027-1_103_101 0,0,027-6,30,38 diff --git a/npc/027-1/ctrl.txt b/npc/027-1/ctrl.txt new file mode 100644 index 0000000..234fda9 --- /dev/null +++ b/npc/027-1/ctrl.txt @@ -0,0 +1,38 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Magic School controlled warps + +027-1,120,131,0 script #0271W01 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (ACADEMIC_RANK || is_sponsor()) + warp "027-5", 28, 23; + end; +} + +027-1,82,118,0 script #0271W02 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (ACADEMIC_RANK || is_sponsor()) + warp "027-7", 60, 48; + end; +} + +027-1,76,118,0 script #0271W03 NPC_HIDDEN,1,0,{ + end; +OnTouch: + if (ACADEMIC_RANK || is_sponsor()) + warp "027-7", 41, 48; + end; +} + +027-1,72,118,0 script #0271W04 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (ACADEMIC_RANK || is_sponsor()) + warp "027-7", 24, 48; + end; +} + diff --git a/npc/027-1/eliza.txt b/npc/027-1/eliza.txt new file mode 100644 index 0000000..48c0592 --- /dev/null +++ b/npc/027-1/eliza.txt @@ -0,0 +1,22 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Forgetful + +027-1,75,56,0 script Eliza NPC_DARK_SORCERER_F,{ + mesn l("%s, the Forgetful", .name$); + mesq l("What was I doing before...? I totally forgot!"); + if (any(true, false)) + mesc l("Have you seen my sister Elza?"); + next; + ForgetfulNPC(.name$, CLASS_DESTRUCTION); + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; + +} + diff --git a/npc/027-1/elza.txt b/npc/027-1/elza.txt new file mode 100644 index 0000000..cd72c25 --- /dev/null +++ b/npc/027-1/elza.txt @@ -0,0 +1,22 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Forgetful + +027-1,104,61,0 script Elza NPC_DARK_SORCERER_F,{ + mesn l("%s, the Forgetful", .name$); + mesq l("What was I doing before...? I totally forgot!"); + if (any(true, false)) + mesc l("Have you seen my brother %s?", any("Enzo", "Ezra")); + next; + ForgetfulNPC(.name$, CLASS_SCHOLARSHIP); + close; + +OnInit: + .sex = G_FEMALE; + .distance = 5; + end; + +} + diff --git a/npc/027-1/enzo.txt b/npc/027-1/enzo.txt new file mode 100644 index 0000000..7ca4259 --- /dev/null +++ b/npc/027-1/enzo.txt @@ -0,0 +1,20 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Forgetful + +027-1,49,102,0 script Enzo NPC_BLACKWIZARD,{ + mesn l("%s, the Forgetful", .name$); + mesq l("What was I doing before...? I forgot!"); + next; + ForgetfulNPC(.name$, CLASS_PHYSICAL); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; + +} + diff --git a/npc/027-1/ezra.txt b/npc/027-1/ezra.txt new file mode 100644 index 0000000..b16a42c --- /dev/null +++ b/npc/027-1/ezra.txt @@ -0,0 +1,23 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Forgetful + +027-1,135,25,0 script Ezra NPC_BLACKWIZARD,{ + mesn l("%s, the Forgetful", .name$); + mesq l("What was I doing before...? I forgot!"); + next; + ForgetfulNPC(.name$, CLASS_TRICKS); + + mesn l("%s, the Forgetful", .name$); + mesq l("Do you know where my brother Enzo is? I forgot that too!"); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; + +} + diff --git a/npc/027-1/laura.txt b/npc/027-1/laura.txt new file mode 100644 index 0000000..4684764 --- /dev/null +++ b/npc/027-1/laura.txt @@ -0,0 +1,395 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Magic School special class + +027-1,117,131,0 script Laura NPC_FEMALE,{ + if (MGQUEST && getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER >= 1) goto L_T3_S0; + + mesn; + mesq l("Hello, and welcome to the Magic Academy."); + next; + mesn; + mesq l("I love living here, but I wish people weren't locked inside the rooms all time."); + next; + switch (getskilllv(TMW2_SKILLPERMIT)) { + case 0: + if (MAGIC_LVL >= 1) + goto L_Tier1; + break; + case 1: + if (MAGIC_LVL >= 2) + goto L_Tier2; + break; + case 2: + if (MAGIC_LVL >= 3) + goto L_Tier3; + break; + case 3: + if (MAGIC_LVL >= 4) + goto L_Tier4; + break; + } + closeclientdialog; + goodbye; + close; + +//////////////// +/* First Tier */ +//////////////// +L_Tier1: + mesn; + mesq l("Also, I see you're a newly registered mage. Am I right?"); + next; + if (askyesno() == ASK_NO) { + mesn; + mesq l("Alright. I wish you good luck in your studies."); + close; + } + mesn; + mesq l("Good. Did you knew you could register to the Special Class, in order to get an extra skill point?"); + next; + mesn; + mesq l("We only require a small fee of %d %s, or %d %s, or %d %s if you are poor adventurer.", + 1, getitemlink(DivineApple), + 25, getitemlink(SnakeEgg), + 360, getitemlink(MaggotSlime)); + next; + inventoryplace ScholarshipBadge, 1; + switch(select( + rif(countitem(DivineApple) >= 1, l("I got the apple.")), + rif(countitem(SnakeEgg) >= 25, l("I got the eggs.")), + rif(countitem(MaggotSlime) >= 360, l("I got the maggots slimes.")), + l("I will apply later."))) { + + case 1: + delitem DivineApple, 1; + getexp $MANA_BLVL*100, $MANA_JLVL*10; + getitem ScholarshipBadge, 1; // NOT A BUG + break; + case 2: + delitem SnakeEgg, 25; + getexp $MANA_BLVL*100, $MANA_JLVL*10; + break; + case 3: + delitem MaggotSlime, 360; + break; + default: + close; + break; + } + skill TMW2_SKILLPERMIT, 1, 0; + getitem ScholarshipBadge, 1; + mes ""; + mesn; + mesq l("Many thanks, your help has been invaluable. You now have an extra point, use it wisely."); + next; + mesn; + mesq l("Listen, as you're new here, I'll give you my %s. I won't give it again, understood? This is my personal gratitude. Come again!", getitemlink(ScholarshipBadge)); + close; + +///////////////// +/* Second Tier */ +///////////////// +L_Tier2: + mesn; + mesq l("Maybe you're interested in the Special Class again? An extra magic skill point for a lot of items, what do ya say?"); + next; + if (askyesno() == ASK_NO) { + mesn; + mesq l("Alright. I wish you good luck in your studies."); + close; + } + mesn; + mesq l("Great news! Then please bring me 1 @@, or 200 @@ @@ 20 @@.", getitemlink(DivineApple), getitemlink(SilkCocoon), b(l("and")), getitemlink(ChocolateMouboo)); + next; + switch(select( + rif(countitem(DivineApple) >= 1, l("I got the apple.")), + rif(countitem(SilkCocoon) >= 200 && countitem(ChocolateMouboo) >= 20, l("I got the silk and chocolate.")), + l("I will apply later."))) { + + case 1: + delitem DivineApple, 1; + break; + case 2: + delitem SilkCocoon, 200; + delitem ChocolateMouboo, 20; + break; + default: + close; + break; + } + skill TMW2_SKILLPERMIT, 2, 0; + getexp 30000, 1000; // Level cannot be lower than 35, use 40 as ref. + mes ""; + mesn; + mes l("Many thanks, and once again, your help has been invaluable."); + mes l("You now have an extra point, use it wisely."); + close; + +///////////////// +/* Third Tier */ +///////////////// +L_Tier3: + ST_TIER=1; + mesn; + mesq l("Maybe you're interested in the Special Class again? An extra magic skill point, but this time in a dangerous journey, what do ya say?"); + next; + if (nard_reputation() < 8) { + mesn; + mesc l("I advise you to do more quests on Tulimshar and Candor, otherwise, you will fail right at the end."), 1; + next; + } + if (askyesno() == ASK_NO) { + mesn; + mesq l("Alright. I wish you good luck in your studies."); + close; + } + mesn; + mesq l("I will prepare you a potion. But beware, that potion will only last 20 minutes. You should assign some intelligence points to succeed."); + next; + mesn; + mes l("If it expires, you'll need to do another. To bake it I need 1 @@, 10 @@ and an @@.", getitemlink(EverburnPowder), getitemlink(MaggotSlime), getitemlink(EmptyBottle)); + mesc l("Have Maggot Slimes, Bug Legs, Mauve Herbs and Money, lots of them."), 1; + next; + select + rif(countitem(MaggotSlime) >= 10 && countitem(EverburnPowder) && countitem(EmptyBottle), l("I have everything.")), + l("I'm not ready."); + mes ""; + if (@menu == 2) { + mesn; + mesq l("Yes, as you see, the costs are high. Prepare yourself."); + close; + } + + if (TUTORIAL && nard_reputation() < 8) { + mesn col(l("Tutorial Tom"), 9); + mesc l("Unfortunately you're in tutorial mode, so I cannot let you go ahead knowing you'll fail. Please improve your reputation with Nard and then continue."); + close; + } + delitem EmptyBottle, 1; + delitem EverburnPowder, 1; + delitem MaggotSlime, 10; + ST_TIER=2; + QUEST_ELEVARTEMPO=gettimetick(2) + (60 * 20); + getexp 400, 0; + mesn; + mesc l("She mix the powder with the slime inside the bottle, and makes some weird mixture."); + next; + mesn; + mesc l("She pours something on it, you're not sure what. And then utters some magic words."); + next; + // Reset timer, this is the place where it should really happen. + QUEST_ELEVARTEMPO=gettimetick(2) + (60 * 20); + mesn; + mesq l("The potion is baked, and the time is now running! Read as fast as you can, don't miss details!"); + next; + mesn; + mesq l("First thing is to get a @@. One from black market won't do, go to Halinarzo!", getitemlink(SunnyCrystal)); + next; + mesn; + mesq l("Speak with ##BBarzil##b. Tell him it is for the Magic Academy. HURRY UP!"); + close; + +// Logic handler +L_T3_S0: + if (getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER == 1) goto L_Tier3; + if (gettimetick(2) > QUEST_ELEVARTEMPO) goto L_T3_Fail; + + if (getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER == 2) goto L_T3_S2; + if (getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER == 3) goto L_T3_S3; + if (getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER == 4) goto L_T3_S4; + if (getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER == 5) goto L_T3_S5; + if (getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER == 6) goto L_T3_S6; + if (getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER <= 9) goto L_T3_S7; + if (getskilllv(TMW2_SKILLPERMIT) == 2 && MAGIC_LVL >= 3 && ST_TIER == 10) goto L_T3_Final; + mesc l("Error, error, L_T3_S0 General Error, REPORT ME"); + close; + +L_T3_S2: + mesn; + mesq l("Hurry up! Bring a @@ from Barzil in Halinarzo!! You only have @@ left!", getitemlink(SunnyCrystal), FuzzyTime(QUEST_ELEVARTEMPO,2,2)); + close; + +L_T3_S3: + if (countitem(SunnyCrystal) == 0) { + mesn; + mesq l("Where's the Sunny Crystal? Hurry up, you only have @@ left!", FuzzyTime(QUEST_ELEVARTEMPO,2,2)); + } + delitem SunnyCrystal, 1; + ST_TIER=4; + getexp 250, 0; + mesn; + mesq l("Good, you did it!"); + next; + mesn; + mesc l("*chants more words, while the crystal hovers the potion*"); + next; + +L_T3_S4: + mesn; + mesq l("I will need many Mauve Herbs! Do you have them with you? If you don't have enough, we'll lose everything! You need at most @@!", BaseLevel+40); + select + rif(countitem(MauveHerb), l("Yes, I have herbs. I assume the risks.")), + l("No I don't have herbs. I'll be back."); + + if (@menu == 2) + close; + + if (gettimetick(2) > QUEST_ELEVARTEMPO) goto L_T3_Fail; + .@req=rand2(BaseLevel-20, BaseLevel+40); + // Minimum is 40, max is unknown, defaults to 100 + + mesn; + mesq l("I need @@ Herbs!", .@req); + //next; // If you comment this next, you'll allow players to logout and prevent penalty. + mes ""; + + if (countitem(MauveHerb) < .@req) goto L_T3_Fail; + delitem MauveHerb, .@r; + ST_TIER=5; + getexp .@r*3, 0; + //getitem MagicPotion, 1; + + mesc l("You quickly give her the herbs, and she skillfully mix them on a potion."); + next; + // You'll get a random amount of time, based on spent herbs + // Usually, 10~73 sec, being 73 sec = 1m13s + QUEST_ELEVARTEMPO=QUEST_ELEVARTEMPO+rand2(10,.@r-27); + +L_T3_S5: + mesn; + mesq l("Good! Last step! West of Hurnscald, there is a magic fountain."); + next; + mesn; + mesq l("Talk to the Fountain. Pour the potion on it. I advise you to put all your points on int if possible."); + next; + mesn; + mesq l("Hurry up, you'll run out of time in @@!", FuzzyTime(QUEST_ELEVARTEMPO,2,2)); + close; + +L_T3_S6: + ST_TIER=7; + mesn; + mesq l("You did it! You're now on the last stage of this BORING and LONG quest!"); + next; + +L_T3_S7: + mesn; + mesq l("Jesusaves wrote a grimorie, with ancient secrets of our world."); + next; + mesn; + mesq l("Captain Nard have it. Fetch it with him! Quick, you only have @@ left!", FuzzyTime(QUEST_ELEVARTEMPO,2,2)); + close; + +L_T3_Final: + skill TMW2_SKILLPERMIT, 3, 0; + getexp 120000, 0; // Yes, 120k experience points. Waw. + mesc l(".:: Congratulations! ::."), 2; + mesc l("You have completed the Jesusaves Grimorium Quest!"), 2; + next; + mesn; + mesq l("Keep the Grimorie with you. It's a rare book which holds data from all others. The book shall guide your advances!"); + next; + mesn; + mesq lg("Yes, courageous and worthy adventurer. You did well!"); + next; + mesn; + mesc l("*sigh*"); + mesq l("Now I can turn in my report to Professor Volrtaw... I should not have stayed behind the classes."); + close; + +/// Fail handlers +L_T3_Fail: + if (ST_TIER == 3) { + if (countitem(SunnyCrystal) > 0) { + delitem SunnyCrystal, 1; + } else { + mesn; + mesc l("WARNING. YOU ARE CHEATING THE SUNNY CRYSTAL QUEST."), 1; + next; + mesn; + mesc l("YOU WILL BE PENALIZED WITH 60% OF HEALTH."), 1; + mesc l("IF YOU DIE, YOU'LL SUFFER THE EXP PENALTY."), 1; + percentheal -60, -100; + close; + } + } + + if (ST_TIER == 10) { + if (countitem(JesusalvaGrimorium) > 0) { + delitem JesusalvaGrimorium, 1; + } else { + mesn; + mesc l("WARNING. YOU ARE CHEATING THE GRIMORIE QUEST."), 1; + next; + mesn; + mesc l("YOU WILL BE PENALIZED WITH 70% OF HEALTH."), 1; + mesc l("IF YOU DIE, YOU'LL SUFFER THE EXP PENALTY."), 1; + percentheal -70, -100; + close; + } + } + + mesc l(".:: Mission Failed ::."), 1; + mesc l("You ran out of time."), 1; + mes ""; + mes l("You should have gotten here @@.", FuzzyTime(QUEST_ELEVARTEMPO,0,2)); + ST_TIER=1; + close; + +L_Tier4: + if (!ACADEMIC_RANK) { + mesn; + mesq l("Oh hello again. This time I need you to be enrolled here, for this, you need to either get a good rank at the Magic Olympics or by contributing to the world's knowledge. Talk to headmaster if you need help."); + close; + } + mesn; + mesq l("%s! I see you are a(n) %s here, very good!", strcharinfo(0), academicrank()); + next; + mesn; + mesq l("Still interested in extra credit? Now that you're a(n) %s, you can sign this perfectly normal and standard %s, and by helping us out, by helping ME out, you'll gain the extra credit. What do you say? Not a bad deal, right?", academicrank(), b(l("Non Disclosure Agreemeent"))); + next; + mesc l("Sign the non-disclosure agreement?"); + if (askyesno() == ASK_NO) close; + mesn; + mesq l("Perfect! So now that you swore secrecy, you can help me! Thing is... there was... a %s!", col(l("murder"), 1)); + next; + mesn; + mesq l("You have access to the storehouse now, right? Well, that's where it happened!"); + next; + mesn; + mesc l("*sighs*"); + mesq l("And obviously, Professor Volrtaw assigned me to investigate! Look at me, do I look like an investigator to you? But, he told me I could pick any student to help me! And I choose you!"); + next; + mesn; + mesq l("Please be a pal and help me! I'll be in a dire situation if you don't! Besides, you also want the extra credit, so it is a win-win situation!"); + next; + mesn; + mesq l("You will find a statue different from the others. It is where the game balance was murdered and bugs added instead! No, not really - this is just a placeholder. Not cool!"); + // Reward is 550k exp for this arc (investigate, interrogate, etc) + // Next T5 arc will give 2 million exp points (find the PoN) + // Then there'll be no more arcs nor extra credits + // You'll gain the "Summon Princess of Night skill" + // And a permanent MP boost. PoN takes 1h to expire + // Real effect depends on your decision in Elenium Mines + // Sparing the girl will boost PoN considerably. + setq1 General_NightInRed, 1; + close; + +/// Core code +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, FancyHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SorcererRobe); + //setunitdata(.@npcId, UDT_HEADBOTTOM, NPCEyes); + //setunitdata(.@npcId, UDT_WEAPON, JeansShorts); + setunitdata(.@npcId, UDT_HAIRSTYLE, any(8,11,20)); + setunitdata(.@npcId, UDT_HAIRCOLOR, 5); + + .sex=G_FEMALE; + .distance=5; + end; +} + diff --git a/npc/027-1/statue.txt b/npc/027-1/statue.txt new file mode 100644 index 0000000..dc21ef3 --- /dev/null +++ b/npc/027-1/statue.txt @@ -0,0 +1,26 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// These statues are of great honor to whoever have their name written in them! + +027-1,57,115,0 script Hero Statue#027-1 NPC_STATUE_ANDREI,{ + + mes l("In honor of %s, the greatest hero this world has even seen.", b("Andrei Sakar")); + mes ""; + if ($MOST_HEROIC$ != "") { + mes l("In honor of %s, for their great deeds of recent bravery in face of impending doom.", $MOST_HEROIC$); + mes ""; + } + mes l("In honor of %s, founder of this academy, whom built this academy WITH HIS OWN HANDS, WITH BLOOD SWEAT AND TEARS.", b("Jesusalva")); + mes ""; + mes l("Also in honor of the other two sages, %s and %s. And finally, notable mention for the noble %s, %s and %s, for sponsoring this Academy.", b("Saulc"), b("Crazyfefe"), b("Micksha"), b("omatt"), b(rand_sponsor())); + + close; + +OnInit: + .sex = G_OTHER; + .distance = 4; + end; +} + diff --git a/npc/027-1/students.txt b/npc/027-1/students.txt new file mode 100644 index 0000000..cf3e124 --- /dev/null +++ b/npc/027-1/students.txt @@ -0,0 +1,45 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Random NPC (HF = Human Female) + +027-1,132,112,0 script Student#A01 NPC_ACADEMY_HF,{ + +studenttalk(); +close; + +OnTimer1000: + domovestep(); + end; + +OnInit: + initpath "move", 132, 112, + "dir", DOWN, 0, + "wait", 20, 0, + "move", 132, 122, + "dir", DOWN, 0, + "wait", 20, 0, + "dir", UP, 0, + "wait", 1, 0, + "move", 132, 102, + "dir", DOWN, 0, + "wait", 20, 0, + "dir", LEFT, 0, + "wait", 1, 0, + "move", 115, 102, + "dir", DOWN, 0, + "wait", 20, 0, + "move", 112, 112, + "dir", DOWN, 0, + "wait", 20, 0, + "dir", RIGHT, 0, + "wait", 2, 0; + + initialmove; + initnpctimer; + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/027-1/students2.txt b/npc/027-1/students2.txt new file mode 100644 index 0000000..82357b9 --- /dev/null +++ b/npc/027-1/students2.txt @@ -0,0 +1,136 @@ +// TMW2 Scripts +// Author: +// jak1 +// Description: +// Random NPC (HF = Human Female) + +027-1,134,121,0 script Student#A02 NPC_ACADEMY_HF,{ + +studenttalk(); +close; + +OnTimer1000: + domovestep(); + end; + +OnInit: + initpath "wait", 1, 0, + "move", 134, 121, + "dir", DOWN, 0, + "wait", 2, 0, + "move", 131, 123, + "wait", 2, 0, + "move", 124, 121, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 121, 123, + "wait", 2, 0, + "move", 114, 121, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 111, 123, + "wait", 2, 0, + "move", 103, 121, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 2, 0, + "move", 111, 117, + "wait", 2, 0, + "move", 104, 111, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 111, 113, + "wait", 2, 0, + "move", 114, 111, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 121, 113, + "wait", 2, 0, + "move", 124, 111, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 131, 113, + "wait", 1, 0, + "move", 134, 111, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 131, 103, + "wait", 1, 0, + "move", 134, 101, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 131, 103, + "wait", 1, 0, + "move", 124, 101, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 121, 103, + "wait", 1, 0, + "move", 114, 101, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 111, 103, + "wait", 1, 0, + "move", 103, 101, + "wait", 1, 0, + "class", NPC_HIDDEN, 0, + "wait", 2, 0, + "dir", DOWN, 0, + "class", NPC_ACADEMY_HF, 0, + "wait", 1, 0, + "move", 111, 103, + "wait", 1, 0, + "move", 111, 113, + "wait", 1, 0, + "move", 121, 113, + "wait", 1, 0, + "move", 131, 113, + "wait", 1, 0, + "move", 131, 123; + + initialmove; + initnpctimer; + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/027-1/wizard.txt b/npc/027-1/wizard.txt new file mode 100644 index 0000000..3551544 --- /dev/null +++ b/npc/027-1/wizard.txt @@ -0,0 +1,33 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Wizard at Magic Academy Entrance + +027-1,44,91,0 script Purple Wizard NPC_PURPLE_WIZARD_F,{ + mesn; + mesq l("Hello. I was assigned to guard the ranged training grounds of the mystic forest."); + next; + mesn; + mesq l("It is a dangerous area, so we only allow students enrolled at the academy to participate."); + if (!ACADEMIC_RANK && !is_sponsor()) + close; + next; + mesn; + mesq l("Do you want to go there? The Red Wizard may be able to offer you training."); + next; + select + l("Not yet."), + l("Yes please."); + mes ""; + closeclientdialog; + if (@menu == 2) + warp "017-0", 249, 228; + close; + +OnInit: + .distance = 3; + .sex = G_FEMALE; + end; +} + diff --git a/npc/027-2/_import.txt b/npc/027-2/_import.txt new file mode 100644 index 0000000..c01f816 --- /dev/null +++ b/npc/027-2/_import.txt @@ -0,0 +1,4 @@ +// Map 027-2: Wizardry Institute Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/027-2/_warps.txt", +"npc/027-2/colin.txt", diff --git a/npc/027-2/_warps.txt b/npc/027-2/_warps.txt new file mode 100644 index 0000000..15b84fa --- /dev/null +++ b/npc/027-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-2: Wizardry Institute Indoors warps +027-2,28,29,0 warp #027-2_28_29 0,0,027-1,73,54 +027-2,32,43,0 warp #027-2_32_43 0,0,027-1,77,66 diff --git a/npc/027-2/colin.txt b/npc/027-2/colin.txt new file mode 100644 index 0000000..f0384c7 --- /dev/null +++ b/npc/027-2/colin.txt @@ -0,0 +1,174 @@ +// TMW2 script. +// Author: +// Saulc +// Jesusalva +// Description: +// Colin, of Destructive Magic Class. + +027-2,28,23,0 script Colin NPC_PLAYER,{ + function basicMagic; + function standardMagic; + function advancedMagic; + if (!MAGIC_LVL) goto L_NoMagic; + mes l(".:: Destructive Magic Class ::."); + mesc l("Specialized in destructive, magical skills."); + next; + mesn; + mesc l("You have @@ magic skill points available.", sk_points()); + mesc l("Note: You can exchange 1 @@ for 3 @@", getitemlink(GemPowder), getitemlink(Quill)), 1; + mes ""; + select + l("Basic Magic"), + l("Standard Magic"), + l("Advanced Magic"), + //l("Mastery Magic"), + rif(countitem(GemPowder), l("Exchange Gempowder for Quill")); + mes ""; + .@lv=@menu; + do + { + switch (.@lv) { + case 1: + basicMagic(); + break; + case 2: + standardMagic(); + break; + case 3: + advancedMagic(); + break; + case 4: + inventoryplace Quill, 3; + delitem GemPowder, 1; + getitem Quill, 3; + @menuret=0; + break; + } + + // Handle result + mes ""; + if (@menuret) { + if (!learn_magic(@menuret)) { + mesc l("You do not meet all requisites for this skill."), 1; + next; + } + } else { + closeclientdialog; + } + + } while (@menuret); + close; + +L_NoMagic: + next; + mesn; + mesq l("You do not have enough magic power for these classes."); + next; + if ($FIRESOFSTEAM < 9) { + mesn; + mesq l("Besides the Magic Council, Andrei Sakar have his own Mana Stone, but I doubt he would train the likes of you, or share his Mana Stone."); + next; + } + mesn; + mesq l("Perhaps, in the city, someone knows rumors about Mana Stones and can teach you. Other than that, you're on your own."); + close; + +function basicMagic { + if (MAGIC_LVL < 1) goto L_NoMagic; + mes l(".:: Fire Arrow ::."); + mesc l("Basic fire single target attack. May burn targets."); + mes ""; + mes l(".:: Napalm Beat ::."); + mesc l("Basic multi-target holy attack. Is actually weak."); + mes ""; + mes l(".:: Magic Strike ::."); + mesc l("Basic wind single target attack. Strong in overall."); + mes ""; + mes l(".:: Frost Diver ::."); + mesc l("Basic ice single target attack. May freeze targets."); + mes ""; + mes l(".:: Meteor Strike ::."); + mesc l("Basic earth single target attack. May stun targets."); + mes ""; + menuint + l("Fire Arrow"), TMW2_FIREARROW, + l("Napalm Beat"), TMW2_NAPALMBEAT, + l("Magic Strike"), TMW2_MAGICSTRIKE, + l("Frost Diver"), TMW2_FROSTDIVER, + l("Meteor Strike"), TMW2_METEORSTRIKE, + l("Cancel"), 0; + return; +} + +function standardMagic { + if (MAGIC_LVL < 2) goto L_NoMagic; + mes l(".:: Fireball ::."); + mesc l("Area of effect fire damage. May burn targets."); + mes ""; + mes l(".:: Holy Light ::."); + mesc l("Basic single target holy attack. Splashes in the nearby tiles."); + mes ""; + mes l(".:: Lightning Bolt ::."); + mesc l("Strong wind single target attack."); + mes ""; + mes l(".:: Frost Nova ::."); + mesc l("Basic ice area attack. May freeze targets."); + mes ""; + mes l(".:: Meteor Shower ::."); + mesc l("Basic earth area attack. May stun targets."); + mes ""; + mes l(".:: Firewalk ::."); + mesc l("Superior damage on enemies who walk over the fire."); + mes ""; + menuint + l("Fireball"), TMW2_FIREBALL, + l("Holy Light"), TMW2_HOLYLIGHT, + l("Lightning Bolt"), TMW2_LIGHTNINGBOLT, + l("Frost Nova"), TMW2_FROSTNOVA, + l("Meteor Shower"), TMW2_METEORSHOWER, + l("Firewalk"), SO_FIREWALK, + l("Cancel"), 0; + return; +} + +function advancedMagic { + if (MAGIC_LVL < 4) goto L_NoMagic; + mes l(".:: Armageddon ::."); + mesc l("Huge area of effect fire damage. May burn targets."); + mes ""; + mes l(".:: Judgment ::."); + mesc l("Superior single target damage which splashes in area."); + mes ""; + mes l(".:: Tempest ::."); + mesc l("Strong wind area of effect attack."); + mes ""; + mes l(".:: Nilfheim ::."); + mesc l("Basic ice area attack. Freeze targets in a big area."); + mes ""; + mes l(".:: Gaia Break ::."); + mesc l("Destroys everything in a line (earth). Boosts allied forces DEF."); + mes ""; + menuint + l("Armageddon"), TMW2_ARMAGEDDON, + l("Judgment"), TMW2_JUDGMENT, + l("Tempest"), TMW2_TEMPEST, + l("Nilfheim"), TMW2_NILFHEIM, + l("Gaia Break"), TMW2_GAIABREAK, + l("Cancel"), 0; + return; +} + + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, SorcererRobe); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 21); + setunitdata(.@npcId, UDT_HAIRCOLOR, 20); + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/027-3/_import.txt b/npc/027-3/_import.txt new file mode 100644 index 0000000..feb1974 --- /dev/null +++ b/npc/027-3/_import.txt @@ -0,0 +1,5 @@ +// Map 027-3: Physical Sciences Classroom +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/027-3/_warps.txt", +"npc/027-3/angel.txt", +"npc/027-3/luca.txt", diff --git a/npc/027-3/_warps.txt b/npc/027-3/_warps.txt new file mode 100644 index 0000000..8f4f474 --- /dev/null +++ b/npc/027-3/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-3: Physical Sciences Classroom warps +027-3,27,26,0 warp #027-3_27_26 0,0,027-1,46,101 +027-3,24,40,0 warp #027-3_24_40 0,0,027-1,40,116 diff --git a/npc/027-3/angel.txt b/npc/027-3/angel.txt new file mode 100644 index 0000000..b811e3d --- /dev/null +++ b/npc/027-3/angel.txt @@ -0,0 +1,111 @@ +// TMW2 script. +// Author: +// dangerDuck +// Description: +// Angel, of Physical Science Class. + +027-3,36,28,0 script Angel NPC_PLAYER,{ + function basicMagic; + function standardMagic; + function advancedMagic; + if (!MAGIC_LVL) goto L_NoMagic; + mes l(".:: Physical Sciences Class ::."); + mesc l("Specialized in skills with fist-based damage and unarmed mastery."); + next; + mesn; + mesc l("You have @@ magic skill points available.", sk_points()); + select + l("Basic Magic"), + l("Standard Magic"), + l("Advanced Magic"); + //l("Mastery Magic"); + mes ""; + .@lv=@menu; + do + { + // Display appropriate menu + if (.@lv == 1) + basicMagic(); + else if (.@lv == 2) + standardMagic(); + else if (.@lv == 3) + advancedMagic(); + + // Handle result + mes ""; + if (@menuret) { + if (!learn_magic(@menuret)) { + mesc l("You do not meet all requisites for this skill."), 1; + next; + } + } else { + closeclientdialog; + } + + } while (@menuret); + close; + +L_NoMagic: + next; + mesn; + mesq l("You do not have enough magic power for these classes."); + next; + if ($FIRESOFSTEAM < 9) { + mesn; + mesq l("Besides the Magic Council, Andrei Sakar have his own Mana Stone, but I doubt he would train the likes of you, or share his Mana Stone."); + next; + } + mesn; + mesq l("Perhaps, in the city, someone knows rumors about Mana Stones and can teach you. Other than that, you're on your own."); + close; + +function basicMagic { + if (MAGIC_LVL < 1) goto L_NoMagic; + mes l(".:: Brawling ::."); + mesc l("Three powerful consecutive brawn attacks."); + mes ""; + menuint + l("Brawling"), TMW2_BRAWLING, + l("Cancel"), 0; + return; +} + +function standardMagic { + if (MAGIC_LVL < 2) goto L_NoMagic; + mes l(".:: Bear Strike ::."); + mesc l("Five powerful consecutive brawn attacks."); + mes ""; + mes l(".:: Stunning Strike ::."); + mesc l("Three powerful consecutive brawn attacks with a chance to stun target."); + mes ""; + menuint + l("Bear Strike"), TMW2_BEARSTRIKE, + l("Stunning Strike"), TMW2_STUNNINGSTRIKE, + l("Cancel"), 0; + return; +} + +function advancedMagic { + if (MAGIC_LVL < 3) goto L_NoMagic; + mes l(".:: All In One ::."); + mesc l("Seven consecutive brawn attacks of diff. elements."); + mes ""; + menuint + l("All In One"), TMW2_ALLINONE, + l("Cancel"), 0; + return; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, AssassinChest); + setunitdata(.@npcId, UDT_HEADBOTTOM, AssassinPants); + setunitdata(.@npcId, UDT_WEAPON, AssassinBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 20); + setunitdata(.@npcId, UDT_HAIRCOLOR, 7); + + .sex = G_FEMALE; + .distance = 5; + end; +} diff --git a/npc/027-3/luca.txt b/npc/027-3/luca.txt new file mode 100644 index 0000000..eae8824 --- /dev/null +++ b/npc/027-3/luca.txt @@ -0,0 +1,120 @@ +// TMW2 script. +// Author: +// Saulc +// Jesusalva +// Description: +// Luca, of Physical Science Class. + +027-3,24,36,0 script Luca NPC_PLAYER,{ + function basicMagic; + function standardMagic; + function advancedMagic; + if (!MAGIC_LVL) goto L_NoMagic; + mes l(".:: Physical Sciences Class ::."); + mesc l("Specialized in skills with weapon-based damage and Assassination."); + next; + mesn; + mesc l("You have @@ magic skill points available.", sk_points()); + select + l("Basic Magic"), + l("Standard Magic"), + l("Advanced Magic"); + //l("Mastery Magic"); + mes ""; + .@lv=@menu; + do + { + // Display appropriate menu + if (.@lv == 1) + basicMagic(); + else if (.@lv == 2) + standardMagic(); + else if (.@lv == 3) + advancedMagic(); + + // Handle result + mes ""; + if (@menuret) { + if (!learn_magic(@menuret)) { + mesc l("You do not meet all requisites for this skill."), 1; + next; + } + } else { + closeclientdialog; + } + + } while (@menuret); + close; + +L_NoMagic: + next; + mesn; + mesq l("You do not have enough magic power for these classes."); + next; + if ($FIRESOFSTEAM < 9) { + mesn; + mesq l("Besides the Magic Council, Andrei Sakar have his own Mana Stone, but I doubt he would train the likes of you, or share his Mana Stone."); + next; + } + mesn; + mesq l("Perhaps, in the city, someone knows rumors about Mana Stones and can teach you. Other than that, you're on your own."); + close; + +function basicMagic { + if (MAGIC_LVL < 1) goto L_NoMagic; + mes l(".:: Falkon Strike ::."); + mesc l("Bash your weapon against your enemies with raised damage and accuracy."); + mes ""; + mes l(".:: Ground Strike ::."); + mesc l("Hit the ground, exploding the surroundings and disabling enemies."); + mes ""; + mes l(".:: Sharpshooter ::."); + mesc l("Shoot an arrow or bullet which damages everything on its way."); + mes ""; + menuint + l("Falkon Strike"), TMW2_FALKONSTRIKE, + l("Ground Strike"), TMW2_GROUNDSTRIKE, + l("Sharpshooter"), SN_SHARPSHOOTING, + l("Cancel"), 0; + return; +} + +function standardMagic { + if (MAGIC_LVL < 2) goto L_NoMagic; + mes l(".:: Supreme Attack ::."); + mesc l("Cause a very strong attack with lowered accuracy."); + mes ""; + mes l(".:: Arrow Shower ::."); + mesc l("Shoot FIVE arrows or bullets to the air and cause Area Of Effect Damage."); + mes ""; + menuint + l("Supreme Attack"), TMW2_SUPREMEATTACK, + l("Arrow Shower"), TMW2_ARROWSHOWER, + l("Cancel"), 0; + return; +} + +function advancedMagic { + if (MAGIC_LVL < 3) goto L_NoMagic; + mes l(".:: Counter Attack ::."); + mesc l("Retaliates next attack with a critical hit. This instance lasts a second."); + mes ""; + menuint + l("Counter Attack"), KN_AUTOCOUNTER, + l("Cancel"), 0; + return; +} + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, NPCEyes); + setunitdata(.@npcId, UDT_HEADMIDDLE, BromenalChest); + setunitdata(.@npcId, UDT_HEADBOTTOM, JeansShorts); + setunitdata(.@npcId, UDT_WEAPON, DeepBlackBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 21); + setunitdata(.@npcId, UDT_HAIRCOLOR, 6); + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/027-4/_import.txt b/npc/027-4/_import.txt new file mode 100644 index 0000000..a74f425 --- /dev/null +++ b/npc/027-4/_import.txt @@ -0,0 +1,4 @@ +// Map 027-4: Scholarship Building +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/027-4/_warps.txt", +"npc/027-4/saves.txt", diff --git a/npc/027-4/_warps.txt b/npc/027-4/_warps.txt new file mode 100644 index 0000000..856e5c5 --- /dev/null +++ b/npc/027-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-4: Scholarship Building warps +027-4,29,35,0 warp #027-4_29_35 1,0,027-1,107,60 diff --git a/npc/027-4/saves.txt b/npc/027-4/saves.txt new file mode 100644 index 0000000..c337b80 --- /dev/null +++ b/npc/027-4/saves.txt @@ -0,0 +1,139 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Mr. Saves of Scholar Class + +027-4,28,28,0 script Mr Saves NPC_BLACKALCHEMIST,{ + function basicMagic; + function standardMagic; + function advancedMagic; + if (!MAGIC_LVL) goto L_NoMagic; + mes l(".:: Scholarship Class ::."); + mesc l("Specialized in support, buff, debuff and strengthening skills."); + next; + mesn; + mesc l("You have @@ magic skill points available.", sk_points()); + next; + select + l("Basic Magic"), + l("Standard Magic"), + l("Advanced Magic"); + //l("Mastery Magic"); + mes ""; + .@lv=@menu; + do + { + // Display appropriate menu + if (.@lv == 1) + basicMagic(); + else if (.@lv == 2) + standardMagic(); + else if (.@lv == 3) + advancedMagic(); + + // Handle result + mes ""; + if (@menuret) { + if (!learn_magic(@menuret)) { + mesc l("You do not meet all requisites for this skill."), 1; + next; + } + } else { + closeclientdialog; + } + + } while (@menuret); + close; + +function basicMagic { + if (MAGIC_LVL < 1) goto L_NoMagic; + mes l(".:: First Aid ::."); + mesc l("Minor healing to your wounds."); + mes ""; + mes l(".:: Accumulate Power ::."); + mesc l("Raise damage of next skill."); + mes ""; + mes l(".:: Provoke ::."); + mesc l("Provoke a single monster to attack you."); + mes ""; + mes l(".:: Windwalker ::."); + mesc l("Increase walk speed and flee rate."); + mes ""; + mes l(".:: Chanting ::."); + mesc l("Reduces MP cost when using chanting-based skills."); + mes ""; + mes l(".:: Transfer Mana ::."); + mesc l("Drains your MP bar to replenish target's. Doesn't go past 100%."); + mes ""; + menuint + l("First Aid"), TMW2_FIRSTAID, + l("Accumulate Power"), HW_MAGICPOWER, + l("Provoke"), SM_PROVOKE, + l("Windwalker"), SN_WINDWALK, + l("Chanting"), TMW2_CHANT, + l("Transfer Mana"), TMW2_MPTRANSFER, + l("Cancel"), 0; + return; +} + +function standardMagic { + if (MAGIC_LVL < 2) goto L_NoMagic; + // NOTE: Alternate between First Aid + Healing for less cooldown wait + mes l(".:: Healing ::."); + mesc l("Minor healing to yourself or to allies."); + mes ""; + mes l(".:: Mana Wisdom ::."); + mesc l("(Passive) Increases Mana EXP/Control Gain rate."); + mes ""; + mes l(".:: Last Standing Man ::."); + mesc l("(Passive) Raise Max HP and Holy Defense."); + mes ""; + mes l(".:: Area Provoke ::."); + mesc l("Provokes all monsters around the target, and the target itself."); + mes ""; + menuint + l("Healing"), TMW2_HEALING, + l("Mana Wisdom"), TMW2_SAGE, + l("Last Standing Man"), CR_TRUST, + l("Area Provoke"), EVOL_AREA_PROVOKE, + l("Cancel"), 0; + return; +} + +function advancedMagic { + if (MAGIC_LVL < 3) goto L_NoMagic; + mes l(".:: Magnus Healing ::."); + mesc l("Heals in area every friendly unit (incl. homuns and mercs). Req. Lifestone to cast."); + mes ""; + mes l(".:: Resurrection ::."); + mesc l("Revives an already dead allied player. Req. Lifestone to cast."); + mes ""; + menuint + l("Magnus Healing"), TMW2_MAGNUSHEAL, + l("Resurrection"), TMW2_RESURRECT, + l("Cancel"), 0; + return; +} + +L_NoMagic: + next; + mesn; + mesq l("You do not have enough magic power for these classes."); + next; + if ($FIRESOFSTEAM < 9) { + mesn; + mesq l("Besides the Magic Council, Andrei Sakar have his own Mana Stone, but I doubt he would train the likes of you, or share his Mana Stone."); + next; + } + mesn; + mesq l("Perhaps, in the city, someone knows rumors about Mana Stones and can teach you. Other than that, you're on your own."); + close; + +OnInit: + .sex = G_MALE; + .distance = 5; + end; + +} + diff --git a/npc/027-5/_import.txt b/npc/027-5/_import.txt new file mode 100644 index 0000000..0b922fe --- /dev/null +++ b/npc/027-5/_import.txt @@ -0,0 +1,3 @@ +// Map 027-5: Student's Office +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/027-5/_warps.txt", diff --git a/npc/027-5/_warps.txt b/npc/027-5/_warps.txt new file mode 100644 index 0000000..f8ddf9a --- /dev/null +++ b/npc/027-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-5: Student's Office warps +027-5,28,24,0 warp #027-5_28_24 0,0,027-1,120,132 diff --git a/npc/027-6/_import.txt b/npc/027-6/_import.txt new file mode 100644 index 0000000..2142c70 --- /dev/null +++ b/npc/027-6/_import.txt @@ -0,0 +1,3 @@ +// Map 027-6: Student House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/027-6/_warps.txt", diff --git a/npc/027-6/_warps.txt b/npc/027-6/_warps.txt new file mode 100644 index 0000000..f7e257a --- /dev/null +++ b/npc/027-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-6: Student House warps +027-6,30,39,0 warp #027-6_30_39 0,0,027-1,103,102 diff --git a/npc/027-7/_import.txt b/npc/027-7/_import.txt new file mode 100644 index 0000000..cec57a4 --- /dev/null +++ b/npc/027-7/_import.txt @@ -0,0 +1,4 @@ +// Map 027-7: Academy's Storage +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/027-7/_warps.txt", +"npc/027-7/banker.txt", diff --git a/npc/027-7/_warps.txt b/npc/027-7/_warps.txt new file mode 100644 index 0000000..53b128c --- /dev/null +++ b/npc/027-7/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 027-7: Academy's Storage warps +027-7,41,49,0 warp #027-7_41_49 0,0,027-1,77,119 +027-7,60,49,0 warp #027-7_60_49 0,0,027-1,82,119 +027-7,24,49,0 warp #027-7_24_49 0,0,027-1,72,119 diff --git a/npc/027-7/banker.txt b/npc/027-7/banker.txt new file mode 100644 index 0000000..0a478f8 --- /dev/null +++ b/npc/027-7/banker.txt @@ -0,0 +1,16 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Magic School bank + +027-7,66,40,0 script Benjamin NPC_LLOYD,{ + Banker(.name$, "Magic Academy", 1000000); + close; + +OnInit: + .sex = G_MALE; + .distance = 4; + end; +} + diff --git a/npc/029-0/_import.txt b/npc/029-0/_import.txt new file mode 100644 index 0000000..56beb84 --- /dev/null +++ b/npc/029-0/_import.txt @@ -0,0 +1,10 @@ +// Map 029-0: Artis +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-0/_mobs.txt", +"npc/029-0/amelia.txt", +"npc/029-0/elora.txt", +"npc/029-0/event.txt", +"npc/029-0/mobs.txt", +"npc/029-0/sakar.txt", +"npc/029-0/town.txt", +"npc/029-0/warps.txt", diff --git a/npc/029-0/_mobs.txt b/npc/029-0/_mobs.txt new file mode 100644 index 0000000..5a144e1 --- /dev/null +++ b/npc/029-0/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-0: Artis mobs +029-0,95,85,59,53 monster Mouboo Slime 1201,12,90000,35000 +029-0,121,47,78,30 monster Mouboo 1023,36,90000,35000 +029-0,71,99,37,45 monster Mouboo 1023,36,90000,35000 +029-0,60,48,31,28 monster Alpha Mouboo 1056,4,90000,35000 +029-0,34,70,16,21 monster Bloody Mouboo 1119,3,90000,35000 +029-0,187,40,16,21 monster Moubi 1038,3,240000,35000 +029-0,113,47,95,22 monster Mineral Bif 1058,12,90000,35000 +029-0,33,112,11,36 monster Mineral Bif 1058,9,90000,35000 diff --git a/npc/029-0/amelia.txt b/npc/029-0/amelia.txt new file mode 100644 index 0000000..da98601 --- /dev/null +++ b/npc/029-0/amelia.txt @@ -0,0 +1,117 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// The Mana Tree Quest (unlocks after Fires of Steam events) + +029-0,30,129,0 script Amelia NPC_CHILD4,{ + .@q = getq(FortressQuest_ManaTree); + .@q2 = getq2(FortressQuest_ManaTree); + + // Cycle + if (.@q == 0) goto L_Start; + if (.@q == 1) goto L_Cheerful; + if (.@q == 2) goto L_Short; + + // Generic reply + npctalk3 l("*sigh*"); + end; + +L_Start: + mesc l("The kid is not paying attention to you."); + if ($FIRESOFSTEAM < 10) end; + mesc l("She is staring at the empty sea and sighing. She seems to be really down after Andrei Sakar passed away."); + next; + mesc l("Maybe we can give her something, to cheer her up?"); + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .@id = requestitem(); + + // No item supplied + if (.@id < 1) { + closeclientdialog; + end; + } + + if (array_find(.gifts, .@id) < 0) { + mesc l("This doesn't looks like a toy."); + mesc l("Maybe we could give her a %s instead.", getitemlink(any(Doll, PlushMouboo, LeatherBall, ReinbooWand, SantaGlobe, RubberDucky, CandorBola))); // Suggest only the most rare and expensive toys + } + mesc l("Really give the %s to %s?", getitemlink(.@id), .name$), 1; + mesc b(l("The item will be lost forever!")), 1; + next; + if (askyesno() != ASK_YES) { + closeclientdialog; + end; + } + delitem .@id, 1; + getexp 27000, 0; // 1% of Level 80 EXP + setq FortressQuest_ManaTree, 1, .@id; + mesn; + mesq l("...Thanks, kind person."); + next; + mesn; + mesq l("My name is Amelia, and I am Elora's daughter."); + close; + +L_Cheerful: + mesc l("Amelia looks much more cheerful than the last time you've met her."); + next; + mesn; + mesq l("Hey, %s! Welcome back!", strcharinfo(0)); + next; + mesn; + mesq l("Thanks for the %s you gave me the other day.", getitemlink(.@q2)); + if (getq(General_Narrator) < 20) + close; + next; + mesn; + mesq l("Here, lemme tell you something good: Do you know that fortress island which all the adventurers are talking about nowadays?"); + next; + if (askyesno() == ASK_NO) { + mesn; + mesq lg("Uh, how come? I thought you were an adventurer too! That island which trusted adventurers can reach using Pihro & Pyndragon's personal airship is so popular... Anyway!"); + next; + } + mesn; + mesq l("I visited it with my mother, the other day, and I found a tree in a cave!"); + next; + mesn; + mesq l("It was so lovely, and it had some very tasty fruits, too! But the cave was so dangerous..."); + next; + mesn; + mesq l("I saw some flower adorns, too. I hope it is not some traveler's grave. This war, it makes me so sad... I'm sure the tree was sad, too."); + next; + mesn; + mesq l("If you ever go there, you should pay it a visit. It is most definitely alive! I'm sure it'll like you, too."); + setq1 FortressQuest_ManaTree, 2; + close; + +L_Short: + mesn; + mesq l("Thanks for the %s you gave me the other day.", getitemlink(.@q2)); + if (getq(General_Narrator) < 20) + close; + next; + if ($AEGIS_HOLDER$ == "" && !rand2(4)) { + mesn; + mesq l("I heard a rumor the other day. About the tree I had found!"); + next; + mesn; + mesq l("It is said that if someone dies to the pinkies but is then avenged and mourned, the avenger may claim from the tree the shield of the world!"); + next; + mesn; + mesq l("The story was so cool!"); + close; + } + mesn; + mesq l("If you ever visit the fortress island, be sure to visit the friendly tree at the cave. I'm sure it'll like you."); + close; + +OnInit: + setarray .gifts, BrokenDoll, Doll, PlushMouboo, LeatherBall, ReinbooWand, MoubooFigurine, ChocolateBunny, RedStocking, SantaGlobe, SnowmanGlobe, RubberDucky, CandorBola, KidBola; + .sex = G_FEMALE; + .distance = 4; + end; +} + diff --git a/npc/029-0/elora.txt b/npc/029-0/elora.txt new file mode 100644 index 0000000..c8caa21 --- /dev/null +++ b/npc/029-0/elora.txt @@ -0,0 +1,107 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Fires of Steam: The Death of Andrei Sakar + +029-0,182,77,0 script Elora NPC_HUMAN_FEMALE_NOOB,{ + mesn; + mesq l("Greetings! I am %s from the Alliance.", .name$); + next; + switch ($FIRESOFSTEAM) { + case 1: + mesn; + mesq l("Thanks for travelling with Andrei Sakar to Artis! However, as you see, this town is deserted."); + next; + mesn; + mesq l("Sir Sakar is conducting an exploration on this land. However, you should be able to refine and tweak equipment, as well as crafting your own. Once you're ready, find him at %s of this town.", b(l("the north exit"))); + break; + case 2: + case 3: + case 4: + case 5: + case 6: + mesn; + mesq l("Sir Sakar and adventurers are raiding Dracula's Fortress. It is a crazy place, though, who knows what they'll find? He should be at the north exit of this town."); + break; + case 7: + mesn; + mesq l("Sir Sakar and adventurers are raiding Dracula's Fortress. They found out the Underworld and there is a magic tree. You'll need a %s to use it.", getitemlink(DruidTreeBranch)); + mesc l("They are at north of the town."); + next; + mesn; + if (FIRESOFSTEAM_CD < gettimetick(2)) { + mesq l("Here is one."); + FIRESOFSTEAM_CD=gettimetick(2)+86400; // 24 hours + getitem DruidTreeBranch, 1; + } else { + mesq l("Some other adventurers are looking for it. Please wait %s more.", FuzzyTime(FIRESOFSTEAM_CD)); + } + break; + case 8: + mesn; + mesq l("Sakar can now bring you directly to Asphodel Moors. Scouts found a second town as well, from where the raiders seems to be coming from!"); + mesc l("Andrei Sakar is at the north exit of the town."); + break; + case 9: + mesn; + mesq l("I'm worried. Will everything be alright? Please, lend your help to sir Sakar - no, to the whole world!"); + mes ""; + mesc l("Contact a GM at Migglemire - the small town at center of swamps - to trigger the final showdown."); + mesc l("Alternatively, %s, the World Hero, should also be able to do it.", $MOST_HEROIC$); + break; + } + close; + +OnInit: + .distance=5; + .sex=G_FEMALE; + if ($FIRESOFSTEAM >= 10) + disablenpc .name$; + end; +} + +// UTILITY +029-0,96,110,0 duplicate(Nicholas) Nicholas#FoS NPC_NICHOLAS +029-0,89,75,0 duplicate(Fortiun) Fortiun#FoS NPC_FORTIUN +029-0,68,87,0 duplicate(Tolchi) Tolchi#FoS NPC_RAIJIN_FEMALE_LEGION_ARTIS +029-0,137,75,0 duplicate(Bracco) Bracco#FoS NPC_ORC_SAILOR +029-0,90,130,0 duplicate(Zitoni) Zitoni#FoS NPC_RUMLY +029-0,132,51,0 duplicate(Cynric) Cynric#FoS NPC_LLOYD +029-0,57,55,0 duplicate(Mercenary Trainer) Mercenary Trainer#FoS NPC_REDY_MALE_SWORD +029-0,123,84,0 duplicate(Estard) Estard#FoS NPC_KENTON +//029-0,143,120,0 duplicate(Guild Storage) Guild Storekeeper#FoS NPC_TERRY +029-0,198,34,0 duplicate(Colin) Colin#FoS NPC_MIRAJ +029-0,182,26,0 duplicate(Luca) Luca#FoS NPC_ANSELMO_BR +029-0,200,49,0 duplicate(Mr Saves) Mr Saves#FoS NPC_BLACKALCHEMIST +029-0,179,38,0 duplicate(Trickmaster) Trickmaster#FoS NPC_SITTED_NINJA +029-0,142,33,0 duplicate(Crafting Table) Crafting Box#FoS NPC_NO_SPRITE +029-0,141,51,0 duplicate(Intense Beard) Intense Beard#FoS NPC_BRGUARD_SPEAR +029-0,127,32,0 duplicate(Jhedia) Jhedia#FoS NPC_ELVEN_FEMALE +029-0,58,118,0 duplicate(Neko) Neko#FoS1 NPC_KAYLO + +// 029-1 +029-1,35,88,0 duplicate(Neko) Neko#FoS2 NPC_KAYLO +029-1,29,88,0 script Elora#1 NPC_HUMAN_FEMALE_NOOB,{ + mesn; + mesq l("Good luck out there!"); + .@m=mobcount("029-1", "all"); + mesc l("Dark Forest : %s monster(s) left", fnum(.@m)); + next; + select + l("Thanks!"), + l("Actually, can I return to Artis?"); + mes ""; + closeclientdialog; + if (@menu == 2) + warp "029-0", 90, 31; + close; +OnInit: + .distance=5; + .sex=G_FEMALE; + end; +} + +029-8,37,170,0 duplicate(Neko) Neko#FoS3 NPC_KAYLO + + diff --git a/npc/029-0/event.txt b/npc/029-0/event.txt new file mode 100644 index 0000000..8b565db --- /dev/null +++ b/npc/029-0/event.txt @@ -0,0 +1,330 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Exclusive for Fires of Steam 2021 - When our OVH server datacenter got fire. + +029-0,111,37,0 script Alliance Hero#FoS NPC_HALBERDBARBARIAN,{ + // No one is supposed to be able to reach NPC if event did not happen + if (!$FIRESOFSTEAM) { + disablenpc .name$; + die(); end; + } + mesn; + mesq l("Sooo, the police station of Moubootaur Legends went ablaze. Constable Perry is too busy to monitor illegal operation on this continent."); + next; + mesn; + mesq l("Therefore... Are you perhaps interested in some... illicit goods? %%p"); + next; + select + l("Show me what you got, noob."), + l("Actually - Do you exchange goods even more illegal goods than this?"), + l("Actually - Do you exchange illegal... research results?"); + mes ""; + if (@menu == 1) { + closeclientdialog; + openshop .name$; + close; + } + else if (@menu == 2) { + mesn; + mesq l("Yes I do... I sell %s %s for the small amount of %s GP. Interested? %%%%p", fnum(.mobp), l("Monster Points"), fnum(.mobg)); + if (Zeny < .mobg) close; + next; + if (askyesno() == ASK_NO) close; + if (Zeny < .mobg) { die(); close; } + Zeny-=.mobg; + Mobpt+=.mobp; + mes ""; + mesn; + mesq l("Hehehe... A pleasure doing business with you!"); + } else if (@menu == 3) { + mesn; + mesq l("Yes I do... I sell %s %s for the small amount of %s GP. Interested? %%%%p", fnum(.robp), l("Research Points"), fnum(.robg)); + if (Zeny < .robg) close; + next; + if (askyesno() == ASK_NO) close; + if (Zeny < .robg) { die(); close; } + Zeny-=.robg; + Mobpt+=.robp; + mes ""; + mesn; + mesq l("Hehehe... A pleasure doing business with you!"); + } + close; + +OnRw: + logmes(sprintf("%s - Reward Granted by System Admin", getcharid(3))); + getitem StrangeCoin, 2000; + getitembound FireScroll, 1, 1; + getexp 1000000, 500000; + Mobpt+=1000000; + Zeny+=1000000; + dispbottom l("Jesusalva : \\o/"); + end; + +OnInit: + bindatcmd "steambk", "Alliance Hero#FoS::OnRw", 100, 99, 1; + tradertype(NST_MARKET); + .mobp=rand2(1000, 2500); + .mobg=.mobp*rand2(100, 150)/10; + .robp=rand2(900, 1200); + .robg=.robp*rand2(40, 60);//rand2(80, 110); + .distance=5; + .sex=G_MALE; + + sleep(SHOPWAIT); + sellitem Coal, -1, 50+($FIRESOFSTEAM*3-3); + sellitem LeatherPatch, 800, 45+($FIRESOFSTEAM*3-3); + sellitem RawLog, -1, 40+($FIRESOFSTEAM*3-3); + sellitem WoodenLog, -1, 40+($FIRESOFSTEAM*3-3); + sellitem IronOre, -1, 30+($FIRESOFSTEAM*3-3); + sellitem CopperOre, 1000, 20+($FIRESOFSTEAM*2-2); + sellitem SilverOre, 2000, 14+($FIRESOFSTEAM*2-2); + sellitem GoldOre, 3000, 14+($FIRESOFSTEAM*2-2); + sellitem TinOre, 3600, 15+($FIRESOFSTEAM*2-2); + sellitem LeadOre, 4000, 15+($FIRESOFSTEAM*2-2); + sellitem TitaniumOre, 6000, 9+($FIRESOFSTEAM*2-2); + sellitem IridiumOre, 16000, 6+($FIRESOFSTEAM*2-2); + sellitem PlatinumOre, 24000, 3+($FIRESOFSTEAM*2-2); + sellitem EarthPowder, -1, 3+$FIRESOFSTEAM-1; + sellitem EverburnPowder, 15000, 2+$FIRESOFSTEAM-1; + + sellitem AncientBlueprint, 12500, max(1, 4+$FIRESOFSTEAM/2); + sellitem RustyKnife, -1, 5+$FIRESOFSTEAM-1; + sellitem TrainingWand, -1, 5+$FIRESOFSTEAM-1; + sellitem TrainingBow, -1, 5+$FIRESOFSTEAM-1; + + sellitem AlchemyBlueprintA, -1, 5+$FIRESOFSTEAM-1; + sellitem AlchemyBlueprintB, -1, 4+$FIRESOFSTEAM-1; + sellitem AlchemyBlueprintC, -1, 3+$FIRESOFSTEAM-1; + sellitem AlchemyBlueprintD, -1, 2+$FIRESOFSTEAM-1; + sellitem AlchemyBlueprintE, -1, max(1, 1+$FIRESOFSTEAM/2); + + sellitem EquipmentBlueprintA, -1, 5+$FIRESOFSTEAM-1; + sellitem EquipmentBlueprintB, -1, 4+$FIRESOFSTEAM-1; + sellitem EquipmentBlueprintC, -1, 3+$FIRESOFSTEAM-1; + sellitem EquipmentBlueprintD, -1, 2+$FIRESOFSTEAM-1; + sellitem EquipmentBlueprintE, -1, max(1, 1+$FIRESOFSTEAM/2); + + sellitem ArcmageBoxset, 10000, 4+$FIRESOFSTEAM-1; + sellitem ScholarshipBadge, -1, 3+$FIRESOFSTEAM-1; + sellitem Bullet, 4, 90000+($FIRESOFSTEAM*1000-1000); + sellitem Lifestone, -1, 500+$FIRESOFSTEAM-1; + sellitem Bread, -1, 450+$FIRESOFSTEAM-1; + sellitem Cheese, -1, 300+$FIRESOFSTEAM-1; + sellitem Aquada, -1, 200+$FIRESOFSTEAM-1; + + sellitem WhiteFur, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem Piberries, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem CherryCake, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem LettuceLeaf, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem BugLeg, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem RoastedMaggot, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem Moss, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem AnimalBones, -1, 1+rand2(40)+$FIRESOFSTEAM; + sellitem Milk, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem Mashmallow, -1, 1+rand2(50)+$FIRESOFSTEAM; + sellitem Dragonfruit, 1200, 1+rand2(50)+$FIRESOFSTEAM; + sellitem Root, -1, 40+$FIRESOFSTEAM-1; + sellitem ManaPiouFeathers, -1, 1+rand2(300)+$FIRESOFSTEAM; + + if (!$BETASERVER && !debug) + disablenpc .name$; + end; + +OnClock0001: +OnClock0201: +OnClock0401: +OnClock0601: +OnClock0801: +OnClock1001: +OnClock1201: +OnClock1401: +OnClock1601: +OnClock1801: +OnClock2001: + if ($EVENT$ != "Steam") + end; +OnClock2201: + .mobp=rand2(1000, 2500); + .mobg=.mobp*rand2(100, 150)/10; + .robp=rand2(900, 1200); + .robg=.robp*rand2(40, 60);//rand2(80, 110); + + restoreshopitem Coal, 50+($FIRESOFSTEAM*3-3); + restoreshopitem LeatherPatch, 800, 45+($FIRESOFSTEAM*3-3); + restoreshopitem RawLog, 40+($FIRESOFSTEAM*3-3); + restoreshopitem WoodenLog, 40+($FIRESOFSTEAM*3-3); + restoreshopitem IronOre, 30+($FIRESOFSTEAM*3-3); + restoreshopitem CopperOre, 1000, 20+($FIRESOFSTEAM*2-2); + restoreshopitem SilverOre, 2000, 14+($FIRESOFSTEAM*2-2); + restoreshopitem GoldOre, 3000, 14+($FIRESOFSTEAM*2-2); + restoreshopitem TinOre, 3600, 15+($FIRESOFSTEAM*2-2); + restoreshopitem LeadOre, 4000, 15+($FIRESOFSTEAM*2-2); + restoreshopitem TitaniumOre, 6000, 9+($FIRESOFSTEAM*2-2); + restoreshopitem IridiumOre, 16000, 6+($FIRESOFSTEAM*2-2); + restoreshopitem PlatinumOre, 24000, 3+($FIRESOFSTEAM*2-2); + restoreshopitem EarthPowder, 3+$FIRESOFSTEAM-1; + restoreshopitem EverburnPowder, 15000, 2+$FIRESOFSTEAM-1; + + restoreshopitem AncientBlueprint, 10000, 4+$FIRESOFSTEAM-1; + restoreshopitem RustyKnife, 5+$FIRESOFSTEAM-1; + restoreshopitem TrainingWand, 5+$FIRESOFSTEAM-1; + restoreshopitem TrainingBow, 5+$FIRESOFSTEAM-1; + + restoreshopitem AlchemyBlueprintA, 5+$FIRESOFSTEAM-1; + restoreshopitem AlchemyBlueprintB, 4+$FIRESOFSTEAM-1; + restoreshopitem AlchemyBlueprintC, 3+$FIRESOFSTEAM-1; + restoreshopitem AlchemyBlueprintD, 2+$FIRESOFSTEAM-1; + restoreshopitem AlchemyBlueprintE, 1+$FIRESOFSTEAM-1; + + restoreshopitem EquipmentBlueprintA, 5+$FIRESOFSTEAM-1; + restoreshopitem EquipmentBlueprintB, 4+$FIRESOFSTEAM-1; + restoreshopitem EquipmentBlueprintC, 3+$FIRESOFSTEAM-1; + restoreshopitem EquipmentBlueprintD, 2+$FIRESOFSTEAM-1; + restoreshopitem EquipmentBlueprintE, 1+$FIRESOFSTEAM-1; + + restoreshopitem ArcmageBoxset, 10000, 4+$FIRESOFSTEAM-1; + restoreshopitem ScholarshipBadge, 3+$FIRESOFSTEAM-1; + restoreshopitem Lifestone, 800+$FIRESOFSTEAM-1; + restoreshopitem Bullet, 4, 90000+$FIRESOFSTEAM-1; + restoreshopitem Bread, 750+$FIRESOFSTEAM-1; + restoreshopitem Cheese, 400+$FIRESOFSTEAM-1; + restoreshopitem Aquada, 200+$FIRESOFSTEAM-1; + + restoreshopitem WhiteFur, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem Piberries, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem CherryCake, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem LettuceLeaf, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem BugLeg, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem RoastedMaggot, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem Moss, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem AnimalBones, 1+rand2(40)+$FIRESOFSTEAM; + restoreshopitem Milk, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem Mashmallow, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem Dragonfruit, 1200, 1+rand2(50)+$FIRESOFSTEAM; + restoreshopitem Root, 40+$FIRESOFSTEAM; + restoreshopitem ManaPiouFeathers, 1+rand2(300)+$FIRESOFSTEAM; + end; + +OnMinute02: + if ($EVENT$ != "Steam") end; + donpcevent "Neko::OnClock2359"; + end; +} + + +003-0-2,34,21,0 script Alliance Officer NPC_HALBERDBARBARIAN,{ + mesn; + mesq l("Good %s, %s. The council is not in session right now.", + (is_night() ? l("evening") : l("morning")), + (strcharinfo(0) == $MOST_HEROIC$ ? lg("hero") : lg("peasant"))); + if (($BETASERVER || debug) && BaseLevel < 60 && !#BETA_REVIVE) goto L_PowerUp; + close; + +L_PowerUp: + next; + mesn strcharinfo(0); + mesq l("Could you awake my lost and forsaken potential?"); + next; + mesn; + mesq l("Yes. I can. But this is irreversible. Are you sure you want this?"); + next; + mesc l("Awake lost potential? This will mess with your char data irreversibly, beware."), 1; + if (askyesno() == ASK_NO) close; + inventoryplace Iten, 1, NPCEyes, 4; + + // Chose a stage (NO TTL) + mesc l("Please select where you left off on Main Quest."); + mesc l("The one with a star (*) is advised."); + mesc l("It is NOT advised for new players to skip parts of the Main Quest."), 1; + mesc l("Skipping will FORSAKE rewards for the quest and related; So choose wisely!"); + mes ""; + menuint + "Nard Quest finished", 1, + "Lua Quest finished", 3, + "Airlia Quest finished", 6, + "Librarian Quest finished", 10, + "Blue Sage Quest finished", 12, + "(*) King Gelid Quest complete", 17, + "Lightbringer/Barbara finished", 19; + mes ""; + // Save stage + .@mq = @menuret; + + // IP Blacklist + if (array_find($@IPBLIST$, getcharip()) < 0) + array_push($@IPBLIST$, getcharip()); + + // Level up + freeloop(true); + while (BaseLevel < 60) + getexp NextBaseExp, 100; + freeloop(false); + + // Skip a few quests + setq ShipQuests_Arpan, 5; + if (.@mq > 10) + setq NivalisQuest_BlueSage, 12; + if (.@mq >= 17) { + sk_lvup(AM_REST); + sk_lvup(AM_RESURRECTHOMUN); + sk_lvup(AM_CALLHOMUN); + } + if (.@mq >= 18) { + BARBARA_STATE=any(1, 2, 3); + } + + // Update main quest + if (getq(General_Narrator) < .@mq) + setq General_Narrator, .@mq; + + // Monster points + MPQUEST=true; + if (!Mobpt) + Mobpt+=100000; + + // Magic Power + adddefaultskills(); + if (!MAGIC_LVL) { + sk_lvup(AL_DP); + MAGIC_LVL=1; + } + + // Crafting + if (!CRAFTQUEST) { + sk_lvup(TMW2_CRAFT); + getitembound RecipeBook, 1, 1; + CRAFTQUEST=true; + } + + // Free skills + sk_lvup(TMW2_MANABOMB); + sk_lvup(any(TMW2_FROSTDIVER, TMW2_NAPALMBEAT, TMW2_MAGICSTRIKE, TMW2_METEORSTRIKE, TMW2_FIREARROW, TMW2_BRAWLING, TMW2_FALKONSTRIKE, TMW2_CHARGEDARROW)); + + // Pure awesomeness + getitembound any(StrengthFruit, AgilityFruit, VitalityFruit, IntelligenceFruit, DexterityFruit, LuckFruit), 1, 4; + getitembound any(StrengthFruit, AgilityFruit, VitalityFruit, IntelligenceFruit, DexterityFruit, LuckFruit), 1, 4; + + // Full power + getitembound Wurtzite, 6, 4; + getitembound Bread, 10, 4; + + // Regeneration and misc + getitem StrangeCoin, 100; + Zeny+=50000; + percentheal 100,100; + mesc l("You awake a long forgotten potential, and feel ready to take over the world."); + #BETA_REVIVE=true; + close; + +OnInit: + .distance=4; + /* + if (!$BETASERVER && !debug) + disablenpc .name$; + */ + end; +} + diff --git a/npc/029-0/mobs.txt b/npc/029-0/mobs.txt new file mode 100644 index 0000000..20d69e3 --- /dev/null +++ b/npc/029-0/mobs.txt @@ -0,0 +1,305 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Fires of Steam: The Death of Andrei Sakar + +////////////////////////////////////////// +// Monster Control +- script SteamFire#Ctrl 32767,{ + function SFsetup1; + function SFsetup2; + function SFsetup3; + function SFsetup4; + function SFsetup5; + function SFsetup6; + function SFsetup7; + function SFsetup8; + function SFreset; + end; + +OnInit: + .e1$="SteamFire#Ctrl::OnEvent1"; + .e2$="SteamFire#Ctrl::OnEvent2"; + .e3$="SteamFire#Ctrl::OnEvent3"; + .e4$="SteamFire#Ctrl::OnEvent4"; + .e5$="SteamFire#Ctrl::OnEvent5"; + .e6$="SteamFire#Ctrl::OnEvent6"; + .e7$="SteamFire#Ctrl::OnEvent7"; + .e8$="SteamFire#Ctrl::OnEvent8"; + SFsetup1(); + SFsetup2(); + SFsetup3(); + SFsetup4(); + SFsetup5(); + SFsetup6(); + SFsetup7(); + SFsetup8(); + end; + +function SFspawn { + .@am=max(1, $FIRESOFSTEAM > getarg(8) ? (getarg(6)*4/10) : getarg(6)); + //debugmes "Spawning %d/%d mobs on %s", .@am, getarg(6), getarg(0); + areamonster(getarg(0), getarg(1), getarg(2), getarg(3), getarg(4), + strmobinfo(1, getarg(5)), getarg(5), .@am, + getarg(7)); + if ($FIRESOFSTEAM > getarg(8) && !$@FOS_RESPAWN[getarg(8)]) { + $@FOS_RESPAWN[getarg(8)]=true; + setmapflag(getarg(0), mf_nopenalty); + } + + return; +} + +function SFsetup1 { + SFspawn("029-1", 15, 15, 77, 120, Scar, 60, .e1$, 1); + SFspawn("029-1", 15, 15, 255, 255, Crafty, 320, .e1$, 1); + SFspawn("029-1", 15, 15, 255, 255, GiantMutatedBat, 30, .e1$, 1); + SFspawn("029-1", 15, 15, 255, 255, Scar, 100, .e1$, 1); + SFspawn("029-1", 195, 15, 290, 100, Forain, 54, .e1$, 1); + SFspawn("029-1", 77, 15, 195, 75, GreenDragon, 45, .e1$, 1); + SFspawn("029-1", 75, 75, 205, 100, EliteDuck, 40, .e1$, 1); + SFspawn("029-1", 15, 90, 75, 280, Terranite, 90, .e1$, 1); + SFspawn("029-1", 177, 140, 280, 280, JackO, 80, .e1$, 1); + SFspawn("029-1", 175, 75, 280, 200, RedSkullSlime, 60, .e1$, 1); + SFspawn("029-1", 75, 175, 185, 280, Michel, 60, .e1$, 1); + SFspawn("029-1", 80, 100, 180, 160, CopperSkullSlime, 60, .e1$, 1); + // MAP BOSS + monster("029-1", 147, 153, "Level Boss", MonsterGeneral, 1, .e1$); + return; +} + +function SFsetup2 { + SFspawn("029-2", 20, 28, 51, 33, BlackMamba, 15, .e2$, 2); + SFspawn("029-2", 20, 34, 51, 42, GreenSkullSlime, 20, .e2$, 2); + SFspawn("029-2", 20, 42, 32, 70, Centaur, 15, .e2$, 2); + SFspawn("029-2", 39, 42, 51, 70, TerraniteProtector, 15, .e2$, 2); + SFspawn("029-2", 32, 42, 40, 70, GoboBear, 13, .e2$, 2); + SFspawn("029-2", 20, 20, 35, 70, Moonshroom, 5, .e2$, 2); + SFspawn("029-2", 20, 20, 35, 70, RobinBandit, 5, .e2$, 2); + // MAP BOSS + monster("029-2", 46, 68, "Level Boss", YetiKing, 1, .e2$); + return; +} + +function SFsetup3 { + SFspawn("029-3", 20, 20, 130, 100, DustGatling, 40, .e3$, 3); + SFspawn("029-3", 20, 20, 130, 100, Scar, 50, .e3$, 3); + SFspawn("029-3", 20, 20, 130, 100, Skeleton, 80, .e3$, 3); + SFspawn("029-3", 20, 20, 130, 100, GreenSkullSlime, 20, .e3$, 3); + SFspawn("029-3", 20, 20, 48, 62, Troll, 25, .e3$, 3); + SFspawn("029-3", 20, 62, 53, 100, Michel, 30, .e3$, 3); + SFspawn("029-3", 47, 20, 72, 50, LavaSkullSlime, 25, .e3$, 3); + SFspawn("029-3", 72, 20, 130, 50, ShadowPixie, 18, .e3$, 3); + SFspawn("029-3", 67, 49, 130, 64, GoboBear, 25, .e3$, 3); + SFspawn("029-3", 67, 62, 99, 100, BlackMamba, 30, .e3$, 3); + SFspawn("029-3", 100, 62, 130, 100, JackO, 30, .e3$, 3); + // MAP BOSS + monster("029-3", 82, 89, "Level Boss", FallenKing2, 1, .e3$); + return; +} + +function SFsetup4 { + SFspawn("029-4", 20, 20, 130, 100, DustRifle, 40, .e4$, 4); + SFspawn("029-4", 20, 20, 130, 100, Skeleton, 60, .e4$, 4); + SFspawn("029-4", 20, 20, 130, 100, ArmoredSkeleton, 20, .e4$, 4); + + SFspawn("029-4", 38, 22, 120, 28, FireSkull, 30, .e4$, 4); + SFspawn("029-4", 38, 30, 128, 44, GreenSkullSlime, 30, .e4$, 4); + SFspawn("029-4", 20, 22, 35, 68, BlackSkullSlime, 30, .e4$, 4); + SFspawn("029-4", 37, 44, 128, 60, Reaper, 24, .e4$, 4); + SFspawn("029-4", 88, 60, 128, 85, Jhon, 20, .e4$, 4); + SFspawn("029-4", 20, 69, 60, 101, Mandragora, 20, .e4$, 4); + SFspawn("029-4", 62, 61, 86, 92, JackO, 15, .e4$, 4); // BOSS + SFspawn("029-4", 60, 94, 128, 101, NulityPixie, 15, .e4$, 4); + // MAP BOSS + monster("029-4", 73, 77, "Level Boss", PsiConscience, 1, .e4$); + return; +} + +function SFsetup5 { + SFspawn("029-5", 20, 20, 130, 100, CursedArcher, 40, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, Skeleton, 20, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, ArmoredSkeleton, 60, .e5$, 5); + + SFspawn("029-5", 20, 20, 130, 100, Reaper, 32, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, JackO, 12, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, Mandragora, 24, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, Jhon, 12, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, ShadowTortuga, 1, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, RedSkullSlime, 32, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, EvilScythe, 2, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, Michel, 8, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, SiegeTower, 3, .e5$, 5); + SFspawn("029-5", 20, 20, 130, 100, GreenSlimeMother, 15, .e5$, 5); + // MAP BOSS + SFspawn("029-5", 20, 20, 130, 100, SiegeTower, 3, .e5$, 5); + monster("029-5", 70, 26, "Level Boss", TerraniteKing, 1, .e5$); + return; +} + +function SFsetup6 { + SFspawn("029-6", 20, 20, 180, 100, CursedArcher, 40, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, ArmoredSkeleton, 60, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, Fluffy, 40, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, SilkWorm, 40, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, SiegeTower, 4, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, EliteDuck, 24, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, TerraniteProtector, 14, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, Snail, 7, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, PinkieSuseran, 4, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, Junglefowl, 8, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, Tengu, 12, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, Moubi, 2, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, SuperiorShroom, 2, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, Moonshroom, 12, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, GreenDragon, 14, .e6$, 6); + SFspawn("029-6", 20, 20, 180, 100, GoboBear, 8, .e6$, 6); + // MAP BOSS + monster("029-6", 31+9, 91, "Level Boss", PinkieEmperor, 1, .e6$); + return; +} + +function SFsetup7 { + SFspawn("029-7", 20, 20, 120, 130, Wolvern, 24, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, GoboBear, 20, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, GreenDragon, 8, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, SiegeTower, 8, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, Tengu, 12, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, Forain, 18, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, Golem, 12, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, ShadowTortuga, 8, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, WaterElement, 2, .e7$, 7); + SFspawn("029-7", 20, 20, 120, 130, EvilWisp, 2, .e7$, 7); + // MAP BOSS + monster("029-7", 66, 35, "Level Boss", PanthomLord, 1, .e7$); + return; +} + +function SFsetup8 { + SFspawn("029-8", 20, 20, 240, 220, Assassin, 30, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, DeathCat, 20, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, LogHead, 20, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, RobinBandit, 20, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, GrassSnake, 35, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, EliteDuck, 22, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, BlackMamba, 16, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Centaur, 24, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, GreenSkullSlime, 18, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Yetifly, 6, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Snail, 22, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, PinkieSuseran, 12, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Jhon, 12, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Mandragora, 12, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, PinkieMaximus, 12, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Junglefowl, 12, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Tengu, 11, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Moubi, 11, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, SuperiorShroom, 11, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, Nutcracker, 10, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, SiegeTower, 8, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, GreenhornAbomination, 32, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, ShadowTortuga, 10, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, FireElement, 8, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, WaterElement, 8, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, EarthElement, 8, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, WindElement, 8, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, SacredWisp, 4, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, EvilWisp, 4, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, PanthomWisp, 4, .e8$, 8); + SFspawn("029-8", 20, 20, 240, 220, EpiphanyWisp, 4, .e8$, 8); + // MAP BOSS + monster("029-8", 206, 149, "Level Boss", Tortuga, 1, .e8$); + return; +} + + +// SFreset(map, state) +function SFreset { + .@m$=getarg(0); + .@s=getarg(1); + .@m=mobcount(.@m$, "all"); + if (!.@m) { + if ($FIRESOFSTEAM == .@s) { + $FIRESOFSTEAM+=1; + // 3 hours cooldown (180 min) + if (gettime(GETTIME_YEAR) != 2021) + $FIRESOFSTEAM_CD=gettimetick(2)+10800; + return 2; + } else { + mapannounce(.@m$, "Map cleared!", 0); + sleep(rand2(15000, 30000)); + return 1; + } + } else if (.@m % 25 == 0) { + mapannounce(.@m$, sprintf("%s monster(s) left", fnum(.@m)), 0); + } + return 0; +} + +OnEvent1: + .@f=SFreset("029-1", 1); + if (.@f == 2) + mapannounce("029-1", "Dracula's Castle magical seal has dissipated!", 0); + if (.@f) + SFsetup1(); + end; + +OnEvent2: + .@f=SFreset("029-2", 2); + if (.@f == 2) + mapannounce("029-2", "Map cleared!", 0); + if (.@f) + SFsetup2(); + end; + +OnEvent3: + .@f=SFreset("029-3", 3); + if (.@f == 2) + mapannounce("029-3", "Map cleared!", 0); + if (.@f) + SFsetup3(); + end; + +OnEvent4: + .@f=SFreset("029-4", 4); + if (.@f == 2) + mapannounce("029-4", "Map cleared!", 0); + if (.@f) + SFsetup4(); + end; + +OnEvent5: + .@f=SFreset("029-5", 5); + if (.@f == 2) + mapannounce("029-5", "Map cleared!", 0); + if (.@f) + SFsetup5(); + end; + +OnEvent6: + .@f=SFreset("029-6", 6); + if (.@f == 2) + mapannounce("029-6", "Map cleared! You'll need a Druid Tree Branch to use the tree warp to next area.", 0); + if (.@f) + SFsetup6(); + end; + +OnEvent7: + .@f=SFreset("029-7", 7); + if (.@f == 2) + mapannounce("029-7", "Swamps cleared! Meet Andrei Sakar on the other side!", 0); + if (.@f) + SFsetup7(); + end; + +OnEvent8: + .@f=SFreset("029-8", 8); + if (.@f == 2) + mapannounce("029-8", "Map cleared! Contact a GM in Migglemire Town!", 0); + if (.@f) + SFsetup8(); + end; +} + + diff --git a/npc/029-0/sakar.txt b/npc/029-0/sakar.txt new file mode 100644 index 0000000..06b990f --- /dev/null +++ b/npc/029-0/sakar.txt @@ -0,0 +1,91 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Fires of Steam: The Death of Andrei Sakar + +////////////////////////////////////////// +// Andrei Sakar Instances + +029-8,50,173,0 script Andrei Sakar#FoS298 NPC_ANDREI,{ + if (!(TELEPORTERS & TP_ARTIS)) { + TELEPORTERS=TELEPORTERS|TP_ARTIS; + } + mesn; + mesq l("Do you want to return?"); + next; + select + l("No, thanks."), + l("Yes, I would like to go back to the Swamps."), + l("Yes, I would like to go back to Artis."); + mes ""; + closeclientdialog; + switch (@menu) { + case 2: + cwarp "029-7", 87, 56; + case 3: + cwarp "029-0", 90, 31; + } + close; +OnInit: + .distance=5; + if ($FIRESOFSTEAM >= 10) + setnpcdisplay("Andrei Sakar#FoS298", "Elora#FoS298", NPC_HUMAN_FEMALE_NOOB); + end; +} + +029-0,88,33,0 script Andrei Sakar#FoS290 NPC_ANDREI,{ + function prologue; + if (getq(General_Narrator) < 20) end; // Not authorized + if (!FIRESOFSTEAM_CD && $FIRESOFSTEAM < 10) + prologue(); + mesn; + mesq l("Do you want to advance?"); + next; + select + l("No, thanks."), + l("Yes, I would like to explore!"), + rif($FIRESOFSTEAM >= 8, l("Yes, I would like to go to Asphodel Moors!")); + mes ""; + closeclientdialog; + switch (@menu) { + case 2: + cwarp "029-1", 32, 91; + case 3: + cwarp "029-8", 51, 174; + } + close; + +function prologue { + mesn; + mesq l("Thanks for attending my call. As you can clearly see, the town is deserted; We only have our own staff in this town."); + next; + mesn; + mesq l("They set up shop in the whole town, so if you are in need of upgrading your gear or buying health food, it might be a good idea to pay them a visit."); + next; + mesn; + mesq l("Now, we're hot on the trails of whatever is the reason for the town to be so empty. We blocked this road so the town is not overrun by the monsters outside."); + next; + mesn; + mesq l("We've traced them to the Dark Forest, our goal is to destroy every raider there and defeat whoever is behind this tragedy. All that while keeping an eye open for survivors."); + next; + mesn; + mesq l("After all the monsters in an area are defeated, they'll respawn at once. I know, that's not good, but well, nothing that can be done about it."); + next; + mesn; + mesq l("Anyway, after that I'll use my magic, so everyone can rest after that until I finish, or keep killing the new monsters for experience and drops."); + next; + mesn; + mesq l("Are you ready? There will be a long path ahead of us until this mistery is solved."); + FIRESOFSTEAM_CD=gettimetick(2); + next; + return; +} + +OnInit: + .distance=5; + if ($FIRESOFSTEAM >= 10) + setnpcdisplay("Andrei Sakar#FoS290", "Elora#FoS290", NPC_HUMAN_FEMALE_NOOB); + end; +} + diff --git a/npc/029-0/town.txt b/npc/029-0/town.txt new file mode 100644 index 0000000..5b0780a --- /dev/null +++ b/npc/029-0/town.txt @@ -0,0 +1,26 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Reset LOCATION$ when entering a town + +029-0 mapflag town +029-0 mapflag nopenalty + +029-0,203,85,0 script #LocArtis NPC_HIDDEN,0,3,{ +OnTouch: + EnterTown("Artis"); end; +} + +029-0,204,85,0 script ArtisShip NPC_HIDDEN,0,0,{ + +OnTouch: + EnterTown("Artis"); + goto L_Warp; + +L_Warp: + warp "002-3@"+LOCATION$, 31, 28; + closedialog; + close; +} + diff --git a/npc/029-0/warps.txt b/npc/029-0/warps.txt new file mode 100644 index 0000000..05857ad --- /dev/null +++ b/npc/029-0/warps.txt @@ -0,0 +1,130 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Fires of Steam: The Death of Andrei Sakar + +////////////////////////////////////////// +// Warp Control + +// FiresOfSteam_Warp(ID) +function script FiresOfSteam_Warp { + if ($FIRESOFSTEAM < getarg(0)) { + dispbottom l("Monsters remaining: %s", fnum(mobcount(getmap(), "all"))); + end; + } + if ($FIRESOFSTEAM > getarg(0)) + return true; + if ($FIRESOFSTEAM_CD > gettimetick(2)) { + dispbottom l("Time left until warp can be used: %s", FuzzyTime($FIRESOFSTEAM_CD)); + end; + } + return false; +} + +////////////////////////////////////////// +029-1,147,153,0 script #291to292 NPC_HIDDEN,1,0,{ + end; +OnTouch: + if (FiresOfSteam_Warp(2)) goto L_Warp; + // Additional conditions here + if ($FIRESOFSTEAM[1] > 0 || + $FIRESOFSTEAM[2] > 0 || + $FIRESOFSTEAM[3] > 0 || + $FIRESOFSTEAM[4] > 0 || + $FIRESOFSTEAM[5] > 0) { + dispbottom l("A magic barrier prevents you from enterering. Maybe it is a good idea to shut down the pentagrams first?"); + end; + } + // Fallthrough +L_Warp: + warp "029-2", 35, 70; + end; +} + +029-2,35,38,0 script #292to293 NPC_HIDDEN,3,0,{ + end; +OnTouch: + if (FiresOfSteam_Warp(3)) goto L_Warp; + // Fallthrough +L_Warp: + warp "029-3", 25, 68; + end; +} + +029-3,126,99,0 script #293to294 NPC_HIDDEN,3,0,{ + end; +OnTouch: + if (FiresOfSteam_Warp(4)) goto L_Warp; + // Fallthrough +L_Warp: + warp "029-4", 125, 18; + end; +} + +029-4,21,98,0 script #294to295 NPC_HIDDEN,3,0,{ + end; +OnTouch: + if (FiresOfSteam_Warp(5)) goto L_Warp; + // Fallthrough +L_Warp: + warp "029-5", 125, 18; + end; +} + +029-5,70,25,0 script #295to296 NPC_HIDDEN,1,0,{ + end; +OnTouch: + if (FiresOfSteam_Warp(6)) goto L_Warp; + // Fallthrough +L_Warp: + warp "029-6", 134+9, 31; + end; +} + +// 27+9 = 36 +029-6,36,90,0 script Great Tree#296to297 NPC_NO_SPRITE,{ + if (FiresOfSteam_Warp(7)) goto L_Warp; + // Additional conditions here + mes l("Apparently, you'll need to use a %s to get this to work!", getitemlink(DruidTreeBranch)); + if (!countitem(DruidTreeBranch)) close; + next; + if (askyesno() == ASK_NO) close; + closeclientdialog; + delitem DruidTreeBranch, 1; + // Fallthrough +L_Warp: + warp "029-7", 96, 121; + end; + +OnInit: + .distance=3; + end; +} + +029-7,80,49,0 script Dead Tree#297to298 NPC_NO_SPRITE,{ + if (FiresOfSteam_Warp(8)) goto L_Warp; + // Additional conditions here (TODO) + // Fallthrough +L_Warp: + warp "029-8", 51, 174; + end; + +OnInit: + .distance=3; + end; +} + +029-8,174,77,0 script #298to299 NPC_HIDDEN,1,0,{ + end; +OnTouch: + if (FiresOfSteam_Warp(9)) goto L_Warp; + // Additional conditions here (TODO) + // Fallthrough +L_Warp: + warp "029-9", 96, 48; + end; +} + +029-9 mapflag zone MMO + diff --git a/npc/029-1/_import.txt b/npc/029-1/_import.txt new file mode 100644 index 0000000..5822170 --- /dev/null +++ b/npc/029-1/_import.txt @@ -0,0 +1,3 @@ +// Map 029-1: Dark Forest +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-1/pentagram.txt", diff --git a/npc/029-1/pentagram.txt b/npc/029-1/pentagram.txt new file mode 100644 index 0000000..261b063 --- /dev/null +++ b/npc/029-1/pentagram.txt @@ -0,0 +1,74 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Fires of Steam: The Death of Andrei Sakar + +////////////////////////////////////////// +// Pentagram Core + +029-1,35,32,0 script Pentagram#FoS_1 NPC_NO_SPRITE,{ + .@n$=strnpcinfo(0, "_0"); + explode(.@ni$, .@n$, "_"); + .@id=atoi(.@ni$[1]); + debugmes "ID %d", .@id; + if (.@id < 1) end; + if ($FIRESOFSTEAM[.@id] < 1) end; + + mesn; + mesq l("Power remaining: %s", fnum($FIRESOFSTEAM[.@id])); + next; + mesc l("Use items to drain the pentagram. Final result is affected by int!"); + mesc l("WARNING: Monsters will appear!"), 1; + select + l("Cancel"), + l("Dark Crystal (5 power)"), + l("Terranite Ore (3 power)"), + l("Coal (2 power)"), + l("Cotton Cloth (1 power)"); + mes ""; + switch (@menu) { + case 2: + .@val=5;.@it=DarkCrystal; + break; + case 3: + .@val=3;.@it=TerraniteOre; + break; + case 4: + .@val=2;.@it=Coal; + break; + case 5: + .@val=1;.@it=CottonCloth; + break; + default: + close; + } + + closeclientdialog; + if (!countitem(.@it)) end; + .@val*=countitem(.@it); + .@val*=(readparam2(bInt)+100)/100; // Each int gives +1% + delitem .@it, countitem(.@it); + $FIRESOFSTEAM[.@id]-=.@val; + getexp 0, .@val*2; + // TODO: Check & unlock + if ($FIRESOFSTEAM[.@id] < 1) + disablenpc .name$; + // Create monsters based on effectivity + areamonster("029-1", .x-3, .y-3, .x+3, .y+3, strmobinfo(1, MagicGoblin), MagicGoblin, (.@val/15)+1, "SteamFire#Ctrl::OnEvent1"); + dispbottom l("Power remaining: %s", fnum($FIRESOFSTEAM[.@id])); + close; + +OnInit: + .distance=3; + end; +} + +// Now we duplicate +029-1,258,39,0 duplicate(Pentagram#FoS_1) Pentagram#FoS_2 NPC_NO_SPRITE +029-1,113,148,0 duplicate(Pentagram#FoS_1) Pentagram#FoS_3 NPC_NO_SPRITE +029-1,229,188,0 duplicate(Pentagram#FoS_1) Pentagram#FoS_4 NPC_NO_SPRITE +029-1,38,257,0 duplicate(Pentagram#FoS_1) Pentagram#FoS_5 NPC_NO_SPRITE + + + diff --git a/npc/029-2/_import.txt b/npc/029-2/_import.txt new file mode 100644 index 0000000..4d63c90 --- /dev/null +++ b/npc/029-2/_import.txt @@ -0,0 +1,3 @@ +// Map 029-2: Graveyard Indoor +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-2/_warps.txt", diff --git a/npc/029-2/_warps.txt b/npc/029-2/_warps.txt new file mode 100644 index 0000000..0704802 --- /dev/null +++ b/npc/029-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-2: Graveyard Indoor warps +029-2,36,71,0 warp #029-2_36_71 5,0,029-1,147,154 diff --git a/npc/029-3/_import.txt b/npc/029-3/_import.txt new file mode 100644 index 0000000..90f23c5 --- /dev/null +++ b/npc/029-3/_import.txt @@ -0,0 +1,3 @@ +// Map 029-3: Crypt Basement +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-3/_warps.txt", diff --git a/npc/029-3/_warps.txt b/npc/029-3/_warps.txt new file mode 100644 index 0000000..7dd5e91 --- /dev/null +++ b/npc/029-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-3: Crypt Basement warps +029-3,26,67,0 warp #029-3_26_67 3,0,029-2,35,37 diff --git a/npc/029-4/_import.txt b/npc/029-4/_import.txt new file mode 100644 index 0000000..bc557bc --- /dev/null +++ b/npc/029-4/_import.txt @@ -0,0 +1,3 @@ +// Map 029-4: Crypt Sub-Basement One +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-4/_warps.txt", diff --git a/npc/029-4/_warps.txt b/npc/029-4/_warps.txt new file mode 100644 index 0000000..ac33c43 --- /dev/null +++ b/npc/029-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-4: Crypt Sub-Basement One warps +029-4,126,17,0 warp #029-4_126_17 3,0,029-3,126,98 diff --git a/npc/029-5/_config.txt b/npc/029-5/_config.txt new file mode 100644 index 0000000..99e975a --- /dev/null +++ b/npc/029-5/_config.txt @@ -0,0 +1,16 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-5: Crypt Sub-Basement Two conf + +029-5,126,18,0 script #029-5_126_18 NPC_HIDDEN,3,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnSick"; + end; +} + +029-5,71,26,0 script #029-5_71_26 NPC_HIDDEN,3,0,{ + end; +OnTouch: + doevent "#DungeonCore::OnSick"; + end; +} diff --git a/npc/029-5/_import.txt b/npc/029-5/_import.txt new file mode 100644 index 0000000..43d51fb --- /dev/null +++ b/npc/029-5/_import.txt @@ -0,0 +1,4 @@ +// Map 029-5: Crypt Sub-Basement Two +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-5/_config.txt", +"npc/029-5/_warps.txt", diff --git a/npc/029-5/_warps.txt b/npc/029-5/_warps.txt new file mode 100644 index 0000000..4523acb --- /dev/null +++ b/npc/029-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-5: Crypt Sub-Basement Two warps +029-5,126,17,0 warp #029-5_126_17 3,0,029-4,21,97 diff --git a/npc/029-6/_import.txt b/npc/029-6/_import.txt new file mode 100644 index 0000000..8b480e2 --- /dev/null +++ b/npc/029-6/_import.txt @@ -0,0 +1,3 @@ +// Map 029-6: Somewhere Strange +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-6/_warps.txt", diff --git a/npc/029-6/_warps.txt b/npc/029-6/_warps.txt new file mode 100644 index 0000000..bf9abfa --- /dev/null +++ b/npc/029-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-6: Somewhere Strange warps +029-6,143,30,0 warp #029-6_143_30 0,0,029-5,70,26 diff --git a/npc/029-7/_import.txt b/npc/029-7/_import.txt new file mode 100644 index 0000000..7393f71 --- /dev/null +++ b/npc/029-7/_import.txt @@ -0,0 +1,3 @@ +// Map 029-7: Ruined Swamps +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-7/_warps.txt", diff --git a/npc/029-7/_warps.txt b/npc/029-7/_warps.txt new file mode 100644 index 0000000..e80a505 --- /dev/null +++ b/npc/029-7/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-7: Ruined Swamps warps +029-7,105,124,0 warp #029-7_105_124 0,0,029-6,27,92 diff --git a/npc/029-8/_import.txt b/npc/029-8/_import.txt new file mode 100644 index 0000000..02bf49b --- /dev/null +++ b/npc/029-8/_import.txt @@ -0,0 +1,2 @@ +// Map 029-8: Asphodel Moors +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/029-9/_import.txt b/npc/029-9/_import.txt new file mode 100644 index 0000000..f93728f --- /dev/null +++ b/npc/029-9/_import.txt @@ -0,0 +1,5 @@ +// Map 029-9: Woodland mining camp +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/029-9/_mobs.txt", +"npc/029-9/_warps.txt", +"npc/029-9/boss.txt", diff --git a/npc/029-9/_mobs.txt b/npc/029-9/_mobs.txt new file mode 100644 index 0000000..46f1c8d --- /dev/null +++ b/npc/029-9/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-9: Woodland mining camp mobs +029-9,129,34,5,7 monster Mana Piou 1155,8,35000,270000 diff --git a/npc/029-9/_warps.txt b/npc/029-9/_warps.txt new file mode 100644 index 0000000..0cba23e --- /dev/null +++ b/npc/029-9/_warps.txt @@ -0,0 +1,23 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 029-9: Woodland mining camp warps +029-9,97,49,0 warp #029-9_97_49 5,0,029-8,174,78 +029-9,103,48,0 script #029-9_103_48 NPC_HIDDEN,1,0,{ + end; +OnTouch: + slide 136,40; end; +} +029-9,91,26,0 script #029-9_91_26 NPC_HIDDEN,1,0,{ + end; +OnTouch: + slide 34,24; end; +} +029-9,34,23,0 script #029-9_34_23 NPC_HIDDEN,1,0,{ + end; +OnTouch: + slide 90,25; end; +} +029-9,137,41,0 script #029-9_137_41 NPC_HIDDEN,1,0,{ + end; +OnTouch: + slide 102,47; end; +} diff --git a/npc/029-9/boss.txt b/npc/029-9/boss.txt new file mode 100644 index 0000000..86e27b6 --- /dev/null +++ b/npc/029-9/boss.txt @@ -0,0 +1,555 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Fires of Steam: The Death of Andrei Sakar +/* ***************************************** +$@FIRESOFSTEAM_BOSS +0 = Off; 1 = Moubootaur summoning; 2 = Moubootaur summoned +$@FIRESOFSTEAM_DIFF +Level of difficulty setting (100/130/160/190/220) +***************************************** */ + +////////////////////////////////////////// +// Boss Room +029-9,96,22,0 script Book#FoS NPC_NO_SPRITE,{ + if (!is_staff() && strcharinfo(0) != $MOST_HEROIC$) end; + if ($@FIRESOFSTEAM_BOSS) end; + select + l("Ignore this book"), + l("Begin classic mode"), + rif(is_staff() && $FIRESOFSTEAM >= 10, l("Skip prologue")); + mes ""; + $@FIRESOFSTEAM_BOSS=(@menu-1); + if ($@FIRESOFSTEAM_BOSS == 0) close; + select + l("Easy Mode"), + rif($FIRESOFSTEAM >= 10, l("Crazy Mode")), + rif($FIRESOFSTEAM >= 10, l("Cadis Mode")), + rif($FIRESOFSTEAM >= 10, l("Crazy Cadis Ultimate Pandorica Mode")), + rif($FIRESOFSTEAM >= 10, l("Portable Apocalypse")); + mes ""; + $@FIRESOFSTEAM_DIFF=70+(@menu*30); + switch (@menu) { + case 1: .@d$="##2Easy"; break; + case 2: .@d$="##3Crazy"; break; + case 3: .@d$="##5Cadis"; break; + case 4: .@d$="##6Crazy Cadis Ultimate Pandorica"; break; + case 5: .@d$="##1Portable Apocalypse"; break; + default: .@d$="Unknown"; + } + mapannounce("029-9", "Difficulty Selected: ##B"+.@d$+"##b", 0); + changemusic("029-9", "mythica.ogg"); + closeclientdialog; + // Dispose of the GM + warp "029-8", 175, 80; + sleep(200); + // PC no longer attached + // Start the event + mapwarp("029-9", "029-9", 96, 41); + sleep(200); + .@c = getunits(BL_PC|BL_MER|BL_HOM, .@players, MAX_CYCLE_PC, "029-9"); + for (.@i = 0; .@i < .@c; .@i++) { + sc_start(SC_STUN, 18000, 1, 10000, + SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@players[.@i]); + } + sleep(100); + // Cleanup previous data (if any) + $@FOS_ISB=0; + $@FOS_MOB=0; + $@FOS_AND=0; + .Support1=0; + .Support2=0; + .Support3=0; + .Support4=0; + .Support5=0; + // Lockdown + disablenpc "#029-9_97_49"; // Exit + disablenpc "#029-9_103_48"; // To Storage + disablenpc "#029-9_91_26"; // To Moubootaur + disablenpc "#029-9_34_23"; // From Moubootaur + disablenpc "#029-9_137_41"; // From Storage + // Summon Andrei, and handle prologue skips + $@FOS_AND=monster("029-9", 96, 35, "Andrei Sakar", AndreiSakar, 1); + if ($@FIRESOFSTEAM_BOSS == 2) goto OnProlEnd; + // Spawn Isbamuth and his support + $@FOS_ISB=monster("029-9", 96, 32, "Isbamuth", Isbamuth, 1, "Book#FoS::OnProlEnd"); + .Support1=monster("029-9", 95, 31, "Hooded Assassin", HoodedAssassin, 1); + .Support2=monster("029-9", 97, 31, "Hooded Assassin", HoodedAssassin, 1); + .Support3=monster("029-9", 95, 27, "Assassin", Assassin, 1); + .Support4=monster("029-9", 96, 27, "Assassin", Assassin, 1); + .Support5=monster("029-9", 97, 27, "Assassin", Assassin, 1); + // Freeze everyone! + sc_start(SC_STUN, 18000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, $@FOS_AND); + sc_start(SC_STUN, 18000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, $@FOS_ISB); + sc_start(SC_STUN, 18000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .Support1); + sc_start(SC_STUN, 18000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .Support2); + sc_start(SC_STUN, 18000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .Support3); + sc_start(SC_STUN, 18000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .Support4); + sc_start(SC_STUN, 18000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .Support5); + // Boost Isbamuth stats + .@avg=$@FIRESOFSTEAM_DIFF; + // Reconfigure the monster + setunitdata($@FOS_ISB, UDT_LEVEL, .@avg); + setunitdata($@FOS_ISB, UDT_STR, 1+.@avg/2); + setunitdata($@FOS_ISB, UDT_AGI, 1+.@avg/2); + setunitdata($@FOS_ISB, UDT_VIT, 1+.@avg/2); + setunitdata($@FOS_ISB, UDT_INT, 1+.@avg/2); + setunitdata($@FOS_ISB, UDT_DEX, 1+.@avg/2); + setunitdata($@FOS_ISB, UDT_LUK, 1+.@avg/2); + setunitdata($@FOS_ISB, UDT_ADELAY, 1072); + setunitdata($@FOS_ISB, UDT_ATKRANGE, 4); + // Battle Status + setunitdata($@FOS_ISB, UDT_MAXHP, .@avg*700); + setunitdata($@FOS_ISB, UDT_HP, .@avg*700); + setunitdata($@FOS_ISB, UDT_ATKMIN, .@avg*6); + setunitdata($@FOS_ISB, UDT_ATKMAX, .@avg*8); + setunitdata($@FOS_ISB, UDT_DEF, 4+.@avg); + setunitdata($@FOS_ISB, UDT_MDEF, 1+.@avg); + setunitdata($@FOS_ISB, UDT_HIT, .@avg*12); // Advised: x18 + setunitdata($@FOS_ISB, UDT_FLEE, .@avg*4); // Advised: x5 + setunitdata($@FOS_ISB, UDT_CRIT, 20+.@avg); + // Reconfigure the AI + .@opt=getunitdata($@FOS_ISB, UDT_MODE); + // Disable looting + if (.@opt & MD_LOOTER) + .@opt=.@opt^MD_LOOTER; + // Add knockback immunity + .@opt=.@opt|MD_NOKNOCKBACK; + // Mark as boss + .@opt=.@opt|MD_BOSS; + // Mark as aggressive + .@opt=.@opt|MD_AGGRESSIVE; + .@opt=.@opt|MD_ANGRY; + // Make it crazy + .@opt=.@opt|MD_RANDOMTARGET; + setunitdata($@FOS_ISB, UDT_MODE, .@opt); + + // Begin Isbamuth's monologue + unittalk($@FOS_ISB, "You are too late! I did it!! Muahahaha!"); + sleep(1000); + unittalk(.Support1, "Yeah, you did it!"); + sleep(400); + unittalk(.Support2, "Yeah, you are the best!"); + sleep(600); + unittalk($@FOS_ISB, "Silence!"); + sleep(1000); + unittalk($@FOS_ISB, "You did well tracking me down... However the rite is almost complete."); + sleep(3000); + unittalk($@FOS_ISB, "The power of the Moubootaur will be mine..."); + sleep(2000); + unittalk($@FOS_ISB, "And you no longer can stop me!! Muahahahaha!"); + sleep(1000); + unittalk(.Support1, "Yeah, that's our boss!"); + sleep(600); + unittalk(.Support2, "Yeah, none is like boss!"); + sleep(400); + unittalk($@FOS_AND, "Your evil plans shall never prevail, Isbamuth!"); + sleep(2500); + unittalk($@FOS_AND, "Adventurers! I'll go ahead, you shall buy me time!"); + maptimer2("029-9", 200, "Book#FoS::OnMFAndreiMagic"); + sleep(500); + sc_end(SC_STUN, $@FOS_AND); + unitwalk($@FOS_AND, 96, 24); + sleep(1000); + unitwalk($@FOS_AND, 96, 24); + sleep(1000); + unittalk($@FOS_AND, "I'm sure Isbamuth will be no match for all of you!"); + unitwalk($@FOS_AND, 90, 26); + sleep(1500); + unitwarp($@FOS_AND, "029-9", 39, 21); + unittalk($@FOS_ISB, "Hahaha... YOU THINK YOU CAN DEFEAT ME??"); + sc_start(SC_STUN, INT_MAX, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, $@FOS_AND); + sleep(1000); + unittalk($@FOS_ISB, "We will see! Engarde!!"); + initnpctimer; + changemusic("029-9", "let_the_battles_begin.ogg"); + end; + +////////////// Prologue End ////////////// +OnProlEnd: + if (playerattached()) + getexp 200000, 50000; + changemusic("029-9", "steam.ogg"); + mapannounce("029-9", "##1##BA huge sound is heard from the basement.", 0); + $@FIRESOFSTEAM_BOSS=2; + enablenpc "#029-9_91_26"; // To Moubootaur + // Cleanup previous data (if any) + $@FOS_ISB=0; + $@FOS_MOB=0; + .Support1=0; + .Support2=0; + .Support3=0; + .Support4=0; + .Support5=0; + maptimer2("029-9", 10000, "Book#FoS::OnMFMark"); + maptimer2("029-9", 18000, "Book#FoS::OnMFScene"); + // Ensure Andrei Sakar is at his place and stunned + unitwarp($@FOS_AND, "029-9", 39, 21); + sc_start(SC_STUN, INT_MAX, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, $@FOS_AND); + // Bring on the Gemini Assassins + .Support1=monster("029-9", 38, 21, "Valia", Sagratha, 1); + .Support2=monster("029-9", 40, 21, "Luvia", Luvia, 1); + sc_start(SC_STUN, INT_MAX, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .Support1); + sc_start(SC_STUN, INT_MAX, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .Support2); + setunitdata($@FOS_AND, UDT_MAXHP, 999999); + setunitdata($@FOS_AND, UDT_HP, 999999); + setunitdata(.Support1, UDT_MAXHP, 999999); + setunitdata(.Support1, UDT_HP, 999999); + setunitdata(.Support2, UDT_MAXHP, 999999); + setunitdata(.Support2, UDT_HP, 999999); + sleep(18000+100); // Wait for cutscene to start + latebloomers + .@c = getunits(BL_PC|BL_MER|BL_HOM, .@players, MAX_CYCLE_PC, "029-9"); + for (.@i = 0; .@i < .@c; .@i++) { + sc_start(SC_STUN, 18000, 1, 10000, + SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@players[.@i]); + } + sleep(100); + ////////////////////////////////////////////////////////////// + unittalk(.Support1, "...I'm sorry!"); + sleep(1000); + unittalk(.Support2, "...But Isbamuth told us to kill you!"); + sleep(1000); + unittalk(.Support1, "...Die for us!"); + sleep(1000); + unitkill($@FOS_AND); // DEAD + $@FOS_AND=0; + maptimer2("029-9", 3000, "Book#FoS::OnMFShake"); + sleep(3200); // Dramatic Silence + mapannounce("029-9", "##1##BGRRRRRRRRR!!!!", 0); + sleep(800); + mapannounce("029-9", "##1##BHUMANS!!!!", 0); + sleep(2000); + unittalk(.Support2, "Sister, we should go."); + sleep(3000); + unittalk(.Support1, "...Yes, sister, let's go!"); + sleep(3000); + unitwarp(.Support1, "029-9", 130, 32); + unitwarp(.Support2, "029-9", 131, 32); + sleep(1000); // Dramatic Silence + unitkill(.Support1); + unitkill(.Support2); + $@FOS_MOB=monster("029-9", 39, 36, "Moubootaur (Sealed)", MobMoubootaur, 1, "Book#FoS::OnEventEnd"); + sc_start(SC_STUN, 31000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, $@FOS_MOB); + sleep(1000); // Dramatic Silence + unittalk($@FOS_MOB, "##1Who dares awake me from my slumber..."); + sleep(3000); + unittalk($@FOS_MOB, "##1It was just a few nights since I turned a nearby town in Mouboos for their insolence..."); + sleep(5000); + maptimer2("029-9", 2000, "Book#FoS::OnMFShake"); + sleep(2000); + unittalk($@FOS_MOB, "##1AND YOU DARE TO NOT LEARN YOUR LESSON?? %%e"); + sleep(2000); + unittalk($@FOS_MOB, "##1USELESS, UNWORTHY CREATURES!! %%e"); + sleep(6000); + unittalk($@FOS_MOB, "##1I SHALL TURN THIS WHOLE WORLD INTO MOUBOOS..."); + sleep(6000); + unittalk($@FOS_MOB, "##1THE PROPHECY... OF MY RETURN... IS FULFILLED AT LEAST!!"); + .@avg=$@FIRESOFSTEAM_DIFF*13/10; // Moubootaur: +30% bonus (+30~60 lvls) + // Reconfigure the monster (otherwise it is night immortal) + setunitdata($@FOS_MOB, UDT_LEVEL, .@avg); + setunitdata($@FOS_MOB, UDT_STR, 1+.@avg*3/4); + setunitdata($@FOS_MOB, UDT_AGI, 1+.@avg*3/4); + setunitdata($@FOS_MOB, UDT_VIT, 1+.@avg*3/4); + setunitdata($@FOS_MOB, UDT_INT, 1+.@avg*3/4); + setunitdata($@FOS_MOB, UDT_DEX, 1+.@avg*3/4); + setunitdata($@FOS_MOB, UDT_LUK, 1+.@avg*3/4); + setunitdata($@FOS_MOB, UDT_ADELAY, 1472); + setunitdata($@FOS_MOB, UDT_ATKRANGE, 4); + // Battle Status + setunitdata($@FOS_MOB, UDT_MAXHP, .@avg*1350); + setunitdata($@FOS_MOB, UDT_HP, .@avg*1350); + setunitdata($@FOS_MOB, UDT_ATKMIN, .@avg*6); + setunitdata($@FOS_MOB, UDT_ATKMAX, .@avg*8); + setunitdata($@FOS_MOB, UDT_DEF, 12+.@avg*5/3); + setunitdata($@FOS_MOB, UDT_MDEF, 1+.@avg); + setunitdata($@FOS_MOB, UDT_HIT, .@avg*18); // Advised: x18 + setunitdata($@FOS_MOB, UDT_FLEE, .@avg*4); // Advised: x5 + setunitdata($@FOS_MOB, UDT_CRIT, 60+.@avg*4/3); + // Reconfigure the AI + .@opt=getunitdata($@FOS_MOB, UDT_MODE); + // Disable looting + if (.@opt & MD_LOOTER) + .@opt=.@opt^MD_LOOTER; + // Add knockback immunity + .@opt=.@opt|MD_NOKNOCKBACK; + // Mark as boss + .@opt=.@opt|MD_BOSS; + // Mark as aggressive + .@opt=.@opt|MD_AGGRESSIVE; + .@opt=.@opt|MD_ANGRY; + // Make it more op + .@opt=.@opt|MD_DETECTOR; + .@opt=.@opt|MD_CASTSENSOR_CHASE; + .@opt=.@opt|MD_CASTSENSOR_IDLE; + .@opt=.@opt|MD_CHANGECHASE; + .@opt=.@opt|MD_CHANGETARGET_MELEE; + .@opt=.@opt|MD_CHANGETARGET_CHASE; + setunitdata($@FOS_MOB, UDT_MODE, .@opt); + sleep(1000); + mapannounce("029-9", "##1##BON YOUR MARKS...", 0); + sleep(2000); + mapannounce("029-9", "##1##B3...", 0); + sleep(1000); + mapannounce("029-9", "##1##B2...", 0); + sleep(1000); + mapannounce("029-9", "##1##B1...", 0); + sleep(1000); + mapannounce("029-9", "##1##BBEGIN!!", 0); + .Support3=monster("029-9", 25, 29, "Magic Commander", Moubi, 1); + .Support4=monster("029-9", 53, 29, "Army Commander", BloodyMouboo, 1); + .Support5=monster("029-9", 39, 45, "Mouboo Governor", AlphaMouboo, 1); + setunitdata(.Support3, UDT_MAXHP, .@avg*300); + setunitdata(.Support3, UDT_HP, .@avg*300); + setunitdata(.Support4, UDT_MAXHP, .@avg*300); + setunitdata(.Support4, UDT_HP, .@avg*300); + setunitdata(.Support5, UDT_MAXHP, .@avg*300); + setunitdata(.Support5, UDT_HP, .@avg*300); + setunitdata(.Support3, UDT_RACE, RC_Legendary); + setunitdata(.Support4, UDT_RACE, RC_Legendary); + setunitdata(.Support5, UDT_RACE, RC_Legendary); + initnpctimer; + changemusic("029-9", "Arabesque.ogg"); + end; + +////////////// Player's Victory ////////////// +OnEventEnd: + stopnpctimer; + changemusic("029-9", "Misty_Shrine.ogg"); + enablenpc "#029-9_97_49"; // Exit + enablenpc "#029-9_103_48"; // To Storage + enablenpc "#029-9_34_23"; // From Moubootaur + enablenpc "#029-9_137_41"; // From Storage + maptimer2("029-9", 10, "Book#FoS::OnMFSurvive"); + mapannounce("029-9", "Moubootaur : ##1##BThis is not the place...", 0); + sleep(2000); + mapannounce("029-9", "Moubootaur : ##1##BMy powers are weak here...", 0); + sleep(2000); + mapannounce("029-9", "Moubootaur : ##1##BHowever... I know where I must go.", 0); + sleep(3000); + mapannounce("029-9", "Moubootaur : ##1##BThe prophecy... LIVES!", 0); + sleep(3000); + // XXX Reward cycle XXX + // This can be slow, beware + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + if ($FIRESOFSTEAM >= 10) { + getexp 50000+($@FIRESOFSTEAM_DIFF*10), 5000+$@FIRESOFSTEAM_DIFF; + } else { + getexp 1000000, 250000; + getitem StrangeCoin, 200; + Mobpt+=500000; + Zeny+=250000; + } + dispbottom l("Congratulations! You've received participation rewards due to the Moubootaur Death."); + detachrid(); + } + if ($FIRESOFSTEAM < 10) { + kamibroadcast("The Moubootaur has been awakened. The gears of destiny are on motion.", "Jesusalva"); + sleep(5000); + kamibroadcast("Great calamity shall soon befall upon this world.", "Jesusalva"); + sleep(5000); + kamibroadcast("But if we all fight together, we might have a chance!", "Saulc"); + sleep(5000); + kamibroadcast("At due time... The Secret of Mana will be revealed.", "Jesusalva"); + sleep(5000); + kamibroadcast("May the apocalypse begin: The revelation of this world!", "Jesusalva"); + kamibroadcast("MOUBOOTAUR LEGENDS, FIFTH ACT - THE WORLD'S CURSE"); + $FIRESOFSTEAM=10; + $GAME_STORYLINE=4; + $MANA_BLVL-=5; // Set level to 15~25 + $MANA_JLVL-=5; // Set job level to 10 + $MANA_BINT-=10; // Drop min. int for minimum magic + disablenpc "Andrei Sakar"; + setnpcdisplay("Andrei Sakar#FoS290", "Elora#FoS290", NPC_HUMAN_FEMALE_NOOB); + setnpcdisplay("Andrei Sakar#FoS298", "Elora#FoS298", NPC_HUMAN_FEMALE_NOOB); + disablenpc "Elora"; + } + $@FIRESOFSTEAM_BOSS=0; + $@FIRESOFSTEAM_DIFF=0; + end; + +////////////// Map Broadcast ////////////// +OnMFAndreiMagic: + .@stat=max(1, 5-((100-$@FIRESOFSTEAM_DIFF)/30)); + dispbottom col(l("Enemies stunned!"), 3); + dispbottom col(l("All Stats temporarily raised!"), 2); + dispbottom l("Andrei Sakar used magic: %s", b(l("Triumph of the Eternals"))); + sc_start(SC_INCALLSTATUS, .boostime, .@stat, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK); + end; + +OnMFMark: + dispbottom col(l("On your marks..."), 1); + end; + +OnMFScene: + if (!isin("029-9", 20, 20, 60, 50)) + warp "029-9", rand2(33,34), rand2(24,26); + end; + +OnMFShake: + sshake(); + closeclientdialog; + sleep2(1000); + sshake(); + sshake(); + closeclientdialog; + end; + +OnMFDispose: + if (ispcdead() && getq(General_Narrator) >= 20) warp("025-2", 100, 27); + end; + +OnMFSurvive: + dispbottom l("Mission accomplished. Well played!"); + getitem StrangeCoin, max(1, 1+(($@FIRESOFSTEAM_DIFF-100)/30)); + if (!MOUBOOTAUR_WINNER) + MOUBOOTAUR_WINNER=gettimetick(2); + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +////////////// Heartbeat ////////////// +OnTimer15000: + maptimer2("029-9", 10, "Book#FoS::OnMFDispose"); + sleep(30); + if (getmapusers("029-9") < 1) goto L_Defeat; + // Main cycle + if ($@FIRESOFSTEAM_BOSS == 1) { + monster("029-9", 102, 47, "Assassin", Assassin, 1); + } else if ($@FIRESOFSTEAM_BOSS == 2) { + // Handle Moubootaur Magic + // But only if .Support3 was defined (control variable) + if (!.Support3) {initnpctimer; end;} + // Spawn a fiend + .@m=monster("029-9", 39, 36, "Mouboo", Mouboo, 1); + setunitdata(.@m, UDT_RACE, RC_Legendary); + .@hp=getunitdata(.@m, UDT_MAXHP)*$@FIRESOFSTEAM_DIFF/80; + .@op=getunitdata(.@m, UDT_MODE); + .@op=.@op|MD_AGGRESSIVE; + setunitdata(.@m, UDT_MAXHP, .@hp); + setunitdata(.@m, UDT_HP, .@hp); + setunitdata(.@m, UDT_MODE, .@op); + // Select random magic from arsenal + .@r=rand2(16); + switch (.@r) { + case 1: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Bleeding", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_BLOODING, BL_PC, 1); + break; + case 2: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Blind", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_BLIND, BL_PC, 1); + break; + case 3: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Lag", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_CONFUSION, BL_PC, 1); + break; + case 4: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Curse", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_CURSE, BL_PC, 1); + break; + case 5: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Poison", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_POISON, BL_PC, 1); + break; + case 6: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Sleep", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_SLEEP, BL_PC, 1); + break; + case 7: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Deadly Poison", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_DPOISON, BL_PC, 1); + break; + case 8: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Chilling", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_COLD, BL_PC, 1); + break; + case 9: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Burning", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_BURNING, BL_PC, 1); + break; + case 10: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Fear", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_FEAR, BL_PC, 1); + break; + case 11: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Obliterate", 0); + rectharm($@FOS_MOB, 7, 7, rand2(700, 900), HARM_MISC, Ele_Holy, "filter_always", BL_PC | BL_MER | BL_HOM); + break; + case 12: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Disarm Homun", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_STUN, BL_HOM, 1); + break; + case 13: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Exterminate Lesserform", 0); + rectharm($@FOS_MOB, 7, 7, rand2(800, 1400), HARM_MISC, Ele_Holy, "filter_always", BL_MER | BL_HOM); + break; + case 14: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Summon Reinforcement", 0); + for (.@i=0;.@i <= rand2(4);.@i++) { + .@m=monster("029-9", rand2(35,43), rand2(35,38), "Reinforcement", + any(AlphaMouboo, Moubi, BloodyMouboo, Moubi), 1); + setunitdata(.@m, UDT_RACE, RC_Legendary); + .@hp=getunitdata(.@m, UDT_MAXHP)*$@FIRESOFSTEAM_DIFF/60; + setunitdata(.@m, UDT_MAXHP, .@hp); + setunitdata(.@m, UDT_HP, .@hp); + } + break; + case 15: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Wizcat", 0); + for (.@i=0;.@i <= 2+rand2(7);.@i++) { + .@m=monster("029-9", rand2(35,43), rand2(35,38), + "Reinforcement", BlackCat, 1); + setunitdata(.@m, UDT_RACE, RC_Legendary); + .@hp=getunitdata(.@m, UDT_MAXHP)*$@FIRESOFSTEAM_DIFF/70; + setunitdata(.@m, UDT_MAXHP, .@hp); + setunitdata(.@m, UDT_HP, .@hp); + } + break; + case 16: + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Nuke", 0); + rectharm($@FOS_MOB, 14, 14, rand2(450, 750), HARM_MISC, Ele_Holy, "filter_always", BL_PC | BL_MER | BL_HOM); + break; + default: // case 0 + mapannounce("029-9", "Moubootaur : ##BAncient Magic: Seal", 0); + areasc2("029-9", 40, 35, 20, 15000, SC_SILENCE, BL_PC, 1); + break; + } + sleep(rand2(45000)); // Wait a bit longer between magic (random) + } + initnpctimer; + end; + +////////////// Player's Defeat ////////////// +OnDefeat: +L_Defeat: + $@FIRESOFSTEAM_BOSS=0; + $@FIRESOFSTEAM_DIFF=0; + $@FOS_ISB=0; + $@FOS_MOB=0; + $@FOS_AND=0; + .Support1=0; + .Support2=0; + .Support3=0; + .Support4=0; + .Support5=0; + killmonsterall("029-9"); + kamibroadcast("The players were defeated at Fires of Steam Showdown.", "Fires of Steam"); + enablenpc "#029-9_97_49"; // Exit + enablenpc "#029-9_91_26"; // To Moubootaur + enablenpc "#029-9_103_48"; // To Storage + enablenpc "#029-9_34_23"; // From Moubootaur + enablenpc "#029-9_137_41"; // From Storage + stopnpctimer; + end; + +OnInit: + .boostime = 900000; + .subitime = 47000; + .distance = 5; + .sex = G_OTHER; + end; +} + + diff --git a/npc/030-01/_import.txt b/npc/030-01/_import.txt new file mode 100644 index 0000000..a4fa24b --- /dev/null +++ b/npc/030-01/_import.txt @@ -0,0 +1,2 @@ +// Map 030-01: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-02/_import.txt b/npc/030-02/_import.txt new file mode 100644 index 0000000..542f3cc --- /dev/null +++ b/npc/030-02/_import.txt @@ -0,0 +1,2 @@ +// Map 030-02: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-03/_import.txt b/npc/030-03/_import.txt new file mode 100644 index 0000000..55cb230 --- /dev/null +++ b/npc/030-03/_import.txt @@ -0,0 +1,2 @@ +// Map 030-03: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-04/_import.txt b/npc/030-04/_import.txt new file mode 100644 index 0000000..af92695 --- /dev/null +++ b/npc/030-04/_import.txt @@ -0,0 +1,2 @@ +// Map 030-04: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-05/_import.txt b/npc/030-05/_import.txt new file mode 100644 index 0000000..194df71 --- /dev/null +++ b/npc/030-05/_import.txt @@ -0,0 +1,2 @@ +// Map 030-05: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-06/_import.txt b/npc/030-06/_import.txt new file mode 100644 index 0000000..c7a9873 --- /dev/null +++ b/npc/030-06/_import.txt @@ -0,0 +1,2 @@ +// Map 030-06: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-07/_import.txt b/npc/030-07/_import.txt new file mode 100644 index 0000000..3187970 --- /dev/null +++ b/npc/030-07/_import.txt @@ -0,0 +1,2 @@ +// Map 030-07: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-08/_import.txt b/npc/030-08/_import.txt new file mode 100644 index 0000000..a8e16c8 --- /dev/null +++ b/npc/030-08/_import.txt @@ -0,0 +1,2 @@ +// Map 030-08: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-09/_import.txt b/npc/030-09/_import.txt new file mode 100644 index 0000000..f35f313 --- /dev/null +++ b/npc/030-09/_import.txt @@ -0,0 +1,2 @@ +// Map 030-09: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-10/_import.txt b/npc/030-10/_import.txt new file mode 100644 index 0000000..1fbf111 --- /dev/null +++ b/npc/030-10/_import.txt @@ -0,0 +1,2 @@ +// Map 030-10: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-11/_import.txt b/npc/030-11/_import.txt new file mode 100644 index 0000000..126df6c --- /dev/null +++ b/npc/030-11/_import.txt @@ -0,0 +1,2 @@ +// Map 030-11: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-12/_import.txt b/npc/030-12/_import.txt new file mode 100644 index 0000000..2625962 --- /dev/null +++ b/npc/030-12/_import.txt @@ -0,0 +1,2 @@ +// Map 030-12: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-13/_import.txt b/npc/030-13/_import.txt new file mode 100644 index 0000000..eea0317 --- /dev/null +++ b/npc/030-13/_import.txt @@ -0,0 +1,2 @@ +// Map 030-13: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-14/_import.txt b/npc/030-14/_import.txt new file mode 100644 index 0000000..c0ace41 --- /dev/null +++ b/npc/030-14/_import.txt @@ -0,0 +1,2 @@ +// Map 030-14: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-15/_import.txt b/npc/030-15/_import.txt new file mode 100644 index 0000000..e7ea911 --- /dev/null +++ b/npc/030-15/_import.txt @@ -0,0 +1,2 @@ +// Map 030-15: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-16/_import.txt b/npc/030-16/_import.txt new file mode 100644 index 0000000..cef5593 --- /dev/null +++ b/npc/030-16/_import.txt @@ -0,0 +1,2 @@ +// Map 030-16: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-17/_import.txt b/npc/030-17/_import.txt new file mode 100644 index 0000000..dc70023 --- /dev/null +++ b/npc/030-17/_import.txt @@ -0,0 +1,2 @@ +// Map 030-17: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-18/_import.txt b/npc/030-18/_import.txt new file mode 100644 index 0000000..e754c67 --- /dev/null +++ b/npc/030-18/_import.txt @@ -0,0 +1,2 @@ +// Map 030-18: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-19/_import.txt b/npc/030-19/_import.txt new file mode 100644 index 0000000..99ee7af --- /dev/null +++ b/npc/030-19/_import.txt @@ -0,0 +1,2 @@ +// Map 030-19: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-20/_import.txt b/npc/030-20/_import.txt new file mode 100644 index 0000000..83d2afc --- /dev/null +++ b/npc/030-20/_import.txt @@ -0,0 +1,2 @@ +// Map 030-20: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-21/_import.txt b/npc/030-21/_import.txt new file mode 100644 index 0000000..fb64664 --- /dev/null +++ b/npc/030-21/_import.txt @@ -0,0 +1,2 @@ +// Map 030-21: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-22/_import.txt b/npc/030-22/_import.txt new file mode 100644 index 0000000..53b88b3 --- /dev/null +++ b/npc/030-22/_import.txt @@ -0,0 +1,2 @@ +// Map 030-22: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-23/_import.txt b/npc/030-23/_import.txt new file mode 100644 index 0000000..10475c6 --- /dev/null +++ b/npc/030-23/_import.txt @@ -0,0 +1,2 @@ +// Map 030-23: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-24/_import.txt b/npc/030-24/_import.txt new file mode 100644 index 0000000..c506064 --- /dev/null +++ b/npc/030-24/_import.txt @@ -0,0 +1,2 @@ +// Map 030-24: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-25/_import.txt b/npc/030-25/_import.txt new file mode 100644 index 0000000..c40f6fc --- /dev/null +++ b/npc/030-25/_import.txt @@ -0,0 +1,2 @@ +// Map 030-25: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-26/_import.txt b/npc/030-26/_import.txt new file mode 100644 index 0000000..9b4a01a --- /dev/null +++ b/npc/030-26/_import.txt @@ -0,0 +1,2 @@ +// Map 030-26: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-27/_import.txt b/npc/030-27/_import.txt new file mode 100644 index 0000000..78720ab --- /dev/null +++ b/npc/030-27/_import.txt @@ -0,0 +1,2 @@ +// Map 030-27: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-28/_import.txt b/npc/030-28/_import.txt new file mode 100644 index 0000000..c816a20 --- /dev/null +++ b/npc/030-28/_import.txt @@ -0,0 +1,2 @@ +// Map 030-28: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-29/_import.txt b/npc/030-29/_import.txt new file mode 100644 index 0000000..afc079a --- /dev/null +++ b/npc/030-29/_import.txt @@ -0,0 +1,2 @@ +// Map 030-29: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-30/_import.txt b/npc/030-30/_import.txt new file mode 100644 index 0000000..402cadd --- /dev/null +++ b/npc/030-30/_import.txt @@ -0,0 +1,2 @@ +// Map 030-30: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-31/_import.txt b/npc/030-31/_import.txt new file mode 100644 index 0000000..5c9091d --- /dev/null +++ b/npc/030-31/_import.txt @@ -0,0 +1,2 @@ +// Map 030-31: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-32/_import.txt b/npc/030-32/_import.txt new file mode 100644 index 0000000..85103ca --- /dev/null +++ b/npc/030-32/_import.txt @@ -0,0 +1,2 @@ +// Map 030-32: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-33/_import.txt b/npc/030-33/_import.txt new file mode 100644 index 0000000..1730f57 --- /dev/null +++ b/npc/030-33/_import.txt @@ -0,0 +1,2 @@ +// Map 030-33: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-34/_import.txt b/npc/030-34/_import.txt new file mode 100644 index 0000000..5940e25 --- /dev/null +++ b/npc/030-34/_import.txt @@ -0,0 +1,2 @@ +// Map 030-34: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-35/_import.txt b/npc/030-35/_import.txt new file mode 100644 index 0000000..d732c55 --- /dev/null +++ b/npc/030-35/_import.txt @@ -0,0 +1,2 @@ +// Map 030-35: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-36/_import.txt b/npc/030-36/_import.txt new file mode 100644 index 0000000..478294a --- /dev/null +++ b/npc/030-36/_import.txt @@ -0,0 +1,2 @@ +// Map 030-36: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-37/_import.txt b/npc/030-37/_import.txt new file mode 100644 index 0000000..0aea561 --- /dev/null +++ b/npc/030-37/_import.txt @@ -0,0 +1,2 @@ +// Map 030-37: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-38/_import.txt b/npc/030-38/_import.txt new file mode 100644 index 0000000..0f21bac --- /dev/null +++ b/npc/030-38/_import.txt @@ -0,0 +1,2 @@ +// Map 030-38: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-39/_import.txt b/npc/030-39/_import.txt new file mode 100644 index 0000000..ead4237 --- /dev/null +++ b/npc/030-39/_import.txt @@ -0,0 +1,2 @@ +// Map 030-39: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-40/_import.txt b/npc/030-40/_import.txt new file mode 100644 index 0000000..31538f9 --- /dev/null +++ b/npc/030-40/_import.txt @@ -0,0 +1,2 @@ +// Map 030-40: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-41/_import.txt b/npc/030-41/_import.txt new file mode 100644 index 0000000..6625938 --- /dev/null +++ b/npc/030-41/_import.txt @@ -0,0 +1,2 @@ +// Map 030-41: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-42/_import.txt b/npc/030-42/_import.txt new file mode 100644 index 0000000..f9857cb --- /dev/null +++ b/npc/030-42/_import.txt @@ -0,0 +1,2 @@ +// Map 030-42: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-43/_import.txt b/npc/030-43/_import.txt new file mode 100644 index 0000000..bb54398 --- /dev/null +++ b/npc/030-43/_import.txt @@ -0,0 +1,2 @@ +// Map 030-43: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-44/_import.txt b/npc/030-44/_import.txt new file mode 100644 index 0000000..d5d7a12 --- /dev/null +++ b/npc/030-44/_import.txt @@ -0,0 +1,2 @@ +// Map 030-44: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-45/_import.txt b/npc/030-45/_import.txt new file mode 100644 index 0000000..373d545 --- /dev/null +++ b/npc/030-45/_import.txt @@ -0,0 +1,2 @@ +// Map 030-45: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-46/_import.txt b/npc/030-46/_import.txt new file mode 100644 index 0000000..6fc385d --- /dev/null +++ b/npc/030-46/_import.txt @@ -0,0 +1,2 @@ +// Map 030-46: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-47/_import.txt b/npc/030-47/_import.txt new file mode 100644 index 0000000..41b8427 --- /dev/null +++ b/npc/030-47/_import.txt @@ -0,0 +1,2 @@ +// Map 030-47: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-48/_import.txt b/npc/030-48/_import.txt new file mode 100644 index 0000000..7b313e4 --- /dev/null +++ b/npc/030-48/_import.txt @@ -0,0 +1,2 @@ +// Map 030-48: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-49/_import.txt b/npc/030-49/_import.txt new file mode 100644 index 0000000..cd33348 --- /dev/null +++ b/npc/030-49/_import.txt @@ -0,0 +1,2 @@ +// Map 030-49: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-50/_import.txt b/npc/030-50/_import.txt new file mode 100644 index 0000000..b0dfe31 --- /dev/null +++ b/npc/030-50/_import.txt @@ -0,0 +1,2 @@ +// Map 030-50: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-51/_import.txt b/npc/030-51/_import.txt new file mode 100644 index 0000000..00017f4 --- /dev/null +++ b/npc/030-51/_import.txt @@ -0,0 +1,2 @@ +// Map 030-51: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-52/_import.txt b/npc/030-52/_import.txt new file mode 100644 index 0000000..06af666 --- /dev/null +++ b/npc/030-52/_import.txt @@ -0,0 +1,2 @@ +// Map 030-52: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-53/_import.txt b/npc/030-53/_import.txt new file mode 100644 index 0000000..a21b3c5 --- /dev/null +++ b/npc/030-53/_import.txt @@ -0,0 +1,2 @@ +// Map 030-53: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-54/_import.txt b/npc/030-54/_import.txt new file mode 100644 index 0000000..03ee139 --- /dev/null +++ b/npc/030-54/_import.txt @@ -0,0 +1,2 @@ +// Map 030-54: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-55/_import.txt b/npc/030-55/_import.txt new file mode 100644 index 0000000..7771137 --- /dev/null +++ b/npc/030-55/_import.txt @@ -0,0 +1,2 @@ +// Map 030-55: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-56/_import.txt b/npc/030-56/_import.txt new file mode 100644 index 0000000..afea9fc --- /dev/null +++ b/npc/030-56/_import.txt @@ -0,0 +1,2 @@ +// Map 030-56: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-57/_import.txt b/npc/030-57/_import.txt new file mode 100644 index 0000000..241e292 --- /dev/null +++ b/npc/030-57/_import.txt @@ -0,0 +1,2 @@ +// Map 030-57: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-58/_import.txt b/npc/030-58/_import.txt new file mode 100644 index 0000000..39423ae --- /dev/null +++ b/npc/030-58/_import.txt @@ -0,0 +1,2 @@ +// Map 030-58: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-59/_import.txt b/npc/030-59/_import.txt new file mode 100644 index 0000000..2b61c13 --- /dev/null +++ b/npc/030-59/_import.txt @@ -0,0 +1,2 @@ +// Map 030-59: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-60/_import.txt b/npc/030-60/_import.txt new file mode 100644 index 0000000..009984d --- /dev/null +++ b/npc/030-60/_import.txt @@ -0,0 +1,2 @@ +// Map 030-60: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-61/_import.txt b/npc/030-61/_import.txt new file mode 100644 index 0000000..18f8233 --- /dev/null +++ b/npc/030-61/_import.txt @@ -0,0 +1,2 @@ +// Map 030-61: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-62/_import.txt b/npc/030-62/_import.txt new file mode 100644 index 0000000..021463a --- /dev/null +++ b/npc/030-62/_import.txt @@ -0,0 +1,2 @@ +// Map 030-62: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-63/_import.txt b/npc/030-63/_import.txt new file mode 100644 index 0000000..420417b --- /dev/null +++ b/npc/030-63/_import.txt @@ -0,0 +1,2 @@ +// Map 030-63: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-64/_import.txt b/npc/030-64/_import.txt new file mode 100644 index 0000000..176e5b8 --- /dev/null +++ b/npc/030-64/_import.txt @@ -0,0 +1,2 @@ +// Map 030-64: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-65/_import.txt b/npc/030-65/_import.txt new file mode 100644 index 0000000..6130759 --- /dev/null +++ b/npc/030-65/_import.txt @@ -0,0 +1,2 @@ +// Map 030-65: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-66/_import.txt b/npc/030-66/_import.txt new file mode 100644 index 0000000..8fbbe80 --- /dev/null +++ b/npc/030-66/_import.txt @@ -0,0 +1,2 @@ +// Map 030-66: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-67/_import.txt b/npc/030-67/_import.txt new file mode 100644 index 0000000..14ab58a --- /dev/null +++ b/npc/030-67/_import.txt @@ -0,0 +1,2 @@ +// Map 030-67: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-68/_import.txt b/npc/030-68/_import.txt new file mode 100644 index 0000000..9ba3c39 --- /dev/null +++ b/npc/030-68/_import.txt @@ -0,0 +1,2 @@ +// Map 030-68: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-69/_import.txt b/npc/030-69/_import.txt new file mode 100644 index 0000000..d1f59f3 --- /dev/null +++ b/npc/030-69/_import.txt @@ -0,0 +1,2 @@ +// Map 030-69: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-70/_import.txt b/npc/030-70/_import.txt new file mode 100644 index 0000000..898077f --- /dev/null +++ b/npc/030-70/_import.txt @@ -0,0 +1,2 @@ +// Map 030-70: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-71/_import.txt b/npc/030-71/_import.txt new file mode 100644 index 0000000..a207c7d --- /dev/null +++ b/npc/030-71/_import.txt @@ -0,0 +1,2 @@ +// Map 030-71: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-72/_import.txt b/npc/030-72/_import.txt new file mode 100644 index 0000000..644928a --- /dev/null +++ b/npc/030-72/_import.txt @@ -0,0 +1,2 @@ +// Map 030-72: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-73/_import.txt b/npc/030-73/_import.txt new file mode 100644 index 0000000..4063b15 --- /dev/null +++ b/npc/030-73/_import.txt @@ -0,0 +1,2 @@ +// Map 030-73: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-74/_import.txt b/npc/030-74/_import.txt new file mode 100644 index 0000000..a4ef70c --- /dev/null +++ b/npc/030-74/_import.txt @@ -0,0 +1,2 @@ +// Map 030-74: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/030-75/_import.txt b/npc/030-75/_import.txt new file mode 100644 index 0000000..2b488d9 --- /dev/null +++ b/npc/030-75/_import.txt @@ -0,0 +1,2 @@ +// Map 030-75: Mazes and Dragons +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/031-0/_import.txt b/npc/031-0/_import.txt new file mode 100644 index 0000000..21649ed --- /dev/null +++ b/npc/031-0/_import.txt @@ -0,0 +1,5 @@ +// Map 031-0: Aethyr +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/031-0/_mobs.txt", +"npc/031-0/_warps.txt", +"npc/031-0/boss.txt", diff --git a/npc/031-0/_mobs.txt b/npc/031-0/_mobs.txt new file mode 100644 index 0000000..b62a927 --- /dev/null +++ b/npc/031-0/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 031-0: Aethyr mobs +031-0,95,82,91,101 monster Ice Maggot 1012,32,30000,30000 +031-0,118,59,36,33 monster Moggun 1070,8,30000,30000 +031-0,58,53,44,35 monster Water Fairy 1184,8,30000,30000 +031-0,104,120,30,30 monster Santa Slime 1096,6,30000,30000 +031-0,46,34,5,5 monster Wolvern 1037,2,10000,10000 +031-0,91,112,10,7 monster Archant 1026,2,30000,30000 diff --git a/npc/031-0/_warps.txt b/npc/031-0/_warps.txt new file mode 100644 index 0000000..9026149 --- /dev/null +++ b/npc/031-0/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 031-0: Aethyr warps +031-0,127,45,0 warp #031-0_127_45 0,0,031-1,68,31 +031-0,25,60,0 warp #031-0_25_60 0,0,031-1,25,26 diff --git a/npc/031-0/boss.txt b/npc/031-0/boss.txt new file mode 100644 index 0000000..efd2425 --- /dev/null +++ b/npc/031-0/boss.txt @@ -0,0 +1,30 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Spider Queen Boss + +031-0,0,0,0 script #BossCtrl_031-0 NPC_HIDDEN,{ + end; + +// Respawn every half hour +OnTimer1800000: + stopnpctimer; +OnInit: + .@m = monster("031-0", 149, 123, strmobinfo(1, Tengu), Tengu, 1, "#BossCtrl_031-0::OnBossDeath"); + set_aggro(.@m, MD_BOSS); + end; + +OnBossDeath: + initnpctimer; + .@party=getcharid(1); + if (.@party > 0) { + mapannounce getmap(), "Boss deafeated by Party: " + getpartyname(.@party), bc_all; + } else { + mapannounce getmap(), "Boss deafeated by: " + strcharinfo(0), bc_all; + } + getexp 0, 1000+BaseLevel*8+JobLevel; + fix_mobkill(Tengu); + end; + +} diff --git a/npc/031-1/_import.txt b/npc/031-1/_import.txt new file mode 100644 index 0000000..9de05ba --- /dev/null +++ b/npc/031-1/_import.txt @@ -0,0 +1,4 @@ +// Map 031-1: Aethyr +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/031-1/_mobs.txt", +"npc/031-1/_warps.txt", diff --git a/npc/031-1/_mobs.txt b/npc/031-1/_mobs.txt new file mode 100644 index 0000000..61c846d --- /dev/null +++ b/npc/031-1/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 031-1: Aethyr mobs +031-1,56,62,52,44 monster Pollet 1219,24,90000,30000 +031-1,68,82,43,23 monster Iced Fluffy 1041,13,60000,30000 +031-1,89,40,15,16 monster White Slime 1094,6,90000,30000 +031-1,24,59,10,44 monster Wind Fairy 1185,4,75000,30000 +031-1,56,48,29,23 monster Santa Slime 1096,7,30000,30000 +031-1,24,23,3,4 monster Whirly Bird 1232,1,300000,300000 diff --git a/npc/031-1/_warps.txt b/npc/031-1/_warps.txt new file mode 100644 index 0000000..88cef18 --- /dev/null +++ b/npc/031-1/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 031-1: Aethyr warps +031-1,68,30,0 warp #031-1_68_30 0,0,031-0,127,46 +031-1,58,104,0 warp #031-1_58_104 0,0,023-4,24,21 +031-1,25,25,0 warp #031-1_25_25 0,0,031-0,25,61 +031-1,56,47,0 warp #031-1_56_47 0,0,031-7,56,51 +031-1,84,58,0 warp #031-1_84_58 0,0,031-5,95,48 +031-1,74,85,0 warp #031-1_74_85 0,0,031-2,74,88 diff --git a/npc/031-2/_import.txt b/npc/031-2/_import.txt new file mode 100644 index 0000000..39fc907 --- /dev/null +++ b/npc/031-2/_import.txt @@ -0,0 +1,4 @@ +// Map 031-2: Aethyr Inn +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/031-2/_warps.txt", +"npc/031-2/tyrca.txt", diff --git a/npc/031-2/_warps.txt b/npc/031-2/_warps.txt new file mode 100644 index 0000000..eac0c1a --- /dev/null +++ b/npc/031-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 031-2: Aethyr Inn warps +031-2,75,89,0 warp #031-2_75_89 1,0,031-1,74,86 diff --git a/npc/031-2/tyrca.txt b/npc/031-2/tyrca.txt new file mode 100644 index 0000000..a9125f2 --- /dev/null +++ b/npc/031-2/tyrca.txt @@ -0,0 +1,66 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +031-2,77,77,0 script Tyrca NPC_SAVIOR_F,{ + .@q = getq(AethyrQuest_Tyrca); + mesn; + mesq l("Welcome to Aethyr, strange traveler. I am a collector of rare gemstones."); + if (REBIRTH || BaseLevel > 90) { + if (.@q == 0 && countitem(LightGreenDiamond)) { + next; + mesn; + mesq l("...This %s you have there. I haven't seen one for a while.", getitemlink(LightGreenDiamond)); + next; + mesn; + mesq l("I offer you either %s GP, or %s Monster Points for it. What do you say?", fnum(100000), fnum(40000)); + next; + select + l("Sorry, I'll keep my gem."), + l("Sure, gimme the Gold."), + l("Sure, gimme the Monster Points."), + rif(REBIRTH, l("Can you give me half the gold and half the monster points?")); + mes ""; + switch (@menu) { + case 2: + delitem LightGreenDiamond, 1; + Zeny+=100000; + break; + case 3: + delitem LightGreenDiamond, 1; + Mobpt+=40000; + break; + case 4: + if (!REBIRTH) { + atcommand("@block "+strcharinfo(0)); end; + } + delitem LightGreenDiamond, 1; + Zeny+=50000; + Mobpt+=20000; + break; + default: + close; + } + getexp 0, 5000; + setq AethyrQuest_Tyrca, 1; + mesn; + mesq l("Please doing business with you."); + } + } + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, SilkRobe); + setunitdata(.@npcId, UDT_HEADMIDDLE, NPCEyes); + setunitdata(.@npcId, UDT_HEADTOP, BunnyEars); + setunitdata(.@npcId, UDT_HAIRSTYLE, 9); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + + .sex = G_FEMALE; + .distance = 5; + end; +} + diff --git a/npc/031-3/_import.txt b/npc/031-3/_import.txt new file mode 100644 index 0000000..18ac517 --- /dev/null +++ b/npc/031-3/_import.txt @@ -0,0 +1,2 @@ +// Map 031-3: Aethyr Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/031-4/_import.txt b/npc/031-4/_import.txt new file mode 100644 index 0000000..dd3355e --- /dev/null +++ b/npc/031-4/_import.txt @@ -0,0 +1,2 @@ +// Map 031-4: Aethyr Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/031-5/_import.txt b/npc/031-5/_import.txt new file mode 100644 index 0000000..89c400a --- /dev/null +++ b/npc/031-5/_import.txt @@ -0,0 +1,4 @@ +// Map 031-5: Aethyr Armory +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/031-5/_warps.txt", +"npc/031-5/beatrice.txt", diff --git a/npc/031-5/_warps.txt b/npc/031-5/_warps.txt new file mode 100644 index 0000000..ed421a8 --- /dev/null +++ b/npc/031-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 031-5: Aethyr Armory warps +031-5,96,49,0 warp #031-5_96_49 1,0,031-1,84,59 diff --git a/npc/031-5/beatrice.txt b/npc/031-5/beatrice.txt new file mode 100644 index 0000000..2c99443 --- /dev/null +++ b/npc/031-5/beatrice.txt @@ -0,0 +1,187 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +031-5,97,41,0 script Beatrice NPC_SAVIOR_F,{ + // Unlocks upon: Rebirth + if (!REBIRTH) { + mesn; + mesq l("Hey there, %s. What I deal with is too much for your weak bones. So please come again after you are reborn, then I'll have something for you.", get_race()); + close; + } + mesn; + mesq l("Hey there, %s. I deal with ultra rare items. If the Powers That Be decided you're out of luck, well, I am here to help you to bypass it.", get_race()); + next; + mesn; + mesq l("For that, I use a special currency - Aethyr points. You can trade rare items for points. I also sell one point for %d GP. So, what will it me?", .price); + next; + +L_Loop: + mesc l("Aethyr Points: %s", fnum(AETHYR_PTS)), 2; + select + l("Nothing right now."), + l("Buying rares"), + l("Selling rares"), + l("Purchasing points"); + mes ""; + switch (@menu) { + case 1: + closeclientdialog; + close; + case 2: + openshop; + closedialog; // Top-Level exit + end; + case 3: + goto L_Seller; + case 4: + mesc l("How many points? (0-%d)", Zeny/.price); + input .@points, 0, Zeny/.price; + if (.@points * .price > Zeny) + .@points = 0; + Zeny-=.price*.@points; + AETHYR_PTS+=.@points; + mesc l("Done!"), 2; + break; + } + clear; + goto L_Loop; + +L_Seller: + .@id = 0; + .@rt = 0; + .@pc = 0; + .@am = 0; + + .@mr$=sprintf("%d.%02d", .ratio/100, .ratio%100); + mesc l("Only items with max %s%% drop rate are accepted!", .@mr$), 1; + .@id = requestitem(); + if (.@id < 1) goto L_Cont; + if (checkbound(.@id)) goto L_Cont; + .@rt = getiteminfo(.@id, ITEMINFO_MAXCHANCE); + // Some items are... special. Refuse them. + if (.@rt == 0) + .@rt=10000; + if (array_find(.blacklist, .@id) >= 0) + .@rt=10000; + + .@mb$=sprintf("%d.%02d", .@rt/100, .@rt%100); + if (.@rt > .ratio) { + mesc l("This item drop rate is %s%%, which exceeds the threshold of %s%%.", .@mb$, .@mr$); + next; + goto L_Cont; + } + debugmes "Ratio %s", .@mb$; + .@pc = 1+.ratio-(.@rt); // The reverse, so it goes from 1 to ratio + .@pc *= 12; // Each 0.01% chance gives a bonus of 12 points + mesn; + mesq l("I offer you %s Aethyr Points per unit of %s. How many do you wish to sell? (0-%d)", fnum(.@pc), getitemlink(.@id), countitem(.@id)); + input .@am, 0, countitem(.@id); + if (.@am < 1) + goto L_Cont; + delitem .@id, .@am; + AETHYR_PTS+=.@pc; + // FALLTHROUGH + +L_Cont: + clear; + goto L_Loop; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, CottonSkirt); + setunitdata(.@npcId, UDT_HEADMIDDLE, SaviorArmor); + setunitdata(.@npcId, UDT_HEADTOP, TrapperHat); + setunitdata(.@npcId, UDT_HAIRSTYLE, 9); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + + .sex = G_FEMALE; + .distance = 5; + .price = 520; // Loosely ased on Crafty Wings price + .ratio = 100; // Max drop rate + // Some items can be obtained by "other means" and thus, their value cannot be + // reliably calculated + setarray .blacklist, GuildCoin, WarlordPlate, Lightsaber, PowerfulLightsaber, Emperium, PinkieCrystal, PinkieHat, IronIngot, Kanabo, WarlordHelmet, Kitana, LeatherGloves, SacredManaPotion, EverburnPowder, WoodenSword, MoubooFigurine, FancyHat, LOFCoin, JeansShorts, MinerHat, LeatherShield, Boots, CommonCarp, IcedBottle, ShortBow, CottonShirt, MinerGloves, LoveLetter, LegendaryTortuga, ChocolateDye; + + tradertype(NST_CUSTOM); + // Okay, so for the catalog + /* Some important prices + Crystallized Maggot - 1,200 pts + Light Green Diamond - 1,176 pts + Amethyst - 384 pts + TL;DR From 12 to 1200 pts */ + + // Rare equips + sellitem HeartNecklace, 7500; + sellitem MoubiHat, 7500; + sellitem BloodyMoubooHat, 7000; + sellitem AlphaMoubooHat, 6500; + sellitem LightRing, 6500; + sellitem ThetaRing, 6500; + sellitem MoubooHat, 6000; + sellitem AntlersHat, 6000; + sellitem FluffyHat, 6000; + sellitem ShroomHat, 6000; + sellitem CaveSnakeHat, 6000; + sellitem SantaBeardHat, 6000; + sellitem MushHat, 6000; + sellitem ForestShroomHat, 6000; + sellitem WickedShroomHat, 6000; + sellitem MoonshroomHat, 6000; + sellitem TerraniteMask, 6000; + sellitem OperaMask, 6000; + sellitem DragonStar, 6000; + sellitem Kanabo, 6000; + sellitem FourLeafClover, 6000; + sellitem IceGladius, 6000; + sellitem CursedScythe, 6000; + sellitem PinkieHat, 5500; + sellitem Setzer, 5500; + + // Rare non-equips are cheaper + sellitem Bloodstone, 5000; + sellitem Butterfly, 5000; + sellitem CrystallizedMaggot, 5000; + sellitem PirateTreasureMap, 5000; + sellitem BlueManaPearl, 5000; + sellitem DesertTablet, 5000; + sellitem LavaManaPearl, 5000; + sellitem LightGreenDiamond, 5000; + sellitem BlackPearl, 5000; + + // "Kinda generic" + sellitem BanditPants, 2500; + sellitem CentaurSpear, 2500; + sellitem DarkDesertMushroom, 2500; + sellitem SkullMask, 2500; + sellitem BunnyEars, 2500; + //sellitem TerraniteBlueprint, 2500; + sellitem Quill, 2400; + sellitem CopperKey, 2400; + sellitem SilverMirror, 2400; + sellitem GoldenApple, 2400; + sellitem SaxsoKey, 1200; + + // Not drops + sellitem StrangeCoin, 1000; + sellitem BottleOfDivineWater, 600; + sellitem Curshroom, 300; + sellitem Coal, 250; + end; + +OnCountFunds: + setcurrency(AETHYR_PTS); + end; + +/* @price is total cost. @points is if we accept two items as currency. */ +OnPayFunds: + if( AETHYR_PTS < @price ) + end; + AETHYR_PTS -= @price; + purchaseok(); + end; + +} + diff --git a/npc/031-6/_import.txt b/npc/031-6/_import.txt new file mode 100644 index 0000000..47b9d7b --- /dev/null +++ b/npc/031-6/_import.txt @@ -0,0 +1,2 @@ +// Map 031-6: Aethyr Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/031-7/_import.txt b/npc/031-7/_import.txt new file mode 100644 index 0000000..dfa82e0 --- /dev/null +++ b/npc/031-7/_import.txt @@ -0,0 +1,4 @@ +// Map 031-7: Aethyr Townhall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/031-7/_warps.txt", +"npc/031-7/tametomo.txt", diff --git a/npc/031-7/_warps.txt b/npc/031-7/_warps.txt new file mode 100644 index 0000000..2a08698 --- /dev/null +++ b/npc/031-7/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 031-7: Aethyr Townhall warps +031-7,56,52,0 warp #031-7_56_52 0,0,031-1,56,48 diff --git a/npc/031-7/tametomo.txt b/npc/031-7/tametomo.txt new file mode 100644 index 0000000..fcd314f --- /dev/null +++ b/npc/031-7/tametomo.txt @@ -0,0 +1,87 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Questmaker Handbook 14.0 (F = Favorable) + +031-7,76,49,2 script Tametomo NPC_SAVIOR,{ + if (BaseLevel < 90) { + mesc l("The wizard seems busy. He ignores you."); + close; + } + .@q = getq(AethyrQuest_Tametomo); + .@bt = (.@q & 1); + .@lp = (.@q & 2); + + // Generate the dialog + .@m$=" "; + if (!.@bt) + .@m$+="unless you have a "+getitemlink(Butterfly)+","; + if (!.@q) + .@m$+=" or "; + if (!.@lp) + .@m$+="unless you have a "+getitemlink(LavaManaPearl)+","; + + mesn; + mesq l("Go away,%s can't you see I'm busy?", .@m$); + + // Generate the menu + if (!.@bt && countitem(Butterfly)) + .@showbt=true; + if (!.@lp && countitem(LavaManaPearl)) + .@showlp=true; + if (!.@showbt && !.@showlp) + close; + + // Display the menu if pertinent + next; + select + l("Sure, sure, I'm going..."), + rif(.@showbt, l("I have a butterfly.")), + rif(.@showlp, l("I have a lava mana pearl.")); + mes ""; + .@opt = @menu; + switch (@menu) { + case 2: + case 3: + mesn; + mesq l("%s Monster Points, and a %s. That's my final offer.", fnum(35000), getitemlink(EquipmentBlueprintE)); + next; + inventoryplace EquipmentBlueprintE, 1; + mesc l("Accept offer?"); + + if (askyesno() == ASK_NO) break; + + if (.@opt == 2 && !.@bt) { + delitem Butterfly, 1; + setq AethyrQuest_Tametomo, .@q | 1; + Mobpt+=35000; + getitem EquipmentBlueprintE, 1; + } + + if (.@opt == 3 && !.@lp) { + delitem LavaManaPearl, 1; + setq AethyrQuest_Tametomo, .@q | 2; + Mobpt+=35000; + getitem EquipmentBlueprintE, 1; + } + + mesn; + mesq l("Hmpf. A pleasure doing business with you."); + } + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADBOTTOM, GraduationRobe); + setunitdata(.@npcId, UDT_HEADMIDDLE, ManaGloves); + setunitdata(.@npcId, UDT_HEADTOP, ImperialCrown); + setunitdata(.@npcId, UDT_HAIRSTYLE, 9); + setunitdata(.@npcId, UDT_HAIRCOLOR, 18); + npcsit; + + .sex = G_MALE; + .distance = 5; + end; +} + diff --git a/npc/031-8/_import.txt b/npc/031-8/_import.txt new file mode 100644 index 0000000..9118158 --- /dev/null +++ b/npc/031-8/_import.txt @@ -0,0 +1,2 @@ +// Map 031-8: Aethyr Indoors +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/031-9/_import.txt b/npc/031-9/_import.txt new file mode 100644 index 0000000..4643e29 --- /dev/null +++ b/npc/031-9/_import.txt @@ -0,0 +1,2 @@ +// Map 031-9: Aethyr Stables +// This file is generated automatically. All manually added changes will be removed when running the Converter. diff --git a/npc/032-1/_import.txt b/npc/032-1/_import.txt new file mode 100644 index 0000000..35ba4a6 --- /dev/null +++ b/npc/032-1/_import.txt @@ -0,0 +1,5 @@ +// Map 032-1: Past Tulimshar +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/032-1/_mobs.txt", +"npc/032-1/_warps.txt", +"npc/032-1/episode.txt", diff --git a/npc/032-1/_mobs.txt b/npc/032-1/_mobs.txt new file mode 100644 index 0000000..b297b35 --- /dev/null +++ b/npc/032-1/_mobs.txt @@ -0,0 +1,11 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 032-1: Past Tulimshar mobs +032-1,107,88,69,78 monster Duck 1029,12,30000,20000 +032-1,84,52,19,37 monster Croc 1006,5,35000,90000 +032-1,44,171,2,4 monster Crocotree 1010,2,5000,150000 +032-1,104,119,22,41 monster Little Blub 1007,7,35000,150000 +032-1,0,0,0,0 monster Maggot 1030,65,35000,450000 +032-1,104,200,63,36 monster Giant Maggot 1031,4,30000,20000 +032-1,143,214,37,29 monster Red Scorpion 1072,11,35000,120000 +032-1,0,0,0,0 monster Desert Bandit 1124,12,85000,50000 +032-1,0,0,0,0 monster Little Red Slime 1234,8,5000,2000 diff --git a/npc/032-1/_warps.txt b/npc/032-1/_warps.txt new file mode 100644 index 0000000..d7e2cb9 --- /dev/null +++ b/npc/032-1/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 032-1: Past Tulimshar warps +032-1,53,108,0 warp #032-1_53_108 0,0,032-2,40,21 +032-1,53,126,0 script #032-1_53_126 NPC_HIDDEN,1,0,{ + end; +OnTouch: + slide 59,127; end; +} diff --git a/npc/032-1/episode.txt b/npc/032-1/episode.txt new file mode 100644 index 0000000..98a1503 --- /dev/null +++ b/npc/032-1/episode.txt @@ -0,0 +1,612 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Part of the Doctor's Quest. + +// Elli is capable to use magic without a mana stone, which puts her on the same +// level as the Moubootaur and the Mana Source. +032-1,58,129,0 script Elli NPC_ELLI,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + .@x = getq3(LoFQuest_EPISODE); + .@banu_prize = (.@x & 8); + .@eurni_info = (.@x & 16); + .@razor_info = (.@x & 32); + .@ellis_info = (.@x & 64); + if (!.@q && getq3(LoFQuest_EPISODE)) { + mesn; + mesq l("Hi. Do you need something?"); + next; + select + l("No, nothing. Sorry."), + l("I need to enter."); + mes ""; + if (@menu == 1) { + closeclientdialog; + close; + } + if (!.@ellis_info) { + mesn; + mesq l("You're stupid. Leave."); + close; + } + if (!.@razor_info) { + mesn; + mesq l("You don't need to talk with any researcher inside, so why bother? Leave!"); + close; + } + if (!.@banu_prize) { + mesn; + mesq l("And who would you be? Another bandit? Leave!"); + close; + } + if (!.@eurni_info) { + mesn; + mesq l("Why? You are most definitely at the wrong place. Leave!"); + close; + } + mesn; + mesq l("Eh... Sure, why not. What could go wrong, after all."); + setq2 LoFQuest_EPISODE, 1; + setq3 LoFQuest_EPISODE, 0; + close; + } + npctalk3 l("Hi."); + end; + +OnFin: + sc_start(SC_STUN, 20000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK); + setpcblock(PCBLOCK_SOFT, true); + npctalk3 l("I am Elli. One of the Originals."); + sleep2(4000); + npctalk3 l("The supreme guardian of the \"Talpan\" creatures, such as you."); + sleep2(4000); + npctalk3 l("From the time you come from, the Moubootaur has escaped its chains."); + sleep2(4000); + npctalk3 l("The Mana Source, supreme guardian of the world itself, has already took providences."); + sleep2(4000); + npctalk3 l("The Moubootaur is evil. Defeat him and protect the world. Now..."); + sleep2(4000); + npctalk3 l("Return to your time!"); + setpcblock(PCBLOCK_SOFT, false); + sc_end SC_STUN; + sleep2(1500); + warp "017-3", 72, 69; + // Nothing else needs to be done, quest was closed earlier + // So we just fix your bank account + BankVault += #MerchantBank; + #MerchantBank = 0; + end; + +OnInit: + .distance=5; + end; +} + +032-1,59,126,0 script #InnerEP NPC_HIDDEN,0,0,{ + end; +OnTouch: + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (getq(LoFQuest_EPISODE) > 15 || .@q) + slide 52, 127; + else + dispbottom l("This door is locked."); + end; +} + +032-1,68,54,0 script Adrian NPC_PLAYER,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q) { + npctalk3 l("Hi. Are you here to get luggage?"); + } else { + mesn; + mesq l("Hi. Are you here to get luggage?"); + next; + select + l("No, sorry."), + l("The Red Queen's Party?"); + mes ""; + if (@menu == 2) { + mesn; + mesq l("Is that someone's luggage?"); + next; + mesn; + mesq l("No, seriously, you should do these questions to Elli. She is the smartest girl I know, close to the Academy."); + setq3 LoFQuest_EPISODE, getq3(LoFQuest_EPISODE) | 64; + next; + } + closeclientdialog; + } + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, SailorHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + //setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); // TODO + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 21); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + + .sex = G_MALE; + .distance=5; + end; +} + +032-1,45,36,0 script Marikel NPC_PLAYER,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q) { + npctalk3 l("Hi. Working on the docks can be hard, but I'm glad I at least get to eat!"); + } else { + mesn; + mesq l("Hi. Working on the docks can be hard, but I'm glad I at least get to eat!"); + do + { + next; + select + l("Cool, thanks."), + l("Why is it hard?"), + l("The wage is high?"), + l("Something to eat?"), + l("The Queen's Party?"); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("Because the council has some crazy project which requires me to move crates all the time!"); + next; + mesn; + mesq l("Civilian traffic keeps declining but they keep bringing more crates back and forth! Crates filled with rubble!"); + next; + mesn; + mesq l("I miss the Red Queen, times were easier when she was around."); + break; + case 3: + mesn; + mesq l("Absolutely not, but not may job offers nowadays."); + break; + case 4: + mesn; + mesq l("The crops have failed again but the government keeps wasting money on research, for what purposes, as if I would know."); + next; + mesn; + mesq l("Importing food from Hurnscald is expensive, local food is expensive, if you don't have a job you're a dead person. Worse if you need medical aid."); + break; + case 5: + mesn; + mesq l("I heard they promised to revive the Red Queen, no idea how and don't care, they don't have money anyway."); + break; + } + } while (@menu != 1); + closeclientdialog; + } + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, SailorHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 21); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + + .sex = G_MALE; + .distance=5; + end; +} + +032-1,75,46,0 script Joelin NPC_FEMALE,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q) { + npctalk3 l("Hi. My ship is about to set sail, we'll try to find somewhere better to live than this ruined place!"); + } else { + mesn; + mesq l("Hi. My ship is about to set sail, we'll try to find somewhere better to live than this ruined place!"); + do + { + next; + select + l("Cool, thanks."), + l("Somewhere better to live?"), + l("Ruined place?"), + l("The Queen's Party?"); + mes ""; + switch (@menu) { + case 2: + mesn; + mesq l("Yes! We'll sail west, and find legendary lands!"); + next; + mesn; + mesq l("And if I end back here, I'll prove the planet is round! This is the perfect plan! %s GP to ride with me TO GLORY!", fnum(3000)); + next; + mesn strcharinfo(0); + if (Zeny < 3000) + mesq l("I don't have that much money, sorry."); + else + mesq l("Uh, maybe another time."); + next; + mesn; + mesq l("Your loss!"); + break; + case 3: + mesn; + mesq l("Where are you from, don't you see all the bandits? Seriously, with these ridiculous plans, the whole continent doomed."); + next; + mesn; + // Be careful with what you wish, Joelin... + mesq l("Leave sir Benjamin in charge for a few more years, and nothing will be left of these towns. I hope this council perishes in a fire!"); + break; + case 4: + mesn; + mesq l("Leave me alone."); + break; + } + } while (@menu != 1); + closeclientdialog; + } + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, CaptainCap); + setunitdata(.@npcId, UDT_HEADMIDDLE, SilkRobe); + //setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); // TODO + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 21); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + + .sex = G_FEMALE; + .distance=5; + end; +} + +032-1,77,46,0 script Harper NPC_PLAYER,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + npctalk3 l("Hi. My captain believe the world is round, can you believe that?"); + end; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, SailorHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, SailorShirt); + setunitdata(.@npcId, UDT_HEADBOTTOM, LeatherTrousers); + setunitdata(.@npcId, UDT_WEAPON, LousyMoccasins); + setunitdata(.@npcId, UDT_HAIRSTYLE, 21); + setunitdata(.@npcId, UDT_HAIRCOLOR, 11); + + .sex = G_MALE; + .distance=5; + end; +} + +032-1,112,142,0 script Banu NPC_GLASS_OLD_LADY,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q) { + npctalk3 l("Hi. My back hurts!"); + } else { + // Quest can be reset, so no major rewards, not even experience + .@x = getq3(LoFQuest_EPISODE); + .@met_banu = (.@x & 1); + .@ask_apple = (.@x & 2); + .@got_apple = (.@x & 4); + .@banu_priz = (.@x & 8); + if (!.@got_apple) { + mesn; + mesq l("You won't have any of my vegetables, you scoundrel! Get off my yard!"); + setq3 LoFQuest_EPISODE, getq3(LoFQuest_EPISODE) | 1; + close; + } else if (!.@banu_priz) { + mesn; + mesq l("Oh, thanks, darling; I was starving. Do you need anything?"); + next; + select + l("I want the apples back."), + l("I'm after the Queen's Party."), + l("I want a knife and a bottle of water!"); + mes ""; + setq3 LoFQuest_EPISODE, getq3(LoFQuest_EPISODE) | 8; + switch (@menu) { + case 1: + getitem RedApple, 12; + mesn; + mesq l("Sure, here you go!"); + close; + case 2: + getitem SharpKnife, 1; + getitem ChickenLeg, 1; + mesn; + mesq l("They did nothing good! They claim the Platinum Queen was a savior, all lies!"); + next; + mesn; + mesq l("We sure flourished at her early years, but the late years were a disaster! Crops were failing way before her death!"); + next; + mesn; + mesq l("Don't believe anything they tell you! Here, take this, so you can defend yourself. And some food, now leave."); + close; + case 3: + getitem BottleOfTonoriWater, 1; + getitem SmallKnife, 1; + mesn; + mesq l("Sure, here you go!"); + close; + } + close; + } else { + npctalk3 l("Hi. Thanks for the pie."); + } + } + end; + +OnInit: + .distance=5; + end; +} + +032-1,71,195,0 script Joanna NPC_JOANA,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q) { + npctalk3 l("Hi. Come to the bakery!"); + } else { + .@x = getq3(LoFQuest_EPISODE); + .@met_banu = (.@x & 1); + .@ask_apple = (.@x & 2); + .@got_apple = (.@x & 4); + .@banu_priz = (.@x & 8); + if (.@met_banu && !.@ask_apple) { + mesn; + mesq l("Hey, you, stranger. I promised Banu a pie, but she is yet to give me the apples."); + next; + mesn; + mesq l("Can you fetch with her? 12 %s.", getitemlink(RedApple)); + next; + select + l("Sure!"), + l("No, sorry."); + mes ""; + if (@menu == 1) setq3 LoFQuest_EPISODE, .@x | 2; + close; + } else if (.@ask_apple && !.@got_apple) { + mesn; + mesq l("Did you brought me 12 %s?", getitemlink(RedApple)); + if (countitem(RedApple) < 12) close; + next; + select + l("Sure!"), + l("No, sorry."); + mes ""; + if (@menu == 2) + close; + delitem RedApple, 12; + setq3 LoFQuest_EPISODE, .@x | 4; + mesn; + mesq l("Thank you, tell her to come to the bakery at... Actually, nevermind. Just give her this note."); + close; + } else { + mesn; + mesq l("Come to the bakery!"); + close; + } + } + end; + +OnInit: + .distance=5; + end; +} + +032-1,151,201,0 script Eurni NPC_EURNI,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q) { + npctalk3 l("Hi. I have questionable items of questionable origins for you."); + openshop; + } else { + mesn; + mesq l("Hi. I have questionable items of questionable origins for you."); + next; + select + l("Trade"), + l("The Queen's Party?"), + l("Not now."); + mes ""; + if (@menu == 1) { + openshop; + closeclientdialog; + } + else if (@menu == 2) { + mesn; + mesq l("I always do shady deals. *cough cough* %s GP.", fnum(500)); + next; + if (askyesno() == ASK_YES && Zeny >= 500) { + Zeny-=500; + setq3 LoFQuest_EPISODE, getq3(LoFQuest_EPISODE) | 16; + mesn; + mesq l("They are hidden in the crypts, inside a cave."); + next; + mesn; + mesq l("This conversation never happened."); + } + } + close; + } + end; + +OnInit: + sleep(SHOPWAIT); + sellitem Bread, getiteminfo(Bread, ITEMINFO_BUYPRICE)*16/10; + sellitem Cheese, getiteminfo(Cheese, ITEMINFO_BUYPRICE)*16/10; + sellitem ChamomileTea, getiteminfo(ChamomileTea, ITEMINFO_BUYPRICE)*18/10; + sellitem BugLeg, getiteminfo(BugLeg, ITEMINFO_BUYPRICE)*192/10; + sellitem LazuriteShard, 700; + sellitem MaggotSlime, getiteminfo(MaggotSlime, ITEMINFO_BUYPRICE)*192/10; + sellitem Coal, getiteminfo(Coal, ITEMINFO_BUYPRICE)*132/10; + sellitem Lockpicks, 10000; + sellitem IcedBottle, getiteminfo(IcedBottle, ITEMINFO_BUYPRICE)*32/10; + sellitem Wurtzite, getiteminfo(Wurtzite, ITEMINFO_BUYPRICE)*164/10; + sellitem CopperArmor; // Fair price! + sellitem StrangeCoin, 1000000; + sellitem WumpusEgg, 99999999; + + .sex = G_MALE; + .distance = 5; + end; + +// Pay your taxes! +OnBuyItem: + end; + +OnSellItem: + end; +} + +032-1,140,213,0 script Weellos NPC_LEGACY_GUARD_A,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (!.@q) { + mesn; + mesq l("Hi. This is a very historic building - the former residence of the red queen no less!"); + next; + select + l("Nice! I love history!"), + l("I'm looking for the Queen's Party."), + l("I hate the Red Queen."); + mes ""; + switch (@menu) { + case 1: + mesn; + mesq l("Yes! History is the best!"); + break; + case 2: + mesn; + mesq l("You should talk to the Black Razor. He was a researcher, so try the academy."); + setq3 LoFQuest_EPISODE, getq3(LoFQuest_EPISODE) | 32; + break; + case 3: + mesn; + mesq l("Most do. I'm not sure if all that hatred is justified, but who cares. She is dead, even if her ghost is back."); + break; + } + close; + } else { + npctalk3 l("Hi. This is a very historic building - the former residence of the red queen no less!"); + } + end; + +OnInit: + .distance=5; + end; +} + +032-1,65,223,0 script Townhall Guard#EP NPC_LEGACY_GUARD_D,{ + mesn; + mesq l("Sorry, but no entry to Townhall under orders of the council head, Benjamin L."); + close; + +OnInit: + .distance=5; + end; +} + +032-1,138,126,0 script Market Guard#EP NPC_LEGACY_GUARD_C,{ + mesn; + mesq l("With the wages I receive, you can use this market at your own risk."); + close; + +OnInit: + .distance=5; + end; +} + +032-1,40,146,0 script Academy Guard#EP NPC_LEGACY_GUARD_B,{ + mesn; + mesq l("I'm actually paid by the Academy, or there would be no security here."); + close; + +OnInit: + .distance=5; + end; +} + +032-1,152,183,0 script Lt. Longburn#EP NPC_LEGACY_LIEUTENANT,{ + mesn; + mesq l("If you have trouble with guards, just bribe them and they'll leave you alone. I asked a few to be fired three years ago, but the council haven't reviewed it yet."); + close; + +OnInit: + .distance=5; + end; +} + +032-1,138,152,0 script Begger#EP NPC_SCAMMER,{ + mesn; + mesq l("I was kicked out of home for not paying rent. But I had no choice, it was either to eat or to have a roof..."); + close; + +OnInit: + .distance=5; + end; +} + +032-1,114,194,0 script Imec#EP NPC_LOF_TRAVMERC,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + .@x = getq3(LoFQuest_EPISODE); + .@met_banu = (.@x & 1); + .@ask_apple = (.@x & 2); + .@got_apple = (.@x & 4); + .@banu_priz = (.@x & 8); + .@warez = (!.@q && (.@ask_apple && !.@got_apple)); + .@apple = 12-countitem(RedApple); + mesn; + mesq l("Warez?"); + next; + select + l("No but thanks."), + l("No, loans."), + rif(.@warez && .@apple > 0, l("Yes, I need %d apples.", .@apple)); + mes ""; + switch (@menu) { + case 1: + closeclientdialog; break; + case 2: + mesn; + mesc l("To pay in the specified date, we'll charge you %s over the loaned amount. So, how much?", "25%"); + next; + menuint + l("I'm sure the Doctor has a trick... No loans, thanks."), 0, + rif(#MerchantBank > 1250, l("1,000 GP")), 1000, + rif(#MerchantBank > 3125, l("2,500 GP")), 2500, + rif(#MerchantBank > 6250, l("5,000 GP")), 5000, + rif(#MerchantBank > 12500, l("10,000 GP")), 10000, + rif(#MerchantBank > 62500, l("50,000 GP")), 50000, + rif(#MerchantBank > 312500, l("250,000 GP")), 250000; + mes ""; + #MerchantBank -= @menuret * 125 / 100; + Zeny += @menuret; + mesn; + mesq l("...As you wish."); + break; + case 3: + // Inflation: 400% + // Plus a discount for buying many at once + .@price = .@apple * getiteminfo(RedApple, ITEMINFO_BUYPRICE) * 4 + (120 - (.@apple*10)); + mesn; + mesq l("That'll be %s GP.", fnum(.@price)); + if (Zeny < .@price) break; + next; + if (askyesno() == ASK_NO) break; + Zeny -= .@price; + getitem RedApple, .@apple; + mesn; + mesq l("My pleasure."); + } + close; + +OnInit: + .distance=5; + end; +} +// NPC_OMAR NPC_KADIYA NPC_SASHA + +// Omatt (NPC_OMAR) swears to make a large donation to the Academy +// shall Kadiya ever be cured. He is a trader of rare gems. +// Reward for saving Kadiya with future's medicine is a Light Green Diamond. + diff --git a/npc/032-2/_import.txt b/npc/032-2/_import.txt new file mode 100644 index 0000000..ac4567d --- /dev/null +++ b/npc/032-2/_import.txt @@ -0,0 +1,5 @@ +// Map 032-2: Tree Maze +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/032-2/_mobs.txt", +"npc/032-2/_warps.txt", +"npc/032-2/episode.txt", diff --git a/npc/032-2/_mobs.txt b/npc/032-2/_mobs.txt new file mode 100644 index 0000000..517dfe1 --- /dev/null +++ b/npc/032-2/_mobs.txt @@ -0,0 +1,10 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 032-2: Tree Maze mobs +032-2,0,0,0,0 monster Red Scorpion 1072,60,35000,120000 +032-2,0,0,0,0 monster Black Scorpion 1074,50,35000,120000 +032-2,0,0,0,0 monster Mountain Snake 1123,50,35000,120000 +032-2,0,0,0,0 monster Fire Skull 1193,20,35000,120000 +032-2,0,0,0,0 monster Green Slime Mother 1236,15,120000,120000 +032-2,0,0,0,0 monster Archant 1026,15,120000,120000 +032-2,0,0,0,0 monster Terranite 1167,5,120000,120000 +032-2,0,0,0,0 monster Wicked Mushroom 1176,15,120000,120000 diff --git a/npc/032-2/_warps.txt b/npc/032-2/_warps.txt new file mode 100644 index 0000000..f12eb98 --- /dev/null +++ b/npc/032-2/_warps.txt @@ -0,0 +1,89 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 032-2: Tree Maze warps +032-2,25,178,0 script #032-2_25_178 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 41,136; end; +} +032-2,41,135,0 script #032-2_41_135 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 36,109; end; +} +032-2,36,108,0 script #032-2_36_108 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 34,71; end; +} +032-2,34,70,0 script #032-2_34_70 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 159,56; end; +} +032-2,75,27,0 warp #032-2_75_27 0,0,032-3,121,101 +032-2,133,28,0 script #032-2_133_28 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 48,150; end; +} +032-2,177,38,0 script #032-2_177_38 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 53,78; end; +} +032-2,147,67,0 script #032-2_147_67 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 130,72; end; +} +032-2,130,71,0 script #032-2_130_71 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 153,172; end; +} +032-2,165,78,0 script #032-2_165_78 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 117,30; end; +} +032-2,178,89,0 script #032-2_178_89 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 170,107; end; +} +032-2,170,106,0 script #032-2_170_106 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 178,88; end; +} +032-2,168,149,0 script #032-2_168_149 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 146,153; end; +} +032-2,146,152,0 script #032-2_146_152 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 159,56; end; +} +032-2,119,153,0 script #032-2_119_153 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 153,172; end; +} +032-2,87,173,0 script #032-2_87_173 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 145,95; end; +} +032-2,47,166,0 script #032-2_47_166 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 25,179; end; +} +032-2,40,19,0 warp #032-2_40_19 0,0,032-1,53,109 +032-2,75,53,0 script #032-2_75_53 NPC_HIDDEN,0,0,{ + end; +OnTouch: + slide 24,166; end; +} diff --git a/npc/032-2/episode.txt b/npc/032-2/episode.txt new file mode 100644 index 0000000..b27f87c --- /dev/null +++ b/npc/032-2/episode.txt @@ -0,0 +1,62 @@ +// TMW2 scripts. +// (Random) Treasure Chest +// Authored by Jesusalva with code parts from Evol, see 007-1/treasure +// Regenerates every 6 hours +032-2,0,0,0 script #chest_03220 NPC_CHEST,{ + + if (!.busy && !.empty) { + TreasureBox(75); // 0.75% better treasure find rate + + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + .dir = .dir == 0 ? 2 : 6; // closed ? opening : closing + .busy = true; // lock until available again + initnpctimer; + } else if (!.busy) { + mesc l("Someone looted this treasure box already..."); + } else { + end; + } + close; + +OnTimer160: + .dir = .dir == 6 ? 0 : 4; // closing ? closed : open + end; + +OnTimer500: + .busy = false; // unlock + if (.dir == 0 || .dir == 4) + stopnpctimer; // stop here if the chest is closed + end; + +OnInit: + .busy = false; + .distance = 2; + .empty = false; + +OnClock0156: +OnClock0756: +OnClock1356: +OnClock1956: + // Try to warp randomly to a walkable spot, up to 20 attempts + // Otherwise, it'll stay where it already is (but will close and refill). + .@e=0; .@x=0; .@y=0; + while (!checkcell(.map$, .@x, .@y, cell_chkpass)) + { + if (.@e == 20) { + .@x=.x; + .@y=.y; + break; + } + // Remember the +20 -20 margin adjustment + .@x = rand2(20, 180); + .@y = rand2(20, 180); + ++.@e; + } + .busy=false; + .empty=false; + movenpc .name$, .@x, .@y, 0; + end; +} + +032-2,0,0,0 duplicate(#chest_03220) #chest_03221 NPC_CHEST + diff --git a/npc/032-3/_import.txt b/npc/032-3/_import.txt new file mode 100644 index 0000000..7fee888 --- /dev/null +++ b/npc/032-3/_import.txt @@ -0,0 +1,4 @@ +// Map 032-3: Beasts Dungeon +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/032-3/_warps.txt", +"npc/032-3/episode.txt", diff --git a/npc/032-3/_warps.txt b/npc/032-3/_warps.txt new file mode 100644 index 0000000..0e50fa8 --- /dev/null +++ b/npc/032-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 032-3: Beasts Dungeon warps +032-3,121,102,0 warp #032-3_121_102 0,0,032-2,103,112 diff --git a/npc/032-3/episode.txt b/npc/032-3/episode.txt new file mode 100644 index 0000000..1c01139 --- /dev/null +++ b/npc/032-3/episode.txt @@ -0,0 +1,354 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Part of the Doctor's Quest. + +032-3,121,70,0 script #CryptEP NPC_HIDDEN,0,0,{ + end; +OnTouch: + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q >= 2) + warp "032-4", 40, 74; + else + dispbottom l("This door is locked."); + end; + +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; + +OnInit: + if (instance_id() >= 0) end; + // The Green Arena + monster("032-3", 28, 25, "Arena", GreenDragon, 1); + monster("032-3", 46, 25, "Arena", GreenDragon, 1); + monster("032-3", 64, 19, "Arena", GreenDragon, 1); + monster("032-3", 78, 19, "Arena", GreenDragon, 1); + monster("032-3", 64, 43, "Arena", GreenDragon, 1); + monster("032-3", 78, 43, "Arena", GreenDragon, 1); + monster("032-3", 102, 19, "Arena", GreenDragon, 1); + monster("032-3", 112, 19, "Arena", GreenDragon, 1); + monster("032-3", 102, 43, "Arena", GreenDragon, 1); + monster("032-3", 112, 43, "Arena", GreenDragon, 1); + + // The Blue Area + monster("032-3", 130, 19, "Arena", Moonshroom, 1); + monster("032-3", 140, 19, "Arena", Moonshroom, 1); + monster("032-3", 164, 19, "Arena", Moonshroom, 1); + monster("032-3", 178, 19, "Arena", Moonshroom, 1); + monster("032-3", 196, 25, "Arena", Moonshroom, 1); + monster("032-3", 214, 25, "Arena", Moonshroom, 1); + monster("032-3", 130, 43, "Arena", Moonshroom, 1); + monster("032-3", 140, 43, "Arena", Moonshroom, 1); + monster("032-3", 164, 43, "Arena", Moonshroom, 1); + monster("032-3", 178, 43, "Arena", Moonshroom, 1); + + // The Yellow Arena + monster("032-3", 64, 106, "Arena", JackO, 1); + monster("032-3", 78, 106, "Arena", JackO, 1); + monster("032-3", 102, 106, "Arena", JackO, 1); + monster("032-3", 112, 106, "Arena", JackO, 1); + monster("032-3", 28, 124, "Arena", JackO, 1); + monster("032-3", 46, 124, "Arena", JackO, 1); + monster("032-3", 64, 130, "Arena", JackO, 1); + monster("032-3", 78, 130, "Arena", JackO, 1); + monster("032-3", 102, 130, "Arena", JackO, 1); + monster("032-3", 112, 130, "Arena", JackO, 1); + + // The Red Arena + monster("032-3", 130, 106, "Arena", GoboBear, 1); + monster("032-3", 140, 106, "Arena", GoboBear, 1); + monster("032-3", 164, 106, "Arena", GoboBear, 1); + monster("032-3", 178, 106, "Arena", GoboBear, 1); + monster("032-3", 196, 124, "Arena", GoboBear, 1); + monster("032-3", 214, 124, "Arena", GoboBear, 1); + monster("032-3", 130, 130, "Arena", GoboBear, 1); + monster("032-3", 140, 130, "Arena", GoboBear, 1); + monster("032-3", 164, 130, "Arena", GoboBear, 1); + monster("032-3", 178, 130, "Arena", GoboBear, 1); + end; +} + +032-3,121,85,0 script Central Switch#EP NPC_SWITCH_OFFLINE,{ + .@m$ = getmap(); + if (mobcount(.@m$, "all")) { + dispbottom l("Clear the arena to leave!"); + end; + } + setnpcdisplay instance_npcname(.name$), NPC_SWITCH_ONLINE; + sleep2(1000); + warp "032-3", 121, 86; + setnpcdisplay instance_npcname(.name$), NPC_SWITCH_OFFLINE; + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q != 1) end; + if (@ep_id) + setq3 LoFQuest_EPISODE, getq3(LoFQuest_EPISODE) | @ep_id; + @ep_id=0; + if (getq3(LoFQuest_EPISODE) == 15) + setq LoFQuest_EPISODE, getq(LoFQuest_EPISODE), 2, 0; + end; + +OnInit: + disablenpc .name$; + end; +OnInstanceInit: + .distance=2; + end; +} + +032-3,118,82,0 script Green Switch#EP NPC_SWITCH_OFFLINE,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q != 1) end; + .@x = getq3(LoFQuest_EPISODE); + if (.@x & 1) { dispbottom l("I have already completed this arena."); end; } + mesc l("This switch controls one of the four dungeons."); + mesc l("All four Dungeons must be completed."); + mes ""; + mesc l("Anyone within this rug will be teleported."); + mesc l("Time limit: 10 minutes"), 3; + mes ""; + mesc l("Do you want to begin the %s Dungeon?", l("Green")), 1; + if (askyesno() == ASK_NO) { closeclientdialog; close; } + closeclientdialog; + setnpcdisplay .name$, NPC_SWITCH_ONLINE; + .@mapn$="epar@"+getcharid(0); + if (instanceowner(@episode) == getcharid(3)) { + instance_set_timeout(605, 605, @episode); + } else { + @episode = instance_create("Episode Arena "+getcharid(0), getcharid(3), IOT_CHAR); + if (@episode < 0) { + mesc l("You cannot begin this now, try again later."), 1; + @episode = 0; + close; + } + instance_attachmap("032-3", @episode, false, .@mapn$); + instance_set_timeout(900, 900, @episode); + instance_init(@episode); + } + sleep2(3000); + if (!playerattached()) end; + monster(.@mapn$, 28, 27, "Arena", GreenDragon, 1); + monster(.@mapn$, 46, 27, "Arena", GreenDragon, 1); + monster(.@mapn$, 64, 21, "Arena", GreenDragon, 1); + monster(.@mapn$, 78, 21, "Arena", GreenDragon, 1); + monster(.@mapn$, 64, 45, "Arena", GreenDragon, 1); + monster(.@mapn$, 78, 45, "Arena", GreenDragon, 1); + monster(.@mapn$, 102, 21, "Arena", GreenDragon, 1); + monster(.@mapn$, 112, 21, "Arena", GreenDragon, 1); + monster(.@mapn$, 102, 45, "Arena", GreenDragon, 1); + monster(.@mapn$, 112, 45, "Arena", GreenDragon, 1); + areatimer("032-3", 112, 76, 119, 83, 10, "Green Switch#EP::OnSet"); + sleep2(50); + areawarp("032-3", 112, 76, 119, 83, .@mapn$, 32, 32, 41, 41); + setnpcdisplay .name$, NPC_SWITCH_OFFLINE; + sleep2(100); + close; + +OnSet: + @ep_id=1; + end; + +OnInit: + .distance=2; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + + + + + + +032-3,124,82,0 script Blue Switch#EP NPC_SWITCH_OFFLINE,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q != 1) end; + .@x = getq3(LoFQuest_EPISODE); + if (.@x & 2) { dispbottom l("I have already completed this arena."); end; } + mesc l("This switch controls one of the four dungeons."); + mesc l("All four Dungeons must be completed."); + mes ""; + mesc l("Anyone within this rug will be teleported."); + mesc l("Time limit: 10 minutes"), 3; + mes ""; + mesc l("Do you want to begin the %s Dungeon?", l("Blue")), 1; + if (askyesno() == ASK_NO) { closeclientdialog; close; } + closeclientdialog; + setnpcdisplay .name$, NPC_SWITCH_ONLINE; + .@mapn$="epar@"+getcharid(0); + if (instanceowner(@episode) == getcharid(3)) { + instance_set_timeout(605, 605, @episode); + } else { + @episode = instance_create("Episode Arena "+getcharid(0), getcharid(3), IOT_CHAR); + if (@episode < 0) { + mesc l("You cannot begin this now, try again later."), 1; + @episode = 0; + close; + } + instance_attachmap("032-3", @episode, false, .@mapn$); + instance_set_timeout(900, 900, @episode); + instance_init(@episode); + } + sleep2(3000); + if (!playerattached()) end; + monster(.@mapn$, 130, 21, "Arena", Moonshroom, 1); + monster(.@mapn$, 140, 21, "Arena", Moonshroom, 1); + monster(.@mapn$, 164, 21, "Arena", Moonshroom, 1); + monster(.@mapn$, 178, 21, "Arena", Moonshroom, 1); + monster(.@mapn$, 196, 27, "Arena", Moonshroom, 1); + monster(.@mapn$, 214, 27, "Arena", Moonshroom, 1); + monster(.@mapn$, 130, 45, "Arena", Moonshroom, 1); + monster(.@mapn$, 140, 45, "Arena", Moonshroom, 1); + monster(.@mapn$, 164, 45, "Arena", Moonshroom, 1); + monster(.@mapn$, 178, 45, "Arena", Moonshroom, 1); + areatimer("032-3", 123, 76, 130, 83, 10, "Blue Switch#EP::OnSet"); + sleep2(50); + areawarp("032-3", 123, 76, 130, 83, .@mapn$, 200, 32, 210, 41); + setnpcdisplay .name$, NPC_SWITCH_OFFLINE; + sleep2(100); + close; + +OnSet: + @ep_id=2; + end; + +OnInit: + .distance=2; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + + + + + + +032-3,118,88,0 script Yellow Switch#EP NPC_SWITCH_OFFLINE,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q != 1) end; + .@x = getq3(LoFQuest_EPISODE); + if (.@x & 4) { dispbottom l("I have already completed this arena."); end; } + mesc l("This switch controls one of the four dungeons."); + mesc l("All four Dungeons must be completed."); + mes ""; + mesc l("Anyone within this rug will be teleported."); + mesc l("Time limit: 10 minutes"), 3; + mes ""; + mesc l("Do you want to begin the %s Dungeon?", l("Yellow")), 1; + if (askyesno() == ASK_NO) { closeclientdialog; close; } + closeclientdialog; + setnpcdisplay .name$, NPC_SWITCH_ONLINE; + .@mapn$="epar@"+getcharid(0); + if (instanceowner(@episode) == getcharid(3)) { + instance_set_timeout(605, 605, @episode); + } else { + @episode = instance_create("Episode Arena "+getcharid(0), getcharid(3), IOT_CHAR); + if (@episode < 0) { + mesc l("You cannot begin this now, try again later."), 1; + @episode = 0; + close; + } + instance_attachmap("032-3", @episode, false, .@mapn$); + instance_set_timeout(900, 900, @episode); + instance_init(@episode); + } + sleep2(3000); + if (!playerattached()) end; + monster(.@mapn$, 64, 108, "Arena", JackO, 1); + monster(.@mapn$, 78, 108, "Arena", JackO, 1); + monster(.@mapn$, 102, 108, "Arena", JackO, 1); + monster(.@mapn$, 112, 108, "Arena", JackO, 1); + monster(.@mapn$, 28, 126, "Arena", JackO, 1); + monster(.@mapn$, 46, 126, "Arena", JackO, 1); + monster(.@mapn$, 64, 132, "Arena", JackO, 1); + monster(.@mapn$, 78, 132, "Arena", JackO, 1); + monster(.@mapn$, 102, 132, "Arena", JackO, 1); + monster(.@mapn$, 112, 132, "Arena", JackO, 1); + areatimer("032-3", 112, 87, 119, 94, 10, "Yellow Switch#EP::OnSet"); + sleep2(50); + areawarp("032-3", 112, 87, 119, 94, .@mapn$, 32, 131, 42, 140); + setnpcdisplay .name$, NPC_SWITCH_OFFLINE; + sleep2(100); + close; + +OnSet: + @ep_id=4; + end; + +OnInit: + .distance=2; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + + + + + + +032-3,124,88,0 script Red Switch#EP NPC_SWITCH_OFFLINE,{ + .@q = (getq(LoFQuest_EPISODE) == 15 ? getq2(LoFQuest_EPISODE) : 99); + if (.@q != 1) end; + .@x = getq3(LoFQuest_EPISODE); + if (.@x & 8) { dispbottom l("I have already completed this arena."); end; } + mesc l("This switch controls one of the four dungeons."); + mesc l("All four Dungeons must be completed."); + mes ""; + mesc l("Anyone within this rug will be teleported."); + mesc l("Time limit: 10 minutes"), 3; + mes ""; + mesc l("Do you want to begin the %s Dungeon?", l("Red")), 1; + if (askyesno() == ASK_NO) { closeclientdialog; close; } + closeclientdialog; + setnpcdisplay .name$, NPC_SWITCH_ONLINE; + .@mapn$="epar@"+getcharid(0); + if (instanceowner(@episode) == getcharid(3)) { + instance_set_timeout(605, 605, @episode); + } else { + @episode = instance_create("Episode Arena "+getcharid(0), getcharid(3), IOT_CHAR); + if (@episode < 0) { + mesc l("You cannot begin this now, try again later."), 1; + @episode = 0; + close; + } + instance_attachmap("032-3", @episode, false, .@mapn$); + instance_set_timeout(900, 900, @episode); + instance_init(@episode); + } + sleep2(3000); + if (!playerattached()) end; + monster(.@mapn$, 130, 108, "Arena", GoboBear, 1); + monster(.@mapn$, 140, 108, "Arena", GoboBear, 1); + monster(.@mapn$, 164, 108, "Arena", GoboBear, 1); + monster(.@mapn$, 178, 108, "Arena", GoboBear, 1); + monster(.@mapn$, 196, 126, "Arena", GoboBear, 1); + monster(.@mapn$, 214, 126, "Arena", GoboBear, 1); + monster(.@mapn$, 130, 132, "Arena", GoboBear, 1); + monster(.@mapn$, 140, 132, "Arena", GoboBear, 1); + monster(.@mapn$, 164, 132, "Arena", GoboBear, 1); + monster(.@mapn$, 178, 132, "Arena", GoboBear, 1); + areatimer("032-3", 123, 87, 130, 94, 10, "Red Switch#EP::OnSet"); + sleep2(50); + areawarp("032-3", 123, 87, 130, 94, .@mapn$, 200, 131, 210, 140); + setnpcdisplay .name$, NPC_SWITCH_OFFLINE; + sleep2(100); + close; + +OnSet: + @ep_id=8; + end; + +OnInit: + .distance=2; + end; +OnInstanceInit: + disablenpc instance_npcname(.name$); + end; +} + diff --git a/npc/032-4/_import.txt b/npc/032-4/_import.txt new file mode 100644 index 0000000..c416c29 --- /dev/null +++ b/npc/032-4/_import.txt @@ -0,0 +1,5 @@ +// Map 032-4: Crypt +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/032-4/_mobs.txt", +"npc/032-4/_warps.txt", +"npc/032-4/episode.txt", diff --git a/npc/032-4/_mobs.txt b/npc/032-4/_mobs.txt new file mode 100644 index 0000000..7edfb80 --- /dev/null +++ b/npc/032-4/_mobs.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 032-4: Crypt mobs +032-4,0,0,0,0 monster Scar 1045,12,85000,50000 +032-4,0,0,0,0 monster Armored Skeleton 1433,41,85000,50000 +032-4,0,0,0,0 monster Michel 1203,8,85000,50000 +032-4,0,0,0,0 monster Jack'O 1120,4,85000,50000 diff --git a/npc/032-4/_warps.txt b/npc/032-4/_warps.txt new file mode 100644 index 0000000..32b3d3a --- /dev/null +++ b/npc/032-4/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 032-4: Crypt warps +032-4,127,44,0 warp #032-4_127_44 2,0,032-5,37,20 +032-4,41,78,0 warp #032-4_41_78 3,0,032-3,121,71 diff --git a/npc/032-4/episode.txt b/npc/032-4/episode.txt new file mode 100644 index 0000000..ee736f8 --- /dev/null +++ b/npc/032-4/episode.txt @@ -0,0 +1,7 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Part of the Doctor's Quest. + +// There is supposed to be a gate but I'm lazy. diff --git a/npc/032-5/_import.txt b/npc/032-5/_import.txt new file mode 100644 index 0000000..f9f3133 --- /dev/null +++ b/npc/032-5/_import.txt @@ -0,0 +1,4 @@ +// Map 032-5: Inner Chamber +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/032-5/_warps.txt", +"npc/032-5/episode.txt", diff --git a/npc/032-5/_warps.txt b/npc/032-5/_warps.txt new file mode 100644 index 0000000..698d90b --- /dev/null +++ b/npc/032-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 032-5: Inner Chamber warps +032-5,37,19,0 warp #032-5_37_19 2,0,032-4,127,43 diff --git a/npc/032-5/episode.txt b/npc/032-5/episode.txt new file mode 100644 index 0000000..73971b8 --- /dev/null +++ b/npc/032-5/episode.txt @@ -0,0 +1,221 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Part of the Doctor's Quest. + +// NOTE: Several things could be done to ensure the original owner is fighting. +// I did none. +// But if at any moment the boss is alone in the room, the fight resets (FIXME). +// Dead bodies should be warped out automagically. (FIXME) +// Reward is 400,000 experience points (almost enough to level up a Lv 61) +// But the reward is only given when cutscene is completed +// Meaning if you merely espectate/help you won't get it. +// For helpers (Everyone on map) they'll get 30,000 exp +// Therefore, experience given is 30k + 70k + 300k + +032-5,37,34,0 script #EpShow NPC_HIDDEN,2,0,{ + end; +OnInit: + .ctrl = false; + .platy = 0; + .razor = 0; + end; + +OnTouch: + if (mobcount("032-5", "all")) end; + if (getq(LoFQuest_EPISODE) != 15) end; + //if (getq2(LoFQuest_EPISODE) != 2); end; + if (.ctrl) end; + + .ctrl=true; + initnpctimer; + end; + +OnTimer1000: + .platy=monster("032-5", 39, 55, strmobinfo(1, RedQueen), RedQueen, 1); + .razor=monster("032-5", 37, 57, "The Black Razor", RedFollower, 1); + immortal(.platy); + setunitdata(.platy, UDT_MODE, MD_BOSS|MD_PLANT|MD_NOKNOCKBACK); + immortal(.razor); + setunitdata(.razor, UDT_MODE, MD_BOSS|MD_PLANT|MD_NOKNOCKBACK); + end; + +OnTimer5000: + unittalk(.razor, "Uh?"); + end; + +OnTimer10000: + unittalk(.razor, "Who are you people!"); + end; + +OnTimer15000: + unittalk(.razor, "Don't tell me... You're with Prsm!"); + end; + +OnTimer20000: + unittalk(.platy, "They think they can conspire against us."); + end; + +OnTimer25000: + unittalk(.platy, "But I am the original queen and no one can steal this from me."); + maptimer("032-5", 3000, "#EpShow::OnWarn1"); + end; + +OnWarn1: + dispbottom l("On your marks!"); + sleep2(1000); + dispbottom l("Ready..."); + sleep2(1000); + dispbottom l("FIGHT!"); + end; + +OnTimer30000: + unitwarp(.platy, "001-3-1", 19, 21); + unitwarp(.razor, "001-3-1", 19, 21); + sleep(25); + unitkill(.platy); + unitkill(.razor); + sleep(25); + .platy=monster("032-5", 39, 55, strmobinfo(1, RedQueen), RedQueen, 1, "#EpShow::OnPlaty"); + .razor=monster("032-5", 37, 57, "The Black Razor", RedFollower, 1, "#EpShow::OnRazor"); + + // The Black Razor needs tweaks + setunitdata(.razor, UDT_MAXHP, 24000); + setunitdata(.razor, UDT_HP, 24000); + setunitdata(.razor, UDT_HIT, 24000); + setunitdata(.razor, UDT_ADELAY, 1272); + setunitdata(.razor, UDT_DMOTION, 48); + setunitdata(.razor, UDT_SPEED, 480); + + // The Red Queen doesn't, but we need spawns and victory conditions + areamonster("032-5", 21, 35, 58, 77, strmobinfo(1, RedFollower), RedFollower, 12, "#EpShow::OnMini"); + areamonster("032-5", 21, 35, 58, 77, strmobinfo(1, RedFollowerF), RedFollowerF, 12, "#EpShow::OnMini"); + stopnpctimer; + // TODO: Red Queen should grow stronger as her allies are defeated + // TODO: Red Queen should be able to revive her allies, only once each. + //.ctrl = false; + end; + +OnMini: + if (.razor) + unittalk(.razor, "My fallen comrade, you'll be avenged!"); + sleep(100); + if (!mobcount("032-5", "all")) goto OnNext; + end; + +OnRazor: + if (.platy) + unittalk(.platy, "Razor, no!"); + .razor = 0; + sleep(100); + if (!mobcount("032-5", "all")) goto OnNext; + end; + +OnPlaty: + if (.razor) + unittalk(.razor, "My queen!"); + .platy = 0; + sleep(100); + if (!mobcount("032-5", "all")) goto OnNext; + end; + +OnWin: + @elli=true; + getexp 30000, 0; + if (getq(LoFQuest_EPISODE) == 15) { + setq LoFQuest_EPISODE, 16, 0, 0; + getitem RedknightArmor, 1; + getexp 70000, 0; + } + specialeffect(FX_FANFARE, SELF, getcharid(3)); + end; + +OnSS: + sshake(); + closeclientdialog; + end; + +OnSSX: + sshake(8); + sleep2(350); + sshake(12); + closeclientdialog; + end; + +OnNext: + maptimer("032-5", 10, "#EpShow::OnWin"); + // TODO: Bring the boss! + .platy=monster("032-5", 39, 55, "Soul Eater", PanthomLord, 1); + immortal(.platy); + setunitdata(.platy, UDT_MODE, MD_BOSS|MD_PLANT|MD_NOKNOCKBACK); + unittalk(.platy, "..."); + sleep(5000); + unittalk(.platy, "How..."); + maptimer("032-5", 4900, "#EpShow::OnSS"); + sleep(5000); + unittalk(.platy, "HOW!!!"); + maptimer("032-5", 4900, "#EpShow::OnSS"); + sleep(5000); + unittalk(.platy, "UNFORGIVABLE!!! You have ruined my disguise!"); + sleep(5000); + unittalk(.platy, "But you underestimate me..."); + freeloop(true); + for (.@i=0;.@i < 25;.@i++) { + .@tmp = areamonster("032-5", 21, 35, 58, 77, strmobinfo(1, JackO), JackO, 1); + immortal(.@tmp); + setunitdata(.@tmp, UDT_MODE, MD_NOKNOCKBACK); + sleep(50); + } + freeloop(false); + unittalk(.platy, "...Of course every one who has fallen here serves me... And all those before!"); + sleep(2500); + unittalk(.platy, "...And with those from past, present and future... I'll be invencible! And reign forever!"); + maptimer("032-5", 1000, "#EpShow::OnMus"); + sleep(2500); + // TODO: Bring elli! + enablenpc("Elli#Ep"); + .razor = getnpcid("Elli#Ep"); + sleep(500); + unittalk(.razor, "That shall not be."); + sleep(5000); + unittalk(.razor, "I am Elli, one of the Originals."); + sleep(5000); + unittalk(.razor, "And I am here to pummel you algebraically with a(n) ginormous, frankenstein, gassy, green carnival hammer."); + sleep(5000); + unittalk(.razor, "Your reign of terror is over, Soul Eater. I'll destroy you and your followers..."); + sleep(5000); + unittalk(.razor, "...Past or future. Therefore:"); + maptimer("032-5", 4900, "#EpShow::OnSSX"); + sleep(5000); + unittalk(.razor, "COME LEGENDARY HOCUS, AND ANNIHILATE EVERYTHING!"); + sleep(500); + disablenpc("Elli#Ep"); + maptimer("032-5", 100, "#EpShow::OnFin"); + sleep(200); + killmonsterall("032-5"); + .ctrl = false; + end; + +OnMus: + changeplayermusic "valkyries.ogg"; + end; + +OnFin: + if (!@elli) end; + warp "032-1", 59, 130; + sleep2(25); + sendmapmask 129; + changeplayermusic "valkyries.ogg"; + addtimer(100, "Elli::OnFin"); + @elli=false; + end; +} + +032-5,39,52,0 script Elli#Ep NPC_ELLI,{ + end; +OnInit: + disablenpc(.name$); + end; +} + diff --git a/npc/033-1/_import.txt b/npc/033-1/_import.txt new file mode 100644 index 0000000..247d96f --- /dev/null +++ b/npc/033-1/_import.txt @@ -0,0 +1,3 @@ +// Map 033-1: Porthos - Town of Portals +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/033-1/_warps.txt", diff --git a/npc/033-1/_warps.txt b/npc/033-1/_warps.txt new file mode 100644 index 0000000..cd8747f --- /dev/null +++ b/npc/033-1/_warps.txt @@ -0,0 +1,6 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 033-1: Porthos - Town of Portals warps +033-1,189,94,0 warp #033-1_189_94 0,0,033-5,29,48 +033-1,167,89,0 warp #033-1_167_89 0,0,033-4,35,42 +033-1,172,101,0 warp #033-1_172_101 0,0,033-2,40,48 +033-1,72,190,0 warp #033-1_72_190 0,0,020-1,69,41 diff --git a/npc/033-2/_import.txt b/npc/033-2/_import.txt new file mode 100644 index 0000000..6941e78 --- /dev/null +++ b/npc/033-2/_import.txt @@ -0,0 +1,3 @@ +// Map 033-2: Building 1 +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/033-2/_warps.txt", diff --git a/npc/033-2/_warps.txt b/npc/033-2/_warps.txt new file mode 100644 index 0000000..39f7084 --- /dev/null +++ b/npc/033-2/_warps.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 033-2: Building 1 warps +033-2,40,49,0 warp #033-2_40_49 0,0,033-1,172,102 +033-2,39,21,0 warp #033-2_39_21 1,0,033-3,38,22 diff --git a/npc/033-3/_import.txt b/npc/033-3/_import.txt new file mode 100644 index 0000000..5b5c079 --- /dev/null +++ b/npc/033-3/_import.txt @@ -0,0 +1,3 @@ +// Map 033-3: Building 2 +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/033-3/_warps.txt", diff --git a/npc/033-3/_warps.txt b/npc/033-3/_warps.txt new file mode 100644 index 0000000..4a6a9d2 --- /dev/null +++ b/npc/033-3/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 033-3: Building 2 warps +033-3,39,21,0 warp #033-3_39_21 1,0,033-2,38,22 diff --git a/npc/033-4/_import.txt b/npc/033-4/_import.txt new file mode 100644 index 0000000..9642c94 --- /dev/null +++ b/npc/033-4/_import.txt @@ -0,0 +1,3 @@ +// Map 033-4: Porthos' Bank +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/033-4/_warps.txt", diff --git a/npc/033-4/_warps.txt b/npc/033-4/_warps.txt new file mode 100644 index 0000000..61a0758 --- /dev/null +++ b/npc/033-4/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 033-4: Porthos' Bank warps +033-4,35,43,0 warp #033-4_35_43 0,0,033-1,167,90 diff --git a/npc/033-5/_import.txt b/npc/033-5/_import.txt new file mode 100644 index 0000000..be6102d --- /dev/null +++ b/npc/033-5/_import.txt @@ -0,0 +1,3 @@ +// Map 033-5: Porthos' General Store +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/033-5/_warps.txt", diff --git a/npc/033-5/_warps.txt b/npc/033-5/_warps.txt new file mode 100644 index 0000000..5ae4d9d --- /dev/null +++ b/npc/033-5/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 033-5: Porthos' General Store warps +033-5,29,49,0 warp #033-5_29_49 0,0,033-1,189,95 diff --git a/npc/034-1/_import.txt b/npc/034-1/_import.txt new file mode 100644 index 0000000..3aa2c29 --- /dev/null +++ b/npc/034-1/_import.txt @@ -0,0 +1,4 @@ +// Map 034-1: Outskirts +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/034-1/_mobs.txt", +"npc/034-1/gemini.txt", diff --git a/npc/034-1/_mobs.txt b/npc/034-1/_mobs.txt new file mode 100644 index 0000000..eb59270 --- /dev/null +++ b/npc/034-1/_mobs.txt @@ -0,0 +1,4 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 034-1: Outskirts mobs +034-1,42,35,21,12 monster LogHead 1025,30,100000,30000 +034-1,42,35,20,11 monster SpikyMushroom 1019,10,100000,30000 diff --git a/npc/034-1/gemini.txt b/npc/034-1/gemini.txt new file mode 100644 index 0000000..b4f337b --- /dev/null +++ b/npc/034-1/gemini.txt @@ -0,0 +1,86 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Forest & Desert Stage + +034-1,67,48,0 script Fake Manastone NPC_MANA_STONE,{ + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(1); + .@p=getcharid(1); + mesc l("This is a weird stone. It looks like a Mana Stone from afar, but anyone can tell it is fake."); + if (strcharinfo(0) != getpartyleader(.@p)) { + mesc l("It may be dangerous. I better ask %s to check it instead.", getpartyleader(.@p)); + close; + } + switch ($@VALIA_STATUS[.@p]) { + case 1: + case 2: + mesc l("It seems to be a mechanism of some kind, but it is missing a Runestone."); + mesc l("Maybe one of the slimes dropped it."); + $@VALIA_STATUS[.@p] = 2; + break; + case 3: + mesc l("You insert the Runestone on it and hear a sound."); + mesc l("Something changed; We should see what is."); + $@VALIA_STATUS[.@p] = 4; + break; + default: + mesc l("I already did everything I could with this. What am I waiting for?"); + break; + } + close; + +OnInit: +OnInstanceInit: + .distance = 2; + end; +} + +034-1,66,45,0 script #GeminiExit1 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(1); + .@p=getcharid(1); + if ($@VALIA_STATUS[.@p] < 4) { + dispbottom l("There seems to be sort of lock preventing you from passing."); + end; + } + slide 118, 55; + end; +} + + +034-1,169,24,0 script #GeminiExit2 NPC_HIDDEN,1,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(4); + .@p=getcharid(1); + if ($@VALIA_STATUS[.@p] < 6) { + if (countitem(SealedSoul) >= 7) { + mesc l("Do you want to use the souls to unlock the passage?"), 1; + next; + if (askyesno() == ASK_YES) { + delitem SealedSoul, 7; + closeclientdialog; + if ($@VALIA_STATUS[.@p] == 4) { + .@u=monster(getmap(), 163, 26, strmobinfo(1, JackO), JackO, 1); + unittalk(.@u, "Souls... Feed me Souls... Nooooooooowwww!!!!"); + $@VALIA_STATUS[.@p]=5; + } else { + dispbottom l("The waterfall open, and you may now pass."); + $@VALIA_STATUS[.@p]=6; + close; + } + } + close; + } + dispbottom l("A powerful magic barrier prevents passage. %d %s should suffice to dispel... Maybe.", 7, getitemlink(SealedSoul)); + end; + } + warp "val2@"+.@p, 24, 59; + end; +} + diff --git a/npc/034-2/_import.txt b/npc/034-2/_import.txt new file mode 100644 index 0000000..ae5722e --- /dev/null +++ b/npc/034-2/_import.txt @@ -0,0 +1,4 @@ +// Map 034-2: Bandit Cave +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/034-2/_mobs.txt", +"npc/034-2/gemini.txt", diff --git a/npc/034-2/_mobs.txt b/npc/034-2/_mobs.txt new file mode 100644 index 0000000..a2577da --- /dev/null +++ b/npc/034-2/_mobs.txt @@ -0,0 +1,18 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 034-2: Bandit Cave mobs +034-2,31,52,10,6 monster RedSlime 1008,8,100000,30000 +034-2,31,52,9,7 monster CaveMaggot 1056,3,100000,30000 +034-2,56,102,1,1 monster Spider 1012,1,100000,250000 +034-2,61,102,1,1 monster Spider 1012,1,100000,250000 +034-2,65,102,1,1 monster Spider 1012,1,100000,250000 +034-2,71,109,2,1 monster BlackScorpion 1009,1,100000,250000 +034-2,59,56,6,18 monster Bandit 1064,37,100000,10000 +034-2,59,44,6,6 monster BanditLord 1065,4,100000,20000 +034-2,60,69,9,4 monster BanditLord 1065,4,100000,20000 +034-2,63,94,0,0 monster SleepingBandit 1099,1,100000,60000 +034-2,53,93,0,0 monster SleepingBandit 1099,1,100000,60000 +034-2,48,71,0,0 monster SleepingBandit 1099,1,100000,60000 +034-2,38,30,8,6 monster Bandit 1064,8,100000,10000 +034-2,39,33,5,4 monster BanditLord 1065,2,100000,20000 +034-2,80,101,5,6 monster CopperSlime 1098,7,100000,60000 +034-2,60,94,9,2 monster CopperSlime 1098,7,100000,60000 diff --git a/npc/034-2/gemini.txt b/npc/034-2/gemini.txt new file mode 100644 index 0000000..ca8a3ae --- /dev/null +++ b/npc/034-2/gemini.txt @@ -0,0 +1,160 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Bandit Den + +034-2,24,60,0 script #GeminiBack1 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(6); + .@p=getcharid(1); + warp "val1@"+.@p, 169, 25; + end; +} + +034-2,37,24,0 script #GeminiExit3 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(7); + .@p=getcharid(1); + if ($@VALIA_STATUS[.@p] < 8) { + dispbottom l("One of the bandits locked this door; We need to find the key!"); + end; + } + .@p=getcharid(1); + warp "val3@"+.@p, 23, 51; + end; +} + +// Ambush +034-2,29,73,0 script #GeminiDen1 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(6); + // TODO: areasc... stun ... etc. + .@c=getunits(BL_PC, .@mbs, false, getmap(), 23, 67, 32, 74); + for (.@i = 0; .@i < .@c; .@i++) { + sc_start(SC_STUN, 15000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@mbs[.@i]); + specialeffect(FX_CRITICAL, AREA, .@mbs[.@i]); + } + .@p=getcharid(1); + .@m$=getmap(); + .@b1=monster(.@m$, 30, 70, "Bandit A", Bandit, 1); + .@b2=monster(.@m$, 32, 71, "Bandit B", RobinBandit, 1); + immortal(.@b1); + immortal(.@b2); + sc_start(SC_STUN, 12000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@b1); + sc_start(SC_STUN, 12000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@b2); + // Begin cutscene + sleep2(2000); // 13 s + unittalk(.@b1, "Did you think you could sneak in our den without a problem?"); + sleep2(2000); // 11s + unittalk(.@b2, "Haha, we caught them, n00bs!"); + sleep2(3000); // 8s + unittalk(.@b1, "Drop your weapons, and come with us!"); + sleep2(3000); // 5s + unittalk(.@b2, "Ahaha, that was easy! Ok, knock them out!"); + sleep2(3000); // 2s + areawarp(.@m$, 23, 67, 32, 77, .@m$, 30, 92, 33, 95); + unitwalk(.@b1, 37, 90); + unitwalk(.@b2, 37, 90); + sleep2(2000); + // Get them... wherever + unitwarp(.@b1, "034-2", 37, 90); + unitwarp(.@b2, "034-2", 37, 90); + unitkill(.@b1); + unitkill(.@b2); + areawarp(.@m$, 30, 92, 33, 95, .@m$, 57, 101, 66, 104); + // First ambush, set some stuff up + if ($@VALIA_STATUS[.@p] == 6) { + $@VALIA_STATUS[.@p]=7; + + .@mob=monster("val2@"+.@p, 57, 104, strmobinfo(1, CopperSlime), CopperSlime, 1, "Valia::OnKey1"); + setunitdata(.@mob, UDT_LEVEL, 1); + .@mob=monster("val2@"+.@p, 61, 104, strmobinfo(1, CopperSlime), CopperSlime, 1, "Valia::OnKey2"); + setunitdata(.@mob, UDT_LEVEL, 1); + .@mob=monster("val2@"+.@p, 66, 104, strmobinfo(1, CopperSlime), CopperSlime, 1, "Valia::OnKey3"); + setunitdata(.@mob, UDT_LEVEL, 1); + + monster("val2@"+.@p, 47, 72, strmobinfo(1, BanditLord), BanditLord, 1, "Valia::OnLord"); + monster("val2@"+.@p, 85, 106, strmobinfo(1, BanditLord), BanditLord, 1, "Valia::OnLord"); + monster("val2@"+.@p, 58, 45, strmobinfo(1, BanditLord), BanditLord, 1, "Valia::OnLord"); + monster("val2@"+.@p, 40, 42, strmobinfo(1, BanditLord), BanditLord, 1, "Valia::OnLord"); + + monster("val2@"+.@p, 47, 25, strmobinfo(1, RedSlimeMother), RedSlimeMother, 1); + } + end; +} + + +// Cell Doors +034-2,58,100,0 script Cell Door#G01 NPC_NO_SPRITE,{ + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(7); + mes l("A complex lock seems to be posing a threat to you."); + mes l("But thanks to your %s skills, maybe you can pry this open.", thiefrank()); + next; + .@s=LockPicking(3, 3, false); + // You broke free! + if (.@s) { + slide .x, 98; + closeclientdialog; + end; + } + @lockpicks=false; + mes l("What's this dark magic, the password has changed!"); + close; +OnInit: +OnInstanceInit: + .distance=2; + end; +} + +034-2,62,100,0 script Cell Door#G02 NPC_NO_SPRITE,{ + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(7); + mes l("A complex lock seems to be posing a threat to you."); + mes l("But thanks to your %s skills, maybe you can pry this open.", thiefrank()); + next; + .@s=LockPicking(3, 3, false); + // You broke free! + if (.@s) { + slide .x, 98; + closeclientdialog; + end; + } + @lockpicks=false; + mes l("What's this dark magic, the password has changed!"); + close; +OnInit: +OnInstanceInit: + .distance=2; + end; +} + +034-2,64,100,0 script Cell Door#G03 NPC_NO_SPRITE,{ + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(7); + mes l("A complex lock seems to be posing a threat to you."); + mes l("But thanks to your %s skills, maybe you can pry this open.", thiefrank()); + next; + .@s=LockPicking(3, 3, false); + // You broke free! + if (.@s) { + slide .x, 98; + closeclientdialog; + end; + } + @lockpicks=false; + mes l("What's this dark magic, the password has changed!"); + close; +OnInit: +OnInstanceInit: + .distance=2; + end; +} + diff --git a/npc/034-3/_import.txt b/npc/034-3/_import.txt new file mode 100644 index 0000000..d9fef7b --- /dev/null +++ b/npc/034-3/_import.txt @@ -0,0 +1,4 @@ +// Map 034-3: Archipelago +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/034-3/_mobs.txt", +"npc/034-3/gemini.txt", diff --git a/npc/034-3/_mobs.txt b/npc/034-3/_mobs.txt new file mode 100644 index 0000000..6b648eb --- /dev/null +++ b/npc/034-3/_mobs.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 034-3: Archipelago mobs +034-3,55,29,21,19 monster AzulSlime 1100,18,100000,20000 diff --git a/npc/034-3/gemini.txt b/npc/034-3/gemini.txt new file mode 100644 index 0000000..72c4707 --- /dev/null +++ b/npc/034-3/gemini.txt @@ -0,0 +1,241 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Outside the Building + +034-3,23,50,0 script #GeminiBack2 NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(8); + .@p=getcharid(1); + warp "val2@"+.@p, 37, 25; + end; +} + +034-3,57,29,0 script #GeminiDoor NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(8); + .@p=getcharid(1); + if ($@VALIA_STATUS[.@p] >= 9) { + warp "val4@"+.@p, 43, 51; + end; + } + slide 58, 34; + percentheal -20, 0; + dispbottom l("You are pushed back violently. There is probably some way to break this barrier."); + end; +} + +034-3,51,38,0 script Engravings#Gemini NPC_NO_SPRITE,{ + mes l("You can read some words engraved inside this rock, but some are erased by wind and time:"); + next; + mes l("\".. can use this .. power..´. . .´. . . amplify a spell ..´."); + mes l(" . ´´. Don't let .. spell power .´..´ . .. fade away ..."); + mes l("´ .Use lazurite stones ´..´. they will appear. ..´ .. drop them inside .. this circle."); + mes l(". ´ power.. drains .. life..´. .´ focus . .not move at all´. .\""); + next; + mes l("Below this strange writing, you notice a word still deeply engraved in the stone:"); + next; + mesq b("catalazuli"); + next; + .@p=getcharid(1); + mes l("... that looks like an incantation or something."); + if (strcharinfo(0) != getpartyleader(.@p)) { + mes l("I probably should let %s chant it.", getpartyleader(.@p)); + close; + } + next; + mes l("I think this blue circle right here can be helpful to break the enchantment that blocks the door of the Inn."); + next; + mes l("Let's see how I can activate it..."); + mes l("But I should probably get inside it, first."); + close; + +OnChannel: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(8); + + // Check location + getmapxy(.@m$, .@x, .@y, 0); + if (.@x < 52 || + .@x > 54 || + .@y < 37 || + .@y > 38) end; + + // Only party leader may proceed + .@p=getcharid(1); + if (strcharinfo(0) != getpartyleader(.@p)) { + percentheal 0, -25; + specialeffect(312, AREA, getcharid(3)); + dispbottom l("Argh! I assume I can't do this chant!"); + end; + } + + // Bonus is based on int + .@bonus = readparam2(bInt) / 25; + // Unlike TMW's, all Lazurites show up at same place + // ... + // amount of initial magic power + .@magic_power = 1811 + readparam2(bInt)*2; + // Channelling required power depends on the base Int of the character + .@magic_power_required = 5707 - 2*readparam2(bInt); + // magic power loss depends also on Int. TMW uses 53, we use 21 + .@magic_power_loss = 21 - (((readparam2(bInt)+1)*2)/10); + + dispbottom l("Ok, let's stay focused now!"); + areamonster getmap(), 27, 20, 77, 47, strmobinfo(1, Forain), Forain, 1; + // TODO: Loop + debugmes "NPC: %s / %s (%d)", .name$, .extname$, instance_id(); + .aid=getcharid(3); + .mp$=getmap(); + .power=.@magic_power; + .state=0; + .maxpw=.@magic_power_required; + .mloss=.@magic_power_loss; + initnpctimer; + end; + +OnTimer13000: + initnpctimer; // Reset timer + // Time to spawn monsters + .@pc = getmapusers(.mp$); + areamonster .mp$, 56, 30, 60, 31, "Guardian", Scar, 1+.@pc; + areamonster .mp$, 27, 20, 77, 47, strmobinfo(1, Forain), Forain, 2; + // Terranites only show up when doing multiplayer, based on number of players + if (.@pc >= 2) + areamonster .mp$, 56, 30, 60, 31, "Guardian", Terranite, (.@pc/2); +OnTimer10000: +OnTimer8000: +OnTimer6000: +OnTimer4000: +OnTimer2000: + if (instance_id() < 0) end; + + if (!attachrid(.aid)) { + npctalk "The caster ceased to exist!"; + stopnpctimer; + end; + } + + getmapxy(.@m$, .@x, .@y, 0, .aid); + if (.@m$ != .mp$) { + npctalk "The caster is gone!"; + stopnpctimer; + end; + } + if (.@x < 52 || + .@x > 54 || + .@y < 37 || + .@y > 38) { + npctalk "The caster left the power circle!"; + dispbottom l("The magic accumulated backfires at you!"); + heal -(.power), -(.power/2); + stopnpctimer; + end; + } + + // Harm you + //misceffect FX_CHANNELLING_RAISE; + percentheal -4, -1; + + if (ispcdead()) { + npctalk "The caster is dead!"; + stopnpctimer; + end; + } + if (Sp <= 0) { + npctalk "The caster ran out of mana!"; + stopnpctimer; + end; + } + + // Deplete power over time + // But only if not doing solo + if (getmapusers(.mp$) > 1 || .mloss < 0) + .power -= .mloss; + + // Remove stones inside the circle + .@cat=getareadropitem(.mp$, 52, 37, 54, 39, LazuriteShard, true) * 12; + .@cbt=getareadropitem(.mp$, 52, 37, 54, 39, LazuriteCrystal, true) * 32; + .@cct=getareadropitem(.mp$, 52, 37, 54, 39, LazuriteHeart, true) * 65; + + // Grant them POWER + .power+=.@cat+.@cbt+.@cct; + .@cur = 4 * .power / .maxpw; // Goes from 0 to 4 + + // Check status and update + if (.@cur < .status) + npctalk "NOTICING a sudden power loss!"; + else if (.@cur > .status) + npctalk "NOTICING an increase of power!"; + .status=.@cur; + + // Are we done? + if (.power >= .maxpw) { + npctalk "*the gate spell has been redefined*"; + if (getcharid(1) > 0) { + $@VALIA_STATUS[getcharid(1)]=9; + stopnpctimer; + .power=0; + .mloss=0; + .maxpw=0; + .state=0; + .mp$=""; + .aid=0; + } + stopnpctimer; + end; + } + detachrid(); + // Should we spawn items? + .@t = (getnpctimer(0)+500) / 1000; + // Item time! + if (.@t == 6 || + .@t == 10) { + .@i = 0; + // More Shards than Crystals and Hearts + freeloop(true); + while (.@i < ((5-getmapusers(.mp$)) * 2 + 5)) { + .@it=any(LazuriteShard, LazuriteShard, LazuriteShard, + LazuriteCrystal, LazuriteCrystal, + LazuriteHeart); + makeitem(.@it, 1, .mp$, rand2(27, 77), rand2(20, 47)); + .@i += 1; + } + freeloop(false); + } + end; + +OnTalkNearby: + // not very obvious stuff by gumi + .@no_nick$ = strip(substr($@p0$, getstrlen(strcharinfo(PC_NAME)) + 3, getstrlen($@p0$) - 1)); + .@message$ = strtoupper(.@no_nick$); + if (.@message$ == "CATALAZULI") { + goto OnChannel; + } + end; + +OnInit: +OnInstanceInit: + .distance=3; + .pid=getnpcid(); + // For players + .aid=0; + .mp$=""; + .power=0; + .state=0; + .maxpw=0; + .mloss=0; + + debugmes "Gemini Pattern %d", .pid; + //defpattern(.pid, "^([Kk][Aa][Tt][Aa][Zz][Uu][Ll][Ii])$", "OnTalkNearby"); + defpattern(.pid, "^(.*)$", "OnTalkNearby"); + activatepset(.pid); + end; +} + diff --git a/npc/034-4/_import.txt b/npc/034-4/_import.txt new file mode 100644 index 0000000..4e1f47c --- /dev/null +++ b/npc/034-4/_import.txt @@ -0,0 +1,6 @@ +// Map 034-4: Forsaken Inn +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/034-4/exit.txt", +"npc/034-4/intro.txt", +"npc/034-4/lobby.txt", +"npc/034-4/storage.txt", diff --git a/npc/034-4/exit.txt b/npc/034-4/exit.txt new file mode 100644 index 0000000..07d96ea --- /dev/null +++ b/npc/034-4/exit.txt @@ -0,0 +1,276 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Part D: Final Chamber + +034-4,144,82,0 script #GeminiFExit NPC_HIDDEN,0,0,{ + +OnWumpus: + GeminiCheck(15); + .@q = getq2(HurnscaldQuest_Gemini); + // TODO: Maybe exchange an Wumpus Egg for a Sunny Crystal or Mylarin Dust? + // Have a NPC do so only once, for the Savior set. + if (.@q == 0 || .@q == 2 || (.@q > 5 && !(.@q % 3))) + getitem WumpusEgg, 1; + else + getitem LightGreenDiamond, 1; + getexp 0, rand2(7500, 9999); + setq2 HurnscaldQuest_Gemini, .@q + 1; + @forced_sick$ = ""; + end; + +OnRw: + getitem StrangeCoin, 1; + if (!GEMINI_WINNER) + GEMINI_WINNER = gettimetick(2); + end; + +OnExit: + .@p=getcharid(1); + partytimer(MAZE_MAP$, 10, "#GeminiFExit::OnRw", getcharid(1)); + if ($GEMINI_WINNER$ == "") { + $GEMINI_WINNER$=strcharinfo(0); + channelmes("#world", $GEMINI_WINNER$+" is the first player to finish Gemini Sisters Quest!! GG, dude! %%N"); + announce "All hail ##B"+$GEMINI_WINNER$+"##b, first to complete the ##3Gemini Sisters Quest!", bc_all|bc_npc; + getitem PrismGift, 1; + mesc l("CONGRATULATIONS! You are the first player to finish Gemini Sisters quest!!"), 2; + mesc l("You just gained a Prism Gift for your bravery!"), 2; + next; + } + sleep2(400); + warp "014-2-2", 35, 20; + warpparty("014-2-2", 35, 20, getcharid(1), MAZE_MAP$, true); + end; + +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(15); + // Only the party leader go ahead + if (strcharinfo(0) != getpartyleader(getcharid(1))) { + mes l("Only %s has the key.", getpartyleader(getcharid(1))); + close; + } + + mesc l("Are you sure you want to leave?"), 1; + mesc l("You, and everyone on the party, won't be able to return."), 1; + if (askyesno() == ASK_NO) close; + closeclientdialog; + + // Create maze and populate (From 45x45 to 60x60) + CreateMaze(IOT_CHAR, MAZE_SIZE_M | MAZE_SIZE_G); + .@mx=getmapinfo(MAPINFO_SIZE_X, MAZE_MAP$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, MAZE_MAP$)-20; + .@tl=(20-.@mx)*(20-.@my) * 3 / 10; // Total tiles + collision guess + .@tl=(.@tl / 10) + 1; // Monster density is a bit random + + /* *** Copied from 006-5/groata.txt & 018-2-2/main.txt! *** */ + .@mb[0] = MagicGoblin; + .@mb[1] = CaveMaggot; + array_push(.@mb, BronzeChest); + array_push(.@mb, SmallMagicBif); + array_push(.@mb, Bif); + array_push(.@mb, RobinBandit); + array_push(.@mb, SilverChest); + array_push(.@mb, DustGatling); + array_push(.@mb, MagicBif); + array_push(.@mb, DustRifle); + array_push(.@mb, DustRevolver); + array_push(.@mb, GoldenChest); + array_push(.@mb, GreatMoubooSlime); + array_push(.@mb, Piousse); + array_push(.@mb, ManaPiou); + array_push(.@mb, ForestPiou); + array_push(.@mb, HouseMaggot); + array_push(.@mb, LittleYellowSlime); + array_push(.@mb, MoubooSlime); + array_push(.@mb, SmallFrog); + array_push(.@mb, BigFrog); + array_push(.@mb, Lavern); + array_push(.@mb, LittleRedSlime); + array_push(.@mb, ChocolateSlime); + array_push(.@mb, Duck); + array_push(.@mb, Bat); + array_push(.@mb, CaveMaggot); + array_push(.@mb, ManaGhost); + array_push(.@mb, ManaBug); + array_push(.@mb, FireGoblin); + array_push(.@mb, ViciousSquirrel); + array_push(.@mb, RedScorpion); + array_push(.@mb, WhiteSlime); + array_push(.@mb, AzulSlime); + array_push(.@mb, DesertLogHead); + array_push(.@mb, RedSlime); + array_push(.@mb, DesertBandit); + array_push(.@mb, Sarracenus); + array_push(.@mb, IceMaggot); + array_push(.@mb, VampireBat); + array_push(.@mb, Bandit); + array_push(.@mb, Assassin); + array_push(.@mb, Skeleton); + array_push(.@mb, CaveSnake); + array_push(.@mb, GreenSlime); + array_push(.@mb, CopperSlime); + array_push(.@mb, YellowSlime); + array_push(.@mb, SantaSlime); + array_push(.@mb, LavaSlime); + array_push(.@mb, Bluepar); + array_push(.@mb, DeathCat); + array_push(.@mb, Moggun); + array_push(.@mb, RedMushroom); + array_push(.@mb, CandiedSlime); + array_push(.@mb, OldSnake); + array_push(.@mb, GrassSnake); + array_push(.@mb, Snake); + array_push(.@mb, BlackSlime); + array_push(.@mb, Pollet); + array_push(.@mb, PiouKnight); + array_push(.@mb, Shrewboo); + array_push(.@mb, Wolvern); + array_push(.@mb, FireSkull); + array_push(.@mb, DarkLizard); + array_push(.@mb, ArmoredSkeleton); + array_push(.@mb, BlackScorpion); + array_push(.@mb, ElectroWorm); + array_push(.@mb, EarthFairy); + array_push(.@mb, FireFairy); + array_push(.@mb, WaterFairy); + array_push(.@mb, WindFairy); + array_push(.@mb, PoisonFairy); + array_push(.@mb, MountainSnake); + array_push(.@mb, HoodedNinja); + array_push(.@mb, ForestMushroom); + array_push(.@mb, GoldenScorpion); + array_push(.@mb, FallenGuard2); + array_push(.@mb, WickedMushroom); + array_push(.@mb, Archant); + array_push(.@mb, Scar); + array_push(.@mb, Crafty); + array_push(.@mb, Forain); + array_push(.@mb, GreenDragon); + array_push(.@mb, Michel); + array_push(.@mb, Troll); + array_push(.@mb, EliteDuck); + array_push(.@mb, AzulSkullSlime); + array_push(.@mb, Moonshroom); + array_push(.@mb, RedSkullSlime); + array_push(.@mb, Terranite); + array_push(.@mb, JackO); + array_push(.@mb, BlackMamba); + array_push(.@mb, GreenSkullSlime); + array_push(.@mb, BloodyMouboo); + array_push(.@mb, GoboBear); + array_push(.@mb, TerraniteProtector); + array_push(.@mb, WhirlyBird); + + /* Spawn them and make hostile */ + freeloop(true); + for (.@i = 0; .@i < 1+(.@tl); .@i++) { + .@mid = any_of(.@mb); + .@m=areamonster(MAZE_MAP$, 20, 20, .@mx, .@my, strmobinfo(1, .@mid), .@mid, 1); + set_aggro(.@m); + } + freeloop(false); + + // Spawn & Configure the boss monster + // Defeating the boss yields a bonus + .@mob=areamonster(MAZE_MAP$, 60, 60, .@mx-40, .@my-40, "Wumpus?", PanthomLord, 1, "#GeminiFExit::OnWumpus"); + setunitdata(.@mob, UDT_MAXHP, 250000); + setunitdata(.@mob, UDT_HP, 250000); + setunitdata(.@mob, UDT_SPEED, 275); + setunitdata(.@mob, UDT_HIT, 3500); + setunitdata(.@mob, UDT_DMOTION, 50); + + // NOTE: Once you exit, put the quest in cooldown for 4 hours as well. + .@mx = getmapinfo(MAPINFO_SIZE_X, MAZE_MAP$); .@my = getmapinfo(MAPINFO_SIZE_Y, MAZE_MAP$); + .@mob=monster(MAZE_MAP$, .@mx-25, .@my-25, "Exit", FortressGate, 1, "#GeminiFExit::OnExit"); + setunitdata(.@mob, UDT_MAXHP, 50000); + setunitdata(.@mob, UDT_HP, 50000); + + // Miscellaneous data + .@old$=getmap(); + .@p=getcharid(1); + InitMaze(7200, false); + $@VALIA_STATUS[.@p]=16; + sleep2(500); + changeplayermusic "Arabesque.ogg"; + dispbottom l("It was a trap! We must escape!"); + $@VALIA_MAP$[.@p]=getmap(); + partytimer(.@old$, 2000, "#GeminiFExit::OnSick", .@p); + sleep2(2000); + @forced_sick$ = getmap(); + doevent "#DungeonCore::OnSick"; + end; + +OnSick: + .@p=getcharid(1); + MAZE_MAP$ = $@VALIA_MAP$[.@p]; + // Find random, warpable coordinates + .@e=0; .@x=0; .@y=0; + .@mx=40; .@my=40; + do { + .@x = rand2(20, .@mx); + .@y = rand2(20, .@my); + .@e += 1; + if (.@e > 30) { + consolebug("Too many failures at Maze \"%s\"! Trying anyway!", MAZE_MAP$); + break; + } + } while (!checknpccell(MAZE_MAP$, .@x, .@y, cell_chkpass)); + + warp MAZE_MAP$, .@x, .@y; + sleep2(500); + changeplayermusic "Arabesque.ogg"; + dispbottom l("It was a trap! We must escape!"); + sleep2(2000); + @forced_sick$ = getmap(); + doevent "#DungeonCore::OnSick"; + end; +} + +034-4,146,83,0 script Chest#gemini NPC_CHEST,{ + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(15); + // Already taken + if (@mystatus > 20) { + dispbottom l("I already took my share from this chest."); + end; + } + // One prize per person + inventoryplace Iten, 1; + @mystatus = 99; + // Non party leaders receive "less" + if (strcharinfo(0) != getpartyleader(getcharid(1))) { + Mobpt+=7500; + getexp 96000, 900; + dispbottom l("I found %s and %s!", "7,500 "+l("monster points"), "96,000 "+l("experience points")); + end; + } + // Party leaders receive "more" + .@q = getq(HurnscaldQuest_Gemini); + setq HurnscaldQuest_Gemini, .@q + 1; + //getitem RentCart, 1; // Not rented + switch (.@q) { + case 1: + getitem SarabArmlet, 1; + dispbottom l("I found %s!", l("a(n) ")+getitemlink(SarabArmlet)); + break; + case 2: + getitem StrangeCoin, 150; + getitem MysteriousFruit, 1; + dispbottom l("I found %s and %s!", "150 "+getitemlink(StrangeCoin), ("a(n) ")+getitemlink(MysteriousFruit)); + break; + default: + Mobpt+=7500; + getexp 96000, 900; + dispbottom l("I found %s and %s!", "7,500 "+l("monster points"), "96,000 "+l("experience points")); + end; + } + end; + +OnInit: + .distance=2; + end; +} + + diff --git a/npc/034-4/intro.txt b/npc/034-4/intro.txt new file mode 100644 index 0000000..43692a3 --- /dev/null +++ b/npc/034-4/intro.txt @@ -0,0 +1,211 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Part A: Party Room + +034-4,43,52,0 script #GeminiNoBack NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(8); + npctalkonce l("Oh noes ─ the door is sealed! We can only press forward and failure is final!"); + end; +} + +034-4,43,51,0 script #GeminiIntro NPC_HIDDEN,2,2,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(8); + .@p=getcharid(1); + if (strcharinfo(0) != getpartyleader(.@p)) end; + + if (!.state) { + .mp$ = getmap(); + .pn$ = getpartyname(getcharid(1)); + .pid = getcharid(1); + .state = true; + initnpctimer; + killmonsterall(.mp$); // Cancel everything done thus far, incl. showdown + } + end; + +OnTimer1000: + .luvia = monster(.mp$, 48, 45, "Luvia Gemini", Luvia, 1); + immortal(.luvia); + setunitdata(.luvia, UDT_MODE, MD_BOSS|MD_PLANT|MD_NOKNOCKBACK); + end; + +OnTimer2500: + unittalk(.luvia, "Well, well, well, look at what we have here!"); + end; + +OnTimer6000: + unittalk(.luvia, sprintf("If it isn't the so-called \"%s\"!", .pn$)); + end; + +OnTimer9500: + unittalk(.luvia, "I'm sure it was a long journey to reach here, and well, this IS an Inn."); + end; + + +OnTimer13000: + unittalk(.luvia, "Where are my manners, of course I'll offer you room to sleep..."); + end; + + +OnTimer16500: + unittalk(.luvia, "...Yes, I'll put all of you to sleep... PERMANENTLY! Hahahaha!"); + .@pi = getmapusers(.mp$) + 2; + areamonster(.mp$, 45, 40, 54, 45, strmobinfo(1, Scar), Scar, .@pi / 2); + setunitdata(.luvia, UDT_MODE, MD_BOSS|MD_PLANT|MD_NOKNOCKBACK|MD_CANMOVE); + unitwalk(.luvia, 51, 45); + end; + +OnTimer18000: + unitwalk(.luvia, 51, 38); + end; + +OnTimer20000: + .@pi = getmapusers(.mp$) + 1; + areamonster(.mp$, 45, 40, 54, 45, strmobinfo(1, Scar), Scar, .@pi); + unittalk(.luvia, "HAHAHahahaha!"); + unitwalk(.luvia, 50, 34); + end; + +OnTimer24000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 37, 33, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 43, 52, strmobinfo(1, Scar), Scar, .@pi); + unitwalk(.luvia, 50, 29); + end; + +OnTimer28000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 50, 29, strmobinfo(1, Scar), Scar, .@pi); + unitwarp(.luvia, "034-4", 45, 45); + end; + +OnTimer25000: + unitkill(.luvia); + end; + +OnTimer50000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 50, 29, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 43, 52, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 37, 33, strmobinfo(1, Scar), Scar, .@pi * 3 / 2); + end; + +// 1 minute + +OnTimer110000: + monster(.mp$, 50, 29, strmobinfo(1, Scar), Scar, 1); + monster(.mp$, 43, 52, strmobinfo(1, Scar), Scar, 1); + monster(.mp$, 37, 33, strmobinfo(1, Scar), Scar, 1); + end; + +// 40 seconds + +OnTimer150000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 50, 29, strmobinfo(1, Scar), Scar, .@pi * 3 / 2); + monster(.mp$, 43, 52, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 37, 33, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 43, 52, strmobinfo(1, BlackMamba), BlackMamba, 1); + end; + +// 1 minute + +OnTimer210000: + monster(.mp$, 50, 29, strmobinfo(1, Scar), Scar, 1); + monster(.mp$, 43, 52, strmobinfo(1, Scar), Scar, 1); + monster(.mp$, 37, 33, strmobinfo(1, Scar), Scar, 1); + end; + +// 1 minute + +OnTimer270000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 50, 29, strmobinfo(1, Scar), Scar, .@pi * 3 / 2); + monster(.mp$, 43, 52, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 37, 33, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 43, 52, strmobinfo(1, Terranite), Terranite, 1+(.@pi/2)); + end; + +// 1.5 minutes + +OnTimer360000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 50, 29, strmobinfo(1, Scar), Scar, 1+(.@pi/2)); + monster(.mp$, 43, 52, strmobinfo(1, Scar), Scar, 1+(.@pi/2)); + monster(.mp$, 37, 33, strmobinfo(1, Scar), Scar, 1+(.@pi/2)); + monster(.mp$, 50, 29, strmobinfo(1, Terranite), Terranite, .@pi); + monster(.mp$, 43, 52, strmobinfo(1, Forain), Forain, .@pi); + monster(.mp$, 37, 33, strmobinfo(1, AzulSkullSlime), AzulSkullSlime, 1+(.@pi/2)); + end; + +// 1 minute + +OnTimer420000: + monster(.mp$, 50, 29, strmobinfo(1, Scar), Scar, 1); + monster(.mp$, 43, 52, strmobinfo(1, Scar), Scar, 1); + monster(.mp$, 37, 33, strmobinfo(1, Scar), Scar, 1); + end; + +// 1 minute + +OnTimer480000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 50, 29, strmobinfo(1, Terranite), Terranite, .@pi); + monster(.mp$, 43, 52, strmobinfo(1, BlackSlimeMother), BlackSlimeMother, 1); + monster(.mp$, 37, 33, strmobinfo(1, BlackMamba), BlackMamba, .@pi + 4); + end; + +// +1 minute + +// Bypass, or it'll take... a while +OnTimer40000: + if (!$@GM_OVERRIDE) end; +OnTimer540000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 50, 29, strmobinfo(1, GoboBear), GoboBear, 1+(.@pi/2)); + $@VALIA_STATUS[.pid] = 10; + stopnpctimer; + end; + +OnInit: +OnInstanceInit: + .state = false; + .mp$ = ""; + .pn$ = ""; + .pid = 0; + .luvia = 0; + end; +} + + +034-4,50,29,0 script #GeminiPartB NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(8); + .@p=getcharid(1); + if ($@VALIA_STATUS[.@p] < 10) { + dispbottom l("Uh? I can't pass. I wonder why, maybe I need to wait?"); + end; + } + if (mobcount(getmap(), "all") > 0) { + dispbottom l("I should defeat all mobs before passing."); + end; + } + if (mobcount(getmap(), "all") <= 0 && $@VALIA_STATUS[.@p] == 10) { + $@VALIA_STATUS[.@p]=11; + } + if ($@VALIA_STATUS[.@p] >= 11) { + slide 33, 81; + } + end; +} + diff --git a/npc/034-4/lobby.txt b/npc/034-4/lobby.txt new file mode 100644 index 0000000..dd9a267 --- /dev/null +++ b/npc/034-4/lobby.txt @@ -0,0 +1,263 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Part C: Showdown + +034-4,98,22,0 script #GeminiPartD NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(13); + .@p=getcharid(1); + if ($@VALIA_STATUS[.@p] < 14 || mobcount(getmap(), "#GeminiShowdown::OnABC")) { + dispbottom l("Luvia is too dangerous to be left alone."); + end; + } + if (mobcount(getmap(), "all") > 0) { + dispbottom l("I cannot leave until Luvia and her allies are dead."); + end; + } + if (mobcount(getmap(), "all") <= 0 && $@VALIA_STATUS[.@p] == 14) { + $@VALIA_STATUS[.@p]=15; + } + if ($@VALIA_STATUS[.@p] >= 15) { + if (!@v_fanfare) + specialeffect(FX_FANFARE, SELF, getcharid(3)); + slide 143, 96; + } + end; +} + +// 98 36 with radius 12? +034-4,98,28,0 script #GeminiShowdown NPC_HIDDEN,2,1,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(13); + .@p=getcharid(1); + if (strcharinfo(0) != getpartyleader(.@p)) end; + + if (!.state) { + .mp$ = getmap(); + .beats = 0; + .pid = getcharid(1); + .state = true; + initnpctimer; + killmonsterall(.mp$); // Cancel everything done thus far, incl. showdown + } + end; + +OnABC: + // Extra drop chance (20% chance) + getmapxy(.@m$, .@x, .@y, 0); + if (!rand2(5)) + makeitem BronzeBossGift, 1, .@m$, .@x, .@y; + end; + +OnTimer1000: + .luvia = monster(.mp$, 98, 32, "Luvia Gemini", Luvia, 1); + immortal(.luvia); + setunitdata(.luvia, UDT_MODE, MD_BOSS|MD_PLANT|MD_NOKNOCKBACK); + end; + +OnTimer2500: + unittalk(.luvia, "Ara! Look at who is trying to sneak past me!"); + end; + +OnTimer6000: + unittalk(.luvia, "How silly. I prepared this party for ya, you know."); + end; + +OnTimer9500: + unittalk(.luvia, "Isbamuth wants all of you dead ─ And I'll carry out his orders."); + end; + +OnTimer13000: + unittalk(.luvia, "Long live Isbamuth... And death to traitors! Now begone!!"); + end; + +OnTimer15000: + // Create a new Luvia Gemini + unitwarp(.luvia, "034-4", 98, 32); + unitkill(.luvia); + .luvia = monster(.mp$, 98, 38, "Luvia Gemini", Luvia, 1, "#GeminiShowdown::OnABC"); + + // Grant her more HP if more players came to attack her + .@hp = getunitdata(.luvia, UDT_MAXHP) + (getmapusers(.mp$) * 12000); + setunitdata(.luvia, UDT_MAXHP, .@hp); + setunitdata(.luvia, UDT_HP, .@hp); + + // Reconfigure the AI + .@opt=getunitdata(.luvia, UDT_MODE); + // Add knockback immunity + .@opt=.@opt|MD_NOKNOCKBACK; + // Make it more op + .@opt=.@opt|MD_DETECTOR; + .@opt=.@opt|MD_CASTSENSOR_CHASE; + .@opt=.@opt|MD_CASTSENSOR_IDLE; + .@opt=.@opt|MD_CHANGECHASE; + .@opt=.@opt|MD_CHANGETARGET_MELEE; + .@opt=.@opt|MD_CHANGETARGET_CHASE; + setunitdata(.luvia, UDT_MODE, .@opt); + + + // Prepare the party, lalala! + .@pi = getmapusers(.mp$) * 2 + 1; + monster(.mp$, 117, 51, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 83, 58, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 83, 51, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 114, 29, strmobinfo(1, Scar), Scar, .@pi); + monster(.mp$, 98, 22, strmobinfo(1, Scar), Scar, .@pi); + $@VALIA_STATUS[.pid] = 14; + end; + +// The fight loops from 20k (the restart point) +// So this cycle happens every 10 seconds +OnTimer60000: +OnTimer45000: + consolewarn("Warning, fail-safe mechanism triggered to Luvia."); +OnTimer30000: + if (mobcount(.mp$, "#GeminiShowdown::OnABC") < 1) { + stopnpctimer; + end; + } + .beats+=1; + + /* Prepare some data */ + .@hp = getunitdata(.luvia, UDT_HP) * 10 / getunitdata(.luvia, UDT_MAXHP); + .@pi = getmapusers(.mp$); + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, .luvia); + .@c=getunits(BL_PC, .@pcs, MAX_CYCLE_PC, .@m$); + /*.@mvp=0;.@rnd=0;.@def=-1; + for (.@i = 0; .@i < .@c; .@i++) { + if (!.@rnd || !rand2(.@c)) + .@rnd=.@pcs[.@i]; + if (readbattleparam(.@pcs[.@i], UDT_DEF) > .@def) { + if (readparam(Hp, .@pcs[.@i]) < 1) continue; + .@mvp=.@pcs[.@i]; + .@def=readbattleparam(.@pcs[.@i], UDT_DEF); + } + }*/ + + // Luvia's spell casting + // She casts every ~30 seconds + switch (.beats % 18) { + case 0: + case 6: + case 12: + unittalk(.luvia, "Hahahah, die, die!"); + specialeffect(64, AREA, .luvia); + sleep(1000); + monster(.mp$, .@x, .@y, strmobinfo(1, Scar), Scar, .@pi + max(1, (11 - .@hp) / 10)); + monster(.mp$, .@x, .@y, strmobinfo(1, MagicGoblin), MagicGoblin, .@pi + max(1, (11 - .@hp) / 10)); + // Doors reinforcements + // As time passes, they get stronger + // But after 7 minutes, it resets + switch ((.beats / 6) % 14) { + case 0: + .@mob = Skeleton; break; // Shouldn't trigger + case 1: + .@mob = LavaSlime; break; + case 2: + .@mob = ArmoredSkeleton; break; + case 3: + .@mob = DustRifle; break; + case 4: + .@mob = DarkLizard; break; + case 5: + .@mob = HoodedNinja; break; + case 6: + .@mob = Archant; break; + case 7: + .@mob = Scar; break; + case 8: + .@mob = Terranite; break; + case 9: + .@mob = EliteDuck; break; + case 10: + .@mob = Troll; break; + case 11: + .@mob = YellowSkullSlime; break; + case 12: + .@mob = Michel; break; + case 13: + .@mob = JackO; break; + } + + monster(.mp$, 114, 29, strmobinfo(1, .@mob), .@mob, 1); + // If you can't deal enough damage, less support comes + if (.@hp <= 7) // Up to 79% HP + monster(.mp$, 117, 51, strmobinfo(1, .@mob), .@mob, 1); + if (.@hp <= 6) // Up to 69% HP + monster(.mp$, 98, 22, strmobinfo(1, .@mob), .@mob, 1); + if (.@hp <= 4) // Up to 39% HP + monster(.mp$, 83, 58, strmobinfo(1, .@mob), .@mob, 1); + if (.@hp <= 3) // Up to 29% HP + monster(.mp$, 83, 51, strmobinfo(1, .@mob), .@mob, 1); + break; + case 3: + case 9: + case 15: + specialeffect(60, AREA, .luvia); + sleep(500); + .@r = (10-.@hp) / 5 + rand2(4); // From 0 to 5 + // Blind has extra chance until her HP falls below 60% + // Curse won't happen if her HP is equal or above 60% + // Once her HP falls below 10%, she no longer silences + // And her poison is strengthened. + switch (.@r) { + case 1: + unittalk(.luvia, "The dead are ##Bsilent##b, just like you!"); + .@sc = SC_SILENCE; + break; + case 2: + unittalk(.luvia, "I am your doom, and ##Bpoison##b is my tool!"); + .@sc = (.@hp < 1 ? SC_DPOISON : SC_POISON); + break; + case 3: + unittalk(.luvia, "You shall ##Bbleed##b, and cease!"); + .@sc = SC_BLOODING; + break; + case 4: + unittalk(.luvia, "You dare to underestimate me?! ##BCurse##b you!"); + .@sc = SC_CURSE; + break; + default: + unittalk(.luvia, "You won't see what killed you when ##Bblind##b!"); + .@sc = SC_BLIND; + break; + } + areasc(9, 45000, .@sc, BL_PC|BL_HOM|BL_MER, 1, "filter_always", .luvia, 95000); + .@dmg=100 + (max(1, (11 - .@hp) / 10) * 40); + areaharm(.luvia, 9, .@dmg, HARM_MAGI, any(Ele_Water,Ele_Fire,Ele_Wind,Ele_Earth), "filter_always", BL_PC|BL_MER|BL_HOM); + break; + default: + specialeffect(60, AREA, .luvia); + unittalk(.luvia, any("I am getting tired of you!", + "That's all you can muster?", + "Long live Isbamuth!", + "Hahahahah!", + "Incompetent! Simply incompetent!", + "You shall not resist!", + "I'm not afraid of you!")); + sleep(500); + .@dmg=40 + (max(1, (11 - .@hp) / 10) * 20); + areaharm(.luvia, 9, .@dmg, HARM_MAGI, any(Ele_Water,Ele_Fire,Ele_Wind,Ele_Earth), "filter_always", BL_PC|BL_MER|BL_HOM); + break; + } + + // And we're done! Wait 10 seconds before next casting + setnpctimer 20000; + end; + +OnInit: +OnInstanceInit: + .state = false; + .mp$ = ""; + .pid = 0; + .luvia = 0; + .beats = 0; + end; +} + diff --git a/npc/034-4/storage.txt b/npc/034-4/storage.txt new file mode 100644 index 0000000..893971f --- /dev/null +++ b/npc/034-4/storage.txt @@ -0,0 +1,186 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Gemini Sisters Quest - Part B: Storage Room + +034-4,45,78,0 script #GeminiPartC NPC_HIDDEN,0,0,{ + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(11); + .@p=getcharid(1); + if ($@VALIA_STATUS[.@p] < 12) { + dispbottom l("Uh? I can't pass. I wonder why, maybe I need to wait?"); + end; + } + if (mobcount(getmap(), "all") > 0) { + dispbottom l("I should defeat all mobs before passing."); + end; + } + if (mobcount(getmap(), "all") <= 0 && $@VALIA_STATUS[.@p] == 12) { + $@VALIA_STATUS[.@p]=13; + } + if ($@VALIA_STATUS[.@p] >= 13) { + slide 83, 58; + } + end; +} + +034-4,33,77,0 script #GeminiStorei NPC_HIDDEN,2,2,{ +function storageutil; + end; +OnTouch: + if (instance_id() < 0 || getcharid(1) < 1) end; + GeminiCheck(11); + .@p=getcharid(1); + if (strcharinfo(0) != getpartyleader(.@p)) end; + + if (!.state) { + .mp$ = getmap(); + .pn$ = getpartyname(getcharid(1)); + .pid = getcharid(1); + .state = true; + /* We now must calculate amount of waves */ + /* It is a hack for now, though */ + .mto = 5 + (BaseLevel / 30) + getmapusers(.mp$); + initnpctimer; + killmonsterall(.mp$); // Cancel everything done thus far, incl. showdown + } + end; + +OnTimer1000: + .luvia = monster(.mp$, 37, 76, "Luvia Gemini", Luvia, 1); + immortal(.luvia); + setunitdata(.luvia, UDT_MODE, MD_BOSS|MD_PLANT|MD_NOKNOCKBACK); + end; + +OnTimer2500: + unittalk(.luvia, "So you aren't made of sugar!"); + end; + +OnTimer6000: + unittalk(.luvia, "I was worried you would all have melted on the lobby!"); + end; + +OnTimer9500: + unittalk(.luvia, "Hahaha, but this is just the beginning of your journey..."); + end; + + +OnTimer13000: + unittalk(.luvia, "I am Luvia Gemini, and this is my trial for you!"); + end; + + +OnTimer16500: + unittalk(.luvia, sprintf("Show me of what you are made of, %s!", .pn$)); + .@pi = getmapusers(.mp$) + 2; + monster(.mp$, 44, 78, strmobinfo(1, Scar), Scar, .@pi); + end; + +OnTimer19000: + unittalk(.luvia, "HAHAHahahaha!"); + end; + +OnTimer20000: + unitwarp(.luvia, "034-4", 45, 45); + end; + +OnTimer21000: + unitkill(.luvia); + end; + +OnTimer30000: + storageutil(); + .sto += 1; + end; + +OnTimer45000: + if (.sto < .mto) + setnpctimer 22000; + monster(.mp$, 44, 78, strmobinfo(1, BlackMamba), BlackMamba, 1); + end; + +OnTimer60000: + .@pi = getmapusers(.mp$) + 1; + monster(.mp$, 44, 78, strmobinfo(1, GoboBear), GoboBear, .@pi); + $@VALIA_STATUS[.pid] = 12; + stopnpctimer; + end; + + +function storageutil { + // Decide if we'll spawn or add items. Previous failures are NOT considerated. + .@r=rand2(10000)+(.sto * 100); + + // Super rare drop?! (~1.5%) + if (.@r < 120 || .@r > 10000) { + makeitem(any(DeathPotion, PoisonAmmoBox, AncientBlueprint, ThornAmmoBox, MercBoxD, ScholarshipBadge, DarkDesertMushroom), 1, .mp$, rand2(30, 35), rand2(83, 86)); + } + + // Super strong monster?! (4%) + if ((.@r > 6000 && .@r < 6400) || .sto == 15 || .sto == 17 || .sto >= 20) { + .@mob=any(WanderingShadow, SeaSlimeMother, NightDragon, GiantMutatedBat, Reaper, Mandragora); + monster(.mp$, 44, 78, strmobinfo(1, .@mob), .@mob, 1); + // Warn players? + } + + // Compulsory monster spawn + switch (.sto) { + case 0: + .@mob = ArmoredSkeleton; break; + case 1: + .@mob = DustRifle; break; + case 2: + .@mob = HoodedNinja; break; + case 3: + .@mob = WickedMushroom; break; + case 4: + .@mob = any(Archant, Scar); break; + case 5: + .@mob = Scar; break; + case 6: + .@mob = AzulSkullSlime; break; + case 7: + .@mob = Forain; break; + case 8: + .@mob = GreenDragon; break; + case 9: + .@mob = Michel; break; + case 10: + .@mob = EliteDuck; break; + case 11: + .@mob = Terranite; break; + case 12: + .@mob = JackO; break; + case 13: + .@mob = BloodyMouboo; break; + default: + .@mob = GoboBear; + } + monster(.mp$, 44, 78, strmobinfo(1, .@mob), .@mob, 1+getmapusers(.mp$)); + + // Compulsory item drop + // If it falls on a collision, the item won't be created at all + freeloop(true); + for (.@i=0; .@i <= ((.sto/2)+getmapusers(.mp$)); .@i++) { + makeitem(any(Wurtzite, ShadowHerb, AlizarinHerb, DiamondPowder, RubyPowder, EmeraldPowder, SapphirePowder, TopazPowder, AmethystPowder, CopperOre, IronOre, Coal, LeadOre, Lifestone, ScorpionClaw, WhiteFur, SquirrelPelt, TinOre, PileOfAsh, EmptyBottle, FluoPowder, TerraniteOre, SulfurPowder, LeatherPatch, LazuriteShard, Root, ReedBundle, GambogeHerb, MauveHerb, CobaltHerb, MaggotSlime, BugLeg, RawLog, BanditHood, BatWing, IronPowder, ArtichokeHerb, LeftCraftyWing, RightCraftyWing, Coral, BlueCoral, Pearl, Moss, RattoTail, RattoTeeth, Knife, SharpKnife, StrangeCoin, PurificationPotion, IcedBottle, Grenade, SmokeGrenade, TreasureMap, AgiPotionA, VitPotionA, IntPotionA, DexPotionA, LukPotionA, EmptyBox, HastePotion, StrengthPotion, Croconut, ChocolateBar, ChocolateBiscuit, PinkieLeg, Potatoz, Coffee, SnakeEgg, Plushroom, Chagashroom, Honey, MoubooSteak, Milk, Orange, CherryCake, Piberries, Aquada, Cheese, Bread, Acorn, Manana), 1, .mp$, rand2(30, 71), rand2(44, 87)); + } + freeloop(false); + return; +} + +OnInit: +OnInstanceInit: + .state = false; + .mp$ = ""; + .pn$ = ""; + .pid = 0; + .luvia = 0; + .sto = 0; + .mto = 0; + end; +} + + diff --git a/npc/042-0/_import.txt b/npc/042-0/_import.txt new file mode 100644 index 0000000..93ab827 --- /dev/null +++ b/npc/042-0/_import.txt @@ -0,0 +1,5 @@ +// Map 042-0: Camelot - Throne Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-0/_warps.txt", +"npc/042-0/arthur.txt", +"npc/042-0/mf.txt", diff --git a/npc/042-0/_warps.txt b/npc/042-0/_warps.txt new file mode 100644 index 0000000..064ab81 --- /dev/null +++ b/npc/042-0/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 042-0: Camelot - Throne Room warps +042-0,60,81,0 warp #042-0_60_81 3,0,014-4,67,27 diff --git a/npc/042-0/arthur.txt b/npc/042-0/arthur.txt new file mode 100644 index 0000000..fc7565f --- /dev/null +++ b/npc/042-0/arthur.txt @@ -0,0 +1,524 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Part of Kamelot Guild Dungeon +// TODO: Random guard (Tezer) on the cursed soldier statues (easter egg) + +042-0,58,28,0 script King Arthur NPC_KING_ARTHUR,{ + function arthurSpawn; + .@g=getcharid(2); + .@pos=getguildrole(.@g, getcharid(3)); + if (.@pos > GPOS_VICELEADER) goto L_Refusal; + if (getguildlvl(.@g) < 2) goto L_Annoyed; + if (!$KAMELOT_COOLDOWN[.@g]) goto L_Tutorial; + if ($KAMELOT_QUEST[.@g] & 1) goto L_Progress; + goto L_Prologue; + +L_Prologue: + mesn l("King Arthur the Micksha"); + mes l("Hello Adventurer."); + next; + mesn l("King Arthur the Micksha"); + mes l("So good you are here. We have a huge problem."); + next; + npctalk l("JAIL THEM!!"); + mesn l("King Arthur the Corrupted"); + mes col(l("JAIL THEM!!"), 9)+l(" -- No, wait!"); + next; + select + "[START] "+l("What is going on here?"), + l("Your problems are not mine. By the way, you really should consider taking a shower."), + l("[TUTORIAL]"), + "[QUIT] "+l("You are weird, I have to go, sorry."); + mes ""; + switch (@menu) { + case 1: + goto L_Quest; + case 2: + if (any(true, true, true, true, false)) + goto L_Smash; + else + goto L_Annoyed; + case 3: + goto L_Tutorial; + case 4: + if (!any(true, true, true, true, false)) + goto L_Smash; + else + goto L_Annoyed; + } + close; + +L_Quest: + mesn l("King Arthur the Micksha"); + mes l("Something is happening down there."); + next; + npctalk l("GUARDS! GET THEM!"); + mesn l("King Arthur the Corrupted"); + mes l("It is so dangerous, and it... ")+col(l("GUARDS! GET THEM!"), 9); + next; + mesn l("King Arthur the Micksha"); + mes l("You must be fast! The guards are also affected."); + next; + npctalk l("OFF WITH THEIR HEADS!"); + mesn l("King Arthur the Corrupted"); + mes col(l("OFF WITH THEIR HEADS!"), 9)+l(" Take this key, it opens the door behind my throne."); + next; + npctalk l("AFTER THEM!!"); + mesn l("King Arthur the Corrupted"); + mes l("Be careful! ")+col(l("AFTER THEM!!"), 9); + next; + // Be sure the quest only starts now, and destroy any eventual artifact + KamelotCleanup(.@g); + getguildmember(.@g, 2); // 2 = count by account IDs + $KAMELOT_QUEST[.@g]=1; + $KAMELOT_MX[.@g]=getguildavg(.@g); + $KAMELOT_PC[.@g]=$@guildmembercount; + $KAMELOT_KEY[.@g]=any(1,2,4,8); + $KAMELOT_PASSCODE[.@g]=rand2(1, 31); + // TODO: Kamelot Event could bypass the cooldown? ...Nah. + $KAMELOT_COOLDOWN[.@g] = gettimeparam(GETTIME_WEEKDAY); + mapannounce(getmap(), "##1KAMELOT CASTLE, GUILD DUNGEON: MISSION START!", bc_map); + // Spawn a few foot soldiers + arthurSpawn(1, 25, 29, 43, 37); + arthurSpawn(3, 22, 43, 43, 73); + arthurSpawn(5, 76, 55, 98, 77); + mesc l(".:: KAMELOT CASTLE, THE GUILD DUNGEON ::."), 1; + mes ""; + mes l("1. Investigate Kamelot Basements"); + mes l("2. Free Kamelot from its curse!"); + next; + mesc l("Please select quest difficulty."); + mesc l("This will affect rewards, and decision is final!"), 1; + select + l("Normal"), + l("Easy"), + l("Hard"), + l("Crazy"); + mes ""; + switch (@menu) { + case 2: // Easy + $KAMELOT_MX[.@g]-=15; + $KAMELOT_PC[.@g]=max(1, $KAMELOT_PC[.@g]*8/10); + mapannounce(getmap(), "Kamelot Difficulty set to: Easy.", bc_map); + break; + case 3: // Hard + $KAMELOT_MX[.@g]+=15; + $KAMELOT_PC[.@g]=max(1, $KAMELOT_PC[.@g]*12/10)+1; + mapannounce(getmap(), "Kamelot Difficulty set to: Hard.", bc_map); + break; + case 4: // Crazy(fefe) + $KAMELOT_MX[.@g]+=30; + $KAMELOT_PC[.@g]=max(1, $KAMELOT_PC[.@g]*15/10)+3; + mapannounce(getmap(), "Kamelot Difficulty set to: Crazy.", bc_map); + break; + } + close; + + +////////////////////////////////////////////////////////////////////////// +// King manages to control himself +L_Annoyed: + npctalk l("Don't bore me."); + closeclientdialog; + close; + +// King loses his coolness. +L_Smash: + npctalk l("MEEEEEERLIN!!! SMASH THEM!!"); + maptimer(getmap(), 300, instance_npcname(.name$)+"::OnPreSmash"); + closeclientdialog; + close; + +OnPreSmash: + specialeffect FX_CUPID, AREA, getcharid(3); + addtimer(380, instance_npcname(.name$)+"::OnSmash"); + end; + +OnSmash: + die(); + end; + +////////////////////////////////////////////////////////////////////////// +L_Progress: + if ($KAMELOT_QUEST[.@g] & 64) goto L_Reward; + npctalk l("What are you still doing here?! GO!!"); + mes l("Read tutorial again?"); + next; + if (askyesno() == ASK_YES) goto L_Tutorial; + close; + +L_Reward: + .@cn=$KAMELOT_MX[.@g]/3; + mesn; + mesq l("Thanks for vanishing the source of the curse."); + next; + mesn; + mesq l("Since Merlin left in an expedition with Morgan, strange things have been happening in Kamelot."); + next; + mesn; + mesq l("Please accept this reward from my part. And if anything else happens again, I'll leave the doors open for you and your guild."); + next; + warp "014-4", 67, 27; + if ($KAMELOT_QUEST[.@g] & 128) + goto L_DoReward2; + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|128; + getitem GuildCoin, .@cn; + guildgetexp(.@cn*1000); + // TODO: Make this a decent reward + getitembound any(SilverRing, GoldenRing), 1, 2; + goto L_DoReward2; + +////////////////////////////////////////////////////////////////////////// +L_Tutorial: + // Update variable + if ($KAMELOT_COOLDOWN[.@g] != gettimeparam(GETTIME_WEEKDAY)) + $KAMELOT_COOLDOWN[.@g]=gettimeparam(GETTIME_WEEKDAY)-1; + mes b(".:: KAMELOT CASTLE ::."); + mes "The Guild Dungeon of Kamelot Castle"; + mes ""; + mes l("Kamelot Castle is a GUILD DUNGEON which refreshes WEEKLY."); + mes l("Only the Guild Master and the Vice Leaders are capable of starting this quest."); + next; + mes l("An unlimited number of members can join."); + mes l("Once the quest is started, entrance will be closed."); + mes l("No one can go out during the quest, so ensure every guild member has proper equipment, potions, and time for this dungeon."); + next; + mes l("Additionally, for the optimal experience, ensure your party has at least the following members and items:"); + mes ""; + mes l("* Thief"); + mes l("* Mage"); + mes l("* %s", getitemlink(TreasureKey)); + mes l("* %s", getitemlink(Lockpicks)); + mes ""; + next; + mes b(".:: VICTORY REWARDS ::."); + mes l("Victory prizes include guild experience, guild money, and guild bound items."); + mes ""; + mes b(".:: DEFEAT PENALTIES ::."); + mes l("If your guild is defeat during the quest, you'll need to wait the weekly cooldown to expire."); + mes ""; + mes "----------------- by Lancelot"; + next; + clear; + if ($KAMELOT_QUEST[.@g] & 1) goto L_Progress; + goto L_Prologue; + + +////////////////////////////////////////////////////////////////////////// +L_Refusal: + if ($KAMELOT_QUEST[.@g] & 64) goto L_Reward2; + mesc l("This man is THE LEGENDARY KING ARTHUR!"); + mes ""; + mesc l("I better leave talking to him to the guild Vice Leaders and Master."); + close; + +L_Reward2: + mesn; + mesq l("Thanks for your services, knight. One day, you might even deserve a seat at this castle."); + next; + +L_DoReward2: + mesn; + mesq l("Farewell, until the next time. Shall we met again, and may the light guide our paths."); + warp "014-4", 67, 27; + .@cn=$KAMELOT_MX[.@g]/10; + // Penalty if you dragged the guild down + if (BaseLevel < getguildavg(.@g)*5/10) + .@cn=.@cn*5/10; + // Penalty if you carried the guild + if (BaseLevel > getguildavg(.@g)*15/10) + .@cn=.@cn*5/10; + // Your reward + .@cn+=1; + getitem GuildCoin, .@cn; + guildgetexp(.@cn*300); + getexp BaseLevel*10, .@cn*35; + Zeny+=.@cn*1000; + close; + +OnKillMob: + if (!playerattached()) end; + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g], $KAMELOT_MX[.@g]/2+1; + end; + +function arthurSpawn { + .@label$=instance_npcname(.name$)+"::OnKillMob"; + .@gcount=getarg(0); + .@x1=getarg(1); + .@x2=getarg(3); + .@y1=getarg(2); + .@y2=getarg(4); + .@g=getcharid(2); + if (.@g < 1) die(); + .@avg=$KAMELOT_MX[.@g]; + .@m$=getmap(); + for (.@i=0; .@i < .@gcount; .@i++) { + .@mobId=CursedSoldier; + .@mob=areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mobId), .@mobId, 1, .@label$); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg/6); + setunitdata(.@mob, UDT_AGI, 1+.@avg/7); + setunitdata(.@mob, UDT_VIT, 1+.@avg/6); + setunitdata(.@mob, UDT_INT, 1+.@avg/6); + setunitdata(.@mob, UDT_DEX, 1+.@avg/7); + setunitdata(.@mob, UDT_LUK, 1+.@avg/6); + setunitdata(.@mob, UDT_ADELAY, 2072); + setunitdata(.@mob, UDT_ATKRANGE, 1); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*20); + setunitdata(.@mob, UDT_HP, .@avg*20); + setunitdata(.@mob, UDT_ATKMIN, .@avg*25/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*45/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg*6/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*3/10); + setunitdata(.@mob, UDT_HIT, .@avg*4); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*25/10); // Advised: x4 + // Critical calculation + .@min=1; + .@max=max(.@min, min(15, .@avg/6)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + // Loop through + } + freeloop(false); + return; +} + +OnInit: + .distance=4; + .sex=G_MALE; + npcsit; + end; + +OnInstanceInit: + .distance=4; + .sex=G_MALE; + npcsit; + end; +} + + + + + + + + + + + + + + +042-0,69,24,0 script #K042-0_69_24 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@g=getcharid(2); + + // Quest already finished. + if ($KAMELOT_QUEST[.@g] & 64) { + dispbottom l("I should talk to King Arthur."); + end; + } + + // Quest (not yet) started + if ($KAMELOT_QUEST[.@g] & 1) { + warp "042-1@"+.@g, 55, 99; + } else { + dispbottom l("The door is locked."); + end; + } + + // Maybe event must be fired + if (!($KAMELOT_KEYMASK[.@g] & 16)) { + .@label$=instance_npcname("#KDoor0421", $@KAMELOT_ID[.@g])+"::OnArrival"; + deltimer .@label$; + addtimer 15000, .@label$; + } + end; +} + + + + + + + + + + + + + + + +042-0,86,27,0 script Guinevere NPC_GUINEVERE,{ + function guinevereSpawn; + .@g=getcharid(2); + .@pos=getguildrole(.@g, getcharid(3)); + if (.@pos > GPOS_VICELEADER) goto L_Refusal; + if ($KAMELOT_QUEST[.@g] & 1) goto L_Hint; + npctalk3 l("Oh, hello %s.", strcharinfo(0)); + end; + +L_Refusal: + mesn; + mesq l("Hello, %s.", strcharinfo(0)); + next; + mesc "["+strcharinfo(0)+"]"; + mesc l("She doesn't seems to trust me... Maybe I should bring a guild vice-leader, or even better, the guild master himself."); + close; + +L_Hint: + mesc l("Guinevere is the King's Wife. If you talk to her, the King will be upset and will send soldiers to you."), 1; + next; + mesc l("However, she may have a better assessment of the current situation better than you and your allies."), 1; + next; + mesc l("Continue anyway? You'll die if your allies cannot protect you!"), 1; + next; + if (askyesno() == ASK_NO) + close; + + // Okay... So here we go (again), spawning monsters + guinevereSpawn(1); + mesn; + mesq l("Ah, %s, a good thing you're here. You must help!", strcharinfo(0)); + next; + guinevereSpawn(1); + mesn; + mesq l("Something very terrible happened."); + next; + guinevereSpawn(2); + mesn; + mesq l("They're back - and they took hold of your majesty - my husband - king Arthur!"); + next; + guinevereSpawn(2); + mesn; + mesq l("Of course, he doesn't likes the fact you're talking to me."); + next; + guinevereSpawn(3); + mesn; + mesq l("*They* couldn't take hold of me, but I'm bound to this castle; I can't leave."); + next; + guinevereSpawn(3); + mesn; + mesq l("They are in the basement, pulling the strings from behind."); + next; + guinevereSpawn(3); + mesn; + mesq l("The way is full of dangers. Not only king soldiers, but the beast has an army of their own!"); + next; + guinevereSpawn(4); + mesn; + mesq l("You need to be very careful. Use the sewers to reach... them."); + next; + guinevereSpawn(4); + mesn; + mesq l("Also, listen well, because there are traps and dangers on the whole way. Their leader sealed themselves in safety."); + next; + guinevereSpawn(5); + mesn; + mesq l("I'm afraid you'll need a key to reach them. But there's a fork on the way."); + next; + guinevereSpawn(3); + mesn; + switch ($KAMELOT_KEY[.@g]) { + case 1: + .@path$=l("West"); break; + case 2: + .@path$=l("North West"); break; + case 4: + .@path$=l("North East"); break; + case 8: + .@path$=l("East"); break; + default: + .@path$=l("ERROR, REPORT ME: Invalid: %d", $KAMELOT_KEY[.@g]); break; + } + mesq l("The key is on the sewer %s path. Be careful. May the light be with you.", b(.@path$)); + if ($KAMELOT_QUEST[.@g] & 2) + close; + next; + guinevereSpawn(5); + mesn; + mesq l("And one more thing..."); + next; + guinevereSpawn(2); + mesn; + mesq l("Take this with you. And please bring Arthur back, the world needs him!"); + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|2; + // Player Reward for completing this stage + getitem any(SacredImmortalityPotion, SacredLifePotion, SacredManaPotion), 1; + getitem GuildCoin, 5; + // Guild Reward for completing this stage + .@ggp=100+$KAMELOT_MX[.@g]*4; + .@gxp=$KAMELOT_MX[.@g]*2; + $GUILD_BANK[.@g]+=.@ggp; + guildgetexp(.@gxp); // 2xp per player average level (max 200/300) + // Guild Master Notification + .@gm$=getguildmaster(.@g); + if (!getcharid(3, .@gm$)) end; + .@gma=getcharid(3, .@gm$); + .@gmb=getcharid(0, .@gm$); + if (!isloggedin(.@gma, .@gmb)) end; + message .@gm$, strcharinfo(0)+" cleared Guinevere: Guild GP +"+.@ggp+" Guild XP +"+.@gxp; + close; + +OnKillMob: + if (!playerattached()) end; + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*2, $KAMELOT_MX[.@g]; + end; + +function guinevereSpawn { + .@label$=instance_npcname(.name$)+"::OnKillMob"; + .@gcount=getarg(0); + .@g=getcharid(2); + if (.@g < 1) die(); + .@avg=$KAMELOT_MX[.@g]; + .@m$=getmap(); + for (.@i=0; .@i < .@gcount; .@i++) { + .@mobId=CursedSoldier; + .@mob=areamonster(.@m$, 82, 26, 90, 34, strmobinfo(1, .@mobId), .@mobId, 1, .@label$); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg/6); + setunitdata(.@mob, UDT_AGI, 1+.@avg/7); + setunitdata(.@mob, UDT_VIT, 1+.@avg/6); + setunitdata(.@mob, UDT_INT, 1+.@avg/6); + setunitdata(.@mob, UDT_DEX, 1+.@avg/7); + setunitdata(.@mob, UDT_LUK, 1+.@avg/6); + setunitdata(.@mob, UDT_ADELAY, 2072); + setunitdata(.@mob, UDT_ATKRANGE, 1); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*20); + setunitdata(.@mob, UDT_HP, .@avg*20); + setunitdata(.@mob, UDT_ATKMIN, .@avg*25/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*45/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg*6/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*3/10); + setunitdata(.@mob, UDT_HIT, .@avg*4); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*25/10); // Advised: x4 + // Critical calculation + .@min=1; + .@max=max(.@min, min(15, .@avg/6)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + // Loop through + } + freeloop(false); + return; +} + +OnInit: +OnInstanceInit: + .distance=4; + .sex=G_FEMALE; + end; +} + diff --git a/npc/042-0/mf.txt b/npc/042-0/mf.txt new file mode 100644 index 0000000..714ce27 --- /dev/null +++ b/npc/042-0/mf.txt @@ -0,0 +1,12 @@ +042-0 mapflag zone MMO +042-1 mapflag zone MMO +042-2 mapflag zone MMO +042-3 mapflag zone MMO +042-4 mapflag zone MMO +042-5 mapflag zone MMO +042-6 mapflag zone MMO +042-7 mapflag zone MMO +042-8 mapflag zone MMO +042-9 mapflag zone MMO +042-10 mapflag zone MMO +042-11 mapflag zone MMO diff --git a/npc/042-1/_import.txt b/npc/042-1/_import.txt new file mode 100644 index 0000000..9e6aaff --- /dev/null +++ b/npc/042-1/_import.txt @@ -0,0 +1,3 @@ +// Map 042-1: Camelot - Weapon Room +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-1/door.txt", diff --git a/npc/042-1/door.txt b/npc/042-1/door.txt new file mode 100644 index 0000000..cbdf085 --- /dev/null +++ b/npc/042-1/door.txt @@ -0,0 +1,204 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls Weapons Room basement door + +042-1,51,23,0 script #KDoor0421 NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@g=getcharid(2); + if ($KAMELOT_KEYMASK[.@g] & 16) { + warp "042-2@"+.@g, 44, 59; + + // Maybe event must be fired + if (!($KAMELOT_QUEST[.@g] & 4)) { + .@label$=instance_npcname("#KDoor0422", $@KAMELOT_ID[.@g])+"::OnArrival"; + deltimer .@label$; + addtimer 5000, .@label$; + } + } else { + dispbottom l("This door is locked."); + //doevent instance_npcname(.name$)+"::OnKillMob"; // Double-check + } + end; + +OnKillBoss: + .@g=getcharid(2); + dispbottom l("You found a key."); + getitem TreasureKey, 1; + $KAMELOT_KEYMASK[.@g]=$KAMELOT_KEYMASK[.@g]|16; + // Player Reward for completing this stage + getitem GuildCoin, min(1, $KAMELOT_MX[.@g]/20); + getexp $KAMELOT_MX[.@g]*25, $KAMELOT_MX[.@g]*5; + // Guild Reward for completing this stage + .@ggp=300+$KAMELOT_MX[.@g]*4; + .@gxp=$KAMELOT_MX[.@g]*5; + $GUILD_BANK[.@g]+=.@ggp; + guildgetexp(.@gxp); // 5xp per player average level (max 500/750) + // Announce + mapannounce getmap(), strcharinfo(0)+" has found the key for the door!", 0; + // Guild Master Notification + .@gm$=getguildmaster(.@g); + if (!getcharid(3, .@gm$)) end; + .@gma=getcharid(3, .@gm$); + .@gmb=getcharid(0, .@gm$); + if (!isloggedin(.@gma, .@gmb)) end; + message .@gm$, strcharinfo(0)+" found the key: Guild GP +"+.@ggp+" Guild XP +"+.@gxp; + end; + +OnKillMob: + .@label$=instance_npcname(.name$)+"::OnKillMob"; + // Oh noes! No player attached D: + // This kill is meaningless, RESPAWN IT, RESPAWN IT + if (!playerattached()) { + //.@i=instance_id(); + .@m$=instance_mapname("042-1"); + monster .@m$, 27, 67, "Intruder", Troll, 1, .@label$; + debugmes "Kamelot: Mob killed without player attached. Troll spawned."; + end; + } + + // Now we have a player attached, we can do all checks. + // For example, if you're lame cheater + .@g=getcharid(2); + if (.@g < 1) + die(); + + // I also want to give you exp + getexp $KAMELOT_MX[.@g]*3, $KAMELOT_MX[.@g]; + + // And finally, check if you're still not done killing it. + if (mobcount(getmap(), .@label$)) + end; + + // Configure the wave + .@avg=$KAMELOT_MX[.@g]; + .@gcount=$KAMELOT_PC[.@g]; + .@m$=getmap(); + sleep2(800); // Give ~1 second before respawn + + // Maybe we should advance the wave + if ($@KAMELOT_WAVE[.@g] == 3) { + debugmes "Kamelot %d: Boss Spawn", .@g; + initnpctimer; + setd("$@GTEMP_"+getmap(), .@avg); + .@mcount=.@gcount; + } else if ($@KAMELOT_WAVE[.@g] == 2) { + .@mcount=.@gcount*2; + mapannounce getmap(), "Don't make me come there myself!! GET RID OF THEM ALREADY!", 0; + } else if ($@KAMELOT_WAVE[.@g] == 1) { + .@mcount=.@gcount*3/2; + mapannounce getmap(), "Guards! What are you waiting for?? Arrest them!!", 0; + } else if ($@KAMELOT_WAVE[.@g] == 0) { + .@mcount=.@gcount; + mapannounce getmap(), "Guards!! Attack the intruders!!!", 0; + } else { + end; + } + freeloop(true); + for (.@i=0; .@i < .@gcount; .@i++) { + .@mobId=any(CursedSoldier, CursedArcher); // 50-50 ratio + .@mob=areamonster(.@m$, 21, 24, 59, 99, strmobinfo(1, .@mobId), .@mobId, 1, .@label$); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg/5); + setunitdata(.@mob, UDT_AGI, 1+.@avg/5); + setunitdata(.@mob, UDT_VIT, 1+.@avg/5); + setunitdata(.@mob, UDT_INT, 1+.@avg/5); + setunitdata(.@mob, UDT_DEX, 1+.@avg/5); + setunitdata(.@mob, UDT_LUK, 1+.@avg/5); + setunitdata(.@mob, UDT_ADELAY, 1872); + setunitdata(.@mob, UDT_ATKRANGE, (.@mobId == CursedArcher ? any(5,6,7) : any(1,1,2))); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*30); + setunitdata(.@mob, UDT_HP, .@avg*30); + setunitdata(.@mob, UDT_ATKMIN, .@avg*35/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*55/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg*8/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*4/10); + setunitdata(.@mob, UDT_HIT, .@avg*35/10); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*3); // Advised: x4 + // Critical calculation + .@min=1; + .@max=max(.@min, min(20, .@avg/5)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + // Loop through + } + freeloop(false); + $@KAMELOT_WAVE[.@g]+=1; + end; + +OnTimer100: + .@m$=instance_mapname("042-1"); + mapannounce .@m$, "*sigh* You force me to come...", 0; + end; + +OnTimer5000: + .@m$=instance_mapname("042-1"); + mapannounce .@m$, "Stupid fools...", 0; + end; + +OnTimer10000: + .@label$=instance_npcname(.name$)+"::OnKillBoss"; + .@m$=instance_mapname("042-1"); + mapannounce .@m$, "I'll get rid of you myself!!", 0; + .@mobId=any(CursedSoldier, CursedArcher); + .@mob=monster(.@m$, 40, 30, any("Lancelot", "Galahard", "Gareth", "Percival"), .@mobId, 1, .@label$); + .@avg=getd("$@GTEMP_"+.@m$); + setd("$@GTEMP_"+.@m$, 0); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg/5); + setunitdata(.@mob, UDT_AGI, 1+.@avg/5); + setunitdata(.@mob, UDT_VIT, 1+.@avg/5); + setunitdata(.@mob, UDT_INT, 1+.@avg/5); + setunitdata(.@mob, UDT_DEX, 1+.@avg/5); + setunitdata(.@mob, UDT_LUK, 1+.@avg/5); + setunitdata(.@mob, UDT_ADELAY, 1672); + setunitdata(.@mob, UDT_ATKRANGE, (.@mobId == CursedArcher ? any(6,7,8) : 2)); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*150); + setunitdata(.@mob, UDT_HP, .@avg*150); + setunitdata(.@mob, UDT_ATKMIN, .@avg*5); + setunitdata(.@mob, UDT_ATKMAX, .@avg*7); + setunitdata(.@mob, UDT_DEF, 1+.@avg*11/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*6/10); + setunitdata(.@mob, UDT_HIT, .@avg*8); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*35/10); // Advised: x4 + // Critical calculation + .@min=15; + .@max=max(.@min, min(40, .@avg/5)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + stopnpctimer; + end; + +OnArrival: + .@g=getcharid(2); + if (.@g < 1) + die(); + if (getmap() != "042-1@"+.@g) + end; + if ($@KAMELOT_WAVE[.@g] == 0) + goto OnKillMob; + end; +} + + +// Required exit +042-1,55,100,0 script #KDoor0421B NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@g=getcharid(2); + if ($KAMELOT_KEYMASK[.@g] & 16) { + dispbottom l("WARNING: If you walk out the main gate you WON'T be able to return!"); + warp "042-0@"+.@g, 69, 25; + } else { + dispbottom l("Oh noes! The guards locked the door!"); + } + end; +} + diff --git a/npc/042-10/_import.txt b/npc/042-10/_import.txt new file mode 100644 index 0000000..2749a19 --- /dev/null +++ b/npc/042-10/_import.txt @@ -0,0 +1,3 @@ +// Map 042-10: Camelot Sewer West Path +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-10/ctrl.txt", diff --git a/npc/042-10/ctrl.txt b/npc/042-10/ctrl.txt new file mode 100644 index 0000000..9fccee6 --- /dev/null +++ b/npc/042-10/ctrl.txt @@ -0,0 +1,635 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls the great hall + +// None of the exits work +042-10,86,139,0 script #KDoor04210a NPC_HIDDEN,3,0,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + dispbottom l("OH NOES! The ceiling seems to have collapsed. I hope we got the key, or the quest is over for us!"); + end; +} + +042-10,149,136,0 duplicate(#KDoor04210a) #KDoor04210b NPC_HIDDEN,0,0 +042-10,21,139,0 duplicate(#KDoor04210a) #KDoor04210c NPC_HIDDEN,2,0 +042-10,20,80,0 duplicate(#KDoor04210a) #KDoor04210d NPC_HIDDEN,0,6 + + +// Boss Room control +042-10,142,20,0 script #KDoor04210e NPC_HIDDEN,4,0,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + if (!($KAMELOT_QUEST[.@g] & 8) || !($KAMELOT_QUEST[.@g] & 16) || !($KAMELOT_QUEST[.@g] & 32)) goto L_NoAccess; + warp "042-11@"+.@g, 41, 56; + addtimer 3000, "#KamelotBoss::OnDialog"; + end; + +L_NoAccess: + dispbottom l("A powerful door is sealed shut. It has no keyhole, but I'm sure it can be opened somewhere else on these caves."); + //dispbottom l("This door is locked, if we only had a %s...", getitemlink(KamelotKey)); + end; +} + +// Boss Room Warning +042-10,150,25,0 script Warning#Kamelot NPC_NO_SPRITE,{ + mesc ".:: "+l("DANGER!")+" ::.", 1; + mes ""; + mesc l("I've sealed a massive amount of dark magic here."), 1; + mesc l("Do not break my seal to prevent the curse from reaching Kamelot."), 1; + mes ""; + mes l("-- Merlin"); + close; +OnInit: + .distance=4; + end; +} + +// Magic Seal Main +042-10,135,42,0 script #KamelotSeal NPC_HIDDEN,0,4,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + if (!($KAMELOT_QUEST[.@g] & 16) && !($KAMELOT_QUEST[.@g] & 8)) goto L_NoAccessBlock; + if (!($KAMELOT_QUEST[.@g] & 16) || !($KAMELOT_QUEST[.@g] & 8)) goto L_NoAccess; + // You can pass freely, so we do nothing + end; + +L_NoAccess: + slide 127, 36; + percentheal -5, -5; + dispbottom l("A powerful magic barrier repeals you!"); + dispbottom l("It's weaker than before, probably only one is working right now."); + end; + +L_NoAccessBlock: + slide 127, 36; + percentheal -10, -10; + dispbottom l("A powerful magic barrier repeals you!"); + dispbottom l("We need to find what powers these two seals and disable it."); + end; +} + + + +//////////////////////////////////////////////////////////////////// +// KatazuliInfo( ) +function script KatazuliInfo { + mes ".:: Magical Seal ::."; + mes ""; + mes l("The Magical Seal which protects the final room is too strong."); + mes l("However, by casting %s here, we'll be able to weaken the seal!", b("katazuli")); + next; + mes l("To cast, one must sit on the circle and say the chant on general chat."); + mes l("The chant must be exact, without any prefix or suffix."); + next; + mes l("While breaking the seal, monsters will spawn. The other monsters may drop %s.", getitemlink(DarkPetal)); + mes l("Drop these near the circle to make the breaking faster."); + next; + mes l("Multiple players may conduct Katazuli at once. If you move or stand, the spell may be aborted."); + mes l("Remember: Taking damage will make you stand!"); + next; + mes l("Katazuli breaking proccess will cause the breaker some harm."); + mes l("It'll also drain mana in the proccess. If you run out of mana, its over."); + next; + mes l("After casting %s, you must wait %d seconds before casting it again.", b("katazuli"), b("60")); + mes l("However, as long as you remain seated and immobile, the spell will slowly destroy the seal."); + next; + mesc l("Caution"), 1; + mes l("The seals slowly grow stronger as time passes!"); + mes l("Once broken, they'll lose the link to the magical seal."); + mes l("Even if they recover their strength, because the connection was severed, the barrier will not power up again."); + return; +} + +// KatazuliCore( Begin, ID ) +function script KatazuliCore { + // Basic checks + // Not sitting - Do nothing and cancel + if (!issit()) + end; + + // Not sitting on the circle - Again, do nothing + getmapxy(.@m$, .@x, .@y, 0); + if (.@x != .x || .@y != .y) + end; + + // No mana - do nothing + if (!Sp) + end; + + // Initialize local variables + .@start=getarg(0); + .@id=getarg(1); + .@g=getcharid(2); + + // User cooldown + if (.@start && @katazuli >= gettimetick(2)) + end; + if (!.@start && @katazuli <= gettimetick(2)) + end; + + // We should give a message + if (.@start) { + @kataspam=0; + callfunc "FYE_Olympics_CH"; + } + + // All checks passed: KATAZULI + specialeffect FX_MGSHIELD, AREA, getcharid(3); // Temporary FX + + // Consume Dark Petals nearby + .@cat=getareadropitem(getmap(), .x-3, .y-3, .x+3, .y+3, DarkPetal, true); + + // Your own power boost + .@bint=rand2(readparam2(bInt)/2, readparam2(bInt)); + .@cat+=limit(1, .@bint/32, 5)+any(0,1); + + // Power the circle + .@power=getd("$@GTEMP_"+.@id+"_"+.@m$)+.@cat; + setd("$@GTEMP_"+.@id+"_"+.@m$, .@power); + + // Inform new status + if (.@power < 5 && @kataspam != 1) { + unittalk(getcharid(3), "This circle is nearly at full power! We need to shut down it!", true); + @kataspam=1; + } else if (.@power >= 5 && .@power < 100 && @kataspam != 2) { + unittalk(getcharid(3), "There is still too much power! Please drop "+getitemlink(DarkPetal)+" nearby!", true); + @kataspam=2; + } else if (.@power >= 100 && .@power < 200 && @kataspam != 3) { + unittalk(getcharid(3), "At this rate, we will do it! Keep going! Please drop "+getitemlink(DarkPetal)+" nearby!", true); + @kataspam=3; + } else if (.@power >= 200 && .@power < 300 && @kataspam != 4) { + unittalk(getcharid(3), "Guys I'm close to shut it down! Please drop "+getitemlink(DarkPetal)+" nearby!", true); + @kataspam=4; + } + + // Debug Information + if (is_staff() && $@GM_OVERRIDE) + dispbottom l("[DEBUG] Current Power: %d", .@power); + + // Circle was shut down (give rewards if appropriate) + if (.@power >= 300) { + if ($KAMELOT_QUEST[.@g] & .@id) end; + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|.@id; + unittalk(getcharid(3), "We did it! The seal is now broken!", true); + // Player Reward for completing this stage + getitem GuildCoin, 1; + getexp $KAMELOT_MX[.@g]*50, $KAMELOT_MX[.@g]*10; + // Guild Reward for completing this stage + .@ggp=1200+$KAMELOT_MX[.@g]*10; + .@gxp=$KAMELOT_MX[.@g]*25; + $GUILD_BANK[.@g]+=.@ggp; + guildgetexp(.@gxp); // 25xp per player average level (max 2500/3750) + // Announce + mapannounce getmap(), strcharinfo(0)+" has broken a magic seal!", 0; + // Guild Master Notification + .@gm$=getguildmaster(.@g); + if (!getcharid(3, .@gm$)) return; + .@gma=getcharid(3, .@gm$); + .@gmb=getcharid(0, .@gm$); + if (!isloggedin(.@gma, .@gmb)) return; + message .@gm$, strcharinfo(0)+" broke a magic seal: Guild GP +"+.@ggp+" Guild XP +"+.@gxp; + end; + } + + // TODO: Spawn monsters, drop Dark Petal + // (Probably spawn Dark Rose Field) + // PS. Every monster in this floor will drop them + getmapxy(.@m$, .@x, .@y, 0); + KamelotCaveSpawn($KAMELOT_PC[.@g], .@x-rand2(4), .@y-rand2(4), .@x+rand2(4), .@y+rand2(4), $KAMELOT_MX[.@g]+(.@power/3), "042-10"); + .@x+=any(-3,-2,-1,1,2,3); + .@y+=any(-3,-2,-1,1,2,3); + monster(.@m$, .@x, .@y, strmobinfo(1, MagicGoblin), MagicGoblin, (@kataspam == 1 ? 6 : (@kataspam == 2 ? 5 : (@kataspam == 3 ? 4 : 3))) ); + + // Take away some HP and MP, but do not make you stand + percentheal -1, -5; + sit(); + + // If it took all your mana - its over + if (!Sp) + end; + + // Set cooldown on start + if (.@start) { + dispbottom l("Please wait 60 seconds to cast again."); + @katazuli=gettimetick(2)+60; + } + return; +} + + +//////////////////////// +// Katazuli's Engravings +042-10,36,110,0 script Engraving#Katazuli1 NPC_NO_SPRITE,{ + mes "Read tutorial?"; + select l("No"), l("Yes"); + if (@menu == 2) + KatazuliInfo(); + close; +OnInit: + .distance=4; + end; +} +042-10,124,85,0 duplicate(Engraving#Katazuli1) Engraving#Katazuli2 NPC_NO_SPRITE + + +//////////////////////// +// The first Katazuli +042-10,128,85,0 script #Katazuli01 NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("This looks magical, I wonder what it does."); + end; + +OnInit: + .kataId=8; + .distance=3; + .pid=getnpcid(); + debugmes "Pattern %d", .pid; + //defpattern(.pid, "^([Kk][Aa][Tt][Aa][Zz][Uu][Ll][Ii])$", "OnTalkNearby"); + defpattern(.pid, "^(.*)$", "OnTalkNearby"); + activatepset(.pid); + end; + +OnTouch: + npctalkonce l("You must %s and chant %s to begin channeling.", b(l("sit")), b(l("katazuli"))); + end; + +OnTalkNearby: + // not very obvious stuff by gumi + .@no_nick$ = strip(substr($@p0$, getstrlen(strcharinfo(PC_NAME)) + 3, getstrlen($@p0$) - 1)); + .@message$ = strtoupper(.@no_nick$); + + // It is with us! + if (.@message$ == "KATAZULI") { + KatazuliCore(true, .kataId); + addtimer 6000, instance_npcname(.name$)+"::OnKatazuli"; + } + end; + +OnKatazuli: + KatazuliCore(false, .kataId); + addtimer 6000, instance_npcname(.name$)+"::OnKatazuli"; + end; + +OnInstanceInit: + initnpctimer; + end; + +OnKillMob: + if (!playerattached()) + end; + .@g=getcharid(2); + + // Handle Dark Petal + // XXX: How MX/PC should affect drop rates? + // I imagine a higher MX will increase DR.... (Right now, level 100 = +50% DR) + .@r=rand2(250); + if (.@r < 100+$KAMELOT_MX[.@g]) { + getmapxy(.@m$, .@x, .@y, 0); + makeitem(DarkPetal, 1, .@m$, rand2(.@x-1, .@x+1), rand2(.@y-1, .@y+1)); + } + + // Maybe a reward is due + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*10, $KAMELOT_MX[.@g]*5; + end; + +// Every minute, recover some energy +OnTimer60000: + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to recharge katazuli for Kamelot %s", .map$; + .@g=0; + } + + // Estimate + .@pc=$KAMELOT_PC[.@g]+1; + .@pow=rand2(.@pc); + + // Recover energy + .@id=.kataId; + .@m$=instance_mapname("042-10"); + .@power=max(0, getd("$@GTEMP_"+.@id+"_"+.@m$)-.@pow); + setd("$@GTEMP_"+.@id+"_"+.@m$, .@power); + initnpctimer; + end; +} + + +//////////////////////// +// The second Katazuli +042-10,32,108,0 script #Katazuli02 NPC_SUMMONING_CIRC,0,0,{ + dispbottom l("This looks magical, I wonder what it does."); + end; + +OnInit: + .kataId=16; + .distance=3; + .pid=getnpcid(); + debugmes "Pattern %d", .pid; + //defpattern(.pid, "^([Kk][Aa][Tt][Aa][Zz][Uu][Ll][Ii])$", "OnTalkNearby"); + defpattern(.pid, "^(.*)$", "OnTalkNearby"); + activatepset(.pid); + end; + +OnTouch: + npctalkonce l("You must %s and chant %s to begin channeling.", b(l("sit")), b(l("katazuli"))); + end; + +OnTalkNearby: + // not very obvious stuff by gumi + .@no_nick$ = strip(substr($@p0$, getstrlen(strcharinfo(PC_NAME)) + 3, getstrlen($@p0$) - 1)); + .@message$ = strtoupper(.@no_nick$); + + // It is with us! + if (.@message$ == "KATAZULI") { + KatazuliCore(true, .kataId); + addtimer 6000, instance_npcname(.name$)+"::OnKatazuli"; + } + end; + +OnKatazuli: + KatazuliCore(false, .kataId); + addtimer 6000, instance_npcname(.name$)+"::OnKatazuli"; + end; + +OnInstanceInit: + initnpctimer; + end; + +OnKillMob: + if (!playerattached()) + end; + .@g=getcharid(2); + + // Handle Dark Petal + // XXX: How MX/PC should affect drop rates? + // I imagine a higher MX will increase DR.... (Right now, level 100 = +20% DR) + .@r=rand2(500); + if (.@r < 100+$KAMELOT_MX[.@g]) { + getmapxy(.@m$, .@x, .@y, 0); + makeitem(DarkPetal, 1, .@m$, rand2(.@x-1, .@x+1), rand2(.@y-1, .@y+1)); + } + + // Maybe a reward is due + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*10, $KAMELOT_MX[.@g]*5; + end; + +// Every minute, recover some energy +OnTimer60000: + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to recharge katazuli for Kamelot %s", .map$; + .@g=0; + } + + // Estimate + .@pc=$KAMELOT_PC[.@g]+1; + .@pow=rand2(.@pc); + + // Recover energy + .@id=.kataId; + .@m$=instance_mapname("042-10"); + .@power=max(0, getd("$@GTEMP_"+.@id+"_"+.@m$)-.@pow); + setd("$@GTEMP_"+.@id+"_"+.@m$, .@power); + initnpctimer; + end; +} + + + +//////////////////////////////////////////////////////////////////// +// KamelotSwitch( "_SWITCH ID" ) +function script KamelotSwitch { + // Extract ID + .@n$=getarg(0, "_0"); + explode(.@ni$, .@n$, "_"); + .@id=atoi(.@ni$[1]); + if (.@id <= 0) Exception("Unparseable switch: "+.@n$, RB_DEFAULT|RB_ISFATAL); + + .@g=getcharid(2); + if (!($KAMELOT_KEYMASK[.@g] & $KAMELOT_KEY[.@g])) end; // Don't care + .@st=($KAMELOT_PASSMASK[.@g] & .@id); + mes l("Status: %s", (.@st ? col(l("Active"),2) : col(l("Inactive"),1) )); + if (!countitem(KamelotKey)) + close; + mes "Change switch status?"; + next; + if (askyesno() == ASK_YES) { + $KAMELOT_PASSMASK[.@g]=$KAMELOT_PASSMASK[.@g]^.@id; + // The meaning of st is now mixed + if (.@st) + setnpcdisplay instance_npcname(.name$), NPC_SWITCH_OFFLINE; + else + setnpcdisplay instance_npcname(.name$), NPC_SWITCH_ONLINE; + } + // Check password and delete key + if ($KAMELOT_PASSMASK[.@g] == $KAMELOT_PASSCODE[.@g]) { + delitem KamelotKey, 1; + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|32; + mesc l("Your hear a clink, and the key breaks."), 2; + // Player Reward for completing this stage + getitem GuildCoin, 1; + getexp $KAMELOT_MX[.@g]*50, $KAMELOT_MX[.@g]*10; + // Guild Reward for completing this stage + .@ggp=1200+$KAMELOT_MX[.@g]*10; + .@gxp=$KAMELOT_MX[.@g]*25; + $GUILD_BANK[.@g]+=.@ggp; + guildgetexp(.@gxp); // 25xp per player average level (max 2500/3750) + // Announce + mapannounce getmap(), strcharinfo(0)+" has opened the gate!", 0; + // Guild Master Notification + .@gm$=getguildmaster(.@g); + if (!getcharid(3, .@gm$)) return; + .@gma=getcharid(3, .@gm$); + .@gmb=getcharid(0, .@gm$); + if (!isloggedin(.@gma, .@gmb)) return; + message .@gm$, strcharinfo(0)+" opened the gate: Guild GP +"+.@ggp+" Guild XP +"+.@gxp; + + } + return; +} + +// Switches hints +// TODO: NPC_PAPER_NOTE sprite +042-10,97,126,0 script #K4210Note01 NPC_SWORDS_SIGN,{ + .@g=getcharid(2); + + if (!($KAMELOT_KEYMASK[.@g] & $KAMELOT_KEY[.@g])) { + mes l("Your guild does not have the key required to complete this part of the puzzle."); + close; + } + + if ($KAMELOT_PASSCODE[.@g] & 1) + dispbottom l("The power system is ##Bactive##b on the north."); + else + dispbottom l("The north power system is offline."); + end; + +OnInit: + .distance=4; + end; +} +042-10,139,98,0 script #K4210Note02 NPC_SWORDS_SIGN,{ + .@g=getcharid(2); + + if (!($KAMELOT_KEYMASK[.@g] & $KAMELOT_KEY[.@g])) { + mes l("Your guild does not have the key required to complete this part of the puzzle."); + close; + } + + if ($KAMELOT_PASSCODE[.@g] & 2) + dispbottom l("The power system is ##Bactive##b on the south."); + else + dispbottom l("The south power system is offline."); + end; + +OnInit: + .distance=4; + end; +} +042-10,127,45,0 script #K4210Note04 NPC_SWORDS_SIGN,{ + .@g=getcharid(2); + + if (!($KAMELOT_KEYMASK[.@g] & $KAMELOT_KEY[.@g])) { + mes l("Your guild does not have the key required to complete this part of the puzzle."); + close; + } + + if ($KAMELOT_PASSCODE[.@g] & 1) + dispbottom l("The power system is ##Bactive##b on the west."); + else + dispbottom l("The west power system is offline."); + end; + +OnInit: + .distance=4; + end; +} +042-10,33,133,0 script #K4210Note08 NPC_SWORDS_SIGN,{ + .@g=getcharid(2); + + if (!($KAMELOT_KEYMASK[.@g] & $KAMELOT_KEY[.@g])) { + mes l("Your guild does not have the key required to complete this part of the puzzle."); + close; + } + + if ($KAMELOT_PASSCODE[.@g] & 8) + dispbottom l("The power system is ##Bactive##b on the east."); + else + dispbottom l("The east power system is offline."); + end; + +OnInit: + .distance=4; + end; +} +042-10,38,39,0 script #K4210Note16 NPC_SWORDS_SIGN,{ + .@g=getcharid(2); + + if (!($KAMELOT_KEYMASK[.@g] & $KAMELOT_KEY[.@g])) { + mes l("Your guild does not have the key required to complete this part of the puzzle."); + close; + } + + if ($KAMELOT_PASSCODE[.@g] & 16) + dispbottom l("The power system is ##Bactive##b on the main."); + else + dispbottom l("The main power system is offline."); + end; + +OnInit: + .distance=4; + end; +} + +// The "_" is used on explode +042-10,89,28,0 script #K4210Switch_1 NPC_SWITCH_OFFLINE,{ + KamelotSwitch(strnpcinfo(0, "_0")); + close; + +OnInit: + .distance=2; + end; +} + +// Other switches +042-10,80,115,0 duplicate(#K4210Switch_1) #K4210Switch_2 NPC_SWITCH_OFFLINE,0,0 // S +042-10,63,76,0 duplicate(#K4210Switch_1) #K4210Switch_4 NPC_SWITCH_OFFLINE,0,0 // W +042-10,100,79,0 duplicate(#K4210Switch_1) #K4210Switch_8 NPC_SWITCH_OFFLINE,0,0 // E +042-10,82,64,0 duplicate(#K4210Switch_1) #K4210Switch_16 NPC_SWITCH_OFFLINE,0,0 // C + + + +/////////////////////////////////////////////////////////////////// +// THIS CONTROLS EVERYTHING ELSE ON THIS ROOM +042-10,0,0,0 script #Kamelot4210 NPC_HIDDEN,{ + end; + +OnKillMob: + if (!playerattached()) + goto OnRespawn; + .@g=getcharid(2); + + // Handle Dark Petal + // XXX: How MX/PC should affect drop rates? + // I imagine a higher MX will increase DR.... (Right now, level 100 = +20% DR) + .@r=rand2(500); + if (.@r < 100+$KAMELOT_MX[.@g]) { + getmapxy(.@m$, .@x, .@y, 0); + makeitem(DarkPetal, 1, .@m$, rand2(.@x-1, .@x+1), rand2(.@y-1, .@y+1)); + } + + // Maybe a reward is due + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*10, $KAMELOT_MX[.@g]*5; + .@delay=max(5000, 30000-$KAMELOT_PC[.@g]*1250); + // FALLTHROUGH + +OnRespawn: + .@delay=(.@delay ? .@delay : 5000); + sleep(.@delay); + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to respawn for Kamelot %s", .map$; + .@g=0; + } + KamelotCaveSpawn(1, 20, 20, 160, 140, $KAMELOT_MX[.@g]+10, .@map$[0]); + end; + +OnInstanceInit: + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to spawn for Kamelot %s", .map$; + .@g=0; + } + .@pc=$KAMELOT_PC[.@g]+1; + .@avg=$KAMELOT_MX[.@g]+15; // Monsters will be at least 15 levels stronger + .@m$="042-10"; // or .@map$[0] + + // Spawns + KamelotCaveSpawn(.@pc*25/10, 60, 24, 115, 85, .@avg, .@m$); // Central Chambers (250% of player population) + + // Fill the whole cave with at least, 1 per 500 tiles + // There are 16800 tiles, so = 36 guards should do the trick + // Actually, lets make that 50% bigger, so, 54 guards. Map is big. + KamelotCaveSpawn(54, 20, 20, 160, 140, .@avg, .@m$); + end; +} + diff --git a/npc/042-11/_import.txt b/npc/042-11/_import.txt new file mode 100644 index 0000000..fde2ef7 --- /dev/null +++ b/npc/042-11/_import.txt @@ -0,0 +1,3 @@ +// Map 042-11: Camelot Sewer West Path +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-11/boss.txt", diff --git a/npc/042-11/boss.txt b/npc/042-11/boss.txt new file mode 100644 index 0000000..5cc3b65 --- /dev/null +++ b/npc/042-11/boss.txt @@ -0,0 +1,220 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls the showdown +042-11,41,57,0 script #KDoor04211a NPC_HIDDEN,4,0,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + if (!($KAMELOT_QUEST[.@g] & 64)) goto L_NoAccess; + warp "042-10@"+.@g, 143, 21; + end; + +L_NoAccess: + dispbottom l("Can't leave right now."); + end; +} + +042-11,20,28,0 script #KDoor04211b NPC_HIDDEN,0,2,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + if (!($KAMELOT_QUEST[.@g] & 64)) goto L_NoAccess; + mes l("Are you sure you want leave?"); + mesc l("You wont be able to go back!"); + next; + if (askyesno() == ASK_YES) + warp "042-0@"+.@g, 46, 25; + close; + +L_NoAccess: + dispbottom l("Can't leave right now."); + end; +} + +042-11,0,0,0 script #KamelotBoss NPC_HIDDEN,{ + end; + +OnDialog: + announce "??? : "+l("Who sent you here? Merlin?"), bc_self|bc_pc; + addtimer 8000, "#KamelotBoss::OnDialog2"; + end; + +OnDialog2: + .@g=getcharid(2); + announce "??? : "+l("It matters not, because soon, all of you will be..."), bc_self|bc_pc; + addtimer 7000, instance_npcname("#KamelotBoss", $@KAMELOT_ID[.@g])+"::OnBegin"; + end; + +// Spawns Terogan +OnBegin: + .@g=getcharid(2); + .@m$=instance_mapname("042-11"); + .@avg=$KAMELOT_MX[.@g]+10; + .@n$=instance_npcname("#KamelotBoss", $@KAMELOT_ID[.@g]); + debugmes "GID: %d Map: %s Power: %d Label: %s", .@g, .@m$, .@avg, .@n$; + + // Dialog + announce l("General Terogan : DEAD! Muahahahaha!"), bc_self|bc_pc; + + // Check if Terogan doesn't exists already + .@exist=getd("$@GTEMP_"+.@m$); + if (.@exist) + end; + + // Check if quest isn't over already '-' + if ($KAMELOT_QUEST[.@g] & 64) + end; + + // Bring Terogan into existence + .@mob=monster(.@m$, 40, 30, strmobinfo(1, GeneralTerogan), GeneralTerogan, 1, .@n$+"::OnKillBoss"); + setd("$@GTEMP_"+.@m$, .@mob); + setd("$@GTEMP_HP_"+.@m$, 20); + + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg/2); + setunitdata(.@mob, UDT_AGI, 1+.@avg/2); + setunitdata(.@mob, UDT_VIT, 1+.@avg/2); + setunitdata(.@mob, UDT_INT, 1+.@avg/2); + setunitdata(.@mob, UDT_DEX, 1+.@avg/2); + setunitdata(.@mob, UDT_LUK, 1+.@avg/2); + setunitdata(.@mob, UDT_ADELAY, 1072); + setunitdata(.@mob, UDT_ATKRANGE, 4); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*700); + setunitdata(.@mob, UDT_HP, .@avg*700); + setunitdata(.@mob, UDT_ATKMIN, .@avg*7); + setunitdata(.@mob, UDT_ATKMAX, .@avg*9); + setunitdata(.@mob, UDT_DEF, 4+.@avg); + setunitdata(.@mob, UDT_MDEF, 1+.@avg); + setunitdata(.@mob, UDT_HIT, .@avg*18); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*5); // Advised: x4 + setunitdata(.@mob, UDT_CRIT, 120); + // Initial batch of reinforcements + KamelotCaveSpawn(6, 30, 20, 50, 40, .@avg, "042-11"); + + // Save info again + setd("$@GTEMP_"+.@m$, .@mob); + setd("$@GTEMP_HP_"+.@m$, 20); + initnpctimer; + end; + +// Checks HP ratio again +OnTimer5000: + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolebug "[ERROR] [KAMELOT] Unable to find Terogan - Kamelot %s", .map$; + .@g=0; + } + + // Retrieve Terogan's GUID and MAP ID + .@m$=instance_mapname("042-11"); + .@mob=getd("$@GTEMP_"+.@m$); + + // Prepare the reinforcement strength data (min 3 Lv 22 spawn) + .@gcount=$KAMELOT_PC[.@g]+2; + .@avg=$KAMELOT_MX[.@g]+rand2(22, 27); + + // Calculate current HP ratio and the difference + .@ratio=getunitdata(.@mob, UDT_HP)*20/getunitdata(.@mob, UDT_MAXHP); + .@hplos=getd("$@GTEMP_HP_"+.@m$)-.@ratio; + + //debugmes "Ratio %d/%d", .@ratio, getd("$@GTEMP_HP_"+.@m$); + if (.@ratio < getd("$@GTEMP_HP_"+.@m$)) { + mapannounce .@m$, "General Terogan : "+any("Charge!", "To the Abyss with you already!", "Kill them already!", "More of them might be coming!", "Minions, ATTACK!", "Muahahaha, Pathetic!"), 0; + + // Unlike Krukan, spawn according to HP loss (no use fast killing) + KamelotCaveSpawn(.@gcount*max(1, .@hplos), 20, 20, 57, 40, .@avg, "042-11"); + } + + // TODO: Special skills + + // Always update the ratio - Terogan could have been healed + setd("$@GTEMP_HP_"+.@m$, .@ratio); + initnpctimer; + end; + +// Monks Rewards +OnKillMob: + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*8, $KAMELOT_MX[.@g]*4; // Lower than previously + end; + +// Boss rewards +OnKillBoss: + stopnpctimer; + if (playerattached()) { + // Setup + .@g=getcharid(2); + .@m$=getmap(); + .@hero$=strcharinfo(0); + // Player Reward for completing this stage + getitem GuildCoin, 10; + getexp $KAMELOT_MX[.@g]*150, $KAMELOT_MX[.@g]*30; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + } else { + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolebug "[ERROR] [KAMELOT] Unable to find Terogan Death %s", .map$; + .@g=0; + } + .@m$=.map$; + .@hero$="The guild"; + } + + // Mark quest as completed + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|64; + + // Get rid of temporary variables + setd("$@GTEMP_"+.@m$, 0); + setd("$@GTEMP_HP_"+.@m$, 0); + killmonsterall(.@m$); + + // Guild Reward for completing this stage + .@ggp=2000+$KAMELOT_MX[.@g]*100; + .@gxp=$KAMELOT_MX[.@g]*250; + $GUILD_BANK[.@g]+=.@ggp; + guildgetexp(.@gxp); // Silently fails if no char attached + + // Announce + mapannounce .@m$, .@hero$+" has defeated the evil in Kamelot!", 0; + + // Spawn GMGiftBox upon end + .@gf=min(30, $KAMELOT_MX[.@g]/5); + areamonster(.@m$, 20, 20, 57, 40, strmobinfo(1, GMGiftBox), GMGiftBox, .@gf); + + // Spawn Treasure Chests upon end + .@gl=$KAMELOT_MX[.@g]; + .@gtype=(.@gl > 100 ? SupremeChest : (.@gl > 75 ? PrismChest : (.@gl > 50 ? GoldenChest : (.@gl > 25 ? SilverChest : BronzeChest)))); + .@gf=$KAMELOT_PC[.@g]; + areamonster(.@m$, 20, 20, 57, 40, strmobinfo(1, .@gtype), .@gtype, .@gf); + + // Possibly, we forgot stuff in Throne Room... + killmonsterall("042-0@"+.@g); + + // Guild Master Notification + .@gm$=getguildmaster(.@g); + if (!getcharid(3, .@gm$)) end; + .@gma=getcharid(3, .@gm$); + .@gmb=getcharid(0, .@gm$); + if (!isloggedin(.@gma, .@gmb)) end; + message .@gm$, strcharinfo(0)+" defeated the boss: Guild GP +"+.@ggp+" Guild XP +"+.@gxp; + end; + +// Instance +OnInstanceInit: + end; + +} + diff --git a/npc/042-2/_import.txt b/npc/042-2/_import.txt new file mode 100644 index 0000000..2d59e35 --- /dev/null +++ b/npc/042-2/_import.txt @@ -0,0 +1,3 @@ +// Map 042-2: Camelot - Basement +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-2/boss.txt", diff --git a/npc/042-2/boss.txt b/npc/042-2/boss.txt new file mode 100644 index 0000000..b3adb12 --- /dev/null +++ b/npc/042-2/boss.txt @@ -0,0 +1,405 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Basement Boss Fight +// We'll be counting attendance now. + +042-2,41,22,0 script #KDoor0422 NPC_HIDDEN,0,0,{ + function kdoor0422Spawn; + end; + +OnTouch: + .@label$=instance_npcname(.name$)+"::OnKillBoss"; + .@g=getcharid(2); + if (.@g < 1) die(); + if (($KAMELOT_QUEST[.@g] & 4) && !mobcount(getmap(), .@label$)) { + warp "042-3@"+.@g, 58, 139; + } else { + dispbottom l("Powerful magic repels you!"); + percentheal -5, -5; + slide 38, 28; + } + end; + +OnArrival: + //debugmes "Arrival detected"; + .@m$=instance_mapname("042-2"); + .@g=getcharid(2); + if (.@g < 1) + die(); + //debugmes getmap(); + if (getmap() != "042-2@"+.@g) + end; + //debugmes $@KAMELOT_WAVE[.@g]; + if ($@KAMELOT_WAVE[.@g] > 10) + end; + $@KAMELOT_WAVE[.@g]=100; + setd("$@GTEMP_PC_"+.@m$, $KAMELOT_PC[.@g]); + setd("$@GTEMP_MX_"+.@m$, $KAMELOT_MX[.@g]); + initnpctimer; + //debugmes "Timer Started"; + end; + +OnTimer1000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "??? : What's this? Why do I hear a commotion up there??", 0; + .@gcount=getd("$@GTEMP_PC_"+.@m$); + kdoor0422Spawn(.@gcount*2, 20, 24, 59, 59); // Prologue monsters + end; + +OnTimer30000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "??? : WHAT?! This is not possible. Who are you, and how did you manage to come down here?", 0; + end; + +OnTimer35000: + .@m$=instance_mapname("042-2"); + .@n$=instance_npcname(.name$); + mapannounce .@m$, "??? : The sheer power of our lord should prevent anyone not under his control to go here.", 0; + maptimer .@m$, 5000, .@n$+"::OnVerifyIntent"; + end; + +OnTimer60000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "??? : HAHAHA! Meh, you will die anyways, so I can tell you that you never will even see our Lord.", 0; + end; + +OnTimer67000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, b("??? : You require the key hidden in the caves, additionally you must overcome the magical seal."), 0; + end; + +OnTimer74000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "??? : Both is impossible for weaklings like you lot.", 0; + end; + +OnTimer80000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "General Krukan : ##1I am General Krukan the Strong. Minions, ATTACK!", 0; + .@gcount=getd("$@GTEMP_PC_"+.@m$); + kdoor0422Spawn(.@gcount*2, 20, 24, 59, 59); + end; + +OnTimer100000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "General Krukan : MUhahahahaha! Minions, Dispose of these Adventurers! Kill them already!", 0; + .@gcount=getd("$@GTEMP_PC_"+.@m$); + kdoor0422Spawn(.@gcount*2, 20, 24, 59, 59); + end; + +OnTimer120000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "General Krukan : *facepalm* Now, you're just being pathetic minions. How about you try a different approach and kill them already!", 0; + .@gcount=getd("$@GTEMP_PC_"+.@m$); + kdoor0422Spawn(.@gcount*2, 20, 24, 59, 59); + end; + +OnTimer180000: + .@m$=instance_mapname("042-2"); + .@n$=instance_npcname(.name$); + mapannounce .@m$, "General Krukan : Looks like if you want something done right you have to do it yourself. ##1Charge!", 0; + .@gcount=getd("$@GTEMP_PC_"+.@m$); + .@avg=getd("$@GTEMP_MX_"+.@m$); + kdoor0422Spawn(.@gcount*3, 20, 24, 59, 59); + .@avg=getd("$@GTEMP_MX_"+.@m$); + //debugmes "BOSS - %d, map %s (power %d)", .@gcount, .@m$, .@avg; + .@mob=monster(.@m$, 34, 26, strmobinfo(1, GeneralKrukan), GeneralKrukan, 1, .@n$+"::OnKillBoss"); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg/3); + setunitdata(.@mob, UDT_AGI, 1+.@avg/3); + setunitdata(.@mob, UDT_VIT, 1+.@avg/3); + setunitdata(.@mob, UDT_INT, 1+.@avg/3); + setunitdata(.@mob, UDT_DEX, 1+.@avg/3); + setunitdata(.@mob, UDT_LUK, 1+.@avg/3); + setunitdata(.@mob, UDT_ADELAY, 1372); + setunitdata(.@mob, UDT_ATKRANGE, 3); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*320); + setunitdata(.@mob, UDT_HP, .@avg*320); + setunitdata(.@mob, UDT_ATKMIN, .@avg*65/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*85/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*8/10); + setunitdata(.@mob, UDT_HIT, .@avg*12); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*39/10); // Advised: x4 + // Critical calculation + .@min=15; + .@max=max(.@min, min(50, .@avg/4)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + // Loop through + setd("$@GTEMP_"+.@m$, .@mob); + setd("$@GTEMP_HP_"+.@m$, 10); + end; + +OnTimer185000: + //debugmes "Timer Running"; + .@m$=instance_mapname("042-2"); + .@mob=getd("$@GTEMP_"+.@m$); + .@gcount=getd("$@GTEMP_PC_"+.@m$); + .@ratio=getunitdata(.@mob, UDT_HP)*10/getunitdata(.@mob, UDT_MAXHP); + //debugmes "Ratio %d/%d", .@ratio, getd("$@GTEMP_HP_"+.@m$); + if (.@ratio < getd("$@GTEMP_HP_"+.@m$)) { + mapannounce .@m$, "General Krukan : "+any("Charge!", "To the Abyss with you already!", "Kill them already!", "More of them might be coming!", "Minions, ATTACK!"), 0; + kdoor0422Spawn(.@gcount*2, 20, 24, 59, 59); + setd("$@GTEMP_HP_"+.@m$, .@ratio); + } + //debugmes "Restart timer"; + setnpctimer 180001; + end; + +OnVerifyIntent: + .@g=getcharid(2); + if (.@g < 1) die(); + mesq l("Who are you, and how did you manage to come down here? The sheer power of our Lord prevents anyone not under his control to go here."); + select + l("We are strong fighters, and we want to free the King from his obsession."), + l("Oh, yea, you are right, it was a mistake. Bye-bye."); + mes ""; + if (@menu == 2) { + mesc l("ARE YOU SURE?"), 1; + mesc l("This will remove you from the quest!"); + next; + if (askyesno() == ASK_YES) { + mapannounce getmap(), "General Krukan : I praise you for running away, "+strcharinfo(0)+". You're not a fool it seems.", 0; + warp "014-4", 67, 27; + percentheal 100, 100; + closeclientdialog; + end; + } + } else { + getexp $KAMELOT_MX[.@g]*6, $KAMELOT_MX[.@g]*2; + } + mesc l("With fresh conviction, you prepare yourself to the fight which draws near."); + getitem GuildCoin, 1; + close; + + + + +OnKillMob: + if (!playerattached()) end; + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*5, $KAMELOT_MX[.@g]*2; + end; + +OnKillBoss: + // Fallback + if (!playerattached()) + goto OnTimer180000; + + // OnKillBoss begin + .@m$=instance_mapname("042-2"); + .@n$=instance_npcname(.name$); + stopnpctimer; + setd("$@GTEMP_"+.@m$, 0); + setd("$@GTEMP_HP_"+.@m$, 0); + setd("$@GTEMP_PC_"+.@m$, 0); + setd("$@GTEMP_MX_"+.@m$, 0); + .@g=getcharid(2); + // Player Reward for completing this stage + getitem GuildCoin, min(1, $KAMELOT_MX[.@g]/20); + getexp $KAMELOT_MX[.@g]*50, $KAMELOT_MX[.@g]*10; + // Guild Reward for completing this stage + .@ggp=1200+$KAMELOT_MX[.@g]*5; + .@gxp=$KAMELOT_MX[.@g]*10; + $GUILD_BANK[.@g]+=.@ggp; + guildgetexp(.@gxp); // 10xp per player average level (max 1000/1500) + // Announce + mapannounce getmap(), strcharinfo(0)+" has defeated Krukan!", 0; + // Arrest scene + exp + maptimer .@m$, 1500, .@n$+"::OnReward"; + setd("$@GTEMP_"+.@m$, .@g); + setnpctimer 1000000; // Prefix 1M + startnpctimer; + // Guild Master Notification + .@gm$=getguildmaster(.@g); + if (!getcharid(3, .@gm$)) end; + .@gma=getcharid(3, .@gm$); + .@gmb=getcharid(0, .@gm$); + if (!isloggedin(.@gma, .@gmb)) end; + message .@gm$, strcharinfo(0)+" defeated Krukan: Guild GP +"+.@ggp+" Guild XP +"+.@gxp; + end; + + +/////////////////////////////////////////////////////////////////////// +OnTimer1002000: + .@m$=instance_mapname("042-2"); + .@n$=instance_npcname(.name$); + // Bring Razha (the only mob with fixed difficulty in Kamelot) + .@raz=monster(.@m$, 41, 23, strmobinfo(1, GeneralRazha), GeneralRazha, 1, + .@n$+"::OnOptionalBoss"); + // Configure the optional boss + setunitdata(.@raz, UDT_LEVEL, 150); + setunitdata(.@raz, UDT_STR, 150); + setunitdata(.@raz, UDT_AGI, 150); + setunitdata(.@raz, UDT_VIT, 150); + setunitdata(.@raz, UDT_INT, 150); + setunitdata(.@raz, UDT_DEX, 150); + setunitdata(.@raz, UDT_LUK, 150); + setunitdata(.@raz, UDT_ADELAY, 1072); + setunitdata(.@raz, UDT_ATKRANGE, 3); + // Battle Status + setunitdata(.@raz, UDT_MAXHP, 150000); + setunitdata(.@raz, UDT_HP, 150000); + setunitdata(.@raz, UDT_ATKMIN, 400); + setunitdata(.@raz, UDT_ATKMAX, 750); + setunitdata(.@raz, UDT_DEF, 300); + setunitdata(.@raz, UDT_MDEF, 300); + setunitdata(.@raz, UDT_HIT, 32767); // Advised: x3 + setunitdata(.@raz, UDT_FLEE, 420); // Advised: x4 + setunitdata(.@raz, UDT_CRIT, 70); + // Loop through + + sc_start(SC_STUN, 15000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@raz); + unittalk(.@raz, "What is happening here??"); + mapannounce .@m$, "General Razha : ##1What's happening here??", 0; + sleep(100); + sc_end(SC_STUN, .@raz); + unitwalk(.@raz, 41, 24); + sleep(200); + sc_start(SC_STUN, 15000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@raz); + end; + +OnTimer1007000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "General Razha : GUARDS!!", 0; + end; + +OnTimer1007500: + .@m$=instance_mapname("042-2"); + .@r1=monster(.@m$, 40, 24, strmobinfo(1, CursedSoldier), CursedSoldier, 1); + .@r2=monster(.@m$, 42, 24, strmobinfo(1, CursedArcher), CursedArcher, 1); + sc_start(SC_STUN, 15000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@r1); + sc_start(SC_STUN, 15000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK, .@r2); + kdoor0422Spawn(49, 20, 24, 59, 59); // 1225 tiles, 1 per 25 tiles + end; + +OnTimer1012000: + .@m$=instance_mapname("042-2"); + mapannounce .@m$, "General Razha : Arrest them all!!", 0; + end; + +OnTimer1015000: + .@m$=instance_mapname("042-2"); + .@t$=instance_mapname("042-3"); + .@g=getd("$@GTEMP_"+.@m$); + .@n$="#KSlimeSpawn"; //instance_npcname("#KSlimeSpawn", $KAMELOT_ID[.@g]); + setarray .@x, 33, 84, 41, 74, 36, 57, 79, 43, 24, 86, 59, 38; + setarray .@y, 135, 127, 119, 107, 96, 84, 63, 67, 67, 22, 49, 27; + .@c=getunits(BL_PC, .@unt, false, .@m$, 20, 24, 59, 59); + for (.@i = 0; .@i < .@c; .@i++) { + specialeffect(FX_HIT, AREA, .@unt[.@i]); + .@v = (.@i % 12); + .@r=attachrid(.@unt[.@i]); + if (.@r) { + warp .@t$, .@x[.@v], .@y[.@v]; + if (!countitem(Lockpicks)) + addtimer 700, .@n$+"::OnFirstSlime"; + setpcblock(PCBLOCK_HARD, false); + } else { + unitwarp(.@unt[.@i], .@t$, .@x[.@v], .@y[.@v]); // And good luck D: + consolebug "FATAL: Could not attach: %d", .@unt[.@i]; + } + } + $KAMELOT_QUEST[.@g]=$KAMELOT_QUEST[.@g]|4; // This part is complete + setd("$@GTEMP_"+.@m$, 0); + stopnpctimer; + end; + +OnReward: + .@g=getcharid(2); + if (.@g < 1) die(); + setpcblock(PCBLOCK_HARD, true); + getitem GuildCoin, min(1, $KAMELOT_MX[.@g]/25); + getexp $KAMELOT_MX[.@g]*50, $KAMELOT_MX[.@g]*10; + dispbottom l("Wait- Something is happening!"); + end; + +OnOptionalBoss: + .@g=getcharid(2); + // Player Reward for completing this stage + getitem GuildCoin, 5; + getexp 50000, 10000; + // Guild Reward for completing this stage + .@ggp=12000; + .@gxp=100000; + $GUILD_BANK[.@g]+=.@ggp; + guildgetexp(.@gxp); // 10xp per player average level (max 1000/1500) + // Announce + mapannounce getmap(), strcharinfo(0)+" has defeated Razha!", 0; + // Guild Master Notification + .@gm$=getguildmaster(.@g); + if (!getcharid(3, .@gm$)) end; + .@gma=getcharid(3, .@gm$); + .@gmb=getcharid(0, .@gm$); + if (!isloggedin(.@gma, .@gmb)) end; + message .@gm$, strcharinfo(0)+" defeated Razha: Guild GP +"+.@ggp+" Guild XP +"+.@gxp; + end; + +function kdoor0422Spawn { + //debugmes "Spawning"; + .@label$=instance_npcname(.name$)+"::OnKillMob"; + .@gcount=getarg(0); + .@x1=getarg(1); + .@y1=getarg(2); + .@x2=getarg(3); + .@y2=getarg(4); + .@m$=instance_mapname("042-2"); + .@avg=getd("$@GTEMP_MX_"+.@m$); + //debugmes "Total %d, map %s (power %d)", .@gcount, .@m$, .@avg; + freeloop(true); + for (.@i=0; .@i < .@gcount; .@i++) { + .@mobId=any(CursedSoldier, CursedArcher); // 50-50 ratio + .@mob=areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mobId), .@mobId, 1, .@label$); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg/4); + setunitdata(.@mob, UDT_AGI, 1+.@avg/4); + setunitdata(.@mob, UDT_VIT, 1+.@avg/4); + setunitdata(.@mob, UDT_INT, 1+.@avg/4); + setunitdata(.@mob, UDT_DEX, 1+.@avg/4); + setunitdata(.@mob, UDT_LUK, 1+.@avg/4); + setunitdata(.@mob, UDT_ADELAY, 1672); + setunitdata(.@mob, UDT_ATKRANGE, (.@mobId == CursedArcher ? any(6,7) : any(1,2))); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*33); + setunitdata(.@mob, UDT_HP, .@avg*33); + setunitdata(.@mob, UDT_ATKMIN, .@avg*40/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*60/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg*9/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*5/10); + setunitdata(.@mob, UDT_HIT, .@avg*45/10); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*35/10); // Advised: x4 + // Critical calculation + .@min=2; + .@max=max(.@min, min(25, .@avg/5)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + // Loop through + } + freeloop(false); + //debugmes "Done spawn 042-2"; + return; +} + +// Script end +} + +// Required exit +042-2,44,60,0 script #KDoor0422B NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@g=getcharid(2); + if ($KAMELOT_QUEST[.@g] & 4) { + warp "042-1@"+.@g, 51, 24; + } else { + dispbottom l("Oh noes! The guards locked the door!"); + } + end; +} + diff --git a/npc/042-3/_import.txt b/npc/042-3/_import.txt new file mode 100644 index 0000000..4596bff --- /dev/null +++ b/npc/042-3/_import.txt @@ -0,0 +1,3 @@ +// Map 042-3: Camelot - Jail +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-3/jail.txt", diff --git a/npc/042-3/jail.txt b/npc/042-3/jail.txt new file mode 100644 index 0000000..d41c33e --- /dev/null +++ b/npc/042-3/jail.txt @@ -0,0 +1,181 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls jails and lockpicks +// Also creates siege towers to keep prisoners in check :3 + +042-3,0,0,0 script #KSlimeSpawn NPC_HIDDEN,{ + end; + +OnKillSlime: + debugmes "Slime slain"; + if (!playerattached()) { + consolewarn "[ERROR] Player not Attached on Slime Death D:"; + debugmes "[ERROR] Cannot retrieve coordinates!!"; + } + if (@lockpicks) + end; + if (rand2(10000) > 1000) { + goto OnFirstSlime; + } + @lockpicks=true; + getitem Lockpicks, 1; + getitem TreasureKey, 1; + end; + +OnFirstSlime: + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to spawn for Kamelot %s", .map$; + debugmes "[ERROR] [KAMELOT] Using dummy data (returned: %d)", .@g; + .@g=0; + } + + // let's not trust .name$ + .@label$="#KSlimeSpawn::OnKillSlime"; + if (!playerattached()) { + consolewarn "[ERROR] Player not Attached on Slime Spawn D:"; + debugmes "[ERROR] Cannot retrieve coordinates!!"; + } + getmapxy(.@m$, .@x, .@y, 0); + sleep2(1800); + .@mob=monster(.@m$, .@x, .@y, strmobinfo(1, CopperSlime), CopperSlime, 1, .@label$); + // This should wipe the monster experience value + setunitdata(.@mob, UDT_LEVEL, 1); + //if ($@GM_OVERRIDE) debugmes "Slime is back: %s [%d]", .@label$, .@mob; + end; + + +// Spawn Siege Towers +OnInstanceInit: + .@m$=instance_mapname("042-3"); + debugmes "Kamelot Init: Original %s Target %s", .map$, .@m$; // Fun fact + monster(.@m$, 89, 133, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 67, 114, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 30, 121, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 23, 137, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 29, 103, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 61, 91, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 75, 69, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 34, 72, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 60, 59, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 87, 32, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 47, 32, strmobinfo(1, SiegeTower), SiegeTower, 1); + monster(.@m$, 31, 35, strmobinfo(1, SiegeTower), SiegeTower, 1); + end; +} + +// Lockpicks functions +function script KamelotLockpick { + // Args: x, y, name. Needs player attached + .@x=getarg(0); + .@y=getarg(1); + .@name$=getarg(2); + mes l("A complex lock seems to be posing a threat to you."); + next; + mes l("But thanks to your %s skills, maybe you can pry this open.", thiefrank()); + next; + + .@s=LockPicking(5, 3, false); + + // You broke free! + if (.@s) { + .@label$=instance_npcname(.@name$)+"::OnKamelotSlide"; + addtimer 10, .@label$; + areatimer getmap(), .@x-1, .@y-1, .@x+1, .@y, 10, .@label$; + return; + } + mes l("What's this dark magic, the password has changed!"); + .@label$=instance_npcname("#KSlimeSpawn")+"::OnKillSlime"; + + // Give player a easy way to get lockpicks for this + // (Overrides original .@x/.@y variables) + if (countitem(Lockpicks) <= 1) { + @lockpicks=false; + getmapxy(.@m$, .@x, .@y, 0); + .@mob=monster(.@m$, .@x, .@y, strmobinfo(1, CopperSlime), CopperSlime, 1, .@label$); + // This should wipe the monster experience value + setunitdata(.@mob, UDT_LEVEL, 1); + } + return; +} + +// Cell Doors +042-3,33,137,0 script Cell Door#K01 NPC_NO_SPRITE,{ + KamelotLockpick(.x, .y, .name$); + close; +OnInit: +OnInstanceInit: + .distance=2; + end; + +OnKamelotSlide: + .@label$=instance_npcname(.name$)+"::OnKamelotSlide"; + deltimer .@label$; + dispbottom l("You're finally free!"); + slide .x, .y+1; + @lockpicks=true; + end; +} + +// Duplication of doors +042-3,84,129,0 duplicate(Cell Door#K01) Cell Door#K02 NPC_NO_SPRITE +042-3,41,121,0 duplicate(Cell Door#K01) Cell Door#K03 NPC_NO_SPRITE +042-3,74,109,0 duplicate(Cell Door#K01) Cell Door#K04 NPC_NO_SPRITE +042-3,36,98,0 duplicate(Cell Door#K01) Cell Door#K05 NPC_NO_SPRITE +042-3,57,86,0 duplicate(Cell Door#K01) Cell Door#K06 NPC_NO_SPRITE +042-3,79,65,0 duplicate(Cell Door#K01) Cell Door#K07 NPC_NO_SPRITE +042-3,43,69,0 duplicate(Cell Door#K01) Cell Door#K08 NPC_NO_SPRITE +042-3,24,69,0 duplicate(Cell Door#K01) Cell Door#K09 NPC_NO_SPRITE +042-3,86,24,0 duplicate(Cell Door#K01) Cell Door#K10 NPC_NO_SPRITE +042-3,59,51,0 duplicate(Cell Door#K01) Cell Door#K11 NPC_NO_SPRITE +042-3,38,29,0 duplicate(Cell Door#K01) Cell Door#K12 NPC_NO_SPRITE + + + + +// Leave the dungeon +042-3,62,19,0 script #KDoor0423 NPC_HIDDEN,1,0,{ + end; + +OnTouch: + .@g=getcharid(2); + warp "042-4@"+.@g, any(59,60), 77; + addtimer 1000, .name$+"::OnHey"; + end; + +OnHey: + dispbottom l("Oh, here the path seems to split. Which way should we go?"); + addtimer 3000, .name$+"::OnHey2"; + end; + +OnHey2: + dispbottom l("Or should we even split ourselves to check all possible ways?")+" "+col(l("[Caution, this may be dangerous!]"), 1); + addtimer 7000, .name$+"::OnHey3"; + end; + +OnHey3: + dispbottom l("Also, I don't think we will be able to go back if we pick the wrong way."); + addtimer 5000, .name$+"::OnHey4"; + end; + +OnHey4: + dispbottom l("If we don't know where to go - Maybe we should go back looking for clues?"); + end; +} + +// Required exit. This one has no conditions, so it is not really required +// But I do not trust Instancing System, so better safe than sorry! +042-3,58,140,0 script #KDoor0423B NPC_HIDDEN,0,0,{ + end; + +OnTouch: + .@g=getcharid(2); + warp "042-2@"+.@g, 41, 23; + end; +} + diff --git a/npc/042-4/_import.txt b/npc/042-4/_import.txt new file mode 100644 index 0000000..00c3684 --- /dev/null +++ b/npc/042-4/_import.txt @@ -0,0 +1,4 @@ +// Map 042-4: Camelot - Sewer +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-4/_warps.txt", +"npc/042-4/ctrl.txt", diff --git a/npc/042-4/_warps.txt b/npc/042-4/_warps.txt new file mode 100644 index 0000000..042fa30 --- /dev/null +++ b/npc/042-4/_warps.txt @@ -0,0 +1,5 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 042-4: Camelot - Sewer warps +042-4,60,78,0 warp #042-4_60_78 1,0,042-3,62,20 +042-4,74,20,0 warp #042-4_74_20 2,0,042-5,43,98 +042-4,67,20,0 warp #042-4_67_20 0,0,042-5,36,98 diff --git a/npc/042-4/ctrl.txt b/npc/042-4/ctrl.txt new file mode 100644 index 0000000..4bea944 --- /dev/null +++ b/npc/042-4/ctrl.txt @@ -0,0 +1,117 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls sewers. +// This one is simple, it just sends a message about path splitting +// And spawn several monsters when instance is initialized D: +// Also handles their respawn. + +// A simple random treasure chest - to be sure players were introduced to this +// awesome system. Same rules as any treasure box still applies. +042-4,94,47,0 script #chest_0424 NPC_CHEST,{ + function monster0424; + TreasureBox(); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; + +OnInit: + .distance = 2; + end; + +OnInstanceInit: + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to spawn for Kamelot %s", .map$; + debugmes "[ERROR] [KAMELOT] Using dummy data (returned: %d)", .@g; + .@g=0; + } + debugmes "Spawning monsters for guild %d", .@g; + .@mx=getguildavg(.@g); + monster0424(1, 20, 20, 115, 100, .@mx); + monster0424(4, 20, 51, 51, 71, .@mx); + monster0424(5, 85, 56, 115, 100, .@mx); + monster0424(2, 79, 40, 97, 52, .@mx); + monster0424(5, 51, 20, 80, 50, .@mx); + + // Neutral monsters + areamonster(.map$, 20, 20, 115, 100, strmobinfo(1, Blub), Blub, 5); + areamonster(.map$, 20, 20, 115, 100, strmobinfo(1, ManaGhost), ManaGhost, max(1, .@mx/10)); + + // Bonus monsters + if (!rand2(3)) + areamonster(.map$, 20, 20, 115, 100, "Micksha's Tortuga", Tortuga, 1); + end; + +OnKillMob: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*7, $KAMELOT_MX[.@g]*3; + .@delay=max(7000, 42000-$KAMELOT_PC[.@g]*2000); + // FALLTHROUGH + +OnRespawn: + .@delay=(.@delay ? .@delay : 7000); + sleep(.@delay); + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to respawn for Kamelot %s", .map$; + .@g=0; + } + monster0424(1, 20, 20, 115, 100, $KAMELOT_MX[.@g]); + end; + +function monster0424 { + .@label$=instance_npcname(.name$)+"::OnKillMob"; + .@gcount=getarg(0); + .@x1=getarg(1); + .@y1=getarg(2); + .@x2=getarg(3); + .@y2=getarg(4); + .@avg=getarg(5); + .@m$=instance_mapname("042-4"); + //debugmes "Total %d, map %s (power %d)", .@gcount, .@m$, .@avg; + freeloop(true); + for (.@i=0; .@i < .@gcount; .@i++) { + .@mobId=any(CursedSoldier, CursedArcher); // 50-50 ratio + .@mob=areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mobId), .@mobId, 1, .@label$); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg*3/10); + setunitdata(.@mob, UDT_AGI, 1+.@avg*3/10); + setunitdata(.@mob, UDT_VIT, 1+.@avg*3/10); + setunitdata(.@mob, UDT_INT, 1+.@avg*3/10); + setunitdata(.@mob, UDT_DEX, 1+.@avg*3/10); + setunitdata(.@mob, UDT_LUK, 1+.@avg*3/10); + setunitdata(.@mob, UDT_ADELAY, 1572); + setunitdata(.@mob, UDT_ATKRANGE, (.@mobId == CursedArcher ? any(6,7) : any(1,2))); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*36); + setunitdata(.@mob, UDT_HP, .@avg*36); + setunitdata(.@mob, UDT_ATKMIN, .@avg*42/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*62/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg*10/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*6/10); + setunitdata(.@mob, UDT_HIT, .@avg*55/10); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*37/10); // Advised: x4 + // Critical calculation + .@min=5; + .@max=max(.@min, min(30, .@avg/4)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + // Loop through + } + freeloop(false); + return; + } + +} + + diff --git a/npc/042-5/_import.txt b/npc/042-5/_import.txt new file mode 100644 index 0000000..cacf2e9 --- /dev/null +++ b/npc/042-5/_import.txt @@ -0,0 +1,4 @@ +// Map 042-5: Camelot - Sewer Paths +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-5/_warps.txt", +"npc/042-5/ctrl.txt", diff --git a/npc/042-5/_warps.txt b/npc/042-5/_warps.txt new file mode 100644 index 0000000..7ef41ef --- /dev/null +++ b/npc/042-5/_warps.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 042-5: Camelot - Sewer Paths warps +042-5,36,99,0 warp #042-5_36_99 0,0,042-4,67,21 +042-5,43,99,0 warp #042-5_43_99 2,0,042-4,74,21 +042-5,59,55,0 warp #042-5_59_55 0,3,042-6,22,76 +042-5,56,20,0 warp #042-5_56_20 2,0,042-7,21,77 +042-5,44,20,0 warp #042-5_44_20 2,0,042-8,51,78 +042-5,20,42,0 warp #042-5_20_42 0,6,042-9,98,79 diff --git a/npc/042-5/ctrl.txt b/npc/042-5/ctrl.txt new file mode 100644 index 0000000..b530ffd --- /dev/null +++ b/npc/042-5/ctrl.txt @@ -0,0 +1,238 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls sewers. +// FIXME: The warps back should only work if treasure was found +// Spawn monsters and respawns them. + +// A simple random treasure chest - to be sure players were introduced to this +// awesome system. Same rules as any treasure box still applies. +042-5,0,0,0 script #ctrl0425 NPC_HIDDEN,{ + function monster0425; + end; + +OnInstanceInit: + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to spawn for Kamelot %s", .map$; + debugmes "[ERROR] [KAMELOT] Using dummy data (returned: %d)", .@g; + .@g=0; + } + debugmes "Spawning monsters for guild %d", .@g; + .@mx=getguildavg(.@g); + monster0425(4, 35, 20, 60, 100, .@mx); + monster0425(4, 20, 20, 35, 100, .@mx); + + // Neutral monsters + areamonster(.map$, 35, 20, 60, 100, strmobinfo(1, Blub), Blub, 5); + areamonster(.map$, 20, 20, 35, 100, strmobinfo(1, ManaGhost), ManaGhost, max(1, .@mx/10)); + + // Bonus monsters + if (!rand2(3)) + areamonster(.map$, 20, 20, 60, 100, strmobinfo(1, WhirlyBird), WhirlyBird, 1); + if (!rand2(2)) + areamonster(.map$, 20, 20, 60, 100, strmobinfo(1, SilverChest), SilverChest, 1); + if (!rand2(2)) + areamonster(.map$, 20, 20, 60, 100, strmobinfo(1, BronzeChest), BronzeChest, 2); + end; + +OnKillMob: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*7, $KAMELOT_MX[.@g]*3; + .@delay=max(7000, 42000-$KAMELOT_PC[.@g]*2000); + // FALLTHROUGH + +OnRespawn: + .@delay=(.@delay ? .@delay : 7000); + sleep(.@delay); + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to respawn for Kamelot %s", .map$; + .@g=0; + } + monster0425(1, 20, 20, 80, 120, $KAMELOT_MX[.@g]); + end; + +function monster0425 { + .@label$=instance_npcname(.name$)+"::OnKillMob"; + .@gcount=getarg(0); + .@x1=getarg(1); + .@y1=getarg(2); + .@x2=getarg(3); + .@y2=getarg(4); + .@avg=getarg(5); + .@m$=instance_mapname("042-5"); + //debugmes "Total %d, map %s (power %d)", .@gcount, .@m$, .@avg; + freeloop(true); + for (.@i=0; .@i < .@gcount; .@i++) { + .@mobId=any(CursedSoldier, CursedArcher); // 50-50 ratio + .@mob=areamonster(.@m$, .@x1, .@y1, .@x2, .@y2, strmobinfo(1, .@mobId), .@mobId, 1, .@label$); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg); + setunitdata(.@mob, UDT_STR, 1+.@avg*5/10); + setunitdata(.@mob, UDT_AGI, 1+.@avg*4/10); + setunitdata(.@mob, UDT_VIT, 1+.@avg*5/10); + setunitdata(.@mob, UDT_INT, 1+.@avg*5/10); + setunitdata(.@mob, UDT_DEX, 1+.@avg*5/10); + setunitdata(.@mob, UDT_LUK, 1+.@avg*4/10); + setunitdata(.@mob, UDT_ADELAY, 1472); + setunitdata(.@mob, UDT_ATKRANGE, (.@mobId == CursedArcher ? any(6,7) : any(1,2))); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*38); + setunitdata(.@mob, UDT_HP, .@avg*38); + setunitdata(.@mob, UDT_ATKMIN, .@avg*45/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*65/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg*11/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*7/10); + setunitdata(.@mob, UDT_HIT, .@avg*65/10); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*40/10); // Advised: x4 + // Critical calculation + .@min=8; + .@max=max(.@min, min(35, .@avg/4)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + // Loop through + } + freeloop(false); + return; + } + +} + +/////////////////////////////////////////////////////////////// +// This is required for others + +// KamelotTreasure( POSITION ID ) +function script KamelotTreasure { + .@id=getarg(0); + .@g=getcharid(2); + if (.@g < 1) die(); + if ($KAMELOT_KEYMASK[.@g] & .@id) { + mesc l("The chest is unlocked and empty."); + close; + } + inventoryplace Iten, 1, NPCEyes, 2; + + mesc l("Open the chest?"); + mesc l("Cost: 1 @@", getitemlink(TreasureKey)), 1; + if (!countitem(TreasureKey)) + close; + next; + if (askyesno() == ASK_NO) + close; + + delitem TreasureKey, 1; + mesc l("You open the chest!"); + + .@empty=($KAMELOT_KEYMASK[.@g] & .@id); + $KAMELOT_KEYMASK[.@g]=$KAMELOT_KEYMASK[.@g]|.@id; + + if (!.@empty) { + if (.@id == $KAMELOT_KEY[.@g]) { + dispbottom l("You found the key!"); + rentitem KamelotKey, 86400; // Ensure they expire after 24 hours + .@key=true; + } + .@r=rand2(10000)-$KAMELOT_MX[.@g]+100; + + // Select treasure list + if (.@r <= 0) // 0.01% total, 0.025% each + .@loot=any(MylarinDust, SaviorBlueprint, SupremeGift, HousingLetterIII, TimeFlask, MercCard_EH); + else if (.@r < 330) // 0.3% each + .@loot=any(MagicApple, PrismGift, EquipmentBlueprintD, DarkPulsar, Halberd, AncientShield, AncientSword, Setzer, MercBoxD, ScrollMagnusHealC, Shemagh); + else + .@loot=any(SacredImmortalityPotion, DivineApple, ArcmageBoxset, GoldenApple, MercBoxA, MercBoxB, MercBoxC, MoveSpeedPotion, AtroposMixture, EverburnPowder, IridiumOre, PlatinumOre, YerbaMate, SmokeGrenade, SnakeEgg, LachesisBrew, BoneAmmoBox, GoldPieces, SilverGift, TerraniteOre, LeadOre, TinOre, SilverOre, GoldOre, TitaniumOre, FluoPowder, EquipmentBlueprintC, AlchemyBlueprintC, AlchemyBlueprintD, AncientBlueprint, JasmineTea, MoubooSteak, ClothoLiquor, Coal, RedPlushWine, HastePotion, CoinBag, StrengthPotion, Pearl, BronzeGift, IronOre, CopperOre, BlueDye, EquipmentBlueprintB, AlchemyBlueprintB, AlchemyBlueprintC, OolongTea, Croconut, CelestiaTea, MoubooSteak, ClothoLiquor, Coal, SmallMushroom, HastePotion, StrengthPotion, WoodenLog, LeatherPatch, DwarvenSake, EquipmentBlueprintA, EquipmentBlueprintB, AlchemyBlueprintA, SpearmintTea, TreasureMap, FatesPotion, CrazyRum, LightGreenDiamond, EarthPowder, WoodenLog, MysteriousBottle, FluoPowder, ChamomileTea, ScrollSCave); // > 70 options (~1% each) + + + mesc l("You find @@ inside!", getitemlink(.@loot)); + // If itemtype == Armor/Weapon, make it guild bound and put bonus + .@t=getiteminfo(.@loot, ITEMINFO_TYPE); + if (.@t == IT_WEAPON) { + getitembound .@loot, 1, 2; + delinventorylist(); // Needed, because we'll rely on rfind() + getinventorylist(); + .@index=array_rfind(@inventorylist_id, .@loot); + setitemoptionbyindex(.@index, 0, IOPT_SPLASHDAMAGE, 1); + } else if (.@t == IT_ARMOR) { + getitembound .@loot, 1, 2; + delinventorylist(); // Needed, because we'll rely on rfind() + getinventorylist(); + .@index=array_rfind(@inventorylist_id, .@loot); + setitemoptionbyindex(.@index, 0, ATTR_TOLERACE_ALL, 10); + } else { + getitem .@loot, 1; + } + } else { + mesc l("You find @@ inside!", l("nothing")); + mesc l("Seems like someone else opened this chest before you!"); + } + + // Announcement + if (.@key) + .@p$=b(" They found the key!"); + mapannounce "042-6@"+.@g, strcharinfo(0)+" has opened a Treasure Chest!"+.@p$, 0; + mapannounce "042-7@"+.@g, strcharinfo(0)+" has opened a Treasure Chest!"+.@p$, 0; + mapannounce "042-8@"+.@g, strcharinfo(0)+" has opened a Treasure Chest!"+.@p$, 0; + mapannounce "042-9@"+.@g, strcharinfo(0)+" has opened a Treasure Chest!"+.@p$, 0; + // Guild Master Notification + .@gm$=getguildmaster(.@g); + if (!getcharid(3, .@gm$)) return; + .@gma=getcharid(3, .@gm$); + .@gmb=getcharid(0, .@gm$); + if (!isloggedin(.@gma, .@gmb)) return; + message .@gm$, strcharinfo(0)+" has opened a Treasure Chest."+.@p$; + return; +} + +///////////////////////////////////////////////////////////////////////////// +// KamelotBoss(Map, x, y, power, NPC) +function script KamelotBoss { + .@label$=instance_npcname(getarg(4))+"::OnKillBoss"; + .@gcount=1; + .@x1=getarg(1); + .@y1=getarg(2); + .@avg=getarg(3); + .@m$=instance_mapname(getarg(0)); + //debugmes "Total %d, map %s (power %d)", .@gcount, .@m$, .@avg; + .@mobId=any(CursedSoldier, CursedArcher); // 50-50 ratio + .@name$=any("Gawain", "Tristan", "Kay", "Palamedes", "Mordred", "Bors", "Bedivere", "Lionel", "Bleoberry", "Lucan", "Lamorak", "Pellas", "Hector", "Dragonet", "Bronor", "Alymere"); // Typos on purpose: https://www.arthurian-legend.com/knights-round-table/ + .@mob=monster(.@m$, .@x1, .@y1, .@name$, .@mobId, 1, .@label$); + // Reconfigure the monster + setunitdata(.@mob, UDT_LEVEL, .@avg+20); + setunitdata(.@mob, UDT_STR, 1+.@avg*7/10); + setunitdata(.@mob, UDT_AGI, 1+.@avg*5/10); + setunitdata(.@mob, UDT_VIT, 1+.@avg*7/10); + setunitdata(.@mob, UDT_INT, 1+.@avg*6/10); + setunitdata(.@mob, UDT_DEX, 1+.@avg*6/10); + setunitdata(.@mob, UDT_LUK, 1+.@avg*7/10); + setunitdata(.@mob, UDT_ADELAY, 1072); + setunitdata(.@mob, UDT_ATKRANGE, (.@mobId == CursedArcher ? any(7,8) : any(2,2,3))); + // Battle Status + setunitdata(.@mob, UDT_MAXHP, .@avg*450); + setunitdata(.@mob, UDT_HP, .@avg*450); + setunitdata(.@mob, UDT_ATKMIN, .@avg*60/10); + setunitdata(.@mob, UDT_ATKMAX, .@avg*70/10); + setunitdata(.@mob, UDT_DEF, 1+.@avg*14/10); + setunitdata(.@mob, UDT_MDEF, 1+.@avg*9/10); + setunitdata(.@mob, UDT_HIT, .@avg*16); // Advised: x3 + setunitdata(.@mob, UDT_FLEE, .@avg*45/10); // Advised: x4 + // Critical calculation + .@min=30; + .@max=max(.@min, min(70, .@avg/2)); + setunitdata(.@mob, UDT_CRIT, rand2(.@min, .@max)); + return; +} + + + + + diff --git a/npc/042-6/_import.txt b/npc/042-6/_import.txt new file mode 100644 index 0000000..5b46268 --- /dev/null +++ b/npc/042-6/_import.txt @@ -0,0 +1,4 @@ +// Map 042-6: Camelot Sewer East Path +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-6/_warps.txt", +"npc/042-6/ctrl.txt", diff --git a/npc/042-6/_warps.txt b/npc/042-6/_warps.txt new file mode 100644 index 0000000..d67d255 --- /dev/null +++ b/npc/042-6/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 042-6: Camelot Sewer East Path warps +042-6,55,20,0 warp #042-6_55_20 0,0,042-10,149,135 diff --git a/npc/042-6/ctrl.txt b/npc/042-6/ctrl.txt new file mode 100644 index 0000000..819d8d8 --- /dev/null +++ b/npc/042-6/ctrl.txt @@ -0,0 +1,116 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls sewers. +// FIXME: People should not be able to return here once they leave to 042-10 +// Spawn monsters and respawns them. + +// A simple random treasure chest - to be sure players were introduced to this +// awesome system. Same rules as any treasure box still applies. +042-6,65,89,0 script #chest_0426 NPC_CHEST,{ + KamelotTreasure(8); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; + +OnInit: + .distance = 2; + end; + +OnInstanceInit: + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to spawn for Kamelot %s", .map$; + debugmes "[ERROR] [KAMELOT] Using dummy data (returned: %d)", .@g; + .@g=0; + } + debugmes "Spawning monsters for guild %d", .@g; + .@mx=getguildavg(.@g); + + // Corritors + KamelotCaveSpawn(2, 21, 75, 50, 85, .@mx, "042-6"); + KamelotCaveSpawn(7, 45, 45, 74, 80, .@mx, "042-6"); + KamelotCaveSpawn(5, 52, 21, 90, 60, .@mx, "042-6"); + KamelotCaveSpawn(3, 54, 28, 82, 39, .@mx, "042-6"); + + // Boss Chamber + KamelotCaveSpawn(5, 59, 71, 82, 91, .@mx, "042-6"); + KamelotBoss("042-6", 74, 80, .@mx+1, .name$); + + // Boss monster + // TODO + + // Neutral monsters + areamonster(.map$, 20, 20, 90, 90, strmobinfo(1, YellowSlime), YellowSlime, 5); + areamonster(.map$, 20, 20, 90, 90, strmobinfo(1, ManaGhost), ManaGhost, max(1, .@mx/10)); + areamonster(.map$, 20, 20, 90, 90, strmobinfo(1, CaveMaggot), CaveMaggot, 30); + + // Bonus monsters + if (!rand2(2)) + areamonster(.map$, 45, 20, 90, 90, strmobinfo(1, MagicBif), MagicBif, 2); + if (!rand2(2)) + areamonster(.map$, 20, 20, 90, 90, strmobinfo(1, GoldenChest), GoldenChest, 1); + if (!rand2(2)) + areamonster(.map$, 20, 20, 90, 90, strmobinfo(1, SilverChest), SilverChest, 2); + if (!rand2(2)) + areamonster(.map$, 20, 20, 90, 90, strmobinfo(1, BronzeChest), BronzeChest, 3); + end; + +OnKillBoss: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getitem GuildCoin, 2+min(13, $KAMELOT_MX[.@g]/10); + getexp $KAMELOT_MX[.@g]*14, $KAMELOT_MX[.@g]*8; + mapannounce getmap(), strcharinfo(0)+" has defeated the boss!", 0; + .@delay=max(5000, 22000-$KAMELOT_PC[.@g]*1500); + goto OnRespawn; + +OnKillMob: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*7, $KAMELOT_MX[.@g]*4; + .@delay=max(7000, 42000-$KAMELOT_PC[.@g]*2000); + // FALLTHROUGH + +OnRespawn: + .@delay=(.@delay ? .@delay : 7000); + sleep(.@delay); + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to respawn for Kamelot %s", .map$; + .@g=0; + } + KamelotCaveSpawn(1, 20, 20, 115, 100, $KAMELOT_MX[.@g], "042-6"); + end; + +} + + +// The exit only works before chest is looted +042-6,21,76,0 script #KDoor0426 NPC_HIDDEN,0,3,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + if ($KAMELOT_KEYMASK[.@g] & 8) goto L_NoAccess; + warp "042-5@"+.@g, 58, 53; + end; + + +L_NoAccess: + dispbottom l("OH NOES! The ceiling seems to have collapsed when the chest was open! We are forced to go forward!!"); + end; +} + diff --git a/npc/042-7/_import.txt b/npc/042-7/_import.txt new file mode 100644 index 0000000..7b351c5 --- /dev/null +++ b/npc/042-7/_import.txt @@ -0,0 +1,4 @@ +// Map 042-7: Camelot Sewer Northeast Path +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-7/_warps.txt", +"npc/042-7/ctrl.txt", diff --git a/npc/042-7/_warps.txt b/npc/042-7/_warps.txt new file mode 100644 index 0000000..25d4167 --- /dev/null +++ b/npc/042-7/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 042-7: Camelot Sewer Northeast Path warps +042-7,62,56,0 warp #042-7_62_56 3,0,042-10,86,138 diff --git a/npc/042-7/ctrl.txt b/npc/042-7/ctrl.txt new file mode 100644 index 0000000..66f211a --- /dev/null +++ b/npc/042-7/ctrl.txt @@ -0,0 +1,116 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls sewers. +// FIXME: People should not be able to return here once they leave to 042-10 +// Spawn monsters and respawns them. + +// A simple random treasure chest - to be sure players were introduced to this +// awesome system. Same rules as any treasure box still applies. +042-7,22,30,0 script #chest_0427 NPC_CHEST,{ + KamelotTreasure(4); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; + +OnInit: + .distance = 2; + end; + +OnInstanceInit: + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to spawn for Kamelot %s", .map$; + debugmes "[ERROR] [KAMELOT] Using dummy data (returned: %d)", .@g; + .@g=0; + } + debugmes "Spawning monsters for guild %d", .@g; + .@mx=getguildavg(.@g); + + // Corritors + KamelotCaveSpawn(2, 18, 52, 30, 80, .@mx, "042-7"); + KamelotCaveSpawn(3, 20, 44, 58, 60, .@mx, "042-7"); + KamelotCaveSpawn(4, 40, 55, 70, 85, .@mx, "042-7"); + KamelotCaveSpawn(5, 50, 57, 70, 85, .@mx, "042-7"); + + // Boss Chamber + KamelotCaveSpawn(8, 19, 20, 56, 44, .@mx, "042-7"); + KamelotBoss("042-7", 30, 30, .@mx+1, .name$); + + // Boss monster + // TODO + + // Neutral monsters + areamonster(.map$, 20, 20, 70, 80, strmobinfo(1, YellowSlime), YellowSlime, 5); + areamonster(.map$, 20, 20, 70, 80, strmobinfo(1, ManaGhost), ManaGhost, max(1, .@mx/10)); + areamonster(.map$, 20, 20, 70, 80, strmobinfo(1, CaveMaggot), CaveMaggot, 30); + + // Bonus monsters + if (!rand2(2)) + areamonster(.map$, 45, 20, 70, 80, strmobinfo(1, MagicBif), MagicBif, 2); + if (!rand2(2)) + areamonster(.map$, 20, 20, 70, 80, strmobinfo(1, GoldenChest), GoldenChest, 1); + if (!rand2(2)) + areamonster(.map$, 20, 20, 70, 80, strmobinfo(1, SilverChest), SilverChest, 2); + if (!rand2(2)) + areamonster(.map$, 20, 20, 70, 80, strmobinfo(1, BronzeChest), BronzeChest, 3); + end; + +OnKillBoss: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getitem GuildCoin, 2+min(13, $KAMELOT_MX[.@g]/10); + getexp $KAMELOT_MX[.@g]*14, $KAMELOT_MX[.@g]*8; + mapannounce getmap(), strcharinfo(0)+" has defeated the boss!", 0; + .@delay=max(3000, 21000-$KAMELOT_PC[.@g]*1000); + goto OnRespawn; + +OnKillMob: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*7, $KAMELOT_MX[.@g]*4; + .@delay=max(7000, 42000-$KAMELOT_PC[.@g]*2000); + // FALLTHROUGH + +OnRespawn: + .@delay=(.@delay ? .@delay : 7000); + sleep(.@delay); + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to respawn for Kamelot %s", .map$; + .@g=0; + } + KamelotCaveSpawn(1, 20, 20, 115, 100, $KAMELOT_MX[.@g], "042-7"); + end; + +} + + +// The exit only works before chest is looted +042-7,21,78,0 script #KDoor0427 NPC_HIDDEN,3,0,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + if ($KAMELOT_KEYMASK[.@g] & 4) goto L_NoAccess; + warp "042-5@"+.@g, 56, 21; + end; + + +L_NoAccess: + dispbottom l("OH NOES! The ceiling seems to have collapsed when the chest was open! We are forced to go forward!!"); + end; +} + diff --git a/npc/042-8/_import.txt b/npc/042-8/_import.txt new file mode 100644 index 0000000..40ae90f --- /dev/null +++ b/npc/042-8/_import.txt @@ -0,0 +1,4 @@ +// Map 042-8: Camelot Sewer Northwest Path +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-8/_warps.txt", +"npc/042-8/ctrl.txt", diff --git a/npc/042-8/_warps.txt b/npc/042-8/_warps.txt new file mode 100644 index 0000000..abdb0d6 --- /dev/null +++ b/npc/042-8/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 042-8: Camelot Sewer Northwest Path warps +042-8,81,54,0 warp #042-8_81_54 2,0,042-10,23,138 diff --git a/npc/042-8/ctrl.txt b/npc/042-8/ctrl.txt new file mode 100644 index 0000000..380092c --- /dev/null +++ b/npc/042-8/ctrl.txt @@ -0,0 +1,116 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls sewers. +// FIXME: People should not be able to return here once they leave to 042-10 +// Spawn monsters and respawns them. + +// A simple random treasure chest - to be sure players were introduced to this +// awesome system. Same rules as any treasure box still applies. +042-8,60,40,0 script #chest_0428 NPC_CHEST,{ + KamelotTreasure(2); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; + +OnInit: + .distance = 2; + end; + +OnInstanceInit: + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to spawn for Kamelot %s", .map$; + debugmes "[ERROR] [KAMELOT] Using dummy data (returned: %d)", .@g; + .@g=0; + } + debugmes "Spawning monsters for guild %d", .@g; + .@mx=getguildavg(.@g); + + // Corritors + KamelotCaveSpawn(2, 35, 58, 54, 80, .@mx, "042-8"); // Sewer + KamelotCaveSpawn(4, 36, 22, 75, 32, .@mx, "042-8"); // North + KamelotCaveSpawn(6, 65, 20, 84, 70, .@mx, "042-8"); // Exit + KamelotCaveSpawn(5, 20, 20, 41, 57, .@mx, "042-8"); // West + + // Boss Chamber + KamelotCaveSpawn(5, 40, 32, 62, 50, .@mx, "042-8"); + KamelotBoss("042-8", 50, 42, .@mx+1, .name$); + + // Boss monster + // TODO + + // Neutral monsters + areamonster(.map$, 20, 20, 85, 80, strmobinfo(1, YellowSlime), YellowSlime, 5); + areamonster(.map$, 20, 20, 85, 80, strmobinfo(1, ManaGhost), ManaGhost, max(1, .@mx/10)); + areamonster(.map$, 20, 20, 85, 80, strmobinfo(1, CaveMaggot), CaveMaggot, 30); + + // Bonus monsters + if (!rand2(2)) + areamonster(.map$, 45, 20, 85, 80, strmobinfo(1, MagicBif), MagicBif, 2); + if (!rand2(2)) + areamonster(.map$, 20, 20, 85, 80, strmobinfo(1, GoldenChest), GoldenChest, 1); + if (!rand2(2)) + areamonster(.map$, 20, 20, 85, 80, strmobinfo(1, SilverChest), SilverChest, 2); + if (!rand2(2)) + areamonster(.map$, 20, 20, 85, 80, strmobinfo(1, BronzeChest), BronzeChest, 3); + end; + +OnKillBoss: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getitem GuildCoin, 2+min(13, $KAMELOT_MX[.@g]/10); + getexp $KAMELOT_MX[.@g]*14, $KAMELOT_MX[.@g]*8; + mapannounce getmap(), strcharinfo(0)+" has defeated the boss!", 0; + .@delay=max(7000, 42000-$KAMELOT_PC[.@g]*2000); + goto OnRespawn; + +OnKillMob: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*7, $KAMELOT_MX[.@g]*4; + .@delay=max(7000, 42000-$KAMELOT_PC[.@g]*2000); + // FALLTHROUGH + +OnRespawn: + .@delay=(.@delay ? .@delay : 7000); + sleep(.@delay); + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to respawn for Kamelot %s", .map$; + .@g=0; + } + KamelotCaveSpawn(1, 20, 20, 115, 100, $KAMELOT_MX[.@g], "042-8"); + end; + +} + + +// The exit only works before chest is looted +042-8,51,79,0 script #KDoor0428 NPC_HIDDEN,2,0,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + if ($KAMELOT_KEYMASK[.@g] & 2) goto L_NoAccess; + warp "042-5@"+.@g, 44, 21; + end; + + +L_NoAccess: + dispbottom l("OH NOES! The ceiling seems to have collapsed when the chest was open! We are forced to go forward!!"); + end; +} + diff --git a/npc/042-9/_import.txt b/npc/042-9/_import.txt new file mode 100644 index 0000000..cfd445a --- /dev/null +++ b/npc/042-9/_import.txt @@ -0,0 +1,4 @@ +// Map 042-9: Camelot Sewer West Path +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/042-9/_warps.txt", +"npc/042-9/ctrl.txt", diff --git a/npc/042-9/_warps.txt b/npc/042-9/_warps.txt new file mode 100644 index 0000000..6766d0b --- /dev/null +++ b/npc/042-9/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map 042-9: Camelot Sewer West Path warps +042-9,99,21,0 warp #042-9_99_21 0,4,042-10,21,79 diff --git a/npc/042-9/ctrl.txt b/npc/042-9/ctrl.txt new file mode 100644 index 0000000..403c159 --- /dev/null +++ b/npc/042-9/ctrl.txt @@ -0,0 +1,115 @@ +// TMW 2 Script +// Author: +// Jesusalva +// Micksha +// Description: +// Controls sewers. +// FIXME: People should not be able to return here once they leave to 042-10 +// Spawn monsters and respawns them. + +// A simple random treasure chest - to be sure players were introduced to this +// awesome system. Same rules as any treasure box still applies. +042-9,48,76,0 script #chest_0429 NPC_CHEST,{ + KamelotTreasure(1); + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; + +OnInit: + .distance = 2; + end; + +OnInstanceInit: + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to spawn for Kamelot %s", .map$; + debugmes "[ERROR] [KAMELOT] Using dummy data (returned: %d)", .@g; + .@g=0; + } + debugmes "Spawning monsters for guild %d", .@g; + .@mx=getguildavg(.@g); + + // Corritors + KamelotCaveSpawn(3, 67, 60, 100, 85, .@mx, "042-9"); // Sewer + KamelotCaveSpawn(12, 20, 20, 100, 50, .@mx, "042-9"); // West+Exit + KamelotCaveSpawn(3, 28, 50, 67, 67, .@mx, "042-9"); // East + + // Boss Chamber + KamelotCaveSpawn(5, 21, 60, 51, 80, .@mx, "042-9"); + KamelotBoss("042-9", 40, 74, .@mx+1, .name$); + + // Boss monster + // TODO + + // Neutral monsters + areamonster(.map$, 20, 20, 100, 85, strmobinfo(1, YellowSlime), YellowSlime, 5); + areamonster(.map$, 20, 20, 100, 85, strmobinfo(1, ManaGhost), ManaGhost, max(1, .@mx/10)); + areamonster(.map$, 20, 20, 100, 85, strmobinfo(1, CaveMaggot), CaveMaggot, 30); + + // Bonus monsters + if (!rand2(2)) + areamonster(.map$, 45, 20, 100, 85, strmobinfo(1, MagicBif), MagicBif, 2); + if (!rand2(2)) + areamonster(.map$, 20, 20, 100, 85, strmobinfo(1, GoldenChest), GoldenChest, 1); + if (!rand2(2)) + areamonster(.map$, 20, 20, 100, 85, strmobinfo(1, SilverChest), SilverChest, 2); + if (!rand2(2)) + areamonster(.map$, 20, 20, 100, 85, strmobinfo(1, BronzeChest), BronzeChest, 3); + end; + +OnKillBoss: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getitem GuildCoin, 2+min(13, $KAMELOT_MX[.@g]/10); + getexp $KAMELOT_MX[.@g]*14, $KAMELOT_MX[.@g]*8; + mapannounce getmap(), strcharinfo(0)+" has defeated the boss!", 0; + .@delay=max(3000, 21000-$KAMELOT_PC[.@g]*1000); + goto OnRespawn; + +OnKillMob: + if (!playerattached()) + goto OnRespawn; + // Maybe a reward is due + .@g=getcharid(2); + if (.@g < 1) die(); + getexp $KAMELOT_MX[.@g]*7, $KAMELOT_MX[.@g]*4; + .@delay=max(7000, 42000-$KAMELOT_PC[.@g]*2000); + // FALLTHROUGH + +OnRespawn: + .@delay=(.@delay ? .@delay : 7000); + sleep(.@delay); + // Yes, we just hope it works out of box + explode(.@map$, .map$, "@"); + .@g=atoi(.@map$[1]); + if (.@g < 1) { + consolewarn "[ERROR] [KAMELOT] Unable to respawn for Kamelot %s", .map$; + .@g=0; + } + KamelotCaveSpawn(1, 20, 20, 115, 100, $KAMELOT_MX[.@g], "042-9"); + end; + +} + + +// The exit only works before chest is looted +042-9,99,80,0 script #KDoor0429 NPC_HIDDEN,0,6,{ + end; + +OnTouch: + .@g=getcharid(2); + if (.@g < 1) die(); + if ($KAMELOT_KEYMASK[.@g] & 1) goto L_NoAccess; + warp "042-5@"+.@g, 21, 43; + end; + + +L_NoAccess: + dispbottom l("OH NOES! The ceiling seems to have collapsed when the chest was open! We are forced to go forward!!"); + end; +} + diff --git a/npc/_import.txt b/npc/_import.txt new file mode 100644 index 0000000..1d7d82c --- /dev/null +++ b/npc/_import.txt @@ -0,0 +1,415 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. + +@include "npc/000-0-0/_import.txt" +@include "npc/000-0-1/_import.txt" +@include "npc/000-0/_import.txt" +@include "npc/000-1/_import.txt" +@include "npc/001-1/_import.txt" +@include "npc/001-10-1/_import.txt" +@include "npc/001-10/_import.txt" +@include "npc/001-11/_import.txt" +@include "npc/001-12/_import.txt" +@include "npc/001-13-0/_import.txt" +@include "npc/001-13-1/_import.txt" +@include "npc/001-13-2/_import.txt" +@include "npc/001-13/_import.txt" +@include "npc/001-14/_import.txt" +@include "npc/001-2/_import.txt" +@include "npc/001-3-1/_import.txt" +@include "npc/001-3/_import.txt" +@include "npc/001-4/_import.txt" +@include "npc/001-5/_import.txt" +@include "npc/001-6/_import.txt" +@include "npc/001-7/_import.txt" +@include "npc/001-8/_import.txt" +@include "npc/001-9/_import.txt" +@include "npc/002-1/_import.txt" +@include "npc/002-2/_import.txt" +@include "npc/002-3/_import.txt" +@include "npc/002-4/_import.txt" +@include "npc/002-5/_import.txt" +@include "npc/003-0-1/_import.txt" +@include "npc/003-0-2/_import.txt" +@include "npc/003-0/_import.txt" +@include "npc/003-1-1/_import.txt" +@include "npc/003-1-2/_import.txt" +@include "npc/003-1-3/_import.txt" +@include "npc/003-1/_import.txt" +@include "npc/003-10/_import.txt" +@include "npc/003-13/_import.txt" +@include "npc/003-2-1/_import.txt" +@include "npc/003-2/_import.txt" +@include "npc/003-3/_import.txt" +@include "npc/003-4-1/_import.txt" +@include "npc/003-4/_import.txt" +@include "npc/003-5/_import.txt" +@include "npc/003-6/_import.txt" +@include "npc/003-7/_import.txt" +@include "npc/003-8/_import.txt" +@include "npc/003-9-1/_import.txt" +@include "npc/003-9/_import.txt" +@include "npc/004-1/_import.txt" +@include "npc/004-2-1/_import.txt" +@include "npc/004-2-10/_import.txt" +@include "npc/004-2-11/_import.txt" +@include "npc/004-2-12/_import.txt" +@include "npc/004-2-2/_import.txt" +@include "npc/004-2-3/_import.txt" +@include "npc/004-2-4/_import.txt" +@include "npc/004-2-5/_import.txt" +@include "npc/004-2-6/_import.txt" +@include "npc/004-2-7/_import.txt" +@include "npc/004-2-8/_import.txt" +@include "npc/004-2-9/_import.txt" +@include "npc/004-2/_import.txt" +@include "npc/004-3-1/_import.txt" +@include "npc/004-3-2/_import.txt" +@include "npc/004-3-3/_import.txt" +@include "npc/004-3-4/_import.txt" +@include "npc/004-3-5/_import.txt" +@include "npc/004-3-6/_import.txt" +@include "npc/004-3/_import.txt" +@include "npc/005-1-1/_import.txt" +@include "npc/005-1/_import.txt" +@include "npc/005-2-1/_import.txt" +@include "npc/005-2/_import.txt" +@include "npc/005-3/_import.txt" +@include "npc/005-4/_import.txt" +@include "npc/005-5/_import.txt" +@include "npc/005-6/_import.txt" +@include "npc/005-7/_import.txt" +@include "npc/006-0/_import.txt" +@include "npc/006-1/_import.txt" +@include "npc/006-10/_import.txt" +@include "npc/006-2-1/_import.txt" +@include "npc/006-2-2/_import.txt" +@include "npc/006-2-3/_import.txt" +@include "npc/006-2-4/_import.txt" +@include "npc/006-2-5/_import.txt" +@include "npc/006-2-6/_import.txt" +@include "npc/006-2/_import.txt" +@include "npc/006-3/_import.txt" +@include "npc/006-4-1/_import.txt" +@include "npc/006-4/_import.txt" +@include "npc/006-5/_import.txt" +@include "npc/006-6/_import.txt" +@include "npc/006-7/_import.txt" +@include "npc/006-8/_import.txt" +@include "npc/006-9/_import.txt" +@include "npc/007-1-1/_import.txt" +@include "npc/007-1-2/_import.txt" +@include "npc/007-1/_import.txt" +@include "npc/007-2/_import.txt" +@include "npc/008-0/_import.txt" +@include "npc/008-1/_import.txt" +@include "npc/008-2/_import.txt" +@include "npc/009-1/_import.txt" +@include "npc/009-2/_import.txt" +@include "npc/009-3/_import.txt" +@include "npc/009-4/_import.txt" +@include "npc/009-5/_import.txt" +@include "npc/009-6/_import.txt" +@include "npc/009-7/_import.txt" +@include "npc/010-1-1/_import.txt" +@include "npc/010-1-10/_import.txt" +@include "npc/010-1-11/_import.txt" +@include "npc/010-1-12/_import.txt" +@include "npc/010-1-2/_import.txt" +@include "npc/010-1-3/_import.txt" +@include "npc/010-1-4/_import.txt" +@include "npc/010-1-5/_import.txt" +@include "npc/010-1-6/_import.txt" +@include "npc/010-1-7/_import.txt" +@include "npc/010-1-8/_import.txt" +@include "npc/010-1-9/_import.txt" +@include "npc/010-1/_import.txt" +@include "npc/010-2-10/_import.txt" +@include "npc/010-2-11/_import.txt" +@include "npc/010-2-12/_import.txt" +@include "npc/010-2-13/_import.txt" +@include "npc/010-2-14/_import.txt" +@include "npc/010-2-15/_import.txt" +@include "npc/010-2-16/_import.txt" +@include "npc/010-2-2/_import.txt" +@include "npc/010-2-3/_import.txt" +@include "npc/010-2-4/_import.txt" +@include "npc/010-2-5/_import.txt" +@include "npc/010-2-6/_import.txt" +@include "npc/010-2-7/_import.txt" +@include "npc/010-2-8/_import.txt" +@include "npc/010-2-9/_import.txt" +@include "npc/010-2/_import.txt" +@include "npc/010-3/_import.txt" +@include "npc/010-4-1/_import.txt" +@include "npc/010-4-2/_import.txt" +@include "npc/011-1/_import.txt" +@include "npc/011-2/_import.txt" +@include "npc/011-3/_import.txt" +@include "npc/012-1/_import.txt" +@include "npc/012-2/_import.txt" +@include "npc/012-3/_import.txt" +@include "npc/012-4/_import.txt" +@include "npc/012-5/_import.txt" +@include "npc/012-6/_import.txt" +@include "npc/012-7/_import.txt" +@include "npc/012-8/_import.txt" +@include "npc/013-1/_import.txt" +@include "npc/014-1/_import.txt" +@include "npc/014-2-1/_import.txt" +@include "npc/014-2-2/_import.txt" +@include "npc/014-2/_import.txt" +@include "npc/014-3/_import.txt" +@include "npc/014-4/_import.txt" +@include "npc/014-5-1/_import.txt" +@include "npc/014-5/_import.txt" +@include "npc/015-1/_import.txt" +@include "npc/015-2/_import.txt" +@include "npc/015-3-1/_import.txt" +@include "npc/015-3-2/_import.txt" +@include "npc/015-3-3/_import.txt" +@include "npc/015-3/_import.txt" +@include "npc/015-4/_import.txt" +@include "npc/015-5/_import.txt" +@include "npc/015-6/_import.txt" +@include "npc/015-7/_import.txt" +@include "npc/015-8-1/_import.txt" +@include "npc/015-8/_import.txt" +@include "npc/016-1/_import.txt" +@include "npc/016-6/_import.txt" +@include "npc/016-7/_import.txt" +@include "npc/017-0/_import.txt" +@include "npc/017-1/_import.txt" +@include "npc/017-10/_import.txt" +@include "npc/017-2-1/_import.txt" +@include "npc/017-2-2/_import.txt" +@include "npc/017-2/_import.txt" +@include "npc/017-3/_import.txt" +@include "npc/017-4/_import.txt" +@include "npc/017-5/_import.txt" +@include "npc/017-6/_import.txt" +@include "npc/017-7/_import.txt" +@include "npc/017-8/_import.txt" +@include "npc/018-1-1/_import.txt" +@include "npc/018-1/_import.txt" +@include "npc/018-2-1/_import.txt" +@include "npc/018-2-2/_import.txt" +@include "npc/018-2-3/_import.txt" +@include "npc/018-2-4/_import.txt" +@include "npc/018-2-5/_import.txt" +@include "npc/018-2-6/_import.txt" +@include "npc/018-2-7/_import.txt" +@include "npc/018-2-8/_import.txt" +@include "npc/018-2/_import.txt" +@include "npc/018-3/_import.txt" +@include "npc/018-4-1/_import.txt" +@include "npc/018-4-2/_import.txt" +@include "npc/018-4/_import.txt" +@include "npc/018-5-0/_import.txt" +@include "npc/018-5-1/_import.txt" +@include "npc/018-5-2/_import.txt" +@include "npc/018-5-3/_import.txt" +@include "npc/018-5-4/_import.txt" +@include "npc/018-5-5/_import.txt" +@include "npc/018-5-boss/_import.txt" +@include "npc/018-5/_import.txt" +@include "npc/018-6-0/_import.txt" +@include "npc/018-6-1/_import.txt" +@include "npc/018-6-2/_import.txt" +@include "npc/018-6-3/_import.txt" +@include "npc/018-7-1/_import.txt" +@include "npc/018-7/_import.txt" +@include "npc/019-1-1/_import.txt" +@include "npc/019-1/_import.txt" +@include "npc/019-2/_import.txt" +@include "npc/019-3/_import.txt" +@include "npc/019-4-1/_import.txt" +@include "npc/019-4/_import.txt" +@include "npc/019-5-1/_import.txt" +@include "npc/019-5-2/_import.txt" +@include "npc/019-5-3/_import.txt" +@include "npc/019-5/_import.txt" +@include "npc/019-6/_import.txt" +@include "npc/020-1/_import.txt" +@include "npc/020-2/_import.txt" +@include "npc/020-3/_import.txt" +@include "npc/020-4/_import.txt" +@include "npc/020-5/_import.txt" +@include "npc/020-6/_import.txt" +@include "npc/020-7-1/_import.txt" +@include "npc/020-7-2/_import.txt" +@include "npc/020-7/_import.txt" +@include "npc/021-0/_import.txt" +@include "npc/021-1/_import.txt" +@include "npc/021-2/_import.txt" +@include "npc/021-3/_import.txt" +@include "npc/021-4/_import.txt" +@include "npc/022-1/_import.txt" +@include "npc/023-1/_import.txt" +@include "npc/023-2/_import.txt" +@include "npc/023-3-1/_import.txt" +@include "npc/023-3-2/_import.txt" +@include "npc/023-3/_import.txt" +@include "npc/023-4/_import.txt" +@include "npc/024-1/_import.txt" +@include "npc/024-10/_import.txt" +@include "npc/024-11/_import.txt" +@include "npc/024-12/_import.txt" +@include "npc/024-13/_import.txt" +@include "npc/024-14/_import.txt" +@include "npc/024-15/_import.txt" +@include "npc/024-16/_import.txt" +@include "npc/024-2/_import.txt" +@include "npc/024-3/_import.txt" +@include "npc/024-4/_import.txt" +@include "npc/024-5/_import.txt" +@include "npc/024-6/_import.txt" +@include "npc/024-7/_import.txt" +@include "npc/024-8/_import.txt" +@include "npc/024-9/_import.txt" +@include "npc/025-1/_import.txt" +@include "npc/025-2-1/_import.txt" +@include "npc/025-2-2/_import.txt" +@include "npc/025-2-3/_import.txt" +@include "npc/025-2-4/_import.txt" +@include "npc/025-2/_import.txt" +@include "npc/025-3/_import.txt" +@include "npc/026-0/_import.txt" +@include "npc/026-1/_import.txt" +@include "npc/026-2/_import.txt" +@include "npc/027-0/_import.txt" +@include "npc/027-1/_import.txt" +@include "npc/027-2/_import.txt" +@include "npc/027-3/_import.txt" +@include "npc/027-4/_import.txt" +@include "npc/027-5/_import.txt" +@include "npc/027-6/_import.txt" +@include "npc/027-7/_import.txt" +@include "npc/029-0/_import.txt" +@include "npc/029-1/_import.txt" +@include "npc/029-2/_import.txt" +@include "npc/029-3/_import.txt" +@include "npc/029-4/_import.txt" +@include "npc/029-5/_import.txt" +@include "npc/029-6/_import.txt" +@include "npc/029-7/_import.txt" +@include "npc/029-8/_import.txt" +@include "npc/029-9/_import.txt" +@include "npc/030-01/_import.txt" +@include "npc/030-02/_import.txt" +@include "npc/030-03/_import.txt" +@include "npc/030-04/_import.txt" +@include "npc/030-05/_import.txt" +@include "npc/030-06/_import.txt" +@include "npc/030-07/_import.txt" +@include "npc/030-08/_import.txt" +@include "npc/030-09/_import.txt" +@include "npc/030-10/_import.txt" +@include "npc/030-11/_import.txt" +@include "npc/030-12/_import.txt" +@include "npc/030-13/_import.txt" +@include "npc/030-14/_import.txt" +@include "npc/030-15/_import.txt" +@include "npc/030-16/_import.txt" +@include "npc/030-17/_import.txt" +@include "npc/030-18/_import.txt" +@include "npc/030-19/_import.txt" +@include "npc/030-20/_import.txt" +@include "npc/030-21/_import.txt" +@include "npc/030-22/_import.txt" +@include "npc/030-23/_import.txt" +@include "npc/030-24/_import.txt" +@include "npc/030-25/_import.txt" +@include "npc/030-26/_import.txt" +@include "npc/030-27/_import.txt" +@include "npc/030-28/_import.txt" +@include "npc/030-29/_import.txt" +@include "npc/030-30/_import.txt" +@include "npc/030-31/_import.txt" +@include "npc/030-32/_import.txt" +@include "npc/030-33/_import.txt" +@include "npc/030-34/_import.txt" +@include "npc/030-35/_import.txt" +@include "npc/030-36/_import.txt" +@include "npc/030-37/_import.txt" +@include "npc/030-38/_import.txt" +@include "npc/030-39/_import.txt" +@include "npc/030-40/_import.txt" +@include "npc/030-41/_import.txt" +@include "npc/030-42/_import.txt" +@include "npc/030-43/_import.txt" +@include "npc/030-44/_import.txt" +@include "npc/030-45/_import.txt" +@include "npc/030-46/_import.txt" +@include "npc/030-47/_import.txt" +@include "npc/030-48/_import.txt" +@include "npc/030-49/_import.txt" +@include "npc/030-50/_import.txt" +@include "npc/030-51/_import.txt" +@include "npc/030-52/_import.txt" +@include "npc/030-53/_import.txt" +@include "npc/030-54/_import.txt" +@include "npc/030-55/_import.txt" +@include "npc/030-56/_import.txt" +@include "npc/030-57/_import.txt" +@include "npc/030-58/_import.txt" +@include "npc/030-59/_import.txt" +@include "npc/030-60/_import.txt" +@include "npc/030-61/_import.txt" +@include "npc/030-62/_import.txt" +@include "npc/030-63/_import.txt" +@include "npc/030-64/_import.txt" +@include "npc/030-65/_import.txt" +@include "npc/030-66/_import.txt" +@include "npc/030-67/_import.txt" +@include "npc/030-68/_import.txt" +@include "npc/030-69/_import.txt" +@include "npc/030-70/_import.txt" +@include "npc/030-71/_import.txt" +@include "npc/030-72/_import.txt" +@include "npc/030-73/_import.txt" +@include "npc/030-74/_import.txt" +@include "npc/030-75/_import.txt" +@include "npc/031-0/_import.txt" +@include "npc/031-1/_import.txt" +@include "npc/031-2/_import.txt" +@include "npc/031-3/_import.txt" +@include "npc/031-4/_import.txt" +@include "npc/031-5/_import.txt" +@include "npc/031-6/_import.txt" +@include "npc/031-7/_import.txt" +@include "npc/031-8/_import.txt" +@include "npc/031-9/_import.txt" +@include "npc/032-1/_import.txt" +@include "npc/032-2/_import.txt" +@include "npc/032-3/_import.txt" +@include "npc/032-4/_import.txt" +@include "npc/032-5/_import.txt" +@include "npc/033-1/_import.txt" +@include "npc/033-2/_import.txt" +@include "npc/033-3/_import.txt" +@include "npc/033-4/_import.txt" +@include "npc/033-5/_import.txt" +@include "npc/034-1/_import.txt" +@include "npc/034-2/_import.txt" +@include "npc/034-3/_import.txt" +@include "npc/034-4/_import.txt" +@include "npc/042-0/_import.txt" +@include "npc/042-1/_import.txt" +@include "npc/042-10/_import.txt" +@include "npc/042-11/_import.txt" +@include "npc/042-2/_import.txt" +@include "npc/042-3/_import.txt" +@include "npc/042-4/_import.txt" +@include "npc/042-5/_import.txt" +@include "npc/042-6/_import.txt" +@include "npc/042-7/_import.txt" +@include "npc/042-8/_import.txt" +@include "npc/042-9/_import.txt" +@include "npc/boss/_import.txt" +@include "npc/botcheck/_import.txt" +@include "npc/guilds/_import.txt" +@include "npc/sec_pri/_import.txt" +@include "npc/soren-2/_import.txt" +@include "npc/soren/_import.txt" +@include "npc/test/_import.txt" +@include "npc/testbg/_import.txt" diff --git a/npc/boss/_import.txt b/npc/boss/_import.txt new file mode 100644 index 0000000..60544f5 --- /dev/null +++ b/npc/boss/_import.txt @@ -0,0 +1,4 @@ +// Map boss: Boss Arena +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/boss/manamarket.txt", +"npc/boss/throne.txt", diff --git a/npc/boss/manamarket.txt b/npc/boss/manamarket.txt new file mode 100644 index 0000000..c3c5bc2 --- /dev/null +++ b/npc/boss/manamarket.txt @@ -0,0 +1,107 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// ManaMarket sketch + +boss,41,41,0 script ManaMarket NPC_TEDDYGIRL,{ + function MMCooldown; + function MMBuy; + function MMBuyMenu; + + if (!is_staff()) end; + mesn; + mesq l("Hello! How can I help you?"); + if (MM_DELAY > gettimetick(2)) close; + next; + select + l("Buy"), + l("Sell"), + l("Nothing"); + mes ""; + if (@menu == 1) + MMBuy(); + close; + + +// Set the cooldown value to the same as interserver value +function MMCooldown { + MM_DELAY=gettimetick(2)+300; + return; +} + +// MMBuy(page=0) +function MMBuy { + .@p=getarg(0,0); + .@v=MMBuyMenu(.@p); + + // Special results + switch (.@v) { + case -1: + return; + case -2: + // FIXME + MMBuy(.@p+1); + break; + default: + break; + } + + .@it=$@MM_nameid[.@v]; + // Can't buy stuff you already have + if (countitem(.@it)) { + mesn; + mesq l("You already have this."); + return; + } + + + // Report + mesn; + mesq l("Purchase %02d %s for %d GP?", + $@MM_amount[.@v], getitemlink(.@it), $@MM_price[.@v]); + next; + if (askyesno() == ASK_YES) { + // TODO: Check if still in stock + mesn; + mesq l("Sorry. The arrays can't have zeros."); + // getitem2 + } + return; +} + +// MMBuyMenu ( page=0 ) +function MMBuyMenu { + deletearray @mm_menu$; + setarray @mm_menu$, l("Cancel"), "-1"; + .@pg=getarg(0, 0); + .@limit=min(getarraysize($@MM_id), (.@pg+1)*20); + + // Prepare the information array + for (.@i=.@pg*20; .@i < .@limit; .@i++) { + //@mm_menu$+=getitemname($@MM_nameid[.@i])+":"; + array_push(@mm_menu$, getitemname($@MM_nameid[.@i])); + array_push(@mm_menu$, str(.@i)); + } + + // Still more pages + if (.@limit < getarraysize($@MM_id)) { + array_push(@mm_menu$, "Next Page >>"); + array_push(@mm_menu$, "-2"); + } + + // Handle input + menuint2(@mm_menu$); + deletearray @mm_menu$; + return @menuret; +} + +OnInit: + .distance=6; + .sex = G_FEMALE; + + // Load ManaMarket (max 100 entries) + .@nb = query_sql("SELECT `id`, `account_id`, `price`, `expire_time`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `opt_idx0`, `opt_val0`, `opt_idx1`, `opt_val1`, `opt_idx2`, `opt_val2`, `opt_idx3`, `opt_val3`, `opt_idx4`, `opt_val4` FROM `manamarket` ORDER BY `id` DESC LIMIT 100", $@MM_id, $@MM_account_id, $@MM_price, $@MM_expire_time, $@MM_nameid, $@MM_amount, $@MM_equip, $@MM_identify, $@MM_refine, $@MM_attribute, $@MM_card0, $@MM_card1, $@MM_card2, $@MM_card3, $@MM_opt_idx0, $@MM_opt_val0, $@MM_opt_idx1, $@MM_opt_val1, $@MM_opt_idx2, $@MM_opt_val2, $@MM_opt_idx3, $@MM_opt_val3, $@MM_opt_idx4, $@MM_opt_val4); + end; +} + diff --git a/npc/boss/throne.txt b/npc/boss/throne.txt new file mode 100644 index 0000000..8bf51d0 --- /dev/null +++ b/npc/boss/throne.txt @@ -0,0 +1,162 @@ +// TMW2 Scripts. +// Author: +// Jesusalva +// Description: +// Monster King Throne ($@MK_CHALLENGE) + +boss,45,45,0 script #monsterthrone NPC_HIDDEN,0,0,{ + end; + +OnTouch: + if (strcharinfo(2) == "Monster King") end; + warp "boss", 45, 48; + percentheal -15,0; + dispbottom "The throne is cursed, only the Monster King may seat on it."; + end; + +// Controls the Event +OnBegin: + .CYCLES=0; + $@MK_CHALLENGE=true; + .MK=monster("boss", 45, 45, "The Monster King", MonsterKing, 1, .name$+"::OnVictory"); + .@bhp=getunitdata(.MK, UDT_MAXHP); + setunitdata(.MK, UDT_MAXHP, .@bhp+2000*.FAILS); + setunitdata(.MK, UDT_HP, .@bhp+2000*.FAILS); + + // Spawn reinforcements + .@mobId=MonsterLieutenant; + .@ts$="Lieutenant"; + monster("boss", 40, 40, strmobinfo(1, .@mobId), .@mobId, 1); + monster("boss", 50, 50, strmobinfo(1, .@mobId), .@mobId, 1); + monster("boss", 40, 50, strmobinfo(1, .@mobId), .@mobId, 1); + monster("boss", 50, 40, strmobinfo(1, .@mobId), .@mobId, 1); + + mapannounce("boss", "Begin!", bc_map|bc_npc); + initnpctimer; + end; + +function CheckFinalAssault { + if (!siege_calcdiff("boss", 5)) { + kamibroadcast("You noobs, you all deserve to die!", "Monster King"); + stopnpctimer; + $@MK_CHALLENGE=false; + // Clean Up + mapwarp("boss", "017-1", 120, 88); + killmonsterall("boss"); + // Raise difficulty + .FAILS+=1; + // Halt execution + end; + } + return; +} + +OnTimer120000: + .CYCLES+=1; + //areamonster("boss", 20, 20, 70, 70, "Monster King Slave", ); + // Spawn several monsters on the Boss Room every 2 minutes + siege_cast("boss", .name$, .FAILS, TP_TULIM|TP_HURNS|TP_NIVAL); + // Spawn an extra mini-boss at minutes: 10 and 30 + if (.CYCLES == 5) { + .@mobId=MonsterColonel; + .@ts$="Colonel"; + monster("boss", 45, 44, strmobinfo(1, .@mobId), .@mobId, 1); + } + if (.CYCLES == 15) { + .@mobId=MonsterGeneral; + .@ts$="General"; + monster("boss", 45, 44, strmobinfo(1, .@mobId), .@mobId, 1); + } + initnpctimer; +OnTimer15000: + // Each fail raise curse duration in 0.1s - chance of curse is 15% each 15s + if (rand2(100) <= 15) { + areasc2("boss", 45, 45, 25, 3000+(.FAILS*100), SC_CURSE, BL_PC | BL_HOM | BL_MER); + //globalmes("MSG"); + unittalk(.MK, "Be cursed, you fools! I am the mighty Monster King!!"); + } +OnTimer5000: +OnTimer10000: +OnTimer20000: +OnTimer25000: +OnTimer30000: +OnTimer35000: +OnTimer40000: +OnTimer45000: +OnTimer50000: +OnTimer55000: +OnTimer60000: +OnTimer65000: +OnTimer70000: +OnTimer75000: +OnTimer80000: +OnTimer85000: +OnTimer90000: +OnTimer95000: +OnTimer100000: +OnTimer105000: +OnTimer110000: +OnTimer115000: + CheckFinalAssault(); + end; + +// Monster King was defeated - game won +OnVictory: + // Not killed by a player? It doesn't counts, then + if (!playerattached()) + end; + $@MK_CHALLENGE=false; + kamibroadcast("has just defeated the Monster King.", strcharinfo(0)); + stopnpctimer; + mapwarp("boss", "017-1", 120, 88); + $GAME_STORYLINE=5; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + // Without the Monster King to rule monsters... TODO Isbamuth + setbattleflag("monster_ai", 0x209); + setbattleflag("monster_active_enable", false); + setbattleflag("mob_count_rate", 25); + //charcommand("@reloadbattleconf"); // Careful! + donpcevent("@exprate::OnReload"); + donpcevent("@droprate::OnReload"); + // Player Reward + /*getitembound(AegisShield, 1, 1); + dispbottom l("For defeating the Monster King, you've got the Legendary @@.", getitemlink(AegisShield)); + dispbottom l("This item cannot be traded normally and is a Legendary Item."); + dispbottom l("You can transfer it with \"@grantpower\" command. Please contact a GM for more info."); + dispbottom l("Protip: If you plan in selling it, it's adviseable to ask for GM mediation."); */ + end; +} + +// Room Traps, only against players +boss,0,0,0 script #MKBossTrap01 NPC_TRAP,0,0,{ + end; + +OnTouch: + SteelTrap(rand2(10, 20)); + end; + +OnTimer10000: + stopnpctimer; + setnpctimer 0; + setnpcdisplay .name$, NPC_TRAP; + //end; + // Move the trap away after it disarms + +OnMinute14: +OnMinute26: +OnMinute40: +OnMinute54: +OnInit: + .@x=rand2(20,70); + .@y=rand2(20,70); + movenpc .name$, .@x, .@y; + end; +} + +// Create more traps. (They can be on walls) +boss,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap02 NPC_TRAP,0,0 +boss,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap03 NPC_TRAP,0,0 +boss,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap04 NPC_TRAP,0,0 +boss,0,0,0 duplicate(#MKBossTrap01) #MKBossTrap05 NPC_TRAP,0,0 + + diff --git a/npc/botcheck/_import.txt b/npc/botcheck/_import.txt new file mode 100644 index 0000000..e6f4365 --- /dev/null +++ b/npc/botcheck/_import.txt @@ -0,0 +1,3 @@ +// Map botcheck: Botcheck Area +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/botcheck/botcheck_blackbox.txt", diff --git a/npc/commands/debug-look.txt b/npc/commands/debug-look.txt new file mode 100644 index 0000000..26e9fff --- /dev/null +++ b/npc/commands/debug-look.txt @@ -0,0 +1,93 @@ +// Evol Script +// Authors: Gumi, Monwarez, Jesusalva + +function script BarberDebug { + + function setGender { + clear; + setnpcdialogtitle l("Appearance Debug - Sex Change"); + @menuret=99; + /* + mes l("Please choose the desired gender:"); + next; + menuint + l("Male"), G_MALE, + l("Female"), G_FEMALE, + l("Legacy"), 99; + */ + + if (@menuret == 99) { + closedialog; + changecharsex; + } else { + //setparam(Sex, @menuret); + //query_sql("UPDATE `char` SET `Sex`="+@menuret+" WHERE `char_id`="+getcharid(0)); + return; + } + } + + function setStyle { + clear; + setnpcdialogtitle l("Appearance Debug - Barber"); + mes l("Hair style") + ": " + getlook(LOOK_HAIR); + next; + mes l("Please enter the desired style") + " (1-255)"; + input .@h, 1, 0xFF; + setlook LOOK_HAIR, max(1, min(0xFF, .@h)); + return; + } + function setColor { + clear; + setnpcdialogtitle l("Appearance Debug - Barber"); + mes l("Hair color") + ": " + getlook(LOOK_HAIR_COLOR); + next; + mes l("Please enter the desired color") + " (0-255)"; + input .@h, 0, 0xFF; + setlook LOOK_HAIR_COLOR, max(0, min(0xFF, .@h)); + return; + } + function setRace { + clear; + setnpcdialogtitle l("Appearance Debug - Race"); + mes l("Race") + ": " + Class; + next; + mes l("Please enter the desired race") + " (0-32767)"; + input .@r, 0, 0x7FFF; + jobchange max(0, min(0x7FFF, .@r)); + return; + } + + do + { + clear; + setnpcdialogtitle l("Appearance Debug"); + mes l("This menu allows you to customize your appearance."); + mes ""; + + mes "---"; + mes l("Gender") + ": " + Sex; + mes l("Hair style") + ": " + getlook(LOOK_HAIR); + mes l("Hair color") + ": " + getlook(LOOK_HAIR_COLOR); + mes l("Race") + ": " + Class; + mes "---"; + + next; + mes l("What do you want to change?"); + select + menuimage("actions/edit", l("Gender") + " [" + l("Requires logout") + "]"), + menuimage("actions/edit", l("Hair style")), + menuimage("actions/edit", l("Hair color")), + menuimage("actions/edit", l("Race")), + rif(getarg(0,0), menuimage("actions/back", l("Return to Debug menu"))); + + switch (@menu) + { + case 1: setGender; break; + case 2: setStyle; break; + case 3: setColor; break; + case 4: setRace; break; + case 5: return; + } + } while (1); +} + diff --git a/npc/commands/debug-quest.txt b/npc/commands/debug-quest.txt new file mode 100644 index 0000000..1171087 --- /dev/null +++ b/npc/commands/debug-quest.txt @@ -0,0 +1,113 @@ +// Evol Script +// Author: Gumi, Jesusalva + +// TODO: This script must be auto-generated from db/quests.conf to be of any use +function script GlobalQuestDebug { + do + { + clear; + setnpcdialogtitle l("Quest debug") + " - " + l("Other"); + mes l("This menu gives access to quest debug menus for @@ quests.", strtolower(l("Other"))); + next; + mes l("Please select a quest:"); + + menuint + menuimage("actions/back", l("Go back")), -1, + l("Custom"), -136; + + switch (@menuret) + { + case -1: return; + case -136: + mes "Determine the quest number, as stated in db/quests.conf"; + input .@quest; + if (!.@quest) return; + mes ""; + mes l("DEBUG: Changing @@, Values: (@@, @@, @@).", getquestlink(.@quest), getq(.@quest), getq2(.@quest), getq3(.@quest)); + select + "set 1", + "set 2", + "set 3", + "reset"; + mes l("DEBUG: Changing @@ field @@ to something else.", getquestlink(.@quest), @menu); + mes ""; + mes "Determine the new value (numeric only)"; + input .@value; + if (.@value < 0) return; + if (@menu == 1) + setq1 .@quest, .@value; + if (@menu == 2) + setq2 .@quest, .@value; + if (@menu == 3) + setq3 .@quest, .@value; + if (@menu == 4) + setq .@quest, .@value, 0, 0; + + return; + default: return; + } + } while (1); + return; +} + + +// TODO FIXME: Include OnGetq (@getq) +- script @qdebug 32767,{ + end; + +OnCall: + if (!is_gm()) { + end; + } + GlobalQuestDebug; + closedialog; + end; + +OnSetq: + if (.@atcmd_numparameters < 2) { + dispbottom "setq called with invalid arguments (min. 2)"; + dispbottom "GM Command syntax: @setq <quest_id> <val1> <val2> <val3>"; + end; + } + .@q=atoi(.@atcmd_parameters$[0]); + switch (.@atcmd_numparameters) { + case 4: + setq3 .@q, atoi(.@atcmd_parameters$[3]); + case 3: + setq2 .@q, atoi(.@atcmd_parameters$[2]); + case 2: + setq1 .@q, atoi(.@atcmd_parameters$[1]); + dispbottom l("Quest @@ modified by GM", getquestlink(.@q)); + specialeffect 50, SELF, playerattached(); + break; + default: + dispbottom "setq called with invalid arguments (max. 4)"; + dispbottom "GM Command syntax: @setq <quest_id> <val1> <val2> <val3>"; + break; + } + end; + +OnGetq: + if (.@atcmd_numparameters != 1) { + dispbottom "getq called with invalid arguments."; + dispbottom "Message is sent as a console notice."; + dispbottom "GM Command syntax: @getq <quest_id>"; + end; + } + .@q=atoi(.@atcmd_parameters$[0]); + consoleinfo("Information for Quest [%d/%d]: Q1 %d | Q2 %d | Q3 %d | QT %d", + .@q, getcharid(0), getq(.@q), getq2(.@q), getq3(.@q), getqtime(.@q)); + end; + +OnCharData: + consoleinfo("Information for %s: Char %d Party %d Guild %d Account %d BG %d Clan %d", + strcharinfo(0), getcharid(0), getcharid(1), getcharid(2), getcharid(3), getcharid(4), getcharid(5)); + end; + +OnInit: + bindatcmd "qdebug", "@qdebug::OnCall", 99, 99, 1; + bindatcmd "setq", "@qdebug::OnSetq", 99, 99, 1; + bindatcmd "getq", "@qdebug::OnGetq", 99, 99, 0; + bindatcmd "chardata", "@qdebug::OnCharData", 80, 80, 0; + end; +} diff --git a/npc/commands/debug.txt b/npc/commands/debug.txt new file mode 100644 index 0000000..586e980 --- /dev/null +++ b/npc/commands/debug.txt @@ -0,0 +1,156 @@ +// Evol Script +// Author: Gumi, Monwarez + +function script GlobalDebugMenu { + function resetAll { + function doReset { + resetstatus; + resetskill; + resetlvl 1; + dispbottom b("ALL PERMANENT STATUS BOOSTS WERE ALSO RESET"); + dispbottom l("Reset done!"); + if (getarg(0,0) == 3) { + closedialog; + doevent "::OnGlobalQuestReset"; // executes in all quest npcs // FIXME: maybe have a `resetquest` buildin? + Zeny = 0; + BankVault = 0; + clearitem; + warp "000-0", 0, 0; // starting point + end; // script must end for doevent to execute + } + return; + } + clear; + setnpcdialogtitle l("Debug - Reset"); + mes l("What do you want to reset?"); + select + l("Abort"), + l("Reset stats, skills, level"), + l("Reset EVERYTHING"), + rif(is_admin() && debug && !@allperms, l("Set All Perms")), + l("Return to Debug menu"); + + switch (@menu) { + case 2: + case 3: doReset @menu; return; + case 4: + if (!debug) atcommand("@ban 1h "+strcharinfo(0)); + if (!is_admin()) atcommand("@ban 2h "+strcharinfo(0)); + atcommand("@addperm all_skill"); + atcommand("@addperm all_equipment"); + atcommand("@addperm skill_unconditional"); + atcommand("@addperm join_chat"); + atcommand("@addperm hide_session"); + atcommand("@addperm any_warp"); + atcommand("@addperm view_hpmeter"); + atcommand("@addperm view_equipment"); + atcommand("@addperm receive_requests"); + atcommand("@addperm can_trade_bound"); + atcommand("@addperm bypass_nostorage"); + @allperms=true; + break; + } + + return; + } + + do + { + clear; + setnpcdialogtitle l("Debug"); + mes l("This menu allows you to modify your account data."); + mes ""; + mes l("What do you want to do?"); + select + l("Change my appearance"), + l("Change my quests"), + l("Reset"), + rif(getarg(0,0), l("Return to Super Menu")); + + .@c = getarg(0,0) ? 2 : 1; + + switch (@menu) + { + case 1: BarberDebug .@c; break; + case 4: GlobalQuestDebug .@c; break; + case 5: resetAll; break; + case 6: return; + } + } while(1); +} + + + +- script @debug 32767,{ + end; + +OnCall: + if (!is_admin()) { + end; + } + GlobalDebugMenu(); + closedialog; + end; + +OnSetVar: + if (getarraysize(.@atcmd_parameters$) != 3) + Exception("Usage: @set-var VARIABLE INDEX VALUE", RB_DISPBOTTOM|RB_ISFATAL); + + .@cmd$=array_shift(.@atcmd_parameters$); + .@idx=atoi(array_shift(.@atcmd_parameters$)); + if (charat(.@atcmd_parameters$[0], + getstrlen(.@atcmd_parameters$[0])-1) == "$") + .@str=true; + + if (.@str) + .@val$=array_shift(.@atcmd_parameters$); + else + .@val=array_shift(.@atcmd_parameters$); + + if (.@str) + setd(sprintf("%s[%d]", .@cmd$, .@idx), .@val$); + else + setd(sprintf("%s[%d]", .@cmd$, .@idx), .@val); + + .@msg$=sprintf("%s[%d] is now: %s", .@cmd$, .@idx, + getd(sprintf("%s[%d]", .@cmd$, .@idx))); + + if (!is_staff()) + atcommand("@request System Information: "+.@msg$); + else + dispbottom(.@msg$); + end; + +// If the char is not a staff member, it'll be sent to GM Log instead +OnGetVar: + if (getarraysize(.@atcmd_parameters$) != 2) + Exception("Usage: @get-var VARIABLE INDEX", RB_DISPBOTTOM|RB_ISFATAL); + + .@cmd$=array_shift(.@atcmd_parameters$); + .@idx=atoi(array_shift(.@atcmd_parameters$)); + + .@mg$=sprintf("%s[%d] == %s", .@cmd$, .@idx, + getd(sprintf("%s[%d]", .@cmd$, .@idx))); + + if (!is_staff()) + atcommand("@request System Information: "+.@mg$); + else + dispbottom(.@mg$); + end; + +OnSClear: + sc_end SC_ALL; + sc_end SC_DAILYSENDMAILCNT; + dispbottom l("Status Condition Cleared"); + end; + +OnInit: + bindatcmd "debug", "@debug::OnCall", 99, 99, 1; + bindatcmd "getvar", "@debug::OnGetVar", 80, 99, 1; + bindatcmd "get-var", "@debug::OnGetVar", 80, 99, 1; + bindatcmd "setvar", "@debug::OnSetVar", 99, 99, 1; + bindatcmd "set-var", "@debug::OnSetVar", 99, 99, 1; + bindatcmd "sclear", "@debug::OnSClear", 99, 99, 1; + end; +} + diff --git a/npc/commands/discord.txt b/npc/commands/discord.txt new file mode 100644 index 0000000..a5773af --- /dev/null +++ b/npc/commands/discord.txt @@ -0,0 +1,156 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// LawnCable +// Notes: +// Controls `discord` table with @discord command. +// Only useful for TMW2-Discord integration. + +- script @discord 32767,{ + end; + +OnCall: + // Anti-Flood System + if (@discord) { + mesc l("You already ran this command today. Please try again at a later time."), 1; + close; + } + + // Live server only + if (debug || $@GM_OVERRIDE) { + dispbottom l("This command cannot be used on test servers."); + end; + } + + // Bot cannot (or should not) alter staff data + if (is_staff() && !is_admin()) { + dispbottom l("Staff is not allowed to use this command."); + end; + } + + // Minimum account requeriments + if (#REG_DATE < (gettimetick(2)+259200) && BaseLevel < 15) { + dispbottom l("Your account must be at least 72 hours old or have level 15+ to use this command."); + end; + } + + // Use this instead of min acc req if desired + //if (!validatepin()) + // close; + + // Prevent reusing the command on same session + @discord=1; + .@link=true; + + // Search on cache + .@key$=str(getcharid(3)); + .@discord$ = htget(.discmem, .@key$, "Not found"); + + if (.@discord$ == "Not found") { + // Only do SQL query if not in cache + .@nb = query_sql("select `discord_name` from `discord` WHERE `account_id`='"+getcharid(3)+"' limit 1", .@discord$); + // Override default behavior + if (.@discord$ == "" || .@discord$ == "Not found") { + .@discord$="Not Linked"; + .@link=false; + } + // Add to Cache + htput(.discmem, str(getcharid(3)), .@discord$); + } + + do + { + mesn "Lawn Cable"; + mesq l("Current linked Discord account: @@", .@discord$); + next; + select + rif(DISCTRL < gettimeparam(GETTIME_DAYOFMONTH), l("Change Linked Discord Account")), + rif(.@link, l("Disconnect")), + l("Quit"); + + switch (@menu) { + case 1: + if (DISCTRL >= gettimeparam(GETTIME_DAYOFMONTH)) + atcommand("@ban 7d "+strcharinfo(0)); + + mesc l("Please insert your Discord ID, on the following format: "), 1; + mesc l("Username#0000"), 2; + input .@discord$; + if (.@discord$ == "") close; + if (strtolower(.@discord$) == "username#0000") { break; } + .@i = explode(.@d$, .@discord$, "#"); + if (.@i != 2) { mesc l("Invalid Discord ID."); next; break; } + if (getstrlen(.@d$[1]) != 4) { mesc l("Invalid Discord ID."); next; break; } + mes ""; + clear; + mesc l("Linking the following Discord account:"); + mesc .@discord$, 1; + mesc l("Is this correct?"); + if (askyesno() == ASK_NO) close; + + // Run SQL query (will halt execution on dupe) + if (.@link) { + if ($@HAS_API) { + apiasync("SQL", sprintf("UPDATE `discord` SET `discord_name` = '?1', `verified` = '0', `discord_id` = '?2' WHERE `account_id`='%d'", getcharid(3))); + apiasync("SAD1", .@discord$); + apiasync("DISCORDID2", .@discord$); + apiasync("SQLRUN", ""); + } else { + query_sql(sprintf("UPDATE `discord` SET `discord_name` = '%s', `verified` = '0', `discord_id` = '' WHERE `account_id`='%d'", + escape_sql(.@discord$), getcharid(3))); + } + } else { + if ($@HAS_API) { + apiasync("SQL", sprintf("INSERT INTO `discord` (`discord_name`, `verified`, `discord_id`, `account_id`) VALUES ('?1', '0', '?2', '%d')", getcharid(3))); + apiasync("SAD1", .@discord$); + apiasync("DISCORDID2", .@discord$); + apiasync("SQLRUN", ""); + } else { + query_sql(sprintf("INSERT INTO `discord` (`discord_name`, `verified`, `discord_id`, `account_id`) VALUES ('%s', '0', '', '%d')", + escape_sql(.@discord$), getcharid(3))); + } + } + + // Encode JSON data + .@p$=json_encode("name", strcharinfo(0), + "accId", getcharid(3), + "disc", escape_sql(.@discord$)); + + // Send to API and update cache + api_send(API_DISCORD, .@p$); + htput(.discmem, str(getcharid(3)), .@discord$); + consoleinfo("%s linked discord account \"%s\".", strcharinfo(0), .@discord$); + + // Prevent changing for the next 3 days + DISCTRL=gettimeparam(GETTIME_DAYOFMONTH)+1; + mesc l("Linking requested."), 1; + mesc l("This setting can only be changed every %d days.", 1), 1; + break; + ///////////////////////////////////////////////////////////////////// + case 2: + if ($@HAS_API) { + apiasync("SQL", sprintf("DELETE FROM `discord` WHERE `account_id`='%d'", getcharid(3))); + apiasync("SQLRUN", ""); + } else { + query_sql("DELETE FROM `discord` WHERE `account_id`='"+getcharid(3)+"'"); + // Prevent setting a new linking right away + DISCTRL=gettimeparam(GETTIME_DAYOFMONTH)+1; + } + .@discord$=""; + // Update Cache + htput(.discmem, str(getcharid(3)), ""); + // TODO: Remove Adventurer role? + logmes(sprintf("User %d \"%s\" unlinked Discord account!", getcharid(3), strcharinfo(0))); + consoleinfo("%s removed Discord account.", strcharinfo(0)); + break; + } + + } while (@menu != 3); + close; + +OnInit: + bindatcmd "discord", "@discord::OnCall", 0, 0, 1; + .discmem = htnew; + end; +} + diff --git a/npc/commands/event.txt b/npc/commands/event.txt new file mode 100644 index 0000000..8b1dc02 --- /dev/null +++ b/npc/commands/event.txt @@ -0,0 +1,544 @@ +// TMW2 Script +// Author: +// Evol Team +// Saulc +// Jesusalva +// Description: +// Handles all major events on TMW2 (Christmas, Easter, Worker Day, etc.) + +// WARNING, possibly dangerous. +function script sClear { + // Obviously CI does not need this + if ($@CI_MODE) + end; + + // Delete all Easter Stuff + if ($EVENT$ != "Easter") { + DelItemFromEveryPlayer(SilverEasteregg); + DelItemFromEveryPlayer(GoldenEasteregg); + } else { + .@nb = query_sql("SELECT c.name, i.amount FROM `inventory` AS i, `char` AS c WHERE i.nameid=834 AND i.char_id=c.char_id ORDER BY i.amount DESC LIMIT 10", $@easter_name$, $@easter_value); + setnpcdisplay("Aurora", "Lilica#final", NPC_EASTER); + delmonsterdrop(Forain, DarkEggshellHat); + } + + // Delete all Christmas stuff + if ($EVENT$ != "Christmas") { + $XMAS_MONEY=0; // Golbarez + $XMAS_GIFTS=0; // Chief + DelItemFromEveryPlayer(ClosedChristmasBox); + DelItemFromEveryPlayer(OpenedChristmasBox); + } else if ($EVENT$ == "Christmas") { + DelAccRegFromEveryPlayer("#XMAS_LOGINBONUS"); + removemapmask "003-1", MASK_CHRISTMAS; + removemapmask "005-1", MASK_CHRISTMAS; + removemapmask "009-1", MASK_CHRISTMAS; + removemapmask "012-1", MASK_CHRISTMAS; + removemapmask "017-2", MASK_CHRISTMAS; + removemapmask "017-2-1", MASK_CHRISTMAS; + removemapmask "017-3", MASK_CHRISTMAS; + removemapmask "020-2", MASK_CHRISTMAS; + } + + // Delete all Valentine Day stuff + if ($EVENT$ != "Valentine") { + // ItemDB prefers account variables than quest log + DelAccRegFromEveryPlayer("#VALENTINE_POINTS"); + DelAccRegFromEveryPlayer("#VALENTINE_RECEIVED"); + DelAccRegFromEveryPlayer("#VALENTINE_OPENED"); + DelAccRegFromEveryPlayer("#VALENTINE_SENT"); + DelAccRegFromEveryPlayer("#VALENTINE_EQUIPMENT2020"); + // Remove event items + DelItemFromEveryPlayer(LoveLetter); + DelItemFromEveryPlayer(BoxOfChocolates); + } else if ($EVENT$ == "Valentine") { + htdelete $@VALENTINE_GIFTSTACKS; + htdelete $@VALENTINE_LOVELETTER; + $@VALENTINE_GIFTSTACKS=0; + $@VALENTINE_LOVELETTER=0; + donpcevent "Aurora::OnValentine"; + } + + // Longer EXP rate controls + if ($EVENT$ == "Anniversary") { + $BCONFB_EXPR-=50; + kamibroadcast("EXP Rate changed from %d%% to %d%%", $BCONFB_EXPR+50, $BCONFB_EXPR); + } + + // Nullify Regnum Blessing + if ($EVENT$ == "Regnum") { + // Remove the blessing + setmapflag($REGNUM_BLESSMAP$, mf_bexp, 100); + $REGNUM_BLESSMAP_H$=""; + $REGNUM_BLESSMAP$=""; + kamibroadcast("Regnum's Blessing is over."); + } + + // Hand Aurora Event Rewards + callfunc("FYRewardEvent"); + $WORLDEXPO_ENEMY$=""; + deletearray $FYREWARD_PT; + deletearray $FYREWARD_ID; + deletearray $FYREWARD_AM; + $FYLOGIN_PTS=0; + + // Simpler events + $PATRICK_DAYCTRL=0; + $PATRICK_DAYMAX=0; + DelAccRegFromEveryPlayer("#PATRICK_DAY"); + DelAccRegFromEveryPlayer("#PATRICK_CTRL"); + DelAccRegFromEveryPlayer("#TMW2_LOGINBONUS"); + DelAccRegFromEveryPlayer("#THANKS_DAY"); + DelAccRegFromEveryPlayer("#THANKS_STREAK"); + + // Not so simple but still needs cleanup. + DelItemFromEveryPlayer(Event1HSword); + DelItemFromEveryPlayer(Event2HSword); + DelItemFromEveryPlayer(EventBow); + DelItemFromEveryPlayer(EventWand); + + // We must remove any event drop + charcommand("@reloadmobdb"); + SeasonReload(1); + return; + } + +function script GlobalEventMenu { + + function resetCandor { + $@FEFE_CAVE_LEVEL=0; + $@FEFE_DELAY=0; + $@FEFE_DIFFICULTY=0; + $@FEFE_CAVE_HERO$=""; + mesc "Candor Fight Reset!"; + return; + } + + // :> + function superSpawn { + mesc l("Monster ID, 0 to cancel"); + input .@mobid; + if (.@mobid <= 0) + return; + //.@tmp=strmobinfo(4, .@mobid); + .@tmp=getmonsterinfo(.@mobid, MOB_MAXHP); + mesc l("Monster HP, Default %d, use 0 to cancel", .@tmp); + input .@newhp; + if (.@newhp <= 0) + return; + + .@tmp=(getmonsterinfo(.@mobid, MOB_ATK1)+getmonsterinfo(.@mobid, MOB_ATK2))/2; + mesc l("Monster ATK (varies 10%%), Default %d, use 0 to cancel", .@tmp); + input .@newak; + if (.@newak <= 0) + return; + .@minatk=.@newak*9/10; + .@maxatk=.@newak*11/10; + + .@tmp=getmonsterinfo(.@mobid, MOB_DEF); + mesc l("Monster DEF, Default %d, use 0 to cancel", .@tmp); + input .@newdf; + if (.@newdf <= 0) + return; + + .@tmp=getmonsterinfo(.@mobid, MOB_DEX); + mesc l("Monster DEX, Default %d, use 0 to cancel", .@tmp); + input .@newdx; + if (.@newdx <= 0) + return; + + .@tmp=getmonsterinfo(.@mobid, MOB_AGI); + mesc l("Monster AGI, Default %d, use 0 to cancel", .@tmp); + input .@newag; + if (.@newag <= 0) + return; + + .@tmp=getmonsterinfo(.@mobid, MOB_RANGE); + mesc l("Monster ATK RANGE, Default %d, use 0 to cancel", .@tmp); + input .@newrg; + if (.@newrg <= 0) + return; + + mesc l("Monster SPEED, Player speed is 150, minimum is 100!"); + input .@newspd; + if (.@newspd < 100) + return; + + .@tmp$=strmobinfo(1, .@mobid); + mesc l("Monster NAME, Default %s, empty to cancel", .@tmp$); + input .@name$; + if (.@name$ == "") + return; + + // Spawn + getmapxy(.@m$, .@x, .@y, 0); + .@mob=monster(.@m$, .@x, .@y, .@name$, .@mobid, 1); + + // Reconfigure + setunitdata(.@mob, UDT_MAXHP, .@newhp); + setunitdata(.@mob, UDT_HP, .@newhp); + setunitdata(.@mob, UDT_SPEED, .@newspd); + setunitdata(.@mob, UDT_DEX, .@newdx); + setunitdata(.@mob, UDT_AGI, .@newag); + setunitdata(.@mob, UDT_ATKRANGE, .@newrg); + setunitdata(.@mob, UDT_ATKMIN, .@minatk); + setunitdata(.@mob, UDT_ATKMAX, .@maxatk); + setunitdata(.@mob, UDT_DEF, .@newdf); + + logmes "Super spawn!", LOGMES_ATCOMMAND; + + do + { + select + l("Good!"), // 1 + l("Customize attack delay!"), // 2 + l("Customize hit rate!"), // 3 + l("Customize evasion!"), // 4 + l("Modify monster level!"), // 5 + rif($EVENT$ == "Valentine", l("Send them to Valentine Island!")), + l("Show me a report"), // 7 + l("OMG CANCEL IT!!"); // 8 + mes ""; + switch (@menu) { + case 2: + .@tmp=getunitdata(.@mob, UDT_ADELAY); + mesc l("Default: %d, use 0 to cancel", .@tmp); + input .@new; + if (.@new > 0) + setunitdata(.@mob, UDT_ADELAY, .@new); + break; + case 3: + .@tmp=getunitdata(.@mob, UDT_HIT); + mesc l("Default: %d, use 0 to cancel", .@tmp); + input .@new; + if (.@new > 0) + setunitdata(.@mob, UDT_HIT, .@new); + break; + case 4: + .@tmp=getunitdata(.@mob, UDT_FLEE); + mesc l("Default: %d, use 0 to cancel", .@tmp); + input .@new; + if (.@new > 0) + setunitdata(.@mob, UDT_FLEE, .@new); + break; + case 5: + .@tmp=getunitdata(.@mob, UDT_LEVEL); + mesc l("Default: %d, use 0 to cancel", .@tmp); + mesc l("WARNING: Might have unexpected side effects!"), 1; + input .@new; + if (.@new > 0) + setunitdata(.@mob, UDT_LEVEL, .@new); + break; + case 6: + unitwarp(.@mob, "001-12", 132, 119); + break; + case 7: + mesc l("Too lazy."); + next; + break; + case 8: + mesc l("Are you sure?!"), 1; + next; + if (askyesno() == ASK_YES) { + logmes "Super spawn aborted!", LOGMES_ATCOMMAND; + unitkill(.@mob); + return; + } + break; + } + + } while (@menu != 1); + + // Make it a boss + .@md=getunitdata(.@mob, UDT_MODE); + setunitdata(.@mob, UDT_MODE, .@md|MD_BOSS); + logmes "Super spawn completed! (GID "+.@mob+")", LOGMES_ATCOMMAND; + return; + } + + + + + + + + + + + + + + + + + + + + + function seasonManagement { + clear; + mes l("Current event: @@", $EVENT$); + select + ("Disable event"), + ("Enable Valentine Day"), + ("Enable St. Patrick Day"), + ("Enable Easter"), + ("Enable Worker's Day"), + ("Enable Thanksgiving"), + ("Enable Event Horizon"), + ("Enable Christmas"), + ("Enable Demure's Birthday"), + ("[FY] Enable Kamelot"), + ("[FY] Enable Regnum Blessing"), + ("[FY] Enable Candor Season"), + ("Reset Kill Saulc Event (Monthly)"); + + switch (@menu) { + case 1: .@r=playerattached(); sClear(); set $EVENT$, ""; attachrid(.@r); logmes "Disabled events.", LOGMES_ATCOMMAND; + break; + case 2: + set $EVENT$, "Valentine"; $@VALENTINE_LOVELETTER = htnew(); $@VALENTINE_GIFTSTACKS = htnew(); logmes "Enabled VALENTINE DAY event.", LOGMES_ATCOMMAND; + break; + case 3: + $EVENT$="Patrick"; + $@PATRICK_DAYMAX=31; + enablenpc "sPatrick"; + setnpcdisplay("Aurora", "Patrick Saulc", NPC_WEIRDGREEN); + enablenpc "St. Patrick Gold Pot"; + donpcevent "St. Patrick Gold Pot::OnForcedMove"; + logmes "Enabled ST. PATRICK DAY event (until day 31).", LOGMES_ATCOMMAND; + break; + case 4: sEaster(); break; + case 5: + set $EVENT$, "Worker"; + logmes "Enabled WORKERS DAY event.", LOGMES_ATCOMMAND; + /* + addmonsterdrop(Snake, Pearl, 10); + debugmes "Snakes are now dropping Pearls."; + */ + break; + case 6: + $EVENT$="Thanksgiving"; + logmes "Enabled THANKS GIVING event.", LOGMES_ATCOMMAND; + break; + case 7: + $EVENT$="Event"; + logmes "Enabled EVENT (HORIZON) event.", LOGMES_ATCOMMAND; + break; + case 8: + logmes "Enabled CHRISTMAS event.", LOGMES_ATCOMMAND; + DelQuestFromEveryPlayer(SQuest_Christmas); + sChristmas(); break; + case 9: + $EVENT$=any("Demure Birthday", "Blame Saulc"); + dispbottom("Maybe in future this increases everyone attack speed? Well, for now,"); + dispbottom("This is not actually an event, it just lower prices at Lua GM shop."); + break; + case 10: + $EVENT$="Kamelot"; + logmes "Enabled KAMELOT event.", LOGMES_ATCOMMAND; + break; + case 11: + $EVENT$="Regnum"; callfunc("FYEConf_Regnum"); + dispbottom l("Blessing applied at: %s", $REGNUM_BLESSMAP_H$); + logmes "Enabled REGNUM event.", LOGMES_ATCOMMAND; + break; + case 12: + $EVENT$="Candor"; + logmes "Enabled CANDOR event.", LOGMES_ATCOMMAND; + break; + case 13: DelItemFromEveryPlayer(MurdererCrown); break; + } + + return; + } + + do + { + clear; + setnpcdialogtitle l("Event Management"); + mes l("This menu allows you to manage events and gives access to event-related tools."); + mes ""; + mes l("What do you want to access?"); + + select + l("Reset Crazyfefe Fight"), + rif(is_master(), l("Change Season Event")), + rif(is_gm(), l("Spawn customized boss")), + rif(getarg(0,0), menuimage("actions/home", l("Return to Super Menu"))), + l("Close"); + + //.@c = getarg(0,0) ? 2 : 1; // 1 = back to event menu, 2 = back to super menu + + switch (@menu) { + case 1: resetCandor(); break; + case 2: seasonManagement(); break; + case 3: superSpawn(); break; + case 5: close; break; + default: return; + } + + } while (true); +} + + + +- script @event 32767,{ + end; + +OnCall: + if (!is_gm()) { + end; + } + + GlobalEventMenu(); + closedialog; + end; +} + +function script CMD_toevent { + if (!@toeventchk) { + @toeventval1=readparam(Hp); + @toeventval2=readparam(Sp); + @toeventchk=1; + specialeffect FX_CIRCLE, SELF, getcharid(3); + addtimer 4000, "@toevent::OnEffect"; + } + return; +} + +- script @toevent 32767,{ + end; + +OnCall: + CMD_toevent(); + end; + +OnEffect: + @toeventchk=0; + removespecialeffect(FX_CIRCLE, SELF, getcharid(3)); + // Calculate + if (BaseLevel < 10) { + dispbottom l("You are not strong enough to survive this trip."); + atcommand "@refresh"; + end; + } else if (readparam(Sp) < @toeventval2) { + dispbottom l("You must not be using mana to do this trip."); + atcommand "@refresh"; + end; + } else if (readparam(Hp) < @toeventval1) { + dispbottom l("You cannot be fighting to do this trip."); + atcommand "@refresh"; + end; + } else if (compare(getmapname(), "001-") || (getmapinfo(MAPINFO_ZONE, getmap())) == "MMO") { + dispbottom l("You are already at the Mana Plane of Existence."); + atcommand "@refresh"; + end; + } else if (getmapname() == "boss" || getmapname() == "sec_pri" || compare(getmapname(), "000-") || compare(getmapname(), "008-") || compare(getmapname(), "sore")) { + dispbottom l("The Mana Plane is currently out of reach."); + atcommand "@refresh"; + end; + } else { + if ($@MK_SCENE == MK_SIEGE_TULIM || BaseLevel > 20) { + // Monster King events take precedence over Aeros Event + switch ($@MK_SCENE) { + case MK_SIEGE_TULIM: + warp "003-1", 40, 49; + specialeffect(63, AREA, getcharid(3)); + end; + case MK_SIEGE_HALIN: + warp "009-1", 27, 30; + specialeffect(63, AREA, getcharid(3)); + end; + case MK_SIEGE_HURNS: + warp "012-1", 87, 70; + specialeffect(63, AREA, getcharid(3)); + end; + case MK_SIEGE_NIVAL: + warp "020-1", 57, 62; + specialeffect(63, AREA, getcharid(3)); + end; + } + } + + // Aeros Events takes precedence over player events + if ($@GM_EVENT) + { + .@gt=$@AEROS_SPWN; + if (.@gt == 2) + .@gt=rand(0,1); + switch (.@gt) { + case 0: + warp "001-1", 235, 26; break; + case 1: + warp "001-1", 23, 108; break; + } + specialeffect(63, AREA, getcharid(3)); + end; + } + + // Player events takes precedence over permanent events + // They also have an hierarchy + // 1. Candor Battle + if ($@FEFE_DELAY > gettimetick(2) && !$@FEFE_CAVE_LEVEL && $@FEFE_CAVE_HERO$ != "" && !$@FEFE_WAVE) { + warp "006-1", 49, 53; + message strcharinfo(0), l("You are now at Candor Battle Cave at @@'s request.", $@FEFE_CAVE_HERO$); + end; + } + + // Events are the least priority + if ($EVENT$ == "Valentine") { + warp "001-11", 38, 32; + message strcharinfo(0), l("You are now at the Valentine Highlands."); + end; + } + if ($EVENT$ == "Easter") { + warp "001-4", 151, 157; + message strcharinfo(0), l("You are now at the Magical Forest."); + end; + } + if ($EVENT$ == "Worker") { + warp "001-5", 22, 79; + message strcharinfo(0), l("You are now at the Contributor's Cave."); + end; + } + if ($EVENT$ == "Christmas" && BaseLevel >= 20) { + warp "019-4-1", 32, 36; + message strcharinfo(0), l("You are now at the Christmas Workshop."); + end; + } + if ($EVENT$ == "Tower" && countitem(EventDreamTicket)) { + doevent "sDreamTower::OnWarpTo"; + end; + } + if ($EVENT$ == "Olympics") { + if (callfunc("FYE_Olympics_TO")) { + dispbottom l("You are now at Porthos - The Town of Portals."); + end; + } + // Failed for some reason - ignore + } + if ($EVENT$ == "Raid") { + getmapxy(@aurora_map$, @aurora_x, @aurora_y, 0); + callfunc("FYRaid_Select"); + close; + } + } + + // Block here + if (!$@GM_EVENT && !$@MK_SCENE && $EVENT$ == "") { + atcommand "@refresh"; + dispbottom l("The mana bridge is closed at the moment."); + end; + } + + // Seems like it was a Blame Saulc-like event... + dispbottom l("Have a nice @@ day!", $EVENT$); + dispbottom l("The mana bridge is closed at the moment."); + atcommand "@refresh"; + end; + +OnInit: + bindatcmd "event", "@event::OnCall", 80, 99, 0; + bindatcmd "toevent", "@toevent::OnCall", 0, 99, 0; +} diff --git a/npc/commands/exp.txt b/npc/commands/exp.txt new file mode 100644 index 0000000..a8d3492 --- /dev/null +++ b/npc/commands/exp.txt @@ -0,0 +1,35 @@ +// TMW2 Script +// Author: Jesusalva + +// @getexp atcommand +// Gets experience +// +// group lv: 5 +// group char lv: 99 +// log: True + +- script @getexp 32767,{ + end; + +OnCall: + .@delta$ = .@atcmd_parameters$[0]; + .@d = atoi(.@delta$); + + getexp .@d, 0; + end; + +OnHomun: + if (!gethominfo(0)) { + dispbottom l("No homunculus found!"); + end; + } + .@delta$ = .@atcmd_parameters$[0]; + .@d = atoi(.@delta$); + + gethomunexp .@d; + end; + +OnInit: + bindatcmd "getexp", "@getexp::OnCall", 5, 99, 1; + bindatcmd "gethexp", "@getexp::OnHomun", 5, 99, 1; +} diff --git a/npc/commands/grantpower.txt b/npc/commands/grantpower.txt new file mode 100644 index 0000000..fa36799 --- /dev/null +++ b/npc/commands/grantpower.txt @@ -0,0 +1,185 @@ +// TMW2 Script +// +// @grantpower <username> +// Grants a legendary weapon to <username>. Cannot be undone. +// Only way to bypass restrictions on legendary weapons. + +- script @grantpower 32767,{ + end; + +OnCall: + .@request$ = ""; + .@request$ += implode(.@atcmd_parameters$, " "); + + // Player is not attached + .@id = getcharid(3, .@request$); + if (!.@id) { + Exception("Player not found.", RB_ISFATAL|RB_DISPBOTTOM); + } + mes ".:: " + l("Grant Power") + " ::."; + mesc l("You're about to transfer a legendary weapon to @@.", .@request$), 1; + mesc l("This action CANNOT BE UNDONE."), 1; + mes l("Are you sure?"); + if (askyesno() == ASK_NO) + close; + mes ""; + mes ".:: " + l("Grant Power") + " ::."; + mes l("Please select the weapon to transfer."); + mes ""; + mesc l("* Options will be removed after transfer is done."); + mesc l("* Cards will be deleted after transfer is done."); + mesc l("* Refine will be lost after transfer is done."); + if (countitem(Lightbringer)) + mesc l("* Lightbringer is self-aware and cannot be given."), 1; + mes ""; + menuint + l("None"), 0, + rif(countitem(DemureAxe), l("Demure's Axe")), DemureAxe, + rif(countitem(Tyranny), l("Tyranny")), Tyranny, + rif(countitem(Runestaff), l("Runestaff")), Runestaff, + rif(countitem(AegisShield), l("Aegis Shield")), AegisShield, + l("Abort"), 0; + mes ""; + .@ori=getcharid(3); + .@ite=@menuret; + if (!.@ite) + close; + + // Transfer the weapon + if (attachrid(.@id)) { + getitembound .@ite, 1, 1; // Account bound or char bound? (1 or 4) + dispbottom l("You received the @@ from @@.", getitemlink(.@ite), strcharinfo(0, "someone", .@ori)); + consoleinfo("%s is the new owner for the %s.", strcharinfo(0), getitemname(.@ite)); + detachrid(); + attachrid(.@ori); + delitem .@ite, 1; + switch (.@ite) { + case DemureAxe: + $DEMUR_HOLDER$ = .@request$; break; + case Tyranny: + $TYRAN_HOLDER$ = .@request$; break; + case Runestaff: + $RUNES_HOLDER$ = .@request$; break; + case AegisShield: + $AEGIS_HOLDER$ = .@request$; break; + default: + Exception("Invalid legendary item "+str(.@ite), + RB_DEFAULT|RB_IRCBROADCAST); break; + } + } else { + Exception("Player not found.", RB_ISFATAL|RB_SPEECH); + } + + close; + +OnInit: + bindatcmd "grantpower", "@grantpower::OnCall", 0, 100, 1; + end; + +// Legendary Controls: Interact over legendary holders and determine their status +function legendaryAPIWarning { + .@aid=getarg(0); + .@nb = query_sql("SELECT email FROM `login` WHERE `account_id` == "+.@aid+" LIMIT 1", .@email$); + .@msg$=sprintf("[\"%s\", \"Good evening!\nYou have been inactive for a week on Moubootaur Legends.\n\nYou are currently possessing a Legendary Weapon.\nIf you do not login within seven days, your legendary weapon will be returned so the player community can obtain it again.\n\nYour TMW2 Team\", \"Legendary item expiration notice\"]", .@email$); + debugmes .@msg$; + consoleinfo("%d notified for Legendary weapon inactivity. (L:2)", .@aid); + api_send(API_SENDMAIL, .@msg$); + return; +} + +function legendaryRodexWarning { + .@cid=getarg(0); + rodex_sendmail(.@cid, "Legendary Weapon", "Inactivity Warning", "You have not logged in the past 2 days. Shall you fail to login for 15 days, the weapon will be destroyed!"); + consoleinfo("%d notified for Legendary weapon inactivity. (L:1)", .@cid); + return; +} + +OnClock0238: + .@date=gettimetick(2); + .@warn=.@date-(86400*2); + .@kick=.@date-(86400*7); + .@kban=.@date-(86400*15); + ////////////////////////////////////////// + if ($LIGHT_HOLDER$ != "") { + .@nb = query_sql("SELECT last_login, char_id, account_id FROM `char` WHERE `name` == "+$LIGHT_HOLDER$+" LIMIT 1", .@login, .@cid, .@aid); + if (.@login < .@kban) { + // Destroy the item by force + DelItemFromEveryPlayer(Lightbringer); + $LIGHT_HOLDER$=""; + kamibroadcast("The Lightbringer has given up on its previous owner and is now available for someone worthier."); + } else if (.@login < .@kick) { + // Send warning via API + legendaryAPIWarning(.@aid); + } else if (.@login < .@warn) { + // Send warning ingame + legendaryRodexWarning(.@cid); + } + } + ////////////////////////////////////////// + if ($DEMUR_HOLDER$ != "") { + .@nb = query_sql("SELECT last_login, char_id, account_id FROM `char` WHERE `name` == "+$DEMUR_HOLDER$+" LIMIT 1", .@login, .@cid, .@aid); + if (.@login < .@kban) { + // Destroy the item by force + DelItemFromEveryPlayer(DemureAxe); + $DEMUR_HOLDER$=""; + kamibroadcast("The Demure Axe has given up on its previous owner and is now available for someone worthier."); + } else if (.@login < .@kick) { + // Send warning via API + legendaryAPIWarning(.@aid); + } else if (.@login < .@warn) { + // Send warning ingame + legendaryRodexWarning(.@cid); + } + } + ////////////////////////////////////////// + if ($TYRAN_HOLDER$ != "") { + .@nb = query_sql("SELECT last_login, char_id, account_id FROM `char` WHERE `name` == "+$TYRAN_HOLDER$+" LIMIT 1", .@login, .@cid, .@aid); + if (.@login < .@kban) { + // Destroy the item by force + DelItemFromEveryPlayer(Tyranny); + $TYRAN_HOLDER$=""; + kamibroadcast("The Tyranny has given up on its previous owner and is now available for someone worthier."); + } else if (.@login < .@kick) { + // Send warning via API + legendaryAPIWarning(.@aid); + } else if (.@login < .@warn) { + // Send warning ingame + legendaryRodexWarning(.@cid); + } + } + ////////////////////////////////////////// + if ($RUNES_HOLDER$ != "") { + .@nb = query_sql("SELECT last_login, char_id, account_id FROM `char` WHERE `name` == "+$RUNES_HOLDER$+" LIMIT 1", .@login, .@cid, .@aid); + if (.@login < .@kban) { + // Destroy the item by force + DelItemFromEveryPlayer(Runestaff); + $RUNES_HOLDER$=""; + kamibroadcast("The Runestaff has given up on its previous owner and is now available for someone worthier."); + } else if (.@login < .@kick) { + // Send warning via API + legendaryAPIWarning(.@aid); + } else if (.@login < .@warn) { + // Send warning ingame + legendaryRodexWarning(.@cid); + } + } + ////////////////////////////////////////// + if ($AEGIS_HOLDER$ != "") { + .@nb = query_sql("SELECT last_login, char_id, account_id FROM `char` WHERE `name` == "+$AEGIS_HOLDER$+" LIMIT 1", .@login, .@cid, .@aid); + if (.@login < .@kban) { + // Destroy the item by force + DelItemFromEveryPlayer(AegisShield); + $AEGIS_HOLDER$=""; + kamibroadcast("The Aegis Shield has given up on its previous owner and is now available for someone worthier."); + } else if (.@login < .@kick) { + // Send warning via API + legendaryAPIWarning(.@aid); + } else if (.@login < .@warn) { + // Send warning ingame + legendaryRodexWarning(.@cid); + } + } + ////////////////////////////////////////// + end; +} + diff --git a/npc/commands/ipcheck.txt b/npc/commands/ipcheck.txt new file mode 100644 index 0000000..f24acf0 --- /dev/null +++ b/npc/commands/ipcheck.txt @@ -0,0 +1,72 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// @ipcheck <player_name> +// #ipcheck <player_name> +// +// Returns user IP + + +- script @ipcheck 32767,{ + end; + +OnCall: + if (.@atcmd_numparameters == 0) + .@request$ = strcharinfo(0); + else + .@request$ = implode(.@atcmd_parameters$, " "); + dispbottom strip(.@request$)+": IP "+getcharip(.@request$); + //dispbottom strcharinfo(0)+": IP "+getcharip(.@request$); + end; + +OnBan: + if (.@atcmd_numparameters == 0) { + dispbottom col("Syntax: #ipban <reason>", 1); + } + // Do not allow banning staff + if (is_staff()) + end; + .@target$=strcharinfo(0); + .@reason$ = implode(.@atcmd_parameters$, " "); + dispbottom col(l("You were permanently banned by the GM Team."), 1); + sleep2(200); + query_sql "INSERT INTO ipbanlist (list,btime,rtime,reason) VALUES ('"+getcharip(.@target$)+"','"+gettime(7)+"-"+gettime(6)+"-"+gettime(5)+" "+gettime(3)+":"+gettime(2)+":"+gettime(1)+"','2030-01-01 00:00:00','"+.@reason$+"')"; + logmes("was IP-Blocked, and will never connect again."), LOGMES_ATCOMMAND; + consoleinfo("%s was IP-Banned from the server. (R: %s)", .@target$, .@reason$); + sleep2(2000); + charcommand("@kick "+.@target$); + end; + +OnInit: + bindatcmd "ipcheck", "@ipcheck::OnCall", 60, 100, 0; + bindatcmd "ipban", "@ipcheck::OnBan", 99, 100, 1; + end; +} + +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// @checkidle <player_name> +// #checkidle <player_name> +// +// Returns user idle time in seconds. +// Useful when the game prohibits warping to player. + + +- script @checkidle 32767,{ + end; + +OnCall: + if (.@atcmd_numparameters == 0) + .@request$ = strcharinfo(0); + else + .@request$ = implode(.@atcmd_parameters$, " "); + dispbottom strip(.@request$)+" idle time: "+checkidle(.@request$); + //dispbottom strcharinfo(0)+": IP "+getcharip(.@request$); + end; + +OnInit: + bindatcmd "checkidle", "@checkidle::OnCall", 60, 80, 0; + end; +} + diff --git a/npc/commands/kami.txt b/npc/commands/kami.txt new file mode 100644 index 0000000..4c8c6c9 --- /dev/null +++ b/npc/commands/kami.txt @@ -0,0 +1,113 @@ +// TMW2 Script +// +// @k <message> +// Broadcast, and broadcast to #world too +// +// @servmsg <message> +// Experimental, uses servicemessage() - requires up to date server + +- script @k 32767,{ + end; + +OnCall: + .@request$ = strcharinfo(0)+": "; + .@request$ += implode(.@atcmd_parameters$, " "); + channelmes("#world", .@request$); + announce l(.@request$), bc_all|bc_npc; + end; + +OnServMsg: + .@request$ = strcharinfo(0)+": "; + .@request$ += implode(.@atcmd_parameters$, " "); + // This can be slow, beware + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + message(.@players[.@i], .@request$); + } + end; + +OnBuff: + .@n$=strtoupper(strcharinfo(0, "JESUSALVA", playerattached())); + // Disabled command, used for debug purposes + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + sc_start SC_INCMHPRATE, 300000, 100; + sc_start SC_INCMSPRATE, 300000, 100; + sc_start SC_INCFLEERATE, 300000, 100; + sc_start SC_INCHITRATE, 300000, 100; + sc_start SC_WALKSPEED, 300000, 150; + sc_start SC_ATTHASTE_POTION3, 300000, 50; + percentheal 100, 100; + dispbottom l("YOU WERE BLESSED BY %s", .@n$); + dispbottom l("YOU CAN FEEL THE POWER FLOWING TROUGH YOU."); + detachrid(); + } + end; + +OnInstDestroy: + .@request = implode(.@atcmd_parameters$, " "); + if (.@request != 0) + instance_destroy(.@request); + end; + +OnInstCheck: + .@request$ = implode(.@atcmd_parameters$, " "); + dispbottom has_instance2(.@request$); + end; + +OnPurify: + getmapxy(.@m$, .@x, .@y, 0); + .@r=60; + .@b=BL_PET; + + .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); + for (.@i = 0; .@i < .@c; .@i++) { + specialeffect(FX_LIGHTNING, AREA, .@mbs[.@i]); + unitwarp(.@mbs[.@i], "boss", 25, 25); + } + end; + +OnHarm: + harm(getcharid(3), 500, HARM_PHYS); + end; + +OnHarm2: + .@id=getcharid(3); + detachrid(); + harm(.@id, 500, HARM_MISC, Ele_Holy); + end; + +OnInit: + bindatcmd "k", "@k::OnCall", 60, 80, 1; + bindatcmd "servmsg", "@k::OnServMsg", 80, 99, 1; + + bindatcmd "blessing", "@k::OnBuff", 80, 99, 1; + bindatcmd "harm", "@k::OnHarm", 80, 80, 1; + bindatcmd "cruelty", "@k::OnHarm2", 99, 99, 1; + + bindatcmd "instcheck", "@k::OnInstCheck", 99, 100, 1; + bindatcmd "instdestr", "@k::OnInstDestroy", 99, 100, 1; + bindatcmd "burnlivio", "@k::OnPurify", 99, 100, 1; + end; +} + +// kamibroadcast( message{, sender} ) +function script kamibroadcast { + .@msg$=getarg(0); + .@snd$=getarg(1, ""); + + // Send to #world + if (.@snd$ == "") + channelmes("#world", .@msg$); + else + channelmes("#world", "[ "+.@snd$+" ] : "+.@msg$); + + // Make an announce + if (.@snd$ == "") + announce .@msg$, bc_all|bc_npc; + else + announce .@snd$+" : "+.@msg$, bc_all|bc_npc; + + return; +} diff --git a/npc/commands/language.txt b/npc/commands/language.txt new file mode 100644 index 0000000..ce7122f --- /dev/null +++ b/npc/commands/language.txt @@ -0,0 +1,60 @@ +// TMW2 Script +// Author: Jesusalva +// With code parts from Julia (Evol) + +// @lang atcommand +// Changes Language +// +// group lv: 0 +// group char lv: 0 +// log: False +// +// usage: +// @lang +// + +function script CMD_lang { + callfunc "checkclientversion"; + mesq l("Which language do you speak?"); + next; + asklanguage(LANG_IN_SHIP); + mes ""; + mesn; + mesq l("Ok, done."); + return; +} + +- script @lang 32767,{ + end; + +OnCall: + CMD_lang(); + close; + +OnTranslate: + // Implode, using a slash at whitespaces + .@request$ = implode(.@atcmd_parameters$, "%2F"); + // No NPC provided? + if (.@request$ == "") { + dispbottom l("Usage: @translate <npc file>"); + dispbottom l("Example: @translate Nard"); + dispbottom l("Example: @translate Elmo"); + dispbottom l("Example: @translate npc/002-1/arpan"); + dispbottom l("PS. Doesn't always work. You need an account at %s and to be at ManaPlus Team.", "@@https://www.transifex.com/arctic-games|Transifex@@"); + end; + } + // Add .txt extension of needed + if (!compare(.@request$, ".txt")) + .@request$ += ".txt"; + // Fix stuff for URL format + .@request$ = replacestr(.@request$, "/", "%2F"); + .@request$ = strtolower(.@request$); + // Give your translation link + dispbottom "@@https://www.transifex.com/arctic-games/moubootaur-legends/translate/#"+languagecode()+"/serverdata?q=occurrence%3A"+.@request$+"|Translate with Transifex@@"; + close; + +OnInit: + bindatcmd "lang", "@lang::OnCall", 0, 60, 0; + bindatcmd "translate", "@lang::OnTranslate", 0, 60, 0; + end; +} diff --git a/npc/commands/mobinfo.txt b/npc/commands/mobinfo.txt new file mode 100644 index 0000000..6853491 --- /dev/null +++ b/npc/commands/mobinfo.txt @@ -0,0 +1,22 @@ +// TMW2 Script +// +// @monsterinfo <monsterAegis> +// Sends @mobinfo with a delay (moved from atcommand.conf) +// +- script @monsterinfo 32767,{ + end; + +// @monsterinfo uses the same delayer as RSync +OnCall: + if (@rsync_delay > gettimetick(2)) { + dispbottom l("Not doing that to prevent flood."); + end; + } + atcommand("@mobinfo " + implode(.@atcmd_parameters$, " ")); + @rsync_delay=gettimetick(2)+rand(2,3); + end; + +OnInit: + bindatcmd "monsterinfo", "@monsterinfo::OnCall", 0, 80, 0; + end; +} diff --git a/npc/commands/motd.txt b/npc/commands/motd.txt new file mode 100644 index 0000000..dcdb014 --- /dev/null +++ b/npc/commands/motd.txt @@ -0,0 +1,194 @@ +// TMW2 Script +function script displayMOTD { + .@size = getvariableofnpc(.size, "@motd"); + + // generic MOTD + for (.@i = 0; .@i < .@size; ++.@i) { + dispbottom $MOTD_Messages$[.@i]; + } + + return; +} + +function script MOTDConfig { + + function toggleMOTD { + $MOTD_Disabled = !($MOTD_Disabled); + logmes "MOTD modified: toogled: enable/disable", LOGMES_ATCOMMAND; + } + + function addNewLine { + clear; + mes l("Please enter the new line."); + input .@s$; + .@s$ = strip(.@s$); + if (.@s$ != "") { + .@size = getvariableofnpc(.size, "@motd"); + $MOTD_Messages$[.@size] = .@s$; + set getvariableofnpc(.size, "@motd"), getarraysize($MOTD_Messages$); + logmes "MOTD modified: line added", LOGMES_ATCOMMAND; + } + } + + function modifyLine { + + function removeLine { + .@l = getarg(0); + deletearray $MOTD_Messages$[.@l], 1; // remove and shift + mes l("Line @@ has been removed.", .@l); + set getvariableofnpc(.size, "@motd"), getarraysize($MOTD_Messages$); + logmes "MOTD modified: line removed", LOGMES_ATCOMMAND; + } + + function moveUp { + .@l = getarg(0); + .@top$ = $MOTD_Messages$[.@l - 1]; + $MOTD_Messages$[.@l - 1] = $MOTD_Messages$[.@l]; + $MOTD_Messages$[.@l] = .@top$; + } + + function moveDown { + .@l = getarg(0); + .@bottom$ = $MOTD_Messages$[.@l + 1]; + $MOTD_Messages$[.@l + 1] = $MOTD_Messages$[.@l]; + $MOTD_Messages$[.@l] = .@bottom$; + } + + function editLine { + .@l = getarg(0); + clear; + mes l("Old line:"); + mes "---"; + mes $MOTD_Messages$[.@l]; + mes "---"; + mes ""; + mes l("Enter new line:"); + next; + input .@s$; + .@s$ = strip(.@s$); + if (.@s$ != "") { + $MOTD_Messages$[.@l] = .@s$; + logmes "MOTD modified: line edited", LOGMES_ATCOMMAND; + } + } + + .@max = (getarg(0) - 1); + + do + { + mes l("Enter line number:"); + next; + input .@n; + if ($MOTD_Messages$[.@n] != "") { + clear; + mes l("line @@: "+"##0"+$MOTD_Messages$[.@n], .@n); + next; + select + menuimage("actions/back", l("Modify another line")), + menuimage("actions/edit", l("Modify this line")), + menuimage("actions/remove", l("Remove this line")), + rif(.@n > 0, menuimage("actions/raise", l("Move this line up"))), + rif(.@n < .@max, menuimage("actions/lower", l("Move this line down"))), + menuimage("actions/home", l("Return to main menu")); + + switch (@menu) { + case 2: editLine .@n; return; + case 3: removeLine .@n; return; + case 4: moveUp .@n; return; + case 5: moveDown .@n; return; + case 6: return; + } + } + } while (1); + } + + do + { + clear; + setnpcdialogtitle l("MOTD Config"); + mes l("This menu allows you to modify the generic message that is sent to players when they log in."); + mes ""; + + mes "---"; + .@size = getvariableofnpc(.size, "@motd"); + for (.@i = 0; .@i < .@size; ++.@i) { + mes l("line @@: "+"##0"+$MOTD_Messages$[.@i], .@i); + } + if (.@size == 0) { + mes "(" + l("no active MOTD") + ")"; + } + mes "---"; + .@d = $MOTD_Disabled; + mes l("Enabled: @@", (.@d ? l("no") : l("yes"))); + next; + + select + menuimage("actions/toggle", (.@d ? l("Enable") : l("Disable"))), + menuimage("actions/add", l("Add a new line")), + rif(.@size, menuimage("actions/manage", l("Modify, move, or remove a line"))), + rif(.@size, menuimage("actions/test", l("Test MOTD"))), + rif(getarg(0,0), menuimage("actions/home", l("Return to Super Menu"))), + rif(!getarg(0,0), menuimage("actions/home", l("Close"))); + + switch (@menu) { + case 1: toggleMOTD; break; + case 2: addNewLine; break; + case 3: modifyLine .@size; break; + case 4: displayMOTD; break; + case 6: close; break; + default: return; + } + } while (1); +} + + + +- script @motd 32767,{ + end; + +OnCall: + if (!is_master()) { + end; + } + + MOTDConfig; + closedialog; + end; + +OnClock0003: + // Reset TMW2 Login Bonus on February and April + if (!$@TMW2_357GDQST) { + if (gettime(6) == FEBRUARY || gettime(6) == APRIL) { + DelAccRegFromEveryPlayer("#TMW2_LOGINBONUS"); + $@TMW2_357GDQST=1; + } + } + end; + +OnInit: + /* + .login_ref=gettime(5); + .daylength=(60*60*24); + */ + .size = getarraysize($MOTD_Messages$); + bindatcmd "motd", "@motd::OnCall", 99, 99, 1; +} + + + + +function script MOTDHandler { + // Handle events + if ($@GM_EVENT) + dispbottom l("An event is happening at Aeros! Hurry up!"); + if ($EVENT$ != "") + dispbottom l("It's @@ (day)!", $EVENT$); + + // Handle MOTD + if (!$MOTD_Disabled) + displayMOTD; + if (debug) + dispbottom "##7<<##B @@help://test-server|" + col(l("This is the test server."),6) + "@@ ##7>>"; + return; +} + diff --git a/npc/commands/music.txt b/npc/commands/music.txt new file mode 100644 index 0000000..37ee7ea --- /dev/null +++ b/npc/commands/music.txt @@ -0,0 +1,103 @@ +// Authors: Gumi, Jesusalva +// @music atcommand +// changes the music for all players on the map +// +// group lv: 80 +// group char lv: 80 +// log: True +// +// usage: +// @music <short name> +// +// example: +// @music forest + +- script @music 32767,{ + end; + +function listMusic { + dispbottom "ship, city, ghoul, surreal, magic, forest, mythica, acid, misuse, prelude, sunrise, peace, peace2, peace3, toast, woodland2, fortress, adonthell"; + dispbottom "unforgiving, deepcave, 8bit, action, hurns, fields, tulim, candor, lof, icecave, manacave, adventure, dance, academy, shrine, boss, ruins, minstrel"; + dispbottom "sprint, valkyries"; + return; +} + +OnCall: + if (!is_gm()) { + end; + } + + // TODO: tmw-like argv splitter + getmapxy .@map$, .@void, .@void, UNITTYPE_PC; // get map + + .@key$ = strtolower(.@atcmd_parameters$[0]); + .@m$ = htget(.hash, .@key$, "Not found"); + + if (.@m$ == "Not found") { + //.@m$ = implode(.@atcmd_parameters$[0], " "); + dispbottom "Invalid music key. Current accepted values are:"; + listMusic(); + } else { + changemusic .@map$, .@m$; + } + end; + +OnMyself: + .@key$ = strtolower(.@atcmd_parameters$[0]); + .@m$ = htget(.hash, .@key$, "Not found"); + + if (.@m$ == "Not found") { + //.@m$ = implode(.@atcmd_parameters$[0], " "); + dispbottom l("Invalid music key. Current accepted values are:"); + listMusic(); + } else { + //debugmes "Casting with: %s", .@m$; + changeplayermusic .@m$; + } + end; + +OnInit: + bindatcmd "music", "@music::OnCall", 60, 80, 1; + bindatcmd "mymusic", "@music::OnMyself", 1, 80, 0; + + .hash = htnew; // create hashtable + htput(.hash, "ship", "sail_away.ogg"); + htput(.hash, "city", "bartk_adventure.ogg"); + htput(.hash, "ghoul", "eric_matyas_ghouls.ogg"); + htput(.hash, "surreal", "eric_matyas_surreal.ogg"); + htput(.hash, "magic", "magick_real.ogg"); + htput(.hash, "forest", "dariunas_forest.ogg"); + htput(.hash, "mythica", "mythica.ogg"); + htput(.hash, "acid", "3b5.ogg"); + htput(.hash, "misuse", "misuse.ogg"); + htput(.hash, "prelude", "water_prelude.ogg"); + htput(.hash, "sunrise", "tws_birds_in_the_sunrise.ogg"); + htput(.hash, "peace", "peace.ogg"); + htput(.hash, "peace2", "peace2.ogg"); + htput(.hash, "peace3", "peace3.ogg"); + htput(.hash, "toast", "dragon_and_toast.ogg"); + htput(.hash, "woodland2", "New_Woodlands.ogg"); + htput(.hash, "unforgiving","Unforgiving_Lands.ogg"); + htput(.hash, "deepcave", "Deep_Cave.ogg"); + htput(.hash, "8bit", "8bit_the_hero.ogg"); + htput(.hash, "action", "Arabesque.ogg"); + htput(.hash, "hurns", "caketown.ogg"); + htput(.hash, "fields", "woodland_fantasy.ogg"); + htput(.hash, "icecave", "icecave.ogg"); + htput(.hash, "tulim", "mvrasseli_nochains.ogg"); + htput(.hash, "candor", "school_of_quirks.ogg"); + htput(.hash, "lof", "steam.ogg"); + htput(.hash, "adventure", "tmw_adventure.ogg"); + htput(.hash, "manacave", "tws_green_island.ogg"); + htput(.hash, "dance", "dance_monster.ogg"); + htput(.hash, "academy", "academy_bells.ogg"); + htput(.hash, "shrine", "Misty_Shrine.ogg"); + htput(.hash, "boss", "let_the_battles_begin.ogg"); + htput(.hash, "ruins", "Ruins.ogg"); + htput(.hash, "fortress", "sidequests.ogg"); + htput(.hash, "minstrel", "PerituneMaterial_Minstrel2_Harp.ogg"); + htput(.hash, "adonthell", "adonthell.ogg"); + htput(.hash, "sprint", "forest-sprint.ogg"); + htput(.hash, "valkyries", "valkyries.ogg"); +} + diff --git a/npc/commands/python.txt b/npc/commands/python.txt new file mode 100644 index 0000000..5273731 --- /dev/null +++ b/npc/commands/python.txt @@ -0,0 +1,27 @@ +// The Mana World script +// Author: Gumi <gumi@themanaworld.org> +// Author: Jesusalva <jesusalva@themanaworld.org> +// +// Stomp stomp stomp (use with caution) + +- script @python 32767,{ + end; + +OnCall: + specialeffect(34, AREA, playerattached()); + .@zone$=getmapinfo(MAPINFO_ZONE, .@mapa$); + if (.@zone$ == "MMO") + end; + sc_start SC_CASH_DEATHPENALTY, 1000, 1; + addtimer 380, .name$+"::OnKill"; + end; + +OnKill: + percentheal -100, -100; + //dispbottom l("Oh look, it is Cupid!"); + end; + +OnInit: + bindatcmd "python", "@python::OnCall", 60, 60, 1; + end; +} diff --git a/npc/commands/rate-management.txt b/npc/commands/rate-management.txt new file mode 100644 index 0000000..5215177 --- /dev/null +++ b/npc/commands/rate-management.txt @@ -0,0 +1,359 @@ +// Authors: Gumi, Jesusalva +- script @exprate 32767,{ + end; + + function expRateReal { + if (is_night()) + return $BCONFB_EXPR+$BCONFN_EXPR; + else + return $BCONFB_EXPR+$BCONFD_EXPR; + } + + function expRecalc { + .@val=getarg(0); + if (is_night()) + return .@val+$BCONFN_EXPR; + else + return .@val+$BCONFD_EXPR; + } + + function rateCleanUp { + stopnpctimer; + .hours = 0; + .max_hours = 0; + .current_rate = $BCONFB_EXPR; + setbattleflag("base_exp_rate", expRateReal()); + setbattleflag("job_exp_rate", expRateReal()); + charcommand("@reloadmobdb"); // this is on purpose (callable without RID) + SeasonReload(1); + channelmes("#world", "The EXP Rate Bonus is now over."); + } + + function remainingTime { + .@total_seconds = (3600 * .max_hours); + .@seconds_elapsed = (3600 * .hours) + (getnpctimer(0) / 1000); + .@seconds_remaining = max(1, .@total_seconds - .@seconds_elapsed); + return FuzzyTime(time_from_seconds(.@seconds_remaining), 2, 2); + } + +OnCall: + if (!is_gm()) + end; + if ($@CI_MODE) + end; + + .@special$ = strip(.@atcmd_parameters$[0]); // special value + .@new_rate = min(atoi(.@special$), 1000); // or just a regular integer + .@hours = min(0x7FFFFFFE, max(1, atoi(strip(.@atcmd_parameters$[1])))); // number of hours + + if (.@new_rate > 0) { + // Overwriting previous rate? + // Confirmation Required + if (expRecalc(.current_rate) != expRateReal()) { + setnpcdialogtitle("@rate-managment"); + mesc l("WARNING!"), 1; + mesc l("A previous exp rate up event is already ongoing."), 1; + mesc l("IF YOU CHANGE EXP RATE NOW, PREVIOUS BONUS WILL BE LOST!"), 1; + next; + mesc l("CONTINUE ANYWAY? [Y/N]"), 1; + // aborted + if (askyesno() == ASK_NO) + close; + closeclientdialog; + } + + // set new exp rate + .hours = 0; + .max_hours = .@hours; + .current_rate = .@new_rate; + setbattleflag("base_exp_rate", expRecalc(.@new_rate)); + setbattleflag("job_exp_rate", expRecalc(.@new_rate)); // Should GM event do this? + //setbattleflag("quest_exp_rate", expRecalc(.@new_rate)); + charcommand("@reloadmobdb"); + //charcommand("@reloadquestdb"); + SeasonReload(1); + initnpctimer; // start counting + + .@msg$=strcharinfo(0)+" increased experience rate to "+str(.@new_rate)+"%. It will only last "+str(FuzzyTime(time_from_hours(.max_hours), 2, 2))+"!"; + + announce .@msg$, bc_all; + channelmes("#world", .@msg$); + + //dispbottom l("You successfully set the exp rate to @@%. It will reset to @@% (default value) in @@.", + // .@new_rate, expRateReal(), FuzzyTime(time_from_hours(.max_hours), 2, 2)); + dispbottom l("You can also manually stop it at any time with: @exprate default"); + + } else if (.@new_rate == 0 && .@special$ == "") { + + // get current exp rate + if (.current_rate == $BCONFB_EXPR) { + atcommand("@rates"); + dispbottom col(l("Usage of @exprate without argument is deprecated, please use \"@rates\" instead."), 1); + } else { + dispbottom l("Current exp rate is set to @@%, and will reset to @@% (default value) in @@.", + .current_rate, expRateReal(), remainingTime()); + + dispbottom l("If you meant to reset the exp rate to its default value: @exprate default"); + } + + } else { + + // reset + rateCleanUp; + dispbottom l("Exp rate has been reset to @@% (default value).", + expRateReal()); + } + + end; + +OnPlayerCall: + if ($@CI_MODE) + end; + /* + // GM calls take precedence at any time! + if (.max_hours > 0 || .hours > 0) + end; + */ + // $@EXP_EVENT will determine the boost and should not be above 25% + // Default duration is one hour, or whatever $@EXP_EVENT_TIME is + $@EXP_EVENT=limit(0, $@EXP_EVENT, 100); + $@EXP_EVENT+=.current_rate; + if ($@EXP_EVENT_TIME > 6 && gettime(4) != SATURDAY && gettime(3)) { + consolewarn("Tried to set EXP Event Time to %d hours, but max is 6", $@EXP_EVENT_TIME); + $@EXP_EVENT_TIME=limit(1, $@EXP_EVENT_TIME, 6); + } + + // If a GM rate-up was running, we will sum the time, too. + // It'll be rounded down. (so 1h + 30m = 1h) FIXME average is better + if (.hours || .max_hours) { + $@EXP_EVENT_TIME+=max(0, .max_hours-.hours-1); + } + + // Default duration is one hour, or whatever $@EXP_EVENT_TIME is + .hours = 0; + .max_hours = $@EXP_EVENT_TIME; + .current_rate = $@EXP_EVENT; + setbattleflag("base_exp_rate", expRecalc($@EXP_EVENT)); + setbattleflag("job_exp_rate", expRecalc($@EXP_EVENT)); + charcommand("@reloadmobdb"); + SeasonReload(1); + initnpctimer; // start counting + + .@msg$="Experience Rate was modified to "+$@EXP_EVENT+"% for "+$@EXP_EVENT_TIME+" hour(s)!"; + + announce .@msg$, bc_all; + channelmes("#world", .@msg$); + + $@EXP_EVENT=0; + $@EXP_EVENT_TIME=0; + end; + +OnTimer3600000: + // runs every hour + if (++.hours == .max_hours) { + rateCleanUp; + end; + } + initnpctimer; + end; + +OnPCLoginEvent: + if (.max_hours > 0) { + dispbottom col(l("Exp rate is set to @@% for the next @@.", + .current_rate, remainingTime()), 6); + } + end; + +OnInit: + bindatcmd "exprate", "@exprate::OnCall", 60, 80, 1; // change exp rate + + // WARNING: using @reloadscript will change the "original" value + .current_rate = $BCONFB_EXPR; + + // XXX: maybe in the future: + //.original_job_rate = getbattleflag("base_job_rate"); + //.original_pk_mode = getbattleflag("pk_mode"); + //.original_death_penalty = getbattleflag("death_penalty_type"); + end; + +OnReload: + if (debug) { + debugmes("EXP Reload refused: Test server"); + end; + } + .@new_rate = expRecalc(.current_rate); + setbattleflag("base_exp_rate", .@new_rate); + setbattleflag("job_exp_rate", .@new_rate); + //charcommand("@reloadmobdb"); + //SeasonReload(1); // TODO FIXME: We are casting this twice. + end; + +OnInheirtedReload: + if (debug) { + debugmes("Nested Reload refused: Test server"); + end; + } + debugmes "[EXP] Inheir Reload"; + .@new_rate = expRecalc(.current_rate); + setbattleflag("base_exp_rate", .@new_rate); + setbattleflag("job_exp_rate", .@new_rate); + donpcevent("@droprate::OnReload"); + end; +} + + +///////////////////////////////////////////////////////////////////////////////// +- script @droprate 32767,{ + end; + + function dropRateReal { + if (is_night()) + return $BCONFB_DROP+$BCONFN_DROP; + else + return $BCONFB_DROP+$BCONFD_DROP; + } + + function dropRecalc { + .@val=getarg(0); + if (is_night()) + return .@val+$BCONFN_DROP; + else + return .@val+$BCONFD_DROP; + } + + function rateCleanUp { + stopnpctimer; + .hours = 0; + .max_hours = 0; + .current_rate = $BCONFB_DROP; + setbattleflag("item_rate_common", dropRateReal()); + setbattleflag("item_rate_common_boss", dropRateReal()); + setbattleflag("item_rate_heal", dropRateReal()); + setbattleflag("item_rate_heal_boss", dropRateReal()); + setbattleflag("item_rate_use", dropRateReal()); + setbattleflag("item_rate_use_boss", dropRateReal()); + setbattleflag("item_rate_equip", dropRateReal()); + setbattleflag("item_rate_equip_boss", dropRateReal()); + setbattleflag("item_rate_card", dropRateReal()); + setbattleflag("item_rate_card_boss", dropRateReal()); + charcommand("@reloadmobdb"); // this is on purpose (callable without RID) - no idea what is the purpose + SeasonReload(1); + channelmes("#world", "The Drop Rate Bonus is now over."); + } + + function remainingTime { + .@total_seconds = (3600 * .max_hours); + .@seconds_elapsed = (3600 * .hours) + (getnpctimer(0) / 1000); + .@seconds_remaining = max(1, .@total_seconds - .@seconds_elapsed); + return FuzzyTime(time_from_seconds(.@seconds_remaining), 2, 2); + } + +OnCall: + if (!is_gm()) { + end; + } + if ($@CI_MODE) + end; + + .@special$ = strip(.@atcmd_parameters$[0]); // special value + .@new_rate = min(atoi(.@special$), 1000); // or just a regular integer + .@hours = min(0x7FFFFFFE, max(1, atoi(strip(.@atcmd_parameters$[1])))); // number of hours + + if (.@new_rate > 0) + { + // set new exp rate + .hours = 0; + .max_hours = .@hours; + .current_rate = .@new_rate; + setbattleflag("item_rate_common", dropRecalc(.@new_rate)); + setbattleflag("item_rate_common_boss", dropRecalc(.@new_rate)); + setbattleflag("item_rate_heal", dropRecalc(.@new_rate)); + setbattleflag("item_rate_heal_boss", dropRecalc(.@new_rate)); + setbattleflag("item_rate_use", dropRecalc(.@new_rate)); + setbattleflag("item_rate_use_boss", dropRecalc(.@new_rate)); + setbattleflag("item_rate_equip", dropRecalc(.@new_rate)); + setbattleflag("item_rate_equip_boss", dropRecalc(.@new_rate)); + setbattleflag("item_rate_card", dropRecalc(.@new_rate)); + setbattleflag("item_rate_card_boss", dropRecalc(.@new_rate)); + charcommand("@reloadmobdb"); + SeasonReload(1); + initnpctimer; // start counting + + .@msg$=strcharinfo(0)+" modified drop rates to "+str(.@new_rate)+"%. It will only last "+str(FuzzyTime(time_from_hours(.max_hours), 2, 2))+"!"; + + announce .@msg$, bc_all; + channelmes("#world", .@msg$); + + //dispbottom l("You successfully set the drop rate to @@%. It will reset to @@% (default value) in @@.", + // .@new_rate, dropRateReal(), FuzzyTime(time_from_hours(.max_hours), 2, 2)); + dispbottom l("You can also manually stop it at any time with: @droprate default"); + } else if (.@new_rate == 0 && .@special$ == "") { + // get current exp rate + if (.current_rate == dropRateReal()) { + atcommand("@rates"); + dispbottom col(l("Usage of @exprate without argument is deprecated, please use \"@rates\" instead."), 1); + } else { + dispbottom l("Current drop rate is set to @@%, and will reset to @@% (default value) in @@.", + .current_rate, dropRateReal(), remainingTime()); + dispbottom l("If you meant to reset the drop rate to its default value: @droprate default"); + } + } + + else + { + // reset + rateCleanUp; + dispbottom l("Drop rate has been reset to @@% (default value).", + dropRateReal()); + } + + end; + +OnTimer3600000: + // runs every hour + if (++.hours == .max_hours) { + rateCleanUp; + end; + } + initnpctimer; + end; + +OnPCLoginEvent: + if (.max_hours > 0) { + dispbottom col(l("Drop rate is set to @@% for the next @@.", + .current_rate, remainingTime()), 6); + } + end; + +OnInit: + bindatcmd "droprate", "@droprate::OnCall", 80, 80, 1; // change drop rate + + // WARNING: using @reloadscript will change the "original" value, use @reloadbattleconf before! + .current_rate = $BCONFB_DROP; + //force_refreshall(); + end; + +OnReload: + if (debug) { + SeasonReload(1); + debugmes("Drop Reload refused: Test server"); + end; + } + if ($@CI_MODE) + end; + .@new_rate = dropRecalc(.current_rate); + setbattleflag("item_rate_common", .@new_rate); + setbattleflag("item_rate_common_boss", .@new_rate); + setbattleflag("item_rate_heal", .@new_rate); + setbattleflag("item_rate_heal_boss", .@new_rate); + setbattleflag("item_rate_use", .@new_rate); + setbattleflag("item_rate_use_boss", .@new_rate); + setbattleflag("item_rate_equip", .@new_rate); + setbattleflag("item_rate_equip_boss", .@new_rate); + setbattleflag("item_rate_card", .@new_rate); + setbattleflag("item_rate_card_boss", .@new_rate); + charcommand("@reloadmobdb"); + SeasonReload(1); + debugmes("Drop rates were reloaded with success."); + end; +} diff --git a/npc/commands/rent.txt b/npc/commands/rent.txt new file mode 100644 index 0000000..2be2ccc --- /dev/null +++ b/npc/commands/rent.txt @@ -0,0 +1,34 @@ +// TMW2 Script +// +// @rentitem <item> <time> +// Rents an <item> for <time> seconds +// 1 hour: 3600s +// 1 day: 86400s +// 1 month: 2592000s + +- script @rentitem 32767,{ + end; + +OnCall: + //.@request$ = ""; + //.@request$ += implode(.@atcmd_parameters$, " "); + + .@item=atoi(.@atcmd_parameters$[0]); + .@time=atoi(.@atcmd_parameters$[1]); + + if (!.@item || !.@time) { + dispbottom l("@rentitem <item numeric id> <time in seconds>"); + end; + } + + // Limiting to 365 days max + .@time=limit(15, .@time, 31536000); + + rentitem(.@item, .@time); + end; + +OnInit: + bindatcmd "rentitem", "@rentitem::OnCall", 80, 99, 1; + end; +} + diff --git a/npc/commands/resync.txt b/npc/commands/resync.txt new file mode 100644 index 0000000..246bfa8 --- /dev/null +++ b/npc/commands/resync.txt @@ -0,0 +1,45 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Introduces @resync +// +// It'll cast slide to your own position +// Hopefully making client update your real position without causing server warning +// +// This also introduces @resyncall +// Which is an alias for @refresh and causes client to reload the whole map, +// Including yourself and monsters. + +- script @resync 32767,{ + end; + +// Soft Resync +OnCall: + if (ispcdead()) { + dispbottom l("Impossible to resync: You are dead."); + end; + } + if (@rsync_delay > gettimetick(2)) { + dispbottom l("Not resync'ing to prevent flood."); + end; + } + getmapxy(.@m$, .@x, .@y, 0); + slide .@x, .@y; + @rsync_delay=gettimetick(2)+rand2(3,5); + end; + +// Hard Resync +OnCallRefresh: + if (@rsync_delay > gettimetick(2)) { + dispbottom l("Not resync'ing to prevent flood."); + end; + } + @rsync_delay=gettimetick(2)+rand2(3,5); + atcommand("@refresh"); + end; + +OnInit: + bindatcmd "resync", "@resync::OnCall", 0, 60, 0; + bindatcmd "resyncall", "@resync::OnCallRefresh", 0, 60, 0; + end; +} diff --git a/npc/commands/scheduled-broadcasts.txt b/npc/commands/scheduled-broadcasts.txt new file mode 100644 index 0000000..1801663 --- /dev/null +++ b/npc/commands/scheduled-broadcasts.txt @@ -0,0 +1,227 @@ +// Evol Script +// Authors: Gumi +function script StoneBoard { + + function setMessage { + do + { + clear; + mes l("Please enter the message:"); + next; + input .@msg$; + .@msg$ = strip(.@msg$); + if (.@msg$ != "") { + return .@msg$; + } + mes l("The message cannot be empty"); + next; + } while (1); + } + + function setInterval { + clear; + mes l("Please select the interval:"); + next; + menuint + l("Every 1 hour"), 1, + l("Every 3 hours"), 3, + l("Every 5 hours"), 5, + l("Every 6 hours"), 6, + l("Every 12 hours"), 12, + l("Every 24 hours"), 24, + l("Never (only on login)"), 0; + + return @menuret; + } + + function setMaxRep { + if (getarg(0,0) == 0) { + return 0; + } + clear; + mes l("Repeat how many times?"); + next; + menuint + l("Send only once"), 1, + l("Send 2 times"), 2, + l("Send 3 times"), 3, + l("Send 5 times"), 5, + l("Send 10 times"), 10, + l("Send 20 times"), 20, + l("Send indefinitely"), 0; + + return @menuret; + } + + function setOnLogin { + if (getarg(0,0) == 0) + { + return 1; + } + clear; + mes l("Send this message also on login?"); + next; + select + l("No"), + l("Yes"); + + return (@menu - 1); + } + + function newBroadcast { + do + { + setnpcdialogtitle l("Scheduled broadcasts - Create new"); + + // go through all steps + .@msg$ = setMessage(); + .@int = setInterval(); + .@max = setMaxRep(.@int); + .@login = setOnLogin(.@int); + + // recap + clear; + mes l("Message:"); + mes "---"; + mes .@msg$; + mes "---"; + if (.@int) + { + mes l("Interval: every @@ hour(s)", .@int); + mes l("Repeat: @@ times", .@max ? .@max : "∞"); + mes l("Sent on login: @@", .@login ? l("yes") : l("no")); + } + else + { + mes l("Interval: (none, only sent on login)"); + mes l("Sent on login: yes"); + } + + next; + select + menuimage("actions/cancel", l("Discard")), + menuimage("actions/edit", l("Start over")), + menuimage("actions/test", l("Start broadcasting")), + menuimage("actions/test", l("Start broadcasting, and make an extra broadcast right now")); + + switch (@menu) + { + case 3: + case 4: + stopnpctimer "@sched"; + $@SCHED_Opt[0] = .@login; + $@SCHED_Opt[1] = .@int; + $@SCHED_Opt[2] = 0; + $@SCHED_Opt[3] = .@max; + $@SCHED_Opt[4] = 0; + $@SCHED_Msg$ = .@msg$; + if (.@int) + { + initnpctimer "@sched"; + } + if (@menu == 4) + { + announce $@SCHED_Msg$, bc_all; + } + logmes "Scheduled Broadcast: A new broadcast was added", LOGMES_ATCOMMAND; + case 1: return; + } + + } while(1); + } + + do + { + clear; + setnpcdialogtitle l("Scheduled broadcasts"); + mes l("This menu allows you to set the scheduled broadcast that is sent to all players at a specific interval."); + mes ""; + + .@a = $@SCHED_Msg$ != ""; // any active broadcast? + mes "---"; + mes .@a ? $@SCHED_Msg$ : "(" + l("no active broadcast") +")"; + mes "---"; + if (.@a) + { + mes l("Sent on login: @@", ($@SCHED_Opt[0] ? l("yes") : l("no"))); + if ($@SCHED_Opt[1]) + { + .@next = max(1, ((3600000 * ($@SCHED_Opt[1] - $@SCHED_Opt[4])) - getnpctimer(0, "@sched"))); + mes l("Interval: every @@ hour(s)", $@SCHED_Opt[1]); + mes l("Next broadcast: @@", FuzzyTime(time_from_ms(.@next))); + } + else + { + mes l("Interval: (none, only sent on login)"); + mes l("Next broadcast: (never)"); + } + mes l("Sent: @@ times out of @@", $@SCHED_Opt[2], ($@SCHED_Opt[3] ? $@SCHED_Opt[3] : "∞")); + } + next; + + select + menuimage("actions/abort", l("Abort")), + rif(.@a, menuimage("actions/test", l("Manually trigger the current broadcast"))), + rif(.@a, menuimage("actions/remove", l("Stop broadcasting"))), + rif(!(.@a), menuimage("actions/add", l("Set a new broadcast"))), + rif(getarg(0,0), menuimage("actions/home", l("Return to Super Menu"))); + + switch (@menu) + { + case 2: announce $@SCHED_Msg$, bc_all; break; + case 3: $@SCHED_Msg$ = ""; break; + case 4: newBroadcast; break; + default: return; + } + } while (1); +} + + + +- script @sched 32767,{ + end; + +OnTimer3600000: + if ($@SCHED_Msg$ == "") + { + stopnpctimer; + end; + } + + ++$@SCHED_Opt[4]; // increase hours counter + if ($@SCHED_Opt[4] == $@SCHED_Opt[1]) + { + stopnpctimer; + ++$@SCHED_Opt[2]; // increase total counter + announce $@SCHED_Msg$, bc_all; + $@SCHED_Opt[4] = 0; // reset hours counter + if ($@SCHED_Opt[2] >= $@SCHED_Opt[3] && $@SCHED_Opt[3] > 0) + { + $@SCHED_Msg$ = ""; // reset message + end; + } + } + initnpctimer; + end; + +OnCall: + if (!is_gm()) + { + end; + } + + StoneBoard; + closedialog; + end; + +OnInit: + bindatcmd "sched", "@sched::OnCall", 80, 99, 1; +} + +function script StoneBoardRead { + if ($@SCHED_Opt[0] && $@SCHED_Msg$ != "") + { + announce $@SCHED_Msg$, bc_self; + } + return; +} diff --git a/npc/commands/shroom.txt b/npc/commands/shroom.txt new file mode 100644 index 0000000..76a4304 --- /dev/null +++ b/npc/commands/shroom.txt @@ -0,0 +1,62 @@ +// TMW2 Script +// +// @shroom <mobID> <Amount> <DisplayName> +// Plushroom Angel Script (Plush: 1011, Chaga: 1128, Clover: 1028) + +- script @shroom 32767,{ + end; + +OnShroom: + if (.@atcmd_parameters$[0] != "") + .@mobId=atoi(array_shift(.@atcmd_parameters$)); + if (.@atcmd_parameters$[1] != "") + .@mobAm=atoi(array_shift(.@atcmd_parameters$)); + if (.@atcmd_parameters$[2] != "") + .@mobName$=implode(.@atcmd_parameters$, " "); + + // Checks + if (!.@mobId) + .@mobId=PlushroomField; + + if (!.@mobAm) + .@mobAm=1; + + if (.@mobName$ == "") + .@mobName$=strmobinfo(1, .@mobId); + + //.@gmType=(Sex ? NPC_GAMEMASTER : NPC_GAMEMISTRESS); + .@gmType=(Sex ? GameMaster : GameMistress); + .@gmId=monster("boss", 45, 45, strcharinfo(0), .@gmType, 1); + + // Max 40 connected players for this to work + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + debugmes "@shroom: Attach account %d to spawn %d %s (%d)", .@players[.@i], .@mobAm, .@mobName$, .@mobId; + attachrid(.@players[.@i]); + getmapxy(.@m$, .@x, .@y, 0); + unitwarp(.@gmId, .@m$, .@x, .@y); + sleep2(20); + unitwalk(.@gmId, .@x-1, .@y-1); + sleep2(200); + unitemote(.@gmId, any(E_HAPPY, E_HAPPY, E_ANGEL, E_EVILCROC)); + sleep2(150); + + // Adjust amount based on player level + .@Ammo=limit(1, BaseLevel/15*.@mobAm, 60); + //getmapxy(.@dm$, .@dx, .@dy, UNITTYPE_MOB, .@gmId); + //debugmes "Spawn %d mobs at %s, (%d, %d)", .@Ammo, .@dm$, .@dx, .@dy; + + areamonster(.@m$, .@x-3, .@y-3, .@x+3, .@y+3, .@mobName$, .@mobId, .@Ammo); + sleep2(320); + detachrid(); + } + + // Cleanup + unitwarp(.@gmId, "boss", 45, 45); + unitkill(.@gmId); + end; + +OnInit: + bindatcmd "shroom", "@shroom::OnShroom", 60, 100, 1; +} + diff --git a/npc/commands/super-menu.txt b/npc/commands/super-menu.txt new file mode 100644 index 0000000..cf9b49c --- /dev/null +++ b/npc/commands/super-menu.txt @@ -0,0 +1,241 @@ +// Authors: Gumi, Jesusalva +function script SuperMenu { + do + { + clear; + setnpcdialogtitle l("Super Menu"); + mes l("This menu contains all options available to you, based on your access privileges."); + mes ""; + mes l("What do you want to access?"); + next; + select + rif(is_gm(), l("Scheduled broadcasts")), + rif(is_master(), l("MOTD")), + rif(is_gm(), l("Event management")), + rif(is_admin() && !getcharid(2), l("Join teh Guild")), + rif(is_staff(), l("Referral Program Report")), + rif(is_admin(), "Broken checks"), + rif(is_admin() && $@GM_OVERRIDE && !$NIVALIS_LIBDATE && $NLIB_DAY, "Flush NLIB"), + rif(is_gm(), l("Seasonal Drop Control")), + "Debug", + "Quit"; + + switch (@menu) + { + case 1: StoneBoard 1; break; + case 2: MOTDConfig 1; break; + case 3: GlobalEventMenu 1; break; + case 4: + if (!$@HAS_API) { + query_sql("UPDATE `char` SET `guild_id`=1 WHERE `char_id`="+getcharid(0)); + } else { + apiasync("SQL", sprintf("UPDATE `char` SET `guild_id`=1 WHERE `char_id`='%d'", getcharid(0))); + apiasync("SQLRUN", ""); + } + break; + case 5: HallOfReferral; break; + case 6: + delitem Aquada, 1; + delitem Bread, 100; + mes("Either delitem is not working, or you had 1 aquada and 100 bread."); + next; + break; + case 7: + donpcevent("The Monster King#NLib::OnReprocess"); + if ($NLIB_DAY == 7) { + setmapflag("023-2",mf_bexp,200); + donpcevent("The Monster King#NLib::OnBegin"); + } + break; + case 8: SeasonControl; break; + case 9: GlobalDebugMenu 1; break; + default: close; break; + } + } while (1); +} + + + +- script @super 32767,{ + end; + +OnCall: + + if (!is_gm()) { + dispbottom l("You do not have the required access privileges to use the Super Menu."); + end; + } + + SuperMenu; + closedialog; + end; + +OnGM: + mesc ".:: GM Auth ::.", 1; + mesc "--- System Login ---"; + if (!debug) { + if (!validatepin()) + close; + } else { + mes ""; + mes "This is a Test Server, verification mode in AUTO."; + } + .@auth=true; + // NOTE: Pihro and Pyndragon do not have player chars + // FIXME: Use account ID over char name (multi-server stable) + if (strcharinfo(0) == "Jesusalva") { + setgroupid(99); + } else if (strcharinfo(0) == "Saulc") { + setgroupid(99); + } else if (strcharinfo(0) == "seeds") { + setgroupid(60); + } else if (strcharinfo(0) == "Hector") { + setgroupid(60); + } else if (strcharinfo(0) == "demure") { + setgroupid(80); + } else if (strcharinfo(0) == "jak1") { + setgroupid(80); + // Test Server grants + } else if (debug && getgroupid() == 1) { + setgroupid(5); + } else { + dispbottom l("Your privileges do not allow you to use this command."); + .@auth=false; + } + // Handle authentication reports + if (.@auth) { + announce strcharinfo(0)+" has just logged in services.", bc_all; + logmes strcharinfo(0)+" : GM login.", LOGMES_ATCOMMAND; + consoleinfo("%s changed from PC to GM.", strcharinfo(0)); + } else { + logmes strcharinfo(0)+" : GM Authentication failed.", LOGMES_ATCOMMAND; + } + close; + +OnGMOff: + if (is_staff()) { + logmes strcharinfo(0)+" : GM logout.", LOGMES_ATCOMMAND; + consoleinfo("%s changed from GM to PC.", strcharinfo(0)); + setgroupid(1); + } + end; + +OnInit: + bindatcmd "super", "@super::OnCall", 80, 99, 0; + bindatcmd "gmoff", "@super::OnGMOff", 5, 100, 1; + bindatcmd "gm", "@super::OnGM", 0, 100, 1; + + // Special servers + if (debug) { + setbattleflag("mob_spawn_delay", $BCONFD_SPAWN); + setbattleflag("monster_hp_rate", $BCONFD_MOBHP); + donpcevent("@exprate::OnInheirtedReload"); + } else if ($HARDCORE) { + setbattleflag("mob_spawn_delay", 25); // Official: 100~150 + setbattleflag("monster_hp_rate", 250); + setbattleflag("base_exp_rate", 200); + setbattleflag("job_exp_rate", 200); + .@new_rate=300; + setbattleflag("item_rate_common", .@new_rate); + setbattleflag("item_rate_common_boss", .@new_rate); + setbattleflag("item_rate_heal", .@new_rate); + setbattleflag("item_rate_heal_boss", .@new_rate); + setbattleflag("item_rate_use", .@new_rate); + setbattleflag("item_rate_use_boss", .@new_rate); + setbattleflag("item_rate_equip", .@new_rate); + setbattleflag("item_rate_equip_boss", .@new_rate); + setbattleflag("item_rate_card", .@new_rate); + setbattleflag("item_rate_card_boss", .@new_rate); + // Other settings + setbattleflag("mob_count_rate", 150); + setbattleflag("no_spawn_on_player", 3); + setbattleflag("zeny_from_mobs", true); + setbattleflag("item_auto_get", true); + setbattleflag("enable_perfect_flee", 3); // Mobs can now perfect-flee + setbattleflag("mob_critical_rate", 100); + setbattleflag("multi_level_up", true); + setbattleflag("quest_exp_rate", 300); + setbattleflag("death_penalty_type", 2); // Death causes total exp loss + setbattleflag("homunculus_friendly_rate", 1000); + setbattleflag("hom_bonus_exp_from_master", 30); + //setbattleflag("pk_mode", 1); + setbattleflag("party_even_share_bonus", 12); // Official: 9% + setbattleflag("hp_rate", 200); // Official: 100% + setbattleflag("player_invincible_time", 10000); // Official: 5s + setbattleflag("natural_healhp_interval", 1500); // Official: 2.5s + setbattleflag("natural_heal_weight_rate", 75); // Official: 50% + setbattleflag("max_aspd", 193); // Official: 190 + setbattleflag("max_parameter", 150); // The Official Official is 150 + setbattleflag("pc_status_def_rate", 50); // 2x harder to get stat resist + // Reload settings + charcommand("@reloadmobdb"); + SeasonReload(1); + } + end; + +// Servers with "debug" set are debug servers which must reset on their own +// Hardcore servers as well +// They restart every sunday, at 03:00 UTC +OnSun0250: + .@sv$=(debug ? "Test" : "Hardcore"); + if (debug || $HARDCORE) kamibroadcast("WARNING: "+.@sv$+" Server will go down for scheduled maintenance in 10 minutes!"); + end; +OnSun0255: + .@sv$=(debug ? "Test" : "Hardcore"); + if (debug || $HARDCORE) kamibroadcast("WARNING: "+.@sv$+" Server will go down for scheduled maintenance in 5 minutes!"); + end; +OnSun0259: + .@sv$=(debug ? "Test" : "Hardcore"); + if (debug || $HARDCORE) kamibroadcast("WARNING: Imminent "+.@sv$+" Server restart!"); + end; +OnSun0300: + if (debug || $HARDCORE) atcommand("@serverexit 103"); + end; + + +// Live Servers also need to reset, but with less frequency +// They restart on the first wednesday of the month, at 03:00 UTC +// Note: We can use gettimeparam - weeks since epoch - and restart every +// 2 weeks if needed. (weeks % 2 == 1) +OnWed0245: + if (!$AUTORESTART || gettime(GETTIME_DAYOFMONTH) > 7) end; + kamibroadcast("WARNING: Server will go down for scheduled maintenance in 15 minutes!"); + end; +OnWed0250: + if (!$AUTORESTART || gettime(GETTIME_DAYOFMONTH) > 7) end; + kamibroadcast("WARNING: Server will go down for scheduled maintenance in 10 minutes!"); + end; +OnWed0255: + if (!$AUTORESTART || gettime(GETTIME_DAYOFMONTH) > 7) end; + kamibroadcast("WARNING: Server will go down for scheduled maintenance in 5 minutes!"); + end; +OnWed0259: + if (!$AUTORESTART || gettime(GETTIME_DAYOFMONTH) > 7) end; + kamibroadcast("WARNING: Imminent Server restart!"); + end; +OnWed0300: + if (!$AUTORESTART || gettime(GETTIME_DAYOFMONTH) > 7) end; + atcommand("@serverexit 101"); + end; +} + +// Auto-restart scheduled +function script SchedRestart { + // FIXME - How is that even supposed to work? + return; + + if (debug && $SCHED_RESTART) { + + if ($SCHED_RESTART == 1) + $SCHED_RESTART=gettimetick(2)+900; + + if ($SCHED_RESTART > gettimetick(2)) { + $SCHED_RESTART=0; + atcommand("@serverexit 101"); + } + + kamibroadcast(sprintf("A restart has been scheduled to about %s.", FuzzyTime($SCHED_RESTART))); + } + return; +} + diff --git a/npc/commands/titulate.txt b/npc/commands/titulate.txt new file mode 100644 index 0000000..c367964 --- /dev/null +++ b/npc/commands/titulate.txt @@ -0,0 +1,226 @@ +// TMW2 Script +// +// @titulate <username> +// Grants a title to <username>. Cannot be undone. +// @cassate +// Opens a prompt to strip someone from their titles. +// Can also ban from titles. Grandmasters only. + +- script @titulate 32767,{ + end; + +OnCall: + .@request$ = ""; + .@request$ += implode(.@atcmd_parameters$, " "); + .@request$ = strip(.@request$); + + // No argument supplied + if (.@request$ == "") { + Exception("Usage: @titulate <target charname>", RB_ISFATAL|RB_DISPBOTTOM); + } + + // Player is not attached + .@ori = getcharid(3); + .@id = getcharid(3, .@request$); + if (.@id < 1 || .@ori < 1) { + Exception("Player not found.", RB_ISFATAL|RB_DISPBOTTOM); + } + + // Obtain your own title + .@mine=ACADEMIC_RANK; + .@them=getvariableofpc(ACADEMIC_RANK, .@id, 99); + + mes ".:: " + l("Titulation") + " ::."; + if (.@mine <= .@them || .@them < 0) { + mesc l("You can only concede or vouch a title for people of academic rank inferior than your own."); + close; + } + + mesc l("You're about to concede an academic title to \"@@\".", .@request$), 1; + mesc l("If this is found out to be a fraudulent titulation, both you as target will have their titles cased by the Academic Council, or by the Alliance High Council."), 1; + mesc l("This action CANNOT BE UNDONE."), 1; + mes l("Are you sure?"); + if (askyesno() == ASK_NO) + close; + + // Grant the title + if (attachrid(.@id)) { + switch (ACADEMIC_RANK) { + case ACADEMIC_LAYMAN: + case ACADEMIC_STUDENT: + case ACADEMIC_TECHNIC: + // Instant promotion + ACADEMIC_RANK+=1; + .@upgrade=true; + break; + case ACADEMIC_BACHELOR: + case ACADEMIC_MASTER: + // Two-Man rule + if (array_find(ACADEMIC_VOUCH, .@ori)) { + end; + } + array_push(ACADEMIC_VOUCH, .@ori); + if (array_entries(ACADEMIC_VOUCH) >= 2) { + ACADEMIC_RANK+=1; + deletearray(ACADEMIC_VOUCH); + .@upgrade=true; + } + break; + case ACADEMIC_DOCTOR: + case ACADEMIC_PHD: + // Three-Man rule + if (array_find(ACADEMIC_VOUCH, .@ori)) { + end; + } + array_push(ACADEMIC_VOUCH, .@ori); + if (array_entries(ACADEMIC_VOUCH) >= 3) { + ACADEMIC_RANK+=1; + deletearray(ACADEMIC_VOUCH); + .@upgrade=true; + } + break; + // Invalid + case ACADEMIC_SAGE: + Exception("The Grand Master title can only be issued by the president of the Alliance High Council.", RB_ISFATAL|RB_SPEECH); + default: + Exception("Invalid titulation rank: "+ACADEMIC_RANK, RB_ISFATAL|RB_SPEECH); + } + + // Message + if (.@upgrade) { + dispbottom l("You received the %s title from %s.", + academicrank(), strcharinfo(0, "someone", .@ori)); + } else { + dispbottom l("You were vouched to the %s title by %s. You still need %d more vouches to be promoted.", + academicrank(ACADEMIC_RANK+1), strcharinfo(0, "someone", .@ori), + (ACADEMIC_RANK >= ACADEMIC_DOCTOR ? 3 : 2) - array_entries(ACADEMIC_VOUCH)); + } + + // Oops, player disconnected + } else { + Exception("Player not found.", RB_ISFATAL|RB_SPEECH); + } + + attachrid(.@ori); + // Inform everyone + kamibroadcast(sprintf("%s has vouched %s for the title of %s.", + strcharinfo(0), .@request$, academicrank(.@them+1))); + // Log in a special log file as well + logmes(sprintf("%s has vouched %s for the title of %s.", + strcharinfo(0), .@request$, academicrank(.@them+1))); + + close; + +OnCassate: + if (ACADEMIC_RANK < ACADEMIC_GM) { + mesc l("Only Academy Grand Masters may cassate someone."); + close; + } + mes ("To revoke someone's title and optionally apply a ban on them."); + mes ("Please insert the nickname of person to cassate (they must be online)"); + input .@request$; + if (.@request$ == "") + close; + mes ("Ban them from the Magic Academy as well?"); + .@ban=(askyesno() == ASK_YES); + next; + .@ori = getcharid(3); + .@id = getcharid(3, .@request$); + if (.@id < 1 || .@ori < 1) { + Exception("Player not found.", RB_ISFATAL|RB_DISPBOTTOM); + } + + // Execute the banishment + if (attachrid(.@id)) { + ACADEMIC_RANK=(.@ban ? -1 : ACADEMIC_LAYMAN); + skill TMW2_STUDY, 0, 0; + dispbottom l("Your academy titles have been rescinded%s", + (.@ban ? l(", and you have been banned from the Academy.") : ".")); + } else { + Exception("Player not found.", RB_ISFATAL|RB_DISPBOTTOM); + } + + attachrid(.@ori); + // Inform everyone + kamibroadcast(sprintf("%s (A.GM) has stripped %s from their academic titles.", + strcharinfo(0), .@request$)); + // Log in a special log file as well + logmes(sprintf("%s (Grandmaster) has stripped %s from their academic titles.", + strcharinfo(0), .@request$)); + + close; + +OnInit: + bindatcmd "titulate", "@titulate::OnCall", 0, 100, 1; + bindatcmd "cassate", "@titulate::OnCassate", 60, 100, 1; +} + +function script AutoTitulate { + .@ori = gettimetick(2); // Will never duplicate + switch (ACADEMIC_RANK) { + case ACADEMIC_LAYMAN: + case ACADEMIC_STUDENT: + case ACADEMIC_TECHNIC: + // Instant promotion + ACADEMIC_RANK+=1; + .@upgrade=true; + break; + case ACADEMIC_BACHELOR: + case ACADEMIC_MASTER: + // Two-Man rule + if (array_find(ACADEMIC_VOUCH, .@ori)) { + end; + } + array_push(ACADEMIC_VOUCH, .@ori); + if (array_entries(ACADEMIC_VOUCH) >= 2) { + ACADEMIC_RANK+=1; + deletearray(ACADEMIC_VOUCH); + .@upgrade=true; + } + break; + case ACADEMIC_DOCTOR: + case ACADEMIC_PHD: + // Three-Man rule + if (array_find(ACADEMIC_VOUCH, .@ori)) { + end; + } + array_push(ACADEMIC_VOUCH, .@ori); + if (array_entries(ACADEMIC_VOUCH) >= 3) { + ACADEMIC_RANK+=1; + deletearray(ACADEMIC_VOUCH); + .@upgrade=true; + } + break; + // Invalid + case ACADEMIC_SAGE: + getexp 5000000, 150000; + mesc "+500,000 exp"; + mesc "+150,000 job"; + Exception("The Grand Master title can only be issued by the president of the Alliance High Council.", RB_ISFATAL|RB_SPEECH); + default: + Exception("Invalid titulation rank: "+ACADEMIC_RANK, RB_ISFATAL|RB_SPEECH|RB_PLEASEREPORT); + } + + // Message + if (.@upgrade) { + dispbottom l("You received the %s title from %s.", + academicrank(), "Hocus Pocus the Fidibus"); + } else { + dispbottom l("You were vouched to the %s title by %s. You still need %d more vouches to be promoted.", + academicrank(ACADEMIC_RANK+1), "Hocus Pocus the Fidibus", + (ACADEMIC_RANK >= ACADEMIC_DOCTOR ? 3 : 2) - array_entries(ACADEMIC_VOUCH)); + } + + // Inform everyone + .@r = ACADEMIC_RANK + 1; + kamibroadcast(sprintf("%s has vouched %s for the title of %s.", + "Hocus Pocus the Fidibus", strcharinfo(0), academicrank(.@r))); + // Log in a special log file as well + logmes(sprintf("%s has vouched %s for the title of %s.", + "Hocus Pocus the Fidibus", strcharinfo(0), academicrank(.@r))); + + // Continue + return; +} + + diff --git a/npc/commands/ucp.txt b/npc/commands/ucp.txt new file mode 100644 index 0000000..c4cf2a0 --- /dev/null +++ b/npc/commands/ucp.txt @@ -0,0 +1,363 @@ +// TMW2 Script +// Author: +// Jesusalva + +function script UserCtrlPanel { + do + { + @unsaved=false; + clear; + setnpcdialogtitle l("User Control Panel"); + mes l("This menu gives you some options which affect your account."); + mes l("In some cases, your pincode will be required."); + mes ""; + mes l("What do you want to access?"); + next; + if (@unsaved) { + mesc l("Careful: You have unsaved changes!"), 1; + mes ""; + } + select + l("Rules"), + l("Game News"), + rif(#FIRST_TIME == 1, l("Create PIN Number")), + l("Account Information"), + rif(getcharid(2) > 0, l("Guild Information")), + l("Change Language"), + rif(getskilllv(TMW2_CRAFT), l("Change Crafting Options")), + l("Game Settings"), + l("Save & Exit"); + + switch (@menu) + { + case 1: GameRules(); break; + case 2: GameNews(); break; + case 3: + // Account age check (15 days or complete Lua's arc) + if (gettimetick(2) < #REG_DATE+86400*15 && getq(General_Narrator) < 3) { + mesc l("Your account is too young."), 1; + mesc l("The accounts need 15 days to set Pin Codes."), 1; + next; + break; + } + mes l(".:: Create PIN Code ::."); + mes l("If you decide to continue, a random PINCODE will be"); + mes l("sent to the email you used to register on Moubootaur Legends."); + mes ""; + mes l("With a PinCode, you'll have access to restricted features,"); + mes l("Like Discord integration and sensitive options."); + mes ""; + mes l("You can change the PIN from ManaPlus char selection screen."); + mes l("You can also modify your email with %s.", b("@email")); + mes l("This will do nothing if the account already have a PIN."); + next; + mesc l("Are you sure you want to create a PIN now?"), 1; + if (askyesno() == ASK_NO) + break; + mes ""; + .@msg$=json_encode("date", gettimetick(2), + "accid", getcharid(3), + "pin", rand2(10000)); + debugmes .@msg$; + consoleinfo("%s requested a PinCode.", strcharinfo(0)); + api_send(API_PINCODE, .@msg$); + #FIRST_TIME=2; + mesc l("PinCode created, an email should arrive within 15 minutes."), 3; + next; + break; + case 4: + if (!validatepin()) { + next; + break; + } + if (!@lgc || @query) { + query_sql("SELECT email,logincount,last_ip FROM `login` WHERE account_id="+getcharid(3)+" LIMIT 1", .@email$, .@lgc, .@ip$); + @email$=.@email$; + @lgc=.@lgc; + @ip$=.@ip$; + } else { + .@email$=@email$; + .@lgc=@lgc; + .@ip$=@ip$; + } + mes l("Char Name: @@", strcharinfo(0)); + mes l("Party Name: @@", strcharinfo(1)); + mes l("Guild Name: @@", strcharinfo(2)); + mes l("Clan Name: @@", strcharinfo(4)); + mes ""; + mes l("Email: @@", .@email$[0]); + if (Sex) + mes l("Male"); + else + mes l("Female"); + mes l("Last IP: @@", .@ip$[0]); + mes l("Total Logins: @@", .@lgc[0]); + mes l("Registed %s ago", FuzzyTime(#REG_DATE)); + next; + if (@query) + break; + @query=1; + query_sql("SELECT name,last_login,last_map,partner_id FROM `char` WHERE account_id="+getcharid(3)+" LIMIT 9", .@name$, .@lastlogin$, .@map$, .@married); + for (.@i = 1; .@i <= getarraysize(.@name$); .@i++) { + mesn .@name$[.@i-1]; + mes l("Last Seen: @@", FuzzyTime(.@lastlogin$[.@i-1])); + mes l("Last map: @@", .@map$[.@i-1]); + if (.@married[.@i-1]) + mes l("Married with @@", gf_charname(.@married[.@i-1])); + mes ""; + } + next; + break; + case 5: + .@gid=getcharid(2); + mesc (".:: "+getguildname(.@gid)+" ::."), 1; + mesc l("Guild Master: @@", getguildmaster(.@gid)), 3; + if (getguildnxp(.@gid) > 0) + mesc l("Guild Lv @@, @@/@@ EXP to level up", getguildlvl(.@gid), fnum(getguildexp(.@gid)), fnum(getguildnxp(.@gid))); + else + mesc l("Guild Lv @@, @@/@@ EXP to level up", fnum(getguildlvl(.@gid)), getguildexp(.@gid), "???"); + + mes ""; + mesc l("Average player level: @@", getguildavg(.@gid)); + mesc l("Your position on the guild: @@", getguildrole(.@gid, getcharid(3), true)); + next; + break; + case 6: asklanguage(LANG_IN_SHIP); break; + case 7: + // Draw the GUI and any info on it + csysGUI_Report(); + mesc l("NOTE: The effective bonus level applied is the average level of enabled options!"); + //mesc l("Mobpt: @@", Mobpt); + do { + .@opt$="Do nothing"; + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_BASE); + + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_ATK); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_DEF); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_ACC); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_EVD); + + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_REGEN); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_SPEED); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_DOUBLE); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_MAXPC); + + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_SCRESIST); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_SCINFLICT); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_MANAUSE); + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_BOSSATK); + + .@opt$+=":"+csysGUI_OptToogleMenu(CRGROUP_FINAL); + + select (.@opt$); + mes ""; + switch (@menu) { + case 2: csysGUI_ChangeOpt(CRGROUP_BASE); break; + + case 3: csysGUI_ChangeOpt(CRGROUP_ATK); break; + case 4: csysGUI_ChangeOpt(CRGROUP_DEF); break; + case 5: csysGUI_ChangeOpt(CRGROUP_ACC); break; + case 6: csysGUI_ChangeOpt(CRGROUP_EVD); break; + + case 7: csysGUI_ChangeOpt(CRGROUP_REGEN); break; + case 8: csysGUI_ChangeOpt(CRGROUP_SPEED); break; + case 9: csysGUI_ChangeOpt(CRGROUP_DOUBLE); break; + case 10: csysGUI_ChangeOpt(CRGROUP_MAXPC); break; + + case 11: csysGUI_ChangeOpt(CRGROUP_SCRESIST); break; + case 12: csysGUI_ChangeOpt(CRGROUP_SCINFLICT); break; + case 13: csysGUI_ChangeOpt(CRGROUP_MANAUSE); break; + case 14: csysGUI_ChangeOpt(CRGROUP_BOSSATK); break; + + case 15: csysGUI_ChangeOpt(CRGROUP_FINAL); break; + } + } while (@menu > 1); + break; + case 8: + do + { + mesc ".:: " + l("GAME SETTINGS") + " ::.", 3; + + // GSET_SOULMENHIR_MANUAL + // Enables/Disable manual position saving on Soul Menhir + if (GSET_SOULMENHIR_MANUAL) + mes l("Soul Menhir automatic saving: ") + col(l("Disabled"), 1); + else + mes l("Soul Menhir automatic saving: ") + col(l("Enabled"), 2); + + + // GSET_DAILYREWARD_SILENT + // Enables/Disable silent dialog for daily rewards + // (otherwise a image will be shown) + if (GSET_DAILYREWARD_SILENT) + mes l("Display daily reward screen: ") + col(l("Disabled"), 1); + else + mes l("Display daily reward screen: ") + col(l("Enabled"), 2); + + + // GSET_LONGMENU_DENSITY + // How many nexts should be queued in a density list + // (use higher values if you have few recipes or can scroll) + if (!GSET_LONGMENU_DENSITY) + mes l("Long Text Wall Density: ") + col(l("Normal"), 1); + else + mes l("Long Text Wall Density: ") + col(l("Compact")+": "+GSET_LONGMENU_DENSITY, 2); + + + // GSET_FIXED_ALCHEMY + // Alchemy Table Behavior + if (GSET_FIXED_ALCHEMY) + mes l("Alchemy Table: ") + col(l("Never ask: Brew %d", GSET_FIXED_ALCHEMY), 1); + else + mes l("Alchemy Table: ") + col(l("Ask everytime"), 2); + + + // GSET_FISHING_BAIT + // Fishing Bait Behavior + if (GSET_FISHING_BAIT > 1) + mes l("Fishing bait: ") + col(l("Always use %s", getitemlink(GSET_FISHING_BAIT)), 2); + else if (GSET_FISHING_BAIT) + mes l("Fishing bait: ") + col(l("Ask next time"), 1); + else + mes l("Fishing bait: ") + col(l("Ask everytime"), 2); + + + // GSET_ALCOHOL_NOOVERDRINK + // Should players be allowed to drink themselves to death? + if (GSET_ALCOHOL_NOOVERDRINK) + mes l("Lethal overdrinking: ") + col(l("Not allowed"), 1); + else + mes l("Lethal overdrinking: ") + col(l("Allowed"), 2); + + + // GSET_CRAFT_BOUND + // Should players make bound or named items? + if (GSET_CRAFT_BOUND) + mes l("Crafting method: ") + col(l("Account Bound"), 1); + else + mes l("Crafting method: ") + col(l("Named Items"), 2); + + + // TUTORIAL + // Should we show players tutorial info? + if (!TUTORIAL) + mes l("Tutorial Protips: ") + col(l("Disabled"), 1); + else + mes l("Tutorial Protips: ") + col(l("Enabled"), 2); + + + // GSET_NOSCRY + // Should players be allowed to scry your information? + if (GSET_NOSCRY) + mes l("Others scrying you: ") + col(l("Not allowed"), 1); + else + mes l("Others scrying you: ") + col(l("Allowed"), 2); + + + if ($EVENT$ == "Valentine") { + // GSET_VALENTINE_EATONE + // Eat all Chocolate Boxes from Valentine Day event + if (!GSET_VALENTINE_EATALL) + mes l("[Valentine] Eat all chocolate: ") + col(l("Not allowed"), 2); + else + mes l("[Valentine] Eat all chocolate: ") + col(l("Allowed"), 1); + } + + + if (strcharinfo(2) == "Monster King") { + // GSET_AUTORECEIVE_COINS + // Enables/Disable autoreceive strange coins + if (!GSET_AUTORECEIVE_COINS) + mes l("Autoreceive Strange Coins: ") + col(l("Disabled"), 1); + else + mes l("Autoreceive Strange Coins: ") + col(l("Enabled"), 2); + } + + if (@unsaved) { + mes ""; + mesc l("Careful: You have unsaved changes!"), 1; + } + + mes ""; + select + l("Return to User Control Panel"), + l("Toggle Soul Menhir automatic saving"), + l("Toggle Daily Reward screen"), + l("Text Wall Density"), + l("Alchemy Table brewing"), + l("Automatic fishing bait"), + l("Lethal alcohol overdrinking"), + l("Change crafting method"), + l("Show Tutorial Protips"), + l("Others scrying your char data"), + rif($EVENT$ == "Valentine", ("Valentine Eating")), + rif(strcharinfo(2) == "Monster King", ("Toggle Autoreceive Event Coins")); + mes ""; + + switch (@menu) { + case 1: + // Update savepoint if needed + if (@unsaved) { + if (!GSET_SOULMENHIR_MANUAL) savepoint "000-1", 22, 22; + else ResaveRespawn(); + } + break; + case 2: + GSET_SOULMENHIR_MANUAL=!GSET_SOULMENHIR_MANUAL; + @unsaved=true; + break; + case 3: + GSET_DAILYREWARD_SILENT=!GSET_DAILYREWARD_SILENT; break; + case 4: + if (GSET_LONGMENU_DENSITY >= 5) + GSET_LONGMENU_DENSITY=0; + else + GSET_LONGMENU_DENSITY+=1; + break; + case 5: + if (GSET_FIXED_ALCHEMY) { + GSET_FIXED_ALCHEMY=0; + } else { + .@max=(is_sponsor() ? 25 : 10); + mesc l("How many to brew? (%d-%d)", 0, .@max); + input(GSET_FIXED_ALCHEMY, 0, .@max); + GSET_FIXED_ALCHEMY=limit(0, GSET_FIXED_ALCHEMY, 10); + } + break; + case 6: + GSET_FISHING_BAIT = (GSET_FISHING_BAIT ? 0 : 1); break; + case 7: + GSET_ALCOHOL_NOOVERDRINK=!GSET_ALCOHOL_NOOVERDRINK; break; + case 8: + GSET_CRAFT_BOUND=!GSET_CRAFT_BOUND; break; + case 9: + TUTORIAL=!TUTORIAL; break; + case 10: + GSET_NOSCRY=!GSET_NOSCRY; break; + case 11: + GSET_VALENTINE_EATALL=!GSET_VALENTINE_EATALL; break; + case 12: + GSET_AUTORECEIVE_COINS=!GSET_AUTORECEIVE_COINS; break; + } + clear; + } while (@menu != 1); + break; + default: close; break; + } + } while (1); +} + + + +- script @ucp 32767,{ + end; + +OnCall: + + UserCtrlPanel; + closedialog; + end; + +OnInit: + bindatcmd "ucp", "@ucp::OnCall", 0, 99, 0; +} diff --git a/npc/commands/warp.txt b/npc/commands/warp.txt new file mode 100644 index 0000000..4f98ac5 --- /dev/null +++ b/npc/commands/warp.txt @@ -0,0 +1,104 @@ +// TMW2 script +// Evol script, gumi's script +// +// @w atcommand +// warps using anchors or map name +// +// usage: +// @w <map or anchor> [, x [, y]] +// #w "char" <map or anchor> [, x [, y]] +// +// example: +// @w hali +// @w halin +// @w halinarzo +// #w "char" hali + +- script @w 32767,{ + end; + +OnCall: + .@request$ = strtoupper(strip(.@atcmd_parameters$[0])); // sanitize + .@map$ = ""; + .@x = 0; + .@y = 0; + + for (.@i = 0; .@i < .count; .@i += 2) + { + if (.@request$ ~= .aliases$[.@i]) { + sscanf .aliases$[.@i + 1], "%s %d %d", .@map$, .@x, .@y; + break; + } + } + + if (.@map$ == "") { + .@map$ = .@atcmd_parameters$[0]; + } + + if (.@atcmd_parameters$[2] != "") { + .@x = atoi(.@atcmd_parameters$[1]); + .@y = atoi(.@atcmd_parameters$[2]); + } + + // FIXME: here getmapusers() is used only to check if the map exists + // replace this when/if we get a dedicated function for that + if (getmapusers(.@map$) < 0) { + end; // invalid map + } + + while (!checkcell(.@map$, .@x, .@y, cell_chkpass)) + { + if (.@e == 50) end; + .@x = rand(20, 250); + .@y = rand(20, 250); + ++.@e; + } + + if (getmapflag(.@map$, mf_nowarpto) && !(is_admin() || $@GM_OVERRIDE)) { + dispbottom("This map is restricted and cannot be warped to."); + close; + } + cwarp .@map$, .@x, .@y; // XXX: maybe here use a slide_or_warp function + +OnInit: + setarray .aliases$[0], + + // PROLOGUE + "^START|^BEGIN", "000-0 22 24", // starting point + "^NARD|^SHIP", "002-1 53 38", // nard's ship (outside instance) + "^000-0", "000-0 34 17", // Intro Lookout Area + "^000-0-0", "000-0-0 33 26", // Intro B Lookout Area + "^LOOKOUT", "000-0-0 33 26", // Intro B Lookout Area + + // TMW2 + "^TULIM", "003-1 41 48", // Tulishmar + "^CANDOR", "005-1 35 102", // Candor + "^HALI", "009-1 28 33", // Halinarzo + "^HURN", "012-1 83 63", // Hurnscald + "^LOF", "017-1 120 89", // Land Of Fire Village + "^LILIT", "018-5 111 53", // Lilit + "^PORT", "019-2 94 118", // Nivalis Port + "^NIVAL", "020-1 58 65", // Nivalis + "^FROST", "024-1 94 42", // Frostia + "^FORT", "025-1 100 83", // Fortress Town + "^MAGI|^ACADEMY", "027-1 89 83", // Magic Academy + "^ART", "029-0 203 85", // Artis + + // TBR - To Be Removed + "^BOSS", "boss 45 45", // (TBR) Monster King's Throne Room + "^BOT", "botcheck 26 30", // (TBR) Botcheck area + "^HEROES|^HH", "018-2 72 66", // (TBR) Heroes Hold + + // GM Stuff + "^AEROS|^GM", "001-1 235 26", // Floating Island of Aeros (GM Events) + "^ARENA", "001-2 125 222", // Aeros Arena (GM Events) + "^SAULC", "001-3 117 138", // GM Palace + "^EASTER", "001-4 151 157", // Easter Event Map + "^WORK", "001-5 22 79", // Contributor's Cave + "^JAIL|^PRISON", "sec_pri 28 25"; // Jesusalva's Prison (and last line) + + + .count = getarraysize(.aliases$[0]); + + bindatcmd "w", "@w::OnCall", 5, 80, 1; +} diff --git a/npc/commands/welcome.txt b/npc/commands/welcome.txt new file mode 100644 index 0000000..0030447 --- /dev/null +++ b/npc/commands/welcome.txt @@ -0,0 +1,74 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Welcome to all new players on ML! :D + +- script @welcome 32767,{ + end; + +OnCall: + if (!@toeventchk) { + @toeventval1=readparam(Hp); + @toeventval2=readparam(Sp); + @toeventchk=1; + specialeffect FX_CIRCLE, SELF, getcharid(3); + addtimer 4000, "@welcome::OnEffect"; + } + end; + +OnEffect: + @toeventchk=0; + removespecialeffect(FX_CIRCLE, SELF, getcharid(3)); + // Reversions (warp back) + // Not in Candor: Resave your @welcome reversion snippet + if (getmap() != "005-1") { + getmapxy(@welc_map$, @welc_x, @welc_y, 0); + @welc_loc$=LOCATION$; + } else { + // It's defined and you're in Candor, so: Warp back + if (@welc_x && @welc_y) { + warp @welc_map$, @welc_x, @welc_y; + @welc_map$=""; + @welc_x=0; + @welc_y=0; + dispbottom l("%s: Thanks for helping.", "Nard"); + if (@welc_loc$ != "") + LOCATION$=@welc_loc$; + end; + } + // There's no saved coordinates but you're in Candor. + // So warp you back to... Candor? I mean, what? + } + + // Calculate + if ($@WELCOME_TIMER < gettimetick(2)) { + dispbottom l("There are no new players to welcome."); + atcommand "@refresh"; + end; + } else if (readparam(Sp) < @toeventval2) { + dispbottom l("You must not be using mana to do this trip."); + atcommand "@refresh"; + end; + } else if (readparam(Hp) < @toeventval1) { + dispbottom l("You cannot be fighting to do this trip."); + atcommand "@refresh"; + end; + } else if (compare(getmapname(), "001-") || (getmapinfo(MAPINFO_ZONE, getmap())) == "MMO") { + dispbottom l("You currently could not use GM MAGIC to visit Candor."); + atcommand "@refresh"; + end; + } else if (getmapname() == "boss" || getmapname() == "sec_pri" || compare(getmapname(), "000-") || compare(getmapname(), "008-") || compare(getmapname(), "005-") || compare(getmapname(), "006-") || compare(getmapname(), "sore")) { + dispbottom l("You currently could not use GM MAGIC to visit Candor."); + atcommand "@refresh"; + } else { + LOCATION$="Candor"; + warp "005-1", 43, 99; + message strcharinfo(0), l("You are now at Candor."); + } + end; + +OnInit: + bindatcmd "welcome", "@welcome::OnCall", 0, 99, 0; +} + diff --git a/npc/commands/wgm.txt b/npc/commands/wgm.txt new file mode 100644 index 0000000..36a9fdd --- /dev/null +++ b/npc/commands/wgm.txt @@ -0,0 +1,51 @@ +// Author: Jesusalva + +// @wgm atcommand +// High wizardry, NOT for noobs +// +// group lv: 99 +// group char lv: 100 +// log: True +// WARNING: DANGEROUS, may explode server, debug testing only. +// +// usage: +// @callfunc daily_login_bonus_handler +// @doevent @wgm::OnCall +// + +- script @wgm 32767,{ + end; + +OnCall: + .@request$ = strcharinfo(0)+": "; + .@request$ += implode(.@atcmd_parameters$, " "); + atcommand("@request "+.@request$); + end; + +OnCallFunc: + if (!$@GM_OVERRIDE) { + dispbottom col("SEVERE ERROR: COMMAND NOT FOUND. EXECUTING SIMILAR COMMAND...", 1); + die(); + end; + } + .@request$ = ""; + .@request$ += implode(.@atcmd_parameters$, " "); + callfunc .@request$; + end; + +OnDoEvent: + if (!$@GM_OVERRIDE) { + dispbottom col("SEVERE ERROR: COMMAND NOT FOUND. EXECUTING SIMILAR COMMAND...", 1); + die(); + end; + } + .@request$ = ""; + .@request$ += implode(.@atcmd_parameters$, " "); + doevent .@request$; + end; + +OnInit: + bindatcmd "callfunc", "@wgm::OnCallFunc", 99, 100, 1; + bindatcmd "doevent", "@wgm::OnDoEvent", 99, 100, 1; + end; +} diff --git a/npc/config/hairstyle_config.txt b/npc/config/hairstyle_config.txt new file mode 100644 index 0000000..04b73b1 --- /dev/null +++ b/npc/config/hairstyle_config.txt @@ -0,0 +1,28 @@ +// Hairstyle config +// set array of style and colors +// Author: Omatt + +- script hairstyle_config NPC_HIDDEN,{ + + end; + +OnInit: + setarray $@hairstyle$[0], "(none)", "Bald", "Bowl cut", "Combed back", + "Emo", "Mohawk", "Pompadour", "Center parting", "Long and slick", + "Short and curly", "Pigtails", "Long and curly", "Parted", + "Perky ponytail", "Wave", "Mane", "Bun", "Wavy", "Bunches", + "Long ponytail", "Infinitely long", "Choppy", "Wild", "Punk", + "Imperial", "Side strand", "Messy", "Flat ponytail", + "Tapered Nape"; + + setarray $@haircolor$[0], "Off black", "Ash brown", "Dark brown", + "Dark copper", "Auburn brown", "Honey brown", "Copper blonde", + "Golden blonde", "Pure platinum", "Cherry blossom", "Pinky pink", + "Fire red", "Light violet", "Purple plum", "Navy blue", + "Lagoon blue", "Twisted teal", "Spring Green", "Forest Green", + "Silver Grey", "Imperial Blue"; + + setarray $@allraces$[0], "Human", "Human", "Human", "Elf", "Orc", + "Raijin", "Tritan", "Ukar", "Redy", "Savior"; + end; +} diff --git a/npc/config/location.txt b/npc/config/location.txt new file mode 100644 index 0000000..f2db2bf --- /dev/null +++ b/npc/config/location.txt @@ -0,0 +1,35 @@ +// TMW2 Script +// Author: Jesusalva +// Location Config + +- script loc_config 32767,{ + end; + +OnInit: + // TP_FORT TP_BOSSR + setarray $@LOCMASTER_TP, TP_CANDOR,TP_TULIM,TP_HALIN,TP_HURNS, TP_LOF,TP_LILIT,TP_NIVAL, TP_FROST, TP_FORT,TP_ARTIS; + setarray $@LOCMASTER_LOC$, "Candor", "Tulim", "Halin", "Hurns", "LoF", "Lilit", "Nival", "Frostia", "Fort", "Artis"; + setarray $@LOCMASTER_MAP$, "005-1", "003-1", "009-1", "012-1", "017-1", "018-5", "020-1", "024-1", "025-2", "029-0"; + setarray $@LOCMASTER_X, 43, 40, 26, 86, 119, 111, 56, 94, 108, 203; + setarray $@LOCMASTER_Y, 99, 49, 30, 69, 87, 53, 62, 42, 32, 85; + + //debugmes "Locmaster: Index 0: %s [%s.gat (%d, %d)]", $@LOCMASTER_LOC$[0], $@LOCMASTER_MAP$[0], $@LOCMASTER_X[0], $@LOCMASTER_Y[0]; + //debugmes "Locmaster: Index 2: %s [%s.gat (%d, %d)]", $@LOCMASTER_LOC$[2], $@LOCMASTER_MAP$[2], $@LOCMASTER_X[2], $@LOCMASTER_Y[2]; + //debugmes "Locmaster: Index 5: %s [%s.gat (%d, %d)]", $@LOCMASTER_LOC$[5], $@LOCMASTER_MAP$[5], $@LOCMASTER_X[5], $@LOCMASTER_Y[5]; + end; +} + +// Resaves your respawn point +function script ResaveRespawn { + .@i=array_find($@LOCMASTER_LOC$, LOCATION$); + savepoint $@LOCMASTER_MAP$[.@i], $@LOCMASTER_X[.@i], $@LOCMASTER_Y[.@i]; + return; +} + +// Warps you to last visited town +function script ReturnTown { + .@i=array_find($@LOCMASTER_LOC$, LOCATION$); + warp $@LOCMASTER_MAP$[.@i], $@LOCMASTER_X[.@i], $@LOCMASTER_Y[.@i]; + return; +} + diff --git a/npc/config/magic.txt b/npc/config/magic.txt new file mode 100644 index 0000000..ed6a8db --- /dev/null +++ b/npc/config/magic.txt @@ -0,0 +1,558 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script Core Functions +// +// Used for our pseudo-magic. +// These are only helpers, you can add more restrictions and effects freely. +// Important Variables: +// MAGIC_EXP +// Current mana magic experience +// LAST_SKILL +// Last Mana Skill used +// MAGIC_LVL +// Maximum tier of usable magic, capped by Mana Stone + +// AdjustSpellpower(power=100, {target=@skillTarget{, type=HARM_MAGI}}) +function script AdjustSpellpower { + .@power=getarg(0, 100); + .@target=getarg(1, @skillTarget); + .@type=getarg(2, HARM_MAGI); + .@src=getcharid(3); + + // Prevents a possible glitch + if (.@target < 1 || .@target == getcharid(3)) + .@target=0; + + .@dmg=calcdmg(.@src, .@target, .@type); + // Calculation FIX + if (.@type == HARM_MAGI) { + .@dmg*=2; + .@power+=(readparam2(bInt)/2); + } + // Abizit Influence (80%~130% at best, worst shot at perfect ctrl is 105%) + .@dmg = .@dmg * (80 + abizit() * rand2(5,10)) / 100; + .@dmg = .@dmg * .@power / 100; + return .@dmg; +} + +// An alias for simplification +// AdjustAttackpower(power=100, {target=@skillTarget{, type=HARM_PHYS}}) +function script AdjustAttackpower { + .@power=getarg(0, 100); + .@target=getarg(1, @skillTarget); + .@type=getarg(2, HARM_PHYS); + return AdjustSpellpower(.@power, .@target, .@type); +} + + +// SkillID, EXP Points +function script GetManaExp { + .@sk=getarg(0); + .@pt=getarg(1); + .@bonus=rand2(0,getskilllv(TMW2_SAGE)*3/2); + if (LAST_SKILL == .@sk) { + .@pt=limit(0, (.@pt+.@bonus)/3, 1); + .@bonus=0; + } else { + // Update skill memory + LAST_SKILL[4]=LAST_SKILL[3]; + LAST_SKILL[3]=LAST_SKILL[2]; + LAST_SKILL[2]=LAST_SKILL[1]; + LAST_SKILL[1]=LAST_SKILL[0]; + LAST_SKILL[0]=.@sk; + MAGIC_RP+=1; + // Magic RP is gained by switching skills often + } + + // Update Magic EXP + MAGIC_EXP=MAGIC_EXP+.@pt+.@bonus; + callfunc "FYE_Olympics_MX", (.@pt + .@bonus); + return; +} + + +// DEPRECATED: Please do not use in newer scripts. +// It is for Transmigration skill only (@sk-trans needs it) +// SkillID, Mana{, MP per level} +function script MagicCheck { + // PRE EXECUTION + // Load Variables + .@sk=getarg(0); + .@mp=getarg(1); + .@amp=getarg(2,0); + + // Check Skill + if (getskilllv(.@sk) < 1) + return 0; + + // Load mana cost + .@mp=.@mp+getskilllv(.@sk)*.@amp-.@amp; + + // Check mana + if (readparam(Sp) < .@mp) { + dispbottom l("Insufficient mana: @@/@@.", readparam(Sp), .@mp); + return 0; + } + + // Apply mana cost + heal 0, 0-.@mp; + + return 1; +} + + +// SkillID, MobID{, SkillLevelPerMob=2{, Level Override}} +function script SummonMagic { + .@sk=getarg(0); + .@id=getarg(1); + .@adj=getarg(2,2); + .@lv=getarg(3,getskilllv(.@sk)); + + if (.@adj < 1) { + consolewarn "\033[31mInvalid MobPerSkillLevel for SummonMagic (.@adj): "+.@adj+"\033[0m"; + dispbottom l("Invalid parameter specified, blame saulc."); + end; + } + + // Cause effect + // Summoned monsters live from 45 to 60 seconds, and each skill levels grants 10s extra life + // The 35~50 is not a defect, remember skill starts at level 1... + // PS. Abizit makes a variation from 80% to 130% of official values + for (.@i = 0; .@i < (.@lv+(.@adj-1))/.@adj; .@i++) { + .@lifetime=rand(35,50)+.@lv*10; + // Abizit makes lifetime vary (like AdjustSpellpower) + .@lifetime = .@lifetime * (80 + abizit() * rand2(5,10)) / 100; + if (isequippedcnt(AegisShield)) + .@lifetime = .@lifetime * 5 / 2; + .@mids=summon("Summoned Monster", .@id, .@lifetime); + .@bhp=getunitdata(.@mids, UDT_MAXHP); + // Each skill level raises HP in 5% + .@lvx=.@bhp + max(0, (.@lv-1)*.@bhp/20); + // Abizit makes bonus HP vary (like AdjustSpellpower) + .@lvx = .@lvx * (80 + abizit() * rand2(5,10)) / 100; + setunitdata(.@mids, UDT_MAXHP, .@lvx); + setunitdata(.@mids, UDT_HP, .@lvx); + // Reconfigure monster modes + .@opt=getunitdata(.@mids, UDT_MODE); + // Disable looting + if (.@opt & MD_LOOTER) + .@opt=.@opt^MD_LOOTER; + // All summons can suffer knockback + if (.@opt & MD_NOKNOCKBACK) + .@opt=.@opt^MD_NOKNOCKBACK; + // Strip summons from BOSS mode and immunity + if (.@opt & MD_BOSS) + .@opt=.@opt^MD_BOSS; + // Save new options + setunitdata(.@mids, UDT_MODE, .@opt); + } + dispbottom l("All monsters summoned!"); + return; +} + +// areaharm(caster, range, DMG, {type, element, filter, bl}) +// Defaults to HARM_MISC, Ele_Neutral, filter filter_hostile and all BLs +// Valid BL: BL_MOB | BL_PC | BL_HOM | BL_MER +// Do not use: NPC, PET, ELEM +// Range centers on caster (player), implement and use areaharm2 elsewhere +function script areaharm { + .@t=getarg(0); + .@r=getarg(1); + .@d=getarg(2); + .@h=getarg(3, HARM_MISC); + .@e=getarg(4, Ele_Neutral); + .@f$=getarg(5, "filter_hostile"); + .@b=getarg(6, BL_PC | BL_MOB | BL_MER | BL_HOM); + + getmapxy(.@m$, .@x, .@y, getunittype(.@t), .@t); + + .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); + for (.@i = 0; .@i < .@c; .@i++) { + // Filtering + if (!callfunc(.@f$, .@mbs[.@i])) + continue; + harm(.@mbs[.@i], .@d, .@h, .@e, .@t); + specialeffect(FX_ATTACK, AREA, .@mbs[.@i]); + // TODO: Handle MobPt to don't overload timer system? + } + return; +} + + +// rectharm(caster, x, y, DMG, {type, element, filter, bl}) +// Same as areaharm() but causes a rectangle in (x,y) size, instead of a square +function script rectharm { + .@t=getarg(0); + .@rx=getarg(1); + .@ry=getarg(2); + .@d=getarg(3); + .@h=getarg(4, HARM_MISC); + .@e=getarg(5, Ele_Neutral); + .@f$=getarg(6, "filter_hostile"); + .@b=getarg(7, BL_PC | BL_MOB | BL_MER | BL_HOM); + + getmapxy(.@m$, .@x, .@y, getunittype(.@t), .@t); + + .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@rx, .@y-.@ry, .@x+.@rx, .@y+.@ry); + for (.@i = 0; .@i < .@c; .@i++) { + // Filtering + if (!callfunc(.@f$, .@mbs[.@i])) + continue; + harm(.@mbs[.@i], .@d, .@h, .@e, .@t); + specialeffect(FX_ATTACK, AREA, .@mbs[.@i]); + // TODO: Handle MobPt to don't overload timer system? + } + return; +} + + +// mescordialog(text, color, {dialog=1}) +function script mescordialog { + if (getarg(2, true)) + mesc getarg(0), getarg(1); + else + dispbottom col(getarg(0), getarg(1)); + return; +} + +// ShowAbizit({dialog=1}) +function script ShowAbizit { + .@dial=getarg(0, true); + if (.@dial) + mesn l("Current Magic Control"); + + .@val=MAGIC_EXP+rand(-MAGIC_LVL*5, MAGIC_LVL*5); + .@base=((MAGIC_LVL*2)**3); + if (.@val > .@base*5) + mescordialog l("You are perfectly in control of your magic."), 3, .@dial; + else if (.@val > .@base*4) + mescordialog l("You are mostly in control of your magic."), 2, .@dial; + else if (.@val > .@base*3) + mescordialog l("You are somewhat in control of your magic."), 4, .@dial; + else if (.@val > .@base*2) + mescordialog l("Your magic is more powerful than you, but you can control."), 7, .@dial; + else if (.@val > .@base) + mescordialog l("You still are overwhelmed by your magic."), 6, .@dial; + else + mescordialog l("You are completly overwhelmed by your magic."), 1, .@dial; + return; +} + + +// alignment_cansummon() +function script alignment_cansummon { + if (alignment() < 0 && !isequippedcnt(AegisShield)) { + if (!@hatesummon) { + dispbottom l("Nature itself express hate against you!"); + getmapxy(.@m$, .@x, .@y, 0); + // NOTE: .@mob is or may be undefined, so use EntAbomination + .@opo=monster(.@m$, .@x, .@y, "Failed summon", EntAbomination, 1); + unitattack(.@opo, getcharid(3)); + setunitdata(.@opo, UDT_MAXHP, BaseLevel*140); + setunitdata(.@opo, UDT_HP, BaseLevel*140); + @hatesummon=true; + } + return false; + } + return true; +} + +// SK_summon(ID, amount, mexp) +function script SK_summon { + .@mob=getarg(0); + .@amt=getarg(1); + .@mex=getarg(2); + if ($@GM_OVERRIDE || debug) debugmes "Skill "+@skillId+" Lv "+@skillLv; + + // Blocked from summoning magic + if (!alignment_cansummon()) + return; + + if (rand2(5) < abizit()) { + // Summon Magic (with magic level bonus) + SummonMagic(@skillId, .@mob, .@amt, MAGIC_LVL+@skillLv-1, @skillLv); + } else if (rand2(5) < abizit()) { + // Re-roll + dispbottom l("You cannot complete the casting correctly!"); + SummonMagic(@skillId, .@mob, 1, 1, 1); + } else if (abizit() <= 1 && any(true, false, false)) { + // Spell overwhelms you, causing it to be spawned as aggro vs you. (33%) + dispbottom l("The spell takes a mind of its own backfires!"); + getmapxy(.@m$, .@x, .@y, 0); + .@opo=monster(.@m$, .@x, .@y, "Failed summon", .@mob, 1); + unitattack(.@opo, getcharid(3)); + } else { + dispbottom l("The spell fails!"); + } + + // Get a single mana experience point (this is NOT used by Mana Stone) + GetManaExp(@skillId, .@mex); + return; +} + +///////////////////////////////////////// +// RegisterMagic(MSP, Skill, MaxLv, Item, Amount, Class, Cost, {PreReq, PostReq}) +function script RegisterMagic { + .@msp=getarg(0); + .@ski=getarg(1); + .@max=getarg(2); + .@ite=getarg(3); + .@amo=getarg(4); + .@cla=getarg(5); + .@cos=getarg(6); + .@pre=getarg(7, false); + .@pos=getarg(8, false); + + $@MSK_MSPCOST[.@ski]=.@msp; + $@MSK_MAXLV[.@ski]=.@max; + + $@MSK_ITEM[.@ski]=.@ite; + $@MSK_AMOUNT[.@ski]=.@amo; + $@MSK_COST[.@ski]=.@cos; + + $@MSK_CLASS[.@ski]=.@cla; + $@MSK_PREREQ[.@ski]=.@pre; + $@MSK_POSTREQ[.@ski]=.@pos; + + //array_push($@MSK_CLASS[.@cla], .@ski); // 3D Arrays are not supported + array_push($@MSK_MAGIC, .@ski); + return; +} + +- script Magic Load NPC_HIDDEN,{ +OnInit: + // Cleanup in case of reload + deletearray($@MSK_MSPCOST); + deletearray($@MSK_MAXLV); + + deletearray($@MSK_ITEM); + deletearray($@MSK_AMOUNT); + deletearray($@MSK_COST); + + deletearray($@MSK_CLASS); + deletearray($@MSK_PREREQ); + deletearray($@MSK_POSTREQ); + /* RegisterMagic(MSP, Skill, MaxLv, Item, Amount, + Class, Cost, {PreReq, PostReq}) */ + + // Research Points (RP) range: 100~1000 [10k ~ 1M points] + + + + + + //////////////////////// Scholarship + // Mana Wisdom + RegisterMagic(1, TMW2_SAGE, 5, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 25); + // Accumulate Power + RegisterMagic(1, HW_MAGICPOWER, 5, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 25); + // Chanting + RegisterMagic(1, TMW2_CHANT, 5, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 25); + // Transfer Mana + RegisterMagic(1, TMW2_MPTRANSFER, 10, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 5); + + // Windwalker + RegisterMagic(2, SN_WINDWALK, 3, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 25); + // Last Standing Man + RegisterMagic(2, CR_TRUST, 3, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 30); + + // Resurrection + RegisterMagic(3, TMW2_RESURRECT, 10, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 125); + + /* Skillchain */ + // First Aid + RegisterMagic(1, TMW2_FIRSTAID, 10, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 50, false, TMW2_HEALING); + // Healing + RegisterMagic(2, TMW2_HEALING, 10, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 75, TMW2_FIRSTAID, TMW2_MAGNUSHEAL); + // Magnus Healing + RegisterMagic(3, TMW2_MAGNUSHEAL, 10, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 125, TMW2_HEALING, false); + + + /* Skillchain */ + // Provoke + RegisterMagic(1, SM_PROVOKE, 1, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 25, false, EVOL_AREA_PROVOKE); + // Mass Provoke + RegisterMagic(2, EVOL_AREA_PROVOKE, 10, SpellBookPage, 1, + CLASS_SCHOLARSHIP, 30, SM_PROVOKE, false); + + + + + + + + //////////////////////// Physical Sciences + // Ground Strike + RegisterMagic(2, TMW2_GROUNDSTRIKE, 3, FluoPowder, 3, + CLASS_PHYSICAL, 30); + + /* Skillchain */ + // Falkon Punch + RegisterMagic(1, TMW2_FALKONSTRIKE, 10, FluoPowder, 3, + CLASS_PHYSICAL, 25, false, TMW2_SUPREMEATTACK); + // Supreme Attack + RegisterMagic(1, TMW2_SUPREMEATTACK, 10, FluoPowder, 3, + CLASS_PHYSICAL, 25, TMW2_FALKONSTRIKE, KN_AUTOCOUNTER); + // Counter Attack + RegisterMagic(2, KN_AUTOCOUNTER, 5, FluoPowder, 3, + CLASS_PHYSICAL, 75, TMW2_SUPREMEATTACK, false); + + /* Skillchain */ + // Arrow Shower + RegisterMagic(3, TMW2_ARROWSHOWER, 10, FluoPowder, 3, + CLASS_PHYSICAL, 60, false, SN_SHARPSHOOTING); + // Sharpshooter + RegisterMagic(3, SN_SHARPSHOOTING, 1, FluoPowder, 3, + CLASS_PHYSICAL, 50, TMW2_ARROWSHOWER, false); + + + + + + + + //////////////////////// Physical Sub Sciences + // Stunning Strike + RegisterMagic(1, TMW2_STUNNINGSTRIKE, 10, FluoPowder, 2, + CLASS_PHYSICAL, 25, false, false); + + /* Skillchain */ + // Brawling + RegisterMagic(1, TMW2_BRAWLING, 10, FluoPowder, 1, + CLASS_PHYSICAL, 18, false, TMW2_BEARSTRIKE); + // Bear Strike + RegisterMagic(2, TMW2_BEARSTRIKE, 10, FluoPowder, 2, + CLASS_PHYSICAL, 47, TMW2_BRAWLING, TMW2_ALLINONE); + // All In One + RegisterMagic(3, TMW2_ALLINONE, 10, FluoPowder, 3, + CLASS_PHYSICAL, 72, TMW2_BEARSTRIKE, false); + + + + + + + + //////////////////////// Destructive Magic + // Fire Walk + RegisterMagic(2, SO_FIREWALK, 2, Quill, 1, + CLASS_DESTRUCTION, 75); + + /* Skillchain */ + // Fire Arrow + RegisterMagic(1, TMW2_FIREARROW, 10, Quill, 1, + CLASS_DESTRUCTION, 25, false, TMW2_FIREBALL); + // Fire Ball + RegisterMagic(2, TMW2_FIREBALL, 10, Quill, 2, + CLASS_DESTRUCTION, 50, TMW2_FIREARROW, TMW2_ARMAGEDDON); + // Armageddon + RegisterMagic(3, TMW2_ARMAGEDDON, 5, Quill, 3, + CLASS_DESTRUCTION, 75, TMW2_FIREBALL, false); + + /* Skillchain */ + // Napalm Beat + RegisterMagic(1, TMW2_NAPALMBEAT, 10, Quill, 1, + CLASS_DESTRUCTION, 25, false, TMW2_HOLYLIGHT); + // Holy Light + RegisterMagic(2, TMW2_HOLYLIGHT, 10, Quill, 2, + CLASS_DESTRUCTION, 50, TMW2_NAPALMBEAT, TMW2_JUDGMENT); + // Judgement + RegisterMagic(3, TMW2_JUDGMENT, 5, Quill, 3, + CLASS_DESTRUCTION, 75, TMW2_HOLYLIGHT, false); + + /* Skillchain */ + // Frost Diver + RegisterMagic(1, TMW2_FROSTDIVER, 10, Quill, 1, + CLASS_DESTRUCTION, 25, false, TMW2_FROSTNOVA); + // Frost Nova + RegisterMagic(2, TMW2_FROSTNOVA, 10, Quill, 2, + CLASS_DESTRUCTION, 50, TMW2_FROSTDIVER, TMW2_NILFHEIM); + // Nilfheim + RegisterMagic(3, TMW2_NILFHEIM, 5, Quill, 3, + CLASS_DESTRUCTION, 75, TMW2_FROSTNOVA, false); + + /* Skillchain */ + // Magic Strike + RegisterMagic(1, TMW2_MAGICSTRIKE, 10, Quill, 1, + CLASS_DESTRUCTION, 20, false, TMW2_LIGHTNINGBOLT); + // Lightning Bolt + RegisterMagic(2, TMW2_LIGHTNINGBOLT, 10, Quill, 2, + CLASS_DESTRUCTION, 45, TMW2_MAGICSTRIKE, TMW2_TEMPEST); + // Tempest + RegisterMagic(3, TMW2_TEMPEST, 5, Quill, 3, + CLASS_DESTRUCTION, 65, TMW2_LIGHTNINGBOLT, false); + + /* Skillchain */ + // Meteor Strike + RegisterMagic(1, TMW2_METEORSTRIKE, 10, Quill, 1, + CLASS_DESTRUCTION, 25, false, TMW2_METEORSHOWER); + // Meteor Shower + RegisterMagic(2, TMW2_METEORSHOWER, 10, Quill, 2, + CLASS_DESTRUCTION, 50, TMW2_METEORSTRIKE, TMW2_GAIABREAK); + // Gaia Break + RegisterMagic(3, TMW2_GAIABREAK, 5, Quill, 3, + CLASS_DESTRUCTION, 75, TMW2_METEORSHOWER, false); + + + + + + + + //////////////////////// Trickmaster + /* RegisterMagic(MSP, Skill, MaxLv, Item, Amount, + Class, Cost, {PreReq, PostReq}) */ + RegisterMagic(1, TMW2_MANABOMB, 10, SulfurPowder, 1, + CLASS_TRICKS, 20); + RegisterMagic(1, TF_BACKSLIDING, 1, Lockpicks, 1, + CLASS_TRICKS, 24); + RegisterMagic(1, MG_FIREWALL, 10, Lockpicks, 1, + CLASS_TRICKS, 25); + RegisterMagic(1, AC_VULTURE, 2, Lockpicks, 1, + CLASS_TRICKS, 250); + RegisterMagic(1, SA_FREECAST, 1, Lockpicks, 1, + CLASS_TRICKS, 25); + RegisterMagic(1, ALL_FULL_THROTTLE, 1, Lockpicks, 1, + CLASS_TRICKS, 25); + RegisterMagic(2, GC_DARKILLUSION, 3, Lockpicks, 1, + CLASS_TRICKS, 25); + RegisterMagic(2, NV_TRICKDEAD, 1, Lockpicks, 1, + CLASS_TRICKS, 25); + + + + + + + + //////////////////////// Other: Summonning + + + + + + + + //////////////////////// Other: Misc + // Charged Shot + RegisterMagic(0, TMW2_CHARGEDARROW, 10, Manapple, 1, + CLASS_OTHER, 25); + // Study + RegisterMagic(0, TMW2_STUDY, 1, Manapple, 1, + CLASS_OTHER, 400); + + end; +} + diff --git a/npc/config/traps.txt b/npc/config/traps.txt new file mode 100644 index 0000000..1b43e5c --- /dev/null +++ b/npc/config/traps.txt @@ -0,0 +1,82 @@ +// TMW2 Scripts +// Author: +// The Mana World Brazil +// Jesusalva +// Description: +// Traps and other functions. + +// SteelTrap( {damage=80%}, {delay=15s}, {stun=3s}, {npcname=auto} ) +function script SteelTrap { + .@dmg=getarg(0, 80); + .@delay=getarg(1, 15); + .@stun=getarg(2, 3); + .@n$=getarg(3, strnpcinfo(0)); + + // It was disarmed + if (getnpctimer(0) == 0) + { + initnpctimer; + setnpcdisplay .@n$, NPC_TRAP_ONLINE; + return; + } + + // Fire!! + setnpctimer 9000; + setnpcdisplay .@n$, NPC_TRAP_TRIGGERED; + + // Boom - Hurt players and/or stun monsters + // This means you can - and SHOULD - lead Forains into these traps + if (playerattached()) + { + percentheal -(.@dmg), 0; + } + else + { + .@stun*=1000; + sc_start SC_WALKSPEED,(.@delay*1000),60; + sc_start SC_STUN,rand(.@stun,.@stun*3),0; + } + + // A minor special effect and we're done. + specialeffect 11; + return; +} + + +// Unlike SteelTrap, presents same behavior and absolute damage +// IronTrap( {damage=100}, {delay=15s}, {stun=3s}, {npcname=auto} ) +function script IronTrap { + .@dmg=getarg(0, 100); + .@delay=getarg(1, 15); + .@stun=getarg(2, 3); + .@n$=getarg(3, strnpcinfo(0)); + + // It was disarmed + if (getnpctimer(0) == 0 && .@delay) + { + initnpctimer; + setnpcdisplay .@n$, NPC_TRAP_ONLINE; + return; + } + + // Fire!! + setnpctimer 9000; + setnpcdisplay .@n$, NPC_TRAP_TRIGGERED; + + // Boom - Hurt and Stun (only works on players and mobs) + .@stun*=1000; + sc_start SC_STUN, rand2(.@stun,.@stun*3), 0; + .@gid=(playerattached() ? playerattached() : mobattached()); + + // Just to be sure + if (.@gid) + harm(.@gid, .@dmg, HARM_MISC); + else + Exception("IronTrap \""+.@n$+"\" called without GID!", RB_DEBUGMES); + + // A minor special effect and we're done. + specialeffect 11; + return; +} + + diff --git a/npc/craft/alchemy.txt b/npc/craft/alchemy.txt new file mode 100644 index 0000000..ec26800 --- /dev/null +++ b/npc/craft/alchemy.txt @@ -0,0 +1,147 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Alchemy System (Player, Guild, NPC) +// Notes: +// Base for Evol MR + +// Usage: AlchemySystem ({scope}) +// Scopes: CRAFT_NPC, CRAFT_PLAYER, CRAFT_GUILD +// If an invalid scope is passed, .knowledge won't be set but will be required +// Returns true on success, false on failure +function script AlchemySystem { + // Set .scope, .knowledge and .success + .scope=getarg(0, CRAFT_PLAYER); + if (.scope == CRAFT_PLAYER) + { + copyarray(.knowledge,RECIPES_ALCHEMY,getarraysize(RECIPES_ALCHEMY)); + } + else if (.scope == CRAFT_GUILD) + { + copyarray( .knowledge,getd("$RECIPES_ALCHEMY_"+getcharid(2)),getarraysize(getd("$RECIPES_ALCHEMY_"+getcharid(2))) ); + } + .success=false; + + setskin "craft2"; + .@var$ = requestcraft(2); + .@craft = initcraft(.@var$); + .@entry = findcraftentry(.@craft, CRAFT_ALCHEMY); + if (debug || $@GM_OVERRIDE) mes "found craft entry: " + .@entry; + if (debug || $@GM_OVERRIDE) mes "knowledge value: " + .knowledge[.@entry]; + if (.@entry < 0) { + .success=false; + } else { + if (.scope == CRAFT_NPC || .knowledge[.@entry]) { + if (GSET_FIXED_ALCHEMY) { + .@m=limit(1, GSET_FIXED_ALCHEMY, 25); + } else { + .@max=(is_sponsor() ? 25 : 10); + mesc l("How many to brew? (%d-%d)", 1, .@max); + input(.@m, 1, .@max); + } + // Alchemy loop + .@i=0; + while (.@i < .@m) { + .@s=validatecraft(.@craft); + // Could not validate (not enough resources) + if (!.@s) { + mesc l("Not crafting - insufficient materials!"), 1; + break; + } + .@s=usecraft(.@craft); + .@i++; + callfunc "FYE_Olympics_AL"; + // Exploiting?! + if (!.@s) + break; + } + .success=true; + } else { + .success=false; + } + } + deletecraft .@craft; + setskin ""; + return .success; +} + +/* +Alchemy can rely in cross-building +Where a weaker potion is base for a stronger one +Standard Duration = 2 minutes ~ 5 minutes + +Reagents: + Water + ...Eggs? + ...Milk? + Nymph Poison + Death Potion + Manapple + +Products: + Tea (Chamomile, Spearmint, Oolong, Jasmine, Yerba Mate?) + → Argaes Water + «Herbal Reagent» +OK Coffee (Shadow Herb + Tonori Water) +OK Piberries Infusion (Piberries + Curshroom) +OK Atropos Mixture (Lachesis Brew + Clotho Liquor) +OK Death Potion (Dragonfruit + Nightshade Tea) +OK Smoke Grenade (Cactus pot + Coal) +OK Grenade (Cactus pot + Sulfur Powder) +OK Scented Grenade (Cactus pot + Moss) +OK Haste Potion (Plushshroom) +OK Strength Potion (Chagashroom) + Return Potion (Hurnscald Recipe => Ocean Croc Claw + Hard Spike? Grass Seeds?) +OK Status Reset (Curshroom + Mana Piou Feather) +OK Homun Stat Reset (Curshroom + Manapple) +OK Move Speed (Gem Powder + Fluor Powder) +OK Precision (Piberries + Mt. Snake Egg) +OK Dodge Potion (Piberries + Snake Egg) +OK Luck, Dex, Int, Vit, Agi (Gems + Tea) +OK Sacred Life (Golden Apple + Elixir of Life) +OK Sacred Mana (Golden Apple + Celestia Tea) +OK Sacred Revival (Sacred Life + Sacred Mana) +OK Broken Warp Crystal? (Wurtizite + Black Mamba Skin) +OK Magic Apple? (Divine Apple + Manapple? Death Potion? Sacred Life/Revival?) +OK Purification Potion (Nymph Poison + Sacred Life) +OK Iced Bottle (Tonori W. + Argaes W.) +OK Insurance Contract (» Insurance?) (Quill + Reed Bundle) +OK Insurance (Quill + Death Potion) + Mysterious Fruit? (Legendary) + +For all Scrolls: Quill + ? (depends on scroll itself) + » Summon Scrolls (Based on mob parts, 1× mob?) + → alignment_cansummon() + SummonMagic() or summon() directly + → Criteria between weak/strong version is alignment + → When aligned, scrolls always summon strongest ver + → Level must be equal or superior to strongest, tho + » Maggot/Giant Maggot: Bug Leg (Lv 40) + » CaveMaggot: Maggot Slime + » Green Dragon/Nightmare: Dragon Scales (Lv 105) + » Wolvern: Wolvern Pelt + » Moggun/Yeti: Frozen Yeti Tear (Lv 60) + » Terranite/T.Prot.: Terranite Ore (Lv 90) + » Magnus Heal (Lifestone) + » Area Provoke? → Scent grenade? + » Guild Skills? +OK » ScrollAngelLightA ( + ) +OK » ScrollBattlePlansA ( + ) +OK » ScrollDefenseBlessA ( + ) +OK » ScrollCriticalFortuneA ( + ) + → TODO: Kyrie Eleison (Absolute Shield) + → With self-stun, makes you a temporary wall? + → Maybe a item of Quill + LoF Coin for guild skills? (LoF Quill) + + // Skills for Aegis Shield, all beyond maximum level + // Slimes, Snakes, Fairies, Darth Duck, Mr. Prickel + // PoisonS.Mushroom + // TODO: Lizards, (Black)Scorpions, Moonshroom, Black Mamba, Centaur + skill TMW2_HALHISS, 10; + skill TMW2_KALSPIKE, 9; + skill TMW2_LIMERIZER, 10; + skill TMW2_FAIRYKINGDOM, 9; + skill TMW2_DUCKY, 10; + skill TMW2_FAIRYEMPIRE, 10; + +*/ + diff --git a/npc/craft/options.txt b/npc/craft/options.txt new file mode 100644 index 0000000..1ab7203 --- /dev/null +++ b/npc/craft/options.txt @@ -0,0 +1,1198 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Item Option System +// Notes: +// Awarded for crafters and their own base skill tree system + +// Player knowledge structure +// CRAFTSYS[ SKILL_SCOPE ] = SKILL_LV + +// Player craft skills selection: +// CRAFTSYS_CURRENT + +// Generate() takes the scope and finds out the skills on the group +// It'll fill the following variables: +// @csys_attr → Available attributes +// @csys_penalty → Penalty attribute array +// +// use getarraysize(@csys_attr) to know how many are there. +// Players can active the bonus groups they want to use + +// csys_equip( ) +// Returns a bonus from equips (max: 1) +function script csys_equip { + // Same as: isequippedcnt(BlacksmithAxe{, BlacksmithHelmet, etc.}) + return (isequippedcnt(BlacksmithAxe, Monocle, DemureAxe)); +} + +// csys_Generate( cr_id{, preserve, override} ) +// Return average level +function script csys_Generate { + .@gid=getarg(0); + if (!getarg(1, false)) { + deletearray(@csys_attr); + deletearray(@csys_penalty); + } + .@OVR=getarg(2, false); + //.@lvl=getd("CRAFTSYS["+.@gid+"]"); + .@avg=0; + .@stk=0; + + ///////////////////////////////////////////////////////////// + // Basic tier + if (.@gid & CRGROUP_BASE) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_BASE]); + if (.@lvl >= 1) { + array_push(@csys_attr, VAR_STRAMOUNT); + array_push(@csys_attr, VAR_INTAMOUNT); + } + if (.@lvl >= 3) { + array_push(@csys_attr, VAR_DEXAMOUNT); + array_push(@csys_attr, VAR_MAXHPAMOUNT); + } + if (.@lvl >= 5) { + array_push(@csys_attr, VAR_AGIAMOUNT); + array_push(@csys_attr, VAR_MAXSPAMOUNT); + } + if (.@lvl >= 7) { + array_push(@csys_attr, VAR_LUKAMOUNT); + array_push(@csys_attr, VAR_VITAMOUNT); + } + + if (rand2(60) < .@lv) + array_push(@csys_penalty, CLASS_DAMAGE_BOSS_TARGET); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + + ///////////////////////////////////////////////////////////// + // First tier + if (.@gid & CRGROUP_ATK) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_ATK]); + if (.@lvl >= 1) { + array_push(@csys_attr, VAR_ATTPOWER); + array_push(@csys_attr, VAR_ATTMPOWER); + } + if (.@lvl >= 5) { + array_push(@csys_attr, VAR_MAGICATKPERCENT); + array_push(@csys_attr, VAR_ATKPERCENT); + } + array_push(@csys_penalty, VAR_VITAMOUNT); + array_push(@csys_penalty, VAR_MAXHPAMOUNT); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_DEF) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_DEF]); + if (.@lvl >= 1) { + array_push(@csys_attr, VAR_ITEMDEFPOWER); + array_push(@csys_attr, VAR_MDEFPOWER); + } + if (.@lvl >= 5) { + array_push(@csys_attr, DAMAGE_CRI_USER); + array_push(@csys_attr, RANGE_ATTACK_DAMAGE_USER); + } + array_push(@csys_penalty, VAR_DEXAMOUNT); + array_push(@csys_penalty, VAR_INTAMOUNT); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_ACC) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_ACC]); + if (.@lvl >= 1) { + array_push(@csys_attr, VAR_HITSUCCESSVALUE); + } + if (.@lvl >= 5) { + array_push(@csys_attr, VAR_CRITICALRATE); + } + if (.@lvl >= 10) { + array_push(@csys_attr, VAR_CRITICALSUCCESSVALUE); + } + array_push(@csys_penalty, VAR_LUKAMOUNT); + array_push(@csys_penalty, VAR_MDEFPOWER); + array_push(@csys_penalty, VAR_ITEMDEFPOWER); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_EVD) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_EVD]); + if (.@lvl >= 1) { + array_push(@csys_attr, VAR_AVOIDSUCCESSVALUE); + } + if (.@lvl >= 5) { + array_push(@csys_attr, VAR_PLUSAVOIDSUCCESSVALUE); + } + array_push(@csys_penalty, VAR_ATTPOWER); + array_push(@csys_penalty, VAR_ATTMPOWER); + array_push(@csys_penalty, IOPT_CRITDMG); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + + ///////////////////////////////////////////////////////////// + // Second tier + if (.@gid & CRGROUP_REGEN) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_REGEN]); + if (.@lvl >= 1) { + array_push(@csys_attr, VAR_HPACCELERATION); + } + if (.@lvl >= 5) { + array_push(@csys_attr, VAR_SPACCELERATION); + } + array_push(@csys_penalty, VAR_PLUSASPD); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_SPEED) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_SPEED]); + if (.@lvl >= 1) { + array_push(@csys_attr, VAR_PLUSASPD); + } + if (.@lvl >= 3) { + array_push(@csys_attr, VAR_PLUSASPDPERCENT); + } + if (.@lvl >= 5) { + array_push(@csys_attr, IOPT_WALKSPEED); + } + array_push(@csys_penalty, VAR_MAXSPAMOUNT); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_DOUBLE) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_DOUBLE]); + if (.@lvl >= 1) { + array_push(@csys_attr, IOPT_CRITDMG); + } + if (.@lvl >= 5) { + array_push(@csys_attr, IOPT_DOUBLEATTACK); + } + array_push(@csys_penalty, RANGE_ATTACK_DAMAGE_USER); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_MAXPC) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_MAXPC]); + if (.@lvl >= 1) { + array_push(@csys_attr, VAR_MAXHPPERCENT); + array_push(@csys_attr, VAR_MAXSPPERCENT); + } + if (.@lvl >= 5) { + array_push(@csys_attr, CLASS_DAMAGE_BOSS_USER); + } + array_push(@csys_penalty, DAMAGE_CRI_USER); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + + ///////////////////////////////////////////////////////////// + // Third tier + if (.@gid & CRGROUP_SCRESIST) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_SCRESIST]); + if (.@lvl >= 1) { + array_push(@csys_attr, IOPT_SCRESIST_POISON); + } + if (.@lvl >= 2) { + array_push(@csys_attr, IOPT_SCRESIST_SILENCE); + } + if (.@lvl >= 3) { + array_push(@csys_attr, IOPT_SCRESIST_BLIND); + } + if (.@lvl >= 4) { + array_push(@csys_attr, IOPT_SCRESIST_CURSE); + } + array_push(@csys_penalty, VAR_CRITICALSUCCESSVALUE); + array_push(@csys_penalty, IOPT_CRITDMG); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_SCINFLICT) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_SCINFLICT]); + if (.@lvl >= 1) { + array_push(@csys_attr, IOPT_SCPROVOKE_POISON); + } + if (.@lvl >= 2) { + array_push(@csys_attr, IOPT_SCPROVOKE_SILENCE); + } + if (.@lvl >= 3) { + array_push(@csys_attr, IOPT_SCPROVOKE_BLIND); + } + if (.@lvl >= 4) { + array_push(@csys_attr, IOPT_SCPROVOKE_CURSE); + } + array_push(@csys_penalty, IOPT_SCRESIST_POISON); + array_push(@csys_penalty, IOPT_SCRESIST_SILENCE); + array_push(@csys_penalty, IOPT_SCRESIST_BLIND); + array_push(@csys_penalty, IOPT_SCRESIST_CURSE); + array_push(@csys_penalty, VAR_MAXHPAMOUNT); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_MANAUSE) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_MANAUSE]); + if (.@lvl >= 1) { + array_push(@csys_attr, SP_DRAIN); + } + if (.@lvl >= 5) { + array_push(@csys_attr, DEC_SP_CONSUMPTION); + } + array_push(@csys_penalty, VAR_ATTPOWER); + array_push(@csys_penalty, VAR_ITEMDEFPOWER); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + if (.@gid & CRGROUP_BOSSATK) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_BOSSATK]); + if (.@lvl >= 1) { + array_push(@csys_attr, HP_DRAIN); + } + if (.@lvl >= 5) { + array_push(@csys_attr, CLASS_DAMAGE_BOSS_TARGET); + } + array_push(@csys_penalty, VAR_AVOIDSUCCESSVALUE); + array_push(@csys_penalty, VAR_PLUSAVOIDSUCCESSVALUE); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + + ///////////////////////////////////////////////////////////// + // Final tier (needs minimum lv 3) + if (.@gid & CRGROUP_FINAL) { + .@lvl=(.@OVR ? 100 : CRAFTSYS[CRGROUP_FINAL]); + if (.@lvl >= 1) { + array_push(@csys_attr, IOPT_EXPGAIN); + } + if (.@lvl >= 3) { + array_push(@csys_attr, IOPT_RICHNESS); + } + if (.@lvl >= 5) { + array_push(@csys_attr, IOPT_SPLASHDAMAGE); + } + array_push(@csys_penalty, IOPT_WALKSPEED); + + // Update averages + .@avg+=.@lvl; + .@stk+=1; + } + + /////////////////////////////// + // Return the average level + if (!.@stk) + return 0; + return (.@avg/.@stk); +} + +// Confirms if player really wants to tweak a craft. +// Do not cast after new crafts. Returns false to stop script. +// csys_Confirm( invindex ) +function script csys_Confirm { + .@id=getarg(0); + + // Sanitize input + if (.@id < 0) + return false; + + // *getequipisenableopt(<equipment slot>) → cannot use here + // Not an equipment + if (!getiteminfo(.@id, ITEMINFO_LOC)) + return false; + + mesc l("Really try to tweak this item? All current options will be deleted."); + mesc l("NOTE: You're tweaking a(n): @@", getinvindexlink(.@id)); + next; + if (askyesno() == ASK_NO) + return false; + + return true; +} + +// Check if you'll have success in applying options or not +// Returns true if you was successful, and also cleans previous options +// If you only want cleaning, just disregard the output. +// csys_Check( invindex{, base} ) +function script csys_Check { + .@id=getarg(0); + .@base=getarg(1, 40000); + + // Clear all five options + setitemoptionbyindex(.@id, 0, 0, 0); + setitemoptionbyindex(.@id, 1, 0, 0); + setitemoptionbyindex(.@id, 2, 0, 0); + setitemoptionbyindex(.@id, 3, 0, 0); + setitemoptionbyindex(.@id, 4, 0, 0); + + // Base Success Rate is: 40% + 5% each craft skill level + .@base+=(getskilllv(TMW2_CRAFT)*500); + + // Bonus from equips: 4% each + .@base+=csys_equip()*400; + + // Make the roll + if (rand(10000) < .@base) + return true; + return false; +} + +// csys_Multiplier( cr_id ) +// Returns a multiplier for bonus (it can be zero) +function script csys_Multiplier { + .@sk=getarg(0); + switch (.@sk) { + case IOPT_SPLASHDAMAGE: + return 0; + case IOPT_WALKSPEED: + case IOPT_RICHNESS: + return 2; + case VAR_STRAMOUNT: + case VAR_AGIAMOUNT: + case VAR_INTAMOUNT: + case VAR_DEXAMOUNT: + case VAR_LUKAMOUNT: + case VAR_CRITICALSUCCESSVALUE: + return 4; + case VAR_MAXHPPERCENT: + case VAR_MAXSPPERCENT: + case VAR_VITAMOUNT: + case HP_DRAIN: + case SP_DRAIN: + case IOPT_DOUBLEATTACK: + case VAR_PLUSAVOIDSUCCESSVALUE: + case IOPT_EXPGAIN: + case VAR_CRITICALRATE: + case DEC_SP_CONSUMPTION: + case VAR_PLUSASPDPERCENT: + case VAR_MAGICATKPERCENT: + case VAR_ATKPERCENT: + return 5; + case IOPT_SCRESIST_POISON: + case IOPT_SCRESIST_SILENCE: + case IOPT_SCRESIST_CURSE: + case IOPT_SCRESIST_BLIND: + return 15; + case VAR_MAXSPAMOUNT: + return 25; + case VAR_MAXHPAMOUNT: + return 35; + default: + return 10; + } + return 0; +} + +// Remove problematic bonuses from armors +// Use getiteminfo before +// csys_ArmorFix( item{, perfect=False} ) +function script csys_ArmorFix { + // Rare bonus + if (rand2(100) >= 5 && !getarg(1, false)) + array_remove(@csys_attr, IOPT_SPLASHDAMAGE); + + // Sublevel + if (getiteminfo(getarg(0), ITEMINFO_ELV) < 20) { + array_remove(@csys_attr, IOPT_SPLASHDAMAGE); + array_remove(@csys_attr, IOPT_CRITDMG); + } + + // Remove bonuses + array_remove(@csys_attr, IOPT_WALKSPEED); + array_remove(@csys_attr, HP_DRAIN); + array_remove(@csys_attr, SP_DRAIN); + array_remove(@csys_attr, IOPT_DOUBLEATTACK); + array_remove(@csys_attr, VAR_CRITICALSUCCESSVALUE); + // VAR_PLUSASPDPERCENT and VAR_PLUSASPD ? + // Remove penalties + array_remove(@csys_penalty, VAR_ITEMDEFPOWER); + array_remove(@csys_penalty, VAR_MDEFPOWER); + + // If the options were wiped, add a random one + if (getarraysize(@csys_attr) == 0) + array_push(@csys_attr, VAR_MAXHPAMOUNT); + + // Save for csys_BonusCalc + @csysArmor=CSYS_ARMOR; + + // Shields + if (getiteminfo(getarg(0), ITEMINFO_LOC) == EQP_HAND_L) + @csysArmor=@csysArmor|CSYS_SHIELD; + + // Aegis Shield is special and is not classified as armor + if (compare("aegis shield", strtolower(getitemname(getarg(0))))) + @csysArmor=@csysArmor^CSYS_ARMOR; + + // Special sets + if (compare("savior", strtolower(getitemname(getarg(0))))) + @csysArmor=@csysArmor|CSYS_SAVIOR; + + // Legendary Weapons, this formula is hardcoded in C + if (is_between(3600, 3610, getarg(0))) + @csysArmor=@csysArmor|CSYS_LEGENDARY; + return; +} + +// Update problematic bonuses for weapons +// Use getiteminfo before +// csys_WeaponFix( {item} ) +function script csys_WeaponFix { + .@sub=getiteminfo(getarg(0,Acorn), ITEMINFO_SUBTYPE); + @csysArmor=0; + + // Remove the defense options + array_remove(@csys_attr, VAR_ITEMDEFPOWER); + array_remove(@csys_attr, VAR_MDEFPOWER); + + // If the options were wiped, add a random one + if (getarraysize(@csys_attr) == 0) + array_push(@csys_attr, VAR_MAXHPAMOUNT); + + // Weapon Subtype + if (.@sub == W_FIST || .@sub == W_KNUCKLE) + @csysArmor=@csysArmor|CSYS_BRAWLING; + else if (.@sub == W_2HSWORD || .@sub == W_2HSPEAR || + .@sub == W_2HAXE || .@sub == W_2HMACE || + .@sub == W_2HSTAFF) + @csysArmor=@csysArmor|CSYS_ZWEIHANDER; + else if (.@sub == W_BOW || .@sub == W_REVOLVER || + .@sub == W_RIFLE || .@sub == W_GATLING || + .@sub == W_SHOTGUN || .@sub == W_GRENADE) + @csysArmor=@csysArmor|CSYS_RANGED; + else if (.@sub == W_STAFF || .@sub == W_BOOK) + @csysArmor=@csysArmor|CSYS_MAGICAL; + else if (.@sub == W_KATAR) + @csysArmor=@csysArmor|CSYS_SPECIAL; + else + @csysArmor=@csysArmor|CSYS_OTHER; + + // Special sets + if (compare("savior", strtolower(getitemname(getarg(0))))) + @csysArmor=@csysArmor|CSYS_SAVIOR; + + // Legendary Weapons, this formula is hardcoded in C + if (is_between(3600, 3610, getarg(0))) + @csysArmor=@csysArmor|CSYS_LEGENDARY; + + // Lightbringer have even higher bonuses + if (getarg(0) == Lightbringer) + @csysArmor=@csysArmor|CSYS_SAVIOR; + + return; +} + +// csys_BonusCalc( lv1, lv2, vartp{, equip lvl, skip=false} ) +// Calculates the due bonus +function script csys_BonusCalc { + .@craft=getarg(0); + .@skill=getarg(1); + .@var=getarg(2); + .@eqlv=getarg(3, 0); + .@skip=getarg(4, false); + + .@mult=csys_Multiplier(.@var); + .@avmult=(.@craft+.@skill)*.@mult; + + .@avg=.@avmult/10; + // Equip Level Cap + if (!(@csysArmor & CSYS_LEGENDARY)) + .@avg=.@avg*(5+min(5, .@eqlv/20))/10; + // Roll or no roll + if (!.@skip) { + .@base=rand2(1, .@avg+1); + + // Re-roll if you got a too bad result: + // Each equip level will yield 0.2% reroll + // Means a lv 100 equip gets 20% of grace-reroll. + // By default, this rule is skipped for maluses! + if (.@base < (.@avg+1)*.@eqlv/500) + .@base=rand2(1, .@avg+1); + + // If you are in the upper 70%, we do a re-roll + // It usually will lower the result, but is up to luck + if (.@base >= (.@avg+1)*7/10) + .@base=rand2(1, .@avg+1); + + // Bonus grace reroll if crafting is maxed at 10 (SCRIPT only) + if (.@craft >= 10 && .@base < (.@avg+1)*.@eqlv/500) { + .@base=rand2(1, .@avg+1); + } + } else { + .@base=rand2(max(1, .@avg*8/10), .@avg+1); + } + + //////////////////////////////////// + // Legendary Weapon? Effects +50% + if (@csysArmor & CSYS_LEGENDARY) + .@base=max(1, .@base*3/2); + + // Savior Set? Effects +20% + if (@csysArmor & CSYS_SAVIOR) + .@base=max(1, .@base*6/5); + + //////////////////////////////////// + // Normal Attack for 2H? + if (.@var == VAR_ATTPOWER || .@var == VAR_ATKPERCENT) { + // Two Hands/Bows: +50% + if ((@csysArmor & CSYS_ZWEIHANDER) || (@csysArmor & CSYS_RANGED)) + .@base=max(1, .@base*3/2); + // Brawling: +40% + else if (@csysArmor & CSYS_BRAWLING) + .@base=max(1, .@base*7/5); + } + + // Similar rule but for MATK and Wands + if (.@var == VAR_MAGICATKPERCENT || .@var == VAR_ATTMPOWER) { + // Magical: +50% + if (@csysArmor & CSYS_MAGICAL) + .@base=max(1, .@base*3/2); + // Brawling: +25% + else if (@csysArmor & CSYS_BRAWLING) + .@base=max(1, .@base*5/4); + } + + //////////////////////////////////// + // Armor? Cap it to 25% + if (@csysArmor & CSYS_ARMOR) + .@base=max(1, .@base/4); + + // HP/DEF/MDEF for shields? Revert the cap and round it + if (.@var == VAR_ITEMDEFPOWER || .@var == VAR_MAXHPAMOUNT || + .@var == VAR_MDEFPOWER) { + if (@csysArmor & CSYS_SHIELD) + .@base*=4; + } + return .@base; + +} + +// Attribute item options +// Does NOT performs success chance check, and can be used by NPC +// csys_Apply( invindex{, lvl, scope} ) +function script csys_Apply { + .@id=getarg(0); + .@lv=getarg(1, getskilllv(TMW2_CRAFT))+csys_equip(); + .@sc=getarg(2, CRAFTSYS_CURRENT); + + .@lv2=csys_Generate(.@sc); + // @csys_attr → Available attributes + // @csys_penalty → Penalty attribute array + + // Remove weapon-only bonuses if it is armor + delinventorylist(); + getinventorylist(); + .@itemid=@inventorylist_id[.@id]; + if (getiteminfo(.@itemid, ITEMINFO_TYPE) != IT_WEAPON) + csys_ArmorFix(.@itemid); + else + csys_WeaponFix(.@itemid); + .@eqplv=getiteminfo(.@itemid, ITEMINFO_ELV); + + // Shuffle the arrays + array_shuffle(@csys_attr); + array_shuffle(@csys_penalty); + + // How many bonuses we'll have? Never more than 3 bonus and 2 onus. + .@max_attr=getarraysize(@csys_attr); + .@max_pena=getarraysize(@csys_penalty); + + if ($@GM_OVERRIDE) + debugmes "We have %d attributes and %d penalties", + .@max_attr, .@max_pena; + + .@slot=0; + while (.@slot < min(3, .@max_attr)) { + // You have 100% for first bonus, -45% each, depending on skill lv + .@base=4500-(.@lv*75); + if (rand(10000) > 10000-(.@base*.@slot)) + break; + + // Apply a bonus using array_pop (it was shuffled so we're fine) + .@vartp=array_pop(@csys_attr); + .@bonus=csys_BonusCalc(.@lv, .@lv2, .@vartp, .@eqplv); + setitemoptionbyindex(.@id, .@slot, .@vartp, .@bonus); + //debugmes "Bonus applied: %d at %d (slot: %d)", .@vartp, .@bonus, .@slot; + .@slot+=1; + } + + // You have 102% chance of a malus, skill and equips lower it in 0.5% each + .@base=10200-(.@lv*50); + if (rand(10000) < .@base && .@max_pena) { + // Apply a malus using array_pop (it was shuffled so we're fine) + .@vartp=array_pop(@csys_penalty); + .@malus=csys_BonusCalc(.@lv, .@lv2, .@vartp); // .@eqplv ? + .@malus=.@malus*70/100; + if (.@vartp > 0 && .@malus > 0) + setitemoptionbyindex(.@id, .@slot, .@vartp, -(.@malus)); + .@slot+=1; + } + + // The options have been attributed, clear temporary variables + @csysArmor=false; + return; +} + +// Attribute perfect item options +// For Fortress Island only +// csys_ApplyPerfect( invindex, lvl{, scope} ) +function script csys_ApplyPerfect { + .@id=getarg(0); + .@lv=getarg(1); + .@sc=getarg(2, CRAFTSYS_CURRENT); + + // Generate lists, disregarding level + csys_Generate(.@sc, false, true); + // @csys_attr → Available attributes + // @csys_penalty → Penalty attribute array + + // Remove weapon-only bonuses if it is armor + delinventorylist(); + getinventorylist(); + .@itemid=@inventorylist_id[.@id]; + if (getiteminfo(.@itemid, ITEMINFO_TYPE) != IT_WEAPON) + csys_ArmorFix(.@itemid, (rand2(.@lv/10) != 0)); + else + csys_WeaponFix(.@itemid); + .@eqplv=getiteminfo(.@itemid, ITEMINFO_ELV); + + // Shuffle the arrays + array_shuffle(@csys_attr); + array_shuffle(@csys_penalty); + + // How many bonuses we'll have? Never more than 3 bonus and 2 onus. + .@max_attr=getarraysize(@csys_attr); + .@max_pena=getarraysize(@csys_penalty); + + if ($@GM_OVERRIDE) + debugmes "ApplyPerfect: We have %d attributes and %d penalties", + .@max_attr, .@max_pena; + + .@slot=0; + while (.@slot < min(3, .@max_attr)) { + // Apply a bonus using array_pop (it was shuffled so we're fine) + .@vartp=array_pop(@csys_attr); + .@bonus=csys_BonusCalc(0, .@lv, .@vartp, .@eqplv, true); + setitemoptionbyindex(.@id, .@slot, .@vartp, .@bonus); + //debugmes "Bonus applied: %d at %d (slot: %d)", .@vartp, .@bonus, .@slot; + .@slot+=1; + } + + if (.@max_pena) { + // Apply a malus using array_pop (it was shuffled so we're fine) + .@vartp=array_pop(@csys_penalty); + .@malus=csys_BonusCalc(0, .@lv, .@vartp, .@eqplv, true); + .@malus=.@malus*70/100; + if (.@vartp > 0 && .@malus > 0) + setitemoptionbyindex(.@id, .@slot, .@vartp, -(.@malus)); + .@slot+=1; + } + + // The options have been attributed, clear temporary variables + @csysArmor=false; + return; +} + + + + + + + + + + + + + + + + + + + + + + + + + +//////////////////////////////////////// +///////////////// +/////// +// Interface System for Options Craft + +function script csys_ttlgrouptoit { + .@cr=getarg(0); + switch (.@cr) { + case CRGROUP_BASE: + return CRITEM_BASE; + case CRGROUP_ATK: + return CRITEM_ATK; + case CRGROUP_DEF: + return CRITEM_DEF; + case CRGROUP_ACC: + return CRITEM_ACC; + case CRGROUP_EVD: + return CRITEM_EVD; + case CRGROUP_REGEN: + return CRITEM_REGEN; + case CRGROUP_SPEED: + return CRITEM_SPEED; + case CRGROUP_DOUBLE: + return CRITEM_DOUBLE; + case CRGROUP_MAXPC: + return CRITEM_MAXPC; + case CRGROUP_SCRESIST: + return CRITEM_SCRESIST; + case CRGROUP_SCINFLICT: + return CRITEM_SCINFLICT; + case CRGROUP_MANAUSE: + return CRITEM_MANAUSE; + case CRGROUP_BOSSATK: + return CRITEM_BOSSATK; + case CRGROUP_FINAL: + return CRITEM_FINAL; + } + return Bread; +} + +function script csys_ISON { + .@cr=getarg(0); + .@it=csys_ttlgrouptoit(.@cr); + if (CRAFTSYS_CURRENT & .@cr) + return "%%E"+getitemlink(.@it); + else + return getitemlink(.@it); +} + +// csysGUI_Report( {silent} ) +// Report craft skill levels +function script csysGUI_Report { + + mes l("Crafting Skill: Lv @@", getskilllv(TMW2_CRAFT)); + + if (!getarg(0, false)) { + if (getskilllv(TMW2_CRAFT) >= 1) { + mes ""; + mes ".:: " + l("Base Tier") + " ::."; + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_BASE), CRAFTSYS[CRGROUP_BASE]); + } else { + mes ""; + mes ".:: " + l("Base Tier") + " ::."; + mes ""; + mesc l("Reach level @@ to unlock this tier!", 1), 1; + } + + if (getskilllv(TMW2_CRAFT) >= 2) { + mes ""; + mes ".:: " + l("First Tier") + " ::."; + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_ATK), CRAFTSYS[CRGROUP_ATK]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_DEF), CRAFTSYS[CRGROUP_DEF]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_ACC), CRAFTSYS[CRGROUP_ACC]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_EVD), CRAFTSYS[CRGROUP_EVD]); + next; + } else { + mes ""; + mes ".:: " + l("First Tier") + " ::."; + mes ""; + mesc l("Reach level @@ to unlock this tier!", 2), 1; + } + + if (getskilllv(TMW2_CRAFT) >= 3) { + mes ""; + mes ".:: " + l("Second Tier") + " ::."; + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_REGEN), CRAFTSYS[CRGROUP_REGEN]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_SPEED), CRAFTSYS[CRGROUP_SPEED]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_DOUBLE), CRAFTSYS[CRGROUP_DOUBLE]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_MAXPC), CRAFTSYS[CRGROUP_MAXPC]); + } else { + mes ""; + mes ".:: " + l("Second Tier") + " ::."; + mes ""; + mesc l("Reach level @@ to unlock this tier!", 3), 1; + next; + } + + + if (getskilllv(TMW2_CRAFT) >= 4) { + mes ""; + mes ".:: " + l("Third Tier") + " ::."; + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_SCRESIST), CRAFTSYS[CRGROUP_SCRESIST]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_SCINFLICT), CRAFTSYS[CRGROUP_SCINFLICT]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_MANAUSE), CRAFTSYS[CRGROUP_MANAUSE]); + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_BOSSATK), CRAFTSYS[CRGROUP_BOSSATK]); + next; + } else { + mes ""; + mes ".:: " + l("Third Tier") + " ::."; + mes ""; + mesc l("Reach level @@ to unlock this tier!", 4), 1; + } + + if (getskilllv(TMW2_CRAFT) >= 5) { + mes ""; + mes ".:: " + l("Ultimate Tier") + " ::."; + mes ""; + mes l("@@: Lv @@", csys_ISON(CRGROUP_FINAL), CRAFTSYS[CRGROUP_FINAL]); + mes ""; + } else { + mes ""; + mes ".:: " + l("Ultimate Tier") + " ::."; + mes ""; + mesc l("Reach level @@ to unlock this tier!", 5), 1; + } + } + mesc l("Monster Points (Mobpt): @@ | Gold: @@", + format_number(Mobpt), format_number(Zeny)); + next; + return; +} + + +// csysGUI_CRName( cr ) +// Return group name for CR +function script csysGUI_CRName { + .@cr=getarg(0); + switch (.@cr) { + case CRGROUP_BASE: + return l("T0 - Base Bonus"); + // Tier 1 + case CRGROUP_ATK: + return l("T1 - Attack Bonus"); + case CRGROUP_DEF: + return l("T1 - Defense Bonus"); + case CRGROUP_ACC: + return l("T1 - Accuracy Bonus"); + case CRGROUP_EVD: + return l("T1 - Evasion Bonus"); + // Tier 2 + case CRGROUP_REGEN: + return l("T2 - Regeneration Bonus"); + case CRGROUP_SPEED: + return l("T2 - Speed Bonus"); + case CRGROUP_DOUBLE: + return l("T2 - Double Power Bonus"); + case CRGROUP_MAXPC: + return l("T2 - Max Stats Bonus"); + // Tier 3 + case CRGROUP_SCRESIST: + return l("T3 - SC Resist Bonus"); + case CRGROUP_SCINFLICT: + return l("T3 - SC Inflict Bonus"); + case CRGROUP_MANAUSE: + return l("T3 - Mana Economy Bonus"); + case CRGROUP_BOSSATK: + return l("T3 - Boss Techniques Bonus"); + case CRGROUP_FINAL: + return l("T4 - Ultimate Bonus"); + default: + return Exception("Invalid optname group: "+.@cr); + } + return Exception("Definitely Invalid optname group: "+.@cr); +} + + +// csysGUI_OptToogleMenu( cr ) +// Returns a Toogle Menu for option group (CR) +// +function script csysGUI_OptToogleMenu { + .@sk=getarg(0); + if (getd("CRAFTSYS["+.@sk+"]")) { + if (CRAFTSYS_CURRENT & .@sk) + return "Remove "+csysGUI_CRName(.@sk); + else + return "Active "+csysGUI_CRName(.@sk); + } + return ""; +} + + +// csysGUI_ChangeOpt( cr ) +// Change option +function script csysGUI_ChangeOpt { + .@sk=getarg(0); + CRAFTSYS_CURRENT=CRAFTSYS_CURRENT^.@sk; + return; +} + + +// csysGUI_OptReq( cr ) +// Return true if all requisites for Option were met +function script csysGUI_OptReq { + .@sk=getarg(0); + + switch (.@sk) { + case CRGROUP_BASE: + return (getskilllv(TMW2_CRAFT) >= 1); + // Tier 1 + case CRGROUP_ATK: + return (getskilllv(TMW2_CRAFT) >= 2 && + CRAFTSYS[CRGROUP_BASE]); + case CRGROUP_DEF: + return (getskilllv(TMW2_CRAFT) >= 2 && + CRAFTSYS[CRGROUP_BASE]); + case CRGROUP_ACC: + return (getskilllv(TMW2_CRAFT) >= 2 && + CRAFTSYS[CRGROUP_BASE]); + case CRGROUP_EVD: + return (getskilllv(TMW2_CRAFT) >= 2 && + CRAFTSYS[CRGROUP_BASE]); + // Tier 2 + case CRGROUP_REGEN: + return (getskilllv(TMW2_CRAFT) >= 3 && + CRAFTSYS[CRGROUP_ATK] && + CRAFTSYS[CRGROUP_DEF] && + CRAFTSYS[CRGROUP_BASE] >= 2); + case CRGROUP_SPEED: + return (getskilllv(TMW2_CRAFT) >= 3 && + CRAFTSYS[CRGROUP_ACC] && + CRAFTSYS[CRGROUP_EVD] && + CRAFTSYS[CRGROUP_BASE] >= 2); + case CRGROUP_DOUBLE: + return (getskilllv(TMW2_CRAFT) >= 3 && + CRAFTSYS[CRGROUP_ATK] && + CRAFTSYS[CRGROUP_ACC] && + CRAFTSYS[CRGROUP_BASE] >= 2); + case CRGROUP_MAXPC: + return (getskilllv(TMW2_CRAFT) >= 3 && + CRAFTSYS[CRGROUP_DEF] && + CRAFTSYS[CRGROUP_EVD] && + CRAFTSYS[CRGROUP_BASE] >= 2); + // Tier 3 + case CRGROUP_SCRESIST: + return (getskilllv(TMW2_CRAFT) >= 4 && + CRAFTSYS[CRGROUP_MAXPC] >= 2 && + CRAFTSYS[CRGROUP_REGEN] >= 2 && + CRAFTSYS[CRGROUP_BASE] >= 4); + case CRGROUP_SCINFLICT: + return (getskilllv(TMW2_CRAFT) >= 4 && + CRAFTSYS[CRGROUP_SPEED] >= 2 && + CRAFTSYS[CRGROUP_DOUBLE] >= 2 && + CRAFTSYS[CRGROUP_BASE] >= 4); + case CRGROUP_MANAUSE: + return (getskilllv(TMW2_CRAFT) >= 4 && + CRAFTSYS[CRGROUP_DEF] >= 3 && + CRAFTSYS[CRGROUP_EVD] >= 3 && + CRAFTSYS[CRGROUP_BASE] >= 4); + case CRGROUP_BOSSATK: + return (getskilllv(TMW2_CRAFT) >= 4 && + CRAFTSYS[CRGROUP_ATK] >= 3 && + CRAFTSYS[CRGROUP_ACC] >= 3 && + CRAFTSYS[CRGROUP_BASE] >= 4); + case CRGROUP_FINAL: + return (getskilllv(TMW2_CRAFT) >= 5 && + CRAFTSYS[CRGROUP_BOSSATK] && + CRAFTSYS[CRGROUP_MANAUSE] && + CRAFTSYS[CRGROUP_SCINFLICT] && + CRAFTSYS[CRGROUP_SCRESIST] && + CRAFTSYS[CRGROUP_BASE] >= 6); + default: + return Exception("Invalid optreq group: "+.@sk); + } + return Exception("Definitely Invalid optreq group: "+.@sk); +} + +// csysGUI_OptPrice( cr ) +// Return group option price and requisites +function script csysGUI_OptPrice { + .@sk=getarg(0); + .@lv=getd("CRAFTSYS["+.@sk+"]")+1; + + // Every 99 skills levels (including the 0), price raises in 7 + .@lv+=((.@lv/99)*7); + + // Every 25 skills levels (including the 0), price raises in 5 + .@lv+=((.@lv/25)*5); + + // Every 15 skills levels (including the 0), price raises in 1 + .@lv+=(.@lv/15); + + // Every 10 skills levels (including the 0), price raises in 2 + .@lv+=((.@lv/10)*2); + + // Every 3 skills levels (including the 0), price raises in 1 + .@lv+=(.@lv/3); + + switch (.@sk) { + case CRGROUP_BASE: + return (.@lv < 40 ? (.@lv < 10 ? 1000 : 1500) : 3000)*.@lv; + // Tier 1 + case CRGROUP_ATK: + case CRGROUP_DEF: + case CRGROUP_ACC: + case CRGROUP_EVD: + return (.@lv < 10 ? 6200 : 6000)*.@lv; + // Tier 2 + case CRGROUP_REGEN: + case CRGROUP_SPEED: + case CRGROUP_DOUBLE: + case CRGROUP_MAXPC: + return (.@lv < 10 ? 16000 : 14000)*.@lv; + // Tier 3 + case CRGROUP_SCRESIST: + case CRGROUP_SCINFLICT: + case CRGROUP_MANAUSE: + case CRGROUP_BOSSATK: + return (.@lv < 10 ? 27000 : 22000)*.@lv; + // Final + case CRGROUP_FINAL: + return (.@lv < 10 ? 40000 : 32000)*.@lv; + default: + return Exception("Invalid optprice group: "+.@cr); + } + return Exception("Definitely Invalid optprice group: "+.@cr); +} + + +// csysGUI_OptLearnMenu( cr ) +// Returns the menu entry to learn the group skill. +// Cost is NOT taken as requisite, must check it later. +function script csysGUI_OptLearnMenu { + .@sk=getarg(0); + if (csysGUI_OptReq(.@sk)) { + return "Upgrade "+csysGUI_CRName(.@sk)+" for "+csysGUI_OptPrice(.@sk)+" Mobpt"; + } + return ""; +} + + + +// csysGUI_RaiseOpt( cr ) +// Returns true if can raise group, false otherwise +// You can't raise if max level (200) is exceeded +// At current max level (200) you'll have at most the following bonuses: +// 1 Splash Radius, 100% EXP, 500 HP, 300% SC RESIST, 80 AGI, 200 ATK, 100 VIT +// Walk Speed: 40% faster +// At ONE QUARTER max level (50) you'll have at most the following bonuses: +// 1 Splash Radius, 25% EXP, 125 HP, 75% SC RESIST, 20 AGI, 50 ATK, 25 VIT +// Walk Speed: 10% faster +// At level 10 it will be: +// 1 Splash Radius, 5% EXP, 25 HP, 15% SC RESIST, 4 AGI, 10 ATK, 5 VIT +// Walk Speed: 2% faster +// At level 1 it will be: +// 1 Splash Radius, 1% EXP, 2~3 HP, 1~2% SC RESIST, 1 AGI, 1 ATK, 1 VIT +// Walk Speed: 1% faster +function script csysGUI_RaiseOpt { + .@sk=getarg(0); + .@pc=csysGUI_OptPrice(.@sk); + .@lv=getd("CRAFTSYS["+.@sk+"]"); + if (csysGUI_OptReq(.@sk)) { + if (.@lv > CRAFT_MAXLV) { + mesc l("You cannot raise crafting skills beyond level @@!", CRAFT_MAXLV), 1; + return false; + } + if (Mobpt >= .@pc) { + Mobpt-=.@pc; + .@lv=getd("CRAFTSYS["+.@sk+"]"); + setd("CRAFTSYS["+.@sk+"]", .@lv+1); + return true; + } + } + return false; +} + + + + + + + + + + + + + + + + + + + + + + + + + +//////////////////////////////////////// +///////////////// +/////// +// Misc Functions for Options Craft + +// CsysNpcCraft( itemid, {bonus 1, value 1], {bonus 2, value 2}... ) +// Create a craft item in a NPC's stead. Needless to say, never fails. +function script CsysNpcCraft { + // Illegal param number + if (getargcount() % 2 != 1) + return Exception("Invalid craft NPC argument count", RB_DEFAULT|RB_IRCBROADCAST); + + // Setup variables + .@it=getarg(0); + .@opt1=getarg(1,0); + .@val1=getarg(2,0); + .@opt2=getarg(3,0); + .@val2=getarg(4,0); + .@opt3=getarg(5,0); + .@val3=getarg(6,0); + .@opt4=getarg(7,0); + .@val4=getarg(8,0); + .@opt5=getarg(9,0); + .@val5=getarg(10,0); + + getitem(.@it, 1); + delinventorylist(); // Needed, because we'll rely on rfind() + getinventorylist(); + .@index=array_rfind(@inventorylist_id, .@it); + + // Apply the bonuses if needed + if (.@opt1) + setitemoptionbyindex(.@index, 0, .@opt1, .@val1); + if (.@opt2) + setitemoptionbyindex(.@index, 1, .@opt2, .@val2); + if (.@opt3) + setitemoptionbyindex(.@index, 2, .@opt3, .@val3); + if (.@opt4) + setitemoptionbyindex(.@index, 3, .@opt4, .@val4); + if (.@opt5) + setitemoptionbyindex(.@index, 4, .@opt5, .@val5); + + return; +} + diff --git a/npc/craft/price.txt b/npc/craft/price.txt new file mode 100644 index 0000000..4b0ab9c --- /dev/null +++ b/npc/craft/price.txt @@ -0,0 +1,232 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Description: +// Modifies the sell price for crafts +// Always run this when casting @reloaditemdb +function script _fix_cPrice { + .@const$ = data_to_string(getarg(0)); + .@m = getarg(1, 35); + + // Shady code by gumi + if (startswith(.@const$, "Craft")) { + // infer the item constant from the craft constant + .@recipe = getarg(0); + + .@item = string_to_data(substr(.@const$, 5, getstrlen(.@const$) - 1)); + } else { + // infer the craft constant from the item constant + .@recipe = string_to_data(sprintf("Craft%s", .@const$)); + .@item = getarg(0); + } + + if (.@item <= 0) { + // target item not found + consolebug("ERROR, INVALID ITEM ID DETECTED at _fix_cPrice"); + return; + } + + .@price = 0; + // More shady code by gumi + for (.@inv = 0; .@inv < 9; ++.@inv) { + .@size = getcraftrecipe(.@recipe, .@inv, .@qty[0], .@item_id[0]); + + if (.@size < 0) { + if (.@size == -1) { + // recipe does not exist + break; + } + // inventory does not exist + break; + } + + // More shady code to build new price + for (.@it = 0; .@it < .@size; ++.@it) { + .@recipe_item = .@item_id[.@it]; + .@recipe_qty = .@qty[.@it]; + + if (.@recipe_item <= 0) { + break; + } + + // Increase the final price + //debugmes("Price %d + %d GP (%dx %s)", .@price, getiteminfo(.@recipe_item, ITEMINFO_SELLPRICE), + // .@recipe_qty, getitemname(.@recipe_item)); + .@price += getiteminfo(.@recipe_item, ITEMINFO_SELLPRICE) * .@recipe_qty; + //debugmes("New price: %d", .@price); + } + + // Update the final price + if (.@price > 0) { + debugmes("Price for %s adjusted from %d (%d) to %d (%d) GP", getitemname(.@item), getiteminfo(.@item, ITEMINFO_BUYPRICE), getiteminfo(.@item, ITEMINFO_SELLPRICE), .@price * .@m / 10, .@price); + setiteminfo(.@item, ITEMINFO_BUYPRICE, .@price * .@m / 10); + setiteminfo(.@item, ITEMINFO_SELLPRICE, .@price); + //debugmes("New Price for %s is now %d (%d) GP", getitemname(.@item), getiteminfo(.@item, ITEMINFO_BUYPRICE), getiteminfo(.@item, ITEMINFO_SELLPRICE)); + } + } + return; +} + +function script fix_cPrice { + // In some cases, we don't care + if (debug) return; + + // Otherwise... + freeloop(true); + + // Fix potions prices + _fix_cPrice(AgiPotionA); + _fix_cPrice(AgiPotionB); + _fix_cPrice(AgiPotionC); + _fix_cPrice(VitPotionA); + _fix_cPrice(VitPotionB); + _fix_cPrice(VitPotionC); + _fix_cPrice(IntPotionA); + _fix_cPrice(IntPotionB); + _fix_cPrice(IntPotionC); + _fix_cPrice(DexPotionA); + _fix_cPrice(DexPotionB); + _fix_cPrice(DexPotionC); + _fix_cPrice(LukPotionA); + _fix_cPrice(LukPotionB); + _fix_cPrice(LukPotionC); + //_fix_cPrice(HastePotion); // 240 -> 75 + //_fix_cPrice(StrengthPotion); // 240 -> 195 + + // TODO: Scrolls? Reagents? + // And reagents should happen before potions + + // And weapons + _fix_cPrice(WoodenSword); + _fix_cPrice(BugSlayer); + _fix_cPrice(ShortGladius); + _fix_cPrice(Backsword); + _fix_cPrice(ShortSword); + _fix_cPrice(Kitana); + _fix_cPrice(BoneKnife); + _fix_cPrice(LongSword); + _fix_cPrice(RockKnife); + _fix_cPrice(DivineSword); + + // And two hand weapons + _fix_cPrice(MiereCleaver); + _fix_cPrice(Broadsword); + _fix_cPrice(Halberd); + _fix_cPrice(ImmortalSword); + + // And archery + _fix_cPrice(ShortBow); + _fix_cPrice(ForestBow); + _fix_cPrice(ElficBow); + _fix_cPrice(ChampionshipBow); + _fix_cPrice(BansheeBow); + + // And magic + _fix_cPrice(TrainingWand, 22); + _fix_cPrice(NoviceWand, 27); + _fix_cPrice(ApprenticeWand); + _fix_cPrice(LeaderWand); + _fix_cPrice(MysticWand); + + // And Firestaves + _fix_cPrice(PynRevolver); + _fix_cPrice(PynRifle); + _fix_cPrice(PynGatling); + _fix_cPrice(PynShotgun); + + // And misc + _fix_cPrice(TerranitePants); + _fix_cPrice(TerraniteArmor); + _fix_cPrice(Skypiercer, 50); + + // And shields + _fix_cPrice(WoodenShield); + _fix_cPrice(BladeShield); + _fix_cPrice(BraknarShield); + _fix_cPrice(BritShield); + _fix_cPrice(BromenalShield); + _fix_cPrice(BlueKnightShield); + _fix_cPrice(SteelShield); + _fix_cPrice(DragonShield); + _fix_cPrice(SaviorShield, 50); + + // Chest Armor + _fix_cPrice(LeatherShirt); + _fix_cPrice(LieutenantArmor); + _fix_cPrice(Chainmail); + _fix_cPrice(CopperArmor); + _fix_cPrice(LightPlatemail); + _fix_cPrice(GoldenLightPlatemail); + _fix_cPrice(WarlordPlate); + _fix_cPrice(GoldenWarlordPlate); + _fix_cPrice(BromenalChest); + _fix_cPrice(AssassinChest); + _fix_cPrice(SaviorArmor, 50); + + // Pants + //_fix_cPrice(JeansShorts); + _fix_cPrice(RaidTrousers); + _fix_cPrice(LeatherTrousers); + _fix_cPrice(JeansChaps); + _fix_cPrice(SilkPants); + _fix_cPrice(ChainmailSkirt); // <= Pre-Fortress + _fix_cPrice(BromenalPants); // <= Fortress + _fix_cPrice(WarlordPants); + _fix_cPrice(AssassinPants); + + // Gloves (more expensive due ASPD) + _fix_cPrice(SilkGloves, 40); + _fix_cPrice(LeatherGloves, 40); + _fix_cPrice(BromenalGloves, 40); + _fix_cPrice(ManaGloves, 40); + _fix_cPrice(WarlordGloves, 40); + _fix_cPrice(AssassinGloves, 40); + + // Helmets + _fix_cPrice(InfantryHelmet); + _fix_cPrice(DesertHelmet); + _fix_cPrice(BromenalHelmet); + _fix_cPrice(CandleHelmet); + _fix_cPrice(CrusadeHelmet); + _fix_cPrice(WarlordHelmet); + _fix_cPrice(VikingHelmet); + _fix_cPrice(TerraniteHelmet); // Cheaper than the real cost due 2x Earth Powder + _fix_cPrice(CenturionHelmet); + _fix_cPrice(BullHelmet); + _fix_cPrice(DarkHelm); + _fix_cPrice(DarkKnightHelmet); + _fix_cPrice(SamuraiHelmet); + _fix_cPrice(SaviorHelmet); + + // Footwear + _fix_cPrice(LeatherBoots); + _fix_cPrice(DeepBlackBoots); + _fix_cPrice(BromenalBoots); + _fix_cPrice(WarlordBoots); + _fix_cPrice(AssassinBoots); + _fix_cPrice(SaviorBoots, 50); + + // We're done + freeloop(false); + + // Manual fixes (handling _fix_cPrice shortcomings) + setiteminfo(DarkCrystal, ITEMINFO_SELLPRICE, rand2(150, 250)); + return; +} + +- script craft_price_fix -1,{ + end; + +OnCall: + atcommand("@reloaditemdb"); + fix_cPrice(); + end; + +OnInit: + bindatcmd "reloaditemdb2", "craft_price_fix::OnCall", 99, 100, 1; + // This should be called after craft_db is loaded + sleep(750); + fix_cPrice(); + end; +} + diff --git a/npc/craft/recipes.txt b/npc/craft/recipes.txt new file mode 100644 index 0000000..0647ff4 --- /dev/null +++ b/npc/craft/recipes.txt @@ -0,0 +1,605 @@ +// TMW-2 script. +// Author: +// Jesusalva +// Description: +// Recipe Books in TMW2 + +- script #RecipeBook NPC_HIDDEN,{ + function showRecipe; + function readCooking; + function readAlchemy; + function readCrafting; + +OnUse: + setnpcdialogtitle l("Recipe Book"); + + mesc l("You open the Recipe Book. Each recipe you get can be put here."); + next; + do { + mesc l("Which recipes do you want to read?"); + select + l("Nothing."), + l("Cooking Recipes."), + l("Alchemy Recipes."), + l("Crafting Recipes."); + mes ""; + switch (@menu) { + case 2: + readCooking(); break; + case 3: + readAlchemy(); break; + case 4: + readCrafting(); break; + } + } while (@menu != 1); + closeclientdialog; + close; + +// Expects: @scope$ +// showRecipe( recipe{, recipe...} ) +function showRecipe { + if (@scope$ == "") + return Exception("Faulty recipe skill command invoked - error"); + + freeloop(true); + for (.@a = 0; .@a < getargcount(); ++.@a) { + .@const$ = data_to_string(getarg(.@a)); + + if (startswith(.@const$, "Craft")) { + // infer the item constant from the craft constant + .@recipe = getarg(.@a); + + .@item = string_to_data(substr(.@const$, 5, getstrlen(.@const$) - 1)); + } else { + // infer the craft constant from the item constant + .@recipe = string_to_data(sprintf("Craft%s", .@const$)); + .@item = getarg(.@a); + } + + if (.@item <= 0) { + // target item not found + consolebug("ERROR, INVALID ITEM ID DETECTED at showRecipe"); + continue; + } + + if (!getd("RECIPES_"+@scope$+"["+.@recipe+"]") && !$@GM_OVERRIDE) { + // does not have the recipe + continue; + } + + for (.@inv = 0; .@inv < 9; ++.@inv) { + .@size = getcraftrecipe(.@recipe, .@inv, .@qty[0], .@item_id[0]); + + if (.@size < 0) { + if (.@size == -1) { + // recipe does not exist + break; + } + // inventory does not exist + break; + } + + mes(l(".:: %s Recipe ::.", getitemlink(.@item))); + + for (.@it = 0; .@it < .@size; ++.@it) { + .@recipe_item = .@item_id[.@it]; + .@recipe_qty = .@qty[.@it]; + + if (.@recipe_item <= 0) { + break; + } + + mesc(sprintf("%d/%d %s", countitem(.@recipe_item), .@recipe_qty, getitemlink(.@recipe_item))); + } + + mes(""); + .@count++; + } + } + freeloop(false); + + return .@count > 0; +} + +// =============================== Cooking Functions +function readCooking { + setnpcdialogtitle l("Cooking Recipes"); + @scope$="COOKING"; + + mesc l("Eating is a necessity, but cooking is an art."); + mesc l("(All items must be placed exactly in this order.)"); + next; + mesc l("List of known cooking recipes:"); + mes ""; + //showRecipe(0, Iten, WarpedLog, 9999); + next; + @scope$=""; + return; +} + +// =============================== Cooking Functions +function readAlchemy { + setnpcdialogtitle l("Alchemy Recipes"); + @scope$="ALCHEMY"; + + mesc l("Alchemy. The art of having quasi-magical effects without magic."); + mesc l("(All items must be placed exactly in this order.)"); + next; + mesc l("List of known alchemy recipes:"); + mes ""; + // Healing + mesc "----------"+l("Healing Recipes")+"----------", 2; + showRecipe(PiberriesInfusion, + AtroposMixture, + Coffee); + dnext; + + // General Boosts + mesc "----------"+l("General Boosts")+"----------", 2; + showRecipe(HastePotion, + StrengthPotion, + StatusResetPotion, // BROKEN + HomunResetPotion, + MoveSpeedPotion, // BROKEN + PrecisionPotion, + DodgePotion, + SacredLifePotion, + SacredManaPotion, + SacredImmortalityPotion, + MagicApple); + dnext; + + // Stats Boosts + mesc "----------"+l("Stat Boost Recipes")+"----------", 2; + showRecipe(LukPotionA, + LukPotionB, + LukPotionC); + + showRecipe(IntPotionA, + IntPotionB, + IntPotionC); + + showRecipe(VitPotionA, + VitPotionB, + VitPotionC); + + showRecipe(AgiPotionA, + AgiPotionB, + AgiPotionC); + + showRecipe(DexPotionA, + DexPotionB, + DexPotionC); + + // Scrolls + mesc "----------"+l("Magic Scrolls")+"----------", 2; + showRecipe(ScrollSCave, + ScrollSMaggot, + ScrollSWolvern, + ScrollSYeti, + ScrollSTerranite, + ScrollSDragon, + ScrollMagnusHealA, + ScrollAngelLightA, + ScrollBattlePlansA, + ScrollDefenseBlessA, + ScrollCriticalFortuneA); + + // General Stuff + mesc "----------"+l("Reagents & Other Potions")+"----------", 2; + showRecipe(IcedBottle, + PurificationPotion, + DeathPotion, + BrokenWarpCrystal, + SmokeGrenade, + ScentGrenade, + Grenade, + Insurance, + InsuranceContract); + + next; + @scope$=""; + return; +} + +// =============================== Crafting Functions +function readCrafting { + setnpcdialogtitle l("Crafting Recipes"); + @scope$="EQUIPMENT"; + + mesc l("There is only one way towards the best equipment: Smith away!"); + mesc l("(All items must be placed exactly in this order.)"); + next; + mesc l("List of known crafting recipes:"); + mes ""; + // Melee Weapons: Never use Titanium nor Lead. Iron-based, no silver + mesc "----------"+l("One Hand Weapon Recipes")+"----------", 2; + showRecipe(Dagger, + WoodenSword, + BugSlayer, + ShortGladius, + Backsword, + ShortSword, + Kitana, + BoneKnife, + LongSword, + RockKnife, + DivineSword); + dnext; + // Two Hands Melee Weapons: Never use Titanium nor Lead. Silver-based. + mesc "----------"+l("Two Hands Weapon Recipes")+"----------", 2; + // Reserved ID 63 and 64 + // Halberd is really cheap as it doesn't uses Platinum/Iridium :P + showRecipe(MiereCleaver, + Broadsword, + Halberd, + ImmortalSword); + + dnext; + // Archery Weapons: Always use Wood, Root and Carp. + mesc "----------"+l("Archery Weapon Recipes")+"----------", 2; + showRecipe(ShortBow, + ForestBow, + ElficBow, + ChampionshipBow, + BansheeBow); + dnext; + // Magical Weapons: Wood + powders + mesc "----------"+l("Magical Weapon Recipes")+"----------", 2; + showRecipe(TrainingWand, + NoviceWand, + ApprenticeWand, + LeaderWand, + MysticWand); + dnext; + // Firestaff Weapons: Lead + Titanium + mesc "----------"+l("Fire Staffs Recipes")+"----------", 2; + showRecipe(PynRevolver, + PynRifle, + PynGatling, + PynShotgun); + dnext; + // Shields: May use Leather. Titanium or Lead, but never both + mesc "----------"+l("Shield Recipes")+"----------", 2; + // Exception to shield rule: Braknar Shield + showRecipe(WoodenShield, + BladeShield, + BraknarShield, + BritShield, + BromenalShield, + BlueKnightShield, + SteelShield, + DragonShield, + SaviorShield); + dnext; + // Chest Armors -> Primary Ore + Secondary Ore + Iron Powder + Earth Powder + mesc "----------"+l("Chest Armor Recipes")+"----------", 2; + showRecipe(LeatherShirt, + LieutenantArmor, + Chainmail, + CopperArmor, + LightPlatemail, + GoldenLightPlatemail, + WarlordPlate, + GoldenWarlordPlate, + BromenalChest, + AssassinChest, + SaviorArmor); + dnext; + // Pants -> Primary Item + Secondary Item + Leather Patch + Earth Powder + mesc "----------"+l("Pants Recipes")+"----------", 2; + showRecipe(JeansShorts, + RaidTrousers, + LeatherTrousers, + JeansChaps, + SilkPants, + ChainmailSkirt, + BromenalPants, + WarlordPants, + AssassinPants); + dnext; + // Gloves: Gloves items + mesc "----------"+l("Gloves Recipes")+"----------", 2; + showRecipe(SilkGloves, + LeatherGloves, + BromenalGloves, + ManaGloves, + WarlordGloves, + AssassinGloves); + dnext; + // Feet: Shoes items + mesc "----------"+l("Footwear Recipes")+"----------", 2; + showRecipe(LeatherBoots, + DeepBlackBoots, + BromenalBoots, + WarlordBoots, + AssassinBoots, + SaviorBoots); + dnext; + // Helmets: Helmet items + mesc "----------"+l("Helmet Recipes")+"----------", 2; + showRecipe(InfantryHelmet, + DesertHelmet, + BromenalHelmet, + CandleHelmet, + CrusadeHelmet, + WarlordHelmet, + VikingHelmet, + TerraniteHelmet, + CenturionHelmet, + BullHelmet, + DarkHelm, + DarkKnightHelmet, + SamuraiHelmet, + SaviorHelmet); + dnext; + // Misc: Misc items + mesc "----------"+l("Miscellaneous Recipes")+"----------", 2; + showRecipe(GoldenRing, + TerranitePants, + TerraniteArmor, + Skypiercer); + next; + @scope$=""; + return; +} + +OnInit: + .sex = G_OTHER; + .distance = 1; + end; +} + +// Below this line are utils for Gacha. We use callfunc() on itemDB. +// Types: CRAFT_COOKING, CRAFT_ALCHEMY, CRAFT_EQUIPMENT +// Rarity: 1 - basic, 2 - intermediary, 4 - advanced, 8 - expert, 16 - master +// Level equivalents: 1: (1~20) 2: (21~44), 3: (45~75), 4: (76~99), 5: 100+ +function script MakeBlueprint { + .@type=getarg(0, -1); + .@rarity=getarg(1, 1); + + switch (.@type) { + ///////////////////////////////////////////////////// + ///// Alchemy Recipes + ///////////////////////////////////////////////////// + case CRAFT_ALCHEMY: + if (.@rarity & CRAFT_BASIC) { + array_push(.@recipes, CraftPiberriesInfusion); + array_push(.@recipes, CraftHastePotion); + array_push(.@recipes, CraftStrengthPotion); + array_push(.@recipes, CraftCoffee); + array_push(.@recipes, CraftScrollSCave); + array_push(.@recipes, CraftScrollSMaggot); + } + if (.@rarity & CRAFT_INTERMEDIARY) { + array_push(.@recipes, CraftLukPotionA); + array_push(.@recipes, CraftDexPotionA); + array_push(.@recipes, CraftIntPotionA); + array_push(.@recipes, CraftAgiPotionA); + array_push(.@recipes, CraftVitPotionA); + array_push(.@recipes, CraftSpeedPotion); + array_push(.@recipes, CraftIcedBottle); + array_push(.@recipes, CraftInsuranceContract); + array_push(.@recipes, CraftScrollSWolvern); + } + if (.@rarity & CRAFT_ADVANCED) { + array_push(.@recipes, CraftResetPotion); + array_push(.@recipes, CraftPrecisionPotion); + array_push(.@recipes, CraftDodgePotion); + array_push(.@recipes, CraftDeathPotion); + array_push(.@recipes, CraftSmokeGrenade); + array_push(.@recipes, CraftScentGrenade); + array_push(.@recipes, CraftGrenade); + array_push(.@recipes, CraftInsurance); + array_push(.@recipes, CraftScrollSYeti); + } + if (.@rarity & CRAFT_EXPERT) { + array_push(.@recipes, CraftLukPotionB); + array_push(.@recipes, CraftDexPotionB); + array_push(.@recipes, CraftIntPotionB); + array_push(.@recipes, CraftAgiPotionB); + array_push(.@recipes, CraftVitPotionB); + array_push(.@recipes, CraftAtroposMixture); + array_push(.@recipes, CraftPurificationPotion); + array_push(.@recipes, CraftHomunResetPotion); + array_push(.@recipes, CraftScrollSTerranite); + array_push(.@recipes, CraftScrollMagnusHealA); + } + if (.@rarity & CRAFT_MASTER) { + array_push(.@recipes, CraftLukPotionC); + array_push(.@recipes, CraftDexPotionC); + array_push(.@recipes, CraftIntPotionC); + array_push(.@recipes, CraftAgiPotionC); + array_push(.@recipes, CraftVitPotionC); + array_push(.@recipes, CraftSacredLifePotion); + array_push(.@recipes, CraftSacredManaPotion); + array_push(.@recipes, CraftSacredImmortalityPotion); + array_push(.@recipes, CraftBrokenWarpCrystal); + array_push(.@recipes, CraftMagicApple); + array_push(.@recipes, CraftScrollSDragon); + if (getcharid(2) > 0) { + if (getguildlvl(getcharid(2)) >= 4) + array_push(.@recipes, CraftScrollAngelLightA); + if (getguildlvl(getcharid(2)) >= 5) + array_push(.@recipes, CraftScrollBattlePlansA); + if (getguildlvl(getcharid(2)) >= 3) + array_push(.@recipes, CraftScrollDefenseBlessA); + if (getguildlvl(getcharid(2)) >= 6) + array_push(.@recipes, CraftScrollCriticalFortuneA); + } + } + + // Now you'll learn some recipe! + .@rcp=any_of(.@recipes); + + // Half precision failsafe + if (RECIPES_EQUIPMENT[.@rcp] && any(true, false)) + .@rcp=any_of(.@recipes); + + // Maybe you already knew it? + if (RECIPES_ALCHEMY[.@rcp]) { + .@mpot=rand2(900, 1000*.@rarity); + dispbottom l("It was a recipe you already knew... (+ @@ Mobpt)", .@mpot); + getexp (BaseLevel+JobLevel)*rand2(1,.@rarity), JobLevel+rand2(1,.@rarity); + // Give you some Monster Points to use with Intense Beard + // You do NOT need to be registered with Aidan for this. + Mobpt+=.@mpot; + } else { + dispbottom l("Learned a new recipe!"); + RECIPES_ALCHEMY[.@rcp]=true; + } + break; + ///////////////////////////////////////////////////// + ///// Equipment Recipes + ///////////////////////////////////////////////////// +// array_push(.@recipes, Craft); + case CRAFT_EQUIPMENT: + if (.@rarity & CRAFT_BASIC) { + array_push(.@recipes, CraftWoodenSword); + array_push(.@recipes, CraftWoodenShield); + array_push(.@recipes, CraftTrainingWand); + array_push(.@recipes, CraftShortBow); + array_push(.@recipes, CraftSilkGloves); + array_push(.@recipes, CraftInfantryHelmet); + array_push(.@recipes, CraftLeatherShirt); + array_push(.@recipes, CraftJeansShorts); + array_push(.@recipes, CraftLeatherBoots); + } + if (.@rarity & CRAFT_INTERMEDIARY) { + array_push(.@recipes, CraftBugSlayer); + array_push(.@recipes, CraftShortGladius); + array_push(.@recipes, CraftMiereCleaver); + array_push(.@recipes, CraftBladeShield); + array_push(.@recipes, CraftNoviceWand); + array_push(.@recipes, CraftForestBow); + array_push(.@recipes, CraftLeatherGloves); + array_push(.@recipes, CraftDesertHelmet); + array_push(.@recipes, CraftBromenalHelmet); + array_push(.@recipes, CraftLieutenantArmor); + array_push(.@recipes, CraftRaidTrousers); + array_push(.@recipes, CraftDeepBlackBoots); + } + if (.@rarity & CRAFT_ADVANCED) { + array_push(.@recipes, CraftBacksword); + array_push(.@recipes, CraftShortSword); + array_push(.@recipes, CraftBoneKnife); + array_push(.@recipes, CraftKitana); + array_push(.@recipes, CraftBroadsword); + array_push(.@recipes, CraftPynRevolver); + array_push(.@recipes, CraftApprenticeWand); + array_push(.@recipes, CraftElficBow); + array_push(.@recipes, CraftBritShield); + array_push(.@recipes, CraftBromenalShield); + array_push(.@recipes, CraftBlueKnightShield); + array_push(.@recipes, CraftBromenalGloves); + array_push(.@recipes, CraftCandleHelmet); + array_push(.@recipes, CraftCrusadeHelmet); + array_push(.@recipes, CraftWarlordHelmet); + array_push(.@recipes, CraftVikingHelmet); + array_push(.@recipes, CraftChainmail); + array_push(.@recipes, CraftCopperArmor); + array_push(.@recipes, CraftLightPlatemail); + array_push(.@recipes, CraftWarlordPlate); + array_push(.@recipes, CraftBromenalChest); + array_push(.@recipes, CraftLeatherTrousers); + array_push(.@recipes, CraftJeansChaps); + array_push(.@recipes, CraftSilkPants); + array_push(.@recipes, CraftChainmailSkirt); + array_push(.@recipes, CraftBromenalPants); + array_push(.@recipes, CraftWarlordPants); + array_push(.@recipes, CraftBromenalBoots); + } + if (.@rarity & CRAFT_EXPERT) { + array_push(.@recipes, CraftGoldenRing); + array_push(.@recipes, CraftLongSword); + array_push(.@recipes, CraftRockKnife); + array_push(.@recipes, CraftHalberd); + array_push(.@recipes, CraftPynRifle); + array_push(.@recipes, CraftPynGatling); + array_push(.@recipes, CraftLeaderWand); + array_push(.@recipes, CraftChampionshipBow); + array_push(.@recipes, CraftSteelShield); + array_push(.@recipes, CraftDragonShield); + array_push(.@recipes, CraftManaGloves); + array_push(.@recipes, CraftWarlordGloves); + array_push(.@recipes, CraftTerraniteHelmet); + array_push(.@recipes, CraftCenturionHelmet); + array_push(.@recipes, CraftBullHelmet); + array_push(.@recipes, CraftDarkHelm); + array_push(.@recipes, CraftTerraniteArmor); + array_push(.@recipes, CraftTerranitePants); + array_push(.@recipes, CraftWarlordBoots); + array_push(.@recipes, CraftAssassinChest); + array_push(.@recipes, CraftAssassinPants); + } + if (.@rarity & CRAFT_MASTER) { + array_push(.@recipes, CraftDivineSword); + array_push(.@recipes, CraftImmortalSword); + array_push(.@recipes, CraftPynShotgun); + array_push(.@recipes, CraftMysticWand); + array_push(.@recipes, CraftBansheeBow); + array_push(.@recipes, CraftAssassinGloves); + array_push(.@recipes, CraftAssassinBoots); + array_push(.@recipes, CraftDarkKnightHelmet); + array_push(.@recipes, CraftSamuraiHelmet); + } + + // Now you'll learn some recipe! + .@rcp=any_of(.@recipes); + + // Double precision failsafe + if (RECIPES_EQUIPMENT[.@rcp]) + .@rcp=any_of(.@recipes); + + // Maybe you already knew it? + if (RECIPES_EQUIPMENT[.@rcp]) { + .@mpot=rand2(900*.@rarity, 1000*.@rarity); + dispbottom l("It was a recipe you already knew... (+ @@ Mobpt)", .@mpot); + getexp (BaseLevel+JobLevel)*rand2(1,.@rarity), JobLevel+rand2(1,.@rarity); + // Give you some Monster Points to use with Intense Beard + // You do NOT need to be registered with Aidan for this. + Mobpt+=.@mpot; + } else { + dispbottom l("Learned a new recipe!"); + RECIPES_EQUIPMENT[.@rcp]=true; + } + break; + default: + return Exception("Invalid blueprint type "+.@type+" - item was lost."); + } + return; +} + +// Create a blueprint based on level. Extra chance for weaker Blueprint. +// Level equivalents: 1: (1~20) 2: (21~44), 3: (45~75), 4: (76~99), 5: 100+ +function script MakeRandomBlueprint { + array_push(.@blueprints, AlchemyBlueprintA); + array_push(.@blueprints, EquipmentBlueprintA); + if (BaseLevel > 20) { + array_push(.@blueprints, AlchemyBlueprintB); + array_push(.@blueprints, EquipmentBlueprintB); + } + if (BaseLevel > 44) { + array_push(.@blueprints, AlchemyBlueprintB); + array_push(.@blueprints, EquipmentBlueprintB); + array_push(.@blueprints, AlchemyBlueprintC); + array_push(.@blueprints, EquipmentBlueprintC); + } + if (BaseLevel > 75) { + array_push(.@blueprints, AlchemyBlueprintC); + array_push(.@blueprints, EquipmentBlueprintC); + array_push(.@blueprints, AlchemyBlueprintD); + array_push(.@blueprints, EquipmentBlueprintD); + } + if (BaseLevel > 100) { + array_push(.@blueprints, AlchemyBlueprintD); + array_push(.@blueprints, EquipmentBlueprintD); + if (any(true,false)) { + array_push(.@blueprints, AlchemyBlueprintE); + array_push(.@blueprints, EquipmentBlueprintE); + } + } + getitem any_of(.@blueprints), 1; + return; +} + diff --git a/npc/craft/smith.txt b/npc/craft/smith.txt new file mode 100644 index 0000000..9b8387c --- /dev/null +++ b/npc/craft/smith.txt @@ -0,0 +1,91 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Smith System (Player, Guild, NPC) +// Notes: +// Base for Evol MR +// This one is more crazy. Cannot be equipping target craft. +// After successful craft, we use CraftDB return code to equip() the +// new item and apply a random option bonus based on crafter skills +// eg. setequipoption(EQI_HAND_R, 1, VAR_STRAMOUNT, 5) +// We should be able to apply several bonuses for the nicest experience :3 +// We should also add a movespeed bonus... So demure can specialize herself in +// crafting really fast weapons in every aspects, and this system would allow her +// to do so :> But then, maybe we should have a crafting skill where players can +// allocate status points? + +// Usage: SmithSystem ({scope}) +// Scopes: CRAFT_NPC, CRAFT_PLAYER, CRAFT_GUILD +// CRAFT_NPC - Unlocks all recipes +// CRAFT_PLAYER - Normal behavior +// CRAFT_GUILD - Items created will be Guild-bound +// Returns true on success, false on failure +function script SmithSystem { + // Set .scope, .knowledge and .success + .scope=getarg(0, CRAFT_PLAYER); + copyarray(.knowledge,RECIPES_EQUIPMENT,getarraysize(RECIPES_EQUIPMENT)); + .success=false; + + mesc l("WARNING: Strange bugs may happen if you attempt to craft an item you already have on inventory!"), 1; + setskin "craft4"; + .@var$ = requestcraft(4); + .@craft = initcraft(.@var$); + .@entry = findcraftentry(.@craft, CRAFT_EQUIPMENT); + if (debug || $@GM_OVERRIDE) mes "found craft entry: " + .@entry; + if (debug || $@GM_OVERRIDE) mes "knowledge value: " + .knowledge[.@entry]; + if (.@entry < 0) { + .success=false; + } else { + if (.scope == CRAFT_NPC) { + usecraft .@craft; + .@it=getcraftcode(.@entry); + getitem(.@it, 1); + .success=true; + } else if (.knowledge[.@entry] || $@GM_OVERRIDE) { + // Player craft item + usecraft .@craft; + .@it=getcraftcode(.@entry); + + // Mark the crafting in your score variable + CRAFTING_SCORE_COMPLETE+=getiteminfo(.@it, ITEMINFO_ELV); + // Update your score book + CRAFTING_SCORE=(CRAFTING_SCORE_COMPLETE/40); + + if (.scope == CRAFT_GUILD) + getitembound(.@it, 1, 2); // Create a guild-bound item + else if (GSET_CRAFT_BOUND) + getitembound(.@it, 1, 1); // Create an account-bound item + else + getnameditem(.@it, strcharinfo(0)); + if (getskilllv(TMW2_CRAFT)) { + delinventorylist(); // Needed, because we'll rely on rfind() + getinventorylist(); + .@index=array_rfind(@inventorylist_id, .@it); + + // Just to be sure, if this have an option, get something else + if (getitemoptionparambyindex(.@index, 0)) { + .@index=array_find(@inventorylist_id, .@it); + } + + if (csys_Check(.@index, 75000)) { + csys_Apply(.@index); + } + } + + // Get experience for the craft + .@xp=getiteminfo(.@it, ITEMINFO_SELLPRICE); + getexp .@xp+BaseLevel, (.@xp/3)+BaseLevel+JobLevel; + // Monster points too, if appliable - by your Job Level + if (MPQUEST) + Mobpt+=JobLevel; + + .success=true; + } else { + .success=false; + } + } + deletecraft .@craft; + setskin ""; + return .success; +} diff --git a/npc/craft/tweak.txt b/npc/craft/tweak.txt new file mode 100644 index 0000000..1124afa --- /dev/null +++ b/npc/craft/tweak.txt @@ -0,0 +1,128 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Smith System (Player, Guild, NPC) +// Notes: +// It's like smithing, but it only change an item options + +// Usage: SmithTweakReset () +// Asks if player wants to remove an item options. And remove them. +function script SmithTweakReset { + mesc b(l("You are REMOVING an item option.")), 1; + mesc l("Note: This action cannot be undone."), 1; + mes l("Drag and drop here the item you want to remove the options."); + + .@id=requestitemindex(); + mes ""; + + // Ask player to confirm + mesc l("Are you sure?"), 1; + mesc l("Note: This action cannot be undone."), 1; + if (!csys_Confirm(.@id)) + return; + + csys_Check(.@id); + return; +} + +// Usage: SmithTweakSystem ({price=600, retries=1}) +// Returns true on success, false on failure +function script SmithTweakSystem { + .@price=getarg(0, 600); + .@retry=getarg(1, 1); + + // Adjust price (if relevant) + if (.@retry == 1) + .@price=POL_AdjustPrice(.@price); + + // How many times more can you tweak? + // You get 1 action, capped to 6 + .@left=gettimeparam(GETTIME_HOUR)-SMITH_TWEAKS; + if (.@left > 6) { + .@left=6; + SMITH_TWEAKS=gettimeparam(GETTIME_HOUR)-6; + } + + mes l("Which item will you tweak?"); + mesc l("Note: You can only perform this operation @@/6 times.", .@left); + mesc l("You recover a tweaking point every hour."); + mesc l("EXPERTS ONLY - If you are not a talented crafter, avoid this."), 1; + mesc l("The item must have a previous bonus, which WILL BE LOST!"), 1; + mesc l("Note: You may fail to write skills to it."), 1; + mesc l("Operation Cost: @@ GP", .@price), 3; + + // Do you have money or AP + if (Zeny < .@price || !.@left) { + mesc l("You lack money or Action Points."), 1; + return false; + } + + .@id=requestitemindex(); + mes ""; + + // Ask player to confirm + if (!csys_Confirm(.@id)) + return false; + + // Collect the item ID + delinventorylist(); + getinventorylist(); + .@x=@inventorylist_id[.@id]; + + // No duplicates + if (countitem(.@x) > 1) { + mesc l("You are carrying duplicates of the same item. Sorry, but I have no idea which one you want to tweak."), 1; + return false; + } + + // Skip equipped items + if (isequipped(.@x)) { + mesc l("You should unequip this item first."), 1; + return false; + } + + // If the item have no bonuses - fail + setarray .@AlwaysTweaks, 65535, BlacksmithAxe, Dustynator, Lightbringer, + DemureAxe, Tyranny, Runestaff, AegisShield, + SaviorShield, SaviorArmor, SaviorBoots, SaviorPants, + Skypiercer; + + // Tweaked items + if (getitemoptionidbyindex(.@id, 0) <= 0 && !is_master() && array_find(.@AlwaysTweaks, .@x) < 0) { + mesc l("This item have no bonuses, and cannot be tweaked."), 1; + return false; + } + + // Take the money and AP away + POL_PlayerMoney(.@price); + if (.@x != Lightbringer) + SMITH_TWEAKS+=1; + + // Apply the bonuses. This will only loop if `continue;` is cast. + // `continue` will only be cast if .@retry is set + do + { + .@retry-=1; + // Check if you fail + if (!csys_Check(.@id)) { + mesc l("YOU FAIL! It is a simple item now."), 1; + if (.@retry) { + mesc l("...Automatically retrying..."); + continue; + } + return false; + } + + csys_Apply(.@id); + mesc l("SUCCESS! Congratulations, the item was improved!"), 3; + if (.@retry) { + next; + mesc l("Do you want to re-roll?"), 1; + if (askyesno() == ASK_YES) { + continue; + } + } + return true; + } while (.@retry > 0); +} diff --git a/npc/dev/ci_test.txt b/npc/dev/ci_test.txt new file mode 100644 index 0000000..c1735bc --- /dev/null +++ b/npc/dev/ci_test.txt @@ -0,0 +1,48 @@ +//================= Hercules Script ======================================= +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2015 Hercules Dev Team +//= Copyright (C) 2014 Haru +//= +//= Hercules 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/>. +//========================================================================= +//= Script engine self-tests, CI integration +//================= Description =========================================== +//= This script depends on npc/dev/test.txt, and extends it so that it can +//= be called in a CI environment and returns an appropriate value upon +//= termination. +//================= Current Version ======================================= +//= 1.0 +//================= Additional Comments =================================== +//= This script requires the script_mapquit plugin to be loaded. +//= Usage: ./map-server --load-plugin script_mapquit --load-script npc/dev/test.txt --load-script npc/dev/ci_test.txt +//========================================================================= + +- script HerculesSelfTestCI FAKE_NPC,{ + end; + +OnInit: + $@CI_MODE = true; + .@val = callfunc("HerculesSelfTestHelper"); + mapquit(.@val); + end; +} diff --git a/npc/dev/test.txt b/npc/dev/test.txt new file mode 100644 index 0000000..f98651d --- /dev/null +++ b/npc/dev/test.txt @@ -0,0 +1,814 @@ +//================= Hercules Script ======================================= +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2013-2015 Hercules Dev Team +//= Copyright (C) 2013-2015 Haru +//= +//= Hercules 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/>. +//========================================================================= +//= Script engine self-tests +//================= Description =========================================== +//= Script to test operators and possibly other elements of the script +//= engine, useful for regression testing. +//================= Current Version ======================================= +//= 2.0 +//========================================================================= + +function script F_TestReturnValue { + return getarg(0); +} + +function script F_TestScopeVars { + .@x = 2; + return .@x+1; +} + +function script F_TestNPCVars { + .xt = 2; + return .xt+1; +} + +function script F_TestDeepNestedScope { + if (getarg(0) <= 0) + return getarg(1); // Stop recursion + if (getarg(1)) + return callfunc("F_TestDeepNestedScope", getarg(0)-1, getarg(1)); // Recursion step + .@x = 1; + return callfunc("F_TestDeepNestedScope", getarg(0)-1, .@x); // First step +} + +function script F_TestDeepNestedScopeNPC2 { + if (getarg(0) <= 0) + return getarg(1); // Stop recursion + if (getarg(1)) + return callfunc("F_TestDeepNestedScopeNPC", getarg(0)-1, getarg(1)); // Recursion step + .xt = 1; + return callfunc("F_TestDeepNestedScopeNPC", getarg(0)-1, .xt); // First step +} + +function script F_TestDeepNestedScopeNPC { + if (getarg(0) <= 0) + return getarg(1); // Stop recursion + if (getarg(1)) + return callfunc("F_TestDeepNestedScopeNPC2", getarg(0)-1, getarg(1)); // Recursion step + .xt = 1; + return callfunc("F_TestDeepNestedScopeNPC2", getarg(0)-1, .xt); // First step +} + +function script F_TestNestedScope { + .@x = 1; + .@y = callfunc("F_TestReturnValue", .@x); + return .@y; +} + +function script F_TestNestedScopeNPC { + .xt = 1; + .yt = callfunc("F_TestReturnValue", .xt); + return .yt; +} + +function script F_TestArrayRefs { + return getelementofarray(getarg(0), getarraysize(getarg(0)) - 1); +} + +function script F_TestReturnArrayRef { + setarray getarg(0), 5, 6, 7, 8; + return getarraysize(getarg(0)); +} + +function script F_TestScopeArrays { + setarray .@x, 1, 2, 3, 4; + copyarray .@y, getarg(0), getarraysize(getarg(0)); + return getarraysize(.@y); +} + +function script F_TestNPCArrays { + setarray .xt, 1, 2, 3, 4; + copyarray .yt, getarg(0), getarraysize(getarg(0)); + return getarraysize(.yt); +} + +function script F_TestVarOfAnotherNPC { + return getvariableofnpc(.xt, getarg(0)); +} + +- script TestVarOfAnotherNPC FAKE_NPC,{ + // Used to test getvariableofnpc() + end; +} + +function script HerculesSelfTestHelper { + if (.once > 0) + return .errors; + .once = 1; + .errors = 0; + + // Callsub (basic) + callsub(OnCheck, "Callsub", 1, 1); + callsub(OnCheck, "Callsub (getarg default values)", 1); + + + // Array subscript + setarray .@a, 3, 2, 1; + callsub(OnCheck, "Array subscript", .@a[2]); + + + // Increment and decrement operators ++, -- + .@x = 1; + .@y = .@x++; // .@y = .@x; .@x = .@x + 1; + callsub(OnCheck, "Suffix increment ++", .@y); + callsub(OnCheck, "Suffix increment ++", .@x, 2); + .@x = 1; + .@y = .@x--; // .@y = .@x; .@x = .@x - 1; + callsub(OnCheck, "Suffix decrement --", .@y); + callsub(OnCheck, "Suffix decrement --", .@x, 0); + .@x = 0; + .@y = ++.@x; // .@x = .@x + 1; .@y = .@x; + callsub(OnCheck, "Prefix increment ++", .@y); + callsub(OnCheck, "Prefix increment ++", .@x); + .@x = 2; + .@y = --.@x; // .@x = .@x - 1; .@y = .@x; + callsub(OnCheck, "Prefix decrement --", .@y); + callsub(OnCheck, "Prefix decrement --", .@x); + + // Order of [] and --/++ + .@a[1] = 0; + .@a[1]++; // .@a[1] = .@a[1] + 1; + callsub(OnCheck, "Order of [] and ++", .@a[1]); + .@a[1] = 2; + .@a[1]--; // .@a[1] = .@a[1] - 1; + callsub(OnCheck, "Order of [] and --", .@a[1]); + + + // Unary operators -, !, ~ + .@x = 1; + .@y = -.@x; // .@y = 0 - .@x; + callsub(OnCheck, "Unary operator -", .@y, -1); + .@x = 1; + .@y = !.@x; // if(.@x == 0) .@y = 1; else .@y = 0; + callsub(OnCheck, "Unary operator !", .@y, 0); + .@x = 0x00000001; + .@y = ~.@x; // One's complement of 0x00000001 is 0xfffffffe, which is -2 + callsub(OnCheck, "Unary operator ~", .@y, -2); + + // Associativity of unary operators -, !, ~ + .@x = 1; + .@y = ~ ! .@x; // .@y = ~(!.@x); + callsub(OnCheck, "Associativity of unary ~ and !", .@y, -1); + .@x = 0; + .@y = - ! .@x; // .@y = -(!.@x); + callsub(OnCheck, "Associativity of unary - and !", .@y, -1); + .@x = 1; + .@y = ~ - .@x; // .@y = ~(-.@x); + callsub(OnCheck, "Associativity of unary ~ and -", .@y, 0); + .@x = 1; + .@y = - ~ .@x; // .@y = -(~.@x); + callsub(OnCheck, "Associativity of unary - and ~", .@y, 2); + + // Order of unary -, !, ~ and prefix/suffix ++/-- + .@x = 2; + .@y = - --.@x; // .@y = -(--.@x); + callsub(OnCheck, "Order of unary - and prefix --", .@y, -1); + callsub(OnCheck, "Order of unary - and prefix --", .@x); + .@x = 1; + .@y = - .@x--; // .@y = -(.@x--); + callsub(OnCheck, "Order of unary - and suffix --", .@y, -1); + callsub(OnCheck, "Order of unary - and suffix --", .@x, 0); + .@x = 0; + .@y = - ++.@x; // .@y = -(++.@x); + callsub(OnCheck, "Order of unary - and prefix ++", .@y, -1); + callsub(OnCheck, "Order of unary - and prefix ++", .@x); + .@x = 1; + .@y = - .@x++; // .@y = -(.@x++); + callsub(OnCheck, "Order of unary - and suffix ++", .@y, -1); + callsub(OnCheck, "Order of unary - and suffix ++", .@x, 2); + .@x = 1; + .@y = !--.@x; // .@y = !(--.@x); + callsub(OnCheck, "Order of unary ! and prefix --", .@y); + callsub(OnCheck, "Order of unary ! and prefix --", .@x, 0); + .@x = 1; + .@y = !.@x--; // .@y = !(.@x--); + callsub(OnCheck, "Order of unary ! and suffix --", .@y, 0); + callsub(OnCheck, "Order of unary ! and suffix --", .@x, 0); + .@x = 0; + .@y = !++.@x; // .@y = !(++.@x); + callsub(OnCheck, "Order of unary ! and prefix ++", .@y, 0); + callsub(OnCheck, "Order of unary ! and prefix ++", .@x); + .@x = 0; + .@y = !.@x++; // .@y = !(.@x++); + callsub(OnCheck, "Order of unary ! and suffix ++", .@y); + callsub(OnCheck, "Order of unary ! and suffix ++", .@x); + .@x = 2; + .@y = ~--.@x; // .@y = ~(--.@x); + callsub(OnCheck, "Order of unary ~ and prefix --", .@y, -2); + callsub(OnCheck, "Order of unary ~ and prefix --", .@x, 1); + .@x = 1; + .@y = ~.@x--; // .@y = ~(.@x--); + callsub(OnCheck, "Order of unary ~ and suffix --", .@y, -2); + callsub(OnCheck, "Order of unary ~ and suffix --", .@x, 0); + .@x = 0; + .@y = ~++.@x; // .@y = ~(++.@x); + callsub(OnCheck, "Order of unary ~ and prefix ++", .@y, -2); + callsub(OnCheck, "Order of unary ~ and prefix ++", .@x, 1); + .@x = 1; + .@y = ~.@x++; // .@y = ~(.@x++); + callsub(OnCheck, "Order of unary ~ and suffix ++", .@y, -2); + callsub(OnCheck, "Order of unary ~ and suffix ++", .@x, 2); + + // Binary *, /, % operators + .@x = 2 * 3; // .@x = 6; + callsub(OnCheck, "Binary * operator", .@x, 6); + .@x = 7 / 2; // .@x = 3; + callsub(OnCheck, "Binary / operator", .@x, 3); + .@x = 7 % 2; // .@x = 1; + callsub(OnCheck, "Binary % operator", .@x, 1); + + // Associativity of *, /, % + .@x = 8 * 3 / 2; // .@x = (8 * 3) / 2; + callsub(OnCheck, "Associativity of * and /", .@x, 12); + + // Order of binary *%/ and unary !-~ + .@x = 2 * ! 3; // .@x = 2 * (!3); + callsub(OnCheck, "Order of binary * and unary !", .@x, 0); + .@x = ~ 1 * 2; // .@x = (~1) * 2; + callsub(OnCheck, "Order of unary ~ and binary *", .@x, -4); + + + // Binary +, - operators + .@x = 1 + 3; // .@x = 4; + callsub(OnCheck, "Binary + operator", .@x, 4); + .@x = 1 - 3; // .@x = -2; + callsub(OnCheck, "Binary - operator", .@x, -2); + + // Associativity of +,- + .@x = 0x7fffffff - 0x7ffffff0 + 1; // .@x = (0x7fffffff - 0x7ffffff0) + 1; (without overflow) + callsub(OnCheck, "Associativity of + and -", .@x, 16); + + // Order of +, - and *, /, % + .@x = 1 + 3 * 2; // .@x = 1 + (3 * 2); + callsub(OnCheck, "Order of + and *", .@x, 7); + + + // << and >> operators + .@x = 1<<3; // .@x = 1*2*2*2; + callsub(OnCheck, "Left shift << operator", .@x, 8); + .@x = 12>>2; // .@x = 12/2/2; + callsub(OnCheck, "Right shift >> operator", .@x, 3); + + // Associativity of << and >> + .@x = 0x40000000 >> 4 << 2; // .@x = (0x40000000 >> 4) << 2 + callsub(OnCheck, "Associativity of >> and <<", .@x, 0x10000000); + + // Order of <</>> and +/- + .@x = 4 << 2 + 1; // .@x = 4 << (2+1); + callsub(OnCheck, "Order of << and +", .@x, 32); + + + // <, <=, >, >= operators + .@x = (1 < 2); // true + .@y = (2 < 2); // false + callsub(OnCheck, "< operator", .@x); + callsub(OnCheck, "< operator", .@y, 0); + .@x = (1 <= 2); // true + .@y = (2 <= 2); // true + callsub(OnCheck, "<= operator", .@x); + callsub(OnCheck, "<= operator", .@y); + .@x = (2 > 1); // true + .@y = (2 > 2); // false + callsub(OnCheck, "> operator", .@x); + callsub(OnCheck, "> operator", .@y, 0); + .@x = (2 >= 1); // true + .@y = (2 >= 2); // true + callsub(OnCheck, ">= operator", .@x); + callsub(OnCheck, ">= operator", .@y); + + // Associativity of <,<=,>,>= + .@x = 1 > 0 > 0; // (1 > 0) > 0 --> 1 > 0 --> true + callsub(OnCheck, "Associativity of > operators", .@x); + + // Order of >>/<< and </<=/>/>= + .@x = 1 < 1 << 2; // .@x = 1 < (1<<2); + callsub(OnCheck, "Order of < and <<", .@x); + + + // ==, !=, ~=, ~! operators + .@x = (0 == 0); // true + .@y = (1 == 0); // false + callsub(OnCheck, "== operator", .@x); + callsub(OnCheck, "== operator", .@y, 0); + .@x = (1 != 0); // true + .@y = (1 != 1); // false + callsub(OnCheck, "!= operator", .@x); + callsub(OnCheck, "!= operator", .@y, 0); + .@x$ = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. " + "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " + "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + .@y = (.@x$ ~= "^Lorem.*, ([a-z]*).*(Duis).* ([a-z.]*)$"); + callsub(OnCheck, "~= operator", .@y, 4); + callsub(OnCheck, "~= operator", $@regexmatchcount, 4); + if( $@regexmatchcount == 4 ) { + callsub(OnCheck, "~= operator", $@regexmatch$[0], .@x$); + callsub(OnCheck, "~= operator", $@regexmatch$[1], "quis"); + callsub(OnCheck, "~= operator", $@regexmatch$[2], "Duis"); + callsub(OnCheck, "~= operator", $@regexmatch$[3], "laborum."); + } + .@y = (.@x$ ~! "^Not Lorem.*, ([a-z]*).*(Duis).* ([a-z.]*)$"); + callsub(OnCheck, "~! operator", .@y); + + // Associativity of ==, != + .@x = (1 == 0 == 0); // (1 == 0) == 0 --> 0 == 0 --> 1 + .@y = (1 != 0 == 0); // (1 != 0) == 0 --> 1 == 0 --> 0 + callsub(OnCheck, "Associativity of != and == operators", .@x); + callsub(OnCheck, "Associativity of != and == operators", .@y, 0); + + // Order of </<=/>/>= and ==/!= + .@x = (1 == 2 > 1); // true + .@y = (1 < 2 == 1); // true + callsub(OnCheck, "Order of <,>,==", .@x); + callsub(OnCheck, "Order of <,>,==", .@y); + + + .@x$ = "string " + "concatenation" /* test */ " succeeded"; + callsub(OnCheckStr, "String concatenation", .@x$, "string concatenation succeeded"); + + + // Bitwise & operator + .@x = (7&4); // 0111 & 0100 --> 0100 + .@y = (4&1); // 0100 & 0001 --> 0000 + callsub(OnCheck, "Bitwise & operator", .@x, 4); + callsub(OnCheck, "Bitwise & operator", .@y, 0); + + // Order of & and ==/!= + .@x = (4 == 7 & 4); // (4 == 7)&4 + .@y = (1 & 3 != 1); // 1 & (3 != 1) + callsub(OnCheck, "Order of ==/!= and &", .@x, 0); + callsub(OnCheck, "Order of ==/!= and &", .@y); + + + // Bitwise ^ operator + .@x = (3^1); // 0011 ^ 0001 --> 0010 + callsub(OnCheck, "Bitwise ^ operator", .@x, 2); + + // Order of ^ and & + .@x = (0 & 2 ^ 2); // (0 & 2) ^ 2 --> (0000 & 0010) | 0010 --> 0000 ^ 0010 --> 0010 + .@y = (2 ^ 2 & 0); // 2 ^ (2 & 0) --> 0010 | (0010 & 0000) --> 0010 ^ 0000 --> 0010 + callsub(OnCheck, "Order of ^ and &", .@x, 2); + callsub(OnCheck, "Order of ^ and &", .@y, 2); + + + // Bitwise | operator + .@x = (3|4); // 0011 | 0100 --> 0111 + .@y = (4|1); // 0100 | 0001 --> 0101 + callsub(OnCheck, "Bitwise | operator", .@x, 7); + callsub(OnCheck, "Bitwise | operator", .@y, 5); + + // Order of ^ and | + .@x = (2 ^ 2 | 2); // (2 ^ 1) | 4 --> (0010 ^ 0010) | 0010 --> 0000 | 0010 --> 0010 + .@y = (2 | 2 ^ 2); // 4 | (1 ^ 2) --> 0010 | (0010 ^ 0010) --> 0010 | 0000 --> 0010 + callsub(OnCheck, "Order of | and ^", .@x, 2); + callsub(OnCheck, "Order of | and ^", .@y, 2); + + + // Logical && operator + .@x = (1 && 1); // true + .@y = (0 && 1); // false + callsub(OnCheck, "Logical && operator", .@x); + callsub(OnCheck, "Logical && operator", .@y, 0); + + // Associativity of && and short-circuit + .@x = 0; + .@y = (1 && 0 && (.@x = 1)); // should short circuit as false before evaluating the assignment + //FIXME callsub(OnCheck, "Short-circuit of &&", .@x, 0); + callsub(OnCheck, "Associativity of &&", .@y, 0); + + // Order of bitwise | and logical && + .@x = (1 && 0 | 4); // 1 && (0|4) + .@y = (4 | 0 && 1); // (4|0) && 1 + callsub(OnCheck, "Order of && and |", .@x); + callsub(OnCheck, "Order of && and |", .@y); + + + // Logical || operator + .@x = (1 || 1); // true + .@y = (0 || 1); // true + callsub(OnCheck, "Logical || operator", .@x); + callsub(OnCheck, "Logical || operator", .@y); + + // Associativity of || and short-circuit + .@x = 0; + .@y = (1 || 0 || (.@x = 1)); // should short circuit as true before evaluating the assignment + //FIXME callsub(OnCheck, "Short-circuit of ||", .@x, 0); + callsub(OnCheck, "Associativity of ||", .@y); + + // Order of logical && and || + .@x = (0 && 1 || 1); // (0 && 1) || 1 + .@y = (1 || 1 && 0); // 1 || (1 && 0) + callsub(OnCheck, "Order of && and ||", .@x); + callsub(OnCheck, "Order of && and ||", .@y); + + // Ternary conditional operator ?: + .@x = (1 ? 2 : 3); // 2 + .@y = (0 ? 2 : 3); // 3 + callsub(OnCheck, "Ternary conditional operator", .@x, 2); + callsub(OnCheck, "Ternary conditional operator", .@y, 3); + + // Associativity of ?: + .@x = (1 ? 2 : 0 ? 3 : 4); + .@y = (1 ? 1 ? 2 : 3 : 5); + callsub(OnCheck, "Associativity of ?:", .@x, 2); + callsub(OnCheck, "Associativity of ?:", .@y, 2); + + // Order of logical || and ternary ?: + .@x = (1 ? 0 : 0 || 1); // 1 ? 0 : (0 || 1) --> false + callsub(OnCheck, "Order of || and ?:", .@x, 0); + + + // Assignment operators + .@x = 1; + callsub(OnCheck, "Direct assignment operator =", .@x); + .@x += 7; // 1 + 7 + callsub(OnCheck, "Assignment by sum +=", .@x, 8); + .@x -= 1; // 8 - 1 + callsub(OnCheck, "Assignment by difference -=", .@x, 7); + .@x *= 2; // 7 * 2 + callsub(OnCheck, "Assignment by product *=", .@x, 14); + .@x /= 2; // 14 / 2 + callsub(OnCheck, "Assignment by quotient /=", .@x, 7); + .@x %= 4; // 7 % 4 + callsub(OnCheck, "Assignment by remainder %=", .@x, 3); + .@x <<= 2; // 3 << 2 + callsub(OnCheck, "Assignment by bitwise left shift <<=", .@x, 12); + .@x >>= 1; // 12 >> 1 + callsub(OnCheck, "Assignment by bitwise right shift >>=", .@x, 6); + .@x &= 5; // 6 & 5 (0110 & 0101 --> 0100) + callsub(OnCheck, "Assignment by bitwise and &=", .@x, 4); + .@x ^= 5; // 4 ^ 5 (0100 ^ 0101 --> 0001) + callsub(OnCheck, "Assignment by bitwise xor ^=", .@x, 1); + .@x |= 2; // 1 | 2 (0001 | 0010 --> 0011) + callsub(OnCheck, "Assignment by bitwise or |=", .@x, 3); + + // Associativity of assignment operators + .@x = 0; .@y = 0; + .@x = .@y = 1; + callsub(OnCheck, "Associativity of =", .@x); + callsub(OnCheck, "Associativity of =", .@y); + .@x = 0; .@y = 1; + .@x = .@y += 4; + callsub(OnCheck, "Associativity of = and +=", .@x, 5); + callsub(OnCheck, "Associativity of = and +=", .@y, 5); + .@x = 5; .@y = 3; + .@z = 8; + .@x *= .@y += 1; + callsub(OnCheck, "Associativity of *= and +=", .@x, 20); + callsub(OnCheck, "Associativity of *= and +=", .@y, 4); + + .@x = 1; .@y = 3; + .@x += .@y * 10; + callsub(OnCheck, "Order of += and *", .@x, 31); + .@x = 1; .@y = 3; + .@x = .@y != 3 ? .@y = 2 : 4; + callsub(OnCheck, "Order of = and ?:", .@x, 4); + // FIXME callsub(OnCheck, "Short-circuit of ?:", .@y, 3); + + .@x = 0; + if (0) + if (1) + .@x = 2; + else + .@x = 3; + callsub(OnCheck, "Dangling else", .@x, 0); + + + // Array operations + .@x[0] = 1; + callsub(OnCheck, "Array size (single value)", getarraysize(.@x), 1); + .@x[0] = 0; + callsub(OnCheck, "Array size (single value removal)", getarraysize(.@x), 0); + + .@x[0] = 1; + .@x[1] = 2; + .@x[2] = 3; + .@x[5] = 4; + .@x[8] = 5; + .@x[9] = 0; + setarray .@y[0], 1, 2, 3, 0, 0, 4, 0, 0, 5; + callsub(OnCheck, "Array size (assignment)", getarraysize(.@x), 9); + callsub(OnCheck, "Array size (setarray)", getarraysize(.@y), 9); + for (.@i = 0; .@i < 10; ++.@i) { + callsub(OnCheck, "Array subscript and setarray [" + .@i + "]", .@x[.@i], .@y[.@i]); + } + + cleararray .@x[1], 8, 6; + callsub(OnCheck, "cleararray (value) [0]", .@x[0], 1); + for (.@i = 1; .@i < 7; ++.@i) { + callsub(OnCheck, "cleararray (value) [" + .@i + "]", .@x[.@i], 8); + } + callsub(OnCheck, "cleararray (value) [7]", .@x[7], 0); + callsub(OnCheck, "cleararray (value) [8]", .@x[8], 5); + callsub(OnCheck, "cleararray (value) [9]", .@x[9], 0); + + cleararray .@x, 0, getarraysize(.@x); + cleararray .@y, 0, getarraysize(.@y); + callsub(OnCheck, "cleararray and getarraysize", getarraysize(.@x), 0); + for (.@i = 0; .@i < 10; ++.@i) { + callsub(OnCheck, "cleararray (zero) [" + .@i + "]", .@x[.@i], 0); + } + + cleararray .@x, 0, getarraysize(.@x); + setarray .@x[1], 1, 2, 0, 0, 0, 6, 7, 8, 0, 0, 0, 13, 14, 15, 16; + deletearray .@x; + callsub(OnCheck, "deletearray (clear) and getarraysize", getarraysize(.@x), 0); + for (.@i = 0; .@i < 18; ++.@i) { + callsub(OnCheck, "deletearray (clear) [" + .@i + "]", .@x[.@i], 0); + } + + deletearray .@x; + deletearray .@y; + setarray .@x[1], 1, 2, 0, 0, 0, 6, 7, 8, 0, 0, 0, 0, 13, 14, 15, 16; + setarray .@y, 0, 1, 2, 0, 0, 0, 6, 7, 8, 0, 0, 0, 13, 14, 15, 16; + deletearray .@x[9], 1; + callsub(OnCheck, "deletearray (single) and getarraysize", getarraysize(.@x), 16); + for (.@i = 0; .@i < 18; ++.@i) { + callsub(OnCheck, "deletearray (single) [" + .@i + "]", .@x[.@i], .@y[.@i]); + } + + deletearray .@x; + deletearray .@y; + setarray .@x[1], 1, 2, 0, 0, 0, 6, 7, 8, 0, 0, 0, 13, 14, 15, 16; + setarray .@y, 0, 1, 6, 7, 8, 0, 0, 0, 13, 14, 15, 16; + deletearray .@x[2], 4; + callsub(OnCheck, "deletearray (multiple) and getarraysize", getarraysize(.@x), 12); + for (.@i = 0; .@i < 18; ++.@i) { + callsub(OnCheck, "deletearray (multiple) [" + .@i + "]", .@x[.@i], .@y[.@i]); + } + + deletearray .@x; + deletearray .@y; + setarray .@x[1], 1, 2, 0, 0, 0, 6, 7, 8, 0, 0, 0, 13, 14, 15, 16; + setarray .@y, 0, 1; + deletearray .@x[2], 1000; + callsub(OnCheck, "deletearray (large count) and getarraysize", getarraysize(.@x), 2); + for (.@i = 0; .@i < 18; ++.@i) { + callsub(OnCheck, "deletearray (large count) [" + .@i + "]", .@x[.@i], .@y[.@i]); + } + + deletearray .@x; + deletearray .@y; + setarray .@x[1], 1, 2, 0, 0, 0, 6, 7, 8, 0, 0, 0, 13, 14, 15, 16; + setarray .@y, 0, 1; + deletearray .@x[2]; + callsub(OnCheck, "deletearray (truncate) and getarraysize", getarraysize(.@x), 2); + for (.@i = 0; .@i < 18; ++.@i) { + callsub(OnCheck, "deletearray (truncate) [" + .@i + "]", .@x[.@i], .@y[.@i]); + } + + deletearray .@x; + .@x[1] = 2; + .@x[65536] = 1; + callsub(OnCheck, "large array index", .@x[65536], 1); + callsub(OnCheck, "large array index and getarraysize", getarraysize(.@x), 65537); + .@x[65536] = 0; + callsub(OnCheck, "large array index (shrink)", .@x[65536], 0); + callsub(OnCheck, "large array index and getarraysize (shrink)", getarraysize(.@x), 2); + .@x[1] = 0; + callsub(OnCheck, "array shrink", .@x[1], 0); + callsub(OnCheck, "array shrink and getarraysize", getarraysize(.@x), 0); + + // min and max + callsub(OnCheck, "min()", min(5, -10, 8, 3, -2, 1000), -10); + callsub(OnCheck, "max()", max(5, -10, 8, 3, -2, 1000), 1000); + + + // Constants + callsub(OnCheck, "'true' constant", true, 1); + callsub(OnCheck, "'false' constant", false, 0); + callsub(OnCheck, "'Piou' mob ID", Piou, 1002); + callsub(OnCheck, "'NV_BASIC' skill ID", NV_BASIC, 1); + callsub(OnCheck, "'Acorn' item ID", Acorn, 501); + callsub(OnCheck, "'Bread' item ID", Bread, 502); + + + // setd/getd + .@x = 1; .@x$ = ".@x"; + callsub(OnCheck, "getd", getd(".@x"), 1); + callsub(OnCheck, "getd arguments", getd(.@x$), 1); + .@y = 0; .@y$ = ".@y"; + setd(".@y", .@x); + callsub(OnCheck, "setd", .@y, 1); + setd(.@y$, 2); + callsub(OnCheck, "setd arguments", .@y, 2); + set getd(".@x"), getd(".@y"); + callsub(OnCheck, "set getd", .@x, .@y); + .@y = 1; + setd(".@x", getd(".@y")); + callsub(OnCheck, "setd getd", .@x, .@y); + + // getvariableofnpc + .xt = 2; + set getvariableofnpc(.xt, "TestVarOfAnotherNPC"), 1; + callsub(OnCheck, "Setting NPC variables of another NPC", getvariableofnpc(.xt, "TestVarOfAnotherNPC"), 1); + callsub(OnCheck, "Setting NPC variables of another NPC (local variable overwrite check)", .xt, 2); + + // Callsub (advanced) + callsub(OnCheck, "Callsub return value", callsub(OnTestReturnValue, 1)); + .@x = 1; + callsub(OnCheck, "Callsub return with scope variables", callsub(OnTestScopeVars), 3); + callsub(OnCheck, "Callsub (parent scope vars isolation)", .@x, 1); + callsub(OnCheck, "Callsub (nested scopes)", callsub(OnTestNestedScope), 1); + callsub(OnCheck, "Callsub (deeply nested scopes)", callsub(OnTestDeepNestedScope, 30, 0), 1); + .@x = 1; + .@y = callsub(OnSetReference, .@x); + callsub(OnCheck, "Callsub (setting references)", .@y, 2); + callsub(OnCheck, "Callsub (setting references)", .@x, 2); + deletearray .@x; + setarray .@x, 1, 2, 3, 4; + callsub(OnCheck, "Callsub (array references)", callsub(OnTestArrayRefs, .@x), 4); + deletearray .@x; + .@y = callsub(OnTestReturnArrayRef, .@x); + callsub(OnCheck, "Callsub return array references (size check)", getarraysize(.@x), .@y); + callsub(OnCheck, "Callsub return array references", getelementofarray(.@x, 3), 8); + deletearray .@x; + deletearray .@y; + setarray .@x, 1, 2; + .@z = getarraysize(.@x); + setarray .@y, 5, 6, 7, 8, 9; + callsub(OnCheck, "Callsub (copyarray from reference with the same name)", getarraysize(.@y), callsub(OnTestScopeArrays, .@y)); + callsub(OnCheck, "Callsub (parent array vars isolation)", getarraysize(.@x), .@z); + deletearray .@x; + deletearray .@y; + .xt = 2; + set getvariableofnpc(.xt, "TestVarOfAnotherNPC"), 1; + callsub(OnCheck, "Callsub (return NPC variables from another NPC)", callsub(OnTestVarOfAnotherNPC, "TestVarOfAnotherNPC"), 1); + callsub(OnCheck, "Callsub (return NPC variables from another NPC - local variable overwrite check)", .xt, 2); + + // Callfunc + callsub(OnCheck, "Callfunc return value", callfunc("F_TestReturnValue", 1)); + .@x = 1; + callsub(OnCheck, "Callfunc return with scope variables", callfunc("F_TestScopeVars"), 3); + callsub(OnCheck, "Callfunc (parent scope vars isolation)", .@x, 1); + callsub(OnCheck, "Callfunc (nested scopes)", callfunc("F_TestNestedScope"), 1); + callsub(OnCheck, "Callfunc (deeply nested scopes)", callfunc("F_TestDeepNestedScope", 30, 0), 1); + deletearray .@x; + setarray .@x, 1, 2, 3, 4; + callsub(OnCheck, "Callfunc (array references)", callfunc("F_TestArrayRefs", .@x), 4); + deletearray .@x; + .@y = callfunc("F_TestReturnArrayRef", .@x); + callsub(OnCheck, "Callfunc return array references (size check)", getarraysize(.@x), .@y); + callsub(OnCheck, "Callfunc return array references", getelementofarray(.@x, 3), 8); + deletearray .@x; + deletearray .@y; + setarray .@x, 1, 2; + .@z = getarraysize(.@x); + setarray .@y, 5, 6, 7, 8, 9; + callsub(OnCheck, "Callfunc (copyarray from reference with the same name)", getarraysize(.@y), callfunc("F_TestScopeArrays", .@y)); + callsub(OnCheck, "Callfunc (parent array vars isolation)", getarraysize(.@x), .@z); + deletearray .@x; + deletearray .@y; + .xt = 1; + callsub(OnCheck, "Callfunc return with NPC variables", callfunc("F_TestNPCVars"), 3); + callsub(OnCheck, "Callfunc (parent NPC vars isolation)", .xt, 1); + callsub(OnCheck, "Callfunc (nested scopes and NPC variables)", callfunc("F_TestNestedScopeNPC"), 1); + callsub(OnCheck, "Callfunc (deeply nested scopes and NPC variables)", callfunc("F_TestDeepNestedScopeNPC", 30, 0), 1); + deletearray .xt; + setarray .xt, 1, 2, 3, 4; + callsub(OnCheck, "Callfunc (array references and NPC variables)", callfunc("F_TestArrayRefs", .xt), 4); + deletearray .xt; + .yt = callfunc("F_TestReturnArrayRef", .xt); + callsub(OnCheck, "Callfunc return array references with NPC variables (size check)", getarraysize(.xt), .yt); + callsub(OnCheck, "Callfunc return array references wuth NPC variables", getelementofarray(.xt, 3), 8); + deletearray .xt; + deletearray .yt; + setarray .xt, 1, 2; + .@z = getarraysize(.@x); + setarray .yt, 5, 6, 7, 8, 9; + callsub(OnCheck, "Callfunc (copyarray from NPC variable reference with the same name)", getarraysize(.@y), callfunc("F_TestNPCArrays", .@y)); + callsub(OnCheck, "Callfunc (parent array NPC vars isolation)", getarraysize(.@x), .@z); + deletearray .xt; + deletearray .yt; + .xt = 2; + set getvariableofnpc(.xt, "TestVarOfAnotherNPC"), 1; + callsub(OnCheck, "Callfunc (return NPC variables from another NPC)", callfunc("F_TestVarOfAnotherNPC", "TestVarOfAnotherNPC"), 1); + callsub(OnCheck, "Callfunc (return NPC variables from another NPC - local variable overwrite check)", .xt, 2); + + callsub(OnCheckStr, "sprintf (%%)", sprintf("'%%'"), "'%'"); + callsub(OnCheckStr, "sprintf (%d)", sprintf("'%d'", 5), "'5'"); + callsub(OnCheckStr, "sprintf (neg. %d)", sprintf("'%d'", -5), "'-5'"); + callsub(OnCheckStr, "sprintf (%u)", sprintf("'%u'", 5), "'5'"); + callsub(OnCheckStr, "sprintf (%x)", sprintf("'%x'", 10), "'a'"); + callsub(OnCheckStr, "sprintf (%X)", sprintf("'%X'", 31), "'1F'"); + callsub(OnCheckStr, "sprintf (%s)", sprintf("'%s'", "Hello World!"), "'Hello World!'"); + callsub(OnCheckStr, "sprintf (%c)", sprintf("'%c'", "Hello World!"), "'H'"); + callsub(OnCheckStr, "sprintf (%+d)", sprintf("'%+d'", 5), "'+5'"); + callsub(OnCheckStr, "sprintf (%{n}d)", sprintf("'%5d'", 5), "' 5'"); + callsub(OnCheckStr, "sprintf (%-{n}d)", sprintf("'%-5d'", 5), "'5 '"); + callsub(OnCheckStr, "sprintf (%-+{n}d)", sprintf("'%-+5d'", 5), "'+5 '"); + callsub(OnCheckStr, "sprintf (%+0{n}d)", sprintf("'%+05d'", 5), "'+0005'"); + callsub(OnCheckStr, "sprintf (%0*d)", sprintf("'%0*d'", 5, 10), "'00010'"); + callsub(OnCheckStr, "sprintf (Two args)", sprintf("'%+05d' '%x'", 5, 0x7f), "'+0005' '7f'"); + callsub(OnCheckStr, "sprintf (positional)", sprintf("'%2$+05d'", 5, 6), "'+0006'"); + callsub(OnCheckStr, "sprintf (positional)", sprintf("'%2$s' '%1$c'", "First", "Second"), "'Second' 'F'"); + + if (.errors) { + consolemes(CONSOLEMES_DEBUG, "Script engine self-test [ \033[0;31mFAILED\033[0m ]"); + consolemes(CONSOLEMES_ERROR, "**** The test was completed with " + .errors + " errors. ****"); + } else { + consolemes(CONSOLEMES_DEBUG, "Script engine self-test [ \033[0;32mPASSED\033[0m ]"); + } + return .errors; + end; + +OnTestReturnValue: + return getarg(0); + +OnTestScopeVars: + .@x = 2; + return .@x+1; + +OnTestDeepNestedScope: + if (getarg(0) <= 0) + return getarg(1); // Stop recursion + if (getarg(1)) + return callsub(OnTestDeepNestedScope, getarg(0)-1, getarg(1)); // Recursion step + .@x = 1; + return callsub(OnTestDeepNestedScope, getarg(0)-1, .@x); // First step + +OnTestNestedScope: + .@x = 1; + .@y = callsub(OnTestReturnValue, .@x); + return .@y; + +OnTestArrayRefs: + return getelementofarray(getarg(0), getarraysize(getarg(0)) - 1); + +OnTestReturnArrayRef: + setarray getarg(0), 5, 6, 7, 8; + return getarraysize(getarg(0)); + +OnTestScopeArrays: + setarray .@x, 1, 2, 3, 4; + copyarray .@y, getarg(0), getarraysize(getarg(0)); + return getarraysize(.@y); + +OnTestVarOfAnotherNPC: + return getvariableofnpc(.xt, getarg(0)); + +OnReportError: + .@msg$ = getarg(0,"Unknown Error"); + .@val$ = getarg(1,""); + .@ref$ = getarg(2,""); + if (.errors == 1) + consolemes(CONSOLEMES_ERROR, "**** WARNING: Any self-test results past this point are unreliable because of previous errors. ****"); + consolemes(CONSOLEMES_ERROR, "Error: "+.@msg$+": '"+.@val$+"' (found) != '"+.@ref$+"' (expected)"); + ++.errors; + //end; + return; + +OnCheck: + .@msg$ = getarg(0,"Unknown Error"); + .@val = getarg(1,0); + .@ref = getarg(2,1); + if (.@val != .@ref) { + callsub(OnReportError, .@msg$, ""+.@val, ""+.@ref); // String coercion + } + return; +OnCheckStr: + .@msg$ = getarg(0,"Unknown Error"); + .@val$ = getarg(1,""); + .@ref$ = getarg(2,""); + if (.@val$ != .@ref$) { + callsub(OnReportError, .@msg$, .@val$, .@ref$); + } + return; +OnSetReference: + set getarg(0), getarg(0) + 1; + return getarg(0); +} + +- script HerculesSelfTest FAKE_NPC,{ + end; + +OnInit: + callfunc("HerculesSelfTestHelper"); + end; +} diff --git a/npc/functions/array.txt b/npc/functions/array.txt new file mode 100644 index 0000000..d433abd --- /dev/null +++ b/npc/functions/array.txt @@ -0,0 +1,464 @@ +// Evol Script +// Author: Gumi + +// array_pad(<array>, <size>, <value>) +// prepend or append <value> until the array is of <size> size +// returns the amount added on success, or false (0) if nothing changed + +function script array_pad { + .@index = getarrayindex(getarg(0)); // passed index + .@count = getarraysize(getarg(0)) - .@index; // actual size + .@size = getarg(1); // desired size + .@absolute = (.@size >= 0 ? .@size : -(.@size)); // |size| + .@delta = .@absolute - .@count; // amount to fill + + if (.@absolute <= .@count) { + return false; // nothing to do + } + + if (.@size < 0) { + copyarray(getelementofarray(getarg(0), .@index + .@delta), getarg(0), .@count); // shift to the right + cleararray(getarg(0), getarg(2), .@delta); // prepend + } else { + cleararray(getelementofarray(getarg(0), .@index + .@count), getarg(2), .@delta); // append + } + + return .@delta; +} + + + +// array_replace(<array>, <needle>, <replace>{, <neq>}) +// replace every occurence of <needle> with <replace> +// returns the number of replaced elements + +function script array_replace { + .@size = getarraysize(getarg(0)); + .@neq = getarg(3, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + set(getelementofarray(getarg(0), .@i), getarg(2)); + ++.@count; + } + } + + freeloop(false); + return .@count; +} + + + +// array_find(<array>, <needle>{, <neq>}) +// return the index of the first occurence of <needle> in <array> +// if not found it returns -1 + +function script array_find { + .@size = getarraysize(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + freeloop(false); + return .@i; + } + } + + freeloop(false); + return -1; +} + + + +// array_rfind(<array>, <needle>{, <neq>}) +// return the index of the last occurence of <needle> in <array> +// if not found it returns -1 + +function script array_rfind { + .@min = getarrayindex(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = (getarraysize(getarg(0)) - 1); .@i >= .@min; --.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + freeloop(false); + return .@i; + } + } + + freeloop(false); + return -1; +} + + + +// array_exists(<array>, <needle>{, <neq>}) +// return true or false accordingly if <needle> is found in <array> + +function script array_exists { + return array_find(getarg(0), getarg(1), getarg(2, false)) > -1; +} + + + +// array_count(<array>, <needle>{, <neq>}) +// counts the number of occurrence of <needle> in the <array> + +function script array_count { + .@size = getarraysize(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + ++.@count; + } + } + + freeloop(false); + return .@count; +} + + + +// array_entries(<array>) +// returns the number of non-empty entries + +function script array_entries { + if (isstr(getarg(0)) == 1) { + return array_count(getarg(0), "", true); + } + return array_count(getarg(0), 0, true); +} + + + +// array_remove(<array>, <needle>{, <neq>}) +// removes every occurrence of <needle> in the <array> while shifting left + +function script array_remove { + .@size = getarraysize(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + deletearray(getelementofarray(getarg(0), .@i), 1); // shift left + ++.@count; // increase the counter + --.@size; // reduce the size + --.@i; // step back + } + } + + freeloop(false); + return .@count; +} + + + +// array_reverse(<array>) +// reverses the array + +function script array_reverse { + .@index = getarrayindex(getarg(0)); + .@size = getarraysize(getarg(0)); + freeloop(true); + + for (.@i = .@index; .@i < ((.@size + .@index) / 2); ++.@i) { + swap(getelementofarray(getarg(0), .@i), getelementofarray(getarg(0), .@size + .@index - 1 - .@i)); // a <> b + } + + freeloop(false); + return true; +} + + + +// array_sum(<array>) +// return the sum of every element of the array + +function script array_sum { + .@size = getarraysize(getarg(0)); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + .@sum += getelementofarray(getarg(0), .@i); + } + + freeloop(false); + return .@sum; +} + + + +// array_difference(<array>) +// return the difference of every element of the array + +function script array_difference { + .@size = getarraysize(getarg(0)); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + .@diff -= getelementofarray(getarg(0), .@i); + } + + freeloop(false); + return .@diff; +} + + + +// array_shift(<array>) +// returns the first element of the array and removes it, while shifting left + +function script array_shift { + if (isstr(getarg(0)) == 1) { + .@val$ = getarg(0); + } else { + .@int = true; + .@val = getarg(0); + } + + deletearray(getarg(0), 1); // shift left + + return .@int ? .@val : .@val$; +} + + + +// array_unshift(<array>, <value>) +// adds <value> to the start of the array, while shifting right +// returns the new size + +function script array_unshift { + .@size = getarraysize(getarg(0)) + 1; + array_pad(getarg(0), -(.@size - getarrayindex(getarg(0))), getarg(1)); + return .@size; +} + + + +// array_pop(<array>) +// returns the last element of the array and removes it + +function script array_pop { + .@last = getarraysize(getarg(0)) - 1; + + if (isstr(getelementofarray(getarg(0), .@last)) == 1) { + .@val$ = getelementofarray(getarg(0), .@last); + } else { + .@int = true; + .@val = getelementofarray(getarg(0), .@last); + } + + deletearray(getelementofarray(getarg(0), .@last), 1); + + return .@int ? .@val : .@val$; +} + + + +// TODO: Rename to array_append >.< +// array_push(<array>, <value>) +// adds <value> to the end of the array +// returns the new size + +function script array_push { + .@size = getarraysize(getarg(0)); + set(getelementofarray(getarg(0), .@size), getarg(1)); + return .@size + 1; +} + + + +// array_shuffle(<array>) +// shuffles the array + +function script array_shuffle { + .@index = getarrayindex(getarg(0)); + .@size = getarraysize(getarg(0)) - .@index; + freeloop(true); + + if (isstr(getarg(0)) == 1) { + copyarray(.@tmp$[0], getarg(0), .@size); + for (; .@size >= 1; --.@size) { + set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp$[rand(.@size)])); + } + } else { + copyarray(.@tmp[0], getarg(0), .@size); + for (; .@size >= 1; --.@size) { + set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp[rand(.@size)])); + } + } + + freeloop(false); + return true; +} + + + +// array_unique(<array>{, <threshold>}) +// allows entries to appear up to <threshold> in the array + +function script array_unique { + .@size = getarraysize(getarg(0)); + .@max = getarg(1, 1); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + .@count = 1; + for (.@e = .@i + 1; .@e < .@size; ++.@e) { + if (getelementofarray(getarg(0), .@i) == getelementofarray(getarg(0), .@e)) { + if (++.@count >= .@max) { + deletearray(getelementofarray(getarg(0), .@e), 1); + ++.@removed; // increase counter + --.@size; // reduce size + --.@e; // step back + } + } + } + } + + freeloop(false); + return .@removed; +} + + + +// array_diff(<array1>, <array2>{, <array>...}, <array>) +// compares array1 against one or more other arrays and fills the last array +// with the values in array1 that are not present in any of the other arrays +// returns the number of entries not matching + +function script array_diff { + .@size = getarraysize(getarg(0)); + .@index = getarrayindex(getarg(0)); + freeloop(true); + + for (.@a = 1; .@a < (getargcount() - 1); ++.@a) { + for (.@i = .@index; .@i < .@size; ++.@i) { + if (!array_exists(getarg(.@a), getelementofarray(getarg(0), .@i))) { + array_push(getarg(getargcount() - 1), getelementofarray(getarg(0), .@i)); + ++.@count; + } + } + } + + freeloop(false); + return .@count; +} + + + +// array_filter(<array>, "<function>") +// filters the array using a callback function + +function script array_filter { + .@size = getarraysize(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + .@eq = callfunc(getarg(1), getelementofarray(getarg(0), .@i)) != false; + if ((.@neq && .@eq) || (!(.@neq) && !(.@eq))) { + deletearray(getelementofarray(getarg(0), .@i), 1); // shift left + ++.@count; // increase the counter + --.@size; // reduce the size + --.@i; // step back + } + } + + freeloop(false); + return .@count; +} + +// array_highest(<array>) +// Returns the index of the highest value in <array> +// NOTE: Array must be an INT array! + +function script array_highest { + .@size = getarraysize(getarg(0)); + .@win=0; + .@idx=0; + .@dw=false; + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if (getelementofarray(getarg(0), .@i) > .@win) { + .@win=getelementofarray(getarg(0), .@i); + .@idx=.@i; + if (.@dw) { + deletearray .@draw; + .@dw=false; + } + } else if (getelementofarray(getarg(0), .@i) == .@win) { + if (!.@dw) + array_push(.@draw, .@idx); + array_push(.@draw, .@i); + .@dw=true; + } + } + + // Will we return .@idx or do we need to draw a loot? + freeloop(false); + if (.@dw) + return any_of(.@draw); + else + return .@idx; +} + +// relative_array_random(<array: 0, {[value, probability]..}>) +// returns a random entry from the array, by relative probability +// the first key of the array should be 0 and every entries are a tuple +// of [value, probability] + +function script relative_array_random { + .@is_str = getdatatype(getarg(0)) & DATATYPE_STR; + .@total_prob = getelementofarray(getarg(0), 0); + .@initial_index = getarrayindex(getarg(0)); + .@initial_index = .@initial_index ? .@initial_index : 1; + freeloop(true); + + if (.@total_prob < 1 || getarg(1, false)) + { + // first calculation, or forced re-calculation + .@total_prob = 0; + .@size = getarraysize(getarg(0)); + + for (.@i = .@initial_index + 1; .@i < .@size; .@i += 2) { + if (.@is_str) { + .@total_prob += max(1, atoi(getelementofarray(getarg(0), .@i))); + } else { + .@total_prob += max(1, getelementofarray(getarg(0), .@i)); + } + } + + // we cache on the first key + set(getelementofarray(getarg(0), 0), .@total_prob); + } + + .@target_sum = rand(0, .@total_prob); + + for (.@i = .@initial_index; .@sum < .@target_sum; .@i += 2) { + if (.@is_str) { + .@sum += atoi(getelementofarray(getarg(0), .@i + 1)); + } else { + .@sum += getelementofarray(getarg(0), .@i + 1); + } + + if (.@sum >= .@target_sum) { + break; + } + } + + freeloop(false); + return getelementofarray(getarg(0), .@i); +} + diff --git a/npc/functions/asklanguage.txt b/npc/functions/asklanguage.txt new file mode 100644 index 0000000..32e0f7b --- /dev/null +++ b/npc/functions/asklanguage.txt @@ -0,0 +1,72 @@ +// TMW2 script +// Evol functions. +// Author: +// Reid, Jesusalva +// Description: +// Function setting the player language + +function script languagecode { + switch (Lang) { + case LANG_PTBR: + return "pt_BR"; + case LANG_FR: + return "fr"; + case LANG_DE: + return "de"; + case LANG_ES: + return "es"; + default: + return "en"; + } +} + +function script asklanguage { + + dispbottom col("We need help with translations. [@@help://translate|Learn more@@]", 1); + switch (getarg(0, LANG_IN_SHIP)) + { + case LANG_ON_SEA: + setarray .@messages$[0], "I hear you... (English)", // English + "Eu te ouço... (Português)", // Portuguese + "Je vous entends... (Français)", // French + "Ich höre euch... (Deutsch)", // German + "Te oigo... (Español)"; // Spanish + break; + case LANG_IN_SHIP: + setarray .@messages$[0], "I speak English.", // English + "Eu falo Português.", // Portuguese + "Je parle français.", // French + "Ich spreche Deutsch.", // German + "Hablo Español."; // Spanish + break; + default: + return; + } + + setarray .@flags$[0], "flags/en", + "flags/pt_BR", + "flags/fr", + "flags/de", + "flags/es"; + + .@menustr$ = ""; + .@separator$ = ":"; + + for (.@i = 0; .@i <= MAX_LANG; .@i++) + { + if (.@i == MAX_LANG) { + .@separator$ = ""; + } + .@menustr$ = .@menustr$ + .@flags$[.@i] + "|" + .@messages$[.@i] + .@separator$; + } + + select(.@menustr$); + + .@lang = @menu - 1; + + if (.@lang >= 0 || .@lang <= MAX_LANG) { + Lang = .@lang; + } + + return; +} diff --git a/npc/functions/aurora.txt b/npc/functions/aurora.txt new file mode 100644 index 0000000..42a5af2 --- /dev/null +++ b/npc/functions/aurora.txt @@ -0,0 +1,829 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// AURORA EVENT FRAMEWORK +// Previously known as FY:Event System +// +// Controls weekly events so Saulc, seeds and I can take vacations. +// See also: seasons.txt, command/event.txt, event.txt, 003-1/events.txt +// soulmenhir.txt, scoreboards.txt and, of course, the event maps (if any). +// Aurora Event Framework functions are called by event.txt +// specs override and is overriden by those defined in commands/event.txt + +// Variables: +// $MOST_HEROIC$ +// Updated every 2 weeks, the top 1 from previous event +// Only the hero may begin sieges against Fortress Town. +// Every NPC will recognize them, and Lightbringer will also +// pay special attention to them. +// $FYEVENT_CYCLE +// Current cycle. I thought in using gettimeparam(WEEK) but gave up. +// Q_AuroraEvent +// Quest Variable: DayCtrl, Score, ClaimedControl +// +// Event Specific Variables: +// $REGNUM_BLESSMAP$ +// Map under Regnum's Blessing +// $REGNUM_BLESSMAP_H$ +// Human-readable form of the map under Regnum's Blessing +// +// $FYREWARD_PT +// Array with minimum points for rewards (primary key) +// $FYREWARD_ID +// Array with claimable reward IDs +// $FYREWARD_AM +// Array with the amount of the reward ID you'll receive +// $FYLOGIN_PTS +// How many event score boost you'll receive for daily login +// +// $WORLDEXPO_ENEMY$ +// Name of the enemy responsible for ruining the World's Expo +// $RAIDING_BOSS$ +// Name of the enemy who is raiding down the world +// $DREAMTOWER_SAGE$ +// Name of the sage who owns the Dream Towers +// DTOWER_DAY (CharReg) +// Player variable which controls Dream Tower reset +// DTOWER_ROLL (CharReg) +// Player variable which controls Dream Ticket drop rate +// DTOWER_FLOOR (CharReg) +// Player variable which controls current Dream Tower floor + + +// AEF: BEGIN +function script FYNewEvent { + debugmes "\033[1mFY event is set to happen...\033[0m"; + // Aurora Events only begin after Liberation Day + if ($GAME_STORYLINE < 1) + return; + // Update the loop + $FYEVENT_CYCLE+=1; + // Overrides standard event system + $EVENT$=""; + // Delayed cleanup (Just in case) + DelQuestFromEveryPlayer(Q_AuroraEvent); + DelItemFromEveryPlayer(EventTreasure1); + DelItemFromEveryPlayer(EventTreasure2); + DelItemFromEveryPlayer(EventTreasure3); + DelItemFromEveryPlayer(EventFish); + DelItemFromEveryPlayer(EventOre); + DelItemFromEveryPlayer(BrokenMedal); + DelItemFromEveryPlayer(EventDreamTicket); + DelItemFromEveryPlayer(EventNaftalin); + // Olympics are costlier: Clean less often + if ($FYEVENT_CYCLE % 12 == 1) { + DelChrRegFromEveryPlayer("FYMOLY_ENBALL"); + DelChrRegFromEveryPlayer("FYMOLY_ICBOSS"); + DelChrRegFromEveryPlayer("FYMOLY_FLUFFY"); + DelChrRegFromEveryPlayer("FYMOLY_CHANTI"); + DelChrRegFromEveryPlayer("FYMOLY_ALCHMY"); + DelChrRegFromEveryPlayer("FYMOLY_MPWLVL"); + DelChrRegFromEveryPlayer("FYMOLY_MANAXP"); + DelChrRegFromEveryPlayer("FYMOLY_RACERS"); + DelChrRegFromEveryPlayer("FYMOLY_HOCUSM"); + DelChrRegFromEveryPlayer("FYMOLY_SURVIV"); + DelChrRegFromEveryPlayer("FYMOLY_FRIEND"); + DelChrRegFromEveryPlayer("FYMOLY_SPAMMY"); + // Same for Raids, Dream Towers and Gemini (they don't repeat) + DelChrRegFromEveryPlayer("DTOWER_DAY"); + DelChrRegFromEveryPlayer("DTOWER_ROLL"); + DelChrRegFromEveryPlayer("DTOWER_FLOOR"); + DelChrRegFromEveryPlayer("FYRAID_LV"); + DelChrRegFromEveryPlayer("GEMINI_DAY"); + } + deletearray $FYRAID_OWNER; + deletearray $FYRAID_TIME; + deletearray $FYRAID_HP; + deletearray $FYRAID_LV; + // Select the event + switch ($FYEVENT_CYCLE % 12) { + case 1: + $EVENT$="Kamelot"; + kamibroadcast("Kamelot Season is now open!", "Aurora Events"); + break; + case 2: + $EVENT$="Expo"; + callfunc("FYEConf_Expo"); + kamibroadcast("World Expo is now open!", "Aurora Events"); + break; + case 3: + $EVENT$="Regnum"; + callfunc("FYEConf_Regnum"); + kamibroadcast("Regnum's Blessing: "+$REGNUM_BLESSMAP_H$+" is now blessed!", "Aurora Events"); + break; + case 4: + $EVENT$="Raid"; + callfunc("FYEConf_Raid"); + kamibroadcast("Oh noes! "+$RAIDING_BOSS$+" is attacking our world!", "Aurora Events"); + break; + case 5: + $EVENT$="Candor"; + kamibroadcast("Candor Battle Season is now open!", "Aurora Events"); + break; + case 6: + $EVENT$="Mining"; + callfunc("FYEConf_Mining"); + kamibroadcast("Miners Union Research Request is now open!", "Aurora Events"); + break; + case 7: + $EVENT$="Celestia"; + kamibroadcast("Celestia Season is now open!", "Aurora Events"); + break; + case 8: + $EVENT$="Olympics"; + callfunc("FYEConf_Olympics"); + kamibroadcast("Magic Olympics are now open!", "Aurora Events"); + break; + case 9: + $EVENT$="Rebirth"; + kamibroadcast("Rebirth Season has begun!", "Aurora Events"); + break; + case 10: + $EVENT$="Fishing"; + callfunc("FYEConf_Fishing"); + kamibroadcast("Catch the Golden Fish is now open!", "Aurora Events"); + break; + case 11: + $EVENT$="Gemini"; + kamibroadcast("Gemini Season is now open!", "Aurora Events"); + break; + default: + $EVENT$="Tower"; + callfunc("FYEConf_Tower"); + kamibroadcast($DREAMTOWER_SAGE$+"'s Dream Towers have appeared!", "Aurora Events"); + break; + } + donpcevent "Aurora::OnRestore"; + return; +} + + + + + + +// Returns an item: (type, lv). Used by the function below (FYE_Autoset) +// types: misc, bp, warp, pot, heal, ore, magic, gift +// maxlv: 3 5 2 3 - 6 - 5 +function script FYT { + .@t=getarg(0); + .@l=getarg(1, 0); + switch (.@t) { + case FY_MISC: + switch (.@l) { + case 1: + return any(SmokeGrenade, ScentGrenade, Grenade, DeathPotion, Lockpicks); + case 2: + return any(MysteriousBottle, TreasureMap, DungeonMap); + case 3: + return any(ArcmageBoxset, MercBoxA, ScrollSMaggot); + } + break; + case FY_BP: + switch (.@l) { + case 1: + return any(EquipmentBlueprintA, AlchemyBlueprintA); + case 2: + return any(EquipmentBlueprintB, AlchemyBlueprintB); + case 3: + return any(EquipmentBlueprintC, AlchemyBlueprintC); + case 4: + return any(EquipmentBlueprintD, AlchemyBlueprintD); + case 5: + return any(EquipmentBlueprintE, AlchemyBlueprintE, AncientBlueprint); + } + break; + case FY_WARP: + switch (.@l) { + case 1: + return any(TulimWarpCrystal, CandorWarpCrystal, HurnsWarpCrystal, LoFWarpCrystal); + case 2: + return any(HalinWarpCrystal, NivalWarpCrystal, FrostiaWarpCrystal); + } + break; + case FY_POT: + switch (.@l) { + case 1: + return any(LukPotionA, DexPotionA, IntPotionA, VitPotionA, AgiPotionA, HastePotion, StrengthPotion); + case 2: + return any(LukPotionB, DexPotionB, IntPotionB, VitPotionB, AgiPotionB, MoveSpeedPotion, PrecisionPotion, DodgePotion); + case 3: + return any(SacredImmortalityPotion, SacredLifePotion, SacredManaPotion); + } + break; + case FY_ORE: + switch (.@l) { + case 1: + return any(CopperIngot, IronIngot); + case 2: + return any(SilverIngot, GoldIngot); + case 3: + return any(TinIngot, LeadIngot); + case 4: + return any(TitaniumIngot, TerraniteIngot); + case 5: + return any(IridiumIngot, IridiumIngot, PlatinumIngot); + case 6: + return any(Diamond, Ruby, Emerald, Sapphire, Topaz, Amethyst); + } + break; + case FY_GIFT: + switch (.@l) { + case 1: + return BronzeGift; + case 2: + return SilverGift; + case 3: + return GoldenGift; + case 4: + return PrismGift; + case 5: + return any(SupremeGift, MysteriousFruit, PrismGift, HousingLetterIII, PrismGift); + } + break; + // Single cases doesn't needs break + case FY_HEAL: + return any(SpearmintTea, Coffee, ClothoLiquor, BottleOfDivineWater); + case FY_MAGIC: + return any(FluoPowder, Quill, ScholarshipBadge, ScrollSWolvern); + } + + return Exception("Invalid cast to FYEIT: "+.@t+" (Lv "+.@l+")", RB_IRCBROADCAST|RB_DEBUGMES|RB_GLOBALANNOUNCE, Bread); +} + + + + + + +function script FYE_Autoset { + // Sets 30 ranked rewards + .@b=FY_BP; .@g=FY_GIFT; .@h=FY_HEAL; .@m=FY_MISC; .@mg=FY_MAGIC; + .@o=FY_ORE; .@p=FY_POT; .@w=FY_WARP; + + setarray $FYREWARD_ID, FYT(.@g, 1), StrangeCoin, FYT(.@m, 1), FYT(.@b, 1), FYT(.@p, 1), + FYT(.@h, 1), FYT(.@o, 1), FYT(.@o, 6), FYT(.@g, 2), FYT(.@w, 1), + FYT(.@b, 2), FYT(.@m, 2), FYT(.@o, 2), FYT(.@p, 2), FYT(.@o, 3), + FYT(.@b, 3), FYT(.@m, 3), StrangeCoin, FYT(.@mg, 1), FYT(.@g, 3), + FYT(.@w, 2), StrangeCoin, FYT(.@b, 4), FYT(.@p, 3), AncientBlueprint, + FYT(.@g, 4), FYT(.@o, 4), FYT(.@b, 5), FYT(.@o, 5), FYT(.@g, 5); + + // Item Amount Table + .@hv=rand2(1,3); + .@p1=any(1,2); .@p2=(.@p1 == 1 ? 2 : 1); + setarray $FYREWARD_AM, 1, 20, 1, 1, .@p1, + .@hv, 1, 1, 1, 1, + 1, 1, 1, .@p2, 1, + 1, 1, 60, 1, 1, + 1, 100, 1, 1, 1, + 1, 1, 1, 1, 1; + return; +} + + + + + + +// Modify Kamelot +function script FYE_Kamelot { + .@g=getcharid(2); + if ($@FYE_KAMELOT[.@g] != gettimeparam(GETTIME_DAYOFMONTH)) { + mesc l("Kamelot Season is open!"); + + // Instance still exists + if (instanceowner($@KAMELOT_ID[.@g]) == .@g) { + mesc l("However, your guild just challenged Kamelot Dungeons."); + mesc l("Please wait a while."); + return false; + } + mesc l("Should we?"); + next; + askyesno(); + closeclientdialog(); + + // Not going? Spoilsport + if (@menu == ASK_NO) + return false; + + // Someone began while you waited + if (instanceowner($@KAMELOT_ID[.@g]) == .@g) + return true; + + // Begin + //callfunc("KamelotCleanup", .@g); // Not needed + $KAMELOT_COOLDOWN[.@g] = ($KAMELOT_COOLDOWN[.@g] ? 1 : 0); + $@FYE_KAMELOT[.@g] = gettimeparam(GETTIME_DAYOFMONTH); + mesc l("Have fun!"); + next; + closeclientdialog(); + return true; + } + return false; +} + + + + + + +// Configure Regnum Blessing +function script FYEConf_Regnum { + setarray .@ma$, "004-1", "007-1", "004-2", "010-2", "014-3", "014-5", "015-5", + "018-3", "019-1", "025-2", "025-2-1", "018-7", "019-5"; + setarray .@mb$, "Tulimshar Outskirts", "Tulimshar Mines", + "Tulimshar (West) Canyon", "Halinarzo (East) Canyon", + "Central Woodlands", "North Woodlands", + "Abandoned Mines (Woodlands)", "Somber Caves (LoF)", + "Snow Field", "Fortress Island - South", + "Fortress Island South Cave", "Lilit's Sanctuary", + "Rock Plateau (Nivalis)"; + .@m=rand2(getarraysize(.@ma$)); + $REGNUM_BLESSMAP$=.@ma$[.@m]; + $REGNUM_BLESSMAP_H$=.@mb$[.@m]; + // Apply the blessing + setmapflag(.@ma$[.@m], mf_bexp, 300); + return; +} + + + + + + +// Configure World Expo +function script FYEConf_Expo { + $WORLDEXPO_ENEMY$=any("Xakabael the Dark", "Isbamuth", "Saulc", "SUSAN", "Terogan", ($GAME_STORYLINE >= 5 ? "Moubootaur" : "Monster King")); + setarray $FYREWARD_PT, 100, 220, 440, 880, 1320, + 1760, 2640, 3520, 5280, 7040, + 10560, 14080, 21120, 28160, 32000, + 36000, 40000, 45000, 50000, 55000, + 60000, 70000, 80000, 90000, 100000, + 112640, 140800, 168960, 200000, 225280; + // PS. Max Est. 225280 pts (#24 - Before Ancient Blueprint) + + FYE_Autoset(); + $FYLOGIN_PTS=rand2(18,22); + return; +} + + +// Modify Treasure Chests +function script FYE_Expo { + // TODO: Merit-based random formula + getitem EventTreasure1, 1+rand2(15); // 15 + getitem EventTreasure2, rand2(6); // 15 + getitem EventTreasure3, rand2(4); // 15 + // Total: (15+15+15)=1~45 points [AVG: 22 pts] + return; +} + + + + + + +// Configure Dream Tower +function script FYEConf_Tower { + $DREAMTOWER_SAGE$=any("Jaxad", "Mr. Saves", "Tux", "Freeyorp", "Cassy", "Crush", "Rotonen", "Kage", "Bjorn", "The Elven Sage", "Hocus Pocus the Fidibus", "Elli"); + // Bertram? (https://forums.themanaworld.org/viewtopic.php?p=105296#p105296) + setarray $FYREWARD_PT, 1, 4, 7, 10, 15, + 22, 30, 45, 60, 75, + 100, 120, 150, 175, 200, + 250, 300, 350, 400, 500, + 600, 700, 800, 900, 1000, + 1200, 1400, 1600, 1800, 2000; + // PS. Max Est. ? + + FYE_Autoset(); + $FYLOGIN_PTS=0; + return; +} + +// Dream Tower Ticket +function script FYE_Tower1 { + .@mob=getarg(0, killedrid); + // Obtain the monster level and roll a hidden counter + .@lv=getmonsterinfo(.@mob, MOB_LV); + DTOWER_ROLL+=.@lv; + + // There is no luck involved - Just get 1000 dream tower rolls + if (DTOWER_ROLL >= 1000) { + getitem EventDreamTicket, 1; + DTOWER_ROLL-=1000; + } + return; +} + + + + + + +// Configure Boss Raid +function script FYEConf_Raid { + $RAIDING_BOSS$=any("Xakabael the Dark", "Janeb the Evil", "Platyna the Red", "Benjamin the Frost", "Reid the Terrific", "Nu'Rem the Destroyer", "Golbenez the Cruel", "King of Typos"); + deletearray $FYRAID_OWNER; + deletearray $FYRAID_TIME; + deletearray $FYRAID_HP; + deletearray $FYRAID_LV; + setarray $FYREWARD_PT, 10, 25, 50, 100, 150, + 200, 300, 500, 750, 1000, + 1250, 1500, 1750, 2000, 2500, + 3000, 3500, 4000, 5000, 6000, + 7500, 9000, 11000, 15000, 20000, + 25000, 30000, 35000, 40000, 50000; + // PS. Max Est. ? + + FYE_Autoset(); + $FYLOGIN_PTS=rand2(1, 2); + $FYRAID_OWNER[0]=1; // System Reserve + return; +} + +// Boss Raid Appears! (What you killed is meaningless) +function script FYE_Raid { + .@mob=getarg(0, killedrid); + // Actually - ignore system or auto-generated entities + if (.@mob > 1490) + return; + .@lv=getmonsterinfo(.@mob, MOB_LV); + // Chance goes from 10% to 1% + // Defeating the boss lowers the chance in 0.1% + if (rand2(1000) > max(100-FYRAID_LV, 10)) return; + // Low level mobs may, at their level rate, abort + // So a lv 100 mob has 1%, a lv 1 mob has 50%, etc. + if (!rand2(.@lv)) return; + + /* A boss was found, update data */ + .@id = array_find($FYRAID_OWNER, getcharid(3)); + + // Wait - Maybe you already found a boss? + if (.@id >= 0) { + if ($FYRAID_LV[.@id] == FYRAID_LV) + return; + } + + // Never found a boss before, so assign some place for you + if (.@id < 0) { + FYRAID_LV = 1; + array_push($FYRAID_OWNER, getcharid(3)); + .@id = array_find($FYRAID_OWNER, getcharid(3)); + } + + // Assign the boss stats + $FYRAID_HP[.@id]=2000+FYRAID_LV*1000+(FYRAID_LV/5*500); + // Also declared in 001-13/main.txt + $FYRAID_LV[.@id]=FYRAID_LV; + $FYRAID_TIME[.@id]=gettimetick(2)+3600; // One hour + + // Announce that the boss was found! + announce(l("%s: %s (Lv %d) has appeared!", b(l("BOSS WARNING")), $RAIDING_BOSS$, FYRAID_LV), bc_self); + dispbottom l("Talk to Soul Menhir to engage the boss. Time limit = 1 hour."); + // The boss is already available for anyone to kill, you get no priority + // A new boss won't show up until you visit the Soul Menhir. + // (This could be improved) + return; +} + + + + + + + +// Configure Fishing +function script FYEConf_Fishing { + setarray $FYREWARD_PT, 42, 63, 94, 141, 212, + 318, 478, 717, 1076, 1345, + 1614, 2020, 2421, 3026, 3632, + 4540, 5000, 6000, 7000, 8500, + 10000, 11000, 12000, 13500, 15000; + // PS. Max Est. 14400 pts (#21 - Before 100 Strange Coins) + + FYE_Autoset(); + $FYLOGIN_PTS=rand2(6,8); + return; +} + + +// Modify Fishing +function script FYE_Fishing { + // TODO: Merit-based random formula [AVG: 3.5] about 10pts/min + getitem EventFish, rand2(1, 6); + return; +} + + + + + + +// Configure Mining Union Research Event +function script FYEConf_Mining { + setarray $FYREWARD_PT, 100, 220, 440, 880, 1320, + 1760, 2640, 3520, 5275, 7040, + 10560, 14080, 21120, 28160, 35200, + 42240, 57000, 70400, 85000, 112640, + 140800, 169000, 197120, 225300, 281160, + 337920, 394240, 450560, 550000, 675000; + // PS. Max Est. 705740 pts (overflow) + + FYE_Autoset(); + $FYLOGIN_PTS=rand2(16,20); + return; +} + + +// Modify Bifs +function script FYE_Mining { + .@mob=getarg(0, killedrid); + // Only ores + if (getmonsterinfo(.@mob, MOB_RACE) != RC_Mineral) + return; + + // TODO: Merit-based random formula [AVG: 5] about 70pts/min + // TODO: Big vs Small bifs (regex OK, size OK) + .@ore=rand2(3, 6); + if (.@mob == EleniumBif) + .@ore-=1; + getitem EventOre, .@ore; + return; +} + + + + + + +// Configure Magic Olympics +function script FYEConf_Olympics { + setarray $FYREWARD_PT, 10, 22, 44, 88, 132, + 176, 264, 352, 527, 704, + 1056, 1408, 2112, 2816, 3520, + 4224, 5700, 7040, 8500, 11264, + 14080, 16900, 19712, 22530, 28116, + 33792, 39424, 45056, 55000, 67500; + // PS. Max Est. ? pts + + FYE_Autoset(); + $FYLOGIN_PTS=0; + return; +} + + +// Modify Magic Skills +function script FYE_Olympics_SK { + if ($EVENT$ != "Olympics") return; + + .@sk=getarg(0, @skillId); + .@sl=max(getarg(1, @skillLv), 1); + .@st=getarg(2, @skillTarget); + + .@cl = $@MSK_CLASS[.@sk]; + + // Supportive + if (.@cl == CLASS_SCHOLARSHIP && + .@st != getcharid(3) && + .@st > 1 && + .@sk != SM_PROVOKE && + .@sk != EVOL_AREA_PROVOKE) + FMOLY_FRIEND += 1; + + // Generic + if (.@sk != TMW2_FAKESKILL && + .@sk != TMW2_FAKESKILL2 && + .@sk != AM_CALLHOMUN && + .@sk != AM_REST && + .@sk != AM_RESURRECTHOMUN && + .@sk != TMW2_TRANSMIGRATION && + .@sk != TMW2_OVHFIRE) + FYMOLY_SPAMMY += .@sl; + + setq Q_AuroraEvent, getq2(Q_AuroraEvent) + rand2(.@sl + 1); + return; +} + +// Modify Mana EXP Gain +function script FYE_Olympics_MX { + if ($EVENT$ != "Olympics") return; + .@var=getarg(0, 0); + + FYMOLY_MANAXP += .@var; + setq Q_AuroraEvent, getq2(Q_AuroraEvent) + rand2(.@var + 1); + return; +} + +// Count Chanting Usage +function script FYE_Olympics_CH { + if ($EVENT$ != "Olympics") return; + + // TODO: Change based on using different verbs/adjectives? + FYMOLY_CHANTI += 1; + setq Q_AuroraEvent, getq2(Q_AuroraEvent) + 1; + return; +} + +// Count Alchemy Usage +function script FYE_Olympics_AL { + if ($EVENT$ != "Olympics") return; + + .@units = getarg(0, 1); + FYMOLY_ALCHMY += .@units; + setq Q_AuroraEvent, getq2(Q_AuroraEvent) + .@units; + return; +} + +// Send to Porthos, returns false on failure, true on success +function script FYE_Olympics_TO { + if ($EVENT$ != "Olympics") return false; + + .@m$ = "moly@"+getcharid(0); + if (instanceowner(@olympics) != getcharid(3)) { + .@id = instance_create("moly@"+getcharid(0), getcharid(3), IOT_CHAR); + if (.@id < 0) return false; + .@mp$ = instance_attachmap("001-14", .@id, 0, .@m$); + @olympics = .@id; + // It'll be self-destroyed when time runs out (30 minutes) + instance_set_timeout(1800, 1800, .@id); + instance_init(.@id); + } else { + instance_set_timeout(1800, 1800, @olympics); + } + warp .@m$, 92, 90; + return true; +} + + + + + + + +// "Submit" button from 003-1/events.txt +// Don't forget to enable it in npc/utils.txt as well!! +function script FYE_Submit { + .@day=getq(Q_AuroraEvent); + .@pts=getq2(Q_AuroraEvent); + + // Handle daily login score rewards + if (.@day != gettimeparam(GETTIME_DAYOFMONTH)) { + setq1 Q_AuroraEvent, gettimeparam(GETTIME_DAYOFMONTH); + setq2 Q_AuroraEvent, .@pts+$FYLOGIN_PTS; + .@pts=getq2(Q_AuroraEvent); + if ($FYLOGIN_PTS) + dispbottom l("Daily Event Bonus: %d Points!", $FYLOGIN_PTS); + } + + // Give you points + if ($EVENT$ == "Expo") { + // .:: WORLD EXPO EVENT ::. + .@pts+=countitem(EventTreasure1)*1; + .@pts+=countitem(EventTreasure2)*3; + .@pts+=countitem(EventTreasure3)*5; + + setq2 Q_AuroraEvent, .@pts; + delitem EventTreasure1, countitem(EventTreasure1); + delitem EventTreasure2, countitem(EventTreasure2); + delitem EventTreasure3, countitem(EventTreasure3); + } else if ($EVENT$ == "Fishing") { + // .:: CATCH THE GOLD FISH EVENT ::. + .@pts+=countitem(EventFish)*1; + + setq2 Q_AuroraEvent, .@pts; + delitem EventFish, countitem(EventFish); + } else if ($EVENT$ == "Mining") { + // .:: MINION UNION'S RESEARCH REQUEST EVENT ::. + .@pts+=countitem(EventOre)*1; + + setq2 Q_AuroraEvent, .@pts; + delitem EventOre, countitem(EventOre); + } else if ($EVENT$ == "Tower") { + // .:: DREAM TOWER APPEARS ::. + .@pts+=countitem(BrokenMedal)*1; + + setq2 Q_AuroraEvent, .@pts; + delitem BrokenMedal, countitem(BrokenMedal); + } else if ($EVENT$ == "Raid") { + // .:: BOSS RAID ::. + .@pts+=countitem(EventNaftalin)*1; + + setq2 Q_AuroraEvent, .@pts; + delitem EventNaftalin, countitem(EventNaftalin); + } else if ($EVENT$ == "Olympics") { + // .:: MAGIC OLYMPICS ::. + // Handled separately + } else { + // Wut? This is not an Aurora Event + Exception($EVENT$+" is NOT a valid Aurora Event; Misdefinition.\n\nPlease ensure that it is defined in utils, aurora, news, and command/event.\n\nFYE_Submit - FYEventUsesRanking - FYE_* - FYEConf_* - FYStopEvent", RB_DEFAULT|RB_ISFATAL); + } + return; +} + + + + + + +// Stops any Aurora Event +function script FYStopEvent { + setarray .@av$, "Kamelot", "Regnum", "Expo", "Fishing", "Candor", "Mining", "Tower", "Raid", "Olympics", "Celestia", "Rebirth", "Gemini"; + if (array_find(.@av$, $EVENT$) >= 0) { + sClear(); + $EVENT$=""; + } + return; +} + + +// Handle the ending of Aurora Events (see also functions/util.txt) +function script FYRewardEvent { + if (FYEventUsesRanking()) { + debugmes("Rewards are due"); + // This code absolutely can't fail: + if ($EVENT$ == "Olympics") { + callfunc("HocusScoreNew"); + copyarray $@aurora_name$, $@moly_n$, 10; + copyarray $@aurora_value, $@moly_v, 10; + for (.@i=0; .@i < 10; .@i++) { + if ($@aurora_name$[.@i] == "") break; + $@aurora_charid[.@i] = gf_charnameid($@aurora_name$[.@i]); + } + } else { + .@nb = query_sql("SELECT c.name, i.count2, c.char_id FROM `quest` AS i, `char` AS c WHERE i.quest_id="+Q_AuroraEvent+" AND i.char_id=c.char_id ORDER BY i.count2 DESC LIMIT 10", $@aurora_name$, $@aurora_value, $@aurora_charid); + } + $MOST_HEROIC$=$@aurora_name$[0]; + for (.@i=0;.@i < getarraysize($@aurora_charid);.@i++) { + switch (.@i+1) { + case 1: + .@prize=120; break; + case 2: + .@prize=100; break; + case 3: + .@prize=80; break; + case 4: + case 5: + .@prize=60; break; + case 6: + case 7: + .@prize=40; break; + default: + .@prize=20; + } + rodex_sendmail($@aurora_charid[.@i], "Aurora Events", $EVENT$+" Reward!", "Final Ranking: #"+(.@i+1)+". Congratulations on making "+$@aurora_value[.@i]+" points on the event!", 0, StrangeCoin, .@prize); + } + // Send results copy to syslog + consoleinfo("%s event finished:\n#01 - %s (%d)\n#02 - %s (%d)\n#03 - %s (%d)\n#04 - %s (%d)\n#05 - %s (%d)\n", $EVENT$, $@aurora_name$[0], $@aurora_value[0], $@aurora_name$[1], $@aurora_value[1], $@aurora_name$[2], $@aurora_value[2], $@aurora_name$[3], $@aurora_value[3], $@aurora_name$[4], $@aurora_value[4]); + // Magic Olympics has extra rewards + if ($EVENT$ == "Olympics") { + if ($@aurora_charid[0] > 0) + rodex_sendmail($@aurora_charid[0], "Hocus Pocus", "A Small Gift", "I wanted to send you cake but the cat ate it.", 0, BlackyCat, 1, ScholarshipTuition, 1); + if ($@aurora_charid[1] > 0) + rodex_sendmail($@aurora_charid[1], "Hocus Pocus", "A Small Gift", "Here is some noob cake for a bright mind.", 0, ApanaCake, 1, ScholarshipTuition, 1); + } + // Destroy the quest + DelQuestFromEveryPlayer(Q_AuroraEvent); + deletearray $@aurora_name$; + deletearray $@aurora_value; + deletearray $@aurora_charid; + /* + DelItemFromEveryPlayer(EventTreasure1); + DelItemFromEveryPlayer(EventTreasure2); + DelItemFromEveryPlayer(EventTreasure3); + DelItemFromEveryPlayer(EventFish); + DelItemFromEveryPlayer(EventOre); + */ + } + return; +} + + + + + + + + +// Modify Mob Killing +// Serves for multiple events, preserves killedrid +function script AuroraMobkill { + if ($EVENT$ == "Mining") + FYE_Mining(killedrid); + if ($EVENT$ == "Tower") + FYE_Tower1(killedrid); + if ($EVENT$ == "Raid") + FYE_Raid(killedrid); + return; +} + + +// Normalize Aurora Event after a server restart +function script FYE_Normalize { + if ($EVENT$ == "Regnum") { + // Reapply the Regnum's blessing + setmapflag($REGNUM_BLESSMAP$, mf_bexp, 300); + } + return; +} + diff --git a/npc/functions/bank.txt b/npc/functions/bank.txt new file mode 100644 index 0000000..40ff31d --- /dev/null +++ b/npc/functions/bank.txt @@ -0,0 +1,288 @@ +// TMW2 Script +// Modified by Jesusalva +// Evol scripts. +// Authors: +// gumi +// Reid + +function script Banking { + do + { + if (BankVault > 0) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("You currently have @@ GP on your bank account.", + format_number(BankVault)), + l("What do you want to do with your money?"); + } else { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("What do you want to do with your money?"); + } + + select + rif(Zeny > 0, l("Deposit.")), + rif(BankVault > 0, l("Withdraw.")), + l("Buy a Housing Letter"), + l("I'm done."); + + switch (@menu) + { + case 1: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("How much do you want to deposit?"); + + menuint + l("Other."), -1, + rif(Zeny >= 1000, format_number(1000) + " GP."), 1000, + rif(Zeny >= 2500, format_number(2500) + " GP."), 2500, + rif(Zeny >= 5000, format_number(5000) + " GP."), 5000, + rif(Zeny >= 10000, format_number(10000) + " GP."), 10000, + rif(Zeny >= 25000, format_number(25000) + " GP."), 25000, + rif(Zeny >= 50000, format_number(50000) + " GP."), 50000, + rif(Zeny >= 100000, format_number(100000) + " GP."), 100000, + l("All of my money."), -2, + l("I changed my mind."), -3; + + switch (@menuret) { + case -1: + input @menuret; + break; + case -2: + @menuret = Zeny; + } + + if (@menuret > 0) { + if (@menuret > Zeny) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("You do not have enough Gold on yourself."); + break; + } + + @menuret = min(MAX_BANK_ZENY, @menuret); // make sure the variable can't overflow + .@before = BankVault; // amount before the deposit + .@max = MAX_BANK_ZENY - BankVault; // maximum possible deposit + .@deposit = min(.@max, @menuret); // actual deposit + + if (.@deposit > 0) { + BankVault += .@deposit; // add to bank + Zeny -= .@deposit; // remove from inventory + + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("You made a cash deposit of @@ GP.", format_number(.@deposit)); + } + } + break; + + case 2: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("How much do you want to withdraw?"); + + menuint + l("Other."), -1, + rif(BankVault >= 1000, format_number(1000) + " GP."), 1000, + rif(BankVault >= 2500, format_number(2500) + " GP."), 2500, + rif(BankVault >= 5000, format_number(5000) + " GP."), 5000, + rif(BankVault >= 10000, format_number(10000) + " GP."), 10000, + rif(BankVault >= 25000, format_number(25000) + " GP."), 25000, + rif(BankVault >= 50000, format_number(50000) + " GP."), 50000, + rif(BankVault >= 100000, format_number(100000) + " GP."), 100000, + l("All of my money."), -2, + l("I changed my mind."), -3; + + switch (@menuret) + { + case -1: + input @menuret; + break; + case -2: + @menuret = BankVault; + } + + if (@menuret > 0) { + if (@menuret > BankVault) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("You do not have enough Gold Pieces on your bank account."); + break; + } + + @menuret = min(MAX_ZENY, @menuret); // make sure the variable can't overflow + .@before = Zeny; // amount before the withdrawal + .@max = MAX_ZENY - Zeny; // maximum possible withdrawal + .@withdrawal = min(.@max, @menuret); // actual withdrawal + + if (.@withdrawal > 0) { + Zeny += .@withdrawal; // add to inventory + BankVault -= .@withdrawal; // remove from bank + + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("You withdrew a total of @@ GP.", format_number(.@withdrawal)); + } + } + break; + + case 3: + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("You currently have @@ mobiliary credits + GP at your disposal.", format_number(.@gp)); + mesc l("@@ - @@ - @@", getitemlink(HousingLetterI), getitemlink(HousingLetterII), getitemlink(HousingLetterIII)); + next; + select + l("Nothing"), + rif(.@gp >= 11000, l("Housing Letter I for 11,000 GP")), + rif(.@gp >= 101000, l("Housing Letter II for 101,000 GP")), + rif(.@gp >= 1001000, l("Housing Letter III for 1,001,000 GP")); + mes ""; + switch (@menu) { + case 2: + realestate_payment(11000); + getitem HousingLetterI, 1; + break; + case 3: + realestate_payment(101000); + getitem HousingLetterII, 1; + break; + case 4: + realestate_payment(1001000); + getitem HousingLetterIII, 1; + break; + } + break; + default: return; + } + } while (true); +} + +function script BKInfo { + speech S_LAST_NEXT, + l("We organize some auction and we help local merchants to launch their businesses."), + l("We also feature some services like a storage and a bank for members."), + l("Registration is open to everybody, but newcomers need to pay a fee for all of the paperwork."), + l("If you have... references, we may also be able to offer you... premium storing."); + + narrator S_FIRST_BLANK_LINE, + l("The bank and item storage is shared between all characters within a same account."), + l("With it, you can safely move items and funds between your characters."), + l("The Premium and Deluxe Storages are only available for characters which were reborn at least once."); + return; +} + +// name, city, price, ID +function script BKReg { + .@price=max(2000, getarg(2)-#BankP); + .@id=getarg(3, 1); + @menu=3; + do + { + mesn getarg(0); + mesq l("Register fee is @@.", .@price); + mesc l("The fee only need to be paid once and will work in every town."); + next; + select + rif(Zeny >= .@price, l("Register")), + l("Not at the moment"), + l("Information"); + mes ""; + if (@menu == 1) { + Zeny=Zeny-.@price; + setq General_Banker, .@id; + #BankP=#BankP+(rand2(150,400)*(.@id**2)); + mesn getarg(0); + mesq l("Registered! You can now use any banking service, of any town!"); + } else if (@menu == 3) { + BKInfo(); + } + } while (@menu == 3); + return; +} + +// name, city, price (defaults to 10k) +function script Banker { + mesn getarg(0); + mesq l("Welcome! My name is @@, I am a representative of the Merchant Guild on @@.", getarg(0), getarg(1)); + next; + + if (getq(General_Banker) == 0) { + BKReg(getarg(0), getarg(1), getarg(2,10000)); + } else { + do + { + select + l("I would like to store some items."), + l("I would like to perform money transactions."), + l("Did I received any mail?"), + rif(REBIRTH || is_sponsor(), l("I would like to use the Premium Storage.")), + rif(REBIRTH, l("I would like to use the Deluxe Storage.")), + l("What is this guild for?"), + l("Bye."); + + switch (@menu) { + case 1: + closeclientdialog; + openstorage; + close; + break; + case 2: + Banking(); + break; + case 3: + // NOTE: This value is HARDCODED, do not try changing it! + mesc l("Note: Transfering items on mail cost @@ GP/item", 500); + mesc l("Money transference by mail is, however, free."); + next; + closeclientdialog; + openmail(); + close; + break; + case 4: + if (getq(General_Banker) < 2) { + mesn; + mesq l("The Premium Storage is available to all our sponsors and anyone with... references. Such as yourself!"); + next; + .@price=25000; + mesn; + mesq l("It will allow you to store %d extra items, with unlimited weight or size limit, for only %s GP! Although premium clients such as yourself... deserve a discount!", 300, fnum(.@price)); + next; + BKReg(getarg(0), getarg(1), .@price, 2); + } + if (getq(General_Banker) >= 2) { + closeclientdialog; + openstorage 4; + close; + } + break; + case 5: + if (getq(General_Banker) < 2) { + mesn; + mesq l("This option is not yet available for you; Please purchase the Premium Storage first, and then we can get started on the deluxe."); + break; + } + if (getq(General_Banker) < 3) { + mesn; + mesq l("The Deluxe Storage is available only to our best customers, and how lucky you! YOU are eligible!"); + next; + .@price=100000; + mesn; + mesq l("It will allow you to store %d extra items, with unlimited weight or size limit, for only %s GP! Although premium clients such as yourself... deserve a discount!", 500, fnum(.@price)); + next; + BKReg(getarg(0), getarg(1), .@price, 3); + } + if (getq(General_Banker) >= 3) { + closeclientdialog; + openstorage 5; + close; + } + break; + case 6: + mes ""; + BKInfo(); + break; + } + if (@menu != 7) { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT | S_NO_NPC_NAME, + l("Something else?"); + } + } while (@menu != 7); + } + closedialog; + goodbye; + close; + +} diff --git a/npc/functions/barber.txt b/npc/functions/barber.txt new file mode 100644 index 0000000..370285b --- /dev/null +++ b/npc/functions/barber.txt @@ -0,0 +1,110 @@ +// Evol scripts. +// Authors: +// omatt +// Reid +// Travolta +// Description: +// Function for supporting barber NPC. + +function script BarberSayStyle { + + .@get_color = getlook(LOOK_HAIR_COLOR); + .@get_look = getlook(LOOK_HAIR); + .@style_name$ = $@hairstyle$[.@get_look]; + .@color_name$ = $@haircolor$[.@get_color]; + + switch (getarg(0)) + { + case 1: + message strcharinfo(0), l("@@", .@style_name$); + break; + case 2: + message strcharinfo(0), l("@@", .@color_name$); + break; + case 3: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Your hairstyle is @@ and its color is @@.", .@style_name$, .@color_name$); + break; + } + return; +} +function script BarberChangeStyle { + do + { + .@hairsizearray = getarraysize($@hairstyle$); + .@get_look = getlook(LOOK_HAIR); + + // Here .@i starts from 1 because hairstyle 0 doesn't exist. + for (.@i = 1; .@i < .@hairsizearray; .@i++) + { + .@menustr$ = .@menustr$ + + rif(.@get_look != .@i, l("" + $@hairstyle$[.@i] + "")) + + ":"; + } + + .@menustr$ = .@menustr$ + l("I'm fine for now, thank you."); + + .@idx = select(l("As you want!"),.@menustr$); + + if (.@idx == .@i + 1) return; // last choice to quit dialog + + switch (@menu) + { + case 1: + do + { + // here "- 1" because i don't use the 0 of array + .@rand_hair = rand(1,(.@hairsizearray - 1)); + } while (.@rand_hair == getlook(LOOK_HAIR)); + setlook LOOK_HAIR, .@rand_hair; + setlook LOOK_HAIR_COLOR, getlook(LOOK_HAIR_COLOR); + BarberSayStyle 1; + break; + default: + // and here "- 1" because the first choice is taken by the random + setlook LOOK_HAIR, (@menu - 1); + setlook LOOK_HAIR_COLOR, getlook(LOOK_HAIR_COLOR); + break; + } + .@menustr$ = ""; + } while (1); +} + +function script BarberChangeColor { + do + { + .@get_look = getlook(LOOK_HAIR_COLOR); + .@hairsizearray = getarraysize($@haircolor$); + + for (.@i = 0; .@i < .@hairsizearray; .@i++) + { + .@menustr$ = .@menustr$ + + rif(.@get_look != .@i, l("" + $@haircolor$[.@i] + "")) + + ":"; + } + + .@menustr$ = l("Surprise me!") + ":" + .@menustr$ + l("I'm fine for now, thank you."); + + .@idx = select(.@menustr$); + + if (.@idx == .@i + 2) return; + + switch (@menu) + { + case 1: + do + { + .@rand_color = rand(0, .@hairsizearray); + } while (.@rand_color == getlook(LOOK_HAIR_COLOR)); + setlook LOOK_HAIR_COLOR, .@rand_color; + BarberSayStyle 2; + break; + default: + setlook LOOK_HAIR_COLOR, (@menu - 2); + break; + } + .@menustr$ = ""; + } while (1); + + return; +} diff --git a/npc/functions/bitwise.txt b/npc/functions/bitwise.txt new file mode 100644 index 0000000..0236066 --- /dev/null +++ b/npc/functions/bitwise.txt @@ -0,0 +1,176 @@ +// The Mana World Script +// Author: Gumi, Jesusalva +/** + * Gets a bitmasked value in from an integer. If the shift is omitted, it will + * be deduced from the mask. + * + * @arg 0 - the variable + * @arg 1 - mask + * @arg 2 - shift */ +function script bitwise_get { + .@shift = getarg(2, 0); + + if (getargcount() < 3) { + // guess the shift from the mask: + for (.@shift = 0; .@shift < 32; ++.@shift) { + if ((getarg(1) & (1 << .@shift)) != 0) { + break; + } + } + } + + return (getarg(0) & getarg(1)) >> .@shift; +} + +/** + * sets a bitmasked value in a variable + * + * @arg 0 - the target variable + * @arg 1 - mask + * @arg 2 - shift + * @arg 3 - new value + * @return a reference to the variable + */ +function script bitwise_set { + if (getargcount() < 4) { + // guess the shift from the mask: + for (.@shift = 0; .@shift < 32; ++.@shift) { + if ((getarg(1) & (1 << .@shift)) != 0) { + break; + } + } + + return set(getarg(0), (getarg(0) & ~(getarg(1))) | (getarg(2, 0) << .@shift)); + } + + return set(getarg(0), (getarg(0) & ~(getarg(1))) | (getarg(3, 0) << getarg(2, 0))); +} + +// bitmask_count(<int>) +// returns the number of bits set in <int> (up to 4096?) +function script bitmask_count { + .@n = getarg(0); // Number evaluated + .@p=0; // Bits set/unset + .@s=0; // Stack and Check + .@m=0; // Memory + + // Loop only as needed + while (.@s < .@n) { + .@s=2**.@m; + if (.@n & .@s) + .@p++; + .@m++; + } + return .@p; +} + +///////////////////////////////////////////////////////////////////////////////// +// A Nibble can go up to 15. There are 7 nibbles. +// get_nibble(VAR, NIBBLEID) +function script get_nibble { + .@v=getarg(0); + switch (getarg(1)) { + case 0: + .@s=0; .@m=0xF; break; + case 1: + .@s=4; .@m=0xF0; break; + case 2: + .@s=8; .@m=0xF00; break; + case 3: + .@s=12; .@m=0xF000; break; + case 4: + .@s=16; .@m=0xF0000; break; + case 5: + .@s=20; .@m=0xF00000; break; + case 6: + .@s=24; .@m=0xF000000; break; + default: + Exception("Invalid Nibble: "+getarg(1), RB_DEFAULT, .@v); + } + + return bitwise_get(.@v, .@m, .@s); +} + +// A Byte can go up to 255. There are 3 bytes. The forth can go up to 127. +// get_nibble(VAR, BYTEID) +function script get_byte { + .@v=getarg(0); + switch (getarg(1)) { + case 0: + .@s=0; .@m=0xFF; break; + case 1: + .@s=8; .@m=0xFF00; break; + case 2: + .@s=16; .@m=0xFF0000; break; + case 3: + .@s=24; .@m=0x7F000000; break; + default: + Exception("Invalid Byte: "+getarg(1), RB_DEFAULT, .@v); + } + + return bitwise_get(.@v, .@m, .@s); +} + +// A Bitword can go up to 65535 and is fixed in position to handle Soul EXP. +// get_bitword(VAR) +function script get_bitword { + .@v=getarg(0); + + return bitwise_get(.@v, 0xFFFF, 0); +} + +///////////////////////////////////////////////////////////////////////////////// +// A Nibble can go up to 15. There are 7 nibbles. +// set_nibble(VAR, NIBBLEID, VAL) +function script set_nibble { + .@v=getarg(0); + switch (getarg(1)) { + case 0: + .@s=0; .@m=0xF; break; + case 1: + .@s=4; .@m=0xF0; break; + case 2: + .@s=8; .@m=0xF00; break; + case 3: + .@s=12; .@m=0xF000; break; + case 4: + .@s=16; .@m=0xF0000; break; + case 5: + .@s=20; .@m=0xF00000; break; + case 6: + .@s=24; .@m=0xF000000; break; + default: + Exception("Invalid SNibble: "+getarg(1), RB_DEFAULT); + } + + return bitwise_set(getarg(0), .@m, .@s, getarg(2)); +} + +// A Byte can go up to 255. There are 3 bytes. The forth can go up to 127. +// set_nibble(VAR, BYTEID, VAL) +function script set_byte { + .@v=getarg(0); + switch (getarg(1)) { + case 0: + .@s=0; .@m=0xFF; break; + case 1: + .@s=8; .@m=0xFF00; break; + case 2: + .@s=16; .@m=0xFF0000; break; + case 3: + .@s=24; .@m=0x7F000000; break; + default: + Exception("Invalid SByte: "+getarg(1), RB_DEFAULT); + } + + return bitwise_set(getarg(0), .@m, .@s, getarg(2)); +} + +// A Bitword can go up to 65535 and is fixed in position to handle Soul EXP. +// set_bitword(VAR, VAL) +function script set_bitword { + .@v=getarg(0); + + return bitwise_set(getarg(0), 0xFFFF, 0, getarg(1)); +} + diff --git a/npc/functions/captcha.txt b/npc/functions/captcha.txt new file mode 100644 index 0000000..30e8b3c --- /dev/null +++ b/npc/functions/captcha.txt @@ -0,0 +1,258 @@ +// TMW2 functions. +// Author: +// Jesusalva +// Description: +// Random CAPTCHA check against AFK robots. +// TODO: Put this in a blackbox for bots which sniff text chat. +// Variables: +// gettimetick(2) variables: CAPTCHA_TIME | CAPTCHA_OK +// CAPTCHA_1 | CAPTCHA_2 | CAPTCHA_ANSWER => The correct captcha reply +// CAPTCHA_OP$ => The operation, defaults to "+" +// $@BOTCHECK_TARGET => The account ID of the char being probed +// @captcha_cooldown => anti-flood +// $CAPTCHA (bitmask) +// 0 - Captcha Disabled +// 1 - Captcha Enabled +// 2 - Display warnings (@captcha_lastwarning/@captcha_lastwarningt) + +// CaptchName, names the number +function script CaptchName { + switch (getarg(0)) { + case 0: return l("zero"); + case 1: return l("one"); + case 2: return l("two"); + case 3: return l("three"); + case 4: return l("four"); + case 5: return l("five"); + case 6: return l("six"); + case 7: return l("seven"); + case 8: return l("eight"); + case 9: return l("nine"); + case 10: return l("ten"); + case 11: return l("eleven"); + case 12: return l("twelve"); + case 13: return l("thirteen"); + case 14: return l("fourteen"); + case 15: return l("fifteen"); + case 16: return l("sixteen"); + case 17: return l("seventeen"); + case 18: return l("eighteen"); + case 19: return l("nineteen"); + case 20: return l("twenty"); + } + return getarg(0); +} + +// MakeCaptch, makes a captcha and saves it +function script MakeCaptch { + CAPTCHA_TIME=gettimetick(2); + CAPTCHA_OK=CAPTCHA_TIME; + CAPTCHA_1=rand2(21); + CAPTCHA_2=rand2(20); + // select a operation + switch (rand2(1)) { + case 0: + CAPTCHA_OP$="-"; + CAPTCHA_ANSWER=CAPTCHA_1-CAPTCHA_2; + break; + default: + CAPTCHA_OP$="+"; + CAPTCHA_ANSWER=CAPTCHA_1+CAPTCHA_2; + break; + } + return; +} + +function script CaptchVal { + // Make it nice + .@c$=any( + CAPTCHA_1+CAPTCHA_OP$+CAPTCHA_2, + CaptchName(CAPTCHA_1)+CAPTCHA_OP$+CAPTCHA_2, + CAPTCHA_1+CAPTCHA_OP$+CaptchName(CAPTCHA_2), + CaptchName(CAPTCHA_1)+CAPTCHA_OP$+CaptchName(CAPTCHA_2), + CAPTCHA_1+" "+CAPTCHA_OP$+" "+CAPTCHA_2, + CaptchName(CAPTCHA_1)+" "+CAPTCHA_OP$+" "+CAPTCHA_2, + CAPTCHA_1+" "+CAPTCHA_OP$+" "+CaptchName(CAPTCHA_2), + CaptchName(CAPTCHA_1)+" "+CAPTCHA_OP$+" "+CaptchName(CAPTCHA_2)); + return .@c$; +} + +function script CaptchExample { + if (!CAPTCHA_TIME || getarg(0, false)) { + dispbottom("##1TO REPLY TO CAPTCHAS: @captcha <numeric answer>##1"); + dispbottom l("Example: Give the answer for the following: one+1"); + dispbottom l("Reply: %s", b("@captcha 2")); + dispbottom b(l("This example will not be shown again.")); + } + return; +} + +- script @captcha 32767,{ + function captchaProbe; + end; + +OnCall: + // Not you, ignore + if (getcharid(3) != $@BOTCHECK_TARGET) + end; + // Attempt cooldown + if (@captcha_cooldown > gettimetick(2)) { + dispbottom l("CAPTCHA: Cooldown in effect."); end;} + // Process answer + @captcha_cooldown=gettimetick(2)+.antiflood; + // Lets be reasonable + if (gettimetick(2)+2 > CAPTCHA_TIME) { + dispbottom l("CAPTCHA: An error happened, try again."); end;} + + // Verify answer + .@ans$ = implode(.@atcmd_parameters$, " "); + .@ans=atoi(.@ans$); + if (.@ans == CAPTCHA_ANSWER) { + CAPTCHA_OK=gettimetick(2)+.cooldown; + $@BOTCHECK_TARGET=0; + dispbottom any( + l("Captcha successful"), + l("Captcha ok"), + l("Correct"), + l("Understood"), + l("Not bad"), + l("Hmpf. That'll do."), + l("A bit longer and I would have jailed you %%\\ "), + l("%%\\ that'll do."), + l("%%N")); + dispbottom l("Remember: Players can also help enforcing no-AFK-bot rule!"); + //dispbottom l("Remember: Never lend your toothbrush to a slime!"); + } else { + dispbottom l("CAPTCHA: Incorrect answer. Wait %ds and try again.", .antiflood); // Max 10 attempts total + } + end; + +OnInit: + .thr=180; // Seconds to reply + .cooldown=3600; // Captcha Immunity + .antiflood=18; // Seconds between captcha failed attempts + bindatcmd "captcha", "@captcha::OnCall", 0, 0, 0; + initnpctimer; + end; + +// Pick a random target for captcha checks +OnTimer5000: + // Script disabled by admins + if (!$CAPTCHA) { + initnpctimer; + end; + } + + if ($@BOTCHECK_TARGET) captchaProbe(); + + // Maybe we will conduct a captcha + if (rand2(10) < 3) { + // This can be slow, beware + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + // Too lazy + if (rand2(100) > 5) + continue; + + // Okay, lets do it + attachrid(.@players[.@i]); + + // TODO: What about jailed players? + + // 1. Player in immunity, who is next one + if (CAPTCHA_OK > gettimetick(2)) { + detachrid(); + continue; + } + + // 2. Player must be jailed, and we continue + if (CAPTCHA_TIME < CAPTCHA_OK) { + atcommand("@jailfor 40mn "+strcharinfo(0)); + dispbottom l("You failed to reply to the captcha in time and were arrested for AFK Botting. You can use @jailtime to keep track of time left."); + CAPTCHA_OK=CAPTCHA_TIME; + detachrid(); + continue; + } + + // 2.1 Player is AFK for more than 30 seconds + if (checkidle() > 30) { + detachrid(); + continue; + } + + // 3. This is a good target, lets do this + .@g$=""; + CaptchExample(); + MakeCaptch(); + + .@g$=any( + "CAPTCHA: Please reply the following: "+CaptchVal(), + "BOTCHECK: Please reply the following: "+CaptchVal(), + "CAPTCHA: You must answer this: "+CaptchVal(), + "BOTCHECK: You must answer this: "+CaptchVal()); + + // 4. Find a random method + switch (rand2(2)) { + case 0: + message(getcharid(3), .@g$); + message(getcharid(3), "Example for one + one: @captcha 2"); + break; + case 1: + announce(.@g$, bc_self|bc_pc); + announce("Example for one + one: @captcha 2", bc_self|bc_pc); + break; + default: + dispbottom(.@g$); + dispbottom("Example for one + one: @captcha 2"); + break; + } + + // 5. Detach rid, target is set + $@BOTCHECK_TARGET=getcharid(3); + detachrid(); + break; + } + } + + // Continue this timer forever + initnpctimer; + end; + +function captchaProbe { + // Attach rid + .@online=attachrid($@BOTCHECK_TARGET); + + // User disconnected, next captcha they'll be arrested because timer will expire + if (!.@online) { + $@BOTCHECK_TARGET=false; + CAPTCHA_OK=false; + } + + // Timer expired? Ban hammer + if (CAPTCHA_TIME+.thr > gettimetick(2) && CAPTCHA_OK <= CAPTCHA_TIME) { + atcommand("@jailfor 30mn "+strcharinfo(0)); + dispbottom l("You failed to reply to the captcha in time and were arrested for AFK Botting. You can use @jailtime to keep track of time left."); + CaptchExample(true); + $@BOTCHECK_TARGET=false; + CAPTCHA_OK=CAPTCHA_TIME; + } + + // Nothing happened, lets wait + if ($CAPTCHA & 2) { + if (!@captcha_lastwarningt) + @captcha_lastwarningt=3; + if (!@captcha_lastwarning) + @captcha_lastwarning=gettimetick(2); + + if (@captcha_lastwarning < gettimetick(2)) { + dispbottom l("CAPTCHA: You have %s minute(s) remaining", CaptchName(@captcha_lastwarningt)); + @captcha_lastwarningt-=1; + @captcha_lastwarning+=60; + } + } + return; + } + +} + + diff --git a/npc/functions/clientversion.txt b/npc/functions/clientversion.txt new file mode 100644 index 0000000..55ccc3f --- /dev/null +++ b/npc/functions/clientversion.txt @@ -0,0 +1,1205 @@ +// TMW2 Scripts +// Evol functions. +// Author: +// 4144 +// Jesusalva +// Description: +// Function checking the client version and reports if it is too old. +// This also handles all updates, and usually have priority. + +function script clientupdater { + // Bugfix + if (!TUT_VAR && UPDATE) + TUT_VAR=UPDATE; + // Skip any update for newcomers + if (compare(getmap(), "000-0") || $@NOUPDATES) { + UPDATE=gettimetick(2); + } + // If you're in Jail, show a minor message + if (getmap() == "sec_pri") { + dispbottom l("You seem to be jailed. We cannot unjail offline players, so please don't logout."); + dispbottom l("You have the right to defend yourself if you believe we were wrong. If we find out it's true, you'll receive an apology gift."); + dispbottom l("Note: Unless you were doing something REALLY bad which is harmful to player community (eg. scamming, hacking, DDoS'ing, etc.)"); + dispbottom l("Please contact a GM as soon as possible so we can make out a compromise. You should be liberated even if found guilty."); + } + + .@dg=0; + /* + // New skills introduced + if (UPDATE < 1531786864) { + UPDATE=1531786864; + skill TMW2_FAKESKILL, 1, 0; + skill TMW2_FAKESKILL2, 1, 0; + } + // Gallery Contest and 2019-12-29 no-drop incident + // ter jan 8 03:52:20 -02 2019 + if (UPDATE < 1546926740) { + UPDATE=1546926740; + .@u$=strtolower(strcharinfo(0)); + setarray .@apology$, "jesusalva", "mrhedx", "ziah", "soren xd", "warblade", "yuxin", "xtreem", "monking", "demure gm", "se francisc"; + setarray .@gallery$, "jesusalva", "mishana", "bobr", "demure", "lawncable"; + + // Users connected during nodrop incident (2018-12-29) + // Jesusalva Mrhedx Ziah "Soren xd" WarBlade + // yuxin Xtreem monking "demure GM" "Se Francisc" + if (array_exists(.@apology$, .@u$)) { + getitembound GoldenGift, 1, 1; + Zeny=Zeny+570; + .@dg=1; + showavatar NPC_LOF_RICH; + mesn ("TMW2 Staff"); + mesc l("Hello, @@! We just looked up and found out you were active during 2018-12-29!", strcharinfo(0)), 3; + mesc l("And during that time, item dropped was bugged! We wanted to deliver you a token of apology!"), 3; + mesc l("Sorry, and enjoy the game! Your TMW2 Staff Team."), 3; + mesc l("Token Of Apology: 1x @@, 570 GP", getitemlink(GoldenGift)); + next; + } + + // 1st Gallery Contest Participants + // Mishana LawnCable bObr Jesusalva demure + if (array_exists(.@gallery$, .@u$)) { + getitem StrangeCoin, 30; + .@dg=1; + showavatar NPC_LOF_RICH; + mesn ("TMW2 Staff"); + mesc l("Hello, @@! We are proud to announce the @@ RESULTS!", strcharinfo(0) , b(l("1° Screenshooting Contest"))), 3; + mesc l("Participants: Jesusalva * mishana * bObr * demure * LawnCable"), 3; + next; + mesc ("Mishana Entries: Happy"), 3; + mesc ("LawnCable Entries: Login Screen, TMW2 Field"), 3; + mesc ("Demure Entries: Buggy Map"), 3; + mesc ("Jesusalva Entries: LoF Secret Island, GM Plotting Evil Scheme For Next Event, Crazyfefe Cave"), 3; + mesc ("bObr Entries: My Very Own Tiny Orchard"), 3; + next; + mesc l("1st Place - Mishana, LawnCable, Jesusalva, bObr"); + mesc l("5th Place - Demure"); + mes ""; + mesc l("Hey, look, a tie happened!"); + mesc l("How's that even possible? Well, 30 @@ for all contestants!", getitemlink(StrangeCoin)); + next; + } + } + // Valentine Day Event Bug + // sex fev 15 18:04:00 -02 2019 + if (UPDATE < 1550261040) { + UPDATE=1550261040; + if (#VALENTINE_POINTS) { + .@dg=1; + showavatar NPC_LOF_RICH; + mesn ("TMW2 Staff"); + mesc l("Hello, @@! We just looked up and found out you that you were affected on a Valentine Event bug!", strcharinfo(0)), 3; + mesc l("Due this bug, you didn't got a proper amount of points per sent. We wanted to deliver you a token of apology!"), 3; + mesc l("Sorry, and enjoy the game! Your TMW2 Staff Team."), 3; + mesc l("Token Of Apology: 1x @@, 1x @@", getitemlink(SilverGift), getitemlink(MercBoxB)); + getitem SilverGift, 1; + getitem MercBoxB, 1; + #VALENTINE_POINTS=0; + next; + } + } + // Limited bots + // sex fev 15 23:22:21 -02 2019 + if (UPDATE < 1550280141) { + UPDATE=1550280141; + + .@dg=1; + mesc l("Game Rules were updated!"), 1; + mesc l("Use @@ to read the new version.", "@rules"), 1; + mes ""; + //GameRules S_NO_NPC_NAME | S_LAST_NEXT; + } + // Msawis bug + // ter fev 19 12:15:00 -03 2019 + if (UPDATE < 1550589300) { + UPDATE=1550589300; + // Unclaimed Rewards + // 2000283 Msawis 10 points + // 2000552 Mrhedx 4 points + // 2000299 vilbou 1 point + setarray .@apology, 2000552, 2000283, 2000299; + if (array_exists(.@apology, getcharid(3)) && (#VALENTINE_SENT+#VALENTINE_OPENED) > 0) { + if (getcharid(3) == 2000552) { + getitem BronzeGift, 1; + } else if (getcharid(3) == 2000283) { + getitem GoldenGift, 1; + } else if (getcharid(3) == 2000299) { + getitem StrangeCoin, 1; + } + dispbottom l("Valentine Day Event Reward automatically claimed"); + #VALENTINE_SENT=0; + #VALENTINE_OPENED=0; + #VALENTINE_RECEIVED=0; + } + } + // Update Tycoon quest exp + // dom mar 3 12:22:04 -03 2019 + if (UPDATE < 1551626524) { + UPDATE=1551626524; + if (getq(MineQuest_Tycoon) >= 15) + getexp 43000, 0; + } + // Shovel Auto-dig option + // seg mai 27 21:22:55 -03 2019 + if (UPDATE < 1559002975) { + UPDATE=1559002975; + if (countitem(IronShovel) + countitem(SteelShovel)) + dispbottom l("Shovel have been changed, right-click it to bury items."); + } + // General Updates + // sex jun 21 13:20:21 -03 2019 + if (UPDATE < 1561134021) { + UPDATE=1561134021; + if (getq(General_Narrator) >= 3) + getitem MercBoxAA, 1; + // Well, you don't need this crap I hope... + if (BaseLevel < 40) + getitem TulimMap, 1; + if (getq(TulimsharQuest_Sailors) == 4) + getexp 1508, 15; + .@cr=countitem(CroconutBox); + if (.@cr) { + getitem CroconutBox, .@cr; + dispbottom l("Croconut Box magically became two in inventory. Storage unaffected."); // Too much hassle affecting storage/cart/etc. + } + } + // General Updates + // seg jul 15 16:41:35 -03 2019 + if (UPDATE < 1565039378) { + UPDATE=1565039378; + .@dg=true; + mesc l(".:: This is Release 9.5 Academy ::."), 0; + //mesc l(".:: This is Release 10.0 Infinity ::."), 0; + // Fix mounts + unequip(EQI_SHADOW_SHOES); + setmount 0; + // You got recipe book by BSS Quest - mark as complete and get Blueprint + if (getq(NivalisQuest_BlueSageSlimes) == 2) { + getitem any(AncientBlueprint, AlchemyBlueprintA, EquipmentBlueprintA), 1; + CRAFTQUEST=1; + mesc l("You got a blueprint as reward for Blue Sage Slimes Quest completion."), 3; + } + // If you have TMW2_CRAFT skill, you need the bonus recipe + if (getskilllv(TMW2_CRAFT)) { + RECIPES_EQUIPMENT[CraftDagger]=true; + mesc l("Dagger Crafting recipe learnt."), 2; + } + // Bounty Hunter Helmet + if (MERCENARY_DAILYQUEST > 100) { + getitem BountyHunterHelmet, 1; + mesc l("You got a @@ for completing 100+ daily bountyhunter quests!", getitemlink(BountyHunterHelmet)), 2; + } + // Cindy quest new rewards + if (getq(NivalisQuest_Cindy) >= 3) { + getexp 120000, 0; + mesc l("You got 120,000 XP for completing Mercury's quest."), 2; + } + // Mercenary Rank removed + if (MERC_RANK) { + THIEF_RANK=MERC_RANK; + THIEF_EXP=MERC_EXP; + MERC_RANK=0; + MERC_EXP=0; + mesc l("Your class has been changed from %s to %s.", b(l("Merchant Police")), b(l("Thief"))), 1; + } + if (getskilllv(ALL_INCCARRY)) { + skill TF_STEAL, getskilllv(ALL_INCCARRY); + skill ALL_INCCARRY, 0, 0; + mesc l("Increase Weight skill replaced with Stealing."); + } + // NEW MAGIC SYSTEM + if (getskilllv(SN_SHARPSHOOTING)) { + skill AC_CHARGEARROW, getskilllv(SN_SHARPSHOOTING); + skill AC_CHARGEARROW, 0, 0; + getexp 2000, 150; + mesc l("Sharpshooting skill replaced with Charged Arrow."), 1; + mesc l("You've got 2000 xp and 150 job xp in apology tokens."); + } + getskilllist(); + for (.@i=0; .@i < @skilllist_count; .@i++) { + // skip temporary skills + if (@skilllist_flag[.@i] != 0) + continue; + // Only “paid” skills will result in a Scholarship Badge + .@id=@skilllist_id[.@i]; + .@am=0; + switch (.@id) { + case SM_BASH: + case TMW2_DEMURE: + case MG_FIREBALL: + case MG_SRECOVERY: + case AL_DP: + .@am=1; + case SM_ENDURE: + case KN_AUTOCOUNTER: + case KN_TWOHANDQUICKEN: + case AL_ANGELUS: + case CR_TRUST: + case CR_DEFENDER: + case AL_HOLYLIGHT: + case TF_DETOXIFY: + case ALL_RESURRECTION: + case SM_RECOVERY: + case PR_ASPERSIO: + case AB_HIGHNESSHEAL: + case SA_FREECAST: + case SA_DRAGONOLOGY: + case TMW2_SAGE: + case SA_FLAMELAUNCHER: + case SA_FROSTWEAPON: + case SA_LIGHTNINGLOADER: + case SA_SEISMICWEAPON: + case MG_ENERGYCOAT: + case MG_NAPALMBEAT: + case MG_FIREBOLT: + case MG_COLDBOLT: + case MG_LIGHTNINGBOLT: + case WZ_EARTHSPIKE: + mesc l("A skill has been replaced with an @@.", getitemlink(ScholarshipBadge)); + if (!.@am) + .@am=min(5, @skilllist_lv[.@i]); + + getitem ScholarshipBadge, .@am; + skill @skilllist_id[.@i], 0, 0; + getexp 5, (@skilllist_lv[.@i]-1)*1000; + break; + // Free skills (AL_HEAL and mass provoke) will result in XP + // Only Jakod's skills with level 3+ will result in a badge + case AL_HEAL: + case EVOL_MASS_PROVOKE: + if (@skilllist_lv[.@i] >= 3) + getitem ScholarshipBadge, 1; + case SM_PROVOKE: + case NV_TRICKDEAD: + .@delval=@skilllist_lv[.@i]; + mesc l("A skill has been removed, you got @@ job exp and @@ Strange Coin as an apology token.", .@delval*100, .@delval); + skill @skilllist_id[.@i], 0, 0; + getexp .@delval, .@delval*100; + getitem StrangeCoin, .@delval; + break; + } + } + // Grant you AL_DP based on magic level + if (MAGIC_LVL) { + skill AL_DP, MAGIC_LVL, 0; + mesc l("You have learnt \"Divine Protection\" level @@.", MAGIC_LVL), 3; + } + // Grant you Thief Skill Tier 3 + if (THIEF_RANK >= 3) { + skill(ALL_INCCARRY,1,0); + mesc l("You have learnt \"Increase Weight\" in Thief Skills."), 2; + } + // If you got a Scholarship Badge, you need to travel to Tulimshar. + // Give you a Warp Crystal to do so. + if (countitem(ScholarshipBadge)) { + getitem TulimWarpCrystal, 1; + mesc l("You've obtained a @@ to visit Magic Academy.", getitemlink(TulimWarpCrystal)), 1; + } + // Cleanup + deletearray RNGTREASURE_DATE; + + // Open Beta 2019 Main Event Rewards + .@u$=strtolower(strcharinfo(0)); + setarray .@officialnick$, "jesusalva", "kolchak", "xanthem", "seeds", "dangerduck", "test123", "sertrop", "lawncable", "pookie", "saulc", + "apane", "omatt"; + setarray .@expval, 211625, 1955041, 938439, 59663, 73899, 10338, 7494, 7857, 1992, 360, 443, 193, 5180; + setarray .@gpval, 150321, 863215, 3263, 973137, 5650, 18343, 45227, 42002, 37381, 50000, 50450, 50275, 49988; + setarray .@summer, 19, 30, 55, 0, 4, 2, 3, 4, 0, 0, 0, 0, 2; + /* Data for python + # import exp from exptable + i=0 + THEARRAY=[] + while i < len(names): + raw=overflow[i] + c=0 + while c < level[i]: + raw+=exp[c] + c+=1 + print("%s: %d exp" % (names[i], raw)) + THEARRAY.append(int(raw*0.3)) + i+=1 + + setarray .@levelval, 48, 66, 60, 39, 40, 28, 25, 26, 17, 10, 10, 8, 23; + setarray .@overflow, 25762, 338713, 55746, 1275, 14516, 504, 2855, 640, 360, 0, 277, 18, 598; + * / + + // Open Beta 2019 + .@ranking=array_find(.@officialnick$, .@u$); + // Mishana LawnCable bObr Jesusalva demure + showavatar NPC_LOF_RICH; + mes ""; + mes "*************************************"; + mes ""; + mesn ("TMW2 Staff"); + mesc l("Hello, @@! We are proud to announce the @@ RESULTS!", strcharinfo(0) , b(l("Open Beta 2019"))), 3; + mes ""; + mesc l("1st Place - Jesusalva (65)"); + mesc l("2nd Place - Kolchak (63)"); + mesc l("3rd Place - Xanthem (56)"); + mesc l("4th Place - seeds (43)"); + mesc l("5th Place - dangerDuck (23)"); + mesc l("6th Place - test123 (20)"); + mesc l("7th Place - Sertrop (17)"); + mesc l("8th Place - Pookie (13)"); + mesc l("9th Place - LawnCable (13)"); + mesc l("10th Place - Saulc (8)"); + mes ""; + mesc l("It was a reeeeeealy close dispute for the podium, but Jesusalva crafted an item at the last minute and took the first place!"); + mesc l("Although KOLCHAK managed to rank first in almost every ranking, Jesusalva was too close, and the Crafting gave him the edge he needed to take first place."); + mesc l("Congratulations to everyone who participated on the event, even those who didn't made to the top 10!"); + if (.@ranking >= 0) { + #ADD_LVL=.@expval[.@ranking]; + + // Force Ched quest start if you haven't done so already + .@year=getq(SQuest_Ched); + if (.@year != (gettime(GETTIME_YEAR)-2000)) + setq SQuest_Ched, (gettime(GETTIME_YEAR)-2000), 0, 0; + + + @ched=getq2(SQuest_Ched); + .@pts=.@summer[.@ranking]; + if (.@pts) { + getexp rand2(.@pts-1, .@pts*11/10), rand2(0,.@pts/25); + setq2 SQuest_Ched, @ched+.@pts; + } + + switch (.@ranking+1) { + case 1: + getitembound Tyranny, 1, 1; + getitem MercBoxEE, 1; + getitem EquipmentBlueprintE, 1; + getitem HousingLetterI, 1; + getitem StrangeCoin, 150; + break; + case 2: + getitem MercBoxEE, 1; + getitem EquipmentBlueprintE, 1; + getitem HousingLetterI, 1; + getitem StrangeCoin, 100; + break; + case 3: + getitem MercBoxDD, 1; + getitem EquipmentBlueprintD, 1; + getitem HousingLetterI, 1; + getitem StrangeCoin, 100; + break; + case 4: + case 5: + getitem MercBoxCC, 1; + getitem EquipmentBlueprintC, 1; + getitem HousingLetterI, 1; + getitem StrangeCoin, 50; + break; + case 6: + case 7: + getitem MercBoxBB, 1; + getitem StrangeCoin, 30; + break; + case 8: + case 9: + getitem MercBoxAA, 1; + getitem StrangeCoin, 10; + break; + case 10: + getitem MercBoxAA, 1; + break; + } // switch(ranking) + + // GP conversion rules + // if you have less than 50k, we'll return you to initial amount + // Because it is a thankyou for participating on the event ;-) + .@trugp=max(50000, .@gpval[.@ranking]); + + // You get to carry over 1 GP for each 1000 GP too (max 1000 GP bonus) + Zeny+=min(1000, .@trugp/1000); + + // You'll get 1 Rare Point for every 1k GP obtained + // Top was 1,000,000 → 1000 rare points + //#RARE_POINTS+=.@trugp/1000; + + } // if ranking + } + // Negative Karma = good. And positive karma allows PvP. So... + // sex ago 9 18:10:20 -03 2019 + if (UPDATE < 1565385020) { + UPDATE=1565385020; + // PVP bugfix + Karma=0; + // Allow to skip quest if you already have the Grimorium + if (countitem(JesusalvaGrimorium) && getskilllv(TMW2_SKILLPERMIT) == 2) { + skill TMW2_SKILLPERMIT, 3, 0; + } + // This can't happen but anyway, just in case... + if (getskilllv(TMW2_TRANSMIGRATION) > 10) { + skill TMW2_TRANSMIGRATION, 10, 0; + } + // Magic Skill Points for this skill went from 3 to 2 + if (getskilllv(ASC_METEORASSAULT)) + MAGIC_PTS-=1; + // Treasure Key from Lua + if (getq(General_Narrator) >= 3) + getitem TreasureKey, 1; + + // Post-poned Referral Rewards + if (#REFERRAL_PROG && BaseLevel >= 25 && #REFERRAL_CTRL < 1) { + #REFERRAL_CTRL=1; + rodex_sendmail(gf_charid(#REFERRAL_PROG), "TMW2 Team", "Recruited Player got Lv 25!", strcharinfo(0)+" just got level 25!\nAs they get stronger, more rewards will be sent to you!", 0, SilverGift, 1); + } + if (#REFERRAL_PROG && BaseLevel >= 50 && #REFERRAL_CTRL < 2) { + #REFERRAL_CTRL=2; + rodex_sendmail(gf_charid(#REFERRAL_PROG), "TMW2 Team", "Recruited Player got Lv 50!", strcharinfo(0)+" just got level 50!\nAs they get stronger, more rewards will be sent to you!", 0, ArcmageBoxset, 1); + } + #REFERRAL_CTRL=3; + if (#REFERRAL_PROG && BaseLevel >= 75 && #REFERRAL_CTRL < 3) { + rodex_sendmail(gf_charid(#REFERRAL_PROG), "TMW2 Team", "Recruited Player got Lv 75!", strcharinfo(0)+" just got level 75!\nAs they get stronger, more rewards will be sent to you!", 0, PrismGift, 1); + } + // Eisten Rewards + if (BaseLevel >= 50 && getq(TulimsharQuest_Eistein) == 2) + getitem SilverGift, 1; + if (BaseLevel >= 75 && getq(TulimsharQuest_Eistein) == 3) + getitem GoldenGift, 1; + if (BaseLevel >= 75 && getq(TulimsharQuest_Eistein) >= 3) + getitem GraduationRobe, 1; + // Grant you Thief Skill Tier 4 + if (THIEF_RANK >= 4) { + skill(MC_OVERCHARGE,1,0); + dispbottom l("You have learnt \"Barter\" in Thief Skills."); + } + // Update Crafting Score + CRAFTING_SCORE_COMPLETE=CRAFTING_SCORE*39; + // Water bug + if (strcharinfo(0) == "JulieWarhawk") { + .@dg=true; + + // Sell Bottles + if (countitem(BottleOfWoodlandWater) > 1) { + delitem BottleOfWoodlandWater, (countitem(BottleOfWoodlandWater)/2)+1; + } + if (countitem(EmptyBottle)) { + delitem EmptyBottle, countitem(EmptyBottle); + } + if (countitem(IcedBottle)) { + delitem IcedBottle, countitem(IcedBottle); + } + getitem ArcmageBoxset, 1; + + // Unjail player + atcommand "@unjail "+strcharinfo(0); + mesn "Jesusalva"; + mesq l("You're right, you never sold any Water Bottle ever. I'm unjailing you."); + mesc l("As an Apology Token, you're also receiving one @@.", getitemlink(ArcmageBoxset)); + next; + } + + // Bug reward (FF) + if (strcharinfo(0) == "seeds") { + getitembound LegendaryMouboo, 1, 1; + } + } + // Old regex messed things up + // All updates in August are invalid + // inv ali d --:--:-- +-- ---- + if (UPDATE > 1564617600 && UPDATE < 1567295999 && UPDATE != 1565385020) { + UPDATE=1565039378; + mesn "Narrator"; + mesc l("Warning."), 1; + mesc l("Warning."), 1; + mesc l("Warning: Invalid UNIX EPOCH time detected."), 1; + next; + mesc l("We'll be correcting this bug automatically now."); + mesc l("YOU WILL BE DISCONNECTED BY FORCE."), 1; + mesc l("Please login right after, and this error should not happen again."); + next; + // Force user to disconnect + atcommand "@kick "+strcharinfo(0); + } + // Leather Quiver price update + // qua set 4 11:07:12 -03 2019 + if (UPDATE < 1567606032) { + UPDATE=1567606032; + // Leather Quiver update + if (countitem(LeatherQuiver)) { + getitem TitaniumOre, countitem(LeatherQuiver)*3; + getitem Coal, countitem(LeatherQuiver)*2; + dispbottom l("Titanium Ore and Coal refunded on Leather Quiver update."); + } + // Braknar shield recipe + if (getq(NivalisQuest_Baktar) >= 3) { + RECIPES_EQUIPMENT[CraftBraknarShield]=true; + dispbottom l("Braknar Shield Recipe unlocked!"); + } + // Orby's fix + if (strcharinfo(0) == "Orby") { + delinventorylist(); // Needed, because we'll rely on rfind() + getinventorylist(); + .@index=array_rfind(@inventorylist_id, IronQuiver); + setitemoptionbyindex(.@index, 0, VAR_VITAMOUNT, 1); + setitemoptionbyindex(.@index, 1, IOPT_RICHNESS, 1); + dispbottom l("Iron Quiver stats restored"); + // As Orby is Hurnscald Mayor for this term, fix Hurnscald + .@df=$HURNS_TAX-100; + $HURNS_MONEY+=.@df; + $HURNS_TAX=100; + } + // Crazyfefe's fix + if (strcharinfo(0) == "Crazyfefe") { + CRAFTING_SCORE_COMPLETE+=30*39; + CRAFTING_SCORE=CRAFTING_SCORE_COMPLETE/40; + } + // Karma fix (double check + Karma=0; + } + // Baktar changes + // seg out 21 19:32:55 -03 2019 + if (UPDATE < 1571697175) { + UPDATE=1571697175; + .@q2=getq2(NivalisQuest_Baktar); + if (.@q2 & 4) + setq2 NivalisQuest_Baktar, .@q2^4; + if (.@q2 & 2) + Zeny+=1000; + if (.@q2 & 1) + Zeny+=1350; + } + // Ryan reward remaster + Monthly Reward rewrite + // qua nov 6 21:15:41 -03 2019 + if (UPDATE < 1573085741) { + UPDATE=1573085741; + // Login reward fix + if (#LOGIN_ALLTIME >= 6 && #UPDATE < 1573085741) { + dispbottom l("CONGRATULATIONS! For a semester worth of logins, you're getting a pet!"); + makepet Piou; + } + #UPDATE=1573085741; + + // Ryan Quest + if (getq(HalinarzoQuest_LifeDelight) >= 3) + getitem MylarinDust, 1; + + // Impossible... + if (getq(HalinarzoQuest_LifeDelight) >= 4) + getitem SunnyCrystal, 1; + } + // Registration Date + // sáb jan 25 18:30:00 BRT 2020 + if (UPDATE < 1579987800) { + UPDATE=1579987800; + if (!#REG_DATE) { + #REG_DATE=gettimetick(2); // 1520046000 : sáb mar 3 00:00:00 BRT 2018 + // A thank-you for being with us for almost 2 years + getitem StrangeCoin, 1; + dispbottom l("Thank you for being with us for so long. You gained a %s as a random good-hearted action from the team!", getitemlink(StrangeCoin)); + } + } + // Language over simplification + // dom fev 16 14:54:30 BRT 2020 + if (UPDATE < 1581875670) { + UPDATE=1581875670; + switch (Lang) { + case 0: + break; + case 1: // Old French + Lang=LANG_FR; break; + case 3: // Old Spanish + Lang=LANG_ES; break; + case 4: // Old Portuguese + Lang=LANG_PTBR; break; + case 5: // Old German + Lang=LANG_DE; break; + default: + Lang=99; break; + } + + if (Lang > MAX_LANG) { + .@dg=1; + Lang=0; + asklanguage(); + mes l("Ok, done."); + } + } + // AFK Time fix + // dom mar 1 22:05:00 BRT 2020 + if (UPDATE < 1583111100) { + UPDATE=1583111100; + unequipbyid(Slippers); + unequipbyid(AFKCap); + unequipbyid(Blanket); + unequipbyid(Bathrobe); + skill TMW2_SPEECH, 0, 0; + // PS. All values are ÷ 3 + if (AFKING >= 28800) + sk_lvup(TMW2_SPEECH); // 24 hours mark + if (AFKING >= 201600) + sk_lvup(TMW2_SPEECH); // 7 days mark + if (AFKING >= 864000) + sk_lvup(TMW2_SPEECH); // 30 days mark + if (AFKING >= 2592000) + sk_lvup(TMW2_SPEECH); // 90 days mark + } + // Deprecate event points + // dom abr 26 12:41:55 BRT 2020 + if (UPDATE < 1587915715) { + UPDATE=1587915715; + + if (EVENT_POINTS) { + getitem StrangeCoin, EVENT_POINTS/2; + EVENT_POINTS=0; + } + + } + // Magic v3 + // dom jun 21 05:55:00 BRT 2020 + if (UPDATE < 1592729700) { + UPDATE=1592729700; + #FIRST_TIME=true; + + if (getskilllv(TMW2_CRAFT) > 5) { + // Refund part of the money spent + Zeny+=35000+(getskilllv(TMW2_CRAFT) > 6 ? 40000 : 0); + // Update crafting + skill TMW2_CRAFT, 5, 0; + dispbottom l("Crafting rules changed! Your crafting skill was lowered to Level 5, talk to Craftmaster to recover it!"); + } + + // Rebirth Heroics + if (strcharinfo(0) == "Crazyfefe") { + getitem SupremeGift, 1; + channelmes("#world", $REBIRTH_WINNER$+" is the first player to REBIRTH!! They are so OP! %%N"); + announce "All hail ##B"+$REBIRTH_WINNER$+"##b, first player to REBIRTH and become OP!", bc_all|bc_npc; + } + + // Mass Provoke replacement + if (getskilllv(EVOL_AREA_PROVOKE)) { + getitembound ScholarshipBadge, getskilllv(EVOL_AREA_PROVOKE), 4; + MAGIC_PTS-=getskilllv(EVOL_AREA_PROVOKE)+1; + skill EVOL_AREA_PROVOKE, 0, 0; + dispbottom col(l("MSP for Area Provoke was refunded."), 1); + } + if (getskilllv(EVOL_MASS_PROVOKE)) { + skill EVOL_AREA_PROVOKE, getskilllv(EVOL_MASS_PROVOKE), 0; + skill EVOL_MASS_PROVOKE, 0, 0; + dispbottom col(l("Mass Provoke replaced with Area Provoke."), 1); + } + + // MG_LIGHTNINGBOLT and TMW2_LIGHTNINGBOLT MSP cost differ, refund + if (getskilllv(MG_LIGHTNINGBOLT)) { + getitembound ScholarshipBadge, getskilllv(MG_LIGHTNINGBOLT), 4; + MAGIC_PTS-=getskilllv(MG_LIGHTNINGBOLT); + skill MG_LIGHTNINGBOLT, 0, 0; + dispbottom col(l("MSP for Lightning Strike was refunded."), 1); + } + + // SN_WINDWALK MSP cost differ, refund + if (getskilllv(SN_WINDWALK)) { + getitembound ScholarshipBadge, getskilllv(SN_WINDWALK), 4; + MAGIC_PTS-=getskilllv(SN_WINDWALK)+2; + skill CR_TRUST, 0, 0; + dispbottom col(l("MSP for Wind Walker was refunded."), 1); + } + + // CR_TRUST MSP cost differ, refund + if (getskilllv(CR_TRUST)) { + getitembound ScholarshipBadge, getskilllv(CR_TRUST), 4; + MAGIC_PTS-=getskilllv(CR_TRUST)+2; + skill CR_TRUST, 0, 0; + dispbottom col(l("MSP for Last Man Standing was refunded."), 1); + } + + + // Eisten Update + if (getq(TulimsharQuest_Eistein) > 3) { + getitem GraduationAlbum, 1; + dispbottom l("You've got a Graduation Album for Eistein levels!"); + } + + } + // Magic v3 regression + // ter jul 7 18:42:22 BRT 2020 + if (UPDATE < 1594158142) { + UPDATE=1594158142; + + for (.@i=0; .@i < getarraysize($@MSK_MAGIC) ; .@i++) { + /* + debugmes "Debug skill %s - Lv %d", + getskillname($@MSK_MAGIC[.@i]), getskilllv($@MSK_MAGIC[.@i]); + * / + if (getskilllv($@MSK_MAGIC[.@i]) > 1) { + .@msp=(getskilllv($@MSK_MAGIC[.@i])-1); + MAGIC_PTS-=.@msp; + skill $@MSK_MAGIC[.@i], 1, 0; + dispbottom l("%s refunded to level 1 for %d MSP", + getskillname($@MSK_MAGIC[.@i]), .@msp); + } + } + + } + // AFKing bugfix + // qua jul 29 06:52:00 BRT 2020 + if (UPDATE < 1596016320) { + UPDATE=1596016320; + .@x=(AFKING >= 2592000 ? 4 : ( + AFKING >= 864000 ? 3 : ( + AFKING >= 201600 ? 2 : ( + AFKING >= 28800 ? 1 : 0 + )))); + skill(TMW2_SPEECH, .@x, 0); + dispbottom l("AFK skill levels fixed."); + } + // Magic RP bugfix + // sáb ago 1 05:45:00 BRT 2020 + if (UPDATE < 1596271500) { + UPDATE=1596271500; + .@x=(AFKING >= 2592000 ? 4 : ( + AFKING >= 864000 ? 3 : ( + AFKING >= 201600 ? 2 : ( + AFKING >= 28800 ? 1 : 0 + )))); + skill(TMW2_SPEECH, .@x, 0); + dispbottom l("AFK skill levels fixed."); + for (.@i=0; .@i < getarraysize($@MSK_MAGIC) ; .@i++) { + if (getskilllv($@MSK_MAGIC[.@i]) > 1) { + .@msp=max(0, getskilllv(@menuret)-5); + MAGIC_PTS-=.@msp; + skill $@MSK_MAGIC[.@i], 1, 0; + dispbottom l("%s removed - YOU NOOB STOP CHEATING %%e%%Q", + getskillname($@MSK_MAGIC[.@i]), .@msp); + } + } + } + // Skill RP Patch [Magic v3 delayed] + // ter ago 25 20:20:20 BRT 2020 + if (UPDATE < 1598397620) { + UPDATE=1598397620; + + // Skills deleted or with poor equivalence + skillInvoke[EVOL_AREA_PROVOKE]+=skillInvoke[EVOL_MASS_PROVOKE]; + skillInvoke[EVOL_MASS_PROVOKE]=0; + + skillInvoke[TMW2_LIGHTNINGBOLT]+=skillInvoke[MG_LIGHTNINGBOLT]; + skillInvoke[MG_LIGHTNINGBOLT]=0; + + // Skills replaced + skillInvoke[TMW2_FIREARROW]+=skillInvoke[MG_FIREBALL]; + skillInvoke[MG_FIREBALL]=0; + + skillInvoke[TMW2_NAPALMBEAT]+=skillInvoke[AL_HOLYLIGHT]; + skillInvoke[AL_HOLYLIGHT]=0; + + skillInvoke[TMW2_HOLYLIGHT]+=skillInvoke[MG_NAPALMBEAT]; + skillInvoke[MG_NAPALMBEAT]=0; + + skillInvoke[TMW2_MAGICSTRIKE]+=skillInvoke[MG_SOULSTRIKE]; + skillInvoke[MG_SOULSTRIKE]=0; + + skillInvoke[TMW2_METEORSTRIKE]+=skillInvoke[WZ_EARTHSPIKE]; + skillInvoke[WZ_EARTHSPIKE]=0; + + skillInvoke[TMW2_FROSTDIVER]+=skillInvoke[MG_COLDBOLT]; + skillInvoke[MG_COLDBOLT]=0; + + skillInvoke[TMW2_FROSTNOVA]+=skillInvoke[MG_FROSTDIVER]; + skillInvoke[MG_FROSTDIVER]=0; + + skillInvoke[TMW2_NILFHEIM]+=skillInvoke[WZ_FROSTNOVA]; + skillInvoke[WZ_FROSTNOVA]=0; + + skillInvoke[TMW2_FIRSTAID]+=skillInvoke[AL_HEAL]; + skillInvoke[AL_HEAL]=0; + + skillInvoke[TMW2_HEALING]+=skillInvoke[AB_HIGHNESSHEAL]; + skillInvoke[AB_HIGHNESSHEAL]=0; + } + // Misc bugfixes + // dom nov 22 15:27:47 BRT 2020 + if (UPDATE < 1606069667) { + UPDATE=1606069667; + if (RECIPES_EQUIPMENT[0]) { + Zeny += 15000; + getitem PrismGift, 1; + RECIPES_EQUIPMENT[0] = false; + dispbottom l("Invalid recipe from Nahrec partly reimbursed."); + } + } + */ + // Christmas Warp bug + // seg dez 28 20:45:55 BRT 2020 + if (UPDATE < 1609199155) { + UPDATE=1609199155; + if (BaseLevel < 20) { + if (getsavepoint(0) != "000-1" && + getsavepoint(0) != "003-1" && + getsavepoint(0) != "005-1" && + getsavepoint(0) != "009-1") { + // Resave at Candor + EnterTown("Candor"); + ReturnTown(); + } + } + // Assign the initial academic titles + if (strcharinfo(0) == "Jesus Saves") + ACADEMIC_RANK=ACADEMIC_GM; + else if (strcharinfo(0) == "Saulc GM") + ACADEMIC_RANK=ACADEMIC_GM; + else if (strcharinfo(0) == "jak2") + ACADEMIC_RANK=ACADEMIC_GM; + else if (strcharinfo(0) == "LawnCable") + ACADEMIC_RANK=ACADEMIC_BACHELOR; + else if (strcharinfo(0) == "Kolchak") + ACADEMIC_RANK=ACADEMIC_TECHNIC; + else if (strcharinfo(0) == "dangerDuck") + ACADEMIC_RANK=ACADEMIC_TECHNIC; + else if (strcharinfo(0) == "Livio") + ACADEMIC_RANK=ACADEMIC_TECHNIC; + else if (strcharinfo(0) == "Arthur") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Mathias Cronqvist") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Manatauro") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "GoNzO") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "YuckFou") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Xanthem") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Povo") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Esteria") + ACADEMIC_RANK=ACADEMIC_STUDENT; + + if (ACADEMIC_RANK != ACADEMIC_LAYMAN) { + // Select a benefactor + .@benefactor$=rand_sponsor(); + // Inform yourself + dispbottom l("You received the %s title from %s.", + academicrank(), .@benefactor$); + // Inform everyone + kamibroadcast(sprintf("%s has been granted the title of %s. Thank %s!", + strcharinfo(0), academicrank(), .@benefactor$)); + // Log in a special log file as well + logmes(sprintf("%s has been granted the title of %s.", + strcharinfo(0), academicrank())); + } + + // Gallery Contest + .@dg=1; + showavatar NPC_LOF_RICH; + mesn ("TMW2 Staff"); + mesc l("Hello, @@! We are proud to announce the @@ RESULTS!", strcharinfo(0) , b(l("2° Screenshooting Contest"))), 3; + mesc l("Participants: Povo * Manatauro * Woody"), 3; + next; + mesc ("Povo Entries: What to do with Presents?"), 3; + mesc ("Woody Entries: Lava Dungeon"), 3; + mesc ("Manatauro Entries: Yuck Noob =) ; Povo :) ; Lurking GM and Me"), 3; + next; + mesc l("1st Place - Povo (6 likes)"); + mesc l("2nd Place - Manatauro (5 likes)"); + mesc l("3rd Place - Woody (4 likes)"); + mes ""; + next; + if (strcharinfo(0) == "Povo") + getitem StrangeCoin, 80; + else if (strcharinfo(0) == "Manatauro") + getitem StrangeCoin, 40; + else if (strcharinfo(0) == "Woody") + getitem StrangeCoin, 20; + } + // Easter Eggs + // qua jul 29 06:52:00 BRT 2020 + if (UPDATE < 1610083840) { + UPDATE=1610083840; + .@q=getq(General_EasterEggs); + if (.@q) { + setq General_EasterEggs, 1, .@q, bitmask_count(.@q); + dispbottom l("Easter Egg quest updated. Total found: %02d/06", + bitmask_count(.@q)); + } + } + // Change variable scope + // sáb jan 16 19:21:15 BRT 2021 + if (UPDATE < 1610835675) { + UPDATE=1610835675; + #GUILDSHOP=GUILDSHOP; + GUILDSHOP=0; + } + // Post-Fire Titulations + // ter ago 17 20:02:40 BRT 2021 + if (UPDATE < 1629241360) { + UPDATE=1629241360; + // Assign the initial academic titles + if (strcharinfo(0) == "Jesus Saves") + ACADEMIC_RANK=ACADEMIC_GM; + else if (strcharinfo(0) == "Saulc GM") + ACADEMIC_RANK=ACADEMIC_GM; + else if (strcharinfo(0) == "jak1") + ACADEMIC_RANK=ACADEMIC_GM; + else if (strcharinfo(0) == "dangerDuck") + ACADEMIC_RANK=ACADEMIC_TECHNIC; + else if (strcharinfo(0) == "Livio") + ACADEMIC_RANK=ACADEMIC_TECHNIC; + else if (strcharinfo(0) == "Xanofire") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Arthur the King") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Mathias Cronqvist") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Mana") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Povo") + ACADEMIC_RANK=ACADEMIC_STUDENT; + else if (strcharinfo(0) == "Esteria") + ACADEMIC_RANK=ACADEMIC_STUDENT; + + //else if (strcharinfo(0) == "LawnCable") + // ACADEMIC_RANK=ACADEMIC_BACHELOR; + //else if (strcharinfo(0) == "Kolchak") + // ACADEMIC_RANK=ACADEMIC_TECHNIC; + //else if (strcharinfo(0) == "GoNzO") + // ACADEMIC_RANK=ACADEMIC_STUDENT; + //else if (strcharinfo(0) == "YuckFou") + // ACADEMIC_RANK=ACADEMIC_STUDENT; + + if (ACADEMIC_RANK != ACADEMIC_LAYMAN) { + // Select a benefactor + .@benefactor$=rand_sponsor(); + // Inform yourself + dispbottom l("You received the %s title from %s.", + academicrank(), .@benefactor$); + // Inform everyone + kamibroadcast(sprintf("%s has been granted the title of %s. Thank %s!", + strcharinfo(0), academicrank(), .@benefactor$)); + // Log in a special log file as well + logmes(sprintf("%s has been granted the title of %s.", + strcharinfo(0), academicrank())); + } + } + // Change some quest inner workings + // qua set 22 12:30:40 BRT 2021 + if (UPDATE < 1632324640) { + UPDATE=1632324640; + setq1 HalinarzoQuest_Alvasus, getq(HalinarzoQuest_Alvasus)+1; + } + // Christmas 2021 + // ter dez 28 11:57:17 BRT 2021 + if (UPDATE < 1640703437) { + .@u$ = strtolower(strcharinfo(0)); + setarray .@xmas21$, "jesusalva", "poppet", "jak1", "horak", "hocus pocus fidibus", "statue"; + setarray .@xmas20$, "andulkat", "kolchak", "Pacman_I_I"; + if (array_exists(.@xmas21$, .@u$)) { + .@dg=1; + showavatar NPC_LOF_RICH; + mesn ("TMW2 Staff"); + mesc l("Hello, %s! Congratulations for finishing the %s!", strcharinfo(0), b(l("Christmas 2021 Joint Event"))), 3; + mesc l("You need to pick your prizes!"), 3; + inventoryplace NPCEyes, 6, Iten, 5; + next; + mesc l("Select one:"), 3; + menuint + l("Elixir of Life"), ElixirOfLife, + l("Sacred Immortality Potion"), SacredImmortalityPotion; + mes ""; + .@it1 = @menuret; + mesc l("Select one:"), 3; + menuint + l("Short Gunstaff"), PynRevolver, + l("Rapid Gunstaff"), PynGatling, + l("Explosive Gunstaff"), PynShotgun, + l("Powerful Gunstaff"), PynRifle; + mes ""; + .@it2 = @menuret; + getitem .@it1, 5; + getitembound .@it2, 1, 1; + getitem GoldenBossGift, 1; + getitem SupremeGift, 1; + getitem MagicApple, 1; + getitem Arcanum, 1; + getitem StrangeCoin, 30; + } else if (array_exists(.@xmas20$, .@u$)) { + getitem Arcanum, 1; + getitem StrangeCoin, 30; + } + UPDATE=1640703437; + // misc fixes + if (getq(LoFQuest_George) >= 3) + getexp 45000, 0; + if (getq(LoFQuest_George) >= 5) + getexp 135000, 0; + if (getq2(HalinarzoQuest_LifeDelight) >= 20) + getitem Coffee, 1; + if (getq2(HalinarzoQuest_LifeDelight) >= 30) + getitem Manapple, 1; + if (getq2(HalinarzoQuest_LifeDelight) >= 40) + getitem LoFWarpCrystal, 1; + if (getq2(HalinarzoQuest_LifeDelight) >= 60) + getitem SacredLifePotion, 1; + if (getq2(HalinarzoQuest_LifeDelight) >= 70) + getitem MercBoxDD, 1; + if (getq2(HalinarzoQuest_LifeDelight) >= 80) + getitem DivineApple, 1; + if (getq2(HalinarzoQuest_LifeDelight) >= 90) + getitem MysteriousFruit, 1; + if (getq2(HalinarzoQuest_LifeDelight) >= 21) + Mobpt+=((getq2(HalinarzoQuest_LifeDelight) - 1) * (getq2(HalinarzoQuest_LifeDelight) - 20)); // Will work... Sort of + } + // Realm of Drops + // sab set 24 15:37:15 BRT 2022 + if (UPDATE < 1664044635) { + UPDATE=1664044635; + skill TMW2_DROPS, REBIRTH+1, 0; + if (getskilllv(TMW2_SKILLPERMIT) >= 1) + getitem ScholarshipBadge, 1; + if (getskilllv(TMW2_SKILLPERMIT) >= 2) + getexp 20000, 1000; + if (getskilllv(TMW2_SKILLPERMIT) >= 3) + getexp 80000, 0; + // Actual value has changed, you'll be refunded in EXP + if (getskilllv(TMW2_SKILLPERMIT) >= 1) + #ADD_LVL+=80000; + } + // Dausen update + // dom out 22 13:15:32 BRT 2022 + if (UPDATE < 1666541732) { + UPDATE=1666541732; + if (getq(TulimsharQuest_MobTutorial) >= 2) + Zeny+=246; + if (getq(TulimsharQuest_MobTutorial) >= 4) + Zeny+=616; + if (getq(TulimsharQuest_MobTutorial) >= 6) + Zeny+=1068; + if (getq(TulimsharQuest_MobTutorial) >= 8) + Zeny+=2200; + } + + // :// End of Regular Update System + //////////////////////////////////// + // Valentine 2020 Record + if (#VALENTINE20_REWARDS) { + .@dg=1; + // Get misc items + getitem OolongTea, 3; + getitem SpearmintTea, 5; + getitem CrazyRum, 1; + getitem DwarvenSake, 3; + dispbottom col(l("THANK YOU for participating on cross-server Valentine 2020 Event! We hope you had fun!"), 7); + #VALENTINE20_REWARDS=false; + } + + // Permanent Gold Boost + // #ADD_GP means the user have to right to get some gp + if (#ADD_GP) { + Zeny+=#ADD_GP; + #ADD_GP=0; + } + + // Permanent Strange Coins Boost + // #ADD_SC means the user have to right to get some strange coins + if (#ADD_SC) { + logmes "[Merger] Strange Coins +"+#ADD_SC, LOGMES_ATCOMMAND; + getitem StrangeCoin, #ADD_SC; + #ADD_SC=0; + } + + // Permanent Monster Points Boost + // #ADD_MPT means the user have to right to get some Mobpt + if (#ADD_MPT) { + logmes "[Merger] Mobpt +"+#ADD_MPT, LOGMES_ATCOMMAND; + Mobpt+=#ADD_MPT; + #ADD_MPT=0; + } + + // Non Permament Level Boost + if (#ADD_TMP_LVL) { + // Grant the level + if (numdate() <= 20200216 && !#SAVED_LVL) { + #SAVED_LVL=BaseLevel; + BaseLevel=#ADD_TMP_LVL; + dispbottom l("Level set from %d to %d", #SAVED_LVL, BaseLevel); + + // Welcome message + .@dg=true; + showavatar NPC_LOF_RICH; + mesn "Moubootaur Legends Server"; + mesc l("First of all: Welcome! Your level was temporaly modified, it'll be reverted once event is over."), 3; + next; + mesn "Moubootaur Legends Server"; + mesc l("You will be able to pick basic weapons and skills at the Event Soul Menhir."), 3; + next; + mesn "Moubootaur Legends Server"; + mesc l("Be careful during night, use \"@tutorial\" in case of doubt, use \"%s\" to go to the event, and good luck!", b("@toevent")), 3; + next; + // Restore the level + } else if (numdate() > 20200216 && #SAVED_LVL) { + dispbottom l("Level reset from %d to %d", BaseLevel, #SAVED_LVL); + // Save + #DEBUG_LEVEL1=#ADD_TMP_LVL; + #DEBUG_LEVEL2=BaseLevel; + #DEBUG_LEVEL3=#DEBUG_LEVEL2-#DEBUG_LEVEL1; + #DEBUG_EXP=BaseExp; + // Reset + //BaseLevel=#SAVED_LVL; + //#SAVED_LVL=0; + #ADD_TMP_LVL=0; + // Player too late for event! + } else if (numdate() > 20200216) { + dispbottom l("This event has already ended."); + #ADD_TMP_LVL=0; + } + } + + // Permanent Level Boost + // #ADD_LVL means the user have to right to get some levels. This is a sketch. The code might be deleted. + if (#ADD_LVL) { + //logmes "[Merger] EXP +"+#ADD_LVL, LOGMES_ATCOMMAND; + if ((readparam(BaseExp)+#ADD_LVL > readparam(NextBaseExp))) { + do { + if (BaseLevel >= TOP3AVERAGELVL()) break; // Boundaries + .@v=readparam(NextBaseExp)-readparam(BaseExp); + getexp .@v, 0; + #ADD_LVL-=.@v; + } while (readparam(BaseExp)+#ADD_LVL > readparam(NextBaseExp)); + } + // Only give remaining EXP if you did not hit the boundary + if (BaseLevel < TOP3AVERAGELVL()) { + getexp #ADD_LVL, 0; + #ADD_LVL=0; + } + //rodex_sendmail(getcharid(0), "TMW2 Team", "Welcome to Moubootaur Legends", "Hey, thanks for believing on us! We hope to have a great time together. Any problem, ask for help in #world - the Discord bridge! Enjoy our game!", 20); + // TODO: Maybe give an item which lets you open the storage anywhere? + } + + if (.@dg) { + next; + closeclientdialog; + } + return; + +} + +// Just be sure your client version is OK +// NOTE: Current master client version is 26, but M+ 1.9.3.23 is still at 25 +function script checkclientversion { + // Destroy Karma, regardless of version and perpetually + Karma=0; + + // Minimum version + if (ClientVersion >= 25) + return; + + mesn "Narrator"; + mesc l("Warning."), 1; + mesc l("Warning."), 1; + mesc l("Warning: You are using an old client."), 1; + next; + //mesc l("Please install the new client from [@@https://manaplus.org/|https://manaplus.org/@@]"); + mesc l("Alternatively, download a bleeding edge build at [@@https://manaplus.germantmw.de/|https://manaplus.germantmw.de/@@]"); + mesc l("Or use our awesome [@@https://tmw2.org/manalauncher/InstallManaLauncher.exe|Mana Launcher@@] if you're on Windows or Linux."); + next; + if (ClientVersion >= 24) mesc l("Compatibility Support mode enabled. Client may crash AT RANDOM, beware."); + if (ClientVersion >= 24) mesc l("Crazy stuff can happen in overall. YOU HAVE BEEN WARNED TO UPDATE YOUR CLIENT."); + if (ClientVersion >= 24) closeclientdialog; + if (ClientVersion >= 24) return; + mesc l("You won't be allowed to play this game until you have an updated client."); + next; + // Force user to disconnect + atcommand "@kick "+strcharinfo(0); + return; +} diff --git a/npc/functions/daily.txt b/npc/functions/daily.txt new file mode 100644 index 0000000..2c7ff08 --- /dev/null +++ b/npc/functions/daily.txt @@ -0,0 +1,230 @@ +// TMW2 Script. +// Authors: +// Jesusalva +// Description: +// Daily Login Reward + +function script daily_login_bonus_handler { + // Handle daily login bonus + // The Strange Coin output wasn't changed, but now it relies on streaks. + // Variables: + // #LOGIN_DAY + // Current day + // #LOGIN_TABLE + // Current month + // #LOGIN_STREAK + // Number of monthly connections + // #LOGIN_ALLTIME + // Number of times you claimed the top prize (27 days streak) + //debugmes "DLBH"; + + // GMs can receive Strange Coins + if (GSET_AUTORECEIVE_COINS) { + if (is_gm()) { + if (#GMEVENT_T <= gettimetick(2)) { + #GMEVENT_T=gettimetick(2)+(60*60*24); + getitem StrangeCoin, 30; + } + } else { + GSET_AUTORECEIVE_COINS=false; + } + } + + if (#LOGIN_DAY != gettime(5)) { + // demure check: Are you on a start area? + getmapxy(.@m$,.@x,.@y,0); + if (compare(.@m$, "000-0")) + return; + + //debugmes "[DLBH] Mapcheck ok"; + // Is it a new month? + if (#LOGIN_TABLE == gettime(6)) { + #LOGIN_STREAK=#LOGIN_STREAK+1; + } else { + #LOGIN_STREAK=1; + #LOGIN_TABLE=gettime(6); + #TMW2_LOGINBONUS=0; + } + + // Update last day you've claimed a reward + #LOGIN_DAY = gettime(5); + //debugmes "[DLBH] month checks ok"; + + // Handle rewards: Streaks first, daily later. Streak reward prevail over daily reward. + if ($@NOUPDATES) { + dispbottom col(l("Updates were disabled"), 1); + } else if (#LOGIN_STREAK > 27) { + getitem StrangeCoin, 2; + getitem CasinoCoins, 1; + dispbottom l("##2 %d Days login bonus: ##B2x %s, 1x %s##b", #LOGIN_STREAK, getitemlink(StrangeCoin), getitemlink(CasinoCoins)); + } else if (#LOGIN_STREAK == 27) { + #LOGIN_ALLTIME+=1; + .@am=1; + switch (#LOGIN_ALLTIME % 12) { + case 1: + if (#LOGIN_ALLTIME == 1) + .@prize=RightEyePatch; + else + .@prize=SilverGift; + break; + case 2: + .@prize=ArcmageBoxset; break; + case 3: + .@prize=MercBoxC; break; + case 4: + .@prize=AncientBlueprint; break; + case 5: + .@prize=StrangeCoin; .@am=120; break; + case 6: + if (#LOGIN_ALLTIME == 6) { + dispbottom l("CONGRATULATIONS! For a semester worth of logins, you're getting a pet!"); + .@prize=PiouEgg; .@am=0; + makepet Piou; + } else { + .@prize=GoldenGift; + } + break; + case 7: + .@prize=GoldenGift; break; + case 8: + .@prize=MercBoxD; break; + case 9: + .@prize=PrismGift; break; + case 10: + .@prize=StrangeCoin; .@am=150; break; + case 11: + .@prize=MercBoxE; break; + case 0: + .@prize=MysteriousFruit; break; + default: + .@prize=ElixirOfLife; break; + } + + if (.@am) + getitem .@prize, .@am; + dispbottom l("##2 27 Days login bonus: ##B1x %s##b", getitemlink(.@prize)); + } else if (#LOGIN_STREAK == 21) { + getitem BronzeGift, 1; + dispbottom l("##2 21 Days login bonus: ##B1x %s##b", getitemlink(BronzeGift)); + } else if (#LOGIN_STREAK == 14) { + getitem BronzeGift, 1; + dispbottom l("##2 14 Days login bonus: ##B1x %s##b", getitemlink(BronzeGift)); + } else if (#LOGIN_STREAK == 7) { + getitem StrangeCoin, 3; + dispbottom l("##2 7 Days login bonus: ##B3x %s##b", getitemlink(StrangeCoin)); + } else if (#LOGIN_STREAK == 3) { + getitem StrangeCoin, 1; + dispbottom l("##2 3 Days login bonus: ##B1x %s##b", getitemlink(StrangeCoin)); + } else if (#LOGIN_STREAK % 3 == 0) { + .@value=max(20, rand2(#LOGIN_STREAK, #LOGIN_STREAK*2)); + .@value+=(BaseLevel*5/2)+rand2(JobLevel, JobLevel*7/20); + .@value=.@value * 5 / 4; // Bonus 25% + Zeny=Zeny+.@value; + dispbottom l("##2Daily login bonus: ##B%d GP##b", .@value); + } else if (#LOGIN_STREAK % 3 == 2) { + .@value=max(5, rand2(0, (#LOGIN_STREAK/4))); + .@value+=(BaseLevel**2); + .@value=(.@value*3/4)+#LOGIN_STREAK; // 50% → 75% + getexp .@value, 0; + dispbottom l("##2Daily login bonus: ##B%d EXP##b", .@value); + } else { + .@value=max(5, rand2(0, (#LOGIN_STREAK/4))); + .@value+=(JobLevel**2); + .@value=(.@value/2)+#LOGIN_STREAK; // 33% → 50% + getexp 0, .@value; + dispbottom l("##2Daily login bonus: ##B%d Job Exp.##b", .@value); + } + + // Handle event login bonus + if (gettime(6) == JANUARY) { + if (#TMW2_LOGINBONUS != gettime(GETTIME_YEAR) && gettime(5) == 13) { + #TMW2_LOGINBONUS=gettime(GETTIME_YEAR); + getitem StrangeCoin, 10; + // TMW2 Anniversary is project, not server. + // Therefore, contributors get an extra reward + .@m=htget($@CONTRIBUTORS, strtolower(strcharinfo(0)), 0); + if (.@m) + getitem StrangeCoin, min(9, .@m/100)+1; + dispbottom "##B##2"+l("It's TMW2 Project anniversary!")+" "+l("We thank every developer which helped this project thus far!")+"##b##0"; + } + } + if (gettime(6) == MARCH) { + if (#TMW2_LOGINBONUS != gettime(GETTIME_YEAR) && gettime(5) == 2) { + #TMW2_LOGINBONUS=gettime(GETTIME_YEAR); + getitem MercBoxC, 1; + // TMW2 Day is server, not project. + // Therefore, players get extra reward + dispbottom "##B##2"+l("It's TMW2 Server anniversary!")+" "+l("We thank every player, because without them, this would be nothing!")+"##b##0"; + } + } + if (gettime(6) == JUNE) { + if (#TMW2_LOGINBONUS != gettime(GETTIME_YEAR) && gettime(5) == 21) { + #TMW2_LOGINBONUS=gettime(GETTIME_YEAR); + getitem DungeonMap, 1; + dispbottom "##B##2"+l("It's Jesusalva's anniversary!")+" "+l("Also, Summer just started. Why not taking this opportunity to go Treasure Hunting?!")+"##b##0"; + } + } + if (gettime(6) == JULY) { + if (#TMW2_LOGINBONUS != gettime(GETTIME_YEAR) && gettime(5) == 7) { + #TMW2_LOGINBONUS=gettime(GETTIME_YEAR); + getitem ChocolateBar, 1; + dispbottom "##B##2"+l("It's International Chocolate Day!")+"##b "+l("All monsters may drop chocolate during this period. And here is one for you!")+"##0"; + } + } + if (gettime(6) == SEPTEMBER) { + if (#TMW2_LOGINBONUS != gettime(GETTIME_YEAR) && gettime(5) == 9) { + #TMW2_LOGINBONUS=gettime(GETTIME_YEAR); + getitem any(ScentGrenade, Grenade, SmokeGrenade), 2; + dispbottom "##B##2"+l("It's the Free Software Day!")+" "+l("Licensing was one of the worst hassle we had, but just today, all mobs may drop Ancient Blueprints. Enjoy!")+"##b##0"; + } + } + if (gettime(6) == OCTOBER) { + if (#TMW2_LOGINBONUS != gettime(GETTIME_YEAR) && gettime(5) == 1) { + #TMW2_LOGINBONUS=gettime(GETTIME_YEAR); + getitem Coffee, 1; + getexp BaseLevel, JobLevel; + dispbottom "##B##2"+l("It's the International Coffee Day!")+" "+l("Have a warm cup of Coffee on the house, and enjoy!")+"##b##0"; + } + } + if (gettime(6) == DECEMBER) { + if (!#XMAS_LOGINBONUS && gettime(5) >= 24 && gettime(5) <= 26) { + #XMAS_LOGINBONUS=1; + getitem XmasGift, 1; + dispbottom "##B##2"+l("Merry Christmas!")+" "+l("You have gained a special login bonus!")+"##b##0"; + } + } + + // We're almost done with daily logins, just the optional User Interface + if (!GSET_DAILYREWARD_SILENT) { + setnpcdialogtitle l("Daily Login Rewards"); + setskin "daily_"+#LOGIN_STREAK; + mes "Please keep your ManaPlus updated."; + //mes "This is a debug message. Your manaplus version is wrong."; + //mes "You should not be reading this. I'll call you a cheater."; + //mes "I hope you report this (if a bug). Reading source code?"; + //mes "4144 will hear about this. You are NOT amazing by the way."; + select("Ok"); + setskin ""; + closeclientdialog; + } + } + + //debugmes "[DLBH] Finished: "+#LOGIN_DAY+" ok"; + return; +} + +// Gives you guild coins, but weekly (based on Guild Level) +function script guild_login_bonus { + .@g=getcharid(2); + if (.@g < 1) + return; + + .@c=min(5, getguildavg(.@g)/20) + min(1+getguildlvl(.@g)/5, 10); + if (#LOGIN_GUILD_WEEK != gettimeparam(GETTIME_WEEKDAY)) { + #LOGIN_GUILD_WEEK=gettimeparam(GETTIME_WEEKDAY); + getitem GuildCoin, .@c; + dispbottom l("##2Guild's Weekly login bonus: ##B%d %s##b", .@c, getitemlink(GuildCoin)); + } + return; +} + diff --git a/npc/functions/doors.txt b/npc/functions/doors.txt new file mode 100644 index 0000000..c1b6565 --- /dev/null +++ b/npc/functions/doors.txt @@ -0,0 +1,59 @@ +// Evol functions. +// Author: +// 4144 +// Description: +// Doors utility functions +// Variables: +// none + +function script doorTouch { + if (getareausers() <= 1) + { + .dir = 2; + stopnpctimer; + initnpctimer; + } + close; +} + +function script doorUnTouch { + if (getareausers(.map$) == 0) + { + .dir = 4; + initnpctimer; + startnpctimer; + } + close; +} + +function script doorTimer { + stopnpctimer; + if (.dir == 2) .dir = 6; + if (.dir == 4) .dir = 8; + end; +} + +function script doorInit { + .distance = 5; + .alwaysVisible = true; + .sex = G_OTHER; + end; +} + +function script open_door { + .@door$ = getarg(0); + setnpcdir.@door$, 2; + sleep 300; + setnpcdir .@door$, 6; + sleep 300; + return 0; +} + +function script close_door { + .@door$ = getarg(0); + setnpcdir .@door$, 4; + sleep 300; + setnpcdir .@door$, 8; + sleep 300; + return 0; +} diff --git a/npc/functions/dungeon.txt b/npc/functions/dungeon.txt new file mode 100644 index 0000000..641d00d --- /dev/null +++ b/npc/functions/dungeon.txt @@ -0,0 +1,162 @@ +// TMW2 Script +// Authors: +// Jesusalva +// Description: +// Dungeon utilities + +- script #DungeonCore NPC_HIDDEN,{ + end; + +// Main initialization +OnInit: + setarray .heatmap$, "007-2"; + setarray .cursemap$, "006-4", "006-4-1", "025-1"; + setarray .sickmap$, "029-5"; + setarray .bleedmap$, "006-9"; + end; + +///////////////////////////////////////// +// Heartbeat for Heat effects +OnHeat: + // Did you left? + .@i=array_find(.heatmap$, getmap()); + if (.@i < 0) { + @heat$=""; + end; + } + + // First time seeing this + if (@heat$ != getmap()) { + @heat$=getmap(); + dispbottom l("This is a hot map, you're suffering damage over time."); + } + + // You are in a HEATMAP$, so suffer damage from heat + if (@coolio > gettimetick(2) || Class == Redy) + percentheal -1, 0; + else + percentheal -5, 0; + + // New tick (dies in 140~180 seconds) + addtimer2 rand2(7000, 9000), .name$+"::OnHeat"; + end; + + +///////////////////////////////////////// +// Heartbeat for Cursed Land map effects +OnCurse: + // Did you left? + .@i=array_find(.cursemap$, getmap()); + if (.@i < 0) { + @curse$=""; + end; + } + + // First time seeing this + if (@curse$ != getmap()) { + @curse$=getmap(); + dispbottom l("This is a Cursed Lands map, you'll lose mana over time and may also incurr in debuffs if mana is low (may cause death)."); + } + + // You are in a CURSEMAP$, so suffer damage from the curse + if (@purifio > gettimetick(2) || Class == Savior) + heal 0, -1; + else if (islegendary()) + percentheal 0, -2; + else + percentheal 0, -4; + + // MP is below 20%, you'll get cursed + if (Sp*100 < MaxSp*20) + SC_Bonus(15, SC_CURSE, 1); + + // MP is below 1%, you'll get start losing HP rapidly + if (Sp*100 < MaxSp) + percentheal -9, 0; + + // New tick (cycles every 15 seconds in average) + addtimer2 rand2(14000, 15000), .name$+"::OnCurse"; + end; + +///////////////////////////////////////// +// Heartbeat for Disease effects +OnSick: + // Did you left? + .@i=array_find(.sickmap$, getmap()); + if (.@i < 0) { + if (@forced_sick$ != getmap()) { + @sick$=""; + end; + } + } + + // First time seeing this + if (@sick$ != getmap()) { + @sick$=getmap(); + dispbottom l("This map contains poisonous gas and may cause diseases."); + } + + // The effect is based on HP + if (Hp*10 > MaxHp*7) + .@eff = SC_BLOODING; + else if (Hp*10 > MaxHp*4) + .@eff = SC_POISON; + else + .@eff = SC_DPOISON; + + // You are in a SICKMAP$, so suffer damage from disease + if (@sickio > gettimetick(2)) { + heal -50, 0; + } else { + heal -rand2(50, 200), 0; + SC_Bonus(10, .@eff, 1); + } + + // New tick (20 seconds fixed cycle) + addtimer2 20000, .name$+"::OnSick"; + end; + + +///////////////////////////////////////// +// Heartbeat for Bleed effects +OnBleed: + // Did you left? + .@i=array_find(.bleedmap$, getmap()); + if (.@i < 0) { + @bleed$=""; + end; + } + + // First time seeing this + if (@bleed$ != getmap()) { + @bleed$=getmap(); + dispbottom l("This is a bleeding map, HP won't recover naturally, and bleeding may start."); + } + + // You are in a BLEEDMAP$, HP regeneration is disabled + if (@bleedio > gettimetick(2)) + sc_end SC_HALT_REGENERATION; + else + SC_Bonus(15, SC_HALT_REGENERATION, 1); + + // You may bleed at 2% chance per 15 seconds + if (rand2(50) == 25) + SC_Bonus(15, SC_BLOODING, 1); + + // New tick (15 seconds fixed cycle) + addtimer2 15000, .name$+"::OnBleed"; + end; + + +} + + +////////////////////////////// +007-2 mapflag nosave 007-1,99,189 +006-4 mapflag nosave 006-3,54,36 +006-4-1 mapflag nosave 006-3,54,36 +029-5 mapflag nosave 029-4,21,97 +006-9 mapflag nosave 006-6,46,27 + + + diff --git a/npc/functions/estate.txt b/npc/functions/estate.txt new file mode 100644 index 0000000..445f672 --- /dev/null +++ b/npc/functions/estate.txt @@ -0,0 +1,96 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Primary Script Helpers +// WARNING: They affect directly the real estate global variables! + +// This function reduces payment accordingly +// realestate_payment ( amount ) +function script realestate_payment { + REAL_ESTATE_CREDITS=REAL_ESTATE_CREDITS-getarg(0); + if (REAL_ESTATE_CREDITS < 0) { + Zeny+=REAL_ESTATE_CREDITS; + REAL_ESTATE_CREDITS=0; + } + return; +} + +// Generate unique name for setcells +// realestate_cellname ( estate_id, object_id ) +function script realestate_cellname { + return "RESObj_"+getarg(0)+"_"+getarg(1); +} + +// Generate sell price for furniture based on original price and estate ID +// realestate_sellprice ( estate_id, price ) +function script realestate_sellprice { + .@timeleft=$ESTATE_RENTTIME[getarg(0)]-gettimetick(2); // Number of seconds + .@daysleft=.@timeleft/86400; // Number of days left of rent + .@weeksleft=.@timeleft/604800; // Number of weeks left of rent + + //debugmes "Your contract is valid for %d weeks more - %d days", .@weeksleft, .@daysleft; + //debugmes "The divisor is %d", max(1, 8-.@weeksleft); + + return (getarg(1)/max(1, 8-.@weeksleft)) - max(0, 60-.@daysleft); +} + +// This will toggle if mobilia was purchased or not, in the right group +// And as an added bonus, will tell the correct Script to reload NPCs +// realestate_togglemobilia ( estate_id, layer_id, object_id{, npc_file} ) +function script realestate_togglemobilia { + switch (getarg(1)) { + case 1: + $ESTATE_MOBILIA_64[getarg(0)] = $ESTATE_MOBILIA_64[getarg(0)] ^ getarg(2); + break; + case 2: + $ESTATE_MOBILIA_4[getarg(0)] = $ESTATE_MOBILIA_4[getarg(0)] ^ getarg(2); + break; + case 3: + $ESTATE_MOBILIA_8[getarg(0)] = $ESTATE_MOBILIA_8[getarg(0)] ^ getarg(2); + break; + case 4: + $ESTATE_MOBILIA_32[getarg(0)] = $ESTATE_MOBILIA_32[getarg(0)] ^ getarg(2); + break; + case 5: + $ESTATE_MOBILIA_128[getarg(0)] = $ESTATE_MOBILIA_128[getarg(0)] ^ getarg(2); + break; + case 6: + $ESTATE_MOBILIA_2[getarg(0)] = $ESTATE_MOBILIA_2[getarg(0)] ^ getarg(2); + break; + default: + debugmes("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", getarg(2), getarg(1)); + break; + } + if (getarg(3, "error") != "error") { + // Reload NPCs on the meanwhile + donpcevent getarg(3)+"::OnReload"; + } + return; +} + + +// Like the previous function, but returns true if player have said mobilia +// realestate_hasmobilia ( estate_id, layer_id, object_id ) +function script realestate_hasmobilia { + switch (getarg(1)) { + case 1: + return $ESTATE_MOBILIA_64[getarg(0)] & getarg(2); + case 2: + return $ESTATE_MOBILIA_4[getarg(0)] & getarg(2); + case 3: + return $ESTATE_MOBILIA_8[getarg(0)] & getarg(2); + case 4: + return $ESTATE_MOBILIA_32[getarg(0)] & getarg(2); + case 5: + return $ESTATE_MOBILIA_128[getarg(0)] & getarg(2); + case 6: + return $ESTATE_MOBILIA_2[getarg(0)] & getarg(2); + default: + consolebug("[ERROR] [CRITICAL] [REAL ESTATE]: Object %d have Invalid Collision Type: %d (must range 1~6)", getarg(2), getarg(1)); + return false; + } + return false; +} + diff --git a/npc/functions/estate2.txt b/npc/functions/estate2.txt new file mode 100644 index 0000000..99100f2 --- /dev/null +++ b/npc/functions/estate2.txt @@ -0,0 +1,326 @@ +// TMW2: Moubootaur Legends scripts. +// Author: +// Jesusalva +// Description: +// Real Estate System +// Secondary Script Helpers +// WARNING: They affect directly the real estate global variables! +////////////////////////////////////////////////////////////////////////////// +///////////// Dialog Helpers +// "If you copy-paste the same line too often, make a function for it" + +// Rent_Available Label +// Returns true regardless of player decision +// realestate_rent ( estate_id, price, <rent time> ) +function script realestate_rent { + .id=getarg(0); + .price=getarg(1); + .time=getarg(2, 2592000); // Defaults to 30 days + + do + { + mesc l("This Real Estate is available for rent for only @@ GP!", format_number(.price)); + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("You currently have: @@ GP and mobiliary credits", format_number(.@gp)); + next; + select + rif(.@gp > .price, l("Rent it! Make it mine!")), + l("Information"), + l("Don't rent it"); + + // You want to rent + if (@menu == 1) { + if ($ESTATE_RENTTIME[.id] > gettimetick(2)) { + mesc l("Somebody already rented it before you!"); + close; + } + REAL_ESTATE_CREDITS=REAL_ESTATE_CREDITS-.price; + if (REAL_ESTATE_CREDITS < 0) { + Zeny+=REAL_ESTATE_CREDITS; + REAL_ESTATE_CREDITS=0; + } + + // Payment done, you can now acquire the house for a month + $ESTATE_RENTTIME[.id]=gettimetick(2)+.time; + + // If you're not the previous owner + // Remove previous owner furniture and reset room password + if ($ESTATE_OWNER[.id] != getcharid(3)) { + /* + $ESTATE_MOBILIA_2[.id]=0; + $ESTATE_MOBILIA_4[.id]=0; + $ESTATE_MOBILIA_8[.id]=0; + $ESTATE_MOBILIA_32[.id]=0; + $ESTATE_MOBILIA_64[.id]=0; + $ESTATE_MOBILIA_128[.id]=0; + */ + $ESTATE_PASSWORD$[.id]=""; + $ESTATE_DOORBELL[.id]=false; + } + + // Register your info so you can manage it + $ESTATE_OWNER[.id]=getcharid(3); + $ESTATE_OWNERNAME$[.id]=strcharinfo(0); + + mesc l("Rent successful for 30 days!"); + } else if (@menu == 2) { + mesc l("You can rent this house to make it yours.") + " " + l("The rent lasts 30 days."); + mesc l("Then you'll be able to buy furniture and utility."); + mesc l("The door is password-protected, so your friends can enter but strangers stay outside."); + next; + mesc l("Both rent and furniture are bought using money, however, there are mobiliary credits."); + mesc l("Mobiliary Credits is a special currency which can only be used on real estate."); + mesc l("It's obtained with ADMINS or by selling furniture. It is sumed to money and used first."); + next; + } + } while (@menu == 2); + return true; +} + + + + + +// L_Manage Label +// Returns true regardless of player decision +// realestate_manage ( estate_id, price, <rent time> ) +function script realestate_manage { + .id=getarg(0); + .price=getarg(1); + .time=getarg(2, 2592000); // Defaults to 30 days + + do + { + mesc l("@@'s Estate", strcharinfo(0)); + mesc ".:: "+ l("Managment Menu") + " ::."; + + .@gp=REAL_ESTATE_CREDITS+Zeny; + mesc l("Rent time available: @@", FuzzyTime($ESTATE_RENTTIME[.id])); + mesc l("Total Credits and GP: @@", format_number(.@gp)); + mes ""; + mesc l("Rent Renew Price: @@ GP", format_number(.price)); + mesc l("Room password: @@", $ESTATE_PASSWORD$[.id]); + if ($ESTATE_DOORBELL[.id]) + mesc l("Doorbell is disabled"), 1; + + next; + select + l("Leave"), + l("Enable/disable doorbell"), + l("Set room password"), + rif(.@gp >= .price && $ESTATE_RENTTIME[.id] < gettimetick(2)+.time, l("Renew Rent")), + rif($@GM_OVERRIDE, l("Destroy all mobilia")), + rif($@GM_OVERRIDE, l("Expire rent time")); + + switch (@menu) { + case 1: + break; + case 2: + $ESTATE_DOORBELL[.id]=!$ESTATE_DOORBELL[.id]; + break; + case 3: + mesc l("(Leave the password blank to disable)"); + mesc l("Current Room password: @@", $ESTATE_PASSWORD$[.id]); + mesc l("Input new password: "); + input .@password$; + mesc l("Repeat new password: "); + input .@passwordc$; + if (.@password$ == .@passwordc$) { + $ESTATE_PASSWORD$[.id]=.@password$; + mesc l("Password changed with success!"), 3; + } else { + mesc l("The passwords doesn't match."), 1; + } + break; + case 4: + // The check is performed before showing the menu option + // I guess it could be hacked, but I'll probably see negative GP... + REAL_ESTATE_CREDITS=REAL_ESTATE_CREDITS-.price; + if (REAL_ESTATE_CREDITS < 0) { + Zeny+=REAL_ESTATE_CREDITS; + REAL_ESTATE_CREDITS=0; + } + + // Payment done, you can now acquire the house for a month + // If you lost the rent on the meanwhile, it'll renew + // If you lost the rent and somebody else rented it, you lose the GP + $ESTATE_RENTTIME[.id]+=.time; + break; + case 5: + mesc l("Are you sure? This cannot be undone!"), 1; + next; + if (validatepin()) { + $ESTATE_MOBILIA_2[.id]=0; + $ESTATE_MOBILIA_4[.id]=0; + $ESTATE_MOBILIA_8[.id]=0; + $ESTATE_MOBILIA_32[.id]=0; + $ESTATE_MOBILIA_64[.id]=0; + $ESTATE_MOBILIA_128[.id]=0; + $ESTATE_PASSWORD$[.id]=""; + $ESTATE_DOORBELL[.id]=false; + } + case 6: + mesc l("Are you sure? This cannot be undone!"), 1; + next; + if (validatepin()) { + $ESTATE_RENTTIME[.id]=gettimetick(2); + } + } + } while (@menu != 1); + return true; +} + + +// Piano Mobilia +// realestate_piano ( {mapname} ) +function script realestate_piano { + .@loc$=getarg(0, getmap()); + mesc l("Do you want to play a song?"); + mesc l("This is not saved."); + select + l("Nothing"), + l("Default"), + l("Indoors 1 (Peace)"), + l("Indoors 2 (Dimonds)"), + l("TMW Adventure"), + l("Sailing Away!"), + l("Magick Real"), + l("The Forest"), + l("Dragons and Toast"), + l("Unforgiving Lands"), + l("Arabesque (Action)"), + l("No Chains (Tulimshar)"), + l("School of Quirks (Candor)"), + l("Cake Town (Hurnscald)"), + l("Steam (LoF Village)"), + l("Woodland Fantasy"), + l("Birds in the Sunrise"); + + mes ""; + .@m$=""; + switch (@menu) { + case 1: + return; break; + case 2: + .@m$="8bit_the_hero.ogg"; break; + case 3: + .@m$="peace.ogg"; break; + case 4: + .@m$="peace2.ogg"; break; + case 5: + .@m$="tmw_adventure.ogg"; break; + case 6: + .@m$="sail_away.ogg"; break; + case 7: + .@m$="magick_real.ogg"; break; + case 8: + .@m$="dariunas_forest.ogg"; break; + case 9: + .@m$="dragon_and_toast.ogg"; break; + case 10: + .@m$="Unforgiving_Lands.ogg"; break; + case 11: + .@m$="Arabesque.ogg"; break; + case 12: + .@m$="mvrasseli_nochains.ogg"; break; + case 13: + .@m$="school_of_quirks.ogg"; break; + case 14: + .@m$="caketown.ogg"; break; + case 15: + .@m$="steam.ogg"; break; + case 16: + .@m$="woodland_fantasy.ogg"; break; + case 17: + .@m$="tws_birds_in_the_sunrise.ogg"; break; + + } + changemusic .@loc$, .@m$; + return; +} + + + +// Cauldron Mobilia +// realestate_cauldron ( ) +function script realestate_cauldron { + select + l("Alchemy"), + l("Crafting"); + mes ""; + if (@menu == 1) { + do { + mesc l("What will you brew today?"); + if (AlchemySystem(CRAFT_PLAYER)) + mesc l("Success!"), 3; + else + mesc l("That didn't work!"), 1; + next; + mesc l("Try again?"); + } while (askyesno() == ASK_YES); + } + else if (@menu == 2) { + do { + mesc l("What will you craft today?"); + if (SmithSystem(CRAFT_PLAYER)) + mesc l("Success!"), 3; + else + mesc l("That didn't work!"), 1; + next; + mesc l("Try again?"); + } while (askyesno() == ASK_YES); + } + return; +} + +//////////////////////////////////////////////////////////////////////////// +// TODO: NPCs toogle on/off controller? + +//////////////////////////////////////////////////////////////////////////// +012-8,21,23,0 script Wardrobe#RES_0128 NPC_NO_SPRITE,{ + openstorage; + end; + +OnInit: + .distance=3; + end; +} + +017-7,21,23,0 duplicate(Wardrobe#RES_0128) Wardrobe#RES_0177 NPC_NO_SPRITE +017-8,21,23,0 duplicate(Wardrobe#RES_0128) Wardrobe#RES_0178 NPC_NO_SPRITE +009-6,21,23,0 duplicate(Wardrobe#RES_0128) Wardrobe#RES_0096 NPC_NO_SPRITE +009-7,21,23,0 duplicate(Wardrobe#RES_0128) Wardrobe#RES_0097 NPC_NO_SPRITE + + +//////////////////////////////////////////////////////////////////////////// +012-8,29,24,0 script Cauldron#RES_0128 NPC_NO_SPRITE,{ + realestate_cauldron(); + close; + +OnInit: + .distance=3; + end; +} + +017-7,29,24,0 duplicate(Cauldron#RES_0128) Cauldron#RES_0177 NPC_NO_SPRITE +017-8,29,24,0 duplicate(Cauldron#RES_0128) Cauldron#RES_0178 NPC_NO_SPRITE +009-6,29,24,0 duplicate(Cauldron#RES_0128) Stovetop#RES_0096 NPC_NO_SPRITE +009-7,29,24,0 duplicate(Cauldron#RES_0128) Stovetop#RES_0097 NPC_NO_SPRITE + + +//////////////////////////////////////////////////////////////////////////// +012-8,34,25,0 script Piano#RES_0128 NPC_NO_SPRITE,{ + realestate_piano(); + close; + +OnInit: + .distance=3; + end; +} + +017-7,34,25,0 duplicate(Piano#RES_0128) Piano#RES_0177 NPC_NO_SPRITE +017-8,34,25,0 duplicate(Piano#RES_0128) Piano#RES_0178 NPC_NO_SPRITE +009-6,34,25,0 duplicate(Piano#RES_0128) Piano#RES_0096 NPC_NO_SPRITE +009-7,34,25,0 duplicate(Piano#RES_0128) Piano#RES_0097 NPC_NO_SPRITE + + diff --git a/npc/functions/event.txt b/npc/functions/event.txt new file mode 100644 index 0000000..ecb0f81 --- /dev/null +++ b/npc/functions/event.txt @@ -0,0 +1,834 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Controls world events so Saulc and I can take vacations. +// See also: seasons.txt, command/event.txt, aurora.txt +// 003-1/events.txt, soulmenhir.txt and, of course, the event maps (if any). +// Schedule, if needed to change, can be found in the OnInit loop. +// An event, obviously, cannot last more than 7... 15 days at WORST. +// Therefore, month checks aren't included in day checks. + +- script sPatrick NPC_HIDDEN,{ +OnInit: + setarray .maps$, + "005-1", + "013-1", + "014-1", + "014-2", + "014-3", + "014-4", + "014-5", + "017-1", + "018-2", + "018-4", + "018-5", + "soren"; + end; +OnMyMobDeath: + end; +OnClock0000: +OnClock6000: +OnClock1200: +OnClock1500: +OnClock1800: +OnClock2100: + .@d=gettime(GETTIME_DAYOFMONTH); + // Patrick Day should work only in 2 luck days according to Saulc + // If this is required use $PATRICK_DAYCTRL so the days before $@PATRICK_DAYMAX + // act with 100% chances (determinism). + // As it is being spawn 4 times at a day (like TMW-BR events), because it is + // flatly and outright IMPOSSIBLE to add a permanent spawn, that is not required. + if ($EVENT$ == "Patrick" && .@d <= $@PATRICK_DAYMAX) { + for (.@i=0; .@i < getarraysize(.maps$); .@i++) { + .@m$=.maps$[.@i]; + .@x=getmapinfo(MAPINFO_SIZE_X, .@m$)-20; + .@y=getmapinfo(MAPINFO_SIZE_Y, .@m$)-20; + + // Remove previously spawned clovers + killmonster(.@m$, "sPatrick::OnMyMobDeath"); + // It is one clover for each 225 tiles (about a 25x25 square) + .@maparea=(.@x-20)*(.@y-20); + .@mobs=max(1, .@maparea/225); + areamonster .@m$, 20, 20, .@x, .@y, strmobinfo(1, StPatricksClover), StPatricksClover, .@mobs, "sPatrick::OnMyMobDeath"; + } + } + end; + +// Remove previously spawned clovers for event end +// And then disables the NPC +OnCleanUp: + for (.@i=0; .@i < getarraysize(.maps$); .@i++) { + .@m$=.maps$[.@i]; + killmonster(.@m$, "sPatrick::OnMyMobDeath"); + } + disablenpc "sPatrick"; + donpcevent "Aurora::OnRestore"; + end; +} + +- script sDreamTower NPC_HIDDEN,{ +OnWarpTo: + if ($EVENT$ != "Tower") end; + if (!playerattached()) end; + if (!countitem(EventDreamTicket)) end; + + // Reset progress + if (DTOWER_DAY != gettimeparam(GETTIME_DAYOFMONTH) || DTOWER_FLOOR < 1) { + DTOWER_DAY=gettimeparam(GETTIME_DAYOFMONTH); + DTOWER_FLOOR=1; + dispbottom l("A new Dream Tower opens its gates before you..."); + } + + // Create maze and populate + CreateMaze(IOT_CHAR, MAZE_SIZE_S | MAZE_SIZE_M); + MazeMobs(((BaseLevel/10) + DTOWER_FLOOR * 5), true, rand2(9,14)); + + // Spawn & Configure the boss monster + .@mx=getmapinfo(MAPINFO_SIZE_X, MAZE_MAP$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, MAZE_MAP$)-20; + .@mob=areamonster(MAZE_MAP$, 20, 20, .@mx, .@my, "Gatekeeper", MonsterSergeant, 1, "sDreamTower::OnBossDie"); + setunitdata(.@mob, UDT_LEVEL, DTOWER_FLOOR * 5); + setunitdata(.@mob, UDT_STR, DTOWER_FLOOR * 2); + setunitdata(.@mob, UDT_AGI, DTOWER_FLOOR * 2); + setunitdata(.@mob, UDT_VIT, DTOWER_FLOOR * 3); + setunitdata(.@mob, UDT_INT, DTOWER_FLOOR * 1); + setunitdata(.@mob, UDT_DEX, DTOWER_FLOOR * 3); + setunitdata(.@mob, UDT_LUK, DTOWER_FLOOR * 2); + setunitdata(.@mob, UDT_ADELAY, max(640, 1672-(DTOWER_FLOOR * 24))); + setunitdata(.@mob, UDT_MAXHP, 2000+DTOWER_FLOOR*120); + setunitdata(.@mob, UDT_HP, 2000+DTOWER_FLOOR*120); + setunitdata(.@mob, UDT_ATKMIN, 20+DTOWER_FLOOR*4); + setunitdata(.@mob, UDT_ATKMAX, 40+DTOWER_FLOOR*4); + setunitdata(.@mob, UDT_DEF, 10+DTOWER_FLOOR*3); + setunitdata(.@mob, UDT_MDEF, 5+DTOWER_FLOOR); + setunitdata(.@mob, UDT_HIT, (BaseLevel+DTOWER_FLOOR)*32/10); + setunitdata(.@mob, UDT_FLEE, (BaseLevel+DTOWER_FLOOR)*24/10); + setunitdata(.@mob, UDT_CRIT, rand2(40, min(120, 40+DTOWER_FLOOR))); + // TODO: Spawn more treasure chests? (exp, gp, items) + + // Consume the ticket and begin the maze + delitem EventDreamTicket, 1; + InitMaze(3600); + dispbottom col(l("Reminder : Defeat the %s to win.", b("Gatekeeper")), 2); + dispbottom l("Mana Plane - Dream Tower, %dº floor. Time limit: 60 minutes or death.", DTOWER_FLOOR); + end; + +// Dream Tower cleared +OnBossDie: + dispbottom l("Mana Plane - Dream Tower, %dº floor %s", DTOWER_FLOOR, b(l("CLEAR!"))); + DTOWER_FLOOR+=1; + if ($EVENT$ == "Tower") { + if (checkweight(BrokenMedal, 1)) { + getitem BrokenMedal, 1; + } else { + // FIXME: Can be kept for next event, a deletion is required! + rodex_sendmail(getcharid(0), $DREAMTOWER_SAGE$, "You are fat.", "Here is the medal, anyway.", 0, BrokenMedal, 1); + } + } + unitskilluseid(getcharid(3), BS_GREED, 1, getcharid(3)); + sleep2(500); + teleporthome(); + end; +} + +- script sThankAeros NPC_HIDDEN,{ +OnInit: + .users=getusers(1); + .score=0; + .event=0; + .quota=300; +OnTimer90000: + // Abort event and reset score + if (.event && .event < gettimetick(2)) { + .event=0; + .score=0; + $@GM_EVENT=0; + $@MK_SCENE=MK_NONE; + killmonsterall("001-1"); + mapwarp("001-1", "000-1", 22, 22, 0); + announce ("The event is over!"), bc_all|bc_npc; + } + + // Skip processment until current event ends + if (.event) { + initnpctimer; + end; + } + + // Stop counting or update scores + if ($EVENT$ != "Thanksgiving") + end; + .users=getusers(1); + .score+=.users; + .quota = limit(300, 500 - (.users * 25), 500); + + // Do event (lasts 45 minutes) + if (!.event && !$@MK_SCENE && !$@GM_EVENT && .score > .quota && (.users >= 4 || $@GM_OVERRIDE)) { + /* Reserve Aeros */ + $@MK_SCENE=MK_LOCKED; + /* Setup the event */ + $@AEROS_SPWN=(.users > 6 ? 2 : any(0, 1)); + $@AEROS_AUTOSPAWN=.users+rand2(1, .users); + donpcevent("Mana Being#001-1::OnAutoSched"); + /* Open the event */ + $@GM_EVENT = 2; + .event = gettimetick(2) + 2700; + announce ("The mana bridge to Aeros is open! To participate on event, talk to ##BSoul Menhir##b!"), bc_all|bc_npc; + announce ("##1THANKSGIVING SPECIAL. TIME LIMIT: ##B45 MINUTES.##b##0"), bc_all|bc_npc; + channelmes("#world", "An event is happening on Aeros! Hurry up!"); + } + + initnpctimer; + end; + +OnForceInit: + .score+=9999; + goto OnTimer90000; +} + +function script sThanksgiving { + // Variables: + // #THANKS_DAY + // #THANKS_STREAK + // @thanks_card + + // Already spinned + if (#THANKS_DAY == gettime(5) || $EVENT$ != "Thanksgiving") + return; + + // Not spinned yet + showavatar 1102; + mes l(".:: Thanksgiving ::."); + mesc l("Spin daily the card to get prizes!"); + + select + l("SPIN!"), + l("Later"); + mes ""; + if (@menu == 2) { + setskin ""; + closeclientdialog; + return; + } + // Spin it + // Your prize is saved in .@luck. Rigged against fruits + .@luck=rand2(0,11); + .@luck=(.@luck == 10 ? rand2(0,11) : .@luck); + // A fruit before 10 days: rig to Arcmage or to blueprint + if (.@luck == 10 && #THANKS_STREAK < 10) + .@luck+=any(-1,1); + // A fruit before 20 days: Lower chances from 4% to 2% + if (.@luck == 10 && #THANKS_STREAK < 20) + .@luck=rand2(0,11); + // Extra spins + .@extra=rand2(1,3); + .@spins=.@extra*11+.@luck; + //.@extra=0; + + // Show the spins + for (.@i=0;.@i<=.@spins;.@i++) { + .@cur=(.@i%11); + showavatar 1103+.@cur; + sleep2(60); + } + mes l(".:: Congratulations ::."); + #THANKS_DAY = gettime(5); + #THANKS_STREAK+=1; + if (debug || $@GM_OVERRIDE) { + mes l("You got a %d", .@luck); + mesf "Spins: %d/%d (cur %d extra %d)", .@i, .@spins, .@cur, .@extra; + mes ""; + //.@luck=$@GM_OVERRIDE; + } + + // Switch the reward + .@blv = BaseLevel + TOP3AVERAGELVL() / 2; + switch (.@luck) { + case 0: // EXP + .@p=.@blv*#THANKS_STREAK; + .@p*=3; + getexp .@p, 0; + mes l("You got %d EXP!", .@p); + break; + case 1: // JEXP + .@p=.@blv*#THANKS_STREAK; + .@p=.@p*15/10; + getexp 0, .@p; + mes l("You got %d JEXP!", .@p); + break; + case 2: // GP + .@p=.@blv*#THANKS_STREAK; + Zeny+=.@p; + mes l("You got %d GP!", .@p); + break; + case 3: // MOBPT + .@p=.@blv*#THANKS_STREAK; + Mobpt+=.@p; + mes l("You got %d Monster Points!", .@p); + break; + case 4: // RES + .@p=.@blv*#THANKS_STREAK; + .@p*=3; + REAL_ESTATE_CREDITS+=.@p; + mes l("You got %d Real Estate Credits!", .@p); + break; + case 5: // GIFT + .@p=BronzeGift; + .@p=(#THANKS_STREAK > 7 ? SilverGift : .@p); + .@p=(#THANKS_STREAK > 15 ? GoldenGift : .@p); + .@p=(#THANKS_STREAK > 22 ? PrismGift : .@p); + .@p=(#THANKS_STREAK > 27 ? SupremeGift : .@p); + mes l("You got a(n) %s gift!", getitemlink(.@p)); + getitem .@p, 1; + break; + case 6: // STR C + .@p=#THANKS_STREAK; + getitem StrangeCoin, .@p; + mes l("You got %d %s!", .@p, getitemlink(StrangeCoin)); + break; + case 7: // HERO C + .@p=#THANKS_STREAK; + .@p+=getq2(LoFQuest_HH)*max(1,#THANKS_STREAK/10); + getitem HeroCoin, .@p; + mes l("You got %d %s!", .@p, getitemlink(HeroCoin)); + break; + case 8: // MERC + .@p=any(MercCard_GonzoDark, MercCard_Rosa, MercCard_Soren, MercCard_Apane); + .@p=(#THANKS_STREAK > 7 ? any(MercCard_EarthWitch, MercCard_Demure, MercCard_Jesusalva, MercCard_Pookie) : .@p); + .@p=(#THANKS_STREAK > 14 ? any(MercCard_Arthur, MercCard_LawnCable, MercCard_Crazyfefe, MercCard_Saulc) : .@p); + .@p=(#THANKS_STREAK > 21 ? any(MercCard_Swezanne, MercCard_DragonStar, MercCard_Msawis, MercCard_Aisen) : .@p); + .@p=(#THANKS_STREAK > 27 ? any(MercCard_Xanthem, MercCard_Woody, MercCard_Lilanna) : .@p); + getitem .@p, 1; + mes l("You got a %s!", getitemlink(.@p)); + break; + case 9: // CARD + .@p=any(NatureCard, NinjaCard, MageCard, DruidCard, ClericCard, KnightCard, HeroCard, NecromancerCard); + .@p=(#THANKS_STREAK > 24 ? any(SpeedCard, ReflectCard, PowerCard, WallCard) : .@p); + getitem .@p, 1; + mes l("You got a %s!", getitemlink(.@p)); + break; + case 10: // FRUIT + .@p=MysteriousFruit; + getitem .@p, 1; + mes l("You got a %s!", getitemlink(.@p)); + break; + case 11: // BLUE + .@p=any(EquipmentBlueprintA, EquipmentBlueprintA, AlchemyBlueprintA); + .@p=(#THANKS_STREAK > 7 ? any(EquipmentBlueprintB, EquipmentBlueprintB, AlchemyBlueprintB) : .@p); + .@p=(#THANKS_STREAK > 14 ? any(EquipmentBlueprintC, EquipmentBlueprintC, AlchemyBlueprintC) : .@p); + .@p=(#THANKS_STREAK > 21 ? any(EquipmentBlueprintD, EquipmentBlueprintD, AlchemyBlueprintD) : .@p); + .@p=(#THANKS_STREAK > 27 ? any(EquipmentBlueprintE, EquipmentBlueprintE, AlchemyBlueprintE) : .@p); + getitem .@p, 1; + mes l("You got a %s!", getitemlink(.@p)); + break; + } + + // Special bonus + if (#THANKS_STREAK % 7 == 0) { + getitem StrangeCoin, 10; + mesc l("%d day streak! Gained %d bonus %s!", #THANKS_STREAK, 10, getitemlink(StrangeCoin)); + } + + // Close + next; + setskin ""; + closeclientdialog; + return; +} + +// ChocolateDay() +function script ChocolateDay { + if (!playerattached()) + return; + + // Date check + if (gettime(6) != JULY) + return; + if (gettime(5) != 7) + return; + + // Max. 80% chances, 0.4% per monster level + .@lv=min(200, getmonsterinfo(killedrid, MOB_LV)); + if (rand2(10000) < (.@lv*40)) { + // Chocolate Bar: 73%; Chocolate Dye: 6.7% + .@choco=any(ChocolateBar, ChocolateBar, ChocolateBar, ChocolateBar, + ChocolateMouboo, ChocolateBiscuit, ChocolateBunny, + ChocolateBar, ChocolateBar, ChocolateBar, ChocolateBar, + ChocolateDye, ChocolateBar, ChocolateBar, ChocolateBar); + getmapxy(.@m$, .@x, .@y, 0); + .@x+=rand2(-1,1); + .@y+=rand2(-1,1); + makeitem(.@choco, 1, .@m$, .@x, .@y); + } + return; +} + +// CoffeeDay() +function script CoffeeDay { + if (!playerattached()) + return; + + // Date check + if (gettime(6) != OCTOBER) + return; + if (gettime(5) != 1) + return; + + // Max. 22% chances, 0.1% per monster level + .@lv=min(200, getmonsterinfo(killedrid, MOB_LV)); + if (rand2(10000) < (.@lv*11)) { + getmapxy(.@m$, .@x, .@y, 0); + .@x+=rand2(-1,1); + .@y+=rand2(-1,1); + makeitem(Coffee, 1, .@m$, .@x, .@y); + } + return; +} + +// FSFDay() +function script FSFDay { + if (!playerattached()) + return; + + // Date check + if (gettime(6) != SEPTEMBER) + return; + if (gettime(5) != 9) + return; + + // Max. 0.16% chances, 0.008% per monster level + .@lv=min(200, getmonsterinfo(killedrid, MOB_LV)); + if (rand(1000000) < (.@lv*8)) { + getmapxy(.@m$, .@x, .@y, 0); + .@x+=rand2(-1,1); + .@y+=rand2(-1,1); + // Ancient 50%, Equipment 25%, Alchemy 25% + .@bp = any(AncientBlueprint, AncientBlueprint, AncientBlueprint, AncientBlueprint, AncientBlueprint, + AncientBlueprint, AncientBlueprint, AncientBlueprint, AncientBlueprint, AncientBlueprint, + EquipmentBlueprintA, EquipmentBlueprintB, EquipmentBlueprintC, EquipmentBlueprintD, EquipmentBlueprintE, + AlchemyBlueprintA, AlchemyBlueprintB, AlchemyBlueprintC, AlchemyBlueprintD, AlchemyBlueprintE); + makeitem(.@bp, 1, .@m$, .@x, .@y); + } + return; +} + +// CraftmasterDay() +function script CraftmasterDay { + if (!playerattached()) + return; + + // Date check + if (!$BETASERVER) + return; + if (gettime(7) != 2021) + return; + if (gettime(6) != JUNE) + return; + if (gettime(5) != 21) + return; + if (getmap() != "018-3") + return; + + // Scored a Blue Print (2.5%) + if (rand2(10000) < 250) { + if (BaseLevel > 80 && REBIRTH) + getitem any(EquipmentBlueprintE, AlchemyBlueprintE, + EquipmentBlueprintD, AlchemyBlueprintD, + EquipmentBlueprintC, AlchemyBlueprintC, + EquipmentBlueprintB, AlchemyBlueprintB, + EquipmentBlueprintA, AlchemyBlueprintA), 1; + else if (BaseLevel > 70) + getitem any(EquipmentBlueprintD, AlchemyBlueprintD, + EquipmentBlueprintC, AlchemyBlueprintC, + EquipmentBlueprintB, AlchemyBlueprintB, + EquipmentBlueprintA, AlchemyBlueprintA), 1; + else if (BaseLevel > 45) + getitem any(EquipmentBlueprintC, AlchemyBlueprintC, + EquipmentBlueprintB, AlchemyBlueprintB, + EquipmentBlueprintA, AlchemyBlueprintA), 1; + else if (BaseLevel > 20) + getitem any(EquipmentBlueprintB, AlchemyBlueprintB, + EquipmentBlueprintA, AlchemyBlueprintA), 1; + else + getitem any(EquipmentBlueprintA, AlchemyBlueprintA), 1; + return; + } + + // Scored Monster Points (9%) + if (rand2(10000) < 900) { + .@mpt=rand2(BaseLevel*10, BaseLevel*100); // 600~6000 usually + Mobpt+=.@mpt; + dispbottom l("Acquired: %s Monster Points", fnum(.@mpt)); + return; + } + + // Autocraft (1%) + if (rand2(10000) < 100) { + .@rc=any(rand2(51, 62), 65, 66, rand2(72, 76), rand2(77, 81), 82, 98, + 99, 100, rand2(93, 97), rand2(88, 91), rand(83, 87), 92, 101, + rand2(102,117), any(122, 112), rand2(125, 134), rand2(135, 143)); + // Retrieve ID + .@it=getcraftcode(.@rc); + if (.@it <= 0) + return; + // Mark the crafting in your score variable + CRAFTING_SCORE_COMPLETE+=getiteminfo(.@it, ITEMINFO_ELV); + // Update your score book + CRAFTING_SCORE=(CRAFTING_SCORE_COMPLETE/40); + // Receive item + getitembound(.@it, 1, 1); + // Apply options if possible + if (getskilllv(TMW2_CRAFT)) { + delinventorylist(); // Needed, because we'll rely on rfind() + getinventorylist(); + .@index=array_rfind(@inventorylist_id, .@it); + + // Just to be sure, if this have an option, get something else + if (getitemoptionparambyindex(.@index, 0)) { + .@index=array_find(@inventorylist_id, .@it); + } + + callfunc("csys_Apply", .@index); + } + dispbottom l("Successfully Crafted: %s", getitemname(.@it)); + return; + } + return; +} + + + + + + + + + + + + + + + +000-0,0,0,0 script #EventCore NPC_HIDDEN,{ + end; + +// confEvent(event ID, stday, stmon, endday, endmon) +function confEvent { + setd("."+getarg(0)+"_stday", getarg(1)); + setd("."+getarg(0)+"_stmon", getarg(2)); + setd("."+getarg(0)+"_endday", getarg(3)); + setd("."+getarg(0)+"_endmon", getarg(4)); + // Save the event key? + //.@confname=".conf_"+getarg(2)+"$"; + //setd(.@confname+"["+getarraysize(.@confname)+"]", getarg(0)); + // Save only the day? (And use if statement) + .@confname$=".conf_"+getarg(2); + .@size=getarraysize(getd(.@confname$)); + setd(.@confname$+"["+.@size+"]", getarg(1)); + //debugmes "CONFIGURE EVENT, %s[%d] = %d", .@confname$, .@size, getarg(1); + //debugmes "CNAME: %s", getarg(0); + return; +} + +// Event Schedule (all dates are inclusive) +// NEVER, ever, include the last day of the month. +OnInit: + confEvent("valentine", + 13, FEBRUARY, + 16, FEBRUARY); + + confEvent("tmw2day", + 1, MARCH, + 7, MARCH); + + confEvent("patrick", + 15, MARCH, + 19, MARCH); + + confEvent("easter", + 17, APRIL, + 24, APRIL); + + confEvent("worker", + 27, APRIL, + 3, MAY); + + confEvent("thanks", + 1, NOVEMBER, + 29, NOVEMBER); // NEVER use the last day of the month here + + confEvent("xmas", + 19, DECEMBER, + 2, JANUARY); + + // Some setup + .@isinit=true; + disablenpc "sPatrick"; + FYE_Normalize(); + +// Check for events every midnight +OnClock0000: + // No events on test server + if (debug && !$@GM_OVERRIDE) + end; + + // Prepare variables + .@d=gettime(GETTIME_DAYOFMONTH); + .@m=gettime(GETTIME_MONTH); + .@o=gettime(GETTIME_WEEKDAY); // Resets at 0 (sunday) + debugmes "A new day begins! Today is %02d/%02d", .@d, .@m; + + // Override the server-wide randomness + resetrng(); + + // Initialize the event core + if ($@DEBUG_OD) + .@d=$@DEBUG_OD; + if ($@DEBUG_OM) + .@m=$@DEBUG_OM; + consoleinfo "EVENT CORE, the %02d/%02d", .@d, .@m; + + // Is there another event this week? + if (!$HARDCORE && .@o == MONDAY && !.@isinit) { + .@dofy=true; + .@confname$=".conf_"+.@m; + + // NOTE: Maybe make a single .annuals array and cycle it + // Gather the ST variable from interest...? + for (.@i=0;.@i < getarraysize(getd(.@confname$)); .@i++) { + .@val=getd(.@confname$+"["+.@i+"]"); + //debugmes "Looping, %d = %d", .@i, .@val; + + // Erm. + if (.@val == 0) + continue; + + // This script has a weakness: It cannot predict events starting + // at the start or the end of the month. + // So they might still overlap. + // A possible solution is to, eventually, record start and end date. + // And only inhibit FY event if the MONDAY is within the event...? + // XXX: Now it inhibits by itself; But it should NOT begin a FYE if + // another (annual) event is scheduled for the same week. + // Namely: TMW2 Day and Thanksgiving + debugmes "%d >= %d && %d <= %d", .@val, .@d-6, .@val, .@d+6; + // In this case there's already an event! + // .@d == .@val <= .@d+6 AND + if ( .@val >= .@d-6 && .@val <= .@d+6 ) { + .@dofy=false; + break; + } + // continue... + } + + // Stop previous event + FYStopEvent(); + + // Trigger event framework + if (.@dofy && $EVENT$ == "") + FYNewEvent(); + else + debugmes "\033[1mAnother event is scheduled!"; + } + + // Handle Valentine Day + if (.@m >=.valentine_stmon && .@m <= .valentine_endmon) { + // It's tomorrow: Send out an announcement + if (.@d == .valentine_stday - 1) { + kamibroadcast("Valentine Day will start tomorrow!"); + } + // Last Day: Send out an announcement + if (.@d == .valentine_endday) { + kamibroadcast("It's the last day for Valentine Event! Hurry up!"); + } + // Begin the event + if (.@d == .valentine_stday) { + kamibroadcast("Valentine Day begun!"); + if ($EVENT$ != "") + FYStopEvent(); + $EVENT$="Valentine"; + $@VALENTINE_LOVELETTER = htnew(); + $@VALENTINE_GIFTSTACKS = htnew(); + } + // End the event the day after + if (.@d == .valentine_endday+1) { + kamibroadcast("Valentine Day ended!"); + sClear(); + $EVENT$=""; + } + } + + + + // Handle TMW2 Day + if (.@m >=.tmw2day_stmon && .@m <= .tmw2day_endmon) { + // It's tomorrow: Send out an announcement + if (.@d == .tmw2day_stday - 1) { + kamibroadcast("Moubootaur Legends Anniversary will start tomorrow!"); + } + // Last Day: Send out an announcement + if (.@d == .tmw2day_endday) { + kamibroadcast("It's the last day of our Anniversary Event! Hurry up!"); + } + // Begin the event + if (.@d == .tmw2day_stday) { + kamibroadcast("Moubootaur Legends Anniversary Event begun!"); + if ($EVENT$ != "Anniversary") + $BCONFB_EXPR+=50; + $EVENT$="Anniversary"; + } + // End the event the day after + if (.@d == .tmw2day_endday+1) { + kamibroadcast("Our Special Anniversary Event ended! Thanks!"); + sClear(); + $EVENT$=""; + } + } + + + // Handle St. Patrick Day + // During this day, a special four leaf clover is spawned daily, at 4 times in the day + // in all forests: 6:00, 12:00, 18:00 and 00:00. + // $PATRICK_DAYMAX + if (.@m >=.patrick_stmon && .@m <= .patrick_endmon) { + // It's tomorrow: Send out an announcement + if (.@d == .patrick_stday - 1) { + kamibroadcast("St. Patrick Day will start tomorrow!"); + } + // Last Day: Send out an announcement + else if (.@d == .patrick_endday) { + kamibroadcast("It's the last day for St. Patrick Event! Hurry up!"); + } + // Begin the event + else if (.@d == .patrick_stday) { + kamibroadcast("St. Patrick Day begun!"); + if ($EVENT$ != "") + FYStopEvent(); + $EVENT$="Patrick"; + $@PATRICK_DAYMAX=.patrick_endday; + enablenpc "sPatrick"; + setnpcdisplay("Aurora", "Patrick Saulc", NPC_WEIRDGREEN); + enablenpc "St. Patrick Gold Pot"; + donpcevent "St. Patrick Gold Pot::OnForcedMove"; + } + // End the event the day after + else if (.@d == .patrick_endday+1) { + kamibroadcast("St. Patrick Day ended!"); + sClear(); + $EVENT$=""; + donpcevent "sPatrick::OnCleanUp"; + } + } + + + // Handle Easter + if (.@m >=.easter_stmon && .@m <= .easter_endmon) { + // It's tomorrow: Send out an announcement + if (.@d == .easter_stday - 1 && .@m == .easter_stmon) { + kamibroadcast("Easter will start tomorrow!"); + } + // Last Day: Send out an announcement + else if (.@d == .easter_endday && .@m == .easter_endmon) { + kamibroadcast("It's the last day for Easter Event! Hurry up!"); + } + // Begin the event + else if (.@d == .easter_stday && .@m == .easter_stmon) { + kamibroadcast("Easter begun!"); + if ($EVENT$ != "") + FYStopEvent(); + sEaster(); + } + // End the event the day after + else if (.@d == .easter_endday+1 && .@m == .easter_endmon) { + kamibroadcast("Easter ended!"); + sClear(); + $EVENT$=""; + } + } + + + // Handle Worker Day + if (.@m >=.worker_stmon && .@m <= .worker_endmon) { + // It's tomorrow: Send out an announcement + if (.@d == .worker_stday - 1 && .@m == .worker_stmon) { + kamibroadcast("Worker Day will start tomorrow!"); + } + // Last Day: Send out an announcement + else if (.@d == .worker_endday && .@m == .worker_endmon) { + kamibroadcast("It's the last day for Worker Day Event! Hurry up!"); + } + // Begin the event + else if (.@d == .worker_stday && .@m == .worker_stmon) { + kamibroadcast("Worker Day begun!"); + if ($EVENT$ != "") + FYStopEvent(); + $EVENT$="Worker"; + } + // End the event the day after + else if (.@d == .worker_endday+1 && .@m == .worker_endmon) { + kamibroadcast("Worker Day ended!"); + sClear(); + $EVENT$=""; + } + } + + + // Handle Thanksgiving Day + if (.@m >=.thanks_stmon && .@m <= .thanks_endmon) { + // It's tomorrow: Send out an announcement + if (.@d == .thanks_stday - 1 && .@m == .thanks_stmon) { + kamibroadcast("Thanksgiving will start tomorrow!"); + } + // Last Day: Send out an announcement + else if (.@d == .thanks_endday && .@m == .thanks_endmon) { + kamibroadcast("It's the last day for Thanksgiving Event! Hurry up!"); + } + // Begin the event + else if (.@d == .thanks_stday && .@m == .thanks_stmon) { + kamibroadcast("Thanksgiving begun!"); + if ($EVENT$ != "") + FYStopEvent(); + $EVENT$="Thanksgiving"; + donpcevent("sThankAeros::OnTimer90000"); + } + // End the event the day after + else if (.@d == .thanks_endday+1 && .@m == .thanks_endmon) { + kamibroadcast("Thanksgiving ended!"); + sClear(); + $EVENT$=""; + } + } + + + // Handle Christmas. It goes past the year so the rule is different, BEWARE + if (.@m == .xmas_stmon || .@m == .xmas_endmon) { + // It's tomorrow: Send out an announcement + if (.@d == .xmas_stday - 1 && .@m == .xmas_stmon) { + kamibroadcast("Christmas will start tomorrow!"); + } + // Last Day: Send out an announcement + else if (.@d == .xmas_endday && .@m == .xmas_endmon) { + kamibroadcast("It's the last day for Christmas Event! Hurry up!"); + } + // Begin the event + else if (.@d == .xmas_stday && .@m == .xmas_stmon) { + kamibroadcast("Christmas begun!"); + if ($EVENT$ != "") + FYStopEvent(); + $EVENT$="Christmas"; + DelQuestFromEveryPlayer(SQuest_Christmas); + sChristmas(); + } + // End the event the day after + else if (.@d == .xmas_endday+1 && .@m == .xmas_endmon) { + kamibroadcast("Christmas ended!"); + sClear(); + $EVENT$=""; + } + } + debugmes "EVENT CORE has finished"; + end; +} + diff --git a/npc/functions/filters.txt b/npc/functions/filters.txt new file mode 100644 index 0000000..bb4d8d6 --- /dev/null +++ b/npc/functions/filters.txt @@ -0,0 +1,131 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Several filters + +// filter_always( id ) +function script filter_always { + return true; +} + +// filter_onlyme( id ) +function script filter_onlyme { + return (getarg(0) == getcharid(3)); +} + +// filter_notme( id ) +function script filter_notme { + return (getarg(0) != getcharid(3)); +} + +// filter_sameguild( id ) +function script filter_sameguild { + if (getcharid(2) < 1) + return false; + return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2)); +} + +// filter_sameguildnotyou( id ) +function script filter_sameguildnotyou { + if (getcharid(2) < 1) + return false; + if (getarg(0) == getcharid(3)) + return false; + return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2)); +} + +// filter_sameparty( id ) +function script filter_sameparty { + if (getcharid(1) < 1 && getarg(0) != getcharid(3)) + return false; + return (strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1)); +} + +// filter_sameguildorparty( id ) +function script filter_sameguildorparty { + if (getcharid(2) < 1 && getcharid(1) < 1) + return false; + .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1)); + .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2)); + return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild)); +} + +// filter_sameguildorpartynotyou( id ) +function script filter_sameguildorpartynotyou { + if (getarg(0) == getcharid(3)) + return false; + if (getcharid(2) < 1 && getcharid(1) < 1) + return false; + .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1)); + .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2)); + return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild)); +} + +// filter_hostile( id ) +function script filter_hostile { + //.@type=getunitdata(getarg(0), UDT_TYPE); + .@type=getunittype(getarg(0)); + .@chkid=getarg(0); + + // Players outside PVP + if (.@type == UNITTYPE_PC) { + getmapxy(.@m$, .@x, .@y, .@type, .@chkid); + if (!ispvpmap(.@m$)) + return false; + // FIXME: We already have !(filter_sameguildorparty()) + // We might be over-processing this + // Honor party flag + if (!getmapflag(.@mapa$, mf_pvp_noparty) && + getcharid(1) == getcharid(1, strcharinfo(0, "", .@chkid))) + return false; + // Honor guild flag + if (!getmapflag(.@mapa$, mf_pvp_noguild) && + getcharid(2) == getcharid(2, strcharinfo(0, "", .@chkid))) + return false; + } + + // Monsters + if (.@type == UNITTYPE_MOB) + return true; + + // NPCs + if (.@type == UNITTYPE_NPC) + return false; + + // Homunculus + if (.@type == UNITTYPE_HOM) + .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID)); + + // Pets + if (.@type == UNITTYPE_PET) + .@chkid=getunitdata(getarg(0), UDT_MASTERAID); + + // Mercenaries + if (.@type == UNITTYPE_MER) + .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID)); + + // Elementals + if (.@type == UNITTYPE_ELEM) + .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID)); + + //debugmes "filter_hostile: Filtering %d (original %d) (BL %d)", .@chkid, getarg(0), .@type; + // Players (and slaves) + return !(filter_sameguildorparty(.@chkid)); +} + +// filter_friendly( id ) +function script filter_friendly { + return !(filter_hostile(getarg(0))); +} + +// filter_friendlynotme( id ) +function script filter_friendlynotme { + return (getarg(0) != getcharid(3) && filter_friendly(getarg(0))); +} + +// filter_notboss( id ) +function script filter_notboss { + return (!(getunitdata(getarg(0), UDT_MODE) & MD_BOSS)); +} + diff --git a/npc/functions/fishing.txt b/npc/functions/fishing.txt new file mode 100644 index 0000000..103784c --- /dev/null +++ b/npc/functions/fishing.txt @@ -0,0 +1,358 @@ +// TMW2 Script +// Evol functions. +// Authors: +// gumi +// omatt +// Travolta +// Jesusalva +// Description: +// Fishing functions. +// Variable +// .dir +// DOWN Never try or pulled too late +// UP Bait dropped +// LEFT Fish bite bait +// +// player log on .dir is DOWN, start by choose bait menu +// player chooses bait, script addtimer in npc .dir is UP +// if player pulls before signal npc, bait is lost, set .bait to DOWN goto choose bait +// if player pulls after pull delay max, bait is lost, set .bait to DOWN goto choose bait +// npc signal .dir is LEFT +// player pulls between npc signal and pulls delay max, got the fish, set .dir to DOWN goto choose bait + +function script fishing_cleanup { + .@npc$ = getarg(0, ""); + if (.@npc$ == "") end; + + set getvariableofnpc(.char_id, .@npc$), 0; // clean acc id + set getvariableofnpc(.account_id, .@npc$), 0; // clean char id + set getvariableofnpc(.last_used, .@npc$), gettimetick(2); // set last used time + setnpcdir .@npc$, DOWN; // reset direction + return; +} + +- script global fishing handler 32767,{ + end; + +OnBite: + if (getnpcdir(@fishing_spot$) != UP) + end; + + setnpcdir @fishing_spot$, LEFT; + @fishing_tick = gettimetick(0); + .@bite_fx=getvariableofnpc(.bite_fx, @fishing_spot$); + specialeffect(.@bite_fx ? .@bite_fx : 30, SELF, playerattached()); + end; + +OnCleanUp: + dispbottom l("You waited too long and lost the bait..."); + specialeffect(getvariableofnpc(.failure_fx, @fishing_spot$), SELF, playerattached()); // event fail +OnPCLogoutEvent: + fishing_cleanup @fishing_spot$; + @fishing_spot$ = ""; // unbind fishing npc + end; +} + +// Syntax: fishing() +// Syntax: fishing ( OFFSET, COMMON FISHES, RARE FISHES ) +// OFFSET: How many fishes are common +function script fishing { + +/////////////////////////////////////////// +// Var initialization + if (getarg(0, 0) == 0) { + setarray .@common_fish, + CommonCarp; + setarray .@rare_fish, + GrassCarp; + } else { + .@i=0; + freeloop(true); + for (.@i=1; .@i < getargcount(); .@i++) { + if (.@i <= getarg(0)) { + .@common_fish[.@i-1]=getarg(.@i); + } else { + .@rare_fish[.@i-1-getarg(0)]=getarg(.@i); + } + }; + freeloop(false); + } + //debugmes("[FISH] Initialized with %d common and %d rare fishes", getarraysize(.@common_fish), getarraysize(.@rare_fish)); + + .@npc$ = strnpcinfo(0); // the full name of the fishing spot + + .@account_id = getvariableofnpc(.account_id, .@npc$); // the account id of the player using the fishing spot + .@char_id = getvariableofnpc(.char_id, .@npc$); // the char id of the player using the fishing spot + .@dir = getnpcdir(.@npc$); // direction of the fishing spot + .@last_used = getvariableofnpc(.last_used, .@npc$); // when this fishing spot was last used + .@baits = getvariableofnpc(.baits, .@npc$); // bait count + + .@rod = getvariableofnpc(.fishing_rod, .@npc$); // the fishing rod required for this spot + .@rod = (.@rod ? .@rod : FishingRod); + + .@net_ratio = getvariableofnpc(.net_ratio, .@npc$); // How many fishes and baits are required? + .@net_ratio = max(1, (.@net_ratio ? .@net_ratio : 1)); + + .@regen_time = getvariableofnpc(.cooldown, .@npc$); // cooldown for the fishing spot + .@regen_time = (.@regen_time ? .@regen_time : 20); + + .@bp_chance = getvariableofnpc(.bp_chance, .@npc$); // Blueprint chance + .@bp_chance = (.@bp_chance ? .@bp_chance : 1); + + .@success_fx = getvariableofnpc(.success_fx, .@npc$); // effect to show on success + .@success_fx = (.@success_fx ? .@success_fx : FX_SUCCESS); + + .@failure_fx = getvariableofnpc(.failure_fx, .@npc$); // effect to show on failure + if (.@failure_fx < 1) + { + .@failure_fx = FX_FAILURE; + set getvariableofnpc(.failure_fx, .@npc$), .@failure_fx; // needed by global handler + } + + .@initial_fx = getvariableofnpc(.initial_fx, .@npc$); // effect to show when throwing the bait + .@initial_fx = (.@initial_fx ? .@initial_fx : 29); + + .@bite_fx = getvariableofnpc(.bite_fx, .@npc$); // effect to show when something bites + if (.@bite_fx < 1) + { + .@bite_fx = 30; + set getvariableofnpc(.bite_fx, .@npc$), .@bite_fx; // needed by global handler + } + + .@wait_time_min = getvariableofnpc(.wait_time_min, .@npc$); // min amount of time to wait for the line to sink + .@wait_time_min = (.@wait_time_min ? .@wait_time_min : 4000); + + .@wait_time_max = getvariableofnpc(.wait_time_max, .@npc$); // max amount of time to wait for the line to sink + .@wait_time_max = (.@wait_time_max ? .@wait_time_max : 18000); + + // During night time there are more fishes, and therefore, it is easier to fish + // This will make they reply at most 30% faster. Default is a 6 second max delay gain + if (is_night()) + .@wait_time_max = min(.@wait_time_min, .@wait_time_max*7/10); + + .@catch_time = getvariableofnpc(.catch_time, .@npc$); // the player must catch the fish within X ms after the line sinks + .@catch_time = (.@catch_time ? .@catch_time : 5000); + + .@pull_rand_max = getvariableofnpc(.pull_rand_max, .@npc$); + .@pull_rand_max = (.@pull_rand_max ? .@pull_rand_max : 1100); + + .@fish_id = CommonCarp; // failsafe + + + if (getvariableofnpc(.bait_ids[0], .@npc$) < 1) + { + // default baits (bait, chance booster) rand(0, 100) + setarray getvariableofnpc(.bait_ids[0], .@npc$), + SmallTentacles, 4, + Bread, 4, + Aquada, 8, + Tentacles, 10, + BugLeg, 2, + CaveSnakeTongue, 6, + LettuceLeaf, 1, + Cheese, 3, + RoastedMaggot, 6; + + } + + if (.@baits < 1) + { + // only count it once + .@baits = getarraysize(getvariableofnpc(.bait_ids[0], .@npc$)); + set getvariableofnpc(.baits, .@npc$), .@baits; + } + + +/////////////////////////////////////////// +// Logic below + + if (countitem(.@rod) < 1) + { + dispbottom l("You don't have any @@.", getitemlink(.@rod)); + return -1; + } + + if (.@account_id > 0 && !isloggedin(.@account_id, .@char_id)) + { + fishing_cleanup .@npc$; // reset + .@dir = DOWN; + } + + if (.@char_id != getcharid(CHAR_ID_CHAR) && .@dir != DOWN) + { + dispbottom l("This fishing spot is already being used!"); + return -2; + } + + + // pull too soon + if (.@dir == UP) + { + deltimer "global fishing handler::OnCleanUp"; // cancel auto cleanup + deltimer "global fishing handler::OnBite"; + specialeffect(.@failure_fx, SELF, playerattached()); // event fail + fishing_cleanup .@npc$; // do it manually instead + dispbottom l("You pulled too soon and lost the bait."); + return -3; + } + + // pull maybe on time + else if (.@dir == LEFT) + { + deltimer "global fishing handler::OnCleanUp"; // cancel auto cleanup + fishing_cleanup .@npc$; // do it manually instead + + getmapxy .@mapbis$, .@xbis, .@ybis, UNITTYPE_PC; // get current char location + + // Leave spot, lost the bait + if (.@mapbis$ != @fishing_loc$[0] || .@xbis != @fishing_loc[0] || .@ybis != @fishing_loc[1] || @fishing_spot$ != .@npc$) + { + dispbottom l("You left your fishing spot!"); + return -4; + } + + + // RNG to obtain a fish + if (rand2(gettimetick(0) - @fishing_tick) <= .@pull_rand_max) + { + for (.@i=0 ; .@i < .@net_ratio ; .@i++) { + // RNG to obtain a rare fish or common fish + // Luck can increase up to 5% when it is at 100. + // Level can increase up to 10% when it is at 100. + .@boost=(readparam2(bLuk)/20)+(BaseLevel/10); + .@bai=getvariableofnpc(.bait_ids[@bait_d+1], .@npc$); + // Ancient Blueprint: 0.05% per bait bonus (no luck) + if (rand2(2000) < .@bp_chance*.@bai) { + .@fish_id = AncientBlueprint; + } else if (rand2(0, 100) < .@bai+.@boost) { + .@fish_id = any_of(.@rare_fish); + } else { + .@fish_id = any_of(.@common_fish); + } + specialeffect(.@success_fx, SELF, playerattached()); + getexp getvariableofnpc(.bait_ids[@bait_d+1], .@npc$)+(BaseLevel/10), 4+getvariableofnpc(.bait_ids[@bait_d+1], .@npc$); // xp gain is equivalent to bait rarity + BaseLevel boost + + // MobPt gain is equivalent to bait rarity. + if (MPQUEST) + Mobpt+=.@bai*limit(1, BaseLevel/10, 10); + + if(!checkweight(.@fish_id, 1)) { + dispbottom l("You caught a @@ but had no room in your inventory to carry it.", getitemlink(.@fish_id)); + makeitem .@fish_id, 1, .@mapbis$, .@xbis, .@ybis; // drop on the ground + return 0; + } + getitem .@fish_id, 1; + + // Catch the Golden Fish Event + if ($EVENT$ == "Fishing") + FYE_Fishing(); + } + } + else + { + dispbottom l("You pulled too late and lost the bait..."); + specialeffect(.@failure_fx, SELF, playerattached()); // event fail + .@fish_id = 0; + getexp 0, 5; + } + + @fishing_spot$=""; + return .@fish_id; + } + + + + if (gettimetick(2) - .@last_used < .@regen_time) + { + dispbottom l("This fishing spot has just been used, give it a rest."); + return -5; + } + + // This "hack" will prevent you from fishing at two spots (buggy) + // It'll cancel the previous fishing too, per logical rule. + if (@fishing_spot$ != "") { + deltimer "global fishing handler::OnCleanUp"; // cancel auto cleanup + deltimer "global fishing handler::OnBite"; + fishing_cleanup(@fishing_spot$); // clean up manually + @fishing_spot$=""; + dispbottom l("You left your fishing spot!"); + return -4; + } + + // begin fishing + narrator S_LAST_NEXT, + l("You see some fish reflecting the sun on the surface of the water."), + (.@net_ratio == 1 ? l("What will be the bait for the fish?") : l("You need @@ units of bait for this fishing spot. What will you use?", .@net_ratio)); + + if (GSET_FISHING_BAIT > 1) { + .@bait = GSET_FISHING_BAIT; + mesc l("Maybe a %s?", getitemlink(GSET_FISHING_BAIT)); + } else { + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .@bait = requestitem(); + if (.@bait > 1 && GSET_FISHING_BAIT) + GSET_FISHING_BAIT = .@bait; + } + + if (.@bait < .@net_ratio) { + narrator S_FIRST_BLANK_LINE, + l("You take your @@ and leave.", getitemlink(.@rod)); + + return -6; + } + + if (countitem(.@bait) < .@net_ratio) { + mesc l("You do not have enough bait for fishing here."); + return -6; + } + + .@bait_c = false; + for (.@i = 0; .@i < .@baits; .@i += 2) { + if (getvariableofnpc(.bait_ids[.@i], .@npc$) == .@bait) + { + .@bait_c = true; + @bait_d = .@i; + break; + } + } + + if (.@bait_c != true) + { + narrator S_FIRST_BLANK_LINE, + l("This item cannot be used as bait here."); + + return -6; + } + + if (getvariableofnpc(.char_id, .@npc$) > 0) + { + narrator S_FIRST_BLANK_LINE, + l("Somebody took your place on this spot!"), + l("You take your fishing rod and leave."); + return -8; + } + + @fishing_spot$ = .@npc$; // bind player to fishing spot + set getvariableofnpc(.account_id, .@npc$), getcharid(CHAR_ID_ACCOUNT); // record account id + set getvariableofnpc(.char_id, .@npc$), getcharid(CHAR_ID_CHAR); // record char id + set getvariableofnpc(.last_used, .@npc$), gettimetick(2); + getmapxy(@fishing_loc$[0], @fishing_loc[0], @fishing_loc[1], 0); // record char pos + delitem .@bait, .@net_ratio; + + // The player uses this spot, his bait is ready, he just has to wait for the signal. + closedialog; + + specialeffect(.@initial_fx, SELF); // throw the bait + sleep2 800; // wait 0.8s for synchronize the sound of "plop" in water with the npc dir UP. + setnpcdir .@npc$, UP; + + dispbottom l("Wait for the bait to sink underwater."); + + .@delay = rand(.@wait_time_min, .@wait_time_max); + + addtimer .@delay, "global fishing handler::OnBite"; // bite logic + addtimer (.@delay + .@catch_time), "global fishing handler::OnCleanUp"; // auto clean up + + return 0; +} diff --git a/npc/functions/game-rules.txt b/npc/functions/game-rules.txt new file mode 100644 index 0000000..b425afd --- /dev/null +++ b/npc/functions/game-rules.txt @@ -0,0 +1,74 @@ +// TMW-2 script. +// Evol scripts. +// Authors: +// gumi +// Qwerty Dragon +// Reid +// WildX +// Jesusalva +// Description: +// Main Rules of TMW2. + +function script GameRules { + /* + if (checkweight(BookOfLaws, 1) == true && countitem(BookOfLaws) < 1) + getitem BookOfLaws, 1; // give the book if the player lost it + */ + + narrator getarg(0, 0), + //l("1. ##BDo not bot##b, A character must be being controlled by a human, standing, siting, or logged off. You may only control one character at a time."), + //l("* You can use a BOT if it was provided by TMW2 Team. In no circumstance you shall AFK-Bot, or you'll be promptly banned or jailed."), + l("1. You must be at the computer to play. Using a bot ##Bwhile at computer##b will be tolerated."), + l("Failure to respond to other players and/or GMs in a timely manner will be viewed as unacceptable AFK botting. And you'll spend a night in the jail."), + l("2. ##BDo not spam nor flood.##b This rule is not limited to public chat, actions ingame can be flooding too."), + l("3. ##BDo not trade invalid items, or try to cheat on trades.##b This includes any other kind of cheat or bug abuse, passive of account deletion and IP ban as stated by the Terms Of Service."), + l("4. ##BRespect other players.##b This includes but is not limited to using offensive language in nicknames or chat, and begging items or favours to other players."), + l("5. ##BThe public chat is to be understood by everyone.##b Therefore, try to use english when possible."), + l("6. ##BDo not create multi accounts.##b A person may only hold one account and as many chars as allowed by the server/client. Staff members with special privileges in-game may have a second account without those privileges."), + l("7. ##BAs long as you have an account, you agree with the [@@https://tmw2.org/legal|Terms Of Service@@]##b. Shall any rule conflict with these terms, the Terms Of Service provided on that link shall prevail."), + l("8. ##BThe use of real money is prohibited##b for ingame stuff, except by sponsoring."), + l("9. ##BAdmit when you're wrong.##b Users trying to lie to or fool GMs will get no pity from them."), + l("10. ##BDo not logout##b at Botcheck area or at Jail. We cannot unjail an offline player. The opposite of rule 9: If you believe you're right, keep your ground and explain calmly what happened. We'll calmly analyse the situation. If you were jailed without guilt, an apology will be sent to you, provided this rule is not broken."), + l("11. If you believe you are Not Guilty, but the GM deemed you guilty, request a ##BCouncil Trial##b. It's your right. In a Council Trial, we'll have multiple people giving the veredict. Any player may be called to compose the council. All admins shall take part in it. An arragment relative to time, date, and number of counsellors is to be made."); + return; +} + + +- script @rules 32767,{ + end; + + function read_book { + narrator S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("This book outlines the laws which applies everywhere in the World Of Mana."), + l("The first page contains the universal rules that have been agreed upon throughout the land."); + + GameRules S_NO_NPC_NAME | S_LAST_NEXT; + + narrator S_NO_NPC_NAME, + l("The next page begins to list the complex trading laws and political rules. The word \"%s\" shows up everywhere.", "[@@https://git.themanaworld.org/ml|@@]"), + l("All this seems unimportant to you right now."); + if (BaseLevel > 30) { + mesc l("However, there's a link to get a simple python bot software!"); + mes "@@https://git.themanaworld.org/ml/all/tmw2_bot.py|Simple Python Bot (Linux/Windows/Mac)@@"; + } + close; + } + +OnCall: + GameRules; + close; + +OnUseBook: + if (openbook()) + read_book; + bye; + +OnShelfUse: + if (openbookshelf()) + read_book; + bye; + +OnInit: + .book_name$ = "The Book of Laws"; + bindatcmd "rules", "@rules::OnCall", 0, 2, 0; +} diff --git a/npc/functions/gmbot.txt b/npc/functions/gmbot.txt new file mode 100644 index 0000000..5d11e5d --- /dev/null +++ b/npc/functions/gmbot.txt @@ -0,0 +1,268 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// The most powerful sword ever. It's alive. + +boss,45,42,0 script Lightbringer NPC_LIGHTBRINGER,{ + function movelb; + function matrixlb; + function notAvailable; + + if ($GAME_STORYLINE < 3) notAvailable(); + if (islegendary()) notAvailable(); + if ($LIGHT_HOLDER$ != "") notAvailable(); + + .@q=getq(General_Narrator); + // Prologue missed - straight refusal + if (.@q < 19) { + if (strcharinfo(0) != $MOST_HEROIC$) + npctalk3 l("The sword glows too much. Perhaps @@ could take it.", $MOST_HEROIC$); + else + npctalk3 l("I'm not going to touch a FLOATING sword made of unobtainum without at least knowing a bit more about it."); + end; + } + // Determine if you are worthy (from 0 to 124) + if (!@lbscore) + @lbscore=matrixlb(); + + if (is_staff()) + dispbottom "I only did "+@lbscore+" points..."; + + if (@lbscore <= 30) { + npctalk3 l("..."); + end; + } else if (@lbscore <= 60) { + npctalk3 l("You are not worthy..."); + end; + } + + // Else: score > 60, 2 minutes per additional score + // If you are above 100: +2 minutes per score + // If you are above 110: +4 minutes per score + if (@lbscore > 100) + @lbscore*=3; + if (@lbscore > 110*3) + @lbscore*=2; + + // Basic wield time: 2 minutes per point + .@btime=120; + if ($GAME_STORYLINE == 4) + .@btime+=30; // +30 seconds for stage 4 + else if ($GAME_STORYLINE >= 5) + .@btime+=60; // +60 seconds for stage 5 (but it was not yet claimed? o.o) + + // Each siege gives +1 second per point + if ($GAME_STORYLINE < 5) + .@btime+=$MK_TEMPVAR; + + if (@lbscore > 100 && $GAME_STORYLINE > 3 && $LIGHT_HOLDER$ == "" && strcharinfo(0) == $MOST_HEROIC$ && !islegendary()) + goto L_Perma; + + rentitem Lightbringer, (.@btime*(@lbscore-60)); + dispbottom l("This live sword drafts itself to your hand. You can wield it for a while."); + disablenpc .name$; + .busy=gettimetick(2)+(.@btime*(@lbscore-60))+300; + end; + +function notAvailable { + npctalkonce l("It still isn't time to awake the King Of Holy Swords, Light Bringer."); + end; + return; +} + +L_Perma: + mesn l("Lightbringer"), 2; + mesc l("%s, you are pathetically weak.", strcharinfo(0)), 2; + next; + mesn l("Lightbringer"), 2; + mesc l("However, the fate of this world is at your hands. I shall not allow the Moubootaur to cause havoc."), 2; + next; + mesn l("Lightbringer"), 2; + mesc l("Therefore, just for a short while, I offer to be your blade."), 2; + next; + mesn l("Lightbringer"), 2; + mesc l("What do you say?"), 2; + select + l("It'll be my pleasure."), + l("I am a noob and don't need you."); + + mes ""; + if (islegendary()) close; + if ($LIGHT_HOLDER$ != "") close; + if (@menu != 1) close; + inventoryplace Lightbringer, 1; + mesn l("The Mana Source"), 2; + mesc l("%s, you did your best to protect this world inhabitants. It is my wish that you continue protecting this world. Therefore, I bestow upon you, the legendary %s. Please use its powers to protect your friend and the world peace!", strcharinfo(0), getitemlink(Lightbringer)), 2; + mesc l("WARNING: The %s is a %s. Besides being insanely powerful, no duplicate of them exist in the world. They can be tweaked freely and can hold multiple cards as well, and scale according to your level. Use its powers wisely. However, beware: This weapon cannot be traded except with \"@grantpower\" command, and if you abandon the world, the weapon will abandon you as well!", getitemlink(Lightbringer), b(l("legendary weapon"))), 1; + + // Destroy the previous Lightbringer + DelItemFromEveryPlayer(Lightbringer); + getitembound Lightbringer, 1, 1; // Account bound or char bound? (1 or 4) + dispbottom l("You received the @@ from @@.", getitemlink(Lightbringer), l("The Mana Source")); + $LIGHT_HOLDER$=strcharinfo(0); + close; + +OnInit: + .sex = G_OTHER; + .distance = 2; + + if ($GAME_STORYLINE < 3) + disablenpc .name$; + + .npcId=getnpcid(); + .users=getusers(1); + .busy=false; + // Constants + + // We should jump straight to loop (it runs every 5 minutes) +OnTimer300000: + .users=getusers(1); + // If $LIGHT_HOLDER$ is set, we die here + if ($LIGHT_HOLDER$ != "") + end; + + // Proccess busy time + if (.busy) { + if (.busy < gettimetick(2)) { + .busy=false; + enablenpc .name$; + } else { + initnpctimer; + end; + } + } + + // Min. 2 PCs for the Lightbringer to show up + if (!.busy) { + if (.users <= 2 && !$@GM_OVERRIDE) { + disablenpc .name$; + .busy=true; + } + } + + // You cannot interact with it during events + if ($@MK_SCENE || $@GM_EVENT) { + disablenpc .name$; + .busy=true; + initnpctimer; + end; + } + + // Move Lightbringer to a random map + movelb(.npcId); + + // Debug markers + if ($@GM_OVERRIDE) + debugmes "Light Bringer (bot): "+.map$+" ("+.x+", "+.y+")"; + + // We're done, restart loop timer + initnpctimer; + end; + +// Functions +// movelb(npcid) +function movelb { + .@id=getarg(0); + setarray .@m$, "001-1", "001-3", "001-7", + "004-2", "004-2-1", + "010-1", "010-1-1", "011-2", "011-3", + "013-1", "014-5", "015-1", "015-2", "015-3", "015-4", "015-5", "015-6", "015-7", + "017-0", "018-1", "018-1-1", "018-2", "018-3", "018-4", "018-4-1", "018-5-1", "018-7", + "019-4", "021-1", "021-2", "021-3", "022-1", "023-1", + "029-1", "029-7", "029-8"; + .mp$=any_of(.@m$); + + // Try to warp randomly: 30 attempts for a walkable spot + .@e=0; .@x=0; .@y=0; + .@mx=getmapinfo(MAPINFO_SIZE_X, .mp$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, .mp$)-20; + do { + if (.@e >= 30) { + .mp$="boss"; + .@x=45; + .@y=42; + break; + } + .@x = rand2(20, .@mx); + .@y = rand2(20, .@my); + .@e+=1; + } while (!checknpccell(.mp$, .@x, .@y, cell_chkpass)); + + if (!checknpccell(.mp$, .@x, .@y, cell_chkpass)) { + Exception("gm.bot runtime error: GM_ERR_128 highlight @Jesusalva", RB_DEBUGMES|RB_IRCBROADCAST); .mp$="boss"; .@x=45; .@y=42; + } + + // Time to warp the NPC + npctalk("..."); + sleep(20); + unitwarp(.@id, .mp$, .@x, .@y); + sleep(50); // For some reason or other, adding sleep(norid) and sleep2(rid). + return; +} + +// matrixlb() +function matrixlb { + .@pts=0; + // Heroic Matrice + .@pts+=($MOST_HEROIC$ == strcharinfo(0) ? 10 : 0); + // Magic Matrice + .@pts+=min(7, MAGIC_LVL); + // Main levels matrix + .@pts+=min(15, BaseLevel/10); + .@pts+=min(15, JobLevel/10); + // Scoreboards matrix + .@br=array_find($@hoblvl_name$, strcharinfo(0)); + .@jr=array_find($@hojlvl_name$, strcharinfo(0)); + .@mr=array_find($@hofortune_name$, strcharinfo(0)); + .@br=limit(0, (.@br >= 0 ? 10-.@br : 0), 10); + .@jr=limit(0, (.@jr >= 0 ? 10-.@jr : 0), 10); + .@mr=limit(0, (.@mr >= 0 ? 10-.@mr : 0), 10); + .@pts+=.@br; + .@pts+=.@jr; + .@pts+=.@mr; + // Reputation matrix + .@pts+=min(3, reputation("Tulim")/33); + .@pts+=min(3, reputation("Hurns")/33); + .@pts+=min(3, reputation("LoF")/33); + .@pts+=min(3, reputation("Nival")/33); + .@pts+=min(3, reputation("Halin")/33); + .@pts+=min(3, reputation("Frostia")/33); + .@pts+=min(3, reputation("Candor")/33); + .@pts+=min(3, reputation("Fortress")/33); + // What about Lilit? + + // Having accomplished deeds + .@cr=0; + .@cr+=(YETIKING_WINNER ? 1 : 0); + .@cr+=(HEROESHOLD_WINNER ? 1 : 0); + .@cr+=(QUIRINO_WINNER ? 1 : 0); + .@cr+=(GEMINI_WINNER ? 1 : 0); + .@cr+=(GHQ_WINNER ? 1 : 0); + .@cr+=(FORT_1ST_VISIT ? 1 : 0); + .@cr+=(MOUBOOTAUR_WINNER ? 1 : 0); + .@cr=limit(0, .@cr, 7); + .@pts+=.@cr; + + // What about Doppelganger TOP 3? + .@dr=array_find($@udt_name$, strcharinfo(0)); + .@dr=limit(0, (.@dr >= 0 ? 3-.@dr : 0), 3); + .@pts+=.@dr; + + // Rebirth (2pts per level) + .@pts+=limit(0, REBIRTH*2, 10); + + // World Story Bonus + if ($GAME_STORYLINE > 3 && $MK_TEMPVAR >= 12) + .@pts+=1; + if ($FIRESOFSTEAM >= 10) + .@pts+=1; + if ($GAME_STORYLINE >=5) + .@pts+=1; // Should never, ever, happen + + // Max points: 101→104→114→117→124 + return .@pts; +} + +} + diff --git a/npc/functions/goodbye.txt b/npc/functions/goodbye.txt new file mode 100644 index 0000000..b521461 --- /dev/null +++ b/npc/functions/goodbye.txt @@ -0,0 +1,152 @@ +// Evol functions. +// Authors: +// gumi +// Reid +// Description: +// script terminator functions + + + +// goodbye_msg +// Tell a random goodbye sentence. +// Variables: +// .@rand = Random number between the number of "goodbye" choice. + +function script goodbye_msg { + setarray .byemsg$[0], + l("See you!"), + l("See you later!"), + l("See you soon!"), + l("Bye!"), + l("Farewell."), + l("Bye then!"), + l("Goodbye."), + l("Bye for now."), + l("Talk to you soon!"), + l("Talk to you later!"), + l("Have a good day!"), + l("Cheers!"), + l("Take care!"); + + return any_of(.byemsg$); +} + + + +// cwarp +// Closes the dialog, then warps the player. +// You almost always want to use this instead of `warp`. +// usage: +// cwarp; +// cwarp x, y; +// cwarp map, x, y; + +function script cwarp { + .@map$ = getarg(0, ""); + .@x = getarg(1, 0); + .@y = getarg(2, 0); + + if (getargcount() > 0 && getargcount() < 3) + { + .@npc$ = strnpcinfo(0); + .@map$ = getvariableofnpc(.map$, .@npc$); + .@x = getarg(0); + .@y = getarg(1); + } + + getmapxy .@pc_map$, .@pc_x, .@pc_y, UNITTYPE_PC; // get char location + + closedialog; // XXX: maybe send closeclientdialog in the future + + if (getargcount() < 1) + { + warp .@pc_map$, .@pc_x, .@pc_y; // no arguments, just refresh + close; + } + + if (.@map$ == .@pc_map$) + { + if (.@pc_x == .@x && .@pc_y == .@y) + { + close; // same location, don't move + } + + else + { + slide .@x, .@y; // same map, slide instead of full warp + close; + } + } + + warp .@map$, .@x, .@y; // different map, warp to given location + close; +} + + + +// cshop +// closes the dialog, then opens a shop +// if no npc is given, calls "#<npc> $" + +function script cshop { + closedialog; // XXX: maybe send closeclientdialog in the future + shop getarg(0, "#" + strnpcinfo(0) + " $"); + //close; => the shop buildin already sends close, and is a terminator itself +} + + + +// cstorage +// closes the dialog, then opens storage + +function script cstorage { + closedialog; // XXX: maybe send closeclientdialog in the future + openstorage; + close; +} + + + +// bye +// closes the dialog without waiting for the player to press close +// can also display an emote + +function script bye { + .@emote = getarg(0, -1); + closedialog; // XXX: maybe send closeclientdialog in the future + + if (.@emote >= 0) + emotion .@emote; + + close; +} + + + +// goodbye +// same as bye, but also displays a canned message +// can also display an emote + +function script goodbye { + npctalkonce(goodbye_msg()); + bye getarg(0, -1); +} + + + +// goodbye2 +// Waits for the player to press close, displays a canned message, +// ends execution. +// Can also display an emote + +function script goodbye2 { + .@emote = getarg(0, -1); + + close2; + npctalkonce(goodbye_msg()); + + if (.@emote >= 0) + emotion .@emote; + + end; +} diff --git a/npc/functions/guild.txt b/npc/functions/guild.txt new file mode 100644 index 0000000..2b4d79b --- /dev/null +++ b/npc/functions/guild.txt @@ -0,0 +1,46 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Guild System Utils + +// Usage: getguildrole (guildid, accid, {type}) +// Returns a string if TYPE is set. Otherwise, Returns the GPOS_* +// On failure, returns "null" or -1 +function script getguildrole { + .@gid=getarg(0); + .@aid=getarg(1); + .@type=getarg(2,0); + .@pos=-1; + getguildmember(.@gid, 2); + for (.@i=0; .@i < $@guildmembercount; .@i++) { + //debugmes "Found AID %d - Position %d", $@guildmemberaid[.@i], $@guildmemberpos[.@i]; + if (.@aid == $@guildmemberaid[.@i]) { + .@pos=$@guildmemberpos[.@i]; + break; + } + } + if (!.@type) + return .@pos; + + // Wait, we now have a command for this + return getguildpostitle(.@gid, .@pos); + + // Legacy behavior + switch (.@pos) { + case GPOS_GUILDMASTER: + return "Guild Master"; + case GPOS_VICELEADER: + return "Vice Leader"; + case GPOS_RECRUITER: + return "Guild Recruiter"; + case GPOS_TREASURER: + return "Guild Treasurer"; + case GPOS_MEMBER: + return "Member"; + case GPOS_NEWBIE: + return "Newbie"; + default: + return "null"; + } +} diff --git a/npc/functions/hammocks.txt b/npc/functions/hammocks.txt new file mode 100644 index 0000000..8e1c2fe --- /dev/null +++ b/npc/functions/hammocks.txt @@ -0,0 +1,50 @@ +// Evol functions. +// Authors: +// 4144 +// Reid +// Description: +// Hammocks utility functions +// Variables: +// none + +function script hamTouchLeft { + if (getareausers() <= 1) + { + .dir = 0; + stopnpctimer; + initnpctimer; + } + close; +} + +function script hamUnTouch { + if (getareausers() == 0) + { + .dir = 2; + initnpctimer; + startnpctimer; + } + close; +} + +function script hamTimerLeft { + stopnpctimer; + if (.dir == 2) .dir = 0; + end; +} + +function script hamTouchRight { + if (getareausers() <= 1) + { + .dir = 0; + stopnpctimer; + initnpctimer; + } + close; +} + +function script hamTimerRight { + stopnpctimer; + if (.dir == 2) .dir = 0; + end; +} diff --git a/npc/functions/honor.txt b/npc/functions/honor.txt new file mode 100644 index 0000000..c9c7f76 --- /dev/null +++ b/npc/functions/honor.txt @@ -0,0 +1,95 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// PvP Honor Rank system + +// Returns if a map is on PVP Mode or Not +// ispvpmap( {mapid} ) +function script ispvpmap { + .@mapa$=getarg(0, getmapname()); + return (getmapflag(.@mapa$, mf_pvp) || getmapflag(.@mapa$, mf_pvp_noparty) || getmapflag(.@mapa$, mf_pvpnoguild)); +} + +// Numerical representation of player strength +// get_BR( getcharid(3) ) +function script get_BR { + .@oid=getcharid(3); + .@rid=getarg(0, .@oid); + .@br=0; + + // attachrid() and detachrid() + // readbattleparam(.@rid) + // Or rather: battleparam() + if (attachrid(.@rid)) { + // 1 BR per allocated status point + .@br+=battleparam(UDT_STR); + .@br+=battleparam(UDT_AGI); + .@br+=battleparam(UDT_VIT); + .@br+=battleparam(UDT_DEX); + .@br+=battleparam(UDT_INT); + .@br+=battleparam(UDT_LUK); + + // 6 BR per level + .@br+=BaseLevel*6; + + // 1 BR for 5 DMG points (average) + .@br+=(battleparam(UDT_ATKMIN)+battleparam(UDT_ATKMAX))/5; + .@br+=battleparam(UDT_MATKMAX)/5; + + // 8 BR for each attack range + .@br+=battleparam(UDT_ATKRANGE)*8; + + // 1 BR for 10 DEF points + .@br+=battleparam(UDT_DEF)/10; + .@br+=battleparam(UDT_MDEF)/10; + } else { + Exception("GET_BR INVALID RID "+.@rid, RB_DEBUGMES|RB_IRCBROADCAST); + } + + // Restore + detachrid(); + if (!attachrid(.@oid)) + Exception("::FATAL :: GET_BR INVALID OID "+.@oid, RB_DEBUGMES|RB_IRCBROADCAST|RB_ISFATAL); + + return .@br; +} + +// Calculate the Honor Points which are due +// calc_HR( get_BR(getcharid(3)), get_BR(killedrid), log=True ) +function script calc_HR { + //.@atk_br=get_BR(getarg(0)); + //.@def_br=get_BR(getarg(1)); + .@atk=readparam(BaseLevel, getarg(0)); + .@def=readparam(BaseLevel, getarg(1)); + .@log=getarg(2, true); + + // Calculate how much levels you've used above needed + .@overpower=.@atk-.@def; + + // Dishonorable: You used 15 levels above target, or target < level 30 + if (.@overpower > 15 || .@def < 30) { + .@honor=-(limit(1, .@overpower-15, 12)); + } else { + .@honor=limit(3, -(.@overpower)+15, 20); + } + + // Log cooldown (30 minutes) + // If cooldown already in effect, do not gain honor. + if (.@log) { + if (.@honor > 0 && PVP_COOLDOWN[.@def] >= gettimetick(2)) + .@honor=0; + PVP_COOLDOWN[.@def]=gettimetick(2)+PVP_WAITTIME; + } + return .@honor; +} + +// getvariableofpc(HONOR, .@rid, 0) < 0 → determine if other player is bandit +// is_bandit( account id ) +function script is_bandit { + .@oid=getcharid(3); + .@rid=getarg(0, .@oid); + + return getvariableofpc(HONOR, .@rid, 0) < 0; +} + diff --git a/npc/functions/hub.txt b/npc/functions/hub.txt new file mode 100644 index 0000000..f8d15a0 --- /dev/null +++ b/npc/functions/hub.txt @@ -0,0 +1,1336 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// dangerDuck +// TMW Org. +// Description: +// HUB functions (Login, Logout, Death) + +// HUB_Login () +function script HUB_Login { + getmapxy(.@mapa$, .@a,.@b, 0); + + // Login on Blue Sage Workshop/Library + if (.@mapa$ == "020-7-1") { + addtimer(1000, "#BlueSageHUB::OnCycle"); + } + + // Random Treasure cleaning + if (CHAREG_CLEANUP < gettimetick(2)-CHEST_WAITTIME) + deletearray RNGTREASURE_DATE; + + // PVP Cooldown cleaning + if (CHAREG_CLEANUP < gettimetick(2)-PVP_WAITTIME) + deletearray PVP_COOLDOWN; + + // Mirror Lake functionality + if (getvaultid() && !getstatus(SC_JAILED)) { + // Christmas 2021 + if ($EVENT$ == "Christmas") { + if (#XMAS2021 < ##01_TMWEXP) { + getexp (##01_TMWEXP-#XMAS2021) * 7 / 10, 0; + #XMAS2021 = ##01_TMWEXP; + } else if (#XMAS2021 > ##01_TMWEXP) { + // You leveled up? + getexp ##01_TMWEXP * 7 / 10, 0; + #XMAS2021 = ##01_TMWEXP; + } + } + + .@gto=get_byte(##00_INFO, 3); + .@mlp=get_nibble(##00_INFO, 5); + /*debugmes "INFO: %d", ##00_INFO; + debugmes "BYTE: %d/%d/%d/%d", get_byte(##00_INFO, 0), get_byte(##00_INFO, 1), get_byte(##00_INFO, 2), get_byte(##00_INFO, 3); + debugmes "NIBBLE: %d/%d/%d/%d/%d/%d/%d/%d", get_nibble(##00_INFO, 0), get_nibble(##00_INFO, 1), get_nibble(##00_INFO, 2), get_nibble(##00_INFO, 3), get_nibble(##00_INFO, 4), get_nibble(##00_INFO, 5), get_nibble(##00_INFO, 6), get_nibble(##00_INFO, 7); + debugmes "Your Vault ID: %d", getvaultid(); + debugmes "VAULT LOGIN, GTO is %d MLP is %d", .@gto, .@mlp; + */ + // Work only on new chars, or chars which cleared Tulimshar. + if (.@gto == WORLD_ID && + ((!getq(General_Narrator) && BaseLevel < 5) || + (getq(General_Narrator) && BaseLevel > 20))) + { + // Warp to the proper Mirror Lake + switch (.@mlp) { + case 1: warp "018-7-1", 90, 47; LOCATION$ = "LoF"; break; + default: warp "014-4", 28, 31; LOCATION$ = "LoF"; break; + } + + // Send debug information + debugmes("Vault User %d moved to lake %d.", getvaultid(), .@mlp); + + // TODO: Handle new user (non-native) accounts + if (!getq(General_Narrator) && BaseLevel < 5) { + adddefaultskills(); + getitembound MirrorLakeArmor, 1, 4; + equip(MirrorLakeArmor); + percentheal(100, 100); + TUTORIAL=true; + //BaseLevel = get_byte(##00_INFO, 0) + 1; + // TODO: Display quick tutorial + dispbottom l("Mirror Lake : Created temporary character; It'll be reset on logout."); + dispbottom l("Mirror Lake : Obtain help with %s.", b("@info")); + } + + // Unset the target lake/world + set_byte(##00_INFO, 3, 0); + set_nibble(##00_INFO, 5, 0); + } else if (.@gto) { + // Heading somewhere which is not here! + mesc l("WARNING: If you use any Mirror Lake feature on this world, the current Mirror Lake Quest will be marked as \"Failed\"."), 1; + mesc l("If this is undesired, select the correct world, and if needed create a new char on it."), 1; + ##VAULT_GOTO=.@gto; + ##VAULT_MLTO=.@mlp; + next; + closeclientdialog; + } + } + return; +} + +// HUB_Logout ( {dead} ) +function script HUB_Logout { + .@dead=getarg(0, false); + getmapxy(.@mapa$, .@a,.@b, 0); + .@zone$=getmapinfo(MAPINFO_ZONE, .@mapa$); + + // Hardcore Server + if ($HARDCORE && .@dead) { + // Update Absolutions + if (ABSOLVE_DAY != gettimeparam(GETTIME_DAYOFMONTH)) { + ABSOLVE_DAY=gettimeparam(GETTIME_DAYOFMONTH); + ABSOLVE_CNT=0; + } + + // Main Loop + if (@grace) { + // Grace is upon you (ie. script death) + @grace=false; + } else if (.@zone$ == "MMO" && ABSOLVE_CNT <= 3) { + // Absolve (limited attempts) + ABSOLVE_CNT+=1; + dispbottom l("This is a special map so your death is not counted."); + dispbottom b(l("You have %d non-counting deaths remaining today.", 3-ABSOLVE_CNT)); + if (ABSOLVE_CNT == 3) + dispbottom col(l("WARNING: if you die again today in a special map it will be PERMANENT."), 1); + } else { + // Meet your final demise! + atcommand("@dropall"); + makeitem CoinBag, Zeny/500, .@mapa$, .@a, .@b; + Zeny=0; + //resetlvl(2); // FIXME: Split the exp + // TODO: Warp back to Candor or it'll be unplayable + // TODO: It could be @jail, but it is buggy + atcommand("@jailfor 1d "+strcharinfo(0)); + } + // Vanished on Cindy Cave + } else if (.@mapa$ == "021-4" && strcharinfo(0) == $@CINDY_HERO$) { + donpcevent("Cindy#Outside::OnReckless"); + recovery(getcharid(3)); + warp any("010-1", "010-2"), 0, 0; + sc_start2 SC_POISON, 1, 90, 10000; + die(); + if (!$HARDCORE) heal -1, -1; + } else if (.@mapa$ == "021-4") { + .@pl = getmapusers("021-4")-1; + if (.@pl < 1) + donpcevent("Cindy#Outside::OnCleanUp"); + recovery(getcharid(3)); + warp "Save", 0, 0; + } + // Logout while donating blood + if (getq(HurnscaldQuest_BloodDonor) == 2) { + slide 35, 28; + setpcblock(PCBLOCK_SOFT, false); + setq HurnscaldQuest_BloodDonor, 0, gettimetick(2)+3600; // one hour penalty + } + // Logout/Death on Nard's ship hold + if (compare(.@mapa$, "002-2") || compare(.@mapa$,"nard")) { + setq2 ShipQuests_Peter, 0; + setq3 ShipQuests_Peter, 0; // Bugfix + } + // Logout on botcheck area + if (compare(.@mapa$,"botcheck") && !.@dead) { + if (!is_staff()) { + atcommand "@jail "+strcharinfo(0); + rodex_sendmail(getcharid(0), "TMW2 Team", "Error detected!", "Please contact GM Team, error code: BOTCHECK-AUTOJAILED"); + } + } + // Died on Terranite Cave where exp penalty is lower + if (.@mapa$ == "015-6" && .@dead) { + @deathpenalty_override=2; + @deathpenalty_realvalue=readparam(BaseExp); + @deathpenalty_realvaljob=readparam(JobExp); + } + // Died or logged out at Tulimshar Arena (FIXME) + if (compare(.@mapa$, "ARENA")) { + if ('UDTf) { + 'UDTf=0; + killmonster(getmap(), instance_npcname("Arnea#003-13")+"::OnGladius"); + deltimer(instance_npcname("Arnea#003-13")+"::OnVerify"); + } + } + // Died or logged out at Candor Battle + if (.@mapa$ == "006-1") { + if (@crazypoints > CRAZYPOINTS) { + CRAZYPOINTS=@crazypoints; + dispbottom l("Crazyfefe Cave: New Highscore: @@ points", CRAZYPOINTS); + @crazypoints=0; + } + } + // Died or logged out at Candor Survival + if (.@mapa$ == "006-9" && $@SURVIVAL_CANDOR) { + .@score = gettimetick(2)-$@SURVIVAL_CANDOR; + if (.@score > SCANDORPTS) { + SCANDORPTS=.@score; + dispbottom l("Candor Survival : New Highscore: %s", FuzzyTime(gettimetick(2)+.@score)); + } + warp "005-1", 66, 63; + } + // Died or logged during Bandit Lord fight + if (.@mapa$ == "015-2") { + if (isin("015-2", 228, 227, 282, 280)) + killmonster("015-2", "#BanditLordDen::OnLordDeath"); + } + // Died or logged out on Blue Sage House + if (.@mapa$ == "020-7-1") { + callfunc("BSClearNest", @nestid); + } + // Died or logged out on Player Story 2 - Magic School Port + if (compare(.@mapa$, "0030")) { + if (@ASSASSIN > 0) + delcells "MQ2Wall"+getcharid(0); + killmonsterall(getmap()); + } + // Died or logged out on Player Story 5 - Forgotten Throne Room + if (compare(.@mapa$, "hmc")) { + if (@instid > 0) + .@n$=instance_npcname("#Core02331", @instid); + else + .@n$=instance_npcname("#Core02331"); + deltimer(.@n$+"::OnW01"); + deltimer(.@n$+"::OnW02"); + deltimer(.@n$+"::OnE07"); + deltimer(.@n$+"::OnE08"); + deltimer(.@n$+"::OnE09"); + deltimer(.@n$+"::OnE10"); + deltimer(.@n$+"::OnE11"); + deltimer(.@n$+"::OnE12"); + killmonsterall(getmap()); + } + // Died or logged out on Player Story 6 - Forgotten Shrine + if (compare(.@mapa$, "brb3")) { + //.@n$=instance_npcname("#Core02331"); + .@q=getq(LoFQuest_Barbara); + // Reset quest to: Forgotten Chamber Puzzle incomplete + if (.@q < 4 && .@q >= 2) { + setq1 LoFQuest_Barbara, 1; + setq3 LoFQuest_Barbara, 1; + // FIXME: Enable Mana Stone#01863 + dispbottom col(l("WARNING: You died at Forgotten Shrine and the Shrine defense triggered."), 1); + dispbottom col(l("WARNING: Your progress on the quest was lost!"), 1); + } + } + // Died or logged out during Sagratha Fight + if (compare(.@mapa$, "sgt2")) { + setq1 HurnscaldQuest_Sagratha, 3; + setq3 HurnscaldQuest_Sagratha, 0; + deltimer("#SaggyDungeonCore::OnW01"); + deltimer("#SaggyDungeonCore::OnW02"); + deltimer("#SaggyDungeonCore::OnW03"); + deltimer("#SaggyDungeonCore::OnW04"); + if (instance_id() >= 0) { + .@n$=instance_npcname("#SaggyDungeonCore"); + deltimer(.@n$+"::OnW01"); + deltimer(.@n$+"::OnW02"); + deltimer(.@n$+"::OnW03"); + deltimer(.@n$+"::OnW04"); + } + } + // Died or logged out during a Boss Raid event + if (compare(.@mapa$, "fyrb")) { + doevent "sBossRaid::OnDie"; + } + // Died or logged out during Yeti King Fight + if (getq(HurnscaldQuest_Celestia) > 1) + setq HurnscaldQuest_Celestia, 1; + // First death produces a warning message + if (PC_DIE_COUNTER <= 1 && .@dead) { + dispbottom l("Dying outside a town square will cause EXP loss."); + } + // If you were travelling and died/logged out, cleaning is needed + if (@timer_navio_running) { + @timer_navio_running=0; + // Logged out? Correct your position to inside the ship + // Of course, this is messy... But still better than Save Point + if (!.@dead) { + if (compare(.@mapa$, "016-")) + warp "016-1", 28, 27; + else if (compare(.@mapa$, "002-")) + warp "002-1", 55, 40; + else + warp "Save", 0, 0; + } + } + // Crazyfefe hot fix + if (.@dead) { + // It was PK + if (killerrid > 2000000 && killerrid < 2100000) { + // PVP flag was off + /* + if (!getmapflag(.@mapa$, mf_pvp) && !getmapflag(.@mapa$, mf_pvp_noparty) && !getmapflag(.@mapa$, mf_pvpnoguild)) { + recovery(getcharid(3)); + warp .@mapa$, .@a, .@b; + percentheal 100, 100; + dispbottom l("REVENGE TIME!"); + .@trueid=getcharid(3); + //detachrid(); + attachrid(killerrid); + setpcblock(PCBLOCK_SOFT, true); + sc_start SC_WALKSPEED,120000,50; + sc_end SC_CASH_PLUSEXP; + sc_end SC_OVERLAPEXPUP; + sc_start SC_OVERLAPEXPUP, 300000, -20; + dispbottom l("For cowardingly killing in a \"secure\" area, you will be severely punished."); + //Karma+=1; + sc_start SC_STUN, 15000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK; + addtimer(15000, "#mobptsys::OnUnlock"); + percentheal -88, -100; + detachrid(); + attachrid(.@trueid); + } + */ + HONOR+=1; + } + } + + // This allows code to override death penalty, just once: + // @deathpenalty_override + // Valid values: 1- No penalty. 2- Halved penalty. + // You must also set: @deathpenalty_realvalue and @deathpenalty_realvaljob + if (@deathpenalty_override && .@dead) { + if (is_staff()) + debugmes("Old values: %d %d Current Values: %d %d", @deathpenalty_realvalue, @deathpenalty_realvaljob, readparam(BaseExp), readparam(JobExp)); + addtimer(300, "#QuirinoHUB::OnNoPenaltyCommand"); + } + + // Register logout time + if (!.@dead) { + CHAREG_CLEANUP=gettimetick(2); + // Send updates to Vault API + if (getvaultid()) { + .@api$=json_encode("UID", ##VAULT, + "GID", getcharid(3), + "VAR1N", "MLQUEST", + "VAR1V", ##02_MLQUEST, + "VAR2N", "MLWORLD", + "VAR2V", ##02_MLWORLD, + "VEXP", ##VAULT_EXP, + "GOTO", ##VAULT_GOTO, + "MLTO", ##VAULT_MLTO); + ##VAULT_EXP=0; + ##VAULT_GOTO=0; + ##VAULT_MLTO=0; + api_send(API_FLUSHVAULT, .@api$); + // Destroy temporary characters + if (countitem(MirrorLakeArmor)) { + delitem MirrorLakeArmor, countitem(MirrorLakeArmor); + //clearitem(); // Hm. + resetlvl(2); + resetstatus(); + resetskill(); + warp "000-0", 22, 24; + debugmes("Vault User %d reset!", getvaultid()); + } + } + } + + callfunc "02524_Avenge_BlackBox", .@dead; + return; +} + +// HUB_SkillInvoke ( ) +function script HUB_SkillInvoke { + // Something is... wrong + if (@skillId < 1) { + Exception("ILLEGAL SKILL PASSED TO HUB. It has been compromised, Jim.", RB_DEBUGMES|RB_IRCBROADCAST); + debugmes "Legal Caster: %s", strcharinfo(0); + debugmes "Effective Caster: %d", @skillCaster; + return; + } + + // Missing Skill Target - Don't ask me, something went wrong. + if (@skillTarget < 1) { + //@skillTarget = getcharid(3); + consolewarn("Skill %d has no target!", @skillId); + consolewarn("Skill Data - Name \"%s\" Level %d User \"%s\"", getskillname(@skillId), @skillLv, strcharinfo(0)); + } + + // Homunculus Cycle + /* *********************************************************************** */ + //debugmes "Skill caster %d", @skillCaster; + if (@skillCaster != getcharid(3)) { + switch (@skillId) { + case TMW2_SKILLX: + sc_start SC_RICHMANKIM, 180000, @skillLv+rand2(9*@skillLv), 10000, SCFLAG_NONE, @skillTarget; + gethomunexp @skillLv; + break; + case TMW2_HOMUN_HEAL: + .@heal=gethominfo(6)*(5+rand2(@skillLv)); + heal .@heal, 0; + harm(@skillCaster, -.@heal, HARM_MISC); + break; + case TMW2_LITTLE_WONDERS: + sc_end SC_POISON; + sc_end SC_CURSE; + sc_end SC_SILENCE; + sc_end SC_CONFUSION; + sc_end SC_BLIND; + sc_start SC_CURSE, rand2(3500, 5000), 1, 10000, SCFLAG_NONE, @skillCaster; + break; + case TMW2_CAPRICE: + .@gid = @skillCaster; + .@ak1 = max(getunitdata(.@gid, UDT_MATKMIN), getunitdata(.@gid, UDT_INT) * 4 / 3); + .@ak2 = max(getunitdata(.@gid, UDT_MATKMAX), .@ak1 * 12 / 10); + /* FIXME: Validate if mob is still alive after each hit */ + for (.@i=0; .@i < limit(1, @skillLv, 10); .@i++) { + .@dmg=rand2(.@ak1, .@ak2); + .@ele=any(Ele_Fire, Ele_Water, Ele_Wind, Ele_Earth); + harm(@skillTarget, .@dmg, HARM_MAGI, .@ele, .@gid); + sleep2(100); + } + break; + case TMW2_AVOID: + .@t = 44 - (@skillLv * 4); + .@p = 105+(@skillLv * 5); + SC_Bonus(.@t, SC_WALKSPEED, .@p); + sc_start SC_WALKSPEED, .@t * 1000, .@p, 10000, SCFLAG_NOAVOID, @skillCaster; + break; + case TMW2_HDEFENCE: + .@t = 43 - (@skillLv * 3); + .@p = 4+(@skillLv * 6); + SC_Bonus(.@t, SC_INCVIT, .@p); + sc_start SC_INCVIT, .@t * 1000, .@p, 10000, SCFLAG_NOAVOID, @skillCaster; + break; + case TMW2_BLOODLUST: + // SC_HAMI_BLOODLUST (val1, % ATK Bonus, % Drain Cth, % Drain Rate) + .@t = (50 + (@skillLv * 10)) * 1000; + .@atk = 10+(@skillLv * 10); + .@drc = 2 * @skillLv; + .@drn = 15 + @skillLv; + sc_start4 SC_HAMI_BLOODLUST, .@t, 100, .@atk, .@drc, .@drn, 10000, SCFLAG_NOAVOID, @skillCaster; + break; + case TMW2_MOON: + .@gid = @skillCaster; // gethominfo(0); + .@ak1 = max(getunitdata(.@gid, UDT_ATKMIN), getunitdata(.@gid, UDT_STR) * 3); + .@ak2 = max(getunitdata(.@gid, UDT_ATKMAX), .@ak1 * 12 / 10); + .@hit = 1 + (@skillLv / 2); + .@dmg = rand2(.@ak1, .@ak2) / .@hit; + .@dmg += .@dmg * @skillLv / 3; // +33% DMG per level + .@dmx = max(.@dmg * 5 / 4, .@dmg + 10); // ~ +25% DMG on crit + for (.@i=0; .@i < .@hit; .@i++) { + // 10% special "crit" chance + if (rand2(10) == 7) + harm(@skillTarget, .@dmx, HARM_MISC, Ele_Neutral, .@gid); + else + harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Neutral, .@gid); + sleep2(100); + } + break; + case TMW2_FLEET: + // SC_HLIF_FLEET (val1, % ASPD, % ATK) + .@t = (65 - (@skillLv * 5)) * 1000; + .@atk = 5+(@skillLv * 5); + .@asp = 3 * @skillLv; + sc_start2 SC_HLIF_FLEET, .@t, .@asp, .@atk, 10000, SCFLAG_NOAVOID, @skillCaster; + break; + case TMW2_SPEED: + // SC_INCFLEE (int flee) + .@t = (65 - (@skillLv * 5)) * 1000; + .@val = 15+(@skillLv * 10); + sc_start SC_INCFLEE, .@t, .@val, 10000, SCFLAG_NOAVOID, @skillCaster; + break; + case TMW2_POISONMIST: + // SC_BLIND + .@ak1 = max(getunitdata(.@gid, UDT_MATKMIN), getunitdata(.@gid, UDT_INT) * 4 / 3); + .@ak2 = max(getunitdata(.@gid, UDT_MATKMAX), .@ak1 * 12 / 10); + .@dmg = rand2(.@ak1, .@ak2) + gethominfo(6); + .@dmg += .@dmg * @skillLv * 2 / 3; // +67% DMG per level + .@dur = (3 + (@skillLv * 2)) * 1000; + .@cth = 2000 + (1000 * @skillLv); + + /* First strike (magic) */ + areaharm(@skillTarget, 7, .@dmg, HARM_MAGI, Ele_Earth); + areasc(7, .@dur, SC_BLIND, BL_MOB|BL_PC|BL_HOM|BL_MER, 1, "filter_hostile", @skillTarget, .@cth); + + sleep2(500); + + /* Delayed strike (physical) */ + areaharm(@skillTarget, 7, .@dmg, HARM_PHYS, Ele_Wind); + areasc(7, .@dur, SC_BLIND, BL_MOB|BL_PC|BL_HOM|BL_MER, 1, "filter_hostile", @skillTarget, .@cth); + break; + case TMW2_GOLDENFERSE: + // SC_GOLDENE_FERSE (val1, % MaxHP, % Aspd, % Holy Cth) + .@t = (50 + (@skillLv * 10)) * 1000; + .@mhp = @skillLv; + .@asp = 6 + (4 * @skillLv); + .@hol = 40 + (10 * @skillLv); + sc_start4 SC_GOLDENE_FERSE, .@t, .@mhp, .@asp, .@hol, 0, 10000, SCFLAG_NOAVOID, @skillCaster; + break; + case TMW2_STAHLHORN: + .@ak1 = max(getunitdata(.@gid, UDT_ATKMIN), getunitdata(.@gid, UDT_STR) * 3); + .@ak2 = max(getunitdata(.@gid, UDT_ATKMAX), .@ak1 * 12 / 10); + .@dmg = rand2(.@ak1, .@ak2) * 2; // 200% DMG + .@dmg += rand2(.@ak1, .@ak2) * @skillLv; // +100% per level + .@rng = 5 + (@skillLv / 2); + .@dur = 500 * @skillLv; + .@cth = 500 + (400 * @skillLv); // 5% + 4% per level (starts at 9%) + areaharm(@skillTarget, .@rng, .@dmg, HARM_MAGI, Ele_Holy); + areasc(.@rng, .@dur, SC_STUN, BL_MOB|BL_PC|BL_HOM|BL_MER, 1, "filter_hostile", @skillTarget, .@cth); + break; + } + return; + } + /* *********************************************************************** */ + + // TODO: Detect what was script-cast and what was player-case. Then, readd RB_IRCBROADCAST + // If you can't do this: You can't do this + if (getskilllv(@skillId) < @skillLv && @skillId != BS_GREED) { + // Is this ignorable? + if (@scriptsk) { + @scriptsk=false; + } else { + Exception("System ERROR, HSI."+@skillId+" INVALID CAST (got "+@skillLv+" expected "+getskilllv(@skillId)+", sub-LC."+(getcharid(3)-2000000)+")", RB_DEBUGMES|RB_ISFATAL); + } + } + // You are AFK for over 3 minutes, that's crazy, disregard + if (checkidle() > 180) + return; + + // Record to database + skillInvoke[@skillId] = skillInvoke[@skillId] + 1; + callfunc "FYE_Olympics_SK"; + + // Script-based skills + /* *********************************************************************** */ + switch (@skillId) { + case TMW2_FAKESKILL: + atcommand("@refresh"); + break; + case TMW2_FAKESKILL2: + CMD_toevent(); + break; + case TMW2_CRAFT: + UserCtrlPanel(); + break; + case TMW2_OVHFIRE: + SK_OVHFire(); + break; + case TMW2_MPREGEN: + SK_mpregen(); + break; + case TMW2_MPTRANSFER: + SK_transfermp(@skillTarget); + break; + case TMW2_STUDY: + SK_study(@skillTarget); + break; + case EVOL_AREA_PROVOKE: + if (@skillTargetX && @skillTargetY) + massprovoke(1+@skillLv, getmap(), @skillTargetX, @skillTargetY); + else + massprovoke(1+@skillLv); + // SC_PROVOKE ? + GetManaExp(@skillId, rand2(1,3)); + break; + case TMW2_GD_INCALL: + GD_allboost(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + case TMW2_GD_REGEN: + GD_regenerating(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + case TMW2_GD_DEFUP: + GD_defboost(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + case TMW2_GD_BATTLEPLAN: + GD_atkboost(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + case TMW2_GD_ATKUP: + GD_atkboost2(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + case TMW2_GD_CRITUP: + GD_critboost(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + case TMW2_GD_AUTOREVIVE: + GD_autorevive(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + case TMW2_GDP_MAXPOWER: + SK_maximizepower(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + case TMW2_GDP_SPREGEN: + SK_spregen(); + GetManaExp(GD_DEVELOPMENT, 10); + break; + // Weapon Overload attack + case TMW2_OVERLOAD: + .@PW=200+(@skillLv > 3 ? @skillLv : 0)+(@skillLv > 7 ? @skillLv*2 : 0); + areaharm(@skillTarget, 0, AdjustAttackpower(.@PW), HARM_MISC); + break; + //////////////////////////////// + // Magic v3 + // The basic offensive skill from Trickmaster + case TMW2_MANABOMB: + // This skill takes 100% mana for a 1:1 ratio damage + // And is a trick. Each level improves ratio in 1 + // Has no cooldown, so it is powerful with pots + // And is a good starter offensive skill + areaharm(@skillTarget, 0, Sp*@skillLv, HARM_MISC, Ele_Ghost); + Sp=0; + GetManaExp(@skillId, 1); + break; + //////////////////////////////// + // XXX: Healing Class + case TMW2_FIRSTAID: + .@PW=90+(10*@skillLv); + // First aid only works on you, so + .@heal=max(AdjustSpellpower(.@PW), AdjustAttackpower(.@PW)); + // Penalty if you have HALT_REGEN on (-80%) + if (getstatus(SC_HALT_REGENERATION)) + .@heal /= 5; + heal .@heal, 0; + GetManaExp(TMW2_HEALING, 1); + break; + case TMW2_HEALING: + .@PW=130+(20*@skillLv); + // Penalty if you have HALT_REGEN on and trying to self heal + if (getstatus(SC_HALT_REGENERATION) && @skillTarget == getcharid(3)) + .@heal /= 3; + harm(@skillTarget, -AdjustSpellpower(.@PW), HARM_MISC); + GetManaExp(TMW2_HEALING, 2); + break; + case TMW2_MAGNUSHEAL: + // Area healing + .@PW=200+(20*@skillLv); + .@RG=4+(@skillLv/5); + areaharm(@skillTarget, .@RG, -AdjustSpellpower(.@PW), HARM_MISC, "filter_friendly"); + GetManaExp(TMW2_HEALING, 3); + break; + //////////////////////////////// + // XXX: Revive Class + case TMW2_RESURRECT: + SK_resurrect(@skillLv, @skillTarget); + break; + //////////////////////////////// + // XXX: Fire Class + // (May burn targets for damage over time) + case TMW2_FIREARROW: + .@PW=140+(10*@skillLv); + // 4% chance, 2.5s + sc_start SC_BLOODING, 4500, 1, 400, SCFLAG_NONE, @skillTarget; + harm(@skillTarget, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); + GetManaExp(TMW2_FIREBALL, 1); + break; + case TMW2_FIREBALL: + .@PW=140+(10*@skillLv); + .@RG=2+(@skillLv/5); + // 22% chance, 2.5s + sc_start SC_BLOODING, 2500, 1, 4200, SCFLAG_NONE, @skillTarget; + areaharm(@skillTarget, .@RG, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); + GetManaExp(TMW2_FIREBALL, 2); + break; + case TMW2_ARMAGEDDON: + .@PW=140+(10*@skillLv); + .@RG=5+(@skillLv/5); + // 18% chance, 3s, 3x3 radius + areasc(.@RG, 6000, SC_BLOODING, BL_MOB|BL_PC|BL_HOM|BL_MER, 1, "filter_hostile", @skillTarget, 1800); + areaharm(@skillTarget, .@RG, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Fire); + GetManaExp(TMW2_FIREBALL, 3); + break; + //////////////////////////////// + // XXX: Holy Class + // (Single DPS + AOE) + case TMW2_NAPALMBEAT: + .@PW=35+(5*@skillLv); + .@dmg=AdjustSpellpower(.@PW); + .@RG=2+(@skillLv/3); + areaharm(@skillTarget, .@RG, .@dmg, HARM_MAGI, Ele_Holy); + harm(@skillTarget, .@dmg/10, HARM_MAGI, Ele_Holy); + GetManaExp(TMW2_HOLYLIGHT, 1); + break; + case TMW2_HOLYLIGHT: + .@PW=125+(25*@skillLv); + .@dmg=AdjustSpellpower(.@PW); + areaharm(@skillTarget, 1, .@dmg/5, HARM_MAGI, Ele_Holy); + harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Holy); + GetManaExp(TMW2_HOLYLIGHT, 2); + break; + case TMW2_JUDGMENT: + .@PW=250+(50*@skillLv); + .@SPW=60+(15*@skillLv); + .@dmg=AdjustSpellpower(.@PW); + .@dsub=AdjustSpellpower(.@SPW); + .@RG=3+(@skillLv/5); + areaharm(@skillTarget, .@RG, .@dsub, HARM_MAGI, Ele_Holy); + harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Holy); + GetManaExp(TMW2_HOLYLIGHT, 3); + break; + //////////////////////////////// + // XXX: Wind Class + // (Smaller cooldown than others) + case TMW2_MAGICSTRIKE: + .@PW=125+(25*@skillLv); + .@dmg=AdjustSpellpower(.@PW); + harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Wind); + GetManaExp(TMW2_LIGHTNINGBOLT, 1); + break; + case TMW2_LIGHTNINGBOLT: + .@PW=150+(50*@skillLv); + .@dmg=AdjustSpellpower(.@PW); + harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Wind); + GetManaExp(TMW2_LIGHTNINGBOLT, 2); + break; + case TMW2_TEMPEST: + .@PW=125+(25*@skillLv); + .@dmg=AdjustSpellpower(.@PW); + .@RG=2+(@skillLv/5); + areaharm(@skillTarget, .@RG, .@dmg, HARM_MAGI, Ele_Wind); + GetManaExp(TMW2_LIGHTNINGBOLT, 3); + break; + //////////////////////////////// + // XXX: Ice Class + // (May freeze the targets) + case TMW2_FROSTDIVER: + .@PW=80+(10*@skillLv); + // 22% chance, 2.5s + sc_start SC_FREEZE, 2500, 1, 2200, SCFLAG_NONE, @skillTarget; + harm(@skillTarget, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); + GetManaExp(TMW2_NILFHEIM, 1); + break; + case TMW2_FROSTNOVA: + .@PW=80+(10*@skillLv); + .@RG=2+(@skillLv/5); + // 18% chance, 3s, 3x3 radius + areasc(.@RG, 3000, SC_FREEZE, BL_MOB|BL_PC|BL_HOM|BL_MER, 1, "filter_hostile", @skillTarget, 1800); + areaharm(@skillTarget, .@RG, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); + GetManaExp(TMW2_NILFHEIM, 2); + break; + case TMW2_NILFHEIM: + // Nilfheim cast on self? + .@PW=80+(10*@skillLv); + .@RG=4+(@skillLv/5); + areasc(.@RG, 15000, SC_FREEZE, BL_PC | BL_MOB | BL_MER | BL_HOM, 1, "filter_hostile"); + areaharm(getcharid(3), .@RG*3/2, AdjustSpellpower(.@PW), HARM_MAGI, Ele_Water); + // Maybe filter_notme() would work better, indeed + GetManaExp(TMW2_NILFHEIM, 3); + break; + //////////////////////////////// + // XXX: Earth Class + // DEF Effects at Gaia Break, more expensive + case TMW2_METEORSTRIKE: + .@PW=130+(20*@skillLv); + .@dmg=AdjustSpellpower(.@PW); + .@TM=1200+(@skillLv*300); + sc_start SC_STUN, .@TM, 1, 800, SCFLAG_NONE, @skillTarget; + harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Earth); + GetManaExp(TMW2_METEORSTRIKE, 1); + break; + case TMW2_METEORSHOWER: + .@PW=130+(15*@skillLv); + .@dmg=AdjustSpellpower(.@PW); + .@RG=3+(@skillLv/5); + .@TM=800+(@skillLv*200); + areasc(.@RG, .@TM, SC_STUN, BL_MOB | BL_PC | BL_HOM | BL_MER, 1, "filter_hostile", @skillTarget, 800); + areaharm(@skillTarget, .@RG, .@dmg, HARM_MAGI, Ele_Earth); + GetManaExp(TMW2_METEORSTRIKE, 2); + break; + case TMW2_GAIABREAK: + .@PWA=170+(30*@skillLv); + .@PWB=110+(10*@skillLv); + .@dmg=AdjustSpellpower(.@PWA); + .@dsub=AdjustSpellpower(.@PWB); + areasc(2, 5000, SC_INCDEFRATE, BL_PC, 10, "filter_friendly"); + rectharm(@skillTarget, 2, 5, .@dsub, HARM_MAGI, Ele_Earth); + harm(@skillTarget, .@dmg, HARM_MAGI, Ele_Earth); + GetManaExp(TMW2_METEORSTRIKE, 3); + break; + /* + //////////////////////////////// + // XXX: Assassinate + // » Assassinate: Instantly kills a monster weaker than you. + // » Assassinated monsters give no experience. + // » You'll be stunned for 3 seconds as a penalty. + // » Does not work on boss. Base Success Chance is 100%. + case TMW2_ASSASSINATE: + .@lv = getunitdata(@skillTarget, UDT_LEVEL); + if ((.@lv+10-@skillLv) < (BaseLevel+(REBIRTH*2)) && filter_notboss(@skillTarget)) + sc_start SC_COMA, 90000, 1, 10000, SCFLAG_NONE, @skillTarget; + else + dispbottom l("Assassination failed - target is stronger than you"); + SC_Bonus(3, SC_STUN, 1); + break; + + // TODO: Ultimate Skills (T5/0) + // Sanctum: AoE resurrection, HP like Resurrect, 30min+ cooldown and + // inflict ailment on caster. + // [Ultimate] revives the whole map, like @raisemap + case TMW2_SANCTUM: + SK_sanctum(); + break; + + // Support magic + // STATUS: Awaiting for Alchemy rework + // TODO: Debuffs + // TODO: Buffs + + // XXX: Resurrection skills + // [MAPZONE] Need special care because scripts + // [WIP] Maybe a custom recovery and a flag @canRevive ? + // --------------------------------------------- + // Revive: Makes char alive with 5% HP/Lvl + // Resurrect: Makes char alive with ±80% HP and give a protection SC (lvl) + // Redemption: AoE resurrection (HP works like revive, though) + case TMW2_REVIVE: + SK_revive(@skillTarget); + break; + case TMW2_RESSURECT: + SK_ressurect(@skillLv); + break; + + + + + + // Physical Class - mostly builtin + // TODO: Archery effect-absorb skill + case TMW2_ARROWOFDEVOUR: + //.@x=getstatus(SC_POISON)+getstatus(SC_DPOISON); + // if SC_POISON or SC_DPOISON + break; + case TMW2_ARROWOFURGES: + // if SC_SILENCE or SC_FEAR + break; + case TMW2_ULTIMATEARROW: + // if SC_BLIND or SC_BLOODING + break; + case TMW2_EXECUTION: + // if SC_CURSE or SC_FEAR + break; + case TMW2_SHATTERARROW: + // if SC_STONE or SC_STUN + break; + case TMW2_ARROWOFADVANTAGE: + // IF SC_BURNING or SC_CONFUSION + break; + case TMW2_WAKENINGARROW: + // if SC_SLEEP or SC_DEEP_SLEEP + break; + case TMW2_BURNINGARROW: + // if SC_FREEZE or SC_COLD + break; + + + + case TMW2_SNARE: + // 100.00% inflict STUN/SLEEP/whatever + // Does not work on boss monsters + // (Maybe the one which makes next hit critical?) + // Was it SC_STONE? + break; + // Arrow pierce? (Maybe arrow shot disregarding defense, HARM_MISC ?) + + + case TMW2_RICHNESS: + // Passive, bonus2 bAddGetZenyNum, 5, 100; - Better not + break; + // TODO: Effect resisting passives? + // Windwalker... No wait. + // Immunity to knockback? Meeeeh. Easier to have a trait-giving quest. + // After all, we have about 20 traits and you can only pick 5. + + + // XXX: Sword skills are a 5-skills combo + // No cast time, but cooldown present + // DEF lowers as cast, damage based on combo. + case TMW2_HORIZONTALSLASH: + .@PW=100; + .@PW+=(5*@skillLv); + @SCombo=@skillTarget; + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + case TMW2_DIAGONALSLASH: + .@PW=100; + if (LAST_SKILL[0] == TMW2_HORIZONTALSLASH) + .@PW+=20; + if (.@PW >= 120) + .@PW+=30; + if (@SCombo != @skillTarget) + .@PW=100; + .@PW+=(5*@skillLv); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + case TMW2_VERTICALSLASH: + .@PW=100; + if (LAST_SKILL[0] == TMW2_DIAGONALSLASH) + .@PW+=30; + if (LAST_SKILL[1] == TMW2_HORIZONTALSLASH) + .@PW+=20; + if (.@PW >= 150) + .@PW+=100; + if (@SCombo != @skillTarget) + .@PW=100; + .@PW+=(5*@skillLv); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + case TMW2_STAB: + .@PW=100; + if (LAST_SKILL[0] == TMW2_VERTICALSLASH) + .@PW+=40; + if (LAST_SKILL[1] == TMW2_DIAGONALSLASH) + .@PW+=30; + if (LAST_SKILL[2] == TMW2_HORIZONTALSLASH) + .@PW+=20; + if (.@PW >= 190) + .@PW+=200; + if (@SCombo != @skillTarget) + .@PW=100; + .@PW+=(5*@skillLv); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + case TMW2_GRANDBLAST: + .@PW=100; + if (LAST_SKILL[0] == TMW2_STAB) + .@PW+=50; + if (LAST_SKILL[1] == TMW2_VERTICALSLASH) + .@PW+=40; + if (LAST_SKILL[2] == TMW2_DIAGONALSLASH) + .@PW+=30; + if (LAST_SKILL[3] == TMW2_HORIZONTALSLASH) + .@PW+=20; + if (.@PW >= 200) + .@PW+=300; + if (@SCombo != @skillTarget) + .@PW=100; + .@PW+=(5*@skillLv); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Holy); + GetManaExp(@skillId, rand2(2,3)); + break; + + */ + //////////////////////////////// + // XXX: Physical Class (Regular) + case TMW2_FALKONSTRIKE: + .@PW=100+(25*@skillLv); + .@ST=0+(10*@skillLv); + .@TM=100+(90*@skillLv); + sc_start SC_STUN, .@TM, 1, .@ST, SCFLAG_NONE, @skillTarget; + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + case TMW2_GROUNDSTRIKE: + .@PW=50+(40*@skillLv); + .@dmg=AdjustAttackpower(.@PW); + .@RG=2+(@skillLv/5); + .@TM=100+(@skillLv*200); + .@ST=500+(100*@skillLv); + .@EF=any(SC_STUN, SC_BLIND, SC_BLOODING, SC_BLIND, SC_BLOODING); + areasc(.@RG, .@TM, .@EF, BL_MOB | BL_PC | BL_HOM | BL_MER, 1, "filter_hostile", @skillTarget, .@ST); + areaharm(@skillTarget, .@RG, .@dmg, HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + case TMW2_SUPREMEATTACK: + .@PW=100+(50*@skillLv); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + //////////////////////////////// + // XXX: Physical Class (Archery) + case TMW2_CHARGEDARROW: + .@PW=100+(50*@skillLv); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + case TMW2_ARROWSHOWER: + .@PW=150+(10*@skillLv); + .@dmg=AdjustAttackpower(.@PW); + .@RG=1+(@skillLv/3); + areaharm(@skillTarget, .@RG, .@dmg, HARM_PHYS, Ele_Neutral); + GetManaExp(@skillId, rand2(1,3)); + break; + //////////////////////////////// + // XXX: Brawling Class + case TMW2_BRAWLING: + // 75x3 = 225 + .@PW=70+(5*@skillLv); + // Using a shield, so power is halved + if (getequipid(EQI_HAND_L) > 0) + .@PW=.@PW/2; + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + break; + case TMW2_BEARSTRIKE: + // 60x5 = 300 + .@PW=55+(5*@skillLv); + // Using a shield, so power is halved + if (getequipid(EQI_HAND_L) > 0) + .@PW=.@PW/2; + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + break; + case TMW2_ALLINONE: + // 45x8 = 360 + .@PW=40+(5*@skillLv); + // Using a shield, so power is halved + if (getequipid(EQI_HAND_L) > 0) + .@PW=.@PW/2; + //harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Fire); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Water); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Earth); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Wind); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Holy); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Shadow); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Ghost); + sleep2(10); + // The main elemental-less blast hits all in same square, + // and also hits behind (and on your square) + rectharm(@skillTarget, 0, 1, AdjustAttackpower(.@PW/2), HARM_PHYS, Ele_Neutral); // FIXME: May not fire (properly) if target dies + break; + case TMW2_STUNNINGSTRIKE: + // 70x3 = 210 + .@PW=65+(5*@skillLv); + .@TM=1600+(@skillLv*200); + // Using a shield, so power is halved + if (getequipid(EQI_HAND_L) > 0) + .@PW=.@PW/2; + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sleep2(10); + harm(@skillTarget, AdjustAttackpower(.@PW), HARM_PHYS, Ele_Neutral); + sc_start SC_STUN, .@TM, 1, 3333, SCFLAG_NONE, @skillTarget; + break; + + //////////////////////////////// + // CLASS_OTHER + case TMW2_PARUM: + SK_parum(); + break; + case TMW2_DEMURE: + SK_Demure(); + break; + case TMW2_DRAGOKIN: + SK_Dragokin(); + break; + // Summons which never fail + case TMW2_ZARKOR: + alignment_cansummon(); + SummonMagic(@skillId, CaveMaggot, 2, @skillLv); + GetManaExp(@skillId, 1); + break; + // Summons which may fail + case TMW2_KALWULF: + SK_summon(Wolvern, 4, any(3,4)); + break; + case TMW2_KALBOO: + SK_summon(Mouboo, 4, any(2,3)); + break; + case TMW2_KALSPIKE: + SK_summon(PoisonSpikyMushroom, 4, any(2,3)); + break; + case TMW2_CUTEHEART: + SK_summon(Fluffy, 4, any(2,3)); + break; + // Slightly more complex summons + case TMW2_LIMERIZER: + SK_summon(any(GreenSlime,AzulSlime,RedSlime,AngryYellowSlime), 2, any(3,4)); + break; + case TMW2_FAIRYKINGDOM: + SK_summon(any(FireFairy, EarthFairy, WaterFairy, WindFairy, PoisonFairy), 4, any(3,4)); + break; + case TMW2_FAIRYEMPIRE: + SK_summon(any(VanityPixie, HolyPixie, ShadowPixie, NulityPixie), 5, any(4,5)); + break; + // More complex summons + case TMW2_KALMURK: + .@mobId=Maggot; + if (abizit() > 4 && + GHMEMO[GHQ_GetQuestIDByMonsterID(Maggot)] >= 10000 && + MAGIC_LVL >= 3) + { + .@mobId=any(Maggot, Maggot, Maggot, Maggot, GiantMaggot); + } + SK_summon(.@mobId, (.@mobId == Maggot ? 2 : 4), any(1,2)); + break; + case TMW2_HALHISS: + .@mobId=Snake; + if (abizit() > 3 && + GHMEMO[GHQ_GetQuestIDByMonsterID(MountainSnake)] >= 10000 && + rand2(1,3) == 2) + { + .@mobId=MountainSnake; + } + SK_summon(.@mobId, 4, any(3,4)); + break; + case TMW2_FROZENHEART: + .@mobId=Moggun; + if (rand2(6,12) < (abizit()*2)+1) + { + .@mobId=Yeti; + } + SK_summon(.@mobId, 4, any(3,4)); + break; + case TMW2_STONEHEART: + .@mobId=Terranite; + if (rand2(9,12) < (abizit()*2)+1 && + BaseLevel > 80) + { + .@mobId=TerraniteProtector; + } + SK_summon(.@mobId, 4, any(4,5)); + break; + case TMW2_DUCKY: + .@mobId=Duck; + .@q=getq(LilitQuest_PiratesOfSARAH); + if (!alignment_cansummon()) + break; + if (abizit() > 4 && + .@q > 2 && + MAGIC_LVL >= 3) + { + // GHQ Complete: 33% chances + // Otherwise: 8% chances + if (GHMEMO[GHQ_GetQuestIDByMonsterID(Duck)] >= 10000) + .@mobId=any(Duck, Duck, EliteDuck); + else + .@mobId=any(Duck, Duck, Duck, Duck, + Duck, Duck, Duck, Duck, + Duck, Duck, Duck, EliteDuck); + } + SummonMagic(@skillId, .@mobId, 2); + GetManaExp(@skillId, 1); + break; + + // Special exception + case TMW2_TRANSMIGRATION: + doevent("sk#mkpot::OnCall"); + break; + + // Experience only + case KN_AUTOCOUNTER: + case SN_SHARPSHOOTING: + case HW_MAGICPOWER: + case SM_PROVOKE: + case SN_WINDWALK: + case SO_FIREWALK: + case TF_BACKSLIDING: + case MG_FIREWALL: + case ALL_FULL_THROTTLE: + case GC_DARKILLUSION: + case NV_TRICKDEAD: + GetManaExp(@skillId, rand2(1,3)); + break; + } + + // Debug + if ($@GM_OVERRIDE) + debugmes "Cast skill %d on level %d - Target %d", + @skillId, @skillLv, @skillTarget; + + // Cleanup (double-safe) + @skillTarget = 0; + return; + +} + + + + +// When you kill a player, some special care is needed +// Only a few maps will give you experience for PK: Tulimshar's Guards Arena, +// Frostia Imperial PVP Arena, Call Of Dusty, Arena Quirino Voraz. +// HUB_PvP ( ) - killedrid must be set +function script HUB_PvP { + // Update global PK count + $PLAYERS_KILLED+=1; + + // Prepare local variables + .@atk=get_BR(getcharid(3)); + .@def=get_BR(killedrid); + .@honor=calc_HR(getcharid(3), killedrid); + .@bxp=max(readparam(BaseLevel, killedrid), .@def); + .@jxp=readparam(JobLevel, killedrid); + .@m$=getmap(); + .@gm=getgroupid(killedrid); + + // This is an official PVP Map + if (ispvpmap(.@m$)) { + // Honorable Death + if (.@honor >= 0) { + HONOR+=.@honor; + } else { + // Dishonorable... But... Legit? + if (is_bandit(killedrid) || getmap() == "001-8") + .@honor=1; + HONOR+=.@honor; // It's negative. + } + + // PvP, and a GM was slain + if (.@gm >= 80) { + rentitem MurdererCrown, (86400*7); // 1 week + kamibroadcast(strcharinfo(0)+" killed Game Master \""+strcharinfo(0, "", killedrid)+"\", and now may wear a "+getitemlink(MurdererCrown)+" for a week."); + } + + // It was a duel! + } else { + // Honorable Duel: HONOR +30% + if (.@honor > 0) + HONOR+=max(1, .@honor*3/10); + else if (.@honor < 0) + HONOR+=1; + // ^ Dishonorable duel, but was a duel! + } + + // Report about honor + dispbottom l("%d vs %d: Honor (%d)", .@atk, .@def, .@honor); + + // TODO: Start using readparam2() to read if the opponent was worthy + // That is, read total attack, defense, HP, evasion and hit chance + // And compare with your own readparam2(), then use a % and a table + // based on your (assassin's) level. + if (compare(.@m$, "001-8")) { + // Quirino Voraz PVP Arena + // You get 5 times killed player level, and 1 time job level + getexp .@bxp*5, .@jxp; + } else if (compare(.@m$, "ARENA") || compare(.@m$, "003-13")) { + // Tulimshar Duel Arena + // You get 3 times killed player level, and 2 times job level + getexp .@bxp*3, .@jxp*2; + } else if (compare(.@m$, "001-10")) { + // Call Of Dusty + // You get 3 times killed player level, and 3 times job level + getexp .@bxp*3, .@jxp*3; + } else if (compare(.@m$, "001-10-1")) { + // Call Of Dusty Boss Room + // You _may_ get a Bottled Dusty at random, but dead player status affect + if (.@def > .@atk/10) { + if (rand2(0,250) < readparam2(bLuk)+readparam2(bLuk, killedrid)) + getitem BottledDust, any(1,1,2); + } + } else { + // Anywhere else + // You get 0.5 times killed player level, and 0 times job level + getexp (.@bxp/2), 0; + } + return; +} + + + +// HUB_PCBonus () +function script HUB_PCBonus { + /* Rebirth Passives */ + if (REBIRTH) + bonus bAllStats, REBIRTH; + + /* Rebirth Traits */ + if (PCBONUS & PCB_ATKBONUS) { + bonus bAtk, 25; + } + if (PCBONUS & PCB_MATKBONUS) { + bonus bMatk, 25; + } + if (PCBONUS & PCB_DEFBONUS) { + bonus bDef, 20; + } + if (PCBONUS & PCB_MDEFBONUS) { + bonus bMdef, 20; + } + if (PCBONUS & PCB_EVDBONUS) { + bonus bFlee, 20; + } + if (PCBONUS & PCB_HITBONUS) { + bonus bHit, 25; + } + if (PCBONUS & PCB_CRITBONUS) { + bonus bCritical, 5; + } + if (PCBONUS & PCB_DOUBLEATK) { + bonus bDoubleAddRate, 5; + } + if (PCBONUS & PCB_ALLSTATS) { + bonus bAllStats, 1; + } + if (PCBONUS & PCB_HPBONUS) { + bonus bMaxHP, 500; + } + if (PCBONUS & PCB_MPBONUS) { + bonus bMaxSP, 200; + } + if (PCBONUS & PCB_ASPDBONUS) { + bonus bAspd, 10; + } + if (PCBONUS & PCB_WSPDBONUS) { + bonus bSpeedAddRate, 5; + } + if (PCBONUS & PCB_WEIGHTBONUS) { + bonus bAddMaxWeight, 1000; + } + if (PCBONUS & PCB_EXPBONUS) { + bonus2 bExpAddRace, RC_All, 10; + } + if (PCBONUS & PCB_NOKNOCKBACK) { + bonus bNoKnockback, 1; + } + if (PCBONUS & PCB_SPLASHMASTER) { + bonus bSplashRange, 1; + } + if (PCBONUS & PCB_RANGEMASTER) { + bonus bAtkRange, 1; + } + if (Class != Savior && !(PCBONUS & PCB_LEGENDARY)) { + bonus2 bCriticalAddRace, RC_Legendary, -25; + bonus2 bSPDrainValueRace, RC_Legendary, -5; + bonus2 bAddRace, RC_Legendary, -40; + bonus2 bMagicAddRace, RC_Legendary, -20; + } + if (PCBONUS & PCB_LEGENDARY) { + bonus bDefRatioAtkRace, RC_Legendary; + } + /* Passive Skills */ + bonus2 bSubRace, RC_Legendary, getskilllv(AL_DP)-10; + if (getq(LoFQuest_Barbara) >= 4 || getq(General_Narrator) >= 19) + bonus bMaxSP, 40; + if (getq(HalinarzoQuest_Speed) > 1) + bonus bSpeedAddRate, min(getq(HalinarzoQuest_Speed)-1, 10); + + return; +} + diff --git a/npc/functions/input.txt b/npc/functions/input.txt new file mode 100644 index 0000000..0a510b7 --- /dev/null +++ b/npc/functions/input.txt @@ -0,0 +1,110 @@ +// Evol functions. +// Author: +// 4144 +// Jesusalva +// Description: +// Input utility functions +// Variables: +// none + +function script menuint { + deletearray .@vals; + .@menustr$ = ""; + .@cnt = 0; + + for (.@f = 0; .@f < getargcount(); .@f = .@f + 2) + { + if (getarg(.@f) != "") + { + .@menustr$ = .@menustr$ + getarg(.@f) + ":"; + .@vals[.@cnt] = getarg(.@f + 1); + .@cnt ++; + } + } + + .@vals[.@cnt] = -1; + @menu = 255; + @menuret = -1; + select(.@menustr$); + if (@menu == 255) + return -1; + + @menu --; + if (@menu < 0 || @menu >= getarraysize(.@vals) - 1) + return -1; + + @menuret = .@vals[@menu]; + return @menuret; +} + +function script menustr { + deletearray .@vals$; + .@menustr$ = ""; + .@cnt = 0; + + for (.@f = 0; .@f < getargcount(); .@f = .@f + 2) + { + if (getarg(.@f) != "") + { + .@menustr$ = .@menustr$ + getarg(.@f) + ":"; + .@vals$[.@cnt] = getarg(.@f + 1); + .@cnt ++; + } + } + + @menu = 255; + @menuret = -1; + select(.@menustr$); + if (@menu == 255) + return ""; + + @menu --; + if (@menu < 0 || @menu >= getarraysize(.@vals$)) + return ""; + + @menuret$ = .@vals$[@menu]; + return @menuret$; +} + +// menuint2(<array>) +function script menuint2 { + .@menustr$=""; + + if (!(getdatatype(getarg(0)) & DATATYPE_VAR)) + Exception("Inadequate argument type - Must be var", RB_DEFAULT|RB_ISFATAL); + + copyarray(.@ar$, getarg(0), getarraysize(getarg(0))); + + if (getarraysize(.@ar$) % 2 != 0) + Exception("Invalid array size: "+getarraysize(.@ar$), RB_DEFAULT|RB_ISFATAL); + + freeloop(true); + for (.@f=0; .@f < getarraysize(.@ar$); .@f++) { + // String vs Int + if (.@f % 2 == 0) { + .@menustr$+=.@ar$[.@f]+":"; + } else { + array_push(.@vals, atoi(.@ar$[.@f])); + } + } + freeloop(false); + + // Do the request + // We have: .@vals and .@menustr$ + @menu = 255; + @menuret = -1; + select(.@menustr$); + //debugmes "Option %d", @menu; + //debugmes "Array size %d", getarraysize(.@vals); + + if (@menu == 255) + return -1; + + @menu-=1; + if (@menu < 0 || @menu > getarraysize(.@vals) - 1) + return -1; + + @menuret = .@vals[@menu]; + return @menuret; +} + diff --git a/npc/functions/instances.txt b/npc/functions/instances.txt new file mode 100644 index 0000000..ed11167 --- /dev/null +++ b/npc/functions/instances.txt @@ -0,0 +1,105 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Controls global instances and reset them when they break + +- script #GlobalInstanceCore NPC_HIDDEN,{ +OnInit: + .CANDOR_INSTID=-1; + end; + +OnInterIfInit: +OnInstRestart: + /* Nard's Ship */ + // Candor Instance (we do all tests for debugmes here) + .CANDOR_INSTID = instance_create("002-1@CandorInst", 0, IOT_NONE); + if (.CANDOR_INSTID < 0) { + Exception("CANDY Instance ID failed", RB_DEBUGMES|RB_IRCBROADCAST); + } else { + //debugmes("created new instance id: %s", str(.CANDOR_INSTID)); + .CANDOR1$=instance_attachmap("002-1", .CANDOR_INSTID, 0, "002-1@Candor"); + if (.CANDOR1$ == "") Exception("Map 002-1 CANDY failed", RB_DEBUGMES|RB_IRCBROADCAST); + + .CANDOR3$=instance_attachmap("002-3", .CANDOR_INSTID, 0, "002-3@Candor"); + if (.CANDOR3$ == "") Exception("Map 002-3 CANDY failed", RB_DEBUGMES|RB_IRCBROADCAST); + + debugmes "Nard's Ship in Candor instance is set."; + instance_set_timeout(0, 0, .CANDOR_INSTID); + instance_init(.CANDOR_INSTID); + } + + // Tulim Instance + .TULIM_INSTID = instance_create("002-1@TulimInst", 0, IOT_NONE); + instance_attachmap("002-1", .TULIM_INSTID, 0, "002-1@Tulim"); + instance_attachmap("002-3", .TULIM_INSTID, 0, "002-3@Tulim"); + instance_set_timeout(0, 0, .TULIM_INSTID); + instance_init(.TULIM_INSTID); + + // Artis Instance + .ARTIS_INSTID = instance_create("002-1@ArtisInst", 0, IOT_NONE); + instance_attachmap("002-1", .ARTIS_INSTID, 0, "002-1@Artis"); + instance_attachmap("002-3", .ARTIS_INSTID, 0, "002-3@Artis"); + instance_set_timeout(0, 0, .ARTIS_INSTID); + instance_init(.ARTIS_INSTID); + + + /* La Marine's Ship */ + // Tulim Instance + .TULIM_INSTID = instance_create("016-1@TulimInst", 0, IOT_NONE); + instance_attachmap("016-1", .TULIM_INSTID, 0, "016-1@Tulim"); + instance_set_timeout(0, 0, .TULIM_INSTID); + instance_init(.TULIM_INSTID); + + // Hurns Instance + .HURNS_INSTID = instance_create("016-1@HurnsInst", 0, IOT_NONE); + instance_attachmap("016-1", .HURNS_INSTID, 0, "016-1@Hurns"); + instance_set_timeout(0, 0, .HURNS_INSTID); + instance_init(.HURNS_INSTID); + + // Nivalis Instance + .NIVAL_INSTID = instance_create("016-1@NivalInst", 0, IOT_NONE); + instance_attachmap("016-1", .NIVAL_INSTID, 0, "016-1@Nival"); + instance_set_timeout(0, 0, .NIVAL_INSTID); + instance_init(.NIVAL_INSTID); + + + + /* Heroes Hold Main Dungeon */ + $@HHMD_INSTID1 = instance_create("018-2-2@HHMD1", 0, IOT_NONE); + instance_attachmap("018-2-2", $@HHMD_INSTID1, 0, "018-2-2@No"); + instance_set_timeout(0, 0, $@HHMD_INSTID1); + instance_init($@HHMD_INSTID1); + + $@HHMD_INSTID2 = instance_create("018-2-3@HHMD2", 0, IOT_NONE); + instance_attachmap("018-2-3", $@HHMD_INSTID2, 0, "018-2-3@In"); + instance_set_timeout(0, 0, $@HHMD_INSTID2); + instance_init($@HHMD_INSTID2); + + $@HHMD_INSTID4 = instance_create("018-2-2@HHMD4", 0, IOT_NONE); + instance_attachmap("018-2-2", $@HHMD_INSTID4, 0, "018-2-2@Ad"); + instance_set_timeout(0, 0, $@HHMD_INSTID4); + instance_init($@HHMD_INSTID4); + + $@HHMD_INSTID8 = instance_create("018-2-3@HHMD8", 0, IOT_NONE); + instance_attachmap("018-2-3", $@HHMD_INSTID8, 0, "018-2-3@Ex"); + instance_set_timeout(0, 0, $@HHMD_INSTID8); + instance_init($@HHMD_INSTID8); + + $@HHMD_INSTID16 = instance_create("018-2-5@HHMD16", 0, IOT_NONE); + instance_attachmap("018-2-5", $@HHMD_INSTID16, 0, "018-2-5@Ma"); + instance_set_timeout(0, 0, $@HHMD_INSTID16); + instance_init($@HHMD_INSTID16); + + debugmes("The HHMD level IDs are: %d-%d-%d-%d-%d", $@HHMD_INSTID1, $@HHMD_INSTID2, $@HHMD_INSTID4, $@HHMD_INSTID8, $@HHMD_INSTID16); + donpcevent "#HH_CONTROLLER01::OnHHInit"; + end; + +OnMinute18: +OnMinute47: + if (!isinstance(.HURNS_INSTID) || !isinstance(.NIVAL_INSTID) || !isinstance($@HHMD_INSTID1)) + donpcevent("#GlobalInstanceCore::OnInstRestart"); + end; + +} + diff --git a/npc/functions/inventoryplace.txt b/npc/functions/inventoryplace.txt new file mode 100644 index 0000000..76cdad2 --- /dev/null +++ b/npc/functions/inventoryplace.txt @@ -0,0 +1,36 @@ +// Evol functions. +// Authors: +// Qwerty Dragon +// Reid +// Description: +// Check if the player have enough place on his inventory to accept new items with arguments: +// getarg(even numbers) item ID, +// getarg(odd numbers) number of items, + +function script inventoryplace { + + .@argc = getargcount(); + + if (.@argc % 2 != 0) + { + Exception("inventoryplace: Wrong argument count.", RB_SPEECH|RB_ISFATAL|RB_PLEASEREPORT|RB_DEBUGMES); + } + + for (.@i = .@j = 0; .@i < .@argc; .@i += 2) + { + setarray .@item[.@j], getarg(.@i); + setarray .@amount[.@j], getarg(.@i + 1); + ++.@j; + } + + if (!checkweight2(.@item, .@amount)) + { + narrator S_FIRST_BLANK_LINE, + l("It looks like you can't carry anything else for now."), + l("You should come back when you have some free space."); + + close; + } + + return true; +} diff --git a/npc/functions/lockpicks.txt b/npc/functions/lockpicks.txt new file mode 100644 index 0000000..3a27d47 --- /dev/null +++ b/npc/functions/lockpicks.txt @@ -0,0 +1,176 @@ +// TMW2/LoF Script +// Author: +// Jesusalva +// Description: +// Lockpicking core + +// Important variables: +// THIEF_EXP +// Experience on Thief Tree +// THIEF_RANK +// Position on the Thief Tree + +// LockPicking(num_pins, max_pins, min_rank=num_pins) +// Returns 0 upon failure, 1 upon success +// Closes script if an error happen or if you give up / cannot try. +// +// The 'next' is upon script responsability +// Maximum pin number is infinite. Maximum Pin Positiors range from 2~5. +// If you fail, you can end up having to start again. If you fail too much, +// you'll be caught! +function script LockPicking { + // If you don't have a LockPick, you can't do this (useless) + if (!countitem(Lockpicks)) { + mesc l("You need a @@ to try this.", getitemlink(Lockpicks)), 1; + close; + } + + .@d=getarg(0,1); + .@m=getarg(1,3); + .@minrank=getarg(2, .@d); + + // Invalid Argument (kill script) + if (.@d < 1 || .@m < 2 || .@m > 5) + end; + + // You must be rank (number of locks - 1) to try + if (THIEF_RANK+1 < .@minrank) { + mesc l("This lock is beyond your current capacity."), 1; + close; + } + + // Create @pins array (the answer) + for (.@i=0; .@i < .@d;.@i++) + @pins[.@i] = rand2(1,.@m); + + // Check if you'll try to open it. + mesc l("This lock is simple, maybe with your thief skills you can manage to pry it open. But beware, you can end up in jail!"); + mesc l("Will you try to unlock it?"); + if (askyesno() == ASK_NO) + close; + + // Setup your attempt + delitem Lockpicks, 1; + @pos=0; + @chance=min(.@d*.@m-1, THIEF_RANK+.@d); + mesc l("You insert the hook pick inside the lock, and, without applying any tension, you discover there are only @@ pins to set.", .@d); + + // You have as many attempts as pins and appliable strenghts. + // Each thief rank grants you an extra attempt. + // Each pin takes one attempt. + // It's not multiplied, so 3 pins with 3 positions: 6 chances, 9 possibilities. + // There's no penalty, but the attempt is counted working or not! + // Remember if you fail, all previous pins will be cleared (@pos) + do { + mesc l("You are trying to open the @@th pin. What will to do?", @pos+1); + + menuint + rif(.@m >= 4, l("Apply no pressure")), 4, + rif(.@m >= 2, l("Apply soft pressure")), 2, + rif(.@m >= 1, l("Apply normal pressure")), 1, + rif(.@m >= 3, l("Apply strong pressure")), 3, + rif(.@m >= 5, l("Apply very strong pressure")), 5, + rif($@GM_OVERRIDE, "-- skip minigame --"), -1, + l("Give up!"), 0; + + // Debug + if (@menuret < 0) { + if ($@GM_OVERRIDE) + return 1; + else + atcommand("@block "+strcharinfo(0)); // Unacceptable + } + + if (!@menuret) { + // 25% chance to save the lockpick + if (rand2(1,4) == 2) + getitem Lockpicks, 1; + else + dispbottom l("The lockpick broke."); + close; + } + + // Is your guess correct? + if (@pins[@pos] == @menuret) { + mesc l("*click*"); + @pos+=1; + } else { + mesc l("This didn't work. All pins are now unset!"); + @pos=0; + @chance-=1; + // We don't need to clear console, each successful attempt IS counted. + // Therefore, unsetting 3 pins means you must do 3 new attempts!! + // The biggie is that you're running against time, here!!! + if (@chance < .@d && rand2(0, THIEF_RANK)) + mesc l("Your thief instincts suggest you to hurry."), 1; + } + + if (@chance <= 0) + break; + + if (@pos >= .@d) { + // 20% chance to save the lockpick + if (rand2(1,5) == 3) + getitem Lockpicks, 1; + else + dispbottom l("The lockpick broke."); + + // Get EXP and inform the success + if (THIEF_RANK) + THIEF_EXP += max(0, .@d*.@m-THIEF_RANK); + return 1; + } + } while (true); + + // Failed + if (THIEF_RANK) + THIEF_EXP += 1; + return 0; +} + +// Script helper to say if you were arrested or not +function script ArrestedChances { + .@runaway=limit(0, readparam2(bLuk)+readparam2(bAgi), 200); // 20% + .@runaway+=is_night()*125; // 12.5% + .@runaway+=limit(0, THIEF_RANK*15, 100); // real max 7.5% + // Max runaway chance: 40% + if (rand2(1000) < .@runaway) + return false; + return true; +} + +// Main script +// LootableVault(tier, level, variable) +function script LootableVault { + .@tier=getarg(0)+1; + .@level=getarg(1); + .@var$=getarg(2); + mesn; + mesq l("There's a shiny safe here. How much money is inside? Nobody is looking at you, great!"); + // 2*3 = 6 possibilities, 5 attempts + if (LockPicking(.@tier, .@level)) { + Zeny=Zeny+getd("$VAULT_"+.@var$); + setd("$VAULT_"+.@var$, 40); + mesn; + mesq l("Booty!"); + } else { + mesn; + .@inch=(Zeny/100); + Zeny-=.@inch; + setd("$VAULT_"+.@var$, getd("$VAULT_"+.@var$)+.@inch); + if (ArrestedChances()) { + mesc l("Arrested!"); + atcommand("@jailfor 5mn "+strcharinfo(0)); + } else { + if (is_night()) + .@p$=l("The darkness of night gives you cover."); + else + .@p$=l("Your agile legs and sheer luck allows you to outrun the cops."); + mesc l("You run as far as you could. %s", .@p$); + warp "000-1", 22, 22; + } + } + return; +} + + diff --git a/npc/functions/main.txt b/npc/functions/main.txt new file mode 100644 index 0000000..a099916 --- /dev/null +++ b/npc/functions/main.txt @@ -0,0 +1,536 @@ +// TMW2 Script +// Evol functions. +// Authors: +// 4144 +// Travolta +// gumi +// Jesusalva +// Description: +// Built-in essential functions. + +function script menuimage { + return getarg(0) + "|" + getarg(1); +} + +function script dnext { + if (@dnext >= GSET_LONGMENU_DENSITY) { + @dnext=0; + next; + } else { + @dnext+=1; + } + return; +} + +function script menuaction { + return "[" + getarg(0) + "]"; +} + +function script setq1 { + // Quest, val1 , val2 , val3 , time + setq getarg(0), getarg(1), getq2(getarg(0)), getq3(getarg(0)), getqtime(getarg(0)); + return; +} + +function script setq2 { + // Quest, val1 , val2 , val3 , time + setq getarg(0), getq(getarg(0)), getarg(1), getq3(getarg(0)), getqtime(getarg(0)); + return; +} + +function script setq3 { + // Quest, val1 , val2 , val3 , time + setq getarg(0), getq(getarg(0)), getq2(getarg(0)), getarg(1), getqtime(getarg(0)); + return; +} + +function script setqtime { + // Quest, val1 , val2 , val3 , time + setq getarg(0), getq(getarg(0)), getq2(getarg(0)), getq3(getarg(0)), getarg(1); + return; +} + +function script mesn { + if (getargcount() > 0) { + .@s$ = "[" + getarg(0) + "]"; + } else { + .@s$ = "[" + strnpcinfo(1) + "]"; + } + mes .@s$; + return; +} + +function script mesq { + mes "\"" + getarg(0)+ "\""; + return; +} + +function script g { + return Sex == 0 ? getarg(0) : getarg(1); +} + +function script b { + return "##B" + getarg(0) + "##b"; +} + +function script col { + .@color = getarg(1,9); + if (.@color < 0) .@color = 0; + if (.@color > 9) .@color = 9; + return "##" + .@color + getarg(0) + "##0"; +} + +function script adddefaultskills { + if (getskilllv(NV_BASIC) < 6) { + skill NV_BASIC, 6, 0; + } + if (getskilllv(TMW2_FAKESKILL) < 1) { + skill TMW2_FAKESKILL, 1, 0; + } + if (getskilllv(TMW2_FAKESKILL2) < 1) { + skill TMW2_FAKESKILL2, 1, 0; + } + if (getskilllv(TMW2_DROPS) < REBIRTH+1) { + skill TMW2_DROPS, (REBIRTH+1), 0; + } + return; +} + +function script addremovemapmask { + setmapmask getarg(0), (getmapmask(getarg(0)) | (getarg(1) + getarg(2))) ^ getarg(2); + return; +} + +function script mesc { + mes col(getarg(0),getarg(1,9)); + return; +} + +function script get_race { + .@g=getarg(0, Class); + return l($@allraces$[.@g]); +} + +// tutmes (message, {header=Tutorial, headerfirst=True}) +function script tutmes { + .@header$=getarg(1, l("TUTORIAL")); + .@showheader=getarg(2, true); + .@tcol=9; // Tutorial color code + + if (TUTORIAL) { + dnext; + if (.@showheader) { + mesf(".:: %s ::.", .@header$); + mesc getarg(0), .@tcol; + } else { + mesc .@header$+": "+getarg(0), .@tcol; + } + } + return; +} + +// Function to show narrator text. Accepts string args. +// If first arg is a number N, then it represents bit flags. +// Bit flags : +// 0x1 -- blank line at beginning +// 0x2 -- blank line at the end +// 0x4 -- use last "next;" +// 0x8 -- don't use first "mesn;" +function script narrator { + .@start = 0; + .@argc = getargcount(); + .@flags = 0; + + if (.@argc > 1 && !isstr(getarg(0))) + { + .@start = 1; + .@flags = getarg(0); + } + + if (.@flags & 0x1) + mes ""; + + if (!(.@flags & 0x8)) + mesn l("Narrator"); + + for (.@i = .@start; .@i < .@argc; .@i++) + { + if (getarg(.@i) == "") continue; + + mes col(getarg(.@i), 9); + if (.@i < .@argc - 1) + dnext; + } + + if (.@flags & 0x4) + dnext; + else if (.@flags & 0x2) + mes ""; + + return; +} + +// Function to show NPC speech. Accepts string args. +// If first arg is a number N, then it represents bit flags. +// Bit flags : +// 0x1 -- blank line at beginning +// 0x2 -- blank line at the end +// 0x4 -- use last "next;" +// 0x8 -- don't use first "mesn;" +function script speech { + .@start = 0; + .@argc = getargcount(); + .@flags = 0; + + if (.@argc > 1 && !isstr(getarg(0))) + { + .@start = 1; + .@flags = getarg(0); + } + + if (.@flags & 0x1) + mes ""; + + if (!(.@flags & 0x8)) + mesn; + + for (.@i = .@start; .@i < .@argc; .@i++) + { + mesq getarg(.@i); + + if (.@i < .@argc - 1) + dnext; + } + + if (.@flags & 0x4) + dnext; + else if (.@flags & 0x2) + mes ""; + + return; +} + +// Show debug message if .debug variable of NPC is set to 1 +function script npcdebug { + if (getvariableofnpc(.debug, strnpcinfo(3))) + debugmes strnpcinfo(3) + ": " + getarg(0); + return; +} + +function script askyesno { + return select(menuaction(l("Yes")), + menuaction(l("No"))); +} + +// Argument: +// 0 Quest variable +// 1 Current value +// 2 Next value +function script compareandsetq { + if (getq(getarg(0)) == getarg(1)) + { + setq getarg(0), getarg(2); + return true; + } + return false; +} + +// Use a delay to prevent spams from NPC that display text without the +// use of (a) close/next function(s). +// Argument: +// 0 Text to display +// 1 Lock delay (default = 1) +// 2 Message function: (default = 0) +// 0 = npctalk3 +// 1 = npctalk +// 2 = message +// TODO: Use temp player var, because NPC var affect other players +function script npctalkonce { + // lock mechanism + switch (getarg(2, 0)) + { + case 1: + if (gettimetick(2) <= getvariableofnpc(.talk_lock, strnpcinfo(NPC_NAME_UNIQUE))) + return false; + set(getvariableofnpc(.talk_lock, strnpcinfo(NPC_NAME_UNIQUE)), gettimetick(2) + getarg(1, 1)); + break; + default: + if (gettimetick(2) <= @NPC_TALK_LOCK[getnpcid()]) + return false; + @NPC_TALK_LOCK[getnpcid()] = gettimetick(2) + getarg(1, 1); + } + + // talk mechanism + switch (getarg(2, 0)) + { + case 0: npctalk3(getarg(0)); break; + case 1: npctalk(getarg(0)); break; + case 2: message(strcharinfo(0), getarg(0)); + } + + return true; +} + +// Randomizer functions +///////////////////////////////////////////// + +// pseudo-fix randomness +// rand2( min, max ) +function script rand2 { + if (getargcount() == 2) { + .@min=getarg(0)*100; + .@max=getarg(1)*100+99; + } else { + .@min=0; + .@max=getarg(0)*100-1; + } + return rand(.@min, .@max)/100; +} + +// returns one argument randomly +// any( <arg>{, ...<arg>} ) +function script any { + return getarg(rand2(getargcount())); +} + +// returns any member of the array +// any_of( <array> ) +function script any_of { + return getelementofarray(getarg(0), getarrayindex(getarg(0)) + rand2(getarraysize(getarg(0)) - getarrayindex(getarg(0)))); +} + +function script die { + if ($HARDCORE) { + @grace=true; + percentheal -100, -100; + //setparam(Hp, 1); + //warp "000-1", 22, 22; + //end; // MUST be end; to mimic official behavior + } else { + percentheal -100, -100; + } + return; +} + +// TMW2 Custom Functions +///////////////////////////////////////////// + +// Function meant to be used by Main Storyline Quest +// msObjective ( condition , message ) +function script msObjective { + if (getarg(0)) + mesc getarg(1), 2; + else + mesc getarg(1), 9; + return; +} + +function script getmap { + if (getmapxy(.@mapName$, .@xpos, .@ypos, getarg(0,0)) != 0) + return false; + // TODO: Maybe use getmapname() instead of getmapxy? + return .@mapName$; +} + +// Get unit BL from type +// ( unittype ) +function script getunitbl { + switch (getarg(0)) { + case UNITTYPE_PC: + return BL_PC; + case UNITTYPE_NPC: + return BL_NPC; + case UNITTYPE_PET: + return BL_PET; + case UNITTYPE_MOB: + return BL_MOB; + case UNITTYPE_HOM: + return BL_HOM; + case UNITTYPE_MER: + return BL_MER; + case UNITTYPE_ELEM: + return BL_ELEM; + default: + consolewarn("Invalid unit type on getunitbl: %d", getarg(0)); + return BL_ALL; + } + return 0; +} + +// isin( map, x1, y1, {[x2, y2][radius]} ) +function script isin { + if (getmapxy(.@mapName$, .@xpos, .@ypos, 0) != 0) + return false; + if (.@mapName$ != getarg(0)) + return false; + + if (getarg(4,-1) < 0) { + // Radius Based + if (.@xpos >= getarg(1)-getarg(3) && .@xpos <= getarg(1)+getarg(3) && .@ypos >= getarg(2)-getarg(3) && .@ypos <= getarg(2)+getarg(3)) + return true; + } else { + // Coordinate based + if (.@xpos >= getarg(1) && .@xpos <= getarg(3) && .@ypos >= getarg(2) && .@ypos <= getarg(4)) + return true; + } + return false; +} + +// isat( map, x, y ) +function script isat { + return isin(getarg(0), getarg(1), getarg(2), 0); +} + +// Clear output of getinventorylist() +// delinventorylist() +function script delinventorylist { + deletearray @inventorylist_id; + deletearray @inventorylist_amount; + deletearray @inventorylist_equip; + deletearray @inventorylist_refine; + deletearray @inventorylist_identify; + deletearray @inventorylist_attribute; + deletearray @inventorylist_card1; + deletearray @inventorylist_card2; + deletearray @inventorylist_card3; + deletearray @inventorylist_card4; + deletearray @inventorylist_expire; + deletearray @inventorylist_bound; + @inventorylist_count=0; + return; +} + +// Get some acc id, even if offline +// ( Name ) +function script gf_accid { + .@nb = query_sql("SELECT `account_id` FROM `char` WHERE `name`='"+escape_sql(getarg(0))+"' LIMIT 1", .@value); + return .@value[0]; +} + +// Get some char id, even if offline +// ( Name ) +function script gf_charnameid { + .@nb = query_sql("SELECT `char_id` FROM `char` WHERE `name`='"+escape_sql(getarg(0))+"' LIMIT 1", .@value); + return .@value[0]; +} + +// Get some char name from char ID, even if offline +// ( Name ) +function script gf_charname { + .@nb = query_sql("SELECT `name` FROM `char` WHERE `char_id`="+escape_sql(getarg(0))+" LIMIT 1", .@value$); + return .@value$[0]; +} + +// Get some char ID from account ID, even if offline +// ( Name ) +function script gf_charid { + .@nb = query_sql("SELECT `char_id` FROM `char` WHERE `account_id`="+escape_sql(getarg(0))+" LIMIT 1", .@value$); + return .@value$[0]; +} + +// Request pincode and validate it. Use any non-4-digits code to cancel. Failure will dc you. +// Returns 1 if pin check is OK. +function script validatepin { + if (#FIRST_TIME < 2) { + mesc l("ERROR: You must set a PinCode to make use of this function."), 1; + return 0; + } + mesc l("Please insert your pincode."), 1; + mesc l("WARNING: If you insert wrong pincode, you'll be disconnected."); + mesc l("Use @@ to cancel.", "##B-1##b"); + mes ""; + input .@pin$; + if (getstrlen(.@pin$) != 4) + return 0; + query_sql("SELECT userid FROM `login` WHERE account_id="+escape_sql(getcharid(3))+" AND pincode='"+escape_sql(.@pin$)+"' LIMIT 2", .@value$); + if (getarraysize(.@value$) != 1) { + atcommand "@kick "+strcharinfo(0); + return 0; + } + // Enforce some cooldown to prevent an eventual exploit/abuse + sleep2(rand2(150, 400)); + mesc l("Thanks, @@. We just wanted to be sure it was you.", .@value$[0]); + mes ""; + return true; +} + +// Something went wrong and must be reported (named after raise Exception in python) +// Exception( BugID, {Flags{, Return Code}} ) +function script Exception { + // Fill variable + .@msg$=getarg(0); + .@gf=getarg(1,RB_DEFAULT); + + // Avoid self-errors + if (!playerattached()) { + if (.@gf & RB_DISPBOTTOM) + .@gf = .@gf ^ RB_DISPBOTTOM; + if (.@gf & RB_SPEECH) + .@gf = .@gf ^ RB_SPEECH; + } + + // Main protocols + if (.@gf & RB_DISPBOTTOM) + dispbottom("ERROR: "+.@msg$); + + if (.@gf & RB_DEBUGMES) + consolewarn(.@msg$); + //debugmes("[Warning] "+.@msg$); + + if (.@gf & RB_SPEECH) + mesc("ERROR, REPORT ME! "+.@msg$, 1); + + if (.@gf & RB_IRCBROADCAST) + channelmes("#world", "Error in script: "+.@msg$); + + if (.@gf & RB_GLOBALANNOUNCE) + announce("Error in script: "+.@msg$, bc_all); + + if (.@gf & RB_PLEASEREPORT) { + if (.@gf & RB_DISPBOTTOM) + dispbottom("Please take a screenshot and report this bug, explaining how it happened."); + + if (.@gf & RB_SPEECH) + mesc("Please take a screenshot and report this bug, explaining how it happened."), 1; + } + + if (.@gf & RB_ISFATAL) { + if (.@gf & RB_DISPBOTTOM) + dispbottom("This error is fatal, we stop execution."); + + if (.@gf & RB_DEBUGMES) + consolebug("Previous warning was fatal, halting execution."); + //debugmes("[Error] The error is fatal."); + + if (.@gf & RB_SPEECH) { + mesc l("This error is fatal, we stop execution."), 1; + close; + } + end; + } + + return getarg(2, 0); + +} + +// Linking functions +///////////////////////////////////////////// +function script getquestlink { + return "[@@q" + getarg(0) + "|@@]"; +} + +function script getmonsterlink { + return "[@@m" + getarg(0) + "|@@]"; +} + +function script getpetlink { + return "[@@p" + getarg(0) + "|@@]"; +} + +function script getmercenarylink { + return "[@@M" + getarg(0) + "|@@]"; +} + +function script gethomunculuslink { + return "[@@h" + getarg(0) + "|@@]"; +} diff --git a/npc/functions/marriage.txt b/npc/functions/marriage.txt new file mode 100644 index 0000000..ad46505 --- /dev/null +++ b/npc/functions/marriage.txt @@ -0,0 +1,301 @@ +// TMW-2 Script +// Evol functions. +// Author: +// 4144 +// Jesusalva +// Description: +// Functions for marriage + +// check is player is near marriage npc +// args: +// 0 - player name +// returns: +// true if player located near npc. +function script marriagecheckname { + .@name$ = getarg(0); + if (.@name$ == "") + { + // no other registrand + return false; + } + .@id = getcharid(0, .@name$); + .@accoundId = getcharid(3, .@name$); + if (isloggedin(.@accoundId, .@id) == false) + { + // registrant not logged in + return false; + } + getmapxy(.@mapname$, .@x, .@y, 0, .@name$); + if (.@mapname$ != strnpcinfo(4)) + { + // registrant on other map + return false; + } + if (distance(.@x, .@y, .x, .y) > .distance) + { + // registrant too far + return false; + } + return true; +} + +// return player name registered with same gender like attached player +function script getmarriageregistrant { + if (Sex) + { + .@name$ = getvariableofnpc(.maleName$, strnpcinfo(3)); + } + else + { + .@name$ = getvariableofnpc(.femaleName$, strnpcinfo(3)); + } + return .@name$; +} + +// return registered marriage partner name +function script getmarriagepartner { + if (Sex) + { + .@name$ = getvariableofnpc(.femaleName$, strnpcinfo(3)); + } + else + { + .@name$ = getvariableofnpc(.maleName$, strnpcinfo(3)); + } + if (marriagecheckname(.@name$)) + return .@name$; + return ""; +} + +// register attached player as partner for marriage +function script marriageregisterself { + if (Sex) + { + set getvariableofnpc(.maleName$, strnpcinfo(3)), strcharinfo(0); + set getvariableofnpc(.maleName_partner$, strnpcinfo(3)), ""; + } + else + { + set getvariableofnpc(.femaleName$, strnpcinfo(3)), strcharinfo(0); + set getvariableofnpc(.femaleName_partner$, strnpcinfo(3)), ""; + } + return; +} + +// return player name what was accepted by partner +function script getmarriagepartneraccepted { + if (Sex) + { + .@name$ = getvariableofnpc(.femaleName_partner$, strnpcinfo(3)); + } + else + { + .@name$ = getvariableofnpc(.maleName_partner$, strnpcinfo(3)); + } + return .@name$; +} + +// return true if partner present near and partner accepted you +function script ismarriagepartneraccepted { + .@partner$ = getmarriagepartner(); + if (.@partner$ == "") + return false; + if (readparam(Sex, strcharinfo(0)) == readparam(Sex, .@partner$)) { + // Blame Jesusalva. I am sensing various other possible bugs. Let's play safe. + return false; + } + .@name$ = getmarriagepartneraccepted(); + if (.@name$ == strcharinfo(0)) + return true; + return false; +} + +// accept for attached player his/her partner +// args: +// 0 - partner name +function script marriageacceptpartner { + .@name$ = getarg(0); + if (Sex) + { + set getvariableofnpc(.maleName_partner$, strnpcinfo(3)), .@name$; + } + else + { + set getvariableofnpc(.femaleName_partner$, strnpcinfo(3)), .@name$; + } + return; +} + +function script askmarry { + speech l("Do you want to marry @@?", getarg(0)); + if (askyesno() == ASK_YES) + return true; + return false; +} + +// start marriage registration process +function script marriageregister { + .@partner$ = getmarriagepartner(); + if (.@partner$ == "") + { // no partner registered + speech l("Ok I add your name... @@...", strcharinfo(0)); + marriageregisterself(); + next; + speech lg("Now wait for your partner, then talk to me again."); + npctalk l("@@ waits for their loved one.", strcharinfo(0)); + close; + } + else + { // partner already registered + if (askmarry(.@partner$) == true) + { + marriageregisterself(); + marriageacceptpartner(.@partner$); + npctalk3 l("@@ registered for marriage and accepted partner @@!", strcharinfo(0), .@partner$); + npctalk3 l("Waiting for @@...", .@partner$); + close; + } + else + { + close; + } + } + return; +} + +// remove all marriage registations +function script marriageclear { + set getvariableofnpc(.maleName$, strnpcinfo(3)), ""; + set getvariableofnpc(.femaleName$, strnpcinfo(3)), ""; + set getvariableofnpc(.maleName_partner$, strnpcinfo(3)), ""; + set getvariableofnpc(.femaleName_partner$, strnpcinfo(3)), ""; + return; +} + +// do actual marriage +function script domarriage { + .@name$ = strcharinfo(0); + .@partner$ = getarg(0); + if (marriage(.@partner$)) + { + speech l("You got married to @@!", .@partner$); + mapannounce getmap(), l("@@ and @@ just got married!", .@name$, .@partner$), bc_map; + specialeffect 6; + } + else + { + speech l("Marriage failed."); + } + marriageclear(); + return; +} + +// marry main code +function script marriagemarry { + .@registrant$ = getmarriageregistrant(); + if (marriagecheckname(.@registrant$) == true) + { + if (.@registrant$ == strcharinfo(0)) + { + if (ismarriagepartneraccepted()) + { + .@partner$ = getmarriagepartner(); + if (marriagecheckname(.@partner$) == false) + { + speech l("Partner not ready."); + } + else if (askmarry(.@partner$) == true) + { + domarriage(.@partner$); + } + } + else + { + speech l("You already registered. Waiting for your partner..."); + } + } + else + { + speech l("Sorry, I'm busy with other registrations."), + l("Come after a little while."); + } + close; + } + + speech l("What can I do for you?"); + tutmes l("Marriage unlocks %s powers. The ring is sold in Tulimshar.", getitemlink(WeddingRing)); + switch (select(l("I want to register for marriage."), + l("Nothing"))) + { + case 1: + marriageregister; + break; + case 2: + break; + } + return; +} + +// divorce main code +function script marriagedivorce { + speech l("What can I do for you?"); + switch (select(l("I want to divorce."), + l("Nothing"))) + { + case 1: + speech lg("Are you sure? It costs 800 GP."); + if (askyesno() == ASK_YES) { + if (Zeny < 800) + return; + if (divorce()) { + Zeny=Zeny-800; + speech l("You are now divorced!"); + npctalk l("@@ divorced!", strcharinfo(0)); + } + else + { + speech l("Divorce error!"); + } + } + break; + case 2: + break; + } + return; +} + +// main function for marriage +function script marriagemain { + if (Sex > 1) + { + speech l("Sorry, I can't help you. Go away!"); + close; + } + + if (getpartnerid() != 0) + { // have partner + marriagedivorce(); + } + else + { // no partner + marriagemarry(); + } + + return; +} + +// check registration list by timer +function script marriagecheck { + .@name$ = getvariableofnpc(.maleName$, strnpcinfo(3)); + if (.@name$ != "" && marriagecheckname(.@name) == false) + { + set getvariableofnpc(.maleName$, strnpcinfo(3)), ""; + set getvariableofnpc(.maleName_partner$, strnpcinfo(3)), ""; + } + .@name$ = getvariableofnpc(.femaleName$, strnpcinfo(3)); + if (.@name$ != "" && marriagecheckname(.@name) == false) + { + set getvariableofnpc(.femaleName$, strnpcinfo(3)), ""; + set getvariableofnpc(.femaleName_partner$, strnpcinfo(3)), ""; + } +} diff --git a/npc/functions/math.txt b/npc/functions/math.txt new file mode 100644 index 0000000..22cf881 --- /dev/null +++ b/npc/functions/math.txt @@ -0,0 +1,114 @@ +// Evol functions. +// Authors: +// 4144 +// Reid +// Description: +// Math functions + + +// abs(<int>) +// returns the absolute value of the passed integer + +function script abs { + .@n = getarg(0); + return .@n >= 0 ? .@n : -.@n; +} + + + +// lognbaselvl({<multiplicator>{, <min value>}}) +// returns BaseLevel * logn (BaseLevel * alpha). + +function script lognbaselvl { + .@alpha = getarg(0, 1); + .@min = getarg(1, 1); + .@ret = 0; + .@pc_level = BaseLevel * .@alpha; + + while (.@pc_level >>= 1) + { + ++.@ret; + } + .@ret *= BaseLevel; + + if (.@ret <= .@min) + { + .@ret = .@min; + } + + return .@ret; +} + +// log2(<int>) +// returns the log base 2 of the passed integer, up to 20 (2**20=1.048.576) (round down always) + +function script log2 { + .@v=abs(getarg(0)); + .@ok=0; + .@i=0; + if (.@v < 1) + return -1; + + freeloop(true); + while (!.@ok) { + // exact match + if (2**.@i == .@v) { + .@ok=1; + // inexact match, or limit exceeded + } else if (2**.@i >= .@v || .@i > 20) { + .@ok=1; + .@i-=1; // round down + // not yet + } else { + .@i+=1; + } + } + freeloop(false); + + return .@i; +} + + +// result is: lower < target <= higher +// is_between ( lower, higher, target) +function script is_between { + .@val=getarg(2); + .@min=getarg(0); + .@max=getarg(1); + return (.@min < .@val && .@val <= .@max); +} + + +// result is: lower < target <= higher +// is_between ( lower, target, higher) +function script is_between2 { + .@min=getarg(0); + .@val=getarg(1); + .@max=getarg(2); + return (.@min < .@val && .@val <= .@max); +} + + +// forces the equation: lower <= target <= higher. +// Note it still works if higher and target values are swapped. +// limit ( lower, target, higher) +function script limit { + return max(getarg(0), min(getarg(1), getarg(2))); +} + + +// result is the ponderate average. +// ponderate_avg ( arg1, sub1, arg2, sub2) +function script ponderate_avg { + .@a1=getarg(0); + .@s1=getarg(1); + .@a2=getarg(2); + .@s2=getarg(3); + + .@h1=.@a1*.@s1; + .@h2=.@a2*.@s2; + .@dd=.@s1+.@s2; + + return (.@h1+.@h2)/.@dd; +} + diff --git a/npc/functions/maze.txt b/npc/functions/maze.txt new file mode 100644 index 0000000..b5dbf55 --- /dev/null +++ b/npc/functions/maze.txt @@ -0,0 +1,554 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Controls map domain 030 and provide helpers. See constants; +// Player Variables: +// MAZE_ID → ID of the maze being used +// MAZE_MAP$ → Map being used for the maze +// MAZE_INST → Instance ID of the Maze + +///////////////////////////////////////////////////////////////////////////////// +// CreateMaze(scope=IOT_CHAR{, size=MAZE_SIZE_S}) +// Creates the maze instances so they can be configured +// But does not initializes anything. +function script CreateMaze { + .@scope = getarg(0, IOT_CHAR); + .@size = getarg(1, MAZE_SIZE_S); + + // Small mazes (30x30 average) (24 total) + if (.@size & MAZE_SIZE_S) { + .@p = getarraysize(.@ids); + setarray .@ids[.@p], 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24; + } + // Medium mazes (45x45 average) (15 total) + if (.@size & MAZE_SIZE_M) { + .@p = getarraysize(.@ids); + setarray .@ids[.@p], 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39; + } + // Great mazes (60x60 average) (20 total) + if (.@size & MAZE_SIZE_G) { + .@p = getarraysize(.@ids); + setarray .@ids[.@p], 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59; + } + // Xtra mazes (75x75 average) (10 total) + if (.@size & MAZE_SIZE_X) { + .@p = getarraysize(.@ids); + setarray .@ids[.@p], 60, 61, 62, 63, 64, 65, 66, 67, 68, 69; + } + // Abs. mazes (90x90 average) (6 total) + if (.@size & MAZE_SIZE_A) { + .@p = getarraysize(.@ids); + setarray .@ids[.@p], 70, 71, 72, 73, 74, 75; + } + + // Get a random maze, but it must not be repeated + do + { + .@r = any_of(.@ids); + if (MAZE_ID == .@r) + continue; + MAZE_ID = .@r; + + .@MAZEMP$=sprintf("030-%02d", MAZE_ID); + MAZE_MAP$=sprintf("mz%02d@"+getcharid(0), MAZE_ID); + + // Maze is not yet started + if (.@scope == IOT_CHAR) { + MAZE_INST = instance_create("Maze "+MAZE_ID+" "+getcharid(0), getcharid(3), IOT_CHAR); + // Failed + if (MAZE_INST < 0) { + consolebug("Instance \"%s\" already exists! (Error %d)", "Maze "+MAZE_ID+" "+getcharid(0), MAZE_INST); + continue; // Get a new maze + } + // Attach map + instance_attachmap(.@MAZEMP$, MAZE_INST, false, MAZE_MAP$); + } else { + consolebug("Scope %d not yet supported by maze system", .@scope); + return 0; // Exit early + } + } while (MAZE_INST < 0); + return MAZE_ID; +} + +///////////////////////////////////////////////////////////////////////////////// +// InitMaze(duration=2 hours, random_coords=yes, treasure=no) +// Puts maze to work and send player there. +// Exit must have been configured prior to the maze. +function script InitMaze { + .@t = getarg(0, 7200); + instance_set_timeout(.@t, .@t, MAZE_INST); + instance_init(MAZE_INST); + + // Find random, warpable coordinates + .@e=0; .@x=0; .@y=0; + if (getarg(1, true)) { + .@mx=getmapinfo(MAPINFO_SIZE_X, MAZE_MAP$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, MAZE_MAP$)-20; + } else { + .@mx=40; // Wouldn't (30,30) be safer? + .@my=40; + } + do { + .@x = rand2(20, .@mx); + .@y = rand2(20, .@my); + .@e += 1; + if (.@e > 30) { + consolebug("Too many failures at Maze \"%s\"! Trying anyway!", MAZE_MAP$); + break; + } + } while (!checknpccell(MAZE_MAP$, .@x, .@y, cell_chkpass)); + + // Teleport the player + warp MAZE_MAP$, .@x, .@y; + + // Add a random treasure chest if set to do so + if (getarg(2, false)) { + .@n$=instance_npcname(sprintf("#T_030-%02d", MAZE_ID), MAZE_INST); + .@mx=getmapinfo(MAPINFO_SIZE_X, MAZE_MAP$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, MAZE_MAP$)-20; + do { + .@x = rand2(20, .@mx); + .@y = rand2(20, .@my); + .@e += 1; + if (.@e > 20) + break; + } while (!checknpccell(MAZE_MAP$, .@x, .@y, cell_chkpass)); + movenpc(.@n$, .@x, .@y); + } + return true; +} + +///////////////////////////////////////////////////////////////////////////////// +// MazeMobs(level=BaseLevel, chests=yes, density_redux=10) +// Puts monsters on the maze. Less density_redux is more mobs. +function script MazeMobs { + .@mx=getmapinfo(MAPINFO_SIZE_X, MAZE_MAP$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, MAZE_MAP$)-20; + .@tl=(20-.@mx)*(20-.@my) * 3 / 10; // Total tiles + collision guess + .@tl=.@tl / getarg(2, 10) + 1; // Monster density + + /* *** Copied from 006-5/groata.txt & 018-2-2/main.txt! *** */ + /* ********* Spawn monsters based on DTOWER_FLOOR ********* */ + .@lv = getarg(0, BaseLevel); + .@mb[0] = MagicGoblin; + .@mb[1] = CaveMaggot; + + // Artillery & Chests + // TODO: Mimics? + if (getarg(1, true)) { + if (.@lv > 10) + array_push(.@mb, BronzeChest); + if (.@lv > 15) + array_push(.@mb, BronzeMimic); + if (.@lv > 20) + array_push(.@mb, SmallMagicBif); + if (.@lv > 30) + array_push(.@mb, Bif); + if (.@lv > 45) + array_push(.@mb, SilverChest); + if (.@lv > 50) + array_push(.@mb, SilverMimic); + if (.@lv > 55) + array_push(.@mb, MagicBif); + if (.@lv > 75) + array_push(.@mb, GoldenChest); + if (.@lv > 80) + array_push(.@mb, GoldenMimic); + if (.@lv > 85) + array_push(.@mb, BigMagicBif); + if (.@lv > 105) + array_push(.@mb, PrismChest); + } + if (.@lv > 40) + array_push(.@mb, RobinBandit); + if (.@lv > 50) + array_push(.@mb, DustGatling); + if (.@lv > 60) + array_push(.@mb, DustRifle); + if (.@lv > 70) + array_push(.@mb, DustRevolver); + if (.@lv > 80) + array_push(.@mb, DustBoss); + if (.@lv > 90) + array_push(.@mb, GreatMoubooSlime); + if (.@lv > 100) + array_push(.@mb, Jhon); + + // Monsters + if (is_between2(0, .@lv, 30)) { + array_push(.@mb, Piou); + array_push(.@mb, Piousse); + array_push(.@mb, Squirrel); + array_push(.@mb, ManaPiou); + array_push(.@mb, ForestPiou); + array_push(.@mb, RedButterfly); + array_push(.@mb, Maggot); + array_push(.@mb, CandorScorpion); + array_push(.@mb, HouseMaggot); + array_push(.@mb, LittleYellowSlime); + array_push(.@mb, Ratto); + array_push(.@mb, RudolphSlime); + array_push(.@mb, MoubooSlime); + array_push(.@mb, Croc); + array_push(.@mb, Scorpion); + array_push(.@mb, SmallFrog); + } + if (is_between2(10, .@lv, 40)) { + array_push(.@mb, BigFrog); + array_push(.@mb, Lavern); + array_push(.@mb, LittleRedSlime); + array_push(.@mb, ChocolateSlime); + array_push(.@mb, Blub); + array_push(.@mb, Duck); + array_push(.@mb, Bat); + array_push(.@mb, CaveMaggot); + array_push(.@mb, ManaGhost); + array_push(.@mb, ManaBug); + array_push(.@mb, Fluffy); + array_push(.@mb, FireGoblin); + array_push(.@mb, ViciousSquirrel); + array_push(.@mb, RedScorpion); + array_push(.@mb, WhiteSlime); + array_push(.@mb, AzulSlime); + array_push(.@mb, DesertLogHead); + } + if (is_between2(20, .@lv, 50)) { + array_push(.@mb, RedSlime); + array_push(.@mb, PoisonSpikyMushroom); + array_push(.@mb, DesertBandit); + array_push(.@mb, OceanCroc); + array_push(.@mb, ToppyBlub); + array_push(.@mb, Sarracenus); + array_push(.@mb, IceMaggot); + array_push(.@mb, VampireBat); + array_push(.@mb, Bandit); + array_push(.@mb, Pinkie); + array_push(.@mb, LivingPotato); + array_push(.@mb, Assassin); + array_push(.@mb, Skeleton); + } + if (is_between2(30, .@lv, 60)) { + array_push(.@mb, CaveSnake); + array_push(.@mb, GreenSlime); + array_push(.@mb, CopperSlime); + array_push(.@mb, YellowSlime); + array_push(.@mb, SantaSlime); + array_push(.@mb, LavaSlime); + array_push(.@mb, Bluepar); + array_push(.@mb, DeathCat); + array_push(.@mb, Moggun); + array_push(.@mb, SeaSlime); + array_push(.@mb, RedMushroom); + array_push(.@mb, Mouboo); + array_push(.@mb, LogHead); + array_push(.@mb, CandiedSlime); + array_push(.@mb, OldSnake); + array_push(.@mb, GrassSnake); + } + if (is_between2(40, .@lv, 70)) { + array_push(.@mb, GiantMaggot); + array_push(.@mb, IcedFluffy); + array_push(.@mb, Snake); + array_push(.@mb, BlackSlime); + array_push(.@mb, Tipiou); + array_push(.@mb, AlphaMouboo); + array_push(.@mb, Pollet); + array_push(.@mb, PiouKnight); + array_push(.@mb, Shrewboo); + } + if (is_between2(40, .@lv, 80)) { + array_push(.@mb, Wolvern); + array_push(.@mb, FireSkull); + array_push(.@mb, DarkLizard); + } + if (is_between2(50, .@lv, 90)) { + array_push(.@mb, ArmoredSkeleton); + array_push(.@mb, BlackScorpion); + array_push(.@mb, ElectroWorm); + array_push(.@mb, EarthFairy); + array_push(.@mb, FireFairy); + array_push(.@mb, WaterFairy); + array_push(.@mb, WindFairy); + array_push(.@mb, PoisonFairy); + array_push(.@mb, MountainSnake); + array_push(.@mb, HoodedNinja); + array_push(.@mb, ForestMushroom); + array_push(.@mb, GoldenScorpion); + } + if (is_between2(60, .@lv, 100)) { + array_push(.@mb, Yeti); + array_push(.@mb, FallenGuard1); + array_push(.@mb, GreenSlimeMother); + array_push(.@mb, SnowFlower); + array_push(.@mb, BlueSlimeMother); + array_push(.@mb, WickedMushroom); + array_push(.@mb, CopperSlimeMother); + array_push(.@mb, YellowSlimeMother); + array_push(.@mb, RedSlimeMother); + array_push(.@mb, ChocolateSlimeMother); + array_push(.@mb, WhiteSlimeMother); + array_push(.@mb, Archant); + array_push(.@mb, Scar); + } + if (is_between2(70, .@lv, 110)) { + array_push(.@mb, AzulSlimeMother); + array_push(.@mb, SeaSlimeMother); + array_push(.@mb, LavaSlimeMother); + array_push(.@mb, BlackSlimeMother); + array_push(.@mb, Crafty); + array_push(.@mb, Forain); + array_push(.@mb, GreenDragon); + array_push(.@mb, Michel); + array_push(.@mb, Troll); + } + if (is_between2(80, .@lv, 120)) { + array_push(.@mb, EliteDuck); + array_push(.@mb, AzulSkullSlime); + array_push(.@mb, Moonshroom); + array_push(.@mb, RedSkullSlime); + array_push(.@mb, Terranite); + array_push(.@mb, JackO); + array_push(.@mb, BlackMamba); + array_push(.@mb, GreenSkullSlime); + array_push(.@mb, BloodyMouboo); + array_push(.@mb, Centaur); + array_push(.@mb, GoboBear); + } + if (is_between2(90, .@lv, 130)) { + array_push(.@mb, CopperSkullSlime); + array_push(.@mb, LavaSkullSlime); + array_push(.@mb, BlackSkullSlime); + array_push(.@mb, GiantCaveMaggot); + array_push(.@mb, TerraniteProtector); + array_push(.@mb, VanityPixie); + array_push(.@mb, HolyPixie); + } + if (is_between2(100, .@lv, 140)) { + array_push(.@mb, ShadowPixie); + array_push(.@mb, NulityPixie); + array_push(.@mb, Reaper); + array_push(.@mb, NightmareDragon); + array_push(.@mb, Snail); + array_push(.@mb, WhirlyBird); + } + if (is_between2(110, .@lv, 150)) { + array_push(.@mb, PinkieSuseran); + array_push(.@mb, Mandragora); + array_push(.@mb, PinkieMaximus); + } + if (.@lv > 120) { + array_push(.@mb, Junglefowl); + array_push(.@mb, Tengu); + array_push(.@mb, Moubi); + } + if (.@lv > 130) { + array_push(.@mb, SuperiorShroom); + array_push(.@mb, Nutcracker); + array_push(.@mb, Golem); + } + if (.@lv > 140) { + array_push(.@mb, SiegeTower); + array_push(.@mb, GreenhornAbomination); + array_push(.@mb, ShadowTortuga); + array_push(.@mb, FireElement); + array_push(.@mb, WaterElement); + array_push(.@mb, EarthElement); + array_push(.@mb, WindElement); + } + if (.@lv > 150) { + array_push(.@mb, SacredWisp); + array_push(.@mb, EvilWisp); + array_push(.@mb, PanthomWisp); + array_push(.@mb, EpiphanyWisp); + } + if (.@lv > 175) + array_push(.@mb, Tortuga); + + /* Spawn them and make hostile */ + freeloop(true); + for (.@i = 0; .@i < 1+(.@tl); .@i++) { + .@mid = any_of(.@mb); + .@m=areamonster(MAZE_MAP$, 20, 20, .@mx, .@my, strmobinfo(1, .@mid), .@mid, 1); + set_aggro(.@m); + } + freeloop(false); + return; +} + +///////////////////////////////////////////////////////////////////////////////// +// RenewMaze(duration=2 hours) +// Renews the map expiration time +function script RenewMaze { + .@t = getarg(0, 7200); + instance_set_timeout(.@t, .@t, MAZE_INST); + return true; +} + +///////////////////////////////////////////////////////////////////////////////// +// Configure maze maps as MMO zones +030-01 mapflag zone MMO +030-02 mapflag zone MMO +030-03 mapflag zone MMO +030-04 mapflag zone MMO +030-05 mapflag zone MMO +030-06 mapflag zone MMO +030-07 mapflag zone MMO +030-08 mapflag zone MMO +030-09 mapflag zone MMO +030-10 mapflag zone MMO +030-11 mapflag zone MMO +030-12 mapflag zone MMO +030-13 mapflag zone MMO +030-14 mapflag zone MMO +030-15 mapflag zone MMO +030-16 mapflag zone MMO +030-17 mapflag zone MMO +030-18 mapflag zone MMO +030-19 mapflag zone MMO +030-20 mapflag zone MMO +030-21 mapflag zone MMO +030-22 mapflag zone MMO +030-23 mapflag zone MMO +030-24 mapflag zone MMO +030-25 mapflag zone MMO +030-26 mapflag zone MMO +030-27 mapflag zone MMO +030-28 mapflag zone MMO +030-29 mapflag zone MMO +030-30 mapflag zone MMO +030-31 mapflag zone MMO +030-32 mapflag zone MMO +030-33 mapflag zone MMO +030-34 mapflag zone MMO +030-35 mapflag zone MMO +030-36 mapflag zone MMO +030-37 mapflag zone MMO +030-38 mapflag zone MMO +030-39 mapflag zone MMO +030-40 mapflag zone MMO +030-41 mapflag zone MMO +030-42 mapflag zone MMO +030-43 mapflag zone MMO +030-44 mapflag zone MMO +030-45 mapflag zone MMO +030-46 mapflag zone MMO +030-47 mapflag zone MMO +030-48 mapflag zone MMO +030-49 mapflag zone MMO +030-50 mapflag zone MMO +030-51 mapflag zone MMO +030-52 mapflag zone MMO +030-53 mapflag zone MMO +030-54 mapflag zone MMO +030-55 mapflag zone MMO +030-56 mapflag zone MMO +030-57 mapflag zone MMO +030-58 mapflag zone MMO +030-59 mapflag zone MMO +030-60 mapflag zone MMO +030-61 mapflag zone MMO +030-62 mapflag zone MMO +030-63 mapflag zone MMO +030-64 mapflag zone MMO +030-65 mapflag zone MMO +030-66 mapflag zone MMO +030-67 mapflag zone MMO +030-68 mapflag zone MMO +030-69 mapflag zone MMO +030-70 mapflag zone MMO +030-71 mapflag zone MMO +030-72 mapflag zone MMO +030-73 mapflag zone MMO +030-74 mapflag zone MMO +030-75 mapflag zone MMO + +///////////////////////////////////////////////////////////////////////////////// +// Add Treasure chests +030-01,0,0,0 script #T_030-01 NPC_CHEST,{ + if (!reachable(.x, .y, 3)) end; + callfunc "TreasureBox", 150; // 1.5% extra chance + specialeffect(.dir == 0 ? 24 : 25, AREA, getnpcid()); // closed ? opening : closing + close; +OnInit: + .distance=3; + end; +} + +030-02,0,0,0 duplicate(#T_030-01) #T_030-02 NPC_CHEST +030-03,0,0,0 duplicate(#T_030-01) #T_030-03 NPC_CHEST +030-04,0,0,0 duplicate(#T_030-01) #T_030-04 NPC_CHEST +030-05,0,0,0 duplicate(#T_030-01) #T_030-05 NPC_CHEST +030-06,0,0,0 duplicate(#T_030-01) #T_030-06 NPC_CHEST +030-07,0,0,0 duplicate(#T_030-01) #T_030-07 NPC_CHEST +030-08,0,0,0 duplicate(#T_030-01) #T_030-08 NPC_CHEST +030-09,0,0,0 duplicate(#T_030-01) #T_030-09 NPC_CHEST +030-10,0,0,0 duplicate(#T_030-01) #T_030-10 NPC_CHEST +030-11,0,0,0 duplicate(#T_030-01) #T_030-11 NPC_CHEST +030-12,0,0,0 duplicate(#T_030-01) #T_030-12 NPC_CHEST +030-13,0,0,0 duplicate(#T_030-01) #T_030-13 NPC_CHEST +030-14,0,0,0 duplicate(#T_030-01) #T_030-14 NPC_CHEST +030-15,0,0,0 duplicate(#T_030-01) #T_030-15 NPC_CHEST +030-16,0,0,0 duplicate(#T_030-01) #T_030-16 NPC_CHEST +030-17,0,0,0 duplicate(#T_030-01) #T_030-17 NPC_CHEST +030-18,0,0,0 duplicate(#T_030-01) #T_030-18 NPC_CHEST +030-19,0,0,0 duplicate(#T_030-01) #T_030-19 NPC_CHEST +030-20,0,0,0 duplicate(#T_030-01) #T_030-20 NPC_CHEST +030-21,0,0,0 duplicate(#T_030-01) #T_030-21 NPC_CHEST +030-22,0,0,0 duplicate(#T_030-01) #T_030-22 NPC_CHEST +030-23,0,0,0 duplicate(#T_030-01) #T_030-23 NPC_CHEST +030-24,0,0,0 duplicate(#T_030-01) #T_030-24 NPC_CHEST +030-25,0,0,0 duplicate(#T_030-01) #T_030-25 NPC_CHEST +030-26,0,0,0 duplicate(#T_030-01) #T_030-26 NPC_CHEST +030-27,0,0,0 duplicate(#T_030-01) #T_030-27 NPC_CHEST +030-28,0,0,0 duplicate(#T_030-01) #T_030-28 NPC_CHEST +030-29,0,0,0 duplicate(#T_030-01) #T_030-29 NPC_CHEST +030-30,0,0,0 duplicate(#T_030-01) #T_030-30 NPC_CHEST +030-31,0,0,0 duplicate(#T_030-01) #T_030-31 NPC_CHEST +030-32,0,0,0 duplicate(#T_030-01) #T_030-32 NPC_CHEST +030-33,0,0,0 duplicate(#T_030-01) #T_030-33 NPC_CHEST +030-34,0,0,0 duplicate(#T_030-01) #T_030-34 NPC_CHEST +030-35,0,0,0 duplicate(#T_030-01) #T_030-35 NPC_CHEST +030-36,0,0,0 duplicate(#T_030-01) #T_030-36 NPC_CHEST +030-37,0,0,0 duplicate(#T_030-01) #T_030-37 NPC_CHEST +030-38,0,0,0 duplicate(#T_030-01) #T_030-38 NPC_CHEST +030-39,0,0,0 duplicate(#T_030-01) #T_030-39 NPC_CHEST +030-40,0,0,0 duplicate(#T_030-01) #T_030-40 NPC_CHEST +030-41,0,0,0 duplicate(#T_030-01) #T_030-41 NPC_CHEST +030-42,0,0,0 duplicate(#T_030-01) #T_030-42 NPC_CHEST +030-43,0,0,0 duplicate(#T_030-01) #T_030-43 NPC_CHEST +030-44,0,0,0 duplicate(#T_030-01) #T_030-44 NPC_CHEST +030-45,0,0,0 duplicate(#T_030-01) #T_030-45 NPC_CHEST +030-46,0,0,0 duplicate(#T_030-01) #T_030-46 NPC_CHEST +030-47,0,0,0 duplicate(#T_030-01) #T_030-47 NPC_CHEST +030-48,0,0,0 duplicate(#T_030-01) #T_030-48 NPC_CHEST +030-49,0,0,0 duplicate(#T_030-01) #T_030-49 NPC_CHEST +030-50,0,0,0 duplicate(#T_030-01) #T_030-50 NPC_CHEST +030-51,0,0,0 duplicate(#T_030-01) #T_030-51 NPC_CHEST +030-52,0,0,0 duplicate(#T_030-01) #T_030-52 NPC_CHEST +030-53,0,0,0 duplicate(#T_030-01) #T_030-53 NPC_CHEST +030-54,0,0,0 duplicate(#T_030-01) #T_030-54 NPC_CHEST +030-55,0,0,0 duplicate(#T_030-01) #T_030-55 NPC_CHEST +030-56,0,0,0 duplicate(#T_030-01) #T_030-56 NPC_CHEST +030-57,0,0,0 duplicate(#T_030-01) #T_030-57 NPC_CHEST +030-58,0,0,0 duplicate(#T_030-01) #T_030-58 NPC_CHEST +030-59,0,0,0 duplicate(#T_030-01) #T_030-59 NPC_CHEST +030-60,0,0,0 duplicate(#T_030-01) #T_030-60 NPC_CHEST +030-61,0,0,0 duplicate(#T_030-01) #T_030-61 NPC_CHEST +030-62,0,0,0 duplicate(#T_030-01) #T_030-62 NPC_CHEST +030-63,0,0,0 duplicate(#T_030-01) #T_030-63 NPC_CHEST +030-64,0,0,0 duplicate(#T_030-01) #T_030-64 NPC_CHEST +030-65,0,0,0 duplicate(#T_030-01) #T_030-65 NPC_CHEST +030-66,0,0,0 duplicate(#T_030-01) #T_030-66 NPC_CHEST +030-67,0,0,0 duplicate(#T_030-01) #T_030-67 NPC_CHEST +030-68,0,0,0 duplicate(#T_030-01) #T_030-68 NPC_CHEST +030-69,0,0,0 duplicate(#T_030-01) #T_030-69 NPC_CHEST +030-70,0,0,0 duplicate(#T_030-01) #T_030-70 NPC_CHEST +030-71,0,0,0 duplicate(#T_030-01) #T_030-71 NPC_CHEST +030-72,0,0,0 duplicate(#T_030-01) #T_030-72 NPC_CHEST +030-73,0,0,0 duplicate(#T_030-01) #T_030-73 NPC_CHEST +030-74,0,0,0 duplicate(#T_030-01) #T_030-74 NPC_CHEST +030-75,0,0,0 duplicate(#T_030-01) #T_030-75 NPC_CHEST + diff --git a/npc/functions/mkbot.txt b/npc/functions/mkbot.txt new file mode 100644 index 0000000..f396d82 --- /dev/null +++ b/npc/functions/mkbot.txt @@ -0,0 +1,259 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// GM Bot for the Monster King. +// VARIABLES +// $GAME_STORYLINE - Current Storyline status +// $@MK - Monster King Game ID +// $@MK_SCENE - Current event being handled by the Monster King +// $MK_TEMPVAR - Temporary Variable +// $@MK_THROTTLE - Event Throttler +// +// Storyline statuses: +// 0 - The Monster King is inactive (leading sieges to Hurnscald and Nivalis) +// 1 - The Monster King is known by players and is giving them a month break +// 2 - The Monster King is currently sieging towns at random +// 3 - The Monster King is preparing to perfom the Rite and Lightbringer seeks +// a wielder +// 4 - The Rite is CONCLUDED. Players must walk to MK evil lair and fight. +// 5 - The Monster King is dead. Or something. Depends on players. +// +// $MK_TEMPVAR meaning depends on GAME STORYLINE +// GS 0 +// Ignored +// GS 1 +// Tracks the day since 1970 when the town was cleared. A month break. +// GS 2 +// Player score (1pt per Lieutenant, 10pts per Colonel) +// Affects the end of Game Story 2 and begin of Game Story 3 +// Because we must wait players... +// GS 3 +// Number of successful Fortress Town breaches +// GS 4 +// Inheirs stage 3 + +000-0,0,0,0 script Monster King NPC_HIDDEN,{ +OnSlaveDie: + end; +OnBourneAgain: + // Reset aggro + $@MK_AGGRO=0; + if (playerattached()) { + channelmes("#world", strcharinfo(0)+" did an act worth of notice."); + dispbottom l("Oh well, this sucks, but that was only an illusion."); + dispbottom l("The real Monster King is probably on his fortress. It'll take more than that to take him down."); + if ($REBIRTH_WINNER$ == "" && TOP3AVERAGELVL() < 100) + getexp min(641500, BaseLevel**3), 0; + else + Mobpt+=max(13500, rand2(10000, BaseLevel*90)); + Mobpt = Mobpt + 165; + //$MOST_HEROIC$=strcharinfo(0); + specialeffect(FX_FANFARE, AREA, getcharid(3)); + } + // We need to start over + .bar=true; +OnInit: + $@MK=monster("boss", 45, 45, "The Monster King", MonsterKing, 1, "Monster King::OnBourneAgain"); + + if (!.bar) { + // Variables which other NPCs must take in account + $@MK_AGGRO=0; + $@MK_SCENE=0; + } else { + .bar=false; + } + + // Variables only for this NPC + .users=getusers(1); + .nearby=getusers(8); + .mp$="boss"; + .aid=200000; + .cid=150002; + // Constants + + // We should jump straight to loop (it runs every 90 seconds) +OnTimer90000: + // Regenerate some data, and kill spurious mobs + .users=getusers(1); + if (mobcount(.mp$, "Monster King::OnSlaveDie")) { + announce ("Monster King: Noobs, you are all a bunch of noobs!"), bc_map|bc_npc; + killmonster(.mp$, "Monster King::OnSlaveDie"); + } + + // We are on an event, so skip this loop + if ($@MK_SCENE || $@GM_EVENT) { + initnpctimer; + end; + } + + // The Monster King is online. This loop is not needed + if (isloggedin(.aid, .cid)) { + if (!$@MK_SCENE) + unitwarp($@MK, "boss", 45, 45); + else + rodex_sendmail(.cid, "MKBot", "Running Event", "An event is currently running by the MK Bot. Please logout and suppress it."); + initnpctimer; + } + + // Raise aggro (1 pt per 2 users) + $@MK_AGGRO+=(.users/2); + + // Mana Stone + if (.mp$ == "011-1") + enablenpc "Mana Stone"; + + // The Monster King will not move anymore because story + if ($GAME_STORYLINE == 0 || + $GAME_STORYLINE >= 5) { + if (.mp$ != "boss") + unitwarp($@MK, "boss", 45, 45); + initnpctimer; + end; + } + + // Select a random map. Never shows up at Candor and cities, nor indoors. Not all maps either. + setarray .@m$, "boss", "boss", "001-1", "001-3", "001-4", "001-5", "001-6", "001-7", "001-10", + "003-1", "003-1-3", "004-1", "004-2", "007-1", "009-1", "010-1", "010-1-1", "010-2", "011-1", + "012-1", "014-1", "014-2", "014-3", "014-4", "014-5", "015-1", "015-2", "015-3", "015-5", + "018-1-1", "018-2", "018-3", "018-4", "018-4-1", + "019-1", "019-2", "019-4", "020-1", "021-1", "022-1", "023-1"; + .mp$=any_of(.@m$); + + // Try to warp randomly, up to 30 attempts + .@e=0; .@x=0; .@y=0; + .@mx=getmapinfo(MAPINFO_SIZE_X, .mp$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, .mp$)-20; + do { + if (.@e >= 30) { + .mp$="boss"; + .@x=45; + .@y=45; + break; + } + .@x = rand2(20, .@mx); + .@y = rand2(20, .@my); + .@e+=1; + } while (!checknpccell(.mp$, .@x, .@y, cell_chkpass)); + if (!checknpccell(.mp$, .@x, .@y, cell_chkpass)) { + Exception("mk.bot runtime error: GM_ERR_128 highlight @Jesusalva", RB_DEBUGMES|RB_IRCBROADCAST); .mp$="boss"; .@x=45; .@y=45; + } + + // Monster King will not warp around for sightseeing if he is threatened + if ($GAME_STORYLINE > 2 && $@MK_AGGRO < 150) { + .mp$="boss";.@x=45;.@y=45; + } + + unitwarp($@MK, .mp$, .@x, .@y); + sleep(50); // For some reason or other, adding sleep(norid) and sleep2(rid). + .nearby=getusers(8); + + // Handle Mana Stone + if (.mp$ == "011-1") + disablenpc "Mana Stone"; + + // Debug markers + if ($@GM_OVERRIDE) + debugmes "Monster King (bot): "+.mp$+" ("+.@x+", "+.@y+")"; + + // If too few players are online, we don't need an event AT ALL! + if (.users < rand2(2,4)) { + initnpctimer; + end; + } + + // Siege events (req. 300 aggro, 3 users, and 70% chances to begin) + if ($@MK_AGGRO >= ($GAME_STORYLINE == 2 ? 300 : 900) && .users >= 3 && rand2(0,100) < 70 && + is_between(1, 4, $GAME_STORYLINE) && $@MK_THROTTLE < gettimetick(2) && + $@MK_TRIGGERED){ + // Delta handles the compulsory wait time between waves. + // 7 hours normally, 24 hours if the army is in disarray. + .@delta=7; + $@SIEGE_ABORTED = false; + if ($GAME_STORYLINE >= 3) + .@delta=24; + // Tulimshar + if (compare(.mp$, "003-")) { + announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc; + $@MK_THROTTLE=gettimetick(2)+.@delta*60*60; + $@MK_SCENE=MK_SIEGE_TULIM; + donpcevent("Lieutenant Dausen::OnMKSiege"); + } + // Halinarzo + else if (compare(.mp$, "009-")) { + announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc; + $@MK_THROTTLE=gettimetick(2)+.@delta*60*60; + $@MK_SCENE=MK_SIEGE_HALIN; + donpcevent("Lieutenant Jacob::OnMKSiege"); + } + // Hurnscald + else if (compare(.mp$, "012-")) { + announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc; + $@MK_THROTTLE=gettimetick(2)+.@delta*60*60; + $@MK_SCENE=MK_SIEGE_HURNS; + donpcevent("#HurnscaldSiege::OnMKSiege"); + } + // Nivalis + else if (compare(.mp$, "020-")) { + announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc; + $@MK_THROTTLE=gettimetick(2)+.@delta*60*60; + $@MK_SCENE=MK_SIEGE_NIVAL; + donpcevent("Lieutenant Joshua::OnMKSiege"); + } + } + + // If a player is nearby while the Monster King prepares, event may happen + // Minimum 80 Aggro + if (.nearby > 1 && $@MK_AGGRO >= 80 && + ($GAME_STORYLINE == 1 || ($GAME_STORYLINE >= 3 && $@MK_THROTTLE >= gettimetick(2)) )){ + // We should decide event kind, but that's NYI + announce ("Monster King: I smell humans! Humans must die!"), bc_map|bc_npc; + + getmapxy(.@m$, .@x, .@y, UNITTYPE_MOB, $@MK); + + // Spawn stuff + areamonster(.@m$, .@x-20, .@y-20, .@x+20, .@y+20, "Monster", ManaGhost, min(60, $@MK_AGGRO/10)+.nearby, "Monster King::OnSlaveDie"); + + // Remove some aggro. TODO: Do not remove so much aggro if it was high + .@diff=($@MK_AGGRO*$GAME_STORYLINE)/5; + $@MK_AGGRO=max(.@diff, 0); + } + + // Maybe, just maybe, game storyline must be updated here + if ($GAME_STORYLINE == 1 && $MK_TEMPVAR && + $MK_TEMPVAR <= gettimeparam(GETTIME_DAYOFMONTH)) { + // Game Story Change: Idle MK -> Active MK + kamibroadcast("I can't handle it anymore! NO MORE!", "Monster King"); + sleep(2500); + kamibroadcast("Come, my minions! Lay siege to towns! LEAVE NO OPPOSITION TO ME!", "Monster King"); + sleep(2500); + kamibroadcast("Burn, destroy, do whatever you need, until your last breath, my lieutenants and colonels!", "Monster King"); + sleep(2500); + kamibroadcast("##4 .:: Game Story Instructions on #world ::."); + channelmes("#world", "##1 **GAME STORY CHANGE** - The Monster King minions will now attack cities and lay waste to them."); + channelmes("#world", "##1 Players must defeat the lieutenants and colonels in order to prevent sieges from continuing."); + + // Apply the changes + $GAME_STORYLINE=2; + $MK_TEMPVAR=0; + } + if ($GAME_STORYLINE == 2 && + $MK_TEMPVAR >= MK_SIEGE_TOTALPOWER) { + kamibroadcast("##1##BThe Monster King army is in total disarray and disorder!", "Saulc"); + sleep(2500); + kamibroadcast("##1##BIt's our chance to strike back!", "Saulc"); + sleep(2500); + + // Apply the changes + $GAME_STORYLINE=3; + $MK_TEMPVAR=0; // In past, we forced 1 month wait. Now we have KW mechs... + $MANA_BLVL-=10; // Set level to 20~30 + $MANA_JLVL-=5; // Set job level to 15 + } + + // We're done, restart loop timer + $@MK_TRIGGERED=false; + initnpctimer; + end; +} + diff --git a/npc/functions/mobhunter.txt b/npc/functions/mobhunter.txt new file mode 100644 index 0000000..b38fea2 --- /dev/null +++ b/npc/functions/mobhunter.txt @@ -0,0 +1,263 @@ +// TMW-2 Script +// Author: Jesusalva +// Desc: Grand Hunter Quest Utils + +// MobID +function script GHQ_GetQuestIDByMonsterID { + switch (getarg(0)) { + case Maggot: + return 1; + break; + case Snake: + return 2; + break; + case Scorpion: + return 3; + break; + case ForestMushroom: + return 4; + break; + case Pinkie: + return 5; + break; + case Moggun: + return 6; + break; + case Fluffy: + return 7; + break; + case MountainSnake: + return 8; + break; + case Duck: + return 9; + break; + case Bat: + return 10; + break; + case GreenDragon: + return 11; + break; + default: + return Exception("GHQ GQID: Invalid ID: "+getarg(0), RB_DEFAULT^RB_PLEASEREPORT|RB_ISFATAL); + break; + } + +} + +// QuestID (basically reverses the previous code) +function script GHQ_GetMonsterIDByQuestID { + switch (getarg(0)) { + case 1: + return Maggot; + break; + case 2: + return Snake; + break; + case 3: + return Scorpion; + break; + case 4: + return ForestMushroom; + break; + case 5: + return Pinkie; + break; + case 6: + return Moggun; + break; + case 7: + return Fluffy; + break; + case 8: + return MountainSnake; + break; + case 9: + return Duck; + break; + case 10: + return Bat; + break; + case 11: + return GreenDragon; + break; + default: + return Exception("GHQ GMID: Invalid ID: "+getarg(0), RB_DEFAULT^RB_PLEASEREPORT|RB_ISFATAL); + break; + } + +} + +///////////////////////////////////////////////////////////////////////////////// + +// Handle milestone rewards; it does NOT update stuff +// Syntax: GHQ_GetRewardsOnMilestone ( rewardoverrideid ) +function script GHQ_GetRewardsOnMilestone { + + // Check if you can store a Strange Coin (you really should) + // Another item too, which I'm sure you won't get it anywhere. + inventoryplace StrangeCoin, 1, NPCEyes, 1; + + // Global setup + .@q=getq(General_Hunter); + .@k=getq2(General_Hunter); + .@monsterId=GHQ_GetMonsterIDByQuestID(getq(General_Hunter)); + // Setup + .@old=GHMEMO[getq(General_Hunter)]; + .@new=getq2(General_Hunter); + .@blv=strmobinfo(3, .@monsterId); + .@bhp=strmobinfo(4, .@monsterId); + .@xp=0; + .@gp=0; + .@jp=0; + //if (getq2(General_Hunter) >= 10000) goto L_Finish; + + // 1st step: 1000 kills + if (is_between(.@old, .@new, 1000)) { + mesc l("Goal: @@/@@ reached!", .@new, 1000), 2; + .@xp+=.@blv*10; + .@gp+=(.@bhp/2); + .@jp+=10; + } else if (.@new < 1000) { + mesc l("@@: @@/@@", l("1st step"), .@new, 1000); + } + + // 2nd step: 2500 kills + if (is_between(.@old, .@new, 2500)) { + mesc l("Goal: @@/@@ reached!", .@new, 2500), 2; + .@xp+=.@blv*25; + .@gp+=.@bhp; + .@jp+=25; + } else if (.@new < 2500) { + mesc l("@@: @@/@@", l("2nd step"), .@new, 2500); + } + + // 3rd step: 5000 kills + if (is_between(.@old, .@new, 5000)) { + mesc l("Goal: @@/@@ reached!", .@new, 5000), 2; + .@xp+=.@blv*50; + .@gp+=.@bhp; + .@jp+=50; + } else if (.@new < 5000) { + mesc l("@@: @@/@@", l("3rd step"), .@new, 5000); + } + + // 4th step: 7500 kills + if (is_between(.@old, .@new, 7500)) { + mesc l("Goal: @@/@@ reached!", .@new, 7500), 2; + .@xp+=.@blv*75; + .@gp+=.@bhp; + .@jp+=75; + } else if (.@new < 7500) { + mesc l("@@: @@/@@", l("4th step"), .@new, 7500); + } + + // 5th step: 10000 kills + if (is_between(.@old, .@new, 10000)) { + mesc l("Goal: @@/@@ reached!", .@new, 10000), 2; + + // Main reward + getitem StrangeCoin, .@blv*2; + Zeny=Zeny+.@bhp*25; // Maggot: 10.000 gp. That's plenty. + getexp .@bhp*BaseLevel, .@bhp; // The monster hp, once again, drastically affects + + // Achievements + if (!GHQ_WINNER) + GHQ_WINNER = gettimetick(2); + + // Grand Prize + switch (.@monsterId) { + case Pinkie: + getitem PinkHelmet, 1; + mesc l("And here's a rare for you, a @@! Good job!", getitemlink(PinkHelmet)); + break; + default: + mesc l("A new pet has been unlocked on the @@!", l("Pet Detective")); + } + + } + + // Get reward (must have Job Exp) + if (.@jp) { + getexp .@xp, .@jp; + Zeny=Zeny+.@gp; + } + + // Update Grand Hunter memory + GHMEMO[.@q]=.@k; + + return; +} + +///////////////////////////////////////////////////////////////////////////////// + +// MobID, Place, Prize Override +function script GHQ_Assign { + // Arguments + .@mobId =getarg(0); + .@loc$ =getarg(1); + .@prize$=getarg(2, ""); + + // Current Quest Status + vars + .@id=GHQ_GetQuestIDByMonsterID(.@mobId); + .@q=getq(General_Hunter); + .@p=getq2(General_Hunter); + //next; + //mesq l("Current Quest Progress: @@/10,000 kills", .@p); + + // Handle Partial Rewards + if (.@q) + GHQ_GetRewardsOnMilestone(); + mes ""; + + mesn; + mes l("I represent the @@ Hunters. We hunt @@ (Lv @@).", .@loc$, getmonsterlink(.@mobId), strmobinfo(3, .@mobId)); + if (.@prize$ != "") + mes l("The great prize is @@.", .@prize$); + else + mes l("The great prize is to unlock a pet!"); + if (.@q == .@id) { + .@m=GHQ_GetMonsterIDByQuestID(.@q); + mes l("You are currently hunting @@/10000 @@.", .@p, getmonsterlink(.@m)); + next; + closedialog; + goodbye; + } + if (.@q) { + .@m=GHQ_GetMonsterIDByQuestID(.@q); + mes l("You are currently hunting @@/10000 @@. Do you want to switch?", .@p, getmonsterlink(.@m)); + mesc l("Protip: Your current progress will be saved."); + } + select + l("I'm not interested."), + rif(GHMEMO[.@id] < 10000,l("I'll hunt them for you.")); + + switch (@menu) { + case 2: + GHMEMO[getq(General_Hunter)]=getq2(General_Hunter); + setq(General_Hunter, .@id, GHMEMO[.@id]); + mesn; + mesq l("Good luck! Don't come back until you reach 10000 kills!"); + break; + } + closedialog; + goodbye; + +} + + +function script mobhunter { + if (getq(General_Hunter) == 0) + return; + + .@ghd=getq(General_Hunter); + if (killedrid == GHQ_GetMonsterIDByQuestID(.@ghd)) { + setq2 General_Hunter, getq2(General_Hunter)+1; + .@ghd=getq2(General_Hunter); + if (! (.@ghd % 1000)) + dispbottom l("Grand Hunter Quest: @@/10,000", format_number(.@ghd)); + } + return; + +} + diff --git a/npc/functions/mobpoint.txt b/npc/functions/mobpoint.txt new file mode 100644 index 0000000..36f647e --- /dev/null +++ b/npc/functions/mobpoint.txt @@ -0,0 +1,111 @@ +// TMW2 Scripts +// Author: Crazyfefe +// Jesusalva +// Desc: Mob Points for Aidan & Ishi. You will gain MONSTER-LEVEL mob points. + +// fix_mobkill(mobID) → Manual fix for scripted mobs +function script fix_mobkill { + killedrid=getarg(0); + doevent "#mobptsys::OnNPCKillEvent"; + return; +} + +function script mobpoint { + if (!MPQUEST) + return; + if (!killedrid) // A bug! + return; + + .@moblv=strmobinfo(3,killedrid); + // You get MobLv + 20% as MobPoints. + // So a level 100 monster gives you 120 MobPt. Slimes gives no bonus. + if (compare("slime", strtolower(strmobinfo(1, killedrid)))) + .@addval=.@moblv; + else + .@addval=.@moblv*12/10; + + // Penalty/Bonus + .@base=(.@moblv-BaseLevel); + + // Rebirth exploit correction + if (.@base > 20 && REBIRTH && BaseLevel < 16) + .@base = 20; + + if (BaseLevel < .@moblv) { + // Target is stronger, +3% per monster level, capped at +75% + .@addval = .@addval * limit(100, 100+(.@base*3), 175) / 100; + } else if (BaseLevel > .@moblv) { + // Target is weaker, -1% per monster level, capped at -50% + .@addval = .@addval * limit(50, 101+.@base, 100) / 100; + } + + // Sanitization + .@addval=max(0, .@addval); + + // Events (+10% Mob Points) + if ($EVENT$ == "Rebirth") + .@addval = .@addval * 11 / 10; + + // Grant you the Monster Points + Mobpt = Mobpt + .@addval; + return; + +} + +- script #mobptsys NPC_HIDDEN,{ + end; + +OnUnlock: + if (checkpcblock() & PCBLOCK_ATTACK) + setpcblock(PCBLOCK_HARD, false); + end; + +OnNPCKillEvent: + $MONSTERS_KILLED+=1; + MONSTERS_KILLED+=1; + + // Remove undue Job exp + // The check is probably correct, but setparam is not working =/ + /* + */ + if (strmobinfo(7, killedrid) == 0 && readparam(JobExp) > 0) { + //setparam(JobExp, readparam(JobExp)-1); + JobExp-=1; + } + + // killedrid was not set, so we skip + if (!killedrid) + end; + + // call functions + callfunc "mobpoint"; + callfunc "mobhunter"; + callfunc "SQuest_Hasan"; + callfunc "SaggyMobCount"; + callfunc "dausen_mobtutorial"; + callfunc "Guardhouse_RandQuestCheck"; + callfunc "AuroraMobkill"; + callfunc "ChocolateDay"; + callfunc "CoffeeDay"; + callfunc "FSFDay"; + callfunc "CraftmasterDay"; + callfunc "CadisQuestCheck"; + callfunc "GeminiKill"; + callfunc "SK_drops"; + + // Other updates + $@MK_TRIGGERED=true; + // Unset killedrid. This affects multiple calls of this function + // But it is in overall more reliable imao + killedrid=0; + end; + +// When you kill a player, some special care is needed +// Only a few maps will give you experience for PK: Tulimshar's Guards Arena, +// Frostia Imperial PVP Arena, Call Of Dusty, Arena Quirino Voraz. +OnPCKillEvent: + // call functions + callfunc "HUB_PvP"; + end; + +} diff --git a/npc/functions/mounts.txt b/npc/functions/mounts.txt new file mode 100644 index 0000000..12d56d7 --- /dev/null +++ b/npc/functions/mounts.txt @@ -0,0 +1,101 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Mount Renting (var MOUBOO_RENTTIME - TODO: Discount when mouboo friend ) + +// MoubooRent( NPC Name ) +function script MoubooRent { + .@n$=getarg(0, strnpcinfo(0)); + if (MOUBOO_RENTTIME > gettimetick(2) || BaseLevel < 20) { + mesn .@n$; + mesq l("Moo!"); + return; + } + + mesn .@n$; + mesq l("Moo!"); + next; + mesn .@n$; + mesq l("Do you want to rent a Mouboo? It allows quick traveling!"); + next; + menuint + rif(Zeny > 100, l("Rent 1 day for 100 GP")), 100, + rif(Zeny > 270, l("Rent 3 days for 270 GP")), 270, + rif(Zeny > 600, l("Rent 7 days for 600 GP")), 600, + rif(Zeny > 1200, l("Rent 15 days for 1200 GP")), 1200, + rif(Zeny > 2000, l("Rent 30 days for 2000 GP")), 2000, + l("Don't rent a mouboo."), 0; + mes ""; + switch (@menuret) { + case 0: + return; + case 100: + .@time=86400; break; + case 270: + .@time=259200; break; + case 600: + .@time=604800; break; + case 1200: + .@time=1296000; break; + case 2000: + .@time=2592000; break; + default: + Exception("Invalid price: "+@menuret, RB_DEFAULT|RB_SPEECH|RB_ISFATAL); + break; + } + inventoryplace RidingMouboo, 1; + MOUBOO_RENTTIME=gettimetick(2)+.@time; + Zeny-=@menuret; + rentitem RidingMouboo, .@time; + return; +} + +// TortugaRent( NPC Name ) +function script TortugaRent { + .@n$=getarg(0, strnpcinfo(0)); + if (MOUBOO_RENTTIME > gettimetick(2)) { + mesn .@n$; + mesq l("Boo!"); + return; + } + + mesn .@n$; + mesq l("Boo!"); + next; + mesn .@n$; + mesq l("Do you want to rent a Tortuga? It allows quick traveling!"); + next; + menuint + rif(Zeny > 50, l("Rent 1 day for 50 GP")), 50, + rif(Zeny > 135, l("Rent 3 days for 135 GP")), 135, + rif(Zeny > 300, l("Rent 7 days for 300 GP")), 300, + rif(Zeny > 600, l("Rent 15 days for 600 GP")), 600, + rif(Zeny > 1000, l("Rent 30 days for 1000 GP")), 1000, + l("Don't rent a tortuga."), 0; + mes ""; + switch (@menuret) { + case 0: + return; + case 50: + .@time=86400; break; + case 135: + .@time=259200; break; + case 300: + .@time=604800; break; + case 600: + .@time=1296000; break; + case 1000: + .@time=2592000; break; + default: + Exception("Invalid price: "+@menuret, RB_DEFAULT|RB_SPEECH|RB_ISFATAL); + break; + } + inventoryplace RidingTortuga, 1; + MOUBOO_RENTTIME=gettimetick(2)+.@time; + Zeny-=@menuret; + rentitem RidingTortuga, .@time; + return; +} + + diff --git a/npc/functions/news.txt b/npc/functions/news.txt new file mode 100644 index 0000000..3812053 --- /dev/null +++ b/npc/functions/news.txt @@ -0,0 +1,480 @@ +// TMW-2 script. +// Authors: +// Jesusalva +// Description: +// Server news. + +function script GameNews { + .@entry=getarg(0, 0); + switch(.@entry) { + case 99: + mes ""; + mes ".:: "+ l("Prologue") + " ::."; + next; + mesc l("[@@https://wiki.moubootaurlegends.org/Storyline|Read the History@@]"); + next; + break; + ////////////////////////////////////////////////////////////////// + case 100: + mes ""; + mes ".:: "+ l("The First Act") + " ::."; + next; + mesc b(l("The Monster King Army has occupied several towns!")); + mesc l("The Monster King has managed to take hold of Nivalis and Hurnscald; Travel to Frostia and Halinarzo has also been made difficult."); + next; + mesc l("With Tulimshar being the last major human settlement standing (besides distant locations like Candor), the situation looks more dire than ever."); + mesc l("Brave players need to group and retake them!"); + mesc l("Stay tuned for announcements regarding raid times to retake the towns."); + mesc l("This event is unique, and cannot be repeated."), 1; + next; + break; + ////////////////////////////////////////////////////////////////// + case 101: + mes ""; + mes ".:: "+ l("The Second Act") + " ::."; + next; + mesc b(l("The Monster King Army left the cities! What will happen next? Anxiety grows!")); + mesc l("After being successful is recovering Hurnscald and Nivalis from the Monster Army, they seem to have withdrawn."); + next; + mesc l("This certainly is just to reorganize their armies. We must take this chance to improve ourselves, or else, we'll be overrun soon enough."); + mesc l("Brave adventurers, be ready for the worst!"); + next; + break; + ////////////////////////////////////////////////////////////////// + case 102: + mes ""; + mes ".:: "+ l("The Third Act") + " ::."; + next; + mesc b(l("The Monster King Army is making siege at towns randomly!")); + mesc l("The Monster King is sending their armies to attack towns with a seemingly random pattern."); + mesc l("However, the Army seems to walk away after some time."); + next; + mesc l("The reason why they are not occupying the towns like before is still unknown."); + mesc l("However, we must defeat the commanders to avoid major damage to them!"); + next; + break; + ////////////////////////////////////////////////////////////////// + case 103: + mes ""; + mes ".:: "+ l("The Fourth Act") + " ::."; + next; + mesc b(l("The Strike Back Is Upon Us!")); + mesc l("The Monster Army appears to be in complete disarray, sieges are much less frequent."); + next; + mesc l("The mist over the Impregnable Fortress Peak finally lowered down, and it seems... The peaks are no more!"); + mesc l("The Monster King Lair is not in a impregnable mountain, but in a small island now!!"); + next; + mesc l("Will adventurers reach it? Will the random attacks at towns cease??"); + mesc l("The world hero %s and the High Council Officers will announce the times to raid the Fortress.", $MOST_HEROIC$); + mesc l("Pay attention to #world chat, as this event is managed by players. The Monster Army is also trying to regroup, neglecting it is not advised."), 1; + next; + break; + ////////////////////////////////////////////////////////////////// + case 1031: + mes ""; + mes ".:: "+ l("Interlude") + " ::."; + next; + mesc b(l("Andrei Sakar Calls for Heroes!")); + mesc l("Andrei Sakar, world renowned hero, is assembling a group of adventurers for an important mission!"); + next; + mesc l("For a long time, towns on the distant continent of Kolev have been supporting humans here."); + mesc l("They had a critical role in ensuring the human's race survival to The Great Fire."); + next; + mesc l("However, for a few months already, nothing else has been heard from that town, and all contact was lost."); + mesc l("Andrei Sakar is decided to investigate, and has made arrangements with %s.", b(l("Nard"))); + next; + mesc l("If you are a brave hero or adventurer, Andrei, his lieutenant Elora and the Alliance will be waiting for you on %s!", b(l("Artis"))); + mesc l("You need to reach a certain point in Player Story in order to partake on this event. Non-repeatable."), 1; + next; + break; + ////////////////////////////////////////////////////////////////// + case 104: + mes ""; + mes ".:: "+ l("The Fifth Act") + " ::."; + next; + mesc l("With the brave sacrifice of Andrei Sakar to save our world, our future seems as bleak than ever."); + mesc l("There is no other solution; A desperate struggle is needed..."); + next; + mesc l("We must defeat the Monster King on his evil lair!"); + mesc l("Only then we may have peace!!"); + next; + break; + ////////////////////////////////////////////////////////////////// + case 105: + mes ""; + mes ".:: "+ l("The Final Act") + " ::."; + next; + // The Final Act has not been finished + // So this part is a bit weaker than others + mesc l("The Moubootaur is the final opponent."); + mesc l("How many lives will be claimed until he is put to rest?"); + next; + mesc l("According to the prophecy, he'll not stop, until life itself as we know today is removed from existence."); + next; + break; + default: + break; + } + + mesc l("We want to thank everyone who did this release possible."); + mes ""; + mesc l("Shall you have any inquiry, do not hesit to [@@mailto:admin@tmw2.org|send us an email@@]##b."); + mesc l("You can also read the [@@news|server news@@], or even [@@https://tmw2.org/news|older entries@@]."); + next; + return; +} + +function script EventHelp { + if ($EVENT$ == "") return; + + if ($EVENT$ == "Kamelot") { + ///////////////////////////////////////////////////////////////////////// + mesc ".:: " + l("Kamelot Raid") + " ::.", 2; + mes ""; + mes l("Group together your guild and challenge the evil power,"); + mes l("which creeps over Kamelot! During this event, the ancient"); + mes l("evil will return every day to curse King Arthur the Micksha."); + mes ""; + mes l("Is your guild strong enough to give Arthur at least good nights sleep?"); + mesc l("Location: Kamelot Castle, west of Hurnscald."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Valentine") { + mesc ".:: " + l("Valentine Day") + " ::.", 2; + mes ""; + mes l("It is time to send %s to your beloved ones!", getitemlink(BoxOfChocolates)); + mes l("Touch Soul Menhir and visit the Valentine's Island."); + mes l("Collect chocolate and love letters from the fluffies"); + mes l("and have Demure to send them to those you admire!"); + mes l("Don't forget to collect and eat any chocolate sent to you, too!"); + mes ""; + mes l("Witness, the power of love!"); + mesc l("Location: Valentine Island, access by Soul Menhir."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Easter") { + mesc ".:: " + l("Easter") + " ::.", 2; + mes ""; + mes l("The Enchanted Forest is now open to visitors!"); + mes l("Collect easter eggs, and exchange them with Lilica!"); + mes ""; + mes l("Compete for the first place, and remember to exchange silver"); + mes l("easter eggs for more useful things!"); + mes ""; + mes l("Who will collect the most?!"); + mesc l("Location: Enchanted Forest, access by Soul Menhir."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Patrick") { + mesc ".:: " + l("St. Patrick Day") + " ::.", 2; + mes ""; + mes l("A golden pot in woodlands shall reward those who wear green."); + mes l("Find it, spin it, and be bestowed in golden rewards!"); + mes ""; + mes l("Meanwhile, at 00h, 06h, 12h, 15h, 18h and 21h UTC,"); + mes l("legendary clovers will spawn almost everywhere."); + mes ""; + mes l("Feeling lucky?"); + mesc l("Location: North Woodlands, south of Nivalis."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Worker") { + mesc ".:: " + l("Worker Day") + " ::.", 2; + mes ""; + mes l("The International Worker Day is a traditional celebration,"); + mes l("focused on lower level players, and to cherish those whom work hard everyday."); + mes ""; + mes l("Visit the special event map, kill low level bosses, collect %s,", getitemlink(Pearl)); + mes l("exchange them, and have fun!"); + mes ""; + mes l("Happy %s!", "@@https://en.wikipedia.org/wiki/International_Workers%27_Day|"+l("international worker day")+"@@"); + mesc l("Location: Worker's Cave, access by Soul Menhir."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Thanksgiving") { + mesc ".:: " + l("Thanksgiving") + " ::.", 2; + mes ""; + mes l("It has been a great year, and the TMW2 Team would like to"); + mes l("give everyone gifts for spending so much time with us %%l"); + mes ""; + mes l("Every day you login, you'll be able to spin a card."); + mes l("the card will determine your gift - the more cards you spin,"); + mes l("the better your chances to get the best cards."); + mes ""; + mes l("So long, and thanks for all the fish!"); + mesc l("Location: Daily Login."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Christmas") { + mesc ".:: " + l("Christmas") + " ::.", 2; + mes ""; + mes l("By far, the most important holiday on Moubootaur Legends."); + mes l("Visit the Christmas Workshop, and talk to the chief in charge."); + mes ""; + mes l("Seems like they're having difficulty handling the demand, and"); + mes l("need help to send gifts to everyone! Compete for scoreboards,"); + mes l("but remember: Rewards will also be based on everyone's progress!"); + mes ""; + mes l("Merry Christmas, and a happy new year! \\o/"); + mesc l("Location: Christmas Workshop, Romantic Field, south of Nivalis."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Anniversary") { + mesc ".:: " + l("Moubootaur Legends Anniversary") + " ::.", 2; + mes ""; + mes l("Moubootaur Legends just got older! %%N"); + mes l("Base Experience Rate is now %d%%.", $BCONFB_EXPR); + mes ""; + mes l("Also known as TMW2 Day, it celebrates the server founding,"); + mes l("in March 2nd 2018."); + mes l("New chars, and reborn chars, will also begin at level 10."); + mes ""; + mes l("Invite your friends, and become a Moubootaur Legend!"); + mesc l("Location: N/A"), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Regnum") { + mesc ".:: " + l("Regnum Blessing") + " ::.", 2; + mes ""; + mes l("The Regnum Blessing causes all monsters in an area"); + mes l("to give %s experience!", l("triple")); + mes ""; + mes l("Farm there and get the best experience value!"); + mesc l("Location: %s", $REGNUM_BLESSMAP_H$), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Expo") { + mesc ".:: " + l("World Expo") + " ::.", 2; + mes ""; + mes l("A few times during the year, an independent team organizes a world exposition of special relics."); + mes l("However, %s stole the treasures which were being showcased!", $WORLDEXPO_ENEMY$); + mes l("Collect the treasured crystals from treasure chests!"); + mes ""; + mes l("While %s's reasons to do so remain unclear, a request was made.", $WORLDEXPO_ENEMY$); + mes l("Aurora will collect the crystals; And adventurers from the whole world shall look for them in the chests and return to Aurora!"); + mes ""; + mes l("This exposition has to be a success!"); + mesc l("Location: Treasure Chests, hidden inside dungeons."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Fishing") { + mesc ".:: " + l("Catch the Golden Fish!") + " ::.", 2; + mes ""; + mes l("A few times during the year, the \"golden wave\" migrates from the north pole to the south pole."); + mes l("As a result, while fishing you may randomly get a golden fish."); + mes ""; + mes l("These gold fishes multiply horribly and have no natural predator."); + mes l("If left unchecked for long, they will eventually become the only specie on the sea."); + mes l("Help controlling their population, and return those you fish to Aurora!"); + mes ""; + mes l("Catch the golden fish swarm!"); + mesc l("Location: Fishing spots in the water."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Mining") { + mesc ".:: " + l("Miners Union Research Request!") + " ::.", 2; + mes ""; + mes l("While mining bifs, a strange powder appeared."); + mes l("The effects and uses - if any - are still unknown."); + mes ""; + mes l("The Miners Union is trying to research this powder."); + mes l("A reward is promised to those whom cooperate."); + mes l("Collaborate by giving this powder to Aurora, before it vanishes!"); + mes ""; + mes l("Mysterious Powder expires after some time!"); + mesc l("Location: All bifs in the world."), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Candor") { + mesc ".:: " + l("Candor Battle Season") + " ::.", 2; + mes ""; + mes l("During this season, challenges to Crazyfefe will be free."); + mes l("Special battle modes will also be available."); + mes ""; + mes l("Will you accept his challenge?! Raise to the highest top score!"); + mesc l("Location: Candor B1F"), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Celestia") { + mesc ".:: " + l("Celestia Season") + " ::.", 2; + mes ""; + mes l("During this season, you can summon the Yeti King for free."); + mes l("This is the perfect opportunity to help Celestia."); + mes ""; + mes l("Mount your party and find the Yeti King!"); + mesc l("Location: Tulimshar Sewers / Hurnscald Town Hall"), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Gemini") { + mesc ".:: " + l("Gemini Season") + " ::.", 2; + mes ""; + mes l("During this season, Gemini Quest can be done with parties of 6."); + mes l("Not only that, but you get one free attempt every day!"); + mes ""; + mes l("Form your party and rescue Luvia Gemini from Isbamuth!"); + mesc l("Location: Forsaken Inn, South Woodlands"), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Rebirth") { + mesc ".:: " + l("Rebirth Season") + " ::.", 2; + mes ""; + mes l("During this season, monster point gain is increased in %d%%!", 10); + mes l("Not only that, but reborn characters will spawn at level %d!", 3); + mes ""; + mes l("Also: You'll be able to rebirth at Tulimshar with Jakod. What are you waiting for?!"); + mesc l("Location: Not applicable"), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Tower") { + mesc ".:: " + l("Dream Tower Appears") + " ::.", 2; + mes ""; + mes l("The dream towers have shown up. They have several floors filled with treasure and dangerous monsters."); + mes l("Every day, they disappear and a new one shows up on its place."); + mes ""; + mes l("You need a %s for each floor, and defeat the gatekeeper to advance.", getitemlink(EventDreamTicket)); + mes l("Floors cannot be re-visited, and rewards increase at each floor."); + mes ""; + mes l("These towers must hold unfathomable secrets from %s.", $DREAMTOWER_SAGE$); + mes l("We must find out what lies at the top, whatever the cost!"); + mesc l("Location: The Mana Plane, Dream Tower"), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Raid") { + mesc ".:: " + l("Boss Raid") + " ::.", 2; + mes ""; + mes l("This is terrible - a fiend from a parallel dimension has broken in ours!"); + mes l("We are unsure of the cause, but they must be repelled at any cost!"); + mes l("Meanwhile, the sages of destiny will prepare a way to kick them back to wherever they belong!"); + mes ""; + mes l("Can we keep %s at bay long enough?", $RAIDING_BOSS$); + mes l("Collect their %s as well, so our wizards can do their magic!", getitemlink(EventNaftalin)); + mesc l("Location: The Mana Plane, Showdown Chamber"), 3; + ///////////////////////////////////////////////////////////////////////// + } else if ($EVENT$ == "Olympics") { + mesc ".:: " + l("Magic Olympics") + " ::.", 2; + mes ""; + mes l("The Magic Olympics are held by the Academy Grand Masters roughly quarterly, and allow the brightest mages to acquire scholarships at the Magic Academy."); + mes l("Only one vouch can be acquired, and you cannot be vouched to Grand Master position. You need to have touched the Mana Stone to participate."); + mes ""; + mes l("Talk to Hocus in Porthos (via Menhir) to enroll."); + mesc l("Location: The Mana Plane, Porthos"), 3; + ///////////////////////////////////////////////////////////////////////// + } else { + mesc l("There's no help available for this event."); + ///////////////////////////////////////////////////////////////////////// + } + next; + return; +} + + + + + + + + +function script Journalman { + /* Random Fun Fact */ + npctalk3 any( + l("Halinarzo Church makes a party every Sunday! Only true believers are invited!"), + l("Beware the Terranite! Only @@ would be brave enough to challenge them!", ($MOST_HEROIC$ == "" ? "Andrei Sakar" : $MOST_HEROIC$)), + l("Terrible manaquake hits the whole world, causing dramatic changes!"), + l("Effects from The Great Fire are still felt by the people."), + l("Green Wars project says that @@ trees were planted by adventurers on the world!", format_number($TREE_PLANTED)), + l("The Team For A Better PvP says that @@ players were killed in fair matches!", format_number($PLAYERS_KILLED)), + l("The Alliance says that @@ monsters have been slain since Hurnscald Siege!", format_number($MONSTERS_KILLED)), + l("All hail @@ and Andrei Sakar, heroes of the world!", $MOST_HEROIC$)); + mesn getarg(0); + mesq l("Bonjour! I am @@, and I am from the Press! Read the latest news with me!", getarg(0)); + mes ""; + + /* Permanent/Weekly Events */ + if ($EVENT$ != "") { + mesc l("It's @@ (day)!", $EVENT$); + mesc l("Try talking to Soul Menhir or with any NPC on Tulimshar Center (near Soul Menhir)."); + } + + /* Short-Timed Events */ + if ($@GM_EVENT) + mesc l("An event is happening at Aeros! Hurry up!"); + else if ($@MK_SCENE) + mesc l("The Monster King is on the move!"); + + /* Seasonal Events */ + switch (season()) { + case SPRING: + mesc l("It's spring! Two lovely NPCs at woodlands can be found..."); + break; + case WINTER: + mesc l("It's winter! An NPC in Nivalis Town is freezing..."); + break; + case SUMMER: + mesc l("It's summer! Ched is having his usual contest, but Luffyx in Hurnscald is up to no good!"); + break; + case AUTUMN: + mesc l("It's autumn! There is no special event during autumn, only special drops."); + break; + default: + Exception(l("Invalid season: @@", season()), RB_DEFAULT|RB_SPEECH); break; + } + + /* Main Storyline */ + mes ""; + switch ($GAME_STORYLINE) { + case 0: + mesc l("The Monster King Army is occupying several towns! Brave players need to group and retake them!"); + break; + case 1: + mesc l("The Monster King Army left the cities! What will happen next? Anxiety grows!"); + break; + case 2: + .@def=100-(($MK_TEMPVAR+rand2(-1,1))*100/MK_SIEGE_TOTALPOWER); + if ($@MK_AGGRO >= 300) + .@st$=col(b(l("very mad")), 1); + else if ($@MK_AGGRO >= 200) + .@st$=col(b(l("very angry")), 6); + else if ($@MK_AGGRO >= 100) + .@st$=col(b(l("furious")), 7); + if ($@MK_AGGRO >= 50) + .@st$=col(b(l("angry")), 4); + else + .@st$=col(b(l("discontent")), 9); + mesc l("The Monster King Army is attacking towns at random, but players already reduced their organization to @@ %%!", .@def); + mesc l("With recent player activity, the Monster king is @@!", .@st$); + break; + case 3: + mesc l("The Monster Army is in complete disarray, sieges are much less frequent."); + mesc l("The Monster King Lair is not in a impregnable mountain, but in a small island now!!"); + mesc l("Will adventurers reach it? Will the random attacks at towns cease??"); + break; + case 4: + mesc l("We must defeat the Monster King on his evil lair!"); + mesc l("Only then we may have peace!!"); + break; + case 5: + mesc l("Moubootaur, please spare me..."); + break; + default: + Exception(l("I do now know what this means: GS-@@-ICXN-@@", $GAME_STORYLINE, $MK_TEMPVAR), RB_DEFAULT|RB_SPEECH); break; + } + + /* Fires of Steam */ + if ($FIRESOFSTEAM) { + mes ""; + if ($FIRESOFSTEAM >= 10) + mesc l("We'll never forget Andrei Sakar, the most courageous hero this world has ever had."); + else + mesc l("Andrei Sakar calls for all the brave in the town of Artis - Kolev!"); + } + next; + + do { + mes ""; + menuint + l("Thanks for your help!"), 1, + rif($EVENT$ != "", l("Event News")), 0, + rif($GAME_STORYLINE >= 5, l("The Final Act")), 105, + rif($GAME_STORYLINE >= 4, l("The Fifth Act")), 104, + rif($FIRESOFSTEAM, l("Interlude - Fourth Act")), 1031, + rif($GAME_STORYLINE >= 3, l("The Fourth Act")), 103, + rif($GAME_STORYLINE >= 2, l("The Third Act")), 102, + rif($GAME_STORYLINE >= 1, l("The Second Act")), 101, + rif($GAME_STORYLINE >= 0, l("The First Act")), 100, + l("Prologue"), 99, + l("Eh, I have to go."), 1; + mes ""; + if (@menuret > 1) + GameNews(@menuret); + if (@menuret == 0) + EventHelp(); + } while (@menuret != 1); + + mesn getarg(0); + mesq l("Good bye!"); + close; + return; +} diff --git a/npc/functions/npcmove.txt b/npc/functions/npcmove.txt new file mode 100644 index 0000000..612ab03 --- /dev/null +++ b/npc/functions/npcmove.txt @@ -0,0 +1,142 @@ +// Evol functions. +// Author: +// 4144 +// Description: +// Moving npc utility functions +// Variables: +// none + +function script initpath { + deletearray getvariableofnpc(.movepathcmd$, strnpcinfo(3)); + deletearray getvariableofnpc(.movepathy, strnpcinfo(3)); + deletearray getvariableofnpc(.movepathx, strnpcinfo(3)); + .@cnt = 0; + + for (.@f = 0; .@f < getargcount(); .@f = .@f + 3) + { + set getvariableofnpc(.movepathcmd$[.@cnt], strnpcinfo(3)), getarg(.@f); + set getvariableofnpc(.movepathx[.@cnt], strnpcinfo(3)), getarg(.@f + 1); + set getvariableofnpc(.movepathy[.@cnt], strnpcinfo(3)), getarg(.@f + 2); + .@cnt ++; + } + //debugmes "array size: " + str(getarraysize(getvariableofnpc(.movepath, strnpcinfo(3)))); + return; +} + +function script domoveaction { + //debugmes "domoveaction: " + str(getvariableofnpc(.movepos, strnpcinfo(3))); + .@pos = getvariableofnpc(.movepos, strnpcinfo(3)); + if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0) + return; + //debugmes "walking"; + .@cmd$ = getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3)); + //debugmes "cmd: " + .@cmd$; + + if (.@cmd$ == "move") + { + npcwalkto getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3)); + } + else if (.@cmd$ == "dir") + { + setnpcdir getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 2; + } + else if (.@cmd$ == "wait") + { + set getvariableofnpc(.waitticks, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + } + else if (.@cmd$ == "emote") + { + unitemote getnpcid(), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 2; + } + else if (.@cmd$ == "class") + { + .class = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 2; + } + else if (.@cmd$ == "warp") + { + movenpc strnpcinfo(3), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3)); + } + else if (.@cmd$ == "goto") + { + set getvariableofnpc(.movepos, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 0; + } + else if (.@cmd$ == "rmove") + { + getmapxy(.@mapName$, .@x, .@y, 1); + npcwalkto .@x + getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), .@y + getvariableofnpc(.movepathy[.@pos], strnpcinfo(3)); + } + else if (.@cmd$ == "speed") + { + .speed = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 2; + } + else if (.@cmd$ == "sit") + { + npcsit; + } + else if (.@cmd$ == "stand") + { + npcstand; + } + return 1; +} + +function script movetonextpos { + .@wait = getvariableofnpc(.waitticks, strnpcinfo(3)); + if (.@wait > 0) + { + .@wait --; + //debugmes "wait"; + set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait; + return; + } + .@true = 1; + while (.@true) + { + .@true = 0; + .@pos = getvariableofnpc(.movepos, strnpcinfo(3)); + //debugmes "movetonextpos: " + str(.@pos); + .@res = domoveaction(.@pos); + if (.@res == 1 || .@res == 2) + { + .@pos++; + if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3)))) + .@pos = 0; + set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos; + } + if (.@res == 0 || .@res == 2) + { + .@true = 1; + } + } + return; +} + +function script initialmove { + set getvariableofnpc(.movepos, strnpcinfo(3)), 0; + set getvariableofnpc(.waitticks, strnpcinfo(3)), -1; + movetonextpos; + return; +} + +function script getmovecmd { + .@pos = getvariableofnpc(.movepos, strnpcinfo(3)); + if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0) + return ""; + return getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3)); +} + +function script domovestep { + if (isunitwalking()) + { + initnpctimer; + end; + } + movetonextpos; + initnpctimer; + end; +} diff --git a/npc/functions/npcmovegraph.txt b/npc/functions/npcmovegraph.txt new file mode 100644 index 0000000..1391e0d --- /dev/null +++ b/npc/functions/npcmovegraph.txt @@ -0,0 +1,489 @@ +// Evol functions. +// Author: +// Travolta +// Description: +// Moving npc utility functions (graph-based) +// Variables: +// none + +function script initmovegraph { + deletearray getvariableofnpc(.movegraphcmd$, strnpcinfo(3)); + deletearray getvariableofnpc(.movegraphlabels$, strnpcinfo(3)); + deletearray getvariableofnpc(.movegraphweight, strnpcinfo(3)); + deletearray getvariableofnpc(.movegraphflags, strnpcinfo(3)); + deletearray getvariableofnpc(.movepos_y1, strnpcinfo(3)); + deletearray getvariableofnpc(.movepos_x1, strnpcinfo(3)); + deletearray getvariableofnpc(.movepos_x2, strnpcinfo(3)); + deletearray getvariableofnpc(.movepos_y2, strnpcinfo(3)); + .@cnt = 0; + + for (.@f = 0; .@f < getargcount();) + { + set getvariableofnpc(.movegraphlabels$[.@cnt], strnpcinfo(3)), getarg(.@f++); + set getvariableofnpc(.movepos_x1[.@cnt], strnpcinfo(3)), getarg(.@f++); + set getvariableofnpc(.movepos_y1[.@cnt], strnpcinfo(3)), getarg(.@f++); + if (!isstr(getarg(.@f, "label"))) + { + set getvariableofnpc(.movepos_x2[.@cnt], strnpcinfo(3)), getarg(.@f++); + set getvariableofnpc(.movepos_y2[.@cnt], strnpcinfo(3)), getarg(.@f++); + } + .@cnt ++; + } + return; +} + +function script findmovegraphlabel { + if (!getargcount()) + { + consolebug "findmovegraphlabel: no argument"; + return -1; + } + if (!isstr(getarg(0))) + { + consolebug "findmovegraphlabel: need string argument"; + return -1; + } + + .@arg$ = getarg(0); + for (.@i = 0; .@i < getarraysize(getvariableofnpc(.movegraphlabels$, strnpcinfo(3))); .@i++) + { + if (getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) == .@arg$) + return .@i; + } + + npcdebug "findmovegraphlabel: label not found: " + getarg(0); + return -1; +} + +/* setmovegraphcmd(fromPositionLabel,toPositionLabel[,moveChanceWeight[,moveFlags]],postCommand, ...); + * This function manipulates NPC moving graph. Before calling it, make sure + * `initmovegraph' was called. The function accepts 3-5 parameters (many times): + * fromPositionLabel, toPositionLabel -- starting and ending position of NPC move + * moveChanceWeight -- positive integer, represents the chance of moving in given direction. (optional) + * moveFlags -- if .mg_flags & moveFlags != 0, move is possible. (optional) + * postCommand -- either "moveon" (start moving to next location straight after arriving from + * fromPositionLabel to toPositionLabel) or a semicolon-separated set of commands + * ("wait 3", "emote 5" etc, see `execmovecmd') that will be executed after arrival. + * The commands don't have to end with ";moveon", it's executed in the end by default. + */ +function script setmovegraphcmd { + .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3))); + + for (.@f = 0; .@f < getargcount();) + { + .@from = findmovegraphlabel(getarg(.@f++)); + .@to = findmovegraphlabel(getarg(.@f++)); + .@weight = 1; + if (!isstr(getarg(.@f))) + .@weight = getarg(.@f++); + .@flags = 0xffff; + if (!isstr(getarg(.@f))) + .@flags = getarg(.@f++); + .@cmd$ = getarg(.@f++); + .@index = .@from * .@size + .@to; // emulation of 2d array + set getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3)), .@cmd$; + set getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3)), .@weight; + set getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3)), .@flags; + } + return; +} + +function script execmovecmd { + + explode(.@cmd$, getarg(0), " "); + + if (.@cmd$[0] == "moveon") + { + return 0; + } + else if (.@cmd$[0] == "dir") + { + .dir = atoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "sit") + { + npcsit; + } + else if (.@cmd$[0] == "stand") + { + npcstand; + } + else if (.@cmd$[0] == "wait") + { + set getvariableofnpc(.waitticks, strnpcinfo(3)), atoi(.@cmd$[1]); + return 1; + } + else if (.@cmd$[0] == "emote") + { + unitemote getnpcid(), atoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "class") + { + .class = atoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "warp") + { + .@pos = -1; + .@map$ = ""; + .@pos_idx = 1; + if (getarraysize(.@cmd$) == 3) + { + .@map$ = .@cmd$[1]; + .@pos_idx = 2; + } + .@pos = findmovegraphlabel(.@cmd$[.@pos_idx]); + if (.@pos != -1) + { + .@x = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3)); + .@y = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3)); + if (getstrlen(.@map$) > 0) + unitwarp getnpcid(), .@map$, .@x, .@y; + else + movenpc strnpcinfo(3), .@x, .@y; + set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos; + } + else + { + consolewarn "execmovecmd: unknown WARP destination label: " + .@cmd$[1]; + } + } + else if (.@cmd$[0] == "call") + { + switch (getarraysize(.@cmd$)) + { + case 1: + consolewarn "execmovecmd: CALL command needs some parameters"; + return 0; + case 2: + return callfunc(.@cmd$[1]); + break; + case 3: + return callfunc(.@cmd$[1], .@cmd$[2]); + case 4: + default: + return callfunc(.@cmd$[1], .@cmd$[2], .@cmd$[3]); + } + } + else if (.@cmd$[0] == "speed") + { + .speed = atoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "say") + { + deletearray .@cmd$[0], 1; + .@msg$=implode(.@cmd$, " "); + if (.@msg$ != "" && .@msg$ != " ") + npctalk .@msg$; + else + consolewarn "Invalid message passed to execmovecmd/npctalk"; + } + else if (.@cmd$[0] == "debugmes") + { + deletearray .@cmd$[0], 1; + debugmes implode(.@cmd$, " "); + } + else if (.@cmd$[0] == "flags") + { + set getvariableofnpc(.mg_flags, strnpcinfo(3)), axtoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "flags_0") + { + .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3)); + .@flags &= ~axtoi(.@cmd$[1]); + set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags; + } + else if (.@cmd$[0] == "flags_1") + { + .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3)); + .@flags |= axtoi(.@cmd$[1]); + set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags; + } + else + { + consolebug "Unknown move graph cmd: " + .@cmd$[0]; + } + return 0; +} + +function script getnextmovecmd { + .@cmds$ = getvariableofnpc(.nextcmd$, strnpcinfo(3)); + .@firstCmd$ = .@cmds$; + .@restCmd$ = "moveon"; + .@index = strpos(.@cmds$, ";"); + if (.@index >= 0) + { + .@firstCmd$ = substr(.@cmds$, 0, .@index - 1); + .@restCmd$ = substr(.@cmds$, .@index + 1, getstrlen(.@cmds$) - 1); + } + // npcdebug "firstCmd = " + .@firstCmd$ + " restCmd = " + .@restCmd$; + set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@restCmd$; + return strip(.@firstCmd$); +} + +// getrandompoint(x1,y1,x2,y2) +// -- Get a random walkable point within a map rectangle +// x1, y1 -- top-left corner of rectangle +// x2, y2 -- bottom-right corner of rectangle +// Returns 0 on success and -1 on error; +// Since we cannot return multiple values, the random +// coordinates are stored in NPC variables .move__rand_x, .move__rand_y +function script getrandompoint { + if (getargcount() < 4) + { + consolebug "error: getrandompoint(x1, y1, x2, y2) takes 4 arguments"; + return -1; + } + + .@max_pokes = 10; + .@x1 = getarg(0); + .@y1 = getarg(1); + .@x2 = getarg(2); + .@y2 = getarg(3); + .@rx = -1; .@ry = -1; + + getmapxy(.@map$, .@cx, .@cy, 1); // npc location + + // let's try max_pokes random cells + for (.@poke = 0; .@poke < .@max_pokes; .@poke++) + { + .@rx = rand(.@x1, .@x2); + .@ry = rand(.@y1, .@y2); + if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass)) + goto L_Found; + } + + // we check each cell from random middle point to the end + for (;.@rx <= .@x2; .@rx++) + { + for (;.@ry <= .@y2; .@ry++) + if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass)) + goto L_Found; + .@ry = .@y1; + } + + // we check the rectangle from beginning to end + for (.@rx = .@x1; .@rx <= .@x2; .@rx++) + for (.@ry = .@y1; .@ry <= .@y2; .@ry++) + if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass)) + goto L_Found; + + // finally, if we don't find anything + consolebug "error: getrandompoint: cannot find walkable cell in rectangle [(" + .@x1 + "," + .@y1 + ") , (" + .@x2 + "," + .@y2 + ")]"; + return -1; + +L_Found: + set getvariableofnpc(.move__rand_x, strnpcinfo(3)), .@rx; + set getvariableofnpc(.move__rand_y, strnpcinfo(3)), .@ry; + return 0; +} + +// wrapper function for npcwalkto. It can accept 4 parameters. +// If #3 and #4 params are set, the walkto location is chosen +// from rectangle (x1,y1,x2,y2). +// It sets the npc variables .move_target_x, .move_target_y +// that are used to resume NPC walking +// Returns 1 if walking is possible, 0 otherwise; +function script mg_npcwalkto { + if (getargcount() < 2) + { + consolebug "usage: mg_npcwalkto(x1,y1[,x2,y2])"; + return -1; + } + + .@x = getarg(0); + .@y = getarg(1); + .@x2 = getarg(2); + .@y2 = getarg(3); + + if (getargcount() >= 4 && .@x2 > 0 && .@y2 > 0) + if (!getrandompoint(.@x, .@y, .@x2, .@y2)) + { + .@x = getvariableofnpc(.move__rand_x, strnpcinfo(3)); + .@y = getvariableofnpc(.move__rand_y, strnpcinfo(3)); + } + else + return 0; + + if (npcwalkto(.@x, .@y)) + { + set getvariableofnpc(.move_target_x, strnpcinfo(3)), .@x; + set getvariableofnpc(.move_target_y, strnpcinfo(3)), .@y; + return 1; + } + return 0; +} + +function script movetonextpoint { + .@wait = getvariableofnpc(.waitticks, strnpcinfo(3)); + if (.@wait > 0) + { + .@wait--; + set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait; + return; + } + + .@nextcmd$ = ""; + while (.@nextcmd$ != "moveon") + { + .@nextcmd$ = getnextmovecmd(); + npcdebug " " + .@nextcmd$; + if (execmovecmd(.@nextcmd$)) + return; + } + + // choose a random path from all possible paths + .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3))); + .@pos = getvariableofnpc(.movepos, strnpcinfo(3)); + .@curr_flags = getvariableofnpc(.mg_flags, strnpcinfo(3)); + .@cur = 0; + .@weight_sum = 0; + // .@dbg$ = getvariableofnpc(.movegraphlabels$[.@pos], strnpcinfo(3)) + ": "; + + for (.@i = 0; .@i < .@size; .@i++) + { + .@index = .@pos * .@size + .@i; + .@cmd$ = getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3)); + .@flags = getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3)); + if (.@cmd$ != "" && + .@curr_flags & .@flags) + { + .@nextpos[.@cur] = .@i; + .@weights[.@cur] = getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3)); + // .@dbg$ += getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) + "=" + .@weights[.@cur] + " "; + .@weight_sum += .@weights[.@cur]; + .@cur++; + } + } + // npcdebug .@dbg$; + + if (!.@weight_sum) + { + npcdebug("error: cannot pick next walk point. flags=" + + getvariableofnpc(.mg_flags, strnpcinfo(3))); + return; + } + + .@pick_tries = 0; +L_TryPick: + // pick a random number based on weight_sum + .@rnd = rand(.@weight_sum); + .@k = -1; .@weight_sum = 0; + while (.@rnd >= .@weight_sum) + { + .@k++; + .@weight_sum += .@weights[.@k]; + } + + .@next_idx = .@nextpos[.@k]; + .@next_x1 = getvariableofnpc(.movepos_x1[.@next_idx], strnpcinfo(3)); + .@next_y1 = getvariableofnpc(.movepos_y1[.@next_idx], strnpcinfo(3)); + .@next_x2 = getvariableofnpc(.movepos_x2[.@next_idx], strnpcinfo(3)); + .@next_y2 = getvariableofnpc(.movepos_y2[.@next_idx], strnpcinfo(3)); + + if (!mg_npcwalkto(.@next_x1, .@next_y1, .@next_x2, .@next_y2)) + { + if (.@pick_tries < 10) + { + .@pick_tries++; + goto L_TryPick; + } + + // move to a nearby position + .@x1 = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3)); + .@y1 = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3)); + .@x2 = getvariableofnpc(.movepos_x2[.@pos], strnpcinfo(3)); + .@y2 = getvariableofnpc(.movepos_y2[.@pos], strnpcinfo(3)); + mg_npcwalkto(.@x1, .@y1, .@x2, .@y2); + set getvariableofnpc(.nextcmd$, strnpcinfo(3)), "wait 1"; + + return; + } + + if (getvariableofnpc(.debug, strnpcinfo(3))) + { + getmapxy(.@map$, .@cx, .@cy, 1); + .@dist = distance(.@cx, .@cy, .@next_x1, .@next_y1); + npcdebug("moving to " + getvariableofnpc(.movegraphlabels$[.@next_idx], strnpcinfo(3)) + + " ("+ getvariableofnpc(.move_target_x, strnpcinfo(3)) + + "," + getvariableofnpc(.move_target_y, strnpcinfo(3)) + + ") [distance=" + .@dist + + "] flags=" + getvariableofnpc(.mg_flags, strnpcinfo(3))); + } + + .@nextcmd$ = getvariableofnpc(.movegraphcmd$[.@pos * .@size + .@next_idx], strnpcinfo(3)); + set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$; + set getvariableofnpc(.movepos, strnpcinfo(3)), .@next_idx; + return; +} + +// initial actions for npc when using move graphs. +// function can accept 2 arguments: +// 1: action sequence, for example "speed 200; dir 4". Default is "moveon" +// 2: start point label. Default is #0 from move graph labels +function script firstmove { + .@nextcmd$ = getarg(0, "moveon"); + .@initpos = findmovegraphlabel(getarg(1, "")); + if (.@initpos < 0) .@initpos = 0; + + set getvariableofnpc(.movepos, strnpcinfo(3)), .@initpos; + movenpc strnpcinfo(3), getvariableofnpc(.movepos_x1[.@initpos], strnpcinfo(3)), + getvariableofnpc(.movepos_y1[.@initpos], strnpcinfo(3)); + set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$; + set getvariableofnpc(.waitticks, strnpcinfo(3)), -1; + set getvariableofnpc(.mg_flags, strnpcinfo(3)), 0xffff; + movetonextpoint; + return; +} + +function script npc_pausemove { + stopnpctimer; + .@move_after = 0; + + if (isunitwalking()) + { + .@move_after = 1; + npcwalkto .x, .y; + npcstop; + } + set getvariableofnpc(.move_after_pause, strnpcinfo(3)), .@move_after; + + return 0; +} + +function script npc_resumemove { + startnpctimer; + + if (getvariableofnpc(.move_after_pause, strnpcinfo(3))) + { + .@x = getvariableofnpc(.move_target_x, strnpcinfo(3)); + .@y = getvariableofnpc(.move_target_y, strnpcinfo(3)); + npcwalkto .@x, .@y; + } + + return 0; +} + +// npc_turntoxy(x,y) +// turn npc toward an object at position (x,y) +function script npc_turntoxy { + .@target_x = getarg(0); + .@target_y = getarg(1); + .@dx = abs(.@target_x - .x); + .@dy = abs(.@target_y - .y); + + if (.@dx > .@dy) + .dir = .@target_x >= .x ? 6 : 2; + else + .dir = .@target_y >= .y ? 0 : 4; + + return 0; +} + +function script dographmovestep { + if (!isunitwalking()) + { + movetonextpoint; + } + initnpctimer; + end; +} diff --git a/npc/functions/nurse.txt b/npc/functions/nurse.txt new file mode 100644 index 0000000..7335c74 --- /dev/null +++ b/npc/functions/nurse.txt @@ -0,0 +1,84 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Nurse for all hurt players + +// Name, Base Price, Price Multiplier +// getarg(3) switches functions: +// 1 - Only healing dialog (returns price) +// 2 - Only core healing +// 3 - everything (default) +function script Nurse { + // Handle redirects + .@d=getarg(3, 3); + + // Calculate price + .@price=(MaxHp-Hp)/getarg(1,5); + .@price=.@price+getarg(2, 10); + if (BaseLevel <= 15) .@price=(.@price/10); + else if (BaseLevel <= 20) .@price=(.@price/5); + else .@price=(.@price/2); + + + // 1 - Only Healing Dialog and return .@price + if (.@d & 1) {//&1 + mes ""; + mesn getarg(0); + + // Random message + .@temp = rand2(1,4); + switch (.@temp) { + case 1: + mesq l("You don't look too well; let me treat your wounds."); + break; + case 2: + mesq l("I will make quick work of your wounds."); + break; + case 3: + mesq l("Need a healing?"); + break; + case 4: + mesq l("Sometimes you just need to run from battle."); + break; + } + mes ""; + + // Skip menu flag + if (!(.@d & 2)) + return .@price; + + mesq l("For you, it'll be @@ GP.", .@price); + mes ""; + select + rif(Zeny >= .@price, l("Please heal me!")), + l("Another time, maybe."); + + if (@menu == 2) { + closedialog; + goodbye; + close; + } + + }// & 1 + + // Allowed Nurse to do the healing + if (.@d & 2) { // &2 + + // Heal persona + mes ""; + set Zeny, Zeny - .@price; + sc_end(SC_POISON); + sc_end(SC_SLOWPOISON); + percentheal 100,100; // We can also use "recovery()" but that revives players :o + mesn getarg(0); + @temp = rand2(1,4); + if(@temp == 1) mesq l("Here you go!"); + if(@temp == 2) mesq l("Painless, wasn't it?"); + if(@temp == 3) mesq l("You should be more careful."); + if(@temp == 4) mesq l("Much better, right?!"); + close; + + }//&2 + return; +} diff --git a/npc/functions/openbook.txt b/npc/functions/openbook.txt new file mode 100644 index 0000000..7cf87dd --- /dev/null +++ b/npc/functions/openbook.txt @@ -0,0 +1,27 @@ +// Evol functions. +// Author: +// Reid +// Description: +// Narrator dialogue to show the selected book. +// Variables: +// @book_name$ = The name of the book to read. + +function script openbook { + .@book_name$ = "\"" + l(getarg(0, getvariableofnpc(.book_name$, strnpcinfo(0)))) + "\""; + + mesc l("You open a book named @@. Do you want to read it?", .@book_name$); + next; + + return (select("Yes.", "No.") == 1); +} + +function script openbookshelf { + .@book_name$ = "\"" + l(getarg(0, getvariableofnpc(.book_name$, strnpcinfo(0)))) + "\""; + + narrator S_LAST_NEXT, + l("You see a dust covered book on the shelf... The name of the book is @@.", .@book_name$), + l("Do you want to read it?"); + + return (select("Yes.", "No.") == 1); +} + diff --git a/npc/functions/permissions.txt b/npc/functions/permissions.txt new file mode 100644 index 0000000..2838f7a --- /dev/null +++ b/npc/functions/permissions.txt @@ -0,0 +1,25 @@ +// TMW2 scripts. +// Author: +// Jesusalva +// Description: +// Checks player GM levels + +function script is_gm { + return (getgmlevel() >= 60); +} + +function script is_master { + return (getgmlevel() >= 80); +} + +function script is_admin { + return (getgmlevel() >= 99); +} + +function script is_staff { + return (getgmlevel() >= 5); +} + +function script is_sponsor { + return (getgmlevel()); +} diff --git a/npc/functions/petsales.txt b/npc/functions/petsales.txt new file mode 100644 index 0000000..b986346 --- /dev/null +++ b/npc/functions/petsales.txt @@ -0,0 +1,43 @@ +// TMW-2 Script +// Author: Jesusalva +// Desc: Grand Hunter Pet Quest Utils. +// Note: Remember to update GHQ and pet_detective on LoF Village (017-1) + +// Core logic for Pet Detective. Doesn't checks for stock, allowing exploits. +// MobID, ItemID, Base Value +function script PDQ_InnerSwitch { + + .@mid=getarg(0); + .@iid=getarg(1); + .@val=getarg(2, 30); + + .@qid=GHQ_GetQuestIDByMonsterID(.@mid); + .@price=.@val*(PETMEMO[.@qid]+1)+PETMEMO[.@qid]; + + mesn; + mesq l("That'll cost you @@/@@ @@.", countitem(.@iid), .@price, getitemlink(.@iid)); + next; + if (askyesno() == ASK_YES) { + if (countitem(.@iid) >= .@price) { + delitem .@iid, .@price; + makepet .@mid; + getexp .@price*BaseLevel, 0; + PETMEMO[.@qid]+=1; + mesn; + mesq l("There you go!"); + return 1; + } else { + mesn; + mesq l("You don't have that, lying is bad for health..."); + } + } + return 0; +} + +// Returns GHMEMO[GHQ_GetQuestIDByMonsterID(PetName)] +// Arguments: PetName (eg. Maggot) +function script PDQ_CheckGHQ { + .@mid=GHQ_GetQuestIDByMonsterID(getarg(0)); + return GHMEMO[.@mid]; +} + diff --git a/npc/functions/politics.txt b/npc/functions/politics.txt new file mode 100644 index 0000000..7c9dd5c --- /dev/null +++ b/npc/functions/politics.txt @@ -0,0 +1,657 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Central Town Political System Controller +// Note: The office is NOT Mayor, we have from kings to mayors here and you're +// not a citzen (yet). It'll be... I'll think in a name better than Minister. +// Perhaps, Town Administrator. +// Variables: +// $LOC_MAYOR$ - Stores the name of current Hurnscald Mayor +// $LOC_MONEY - Total money reserves of Hurnscald +// $LOC_TAX - How much in % is charged as taxes. (OnBuy income) +// $LOC_EXPORT - Defines how much Hurnscald exports (weekly income) +// $LOC_REPUTATION - Town reputation. Affects Max Tax and Weekly Income; +// Note: Tax cannot exceed 10% ie 1000. Reputation must be between 0 and 100. +// +// "Temporary Arrays": +// $LOC_CANDIDATE$ - Candidate for Office +// $LOC_VOTES - Number of votes of Candidate + +// Proccess Taxes from purchases +// PurchaseTaxes( Location ) +function script PurchaseTaxes { + .@tax=0; + for (.@i=0; .@i < getarraysize(@bought_nameid); .@i++) { + // Note: Some NPC might not + .@price=getiteminfo(@bought_nameid[.@i], ITEMINFO_BUYPRICE); + .@tax+=.@price*@bought_quantity[.@i]; + } + .@loc$=strtoupper(getarg(0, LOCATION$)); + .@vat=getd("$"+.@loc$+"_TAX"); + .@tax=.@tax*.@vat/10000; + if (.@tax) { + debugmes "%s paid %d in taxes to %s prefecture!", strcharinfo(0), .@tax, .@loc$; + .@gp=getd("$"+.@loc$+"_MONEY"); + setd("$"+.@loc$+"_MONEY", .@gp+.@tax); + .@tp=POL_LocToTP(.@loc$); + if (#EXILED & .@tp) + Zeny=max(0, Zeny-.@tax); + } + return; +} + +// Proccess Taxes from sales (20% from purchase tax) +// SaleTaxes( Location ) +function script SaleTaxes { + .@tax=0; + for (.@i=0; .@i < getarraysize(@sold_nameid); .@i++) { + // Note: Some NPC might not + .@price=getiteminfo(@sold_nameid[.@i], ITEMINFO_SELLPRICE); + .@tax+=.@price*@sold_quantity[.@i]; + } + .@loc$=strtoupper(getarg(0, LOCATION$)); + .@vat=getd("$"+.@loc$+"_TAX"); + .@vat=.@vat*3/4; // Only 75% of purchase tax (defaults to 0.75% I guess) + .@tax=.@tax*.@vat/10000; + if (.@tax) { + debugmes "Sale: %s paid %d in taxes to %s prefecture!", strcharinfo(0), .@tax, .@loc$; + .@gp=getd("$"+.@loc$+"_MONEY"); + setd("$"+.@loc$+"_MONEY", .@gp+.@tax); + dispbottom col(l("%s is happy because you've paid %d GP in taxes!", getd("$"+.@loc$+"_MAYOR$"), .@tax), 1); + Zeny-=.@tax; // Player must pay the taxes + .@tp=POL_LocToTP(.@loc$); + /* + if (#EXILED & .@tp) + Zeny=max(0, Zeny-.@tax); + */ + } + return; +} + +// Adjusts prices for crafts +// POL_AdjustPrice( price, {TOWNCODE} ) +function script POL_AdjustPrice { + .@p=getarg(0); + .@l$=strtoupper(getarg(1, LOCATION$)); + + // Town Admin is always tax exempt + if (getd("$" + .@l$ + "_MAYOR$") == strcharinfo(0)) + .@p=0; + + // Town option for tax immunity + if (getd("$" + .@l$ + "_TAXOFF")) { + .@vat=getd("$"+.@l$+"_TAX"); + .@tax=.@p*.@vat/10000; + .@p-=.@tax; + } + + // Return adjusted price + return .@p; +} + +// Change a town money at player expense +// POL_PlayerMoney( price, {TOWNCODE} ) +function script POL_PlayerMoney { + .@p=getarg(0); + .@l$=strtoupper(getarg(1, LOCATION$)); + .@t=POL_LocToTP(.@l$); + + Zeny-=.@p; + + // Town option for tax immunity + if (getd("$" + .@l$ + "_TAXOFF")) { + return 0; + } + + // Calculate tax and add to town vault + .@vat=getd("$"+.@l$+"_TAX"); + .@tax=.@p*.@vat/10000; + .@GP=getd("$" + .@l$ + "_MONEY"); + if (playerattached()) { + if (is_gm()) + dispbottom l("GP: %d + %d", .@GP, .@tax); + } + setd("$" + .@l$ + "_MONEY", .@GP+.@tax); + + // Return the taxes paid + return .@tax; +} + +- script Politics NPC_HIDDEN,{ + +// Daily Paperwork Penalty +OnClock2359: + if ($TULIM_DAILYQUEST != gettimeparam(GETTIME_DAYOFMONTH) && $TULIM_MAYOR$ != "Jesus Saves") + $TULIM_REPUTATION=limit(0, $TULIM_REPUTATION-2, 100); + if ($HALIN_DAILYQUEST != gettimeparam(GETTIME_DAYOFMONTH) && $HALIN_MAYOR$ != "Jesus Saves") + $HALIN_REPUTATION=limit(0, $HALIN_REPUTATION-2, 100); + if ($HURNS_DAILYQUEST != gettimeparam(GETTIME_DAYOFMONTH) && $HURNS_MAYOR$ != "Jesus Saves") + $HURNS_REPUTATION=limit(0, $HURNS_REPUTATION-2, 100); + if ($NIVAL_DAILYQUEST != gettimeparam(GETTIME_DAYOFMONTH) && $NIVAL_MAYOR$ != "Jesus Saves") + $NIVAL_REPUTATION=limit(0, $NIVAL_REPUTATION-2, 100); + if ($LOF_DAILYQUEST != gettimeparam(GETTIME_DAYOFMONTH) && $LOF_MAYOR$ != "Jesus Saves") + $LOF_REPUTATION=limit(0, $LOF_REPUTATION-2, 100); + if ($FROSTIA_DAILYQUEST != gettimeparam(GETTIME_DAYOFMONTH) && $FROSTIA_MAYOR$ != "Jesus Saves") + $FROSTIA_REPUTATION=limit(0, $FROSTIA_REPUTATION-2, 100); + if ($CANDOR_DAILYQUEST != gettimeparam(GETTIME_DAYOFMONTH) && $CANDOR_MAYOR$ != "Jesus Saves") + $CANDOR_REPUTATION=limit(0, $CANDOR_REPUTATION-2, 100); + end; + +OnSun0000: + // Weekly reputation-based income + $TULIM_MONEY+=$TULIM_EXPORT*limit(10, $TULIM_REPUTATION, 100)/100; + $HALIN_MONEY+=$HALIN_EXPORT*limit(10, $HALIN_REPUTATION, 100)/100; + $HURNS_MONEY+=$HURNS_EXPORT*limit(10, $HURNS_REPUTATION, 100)/100; + $LOF_MONEY+=$LOF_EXPORT*limit(10, $LOF_REPUTATION, 100)/100; + $NIVAL_MONEY+=$NIVAL_EXPORT*limit(10, $NIVAL_REPUTATION, 100)/100; + $FROSTIA_MONEY+=$FROSTIA_EXPORT*limit(10, $FROSTIA_REPUTATION, 100)/100; + $CANDOR_MONEY+=$CANDOR_EXPORT*limit(10, $CANDOR_REPUTATION, 100)/100; + + // Weekly tax-based income + $TULIM_MONEY+=$TULIM_EXPORT*$TULIM_TAX*7/10000; + $HALIN_MONEY+=$HALIN_EXPORT*$HALIN_TAX*7/10000; + $HURNS_MONEY+=$HURNS_EXPORT*$HURNS_TAX*7/10000; + $LOF_MONEY+=$LOF_EXPORT*$LOF_TAX*7/10000; + $NIVAL_MONEY+=$NIVAL_EXPORT*$NIVAL_TAX*7/10000; + $FROSTIA_MONEY+=$FROSTIA_EXPORT*$FROSTIA_TAX*7/10000; + $CANDOR_MONEY+=$CANDOR_EXPORT*$CANDOR_TAX*7/10000; + + // Send salary to Town Administrators (20% from exports and 5GP/reputation) + // Strange Coins: 1 each 10 reputation points + .@msg$=sprintf( + "You've received the money for the term (%02d/%02d, the %dth legislature)", + gettime(GETTIME_DAYOFMONTH), + gettime(GETTIME_MONTH), + gettimeparam(GETTIME_WEEKDAY)); + + .@tax=$TULIM_EXPORT/500; + .@tax+=$TULIM_REPUTATION*5; + .@tax=min($TULIM_MONEY, .@tax); + .@cp=limit(1, $TULIM_REPUTATION/10, 10); + $TULIM_MONEY-=.@tax; + rodex_sendmail(gf_charnameid($TULIM_MAYOR$), "Tulimshar Townhall", "Term Income", .@msg$, .@tax, StrangeCoin, .@cp); + + .@tax=$HALIN_EXPORT/500; + .@tax+=$HALIN_REPUTATION*5; + .@tax=min($HALIN_MONEY, .@tax); + .@cp=limit(1, $HALIN_REPUTATION/10, 10); + $HALIN_MONEY-=.@tax; + rodex_sendmail(gf_charnameid($HALIN_MAYOR$), "Halinarzo Townhall", "Term Income", .@msg$, .@tax, StrangeCoin, .@cp); + + .@tax=$HURNS_EXPORT/500; + .@tax+=$HURNS_REPUTATION*5; + .@tax=min($HURNS_MONEY, .@tax); + .@cp=limit(1, $HURNS_REPUTATION/10, 10); + $HURNS_MONEY-=.@tax; + rodex_sendmail(gf_charnameid($HURNS_MAYOR$), "Hurnscald Townhall", "Term Income", .@msg$, .@tax, StrangeCoin, .@cp); + + .@tax=$LOF_EXPORT/500; + .@tax+=$LOF_REPUTATION*5; + .@tax=min($LOF_MONEY, .@tax); + .@cp=limit(1, $LOF_REPUTATION/10, 10); + $LOF_MONEY-=.@tax; + rodex_sendmail(gf_charnameid($LOF_MAYOR$), "LoF Townhall", "Term Income", .@msg$, .@tax, StrangeCoin, .@cp); + + .@tax=$NIVAL_EXPORT/500; + .@tax+=$NIVAL_REPUTATION*5; + .@tax=min($NIVAL_MONEY, .@tax); + .@cp=limit(1, $NIVAL_REPUTATION/10, 10); + $NIVAL_MONEY-=.@tax; + rodex_sendmail(gf_charnameid($NIVAL_MAYOR$), "Nivalis Townhall", "Term Income", .@msg$, .@tax, StrangeCoin, .@cp); + + /* Towns with volunteer town admins + .@tax=$FROSTIA_EXPORT/500; + .@tax+=$FROSTIA_REPUTATION*5; + .@tax=min($FROSTIA_MONEY, .@tax); + .@cp=limit(1, $FROSTIA_REPUTATION/10, 10); + $FROSTIA_MONEY-=.@tax; + rodex_sendmail(gf_charnameid($FROSTIA_MAYOR$), "Frostia Townhall", "Term Income", .@msg$, .@tax, StrangeCoin, .@cp); + + .@tax=$CANDOR_EXPORT/500; + .@tax+=$CANDOR_REPUTATION*5; + .@tax=min($CANDOR_MONEY, .@tax); + .@cp=limit(1, $CANDOR_REPUTATION/10, 10); + $CANDOR_MONEY-=.@tax; + rodex_sendmail(gf_charnameid($CANDOR_MAYOR$), "Candor Townhall", "Term Income", .@msg$, .@tax, StrangeCoin, .@cp); + */ + + // Conduct elections + .@w=array_highest($TULIM_VOTES); + if ($TULIM_CANDIDATE$[.@w] != "") + $TULIM_MAYOR$=$TULIM_CANDIDATE$[.@w]; + deletearray($TULIM_CANDIDATE$); + deletearray($TULIM_VOTES); + array_push($TULIM_CANDIDATE$, ($TULIM_REPUTATION < 15 ? "Jesus Saves" : $TULIM_MAYOR$)); + array_push($TULIM_VOTES, 0); + + .@w=array_highest($HALIN_VOTES); + if ($HALIN_CANDIDATE$[.@w] != "") + $HALIN_MAYOR$=$HALIN_CANDIDATE$[.@w]; + deletearray($HALIN_CANDIDATE$); + deletearray($HALIN_VOTES); + array_push($HALIN_CANDIDATE$, ($HALIN_REPUTATION < 15 ? "Jesus Saves" : $HALIN_MAYOR$)); + array_push($HALIN_VOTES, 0); + + .@w=array_highest($HURNS_VOTES); + if ($HURNS_CANDIDATE$[.@w] != "") + $HURNS_MAYOR$=$HURNS_CANDIDATE$[.@w]; + deletearray($HURNS_CANDIDATE$); + deletearray($HURNS_VOTES); + array_push($HURNS_CANDIDATE$, ($HURNS_REPUTATION < 15 ? "Jesus Saves" : $HURNS_MAYOR$)); + array_push($HURNS_VOTES, 0); + + .@w=array_highest($LOF_VOTES); + if ($LOF_CANDIDATE$[.@w] != "") + $LOF_MAYOR$=$LOF_CANDIDATE$[.@w]; + deletearray($LOF_CANDIDATE$); + deletearray($LOF_VOTES); + array_push($LOF_CANDIDATE$, ($LOF_REPUTATION < 15 ? "Jesus Saves" : $LOF_MAYOR$)); + array_push($LOF_VOTES, 0); + + .@w=array_highest($NIVAL_VOTES); + if ($NIVAL_CANDIDATE$[.@w] != "") + $NIVAL_MAYOR$=$NIVAL_CANDIDATE$[.@w]; + deletearray($NIVAL_CANDIDATE$); + deletearray($NIVAL_VOTES); + array_push($NIVAL_CANDIDATE$, ($NIVAL_REPUTATION < 15 ? "Jesus Saves" : $NIVAL_MAYOR$)); + array_push($NIVAL_VOTES, 0); + + .@w=array_highest($FROSTIA_VOTES); + if ($FROSTIA_CANDIDATE$[.@w] != "") + $FROSTIA_MAYOR$=$FROSTIA_CANDIDATE$[.@w]; + deletearray($FROSTIA_CANDIDATE$); + deletearray($FROSTIA_VOTES); + array_push($FROSTIA_CANDIDATE$, ($FROSTIA_REPUTATION < 15 ? "Jesus Saves" : $FROSTIA_MAYOR$)); + array_push($FROSTIA_VOTES, 0); + + .@w=array_highest($CANDOR_VOTES); + if ($CANDOR_CANDIDATE$[.@w] != "") + $CANDOR_MAYOR$=$CANDOR_CANDIDATE$[.@w]; + deletearray($CANDOR_CANDIDATE$); + deletearray($CANDOR_VOTES); + array_push($CANDOR_CANDIDATE$, ($CANDOR_REPUTATION < 15 ? "Jesus Saves" : $CANDOR_MAYOR$)); + array_push($CANDOR_VOTES, 0); + + // Notify new mayors of their victory + rodex_sendmail(gf_charnameid($TULIM_MAYOR$), "Tulimshar Townhall", "Election Victory", "You've been elected to the office!"); + rodex_sendmail(gf_charnameid($HALIN_MAYOR$), "Halinarzo Townhall", "Election Victory", "You've been elected to the office!"); + rodex_sendmail(gf_charnameid($HURNS_MAYOR$), "Hurnscald Townhall", "Election Victory", "You've been elected to the office!"); + rodex_sendmail(gf_charnameid($LOF_MAYOR$), "LoF Townhall", "Election Victory", "You've been elected to the office!"); + rodex_sendmail(gf_charnameid($NIVAL_MAYOR$), "Nivalis Townhall", "Election Victory", "You've been elected to the office!"); + // Towns where admins are named, instead of elected + //rodex_sendmail(gf_charnameid($FROSTIA_MAYOR$), "Frostia Townhall", "Election Victory", "You've been elected to the office!"); + //rodex_sendmail(gf_charnameid($CANDOR_MAYOR$), "Candor Townhall", "Election Victory", "You've been elected to the office!"); + + end; +} +///////////////////////// + + +// Dialog helpers +// General info +// POL_Information( ) +function script POL_Information { + + mesc l("Weekly, at Sunday 00:00, elections are held."); + mesc l("The current town administrator will be inscribed for re-election automatically."); + mesc l("However, if town has less than 15 reputation, they'll need to re-apply manually, and may lose the office for the bot account."); + dnext; + mesc l("Town Administrator can use the town money for investments, and also receive a salary depending on how well the town is."); + next; + mesc l("A player may be the town admin of several different towns."); + mesc l("However, an account may only apply for an office weekly."); + mesc l("The account with highest votes will win. Ties will be solved by randomness."); + mesc l("An account may vote anywhere, but only once per town (weekly)."); + next; + mesc l("The town administrator benefits for free services on the town."); + mesc l("Also, they can control the city taxes, which are applied upon purchases and sales within the town."); + mesc l("However, they must visit their town office and do daily paperwork, every day. Otherwise, town reputation will go down."); + mesc l("If town reputation reaches zero, the town will only be able to get money with taxes."); + return; +} + +// Candidate Info and voting +// POL_Candidate( TOWNCODE ) +function script POL_Candidate { + copyarray( .@cd$, getd("$"+getarg(0)+"_CANDIDATE$"), getarraysize(getd("$"+getarg(0)+"_CANDIDATE$")) ); + copyarray( .@vt, getd("$"+getarg(0)+"_VOTES"), getarraysize(getd("$"+getarg(0)+"_VOTES")) ); + + .@list$="Don't vote"; + for (.@i=0;.@i<getarraysize(.@cd$);.@i++) { + mesc .@cd$[.@i] + " - "+.@vt[.@i] + " " + l("votes"); + .@list$+=":"+.@cd$[.@i]; + } + + next; + if (#POL_VOTEDAY[POL_LocToTP(getarg(0))] == gettimeparam(GETTIME_WEEKDAY)) + return; + mesc l("In whom to vote?"); + select .@list$; + .@vote=@menu-2; + + // Didn't vote + if (.@vote < 0) + return; + + // You cannot vote on yourself + if (getd("$"+getarg(0)+"_CANDIDATE$"+"["+.@vote+"]") == strcharinfo(0)) { + mesc l("You cannot vote on yourself!"), 1; + // mesc l("Use an alt char to do that."); + return; + } + + // Cast the vote + #POL_VOTEDAY[POL_LocToTP(getarg(0))]=gettimeparam(GETTIME_WEEKDAY); + .@str$="$"+getarg(0)+"_VOTES"+"["+.@vote+"]"; + .@vt=getd(.@str$); + setd(.@str$, .@vt+1); + + mesc l("The vote was cast."), 3; + mesc l("You supported: ")+getd("$"+getarg(0)+"_CANDIDATE$"+"["+.@vote+"]"), 3; + next; + return true; +} + + +// Town info +// POL_TownInfo( TOWNCODE ) +function script POL_TownInfo { + .@MAYOR$=getd("$"+getarg(0)+"_MAYOR$"); + .@GP=getd("$"+getarg(0)+"_MONEY"); + .@TX=getd("$"+getarg(0)+"_TAX"); + .@EX=getd("$"+getarg(0)+"_EXPORT"); + .@RP=getd("$"+getarg(0)+"_REPUTATION"); + if (strcharinfo(0) == .@MAYOR$ || is_gm()) { + mesc l("Town Money: @@", .@GP), 2; + mesc l("Town Reputation: %d | %d.%02d %% Tax", .@RP, .@TX/100, .@TX%100), 2; + mesc l("Town Weekly Exports: @@", .@EX), 2; + } else { + mesc l("Town Tax: %d.%02d %%", .@TX/100, .@TX%100), 2; + } + return; +} + + + +// Town Management +// POL_Manage( TOWNCODE ) +function script POL_Manage { + .@town$="$"+getarg(0); + .@MAYOR$=getd("$"+getarg(0)+"_MAYOR$"); + .@TP=POL_LocToTP(getarg(0)); + + if (strcharinfo(0) != .@MAYOR$) + return; + + // How many actions do you have? + // You get 1 action/4-hour, capped to 6 + .@left=(gettimeparam(GETTIME_HOUR)/4)-TOWN_ACTIONS[.@TP]; + if (.@left > 6) { + .@left=6; + TOWN_ACTIONS[.@TP]=(gettimeparam(GETTIME_HOUR)/4)-6; + } + + do + { + .@GP=getd("$"+getarg(0)+"_MONEY"); + .@TX=getd("$"+getarg(0)+"_TAX"); + .@EX=getd("$"+getarg(0)+"_EXPORT"); + .@RP=getd("$"+getarg(0)+"_REPUTATION"); + .@CR=getd("$"+getarg(0)+"_TAXOFF"); + .@DQ=getd("$"+getarg(0)+"_DAILYQUEST"); + .@SE=getd("$"+getarg(0)+"_SIEGEXP"); + .@left=(gettimeparam(GETTIME_HOUR)/4)-TOWN_ACTIONS[.@TP]; + + mesc l("Town Money: @@", .@GP), 2; + mesc l("Town Reputation: %d | %d.%02d %% Tax", .@RP, .@TX/100, .@TX%100), 2; + mesc l("Town Weekly Exports: @@", .@EX), 2; + mesc l("Total actions left: %d", .@left), (.@left > 1 ? 9 : 1); + next; + menuint + l("Nothing"), 0, + rif(.@DQ != gettimeparam(GETTIME_DAYOFMONTH), l("Do some paperwork")), 1, + rif(.@left, l("Invest in Exportations")), 10, + rif(.@left, l("Invest in Reputation")), 20, + rif(.@left, l("Raise city taxes")), 30, + rif(.@left, l("Lower city taxes")), 35, + rif(.@CR && .@RP, l("Tax crafters")), 40, + rif(!.@CR, l("Don't tax crafters")), 41, + rif(.@left && !.@SE, l("Town Defense Program")), 60, + rif(.@left >= 6, l("Exile a player")), 70, + rif(.@left >= 6, l("Revert a player exile")), 71, + rif(.@left, l("Raise server wide EXP")), 80, + rif(Zeny > $ALLIANCE_TAX1, l("Donate to town")), 90, + l("Resign"), 99; + mes ""; + switch (@menuret) { + // Mark 0: Cycle + case 1: + if (rand2(3) == 1) + setd(.@town$+"_REPUTATION", limit(0, .@RP+1, 100)); + setd(.@town$+"_DAILYQUEST", gettimeparam(GETTIME_DAYOFMONTH)); + mesc l("You dealt with paperwork."), 2; + break; + // Mark 10: Exports + case 10: + .@cost=.@EX/10; + mesc l("Investing in Exportations"), 3; + mesc l("You need @@ GP to make this investment.", .@cost); + if (.@GP < .@cost) + break; + mesc l("Are you sure?"); + if (askyesno() == ASK_YES) { + .@BN=rand2(.@cost/52, .@cost/24) + 1; + .@GP=getd("$"+getarg(0)+"_MONEY"); + setd(.@town$+"_MONEY", .@GP-.@cost); + setd(.@town$+"_EXPORT", .@EX+.@BN); + TOWN_ACTIONS[.@TP]+=1; + mesc l("Investment executed"), 2; + next; + } + break; + // Mark 20: Reputation + case 20: + .@cost=.@RP*3; + mesc l("Investing in Reputation"), 3; + if (.@RP >= 100) { + mesc l("Reputation cannot go above 100!"), 1; + next; + break; + } + mesc l("You need @@ GP to make this investment.", .@cost); + if (.@GP < .@cost) + break; + mesc l("Are you sure?"); + if (askyesno() == ASK_YES) { + .@GP=getd("$"+getarg(0)+"_MONEY"); + setd(.@town$+"_MONEY", .@GP-.@cost); + setd(.@town$+"_REPUTATION", .@RP+1); + TOWN_ACTIONS[.@TP]+=1; + mesc l("Investment executed"), 2; + next; + } + break; + // Mark 30: TAXES and Tax Governance + case 30: + .@cost=.@TX/11; + mesc l("Raising Taxes"), 3; + mesc l("You need @@ Reputation to make this investment.", .@cost); + mesc l("Taxes will raise in 0.02~0.05%, capped at 15%."); + if (.@RP < .@cost || .@TX >= 1500) + break; + mesc l("Are you sure?"); + if (askyesno() == ASK_YES) { + setd(.@town$+"_REPUTATION", .@RP-.@cost); + setd(.@town$+"_TAX", .@TX+rand2(2,5)); + TOWN_ACTIONS[.@TP]+=1; + mesc l("Taxes raised"), 1; + next; + } + break; + case 35: + .@cost=.@TX/30; + mesc l("Lowering Taxes"), 3; + mesc l("You will gain @@ Reputation.", .@cost); + mesc l("Taxes will fall in 0.01~0.03%, capped at 0.00%"); + if (.@TX <= 0 || .@RP >= 100) + break; + mesc l("Are you sure?"); + if (askyesno() == ASK_YES) { + .@val=.@TX-(.@cost ? rand2(1,3) : 1); + setd(.@town$+"_TAX", max(0, .@val)); + setd(.@town$+"_REPUTATION", min(100, .@RP+.@cost)); + TOWN_ACTIONS[.@TP]+=1; + mesc l("Taxes lowered"), 1; + next; + } + break; + // Mark 40: Tax Crafters and Town Policies + case 40: + setd("$"+getarg(0)+"_TAXOFF", false); + setd(.@town$+"_REPUTATION", .@RP-1); + mesc l("Crafters are now paying taxes."), 1; + break; + case 41: + setd("$"+getarg(0)+"_TAXOFF", true); + setd(.@town$+"_REPUTATION", .@RP+1); + mesc l("Crafters no longer pays taxes. (Tax exempt)"), 1; + break; + // Mark 60: Global events interference + case 60: + .@cost = 2000 - (.@RP * 10); + mesc l("Town Defense Program"), 3; + mesc l("You need @@ GP to make this investment.", .@cost); + mesc l("This program will cause experience gain to be tripled during the next siege to the town."); + if (.@GP < .@cost) + break; + mesc l("Are you sure?"); + if (askyesno() == ASK_YES) { + setd(.@town$+"_SIEGEXP", 1); + setd(.@town$+"_MONEY", .@GP-.@cost); + TOWN_ACTIONS[.@TP]+=1; + mesc l("Town Defense Program enabled."), 1; + next; + } + break; + // Mark 70: Exile and Player functions + case 70: + mesc l("Exiled players will not be able to save to menhir."), 1; + mesc l("A global announcement will be made."), 1; + mesc l("Player must be online for the operation to finish."), 1; + mesc l("This will cost you all your action points."), 1; + next; + mesc l("Please insert player name to exile."), 1; + input .@ex$; + if (.@ex$ == "") + break; + .@id=getcharid(3, .@ex$); + if (.@id < 1) { + mesc l("The player is offline or does not exist."); + break; + } + if (.@id == getcharid(3)) { + mesc l("You cannot exile yourself!"); + break; + } + .@town=POL_LocToTP(getarg(0)); + .@you=getcharid(3); + attachrid(.@id); + dispbottom l("You have been EXILED from %s by %s.", getarg(0), .@MAYOR$); + #EXILED=#EXILED|.@town; + .@l$=LocToMap(TPToLoc(.@town)); + if (getsavepoint(0) == .@l$) + savepoint "000-1", 22, 22; + // LOCATION$ will be redefined once you leave so no worries + detachrid(); + attachrid(.@you); + kamibroadcast(sprintf("%s has EXILED %s from %s.", + .@MAYOR$, .@ex$, getarg(0))); + TOWN_ACTIONS[.@TP]+=6; + break; + case 71: + mesc l("Please insert player name to cancel exile."), 1; + mesc l("Player must be online for the operation to finish."), 1; + mesc l("This will cost you all your action points."), 1; + input .@ex$; + if (.@ex$ == "") + break; + .@id=getcharid(3, .@ex$); + if (.@id < 1) { + mesc l("The player is offline or does not exist."); + break; + } + .@town=POL_LocToTP(getarg(0)); + .@you=getcharid(3); + attachrid(.@id); + if (#EXILED & .@town) { + dispbottom l("You are NO LONGER EXILED from %s by %s.", getarg(0), .@MAYOR$); + #EXILED=#EXILED^.@town; + } + detachrid(); + attachrid(.@you); + kamibroadcast(sprintf("%s has ANNULLED THE EXILE %s from %s.", + .@MAYOR$, .@ex$, getarg(0))); + TOWN_ACTIONS[.@TP]+=6; + break; + // Mark 80: Town and Server Governance + case 80: + .@gcost=50000; + .@rcost=25; + .@b=5+(.@RP/10); + mesc l(".:: Server Boom ::."), 3; + mesc l("To cause a server happy hour (+%d%% EXP), you need:", .@b); + mesc l("%s GP and %d REP.", fnum(.@gcost), .@rcost); + if (.@RP < .@rcost || .@GP < .@gcost) + break; + mesc l("Are you sure?"); + if (askyesno() == ASK_YES) { + setd(.@town$+"_REPUTATION", .@RP-.@rcost); + setd(.@town$+"_MONEY", .@GP-.@gcost); + TOWN_ACTIONS[.@TP]+=1; + mesc l("Server Happy Hour Decreed"), 1; // Will not be shown + kamibroadcast(sprintf("%s has sponsored a Server Happy Hour using tax money!", .@MAYOR$)); + // Effective immediately + $@EXP_EVENT=.@b; + $@EXP_EVENT_TIME=1; + donpcevent "@exprate::OnPlayerCall"; + // We must terminate dialog now and close all context for EXPRATE. + closeclientdialog; + end; + } + break; + // Mark 90: Office + case 90: + mesc l("The alliance will take %s GP as fee, +%d%% tax over donations.", fnum($ALLIANCE_TAX1), $ALLIANCE_TAX2), 1; + mesc l("You can donate up to %s GP.", fnum(Zeny-$ALLIANCE_TAX1)); + input .@donate, 0, Zeny-$ALLIANCE_TAX1; + if (.@donate <= 0) + break; + Zeny -= $ALLIANCE_TAX1; + .@tax = .@donate * (100-$ALLIANCE_TAX2) / 100; + setd(.@town$+"_MONEY", .@GP+.@tax); + Zeny -= .@donate; + mesc l("Donation completed."), 2; + break; + case 99: + mesc l("Really resign?"), 1; + next; + if (askyesno() == ASK_YES) { + setd(.@town$+"_MAYOR$", "Jesus Saves"); + // If you have already received a vote, this will be skipped + if (getd(.@town$+"_VOTES[0]") <= 0 && + getd(.@town$+"_CANDIDATE$[0]") == strcharinfo(0)) + setd(.@town$+"_CANDIDATE$[0]", "Jesus Saves"); + mesc l("YOU HAVE RESIGNED THE OFFICE."), 1; + close; + } + default: + return; + } + + // End script + } while (true); + return; +} + + diff --git a/npc/functions/random-talk.txt b/npc/functions/random-talk.txt new file mode 100644 index 0000000..44fa9b2 --- /dev/null +++ b/npc/functions/random-talk.txt @@ -0,0 +1,207 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Random dialog for various random NPCs. + +// Functions: +// hello +// moubootalk +// villagertalk +// sailortalk +// legiontalk +// asleep + +// Evol authors (some strings and code): +// Reid +// Akko Teru +// Qwerty Dragon + +function script hello { + + switch (rand2(3)) { + case 0: + npctalkonce(l("Heya!")); + break; + case 1: + npctalkonce(l("Hi.")); + break; + case 2: + if ($EVENT$ == "Christmas") + npctalkonce(l("Merry Christmas!")); + else + npctalkonce(l("Nice day to you.")); + break; + } + + return; +} + +function script moubootalk { + switch (rand2(4)) { + case 0: + npctalkonce(l("Moooooo!")); + break; + case 1: + npctalkonce(l("Moo!")); + break; + case 2: + npctalkonce(l("Moooooooooooo!")); + break; + case 3: + npctalkonce(l("Moooo!")); + break; + } + return; +} + +function script sailortalk { + + .@rand = rand2(9); + if (.@rand == 0) goodbye; + if (.@rand == 1) npctalkonce(l("Arr, I'm bored!")); + if (.@rand == 2) npctalkonce(l("Hey! Good to hear from you!")); + if (.@rand == 3) npctalkonce(l("Yarr arr!")); + if (.@rand == 4) { + if ($EVENT$ == "Christmas") + npctalkonce(l("Merry Christmas, arr yarr!!")); + else { + speech( + l("A sunny and hot day,"), + l("a quiet place,"), + l("a ground!"), + l("What else do you need?")); + } + close; + } + if (.@rand == 5) npctalkonce(l("A-hoy matey!")); + if (.@rand == 6) npctalkonce(l("Arr!")); + if (.@rand == 7) npctalkonce(l("Howdy?")); + if (.@rand == 8) npctalkonce(l("Glad the Monster King avoid the seas, arr!")); + + // just to be sure + closedialog; + close; + end; +} + +function script villagertalk { + + function darn_or_smile { + .@darn = rand(42); + + if (.@darn < 26) { + emotion E_JOY; + hello; + } else if (.@darn > 26) { + emotion E_LOOKAWAY; + goodbye; + } else { + npctalkonce(l("Stop it!")); + } + return; + } + + switch (rand2(4)) { + case 0: + darn_or_smile(); + break; + case 1: + npctalkonce(l("It is a sunny day, don't you think?")); + break; + case 2: + npctalkonce(l("Go fly a kite.")); + break; + case 3: + npctalkonce(l("I just want to live my life in peace.")); + break; + default: + emotion E_HAPPY; + break; + } + + return; +} + +function script legiontalk { + switch (rand2(16)) { + case 0: + npctalkonce(l("Do I look like a tree? I feel like one.")); + break; + case 1: + npctalkonce(l("I'm a little busy right now.")); + break; + case 2: + npctalkonce(l("Not in the mood to chat.")); + break; + case 3: + npctalkonce(l("My breath smells bad.")); + break; + case 4: + npctalkonce(l("Don't distract me, I have to stay alert.")); + break; + case 5: + npctalkonce(l("Give me some space.")); + break; + case 6: + if ($EVENT$ == "Christmas") + npctalkonce(l("Merry Christmas, adventurer.")); + else + npctalkonce(l("Can you please go away?")); + break; + case 7: + npctalkonce(l("Can't talk right now, I'm on patrol duty.")); + break; + case 8: + npctalkonce(l("What're you looking at?!")); + break; + case 9: + npctalkonce(l("I can't stay here and talk all day. I have a job to do.")); + break; + case 10: + npctalkonce(lg("Keep moving girl.", "Keep moving boy.")); + break; + case 11: + npctalkonce(lg("So you think you're tough? A warrior must also be loyal and patient.")); + break; + case 12: + emotion E_LOOKAWAY; + break; + case 13: + npctalkonce(l("Practice! There are no secrets to becoming a warrior.")); + break; + case 14: + npctalkonce(l("There is no honor in fighting a weak opponent.")); + break; + case 15: + npctalkonce(l("The Monster King is scary. To be honest, I'm not sure I would be able to do anything if he showed up.")); + break; + } + + return; +} + +function script asleep { + switch(rand2(6)) { + case 0: npctalkonce(l("Zzzzzzzzz...")); break; + case 1: npctalkonce(l("Rrrr... Pchhhh...")); break; + case 2: npctalkonce(l("Ggrmm... Grmmmm...")); break; + case 3: npctalkonce(l("Hm... Shhhh...")); break; + case 4: npctalkonce(l("ZZZzzzz...")); break; + default: emotion(E_SLEEPY); + } + end; +} + +function script studenttalk { + switch(rand2(6)) { + case 0: npctalkonce(l("I want to sleep...")); break; + case 1: npctalkonce(l("I have homework to do...")); break; + case 2: npctalkonce(l("I need to finish studying for my test...")); break; + case 3: npctalkonce(l("Ah, the Professors will get mad at me again...")); break; + case 4: npctalkonce(l("I'm a little busy right now.")); break; + + default: emotion(E_SLEEPY); + } + end; +} diff --git a/npc/functions/refine.txt b/npc/functions/refine.txt new file mode 100644 index 0000000..220ee6d --- /dev/null +++ b/npc/functions/refine.txt @@ -0,0 +1,242 @@ +// TMW2 Script +// Author: +// Jesusalva +// Pyndragon (inspiration) +// Scall (inspiration) +// Description: +// Refine functions. Includes equipment repair. In future use getequippercentrefinery(.@it, REFINE_CHANCE_TYPE_ENRICHED) too + +// (No arguments) +function script refineMaster { + mesn; + mesq l("Ah, hello there, funny face! Do you want to refine items?"); + mesc l("There's always some chance that THE ITEM WILL BREAK, beware!"); + next; + mesn; + mesq l("First - item must be equipped."); + mesq l("If you succeed, the defense and/or attack will raise. If you fail, refine level goes down."); + //mesq l("And if the item breaks, that is definitive - no repair is possible. What to refine?"); + mesq l("And if the item breaks, that's your loss. What to refine?"); + next; + + // Dark magic from hercules used, take care + menuint + l("Nothing, thanks"), 0, + rif(getequipisequiped(EQI_HEAD_MID), l("My chestplate, @@", getequipname(EQI_HEAD_MID))), EQI_HEAD_MID, + rif(getequipisequiped(EQI_HAND_R), l("My weapon, @@", getequipname(EQI_HAND_R))), EQI_HAND_R, + rif(getequipisequiped(EQI_HAND_L) && getequipid(EQI_HAND_L) != getequipid(EQI_HAND_R), l("My shield, @@", getequipname(EQI_HAND_L))), EQI_HAND_L, + rif(getequipisequiped(EQI_HEAD_TOP), l("My helmet, @@", getequipname(EQI_HEAD_TOP))), EQI_HEAD_TOP, + l("Item break? Too dangerous! I don't want it!!"), 0; + + + if (@menuret == 0) { + mesn; + //mesq l("Wait a moment - you are naked! YOU WILL DIE IN THE FREEZE! o.o"); + mesq l("Well... we'll see around!"); + close; + } + .@it=@menuret; + .@item=getequipid(.@it); + + /* + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + .@item = requestitem(); + + // Check if we received an item + if (.@item < 1) { + mesn; + mesq l("Well, no risk, no gain."); + close; + return; + } + */ + + // Not all items are refinable + if (!getequipisenableref(.@it)) { + mesn; + mesq l("Well, sorry, but only some items can be refined."); + mesq l("There may be some exceptions. Anyway, this item clearly cannot be refined."); + next; + return; + } + + // Savior items have different rules + .@savior = false; + if (compare("savior", strtolower(getitemname(.@item)))) + .@savior = true; + + // Legendary Weapons, this formula is hardcoded in C + if (is_between(3600, 3610, .@item)) + .@savior = true; + + // Max refining level: 10 + if (getequiprefinerycnt(.@it) >= (.@savior ? 12 : 10)) { + mesn; + mesq l("This item cannot be refined further."); + return; + } + + .@rlv=getequiprefinerycnt(.@it); + .@price=(.@rlv+1)*1000; + .@price-=min(.@price, getiteminfo(.@item, ITEMINFO_ELV) * 10); + .@price=POL_AdjustPrice(.@price); + .@amount=(.@rlv/2)+1; + // required item + switch (.@rlv) { + case 0: + case 1: + case 2: + case 3: + .@rg=Wurtzite; break; + case 4: + case 5: + case 6: + case 7: + .@amount=((.@rlv-4)/2)+1; + .@rg=Graphene; break; + case 8: + case 9: + case 10: + default: + .@amount=((.@rlv-8)/2)+1; + .@rg=Arcanum; break; + } + mesn; + mesq l("This @@ is a nice stuff. I can refine it for @@ GP and @@ @@.", getitemlink(.@item), .@price, .@amount, getitemlink(.@rg)); + .@fake = (.@savior ? rand2(-1,1) : rand2(-6, 6)); + mesc l("Success ratio is of aprox. @@ %", (100-(.@rlv*7))+.@fake); + next; + if (askyesno() != ASK_YES) + close; + + // Anti-Crazyfefe™ System + // No money + if (Zeny < .@price) { + mesn; + mesq l("Where's the money? Go away."); + close; + } + // No reagents + if (countitem(.@rg) < .@amount) { + mesn; + mesq l("Where's the reagent? You don't expect me to find it, right? Go bring them!"); + close; + } + // Item removed + if (getequipisequiped(.@it) == 0) { + mesn; + mesc l("All cheaters must die."), 1; + die(); + close; + } + // Item changed + if (getequiprefinerycnt(.@it) != .@rlv || getequipid(.@it) != .@item) { + mesn; + mesc l("All cheaters must die."), 1; + die(); + close; + } + ///// This should cover most of Crazyfefe tricks ¬.¬ I hope ¬.¬ + mesn; + mesq l("HA, HA, HA! The blacksmiths from the Land of Fire are the best from the world! There's no challenge too big for us!"); + mesq l("It's time to show this shiny @@ who is the boss! HAHAHAHAHAHAHAH!", getitemlink(.@item)); + next; + // Cheater should be locked for eternity. + if (getequiprefinerycnt(.@it) != .@rlv || getequipid(.@it) != .@item || getequipisequiped(.@it) == 0) { + atcommand "@jail "+strcharinfo(0); + close; + } + // If you cheat somewhere along the way, the script will explode + POL_PlayerMoney(.@price); + + // Were we successful? + if (getequippercentrefinery(.@it) > rand2(100)) { + successrefitem .@it; + mesn; + mesq l("HAHAHAHAH! Shiny as new, and stronger than ever! Have fun!"); + mesc l("Weapons: +8 attack, +8 magic attack"); + mesc l("Armors: +3~5 defense"); + next; + // Item is weakened. (1% per refine level) (Savior Immunity) + } else if (rand2(100) < .@rlv && !.@savior) { + downrefitem .@it, 1; + mesn; + mesq l("A masterpiece!... Whaaaat, this stuff got ##Bweaker##b??"); + next; + mesn; + mesq l("Well, I did my best, but this had been so refined, that it was difficult to find my way."); + next; + mesn; + mesq l("I'm sure I could refine this again, if you bring me the payment and the reagents again."); + next; + // Perhaps the item has broken? (~0.1% chance/refine level) + } else if (rand2(10000) <= 10*(.@rlv+1)) { + failedrefitem .@it; + mesc l("*CRASH*"); + next; + mesn; + mesq l("Ops... I hammered this stuff too hard... It's junk now."); + next; + mesn; + mesq l("Well, you were warned. Do you have any other stuff for me?"); + next; + // Nothing happens + } else { + mesn; + mesq l("Well, I did my best, but this had been so refined, that it was difficult to find my way."); + next; + mesn; + mesq l("I'm sure I could refine this again, if you bring me the payment and the reagents again."); + next; + } + delitem .@rg, .@amount; // We delete last, to prevent IDX changes + return; +} + + +// (No arguments) +function script repairMaster { + mesn; + mesq l("I am a really good blacksmith. In fact, there is no stuff I can't fix."); + next; + mesn; + if (getbrokenid(1) == 0) { + mesq l("I don't think you have any broken item, but if a friend of mine break your stuff, come talk to me!"); + close; + } else { + // Print the name of the first broken item + mesq l("You seem to have @@ broken items and... Oh, I see you have a broken @@ here! I wanna repair it!", getbrokencount(), getitemname(getbrokenid(1))); + .@save=getbrokenid(1); + next; + } + .@price=getiteminfo(getbrokenid(1), ITEMINFO_SELLPRICE)+getiteminfo(getbrokenid(1), ITEMINFO_ELV)-(getbrokencount()*15); + if (.@price < 100 || getbrokenid(1) == BronzeGladius) + .@price=100; + mesn; + mesq l("I can fix this for you. Just pay me @@ GP and a @@. It'll take no time at all!", .@price, getitemlink(IronIngot)); + next; + if (Zeny < .@price || countitem(IronIngot) < 1 || askyesno() != ASK_YES) { + mes ""; + mesn; + mesq l("Ahh, that's a pity... Well, just talk to me with enough money and I'll gladly fix that for you! %%0"); + close; + } + // Cheating? Well, let's try to be innovative this time + if (.@save != getbrokenid(1)) { + mesn; + mesq l("Ah...! POLICE! POLICE! Quick, send this cheating thief to jail!!"); + atcommand "@jailfor 20mn "+strcharinfo(0); + close; + } + Zeny=Zeny-.@price; + delitem IronIngot, 1; + repair(1); + mesn; + mesq l("Here, sparking new! Nice!!"); + if (getbrokencount() > 0) { + next; + return; + } else { + close; + } +} diff --git a/npc/functions/resetstatus.txt b/npc/functions/resetstatus.txt new file mode 100644 index 0000000..c0a31e2 --- /dev/null +++ b/npc/functions/resetstatus.txt @@ -0,0 +1,121 @@ +// TMW2 Script. +// Authors: +// Vasily_Makarov (original from Evol) +// Jesusalva +// Description: +// Status Reset NPC utils + +// Reset status and return permanent bonuses +// StatusResetReinvest( {script=True} ) +function script StatusResetReinvest { + // Compulsory check + if (getarg(0, true)) { + inventoryplace NPCEyes, 6; + } else if (!checkweight(NPCEyes, 6)) { + getitembound StatusResetPotion, 1, 4; + dispbottom l("You cannot carry the fruits."); + end; // Die + } + + // Permanent boosts were now lost, return the fruits + if (STATUSUP_STR) { + getitembound StrengthFruit, STATUSUP_STR, 4; + STATUSUP_STR=0; + } + if (STATUSUP_AGI) { + getitembound AgilityFruit, STATUSUP_AGI, 4; + STATUSUP_AGI=0; + } + if (STATUSUP_VIT) { + getitembound VitalityFruit, STATUSUP_VIT, 4; + STATUSUP_VIT=0; + } + if (STATUSUP_INT) { + getitembound IntelligenceFruit, STATUSUP_INT, 4; + STATUSUP_INT=0; + } + if (STATUSUP_DEX) { + getitembound DexterityFruit, STATUSUP_DEX, 4; + STATUSUP_DEX=0; + } + if (STATUSUP_LUK) { + getitembound LuckFruit, STATUSUP_LUK, 4; + STATUSUP_LUK=0; + } + resetstatus(); + return true; +} + +// Return wasSP on success, 0 on failure +// ConfirmReset( {price, town=True} ) +function script ConfirmStatusReset { + if (BaseLevel >= 15) + .@raw_price=(1000-BaseLevel*10+(BaseLevel*18)); + else if (BaseLevel >= 10) + .@raw_price=(BaseLevel*210-(10*210))/(BaseLevel/10); + else + .@raw_price=1; + + if (getarg(0,-1) >= 0) + .@raw_price=getarg(0,-1); + + if (getarg(1, true)) + .@raw_price=POL_AdjustPrice(.@raw_price); + else + .@raw_price=.@raw_price; + + mesc l("WARNING: Permanent boosts will return to their fruit form."), 1; + + switch (select(lg("Yes, I am sure."), + lg("I need to think about it..."), + lg("I won't need it, thank you."))) + { + case 1: + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Let me just have a quick look at you. Hm... I will need @@ GP to reset your stats.", .@raw_price); + + select + rif(Zeny >= .@raw_price, l("Here, take as much as you need, I have plenty!")), + rif(Zeny > 0 && Zeny < .@raw_price, l("I don't have enough money...")), + rif(Zeny == 0, l("Oh no, I don't have any money on me right now.")), + l("I have to go, sorry."); + + if (@menu > 1) { + return 0; + } + + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Thank you."), + l("Now stand still... It should not take much time..."); + + // Reset status have an inventorycheck, so we charge later. + .@wasSP = StatusPoint; + StatusResetReinvest(); + + // Nothing to do: Do not charge (eg. you just got the fruits back) + if (StatusPoint == .@wasSP) { + speech S_LAST_NEXT, + l("It seems that you have no status points to reset!"), + l("Come back when you will really need me."); + } else { + if (getarg(1, true)) + POL_PlayerMoney(.@raw_price); + else + Zeny-=.@raw_price; + speech S_LAST_NEXT, + l("Let's see... @@ of your status points have just been reset!", StatusPoint - .@wasSP), + l("Spend it wisely this time."), + l("But you are welcome to reset your stats again! I need the money."); + } + return .@wasSP; + + case 2: + return 0; + case 3: + return 0; + } + Exception("Unknown Error: ConfirmStatusReset() failed"); + return 0; + +} + diff --git a/npc/functions/riddle.txt b/npc/functions/riddle.txt new file mode 100644 index 0000000..b7fd2c5 --- /dev/null +++ b/npc/functions/riddle.txt @@ -0,0 +1,69 @@ +// Evol functions. +// Author: +// Reid +// Description: +// Riddle enigma validator +// +// Arguments +// 0 PC answer +// 1 English correct answer +// 2 Translated correct answer + +// TODO: levenshtein(), similar_text(), and maybe even soundex() + +function script riddlevalidation { + .@answer$ = strip(strtolower(getarg(0))); + .@good$ = strip(strtolower(getarg(1))); + .@good_translated$ = strip(strtolower(getarg(2))); + + .@size_answer = getstrlen(.@answer$); + .@size_good = getstrlen(.@good$); + .@size_good_translated = getstrlen(.@good_translated$); + + .@max = max(.@size_answer, .@size_good_translated, .@size_good); + + // Input is too long. + if (.@max > 30) { + return false; + } + + // if 90% of the word is correct + .@size_good *= 90; + .@size_good_translated *= 90; + + freeloop(true); + for (.@i = 0; .@i < .@max; .@i++) + { + .@correct = 0; + .@correct_translated = 0; + + for (.@k = .@k_translated = .@j = .@i; .@j < .@max; .@j++) + { + if (charat(.@answer$, .@j) == charat(.@good$, .@k)) { + .@correct++; + .@k++; + } else { + .@correct--; + } + + if (charat(.@answer$, .@j) == charat(.@good_translated$, .@k_translated)) { + .@correct_translated++; + .@k_translated++; + } else { + .@correct_translated--; + } + } + // if most of the word is correct. (White spaces can and will mess you up!) + .@correct *= 100; + .@correct_translated *= 100; + + if (.@correct >= .@size_good || + .@correct_translated >= .@size_good_translated) { + freeloop(false); + return true; + } + } + freeloop(false); + + return false; +} diff --git a/npc/functions/scoreboards.txt b/npc/functions/scoreboards.txt new file mode 100644 index 0000000..52bf061 --- /dev/null +++ b/npc/functions/scoreboards.txt @@ -0,0 +1,963 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Leaderboards + GM Command Log + +function script ScoreboardsReload { + debugmes "Reloading scoreboards..."; + .@nb = query_sql("select name, zeny from `char` WHERE `guild_id`!=1 ORDER BY zeny DESC LIMIT 15", $@hofortune_name$, $@hofortune_value); + .@nb = query_sql("select name, base_level from `char` WHERE `guild_id`!=1 ORDER BY base_level DESC LIMIT 15", $@hoblvl_name$, $@hoblvl_value); + .@nb = query_sql("select name, job_level from `char` WHERE `guild_id`!=1 ORDER BY job_level DESC LIMIT 15", $@hojlvl_name$, $@hojlvl_value); + .@nb = query_sql("SELECT c.name, i.amount FROM `inventory` AS i, `char` AS c WHERE i.nameid="+StrangeCoin+" AND i.char_id=c.char_id ORDER BY i.amount DESC LIMIT 15", $@hoc_name$, $@hoc_value); + .@nb = query_sql("select char_name, command from `atcommandlog` ORDER BY atcommand_id DESC LIMIT 15", $@hogm_name$, $@hogm_value$); + .@nb = query_sql("select name, guild_lv from `guild` WHERE `guild_id`!=1 ORDER BY guild_lv DESC LIMIT 5", $@hoguild_name$, $@hoguild_value); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='CRAZYPOINTS' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@cfefe_name$, $@cfefe_value); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='SCANDORPTS' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@blood_name$, $@blood_value); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='UDTRANK' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@udt_name$, $@udt_value); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='AFKING' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@afk_name$, $@afk_value); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='HONOR' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 10", $@hof_name$, $@hof_value); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='ACADEMIC_RANK' AND i.char_id=c.char_id ORDER BY i.value DESC LIMIT 30", $@hoa_name$, $@hoa_value); + .@nb = query_sql("SELECT c.name, i.value FROM `mapreg` AS i, `char` AS c WHERE i.varname='$REFERRAL_IDS' AND i.index=c.account_id ORDER BY i.value DESC LIMIT 20", $@hor_name$, $@hor_value); + .@nb = query_sql("SELECT c.name, i.value FROM `char_reg_num_db` AS i, `char` AS c WHERE i.key='PC_DIE_COUNTER' AND i.char_id=c.char_id AND c.last_login >= "+(gettimetick(2)-86400)+" ORDER BY i.value ASC LIMIT 10", $@hod_name$, $@hod_value); + + // Seasonal Scoreboards + if (season() == SUMMER || season() == AUTUMN) { + .@nb = query_sql("SELECT c.name, i.count2 FROM `quest` AS i, `char` AS c WHERE i.quest_id=305 AND i.char_id=c.char_id AND i.count1="+(gettime(GETTIME_YEAR)-2000)+" ORDER BY i.count2 DESC LIMIT 10", $@ched_name$, $@ched_value); + } + if (season() == AUTUMN || season() == WINTER) { + .@nb = query_sql("SELECT c.name, i.count2 FROM `quest` AS i, `char` AS c WHERE i.quest_id="+SQuest_Autumn+" AND i.char_id=c.char_id ORDER BY i.count2 DESC LIMIT 5", $@hasn_name$, $@hasn_value); + } + if (season() == WINTER || season() == SPRING) { + .@nb = query_sql("SELECT c.name, i.count2 FROM `quest` AS i, `char` AS c WHERE i.quest_id="+SQuest_Christmas+" AND i.char_id=c.char_id ORDER BY i.count2 DESC LIMIT 5", $@xmas_box_name$, $@xmas_box_value); + .@nb = query_sql("SELECT c.name, i.count3 FROM `quest` AS i, `char` AS c WHERE i.quest_id="+SQuest_Christmas+" AND i.char_id=c.char_id ORDER BY i.count3 DESC LIMIT 5", $@xmas_gp_name$, $@xmas_gp_value); + } + if (FYEventUsesRanking()) { + deletearray $@aurora_name$; + deletearray $@aurora_value; + .@nb = query_sql("SELECT c.name, i.count2 FROM `quest` AS i, `char` AS c WHERE i.quest_id="+Q_AuroraEvent+" AND i.char_id=c.char_id ORDER BY i.count2 DESC LIMIT 10", $@aurora_name$, $@aurora_value); + } + debugmes "Scoreboards reloaded"; + return; +} + +- script scoreboardsGen NPC_HIDDEN,{ + end; + +// Monthly Scoreboards Rewards +OnDay0101: +OnDay0201: +OnDay0301: +OnDay0401: +OnDay0501: +OnDay0601: +OnDay0701: +OnDay0801: +OnDay0901: +OnDay1001: +OnDay1101: +OnDay1201: + ScoreboardsReload(); // Ensure + + if ($SCOREBOARD == gettimeparam(GETTIME_MONTH)) end; + + for (.@i=0; .@i < getarraysize($@hof_name$); .@i++) { + rodex_sendmail(gf_charnameid($@hof_name$[.@i]), "Scoreboards", "Hall Of Honor: TOP 10", "For being on the TOP 10 of this hall, you're getting some Strange Coins.", 0, StrangeCoin, 15); + } + + for (.@i=0; .@i < getarraysize($@cfefe_name$); .@i++) { + rodex_sendmail(gf_charnameid($@cfefe_name$[.@i]), "Scoreboards", "Hall Of Candor Battle: TOP 10", "For being on the TOP 10 of this hall, you're getting some Strange Coins.", 0, StrangeCoin, 15); + } + + for (.@i=0; .@i < getarraysize($@blood_name$); .@i++) { + rodex_sendmail(gf_charnameid($@blood_name$[.@i]), "Scoreboards", "Hall Of Bloodbath: TOP 10", "For being on the TOP 10 of this hall, you're getting some Strange Coins.", 0, StrangeCoin, max(15-.@i, 1)); + } + + $SCOREBOARD = gettimeparam(GETTIME_MONTH); + end; + +// Hourly Reload (maybe "OnMinute00" would work better?) +OnHour00: +OnHour01: +OnHour02: +OnHour03: +OnHour04: +OnHour05: +OnHour06: +OnHour07: +OnHour08: +OnHour09: +OnHour10: +OnHour11: +OnHour12: +OnHour13: +OnHour14: +OnHour15: +OnHour16: +OnHour17: +OnHour18: +OnHour19: +OnHour20: +OnHour21: +OnHour22: +OnHour23: +OnInit: + ScoreboardsReload(); + deletearray $@FORT_BLACKLIST; + end; +} + +function script HallOfGuild { + mes ""; + mes l("##BHall Of Guild Level: TOP5##b"); + mesf("1. %s (%d)", $@hoguild_name$[0], $@hoguild_value[0]); + mesf("2. %s (%d)", $@hoguild_name$[1], $@hoguild_value[1]); + mesf("3. %s (%d)", $@hoguild_name$[2], $@hoguild_value[2]); + mesf("4. %s (%d)", $@hoguild_name$[3], $@hoguild_value[3]); + mesf("5. %s (%d)", $@hoguild_name$[4], $@hoguild_value[4]); + return; +} + +function script HallOfFortune { + mes ""; + mes l("##BHall Of Fortune: TOP15##b"); + mesf("1. %s (%s GP)", $@hofortune_name$[0], fnum($@hofortune_value[0])); + mesf("2. %s (%s GP)", $@hofortune_name$[1], fnum($@hofortune_value[1])); + mesf("3. %s (%s GP)", $@hofortune_name$[2], fnum($@hofortune_value[2])); + mesf("4. %s (%s GP)", $@hofortune_name$[3], fnum($@hofortune_value[3])); + mesf("5. %s (%s GP)", $@hofortune_name$[4], fnum($@hofortune_value[4])); + mesf("6. %s (%s GP)", $@hofortune_name$[5], fnum($@hofortune_value[5])); + mesf("7. %s (%s GP)", $@hofortune_name$[6], fnum($@hofortune_value[6])); + mesf("8. %s (%s GP)", $@hofortune_name$[7], fnum($@hofortune_value[7])); + mesf("9. %s (%s GP)", $@hofortune_name$[8], fnum($@hofortune_value[8])); + mesf("10. %s (%s GP)", $@hofortune_name$[9], fnum($@hofortune_value[9])); + mesf("11. %s (%s GP)", $@hofortune_name$[10], fnum($@hofortune_value[10])); + mesf("12. %s (%s GP)", $@hofortune_name$[11], fnum($@hofortune_value[11])); + mesf("13. %s (%s GP)", $@hofortune_name$[12], fnum($@hofortune_value[12])); + mesf("14. %s (%s GP)", $@hofortune_name$[13], fnum($@hofortune_value[13])); + mesf("15. %s (%s GP)", $@hofortune_name$[14], fnum($@hofortune_value[14])); + return; +} + +function script HallOfLevel { + mes ""; + mes l("##BHall Of Level: TOP15##b"); + mesf("1. %s (%d)", $@hoblvl_name$[0], $@hoblvl_value[0]); + mesf("2. %s (%d)", $@hoblvl_name$[1], $@hoblvl_value[1]); + mesf("3. %s (%d)", $@hoblvl_name$[2], $@hoblvl_value[2]); + mesf("4. %s (%d)", $@hoblvl_name$[3], $@hoblvl_value[3]); + mesf("5. %s (%d)", $@hoblvl_name$[4], $@hoblvl_value[4]); + mesf("6. %s (%d)", $@hoblvl_name$[5], $@hoblvl_value[5]); + mesf("7. %s (%d)", $@hoblvl_name$[6], $@hoblvl_value[6]); + mesf("8. %s (%d)", $@hoblvl_name$[7], $@hoblvl_value[7]); + mesf("9. %s (%d)", $@hoblvl_name$[8], $@hoblvl_value[8]); + mesf("10. %s (%d)", $@hoblvl_name$[9], $@hoblvl_value[9]); + mesf("11. %s (%d)", $@hoblvl_name$[10], $@hoblvl_value[10]); + mesf("12. %s (%d)", $@hoblvl_name$[11], $@hoblvl_value[11]); + mesf("13. %s (%d)", $@hoblvl_name$[12], $@hoblvl_value[12]); + mesf("14. %s (%d)", $@hoblvl_name$[13], $@hoblvl_value[13]); + mesf("15. %s (%d)", $@hoblvl_name$[14], $@hoblvl_value[14]); + return; +} + +function script HallOfJob { + mes ""; + mes l("##BHall Of Job Level: TOP15##b"); + mesf("1. %s (%d)", $@hojlvl_name$[0], $@hojlvl_value[0]); + mesf("2. %s (%d)", $@hojlvl_name$[1], $@hojlvl_value[1]); + mesf("3. %s (%d)", $@hojlvl_name$[2], $@hojlvl_value[2]); + mesf("4. %s (%d)", $@hojlvl_name$[3], $@hojlvl_value[3]); + mesf("5. %s (%d)", $@hojlvl_name$[4], $@hojlvl_value[4]); + mesf("6. %s (%d)", $@hojlvl_name$[5], $@hojlvl_value[5]); + mesf("7. %s (%d)", $@hojlvl_name$[6], $@hojlvl_value[6]); + mesf("8. %s (%d)", $@hojlvl_name$[7], $@hojlvl_value[7]); + mesf("9. %s (%d)", $@hojlvl_name$[8], $@hojlvl_value[8]); + mesf("10. %s (%d)", $@hojlvl_name$[9], $@hojlvl_value[9]); + mesf("11. %s (%d)", $@hojlvl_name$[10], $@hojlvl_value[10]); + mesf("12. %s (%d)", $@hojlvl_name$[11], $@hojlvl_value[11]); + mesf("13. %s (%d)", $@hojlvl_name$[12], $@hojlvl_value[12]); + mesf("14. %s (%d)", $@hojlvl_name$[13], $@hojlvl_value[13]); + mesf("15. %s (%d)", $@hojlvl_name$[14], $@hojlvl_value[14]); + return; +} + +function script HallOfCoins { + mes ""; + mes l("##BHall Of @@: TOP15##b", getitemlink(StrangeCoin)); + mes("1."+$@hoc_name$[0]+" ("+$@hoc_value[0]+")"); + mes("2."+$@hoc_name$[1]+" ("+$@hoc_value[1]+")"); + mes("3."+$@hoc_name$[2]+" ("+$@hoc_value[2]+")"); + mes("4."+$@hoc_name$[3]+" ("+$@hoc_value[3]+")"); + mes("5."+$@hoc_name$[4]+" ("+$@hoc_value[4]+")"); + mes("6."+$@hoc_name$[5]+" ("+$@hoc_value[5]+")"); + mes("7."+$@hoc_name$[6]+" ("+$@hoc_value[6]+")"); + mes("8."+$@hoc_name$[7]+" ("+$@hoc_value[7]+")"); + mes("9."+$@hoc_name$[8]+" ("+$@hoc_value[8]+")"); + mes("10."+$@hoc_name$[9]+" ("+$@hoc_value[9]+")"); + mes("11."+$@hoc_name$[10]+" ("+$@hoc_value[10]+")"); + mes("12."+$@hoc_name$[11]+" ("+$@hoc_value[11]+")"); + mes("13."+$@hoc_name$[12]+" ("+$@hoc_value[12]+")"); + mes("14."+$@hoc_name$[13]+" ("+$@hoc_value[13]+")"); + mes("15."+$@hoc_name$[14]+" ("+$@hoc_value[14]+")"); + return; +} + +function script HallOfGMLog { + mes ""; + mes l("##BLatest GM Commands##b"); + mesc l("Read the last 30 days %s", "@@https://tmw2.org/experiments/gm|"+l("here")+"@@"); + mes("1."+$@hogm_name$[0]+" ("+$@hogm_value$[0]+")"); + mes("2."+$@hogm_name$[1]+" ("+$@hogm_value$[1]+")"); + mes("3."+$@hogm_name$[2]+" ("+$@hogm_value$[2]+")"); + mes("4."+$@hogm_name$[3]+" ("+$@hogm_value$[3]+")"); + mes("5."+$@hogm_name$[4]+" ("+$@hogm_value$[4]+")"); + mes("6."+$@hogm_name$[5]+" ("+$@hogm_value$[5]+")"); + mes("7."+$@hogm_name$[6]+" ("+$@hogm_value$[6]+")"); + mes("8."+$@hogm_name$[7]+" ("+$@hogm_value$[7]+")"); + mes("9."+$@hogm_name$[8]+" ("+$@hogm_value$[8]+")"); + mes("10."+$@hogm_name$[9]+" ("+$@hogm_value$[9]+")"); + mes("11."+$@hogm_name$[10]+" ("+$@hogm_value$[10]+")"); + mes("12."+$@hogm_name$[11]+" ("+$@hogm_value$[11]+")"); + mes("13."+$@hogm_name$[12]+" ("+$@hogm_value$[12]+")"); + mes("14."+$@hogm_name$[13]+" ("+$@hogm_value$[13]+")"); + mes("15."+$@hogm_name$[14]+" ("+$@hogm_value$[14]+")"); + return; +} + +function script HallOfCandor { + mes ""; + mes l("##BHall Of Crazyfefe Fight: TOP 10##b"); + mes("1."+$@cfefe_name$[0]+" ("+$@cfefe_value[0]+")"); + mes("2."+$@cfefe_name$[1]+" ("+$@cfefe_value[1]+")"); + mes("3."+$@cfefe_name$[2]+" ("+$@cfefe_value[2]+")"); + mes("4."+$@cfefe_name$[3]+" ("+$@cfefe_value[3]+")"); + mes("5."+$@cfefe_name$[4]+" ("+$@cfefe_value[4]+")"); + mes("6."+$@cfefe_name$[5]+" ("+$@cfefe_value[5]+")"); + mes("7."+$@cfefe_name$[6]+" ("+$@cfefe_value[6]+")"); + mes("8."+$@cfefe_name$[7]+" ("+$@cfefe_value[7]+")"); + mes("9."+$@cfefe_name$[8]+" ("+$@cfefe_value[8]+")"); + mes("10."+$@cfefe_name$[9]+" ("+$@cfefe_value[9]+")"); + return; +} + +function script HallOfBloodbath { + mes ""; + mes l("##BHall Of Candor's Bloodbath: TOP 10##b"); + mes("1."+$@blood_name$[0]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[0])+")"); + mes("2."+$@blood_name$[1]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[1])+")"); + mes("3."+$@blood_name$[2]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[2])+")"); + mes("4."+$@blood_name$[3]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[3])+")"); + mes("5."+$@blood_name$[4]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[4])+")"); + mes("6."+$@blood_name$[5]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[5])+")"); + mes("7."+$@blood_name$[6]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[6])+")"); + mes("8."+$@blood_name$[7]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[7])+")"); + mes("9."+$@blood_name$[8]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[8])+")"); + mes("10."+$@blood_name$[9]+" ("+FuzzyTime(gettimetick(2)-$@blood_value[9])+")"); + return; +} + +function script HallOfUDT { + mes ""; + mes l("##BHall Of UDT Challenge: TOP 10##b"); + mes("1."+$@udt_name$[0]+" ("+$@udt_value[0]+")"); + mes("2."+$@udt_name$[1]+" ("+$@udt_value[1]+")"); + mes("3."+$@udt_name$[2]+" ("+$@udt_value[2]+")"); + mes("4."+$@udt_name$[3]+" ("+$@udt_value[3]+")"); + mes("5."+$@udt_name$[4]+" ("+$@udt_value[4]+")"); + mes("6."+$@udt_name$[5]+" ("+$@udt_value[5]+")"); + mes("7."+$@udt_name$[6]+" ("+$@udt_value[6]+")"); + mes("8."+$@udt_name$[7]+" ("+$@udt_value[7]+")"); + mes("9."+$@udt_name$[8]+" ("+$@udt_value[8]+")"); + mes("10."+$@udt_name$[9]+" ("+$@udt_value[9]+")"); + return; +} + +function script HallOfDeath { + mes ""; + mes l("##BHall Of Deaths: Reverse TOP10##b"); + mesc l("NOTE: Must have logged in the past 24 hours to be shown."); + mesf("1. %s (%s)", $@hod_name$[0], fnum($@hod_value[0])); + mesf("2. %s (%s)", $@hod_name$[1], fnum($@hod_value[1])); + mesf("3. %s (%s)", $@hod_name$[2], fnum($@hod_value[2])); + mesf("4. %s (%s)", $@hod_name$[3], fnum($@hod_value[3])); + mesf("5. %s (%s)", $@hod_name$[4], fnum($@hod_value[4])); + mesf("6. %s (%s)", $@hod_name$[5], fnum($@hod_value[5])); + mesf("7. %s (%s)", $@hod_name$[6], fnum($@hod_value[6])); + mesf("8. %s (%s)", $@hod_name$[7], fnum($@hod_value[7])); + mesf("9. %s (%s)", $@hod_name$[8], fnum($@hod_value[8])); + mesf("10. %s (%s)", $@hod_name$[9], fnum($@hod_value[9])); + if (PC_DIE_COUNTER > $@hod_value[9]) + mesc l("Your personal record: %s", fnum(PC_DIE_COUNTER)); + return; +} + +function script HallOfHonor { + mes ""; + mes l("##BHall Of Honor: TOP 10##b"); + mesc l("WARNING: This is experimental!!"), 1; + mesc l("We may delete all entries and change formulas in the future."), 1; + mes("1."+$@hof_name$[0]+" ("+$@hof_value[0]+")"); + mes("2."+$@hof_name$[1]+" ("+$@hof_value[1]+")"); + mes("3."+$@hof_name$[2]+" ("+$@hof_value[2]+")"); + mes("4."+$@hof_name$[3]+" ("+$@hof_value[3]+")"); + mes("5."+$@hof_name$[4]+" ("+$@hof_value[4]+")"); + mes("6."+$@hof_name$[5]+" ("+$@hof_value[5]+")"); + mes("7."+$@hof_name$[6]+" ("+$@hof_value[6]+")"); + mes("8."+$@hof_name$[7]+" ("+$@hof_value[7]+")"); + mes("9."+$@hof_name$[8]+" ("+$@hof_value[8]+")"); + mes("10."+$@hof_name$[9]+" ("+$@hof_value[9]+")"); + return; +} + + + + + +function script HallOfReferral { + mes ""; + mes "Referral Program Report - pg. 1"; + mes("1."+$@hor_name$[0]+" ("+$@hor_value[0]+")"); + mes("2."+$@hor_name$[1]+" ("+$@hor_value[1]+")"); + mes("3."+$@hor_name$[2]+" ("+$@hor_value[2]+")"); + mes("4."+$@hor_name$[3]+" ("+$@hor_value[3]+")"); + mes("5."+$@hor_name$[4]+" ("+$@hor_value[4]+")"); + mes("6."+$@hor_name$[5]+" ("+$@hor_value[5]+")"); + mes("7."+$@hor_name$[6]+" ("+$@hor_value[6]+")"); + mes("8."+$@hor_name$[7]+" ("+$@hor_value[7]+")"); + mes("9."+$@hor_name$[8]+" ("+$@hor_value[8]+")"); + mes("10."+$@hor_name$[9]+" ("+$@hor_value[9]+")"); + next; + mes "Referral Program Report - pg. 2"; + mes("11."+$@hor_name$[10]+" ("+$@hor_value[10]+")"); + mes("12."+$@hor_name$[11]+" ("+$@hor_value[11]+")"); + mes("13."+$@hor_name$[12]+" ("+$@hor_value[12]+")"); + mes("14."+$@hor_name$[13]+" ("+$@hor_value[13]+")"); + mes("15."+$@hor_name$[14]+" ("+$@hor_value[14]+")"); + mes("16."+$@hor_name$[15]+" ("+$@hor_value[15]+")"); + mes("17."+$@hor_name$[16]+" ("+$@hor_value[16]+")"); + mes("18."+$@hor_name$[17]+" ("+$@hor_value[17]+")"); + mes("19."+$@hor_name$[18]+" ("+$@hor_value[18]+")"); + mes("20."+$@hor_name$[19]+" ("+$@hor_value[19]+")"); + next; + return; +} + + +function script HallOfAurora { + mes ""; + mes l("##BHall Of Aurora: TOP10##b"); + mesf("1. %s (%s)", $@aurora_name$[0], fnum($@aurora_value[0])); + mesf("2. %s (%s)", $@aurora_name$[1], fnum($@aurora_value[1])); + mesf("3. %s (%s)", $@aurora_name$[2], fnum($@aurora_value[2])); + mesf("4. %s (%s)", $@aurora_name$[3], fnum($@aurora_value[3])); + mesf("5. %s (%s)", $@aurora_name$[4], fnum($@aurora_value[4])); + mesf("6. %s (%s)", $@aurora_name$[5], fnum($@aurora_value[5])); + mesf("7. %s (%s)", $@aurora_name$[6], fnum($@aurora_value[6])); + mesf("8. %s (%s)", $@aurora_name$[7], fnum($@aurora_value[7])); + mesf("9. %s (%s)", $@aurora_name$[8], fnum($@aurora_value[8])); + mesf("10. %s (%s)", $@aurora_name$[9], fnum($@aurora_value[9])); + return; +} + + +function script HallOfAcademic { + mes ""; + mes l("##BHall Of Academics: TOP30##b"); + if (playerattached()) { + .@v=array_entries(ACADEMIC_VOUCH); + .@mv=(ACADEMIC_RANK >= ACADEMIC_DOCTOR ? 3 : 2); + .@mv=(ACADEMIC_RANK >= ACADEMIC_BACHELOR ? .@mv : 1); + mesc l("Your current scholar rank: %s (%d/%d vouches)", + academicrank(), .@v, .@mv); + } + mes ""; + mesf("1. %s (%s)", $@hoa_name$[0], academicrank($@hoa_value[0])); + mesf("2. %s (%s)", $@hoa_name$[1], academicrank($@hoa_value[1])); + mesf("3. %s (%s)", $@hoa_name$[2], academicrank($@hoa_value[2])); + mesf("4. %s (%s)", $@hoa_name$[3], academicrank($@hoa_value[3])); + mesf("5. %s (%s)", $@hoa_name$[4], academicrank($@hoa_value[4])); + mesf("6. %s (%s)", $@hoa_name$[5], academicrank($@hoa_value[5])); + mesf("7. %s (%s)", $@hoa_name$[6], academicrank($@hoa_value[6])); + mesf("8. %s (%s)", $@hoa_name$[7], academicrank($@hoa_value[7])); + mesf("9. %s (%s)", $@hoa_name$[8], academicrank($@hoa_value[8])); + mesf("10. %s (%s)", $@hoa_name$[9], academicrank($@hoa_value[9])); + next; + mesf("11. %s (%s)", $@hoa_name$[10], academicrank($@hoa_value[10])); + mesf("12. %s (%s)", $@hoa_name$[11], academicrank($@hoa_value[11])); + mesf("13. %s (%s)", $@hoa_name$[12], academicrank($@hoa_value[12])); + mesf("14. %s (%s)", $@hoa_name$[13], academicrank($@hoa_value[13])); + mesf("15. %s (%s)", $@hoa_name$[14], academicrank($@hoa_value[14])); + mesf("16. %s (%s)", $@hoa_name$[15], academicrank($@hoa_value[15])); + mesf("17. %s (%s)", $@hoa_name$[16], academicrank($@hoa_value[16])); + mesf("18. %s (%s)", $@hoa_name$[17], academicrank($@hoa_value[17])); + mesf("19. %s (%s)", $@hoa_name$[18], academicrank($@hoa_value[18])); + mesf("20. %s (%s)", $@hoa_name$[19], academicrank($@hoa_value[19])); + next; + mesf("21. %s (%s)", $@hoa_name$[20], academicrank($@hoa_value[20])); + mesf("22. %s (%s)", $@hoa_name$[21], academicrank($@hoa_value[21])); + mesf("23. %s (%s)", $@hoa_name$[22], academicrank($@hoa_value[22])); + mesf("24. %s (%s)", $@hoa_name$[23], academicrank($@hoa_value[23])); + mesf("25. %s (%s)", $@hoa_name$[24], academicrank($@hoa_value[24])); + mesf("26. %s (%s)", $@hoa_name$[25], academicrank($@hoa_value[25])); + mesf("27. %s (%s)", $@hoa_name$[26], academicrank($@hoa_value[26])); + mesf("28. %s (%s)", $@hoa_name$[27], academicrank($@hoa_value[27])); + mesf("29. %s (%s)", $@hoa_name$[28], academicrank($@hoa_value[28])); + mesf("30. %s (%s)", $@hoa_name$[29], academicrank($@hoa_value[29])); + return; +} + +// Hall of AFK +function script HallOfAFK { + mes ""; + mes l("##BHall Of AFK: TOP 10##b"); + mesf("1. %s (%dh%02dm)", $@afk_name$[0], $@afk_value[0]/1200, $@afk_value[0]%1200/60*3); + mesf("2. %s (%dh%02dm)", $@afk_name$[1], $@afk_value[1]/1200, $@afk_value[1]%1200/60*3); + mesf("3. %s (%dh%02dm)", $@afk_name$[2], $@afk_value[2]/1200, $@afk_value[2]%1200/60*3); + mesf("4. %s (%dh%02dm)", $@afk_name$[3], $@afk_value[3]/1200, $@afk_value[3]%1200/60*3); + mesf("5. %s (%dh%02dm)", $@afk_name$[4], $@afk_value[4]/1200, $@afk_value[4]%1200/60*3); + mesf("6. %s (%dh%02dm)", $@afk_name$[5], $@afk_value[5]/1200, $@afk_value[5]%1200/60*3); + mesf("7. %s (%dh%02dm)", $@afk_name$[6], $@afk_value[6]/1200, $@afk_value[6]%1200/60*3); + mesf("8. %s (%dh%02dm)", $@afk_name$[7], $@afk_value[7]/1200, $@afk_value[7]%1200/60*3); + mesf("9. %s (%dh%02dm)", $@afk_name$[8], $@afk_value[8]/1200, $@afk_value[8]%1200/60*3); + mesf("10. %s (%dh%02dm)", $@afk_name$[9], $@afk_value[9]/1200, $@afk_value[9]%1200/60*3); + return; +} + + +// HallOfSponsor(minimal=false) +function script HallOfSponsor { + mes l("This is in honor of all the [@@help://about-server|Contributors@@] who helped rebuilding this world, after the Monster War outbreak."); + if (getvaultid()) + mes l("And also in notable mention of those who [@@https://moubootaurlegends.org/en/community|sponsor@@] the Alliance and its administrative structure."); + else + mes l("And also in notable mention of those who [@@https://www.patreon.com/TMW2|sponsor@@] the Alliance and its administrative structure."); + mes ""; + mes "Saulc, Woody, Sharli, Povo"; + // List previous sponsors? + if (!getarg(0, false)) { + mesc "GonzoDark"; + } + return; +} +// Returns the name of a random sponsor +function script rand_sponsor { + return any("Saulc", "Woody", "Sharli", "Povo"); +} + +// HallOfGame() +function script HallOfGame { + mes l("World hero: %s", $MOST_HEROIC$); + if ($GAME_STORYLINE >= 4) { + mes l("The lighting hero: %s", $LIGHT_HOLDER$); + mes l("The fortress hero: %s", $AEGIS_HOLDER$); + mes l("The accurate hero: %s", $TYRAN_HOLDER$); + mes l("The sorcerer hero: %s", $RUNES_HOLDER$); + mes l("The frontier hero: %s", $DEMUR_HOLDER$); + mes ""; + } else { + mes l("The absolute hero: %s", l("Andrei Sakar")); + mes ""; + } + mes l("Planted Trees: %s", fnum($TREE_PLANTED)); + mes l("Players Killed in PvP: %s", fnum($PLAYERS_KILLED)); + mes l("Monsters Killed in PvE: %s", fnum($MONSTERS_KILLED)); + mes ""; + .@s$=(season() == WINTER ? l("Winter") : .@s$); + .@s$=(season() == AUTUMN ? l("Autumn") : .@s$); + .@s$=(season() == SUMMER ? l("Summer") : .@s$); + .@s$=(season() == SPRING ? l("Spring") : .@s$); + mes l("Season: %s", .@s$); + // weather ; game time ; world story ; etc. + mes ""; + if (getvaultid()) + mes l("Notable mentions and thanks for our [@@https://tmw2.org/contact|sponsors@@], we wouldn't be here without their continued support."); + else + mes l("Notable mentions and thanks for our [@@https://www.patreon.com/TMW2|sponsors@@], we wouldn't be here without their continued support."); + mes ""; + return; +} + +function script HallOf2018 { + mes ""; + if ($YETIKING_WINNER$ != "") { + mes l(".:: FIRST PLAYER TO COMPLETE YETI KING QUEST ::."); + mes $YETIKING_WINNER$; + mes ""; + } + mes l(".:: NOTABLE NAMES ON HURNSCALD LIBERATION DAY ::."); + mes l("DragonStar, Aisen"); + mes ""; + mes l(".:: NOTABLE NAMES ON NIVALIS LIBERATION DAY ::."); + mes l("Jesusalva"); + mes ""; + mes l(".:: FIND-THE-NPC 2018 MINI-EVENT WINNER ::."); + mes "shab"; + next; + mes l(".:: Easter 2018 ::."); + mes l("In honor of DragonStar, winner of Easter 2018."); + mesc l("Unfortunately, other victor's names weren't logged."); + mes ""; + mes l(".:: Worker Day 2018 ::."); + mes l("No victor appliable."); + mes ""; + mes l(".:: Purple Day 2018 ::."); + mes l("No victor appliable."); + next; + mes l(".:: Ched's Summer 2018 ::."); + mes ("1. WarBlade - 5325"); + mes ("2. Aisen - 2000"); + mes ("3. msawis - 1000"); + mesc ("4. vilbou - 400"); + mesc ("5. Woody - 353"); + next; + mes l(".:: Hasan Scorpion Killing Challenge 2018 ::."); + mes ("1. Krists - 1070"); + mes ("2. Aisen - 598"); + mes ("3. AndulkaT - 212"); + mesc ("4. monking - 86"); + mesc ("5. Carbon14 - 78"); + next; + mes l(".:: Christmas 2018 ::."); + mes ("1. WarBlade - 324"); + mes ("2. Xtreem - 190"); + mes ("3. msawis - 110"); + mesc ("4. Krists - 75"); + mesc ("5. Mrhedx - 38"); + mes ""; + mesc ("1. WarBlade - 100,000 GP"); + mesc ("2. msawis - 7,500 GP"); + mesc ("3. LawnCable - 4,450 GP"); + return; +} + +function script HallOf2019 { + mes ""; + if ($HEROESHOLD_WINNER$ != "") { + mes l(".:: FIRST PLAYER TO COMPLETE HEROES HOLD MASTER DUNGEON ::."); + mes $HEROESHOLD_WINNER$; + mes ""; + } + mes l(".:: TMW-2 Anniversary ::."); + //mesc l("Scheduled: January 13rd"); + mes l("No victor appliable."); + mesc l("Anniversary marks the project birthdate. Do not mistake with TMW2 Day."); + mes ""; + mes l(".:: Valentine Day ::."); + //mesc l("Scheduled: February 12th - 15th"); + mes ("1. DragonStar - 300"); + mes ("2. Jesusalva - 121"); + mes ("3. Xanthem - 35"); + mesc ("4. Xtreem - 17"); + mesc ("5. Yuppi - 10"); + next; + mes l(".:: TMW2 Day ::."); + //mesc l("Scheduled: March 2nd"); + mes l("No victor appliable."); + mesc l("TMW2 Day marks the server birthdate. Do not mistake with TMW2 Anniversary."); + mes ""; + mes l(".:: Easter 2019 ::."); + //mesc l("Scheduled: April 17th - 24th"); + mes l("In honor of Woody, winner of Easter 2019."); + mesc l("Unfortunately, other victor's names weren't logged."); + mes ""; + mes l(".:: Worker Day ::."); + //mesc l("Scheduled: April 27th - May 3rd"); + mes l("No victor appliable."); + next; + mes l(".:: Ched's Summer 2019 ::."); + //mesc l("June 21st - September 21st"); + mes ("1. Woody - 3954"); + mes ("2. Crazyfefe - 2092"); + mes ("3. Lilanna - 462"); + mesc ("4. Orby - 281"); + mesc ("5. Saulc - 246"); + mes ""; + /* + mes l(".:: Chocolate Day ::."); + mesc l("Event Canceled"), 1; + //mesc l("Scheduled: July 7th"); + mes ""; + */ + mes l(".:: Open Beta 2019 ::."); + //mesc l("Scheduled: July 20th ~ 31st"); + mes l("1. Jesusalva - 65"); + mes l("2. Kolchak - 63"); + mes l("3. Xanthem - 56"); + mesc l("4. seeds - 43"); + mesc l("5. dangerDuck - 23"); + mes ""; + mes l(".:: Free Software Day ::."); + mesc l("Event Canceled"), 1; + //mesc l("Scheduled: September 9th"); + mes ""; + mes l(".:: International Coffee Day ::."); + //mesc l("Scheduled: October 1st"); + mes l("No victor appliable."); + mes ""; + mes l(".:: Hasan Scorpion Killing Challenge 2019 ::."); + mesc l("September 22nd - December 20th"); + mes ""; + mes l(".:: Thanksgiving 2019 ::."); + //mesc l("November 1st - November 30th"); + mes l("No victor appliable."); + mes ""; + mes l(".:: Christmas 2019 ::."); + //mesc l("Scheduled: December 19th - January 2nd"); + mes l("1. Lilanna - 2498"); + mes l("2. seeds - 1167"); + mes l("3. Woody - 1066"); + mesc l("4. dangerDuck - 540"); + mesc l("5. Heaven - 501"); + return; +} + +function script HallOf2020 { + mesc l("This schedule is subject to change without prior notice."), 1; + mesc l("Changes include but are not limited to festive events and dates."), 1; + next; + if ($REBIRTH_WINNER$ != "") { + mes l(".:: FIRST PLAYER TO REBIRTH ::."); + mes $REBIRTH_WINNER$; + mes ""; + } + mes l(".:: TMW-2 Anniversary ::."); + //mesc l("Scheduled: January 13rd"); + mes l("No victor appliable."); + mesc l("Anniversary marks the project birthdate. Do not mistake with TMW2 Day."); + mes ""; + mes l(".:: Valentine Day ::."); + //mesc l("Scheduled: February 13th - 16th"); + mes l("1. Cassio - 43098"); + mes l("2. Rill - 25955"); + mes l("3. luanaf - 20837"); + mesc l("4. Hocus - 17260"); + mesc l("5. seeds - 13395"); + next; + mes l(".:: TMW2 Day ::."); + //mesc l("Scheduled: March 2nd"); + mes l("No victor appliable."); + mesc l("TMW2 Day marks the server birthdate. Do not mistake with TMW2 Anniversary."); + mes ""; + mes l(".:: Easter 2020 ::."); + //mesc l("Scheduled: April 17th - 24th"); + mes l("In honor of Groata, winner of Easter 2020."); + mesc l("Unfortunately, other victor's names weren't logged."); + mes ""; + mes l(".:: Worker Day ::."); + //mesc l("Scheduled: April 27th - May 3rd"); + mes l("No victor appliable."); + next; + mes l(".:: Ched's Summer 2020 ::."); + //mesc l("June 21st - September 21st"); + mes l("1. Mathias Cronqvist - 15000"); + mes l("2. Woody - 3637"); + mes l("3. dangerDuck - 1225"); + mesc l("4. Amdros - 1113"); + mesc l("5. Xanthem - 615"); + mes ""; + mes l(".:: Chocolate Day ::."); + mesc l("Event Canceled"), 1; + //mesc l("Scheduled: July 7th"); + mes ""; + mes l(".:: Free Software Day ::."); + mesc l("Event Canceled"), 1; + //mesc l("Scheduled: September 9th"); + mes ""; + mes l(".:: International Coffee Day ::."); + //mesc l("Scheduled: October 1st"); + mes l("No victor appliable."); + mes ""; + mes l(".:: Hasan Scorpion Killing Challenge 2020 ::."); + //mesc l("September 22nd - December 20th"); + mes l("1. Leather - 4523"); + mes l("2. Manatauro - 4386"); + mes l("3. G II - 4151"); + mesc l("4. Povo - 3999"); + mesc l("5. Woolie - 3345"); + mes ""; + mes l(".:: Thanksgiving 2020 ::."); + //mesc l("November 1st - November 30th"); + mes l("No victor appliable."); + mes ""; + mes l(".:: Christmas 2020 ::."); + //mesc l("Scheduled: December 19th - January 2nd"); + mes l("1. Manatauro - 29504"); + mes l("2. Povo - 10203"); + mes l("3. YuckFou - 9537"); + mesc l("4. Mathias Cronqvist - 2000"); + mesc l("5. Heaven - 806"); + mes ""; + mesc l("1. YuckFou - 10,000,010 GP"); + mesc l("2. Mathias Cronqvist - 7,423,346 GP"); + mesc l("3. Povo - 1,275,000 GP"); + return; +} + +function script HallOf2021 { + mes ""; + mes l(".:: TMW-2 Anniversary ::."); + //mesc l("Scheduled: January 13rd"); + mes l("No victor appliable."); + mesc l("Anniversary marks the project birthdate. Do not mistake with TMW2 Day."); + mes ""; + mes l(".:: Valentine Day ::."); + //mesc l("Scheduled: February 13th - 16th"); + mesc l("Destroyed by the fire"), 1; + mesc l("In honor of Manatauro, Povo, YuckFou, Cadis Etrama di Raizel and everyone whom participated on it!"), 1; + next; + mes l(".:: TMW2 Day ::."); + //mesc l("Scheduled: March 2nd"); + mes l("No victor appliable."); + mesc l("TMW2 Day marks the server birthdate. Do not mistake with TMW2 Anniversary."); + mes ""; + mes l(".:: Easter 2021 ::."); + //mesc l("Scheduled: April 17th - 24th"); + mesc l("Destroyed by the fire"), 1; + mesc l("In honor of everyone whom participated on it!"), 1; + mes ""; + mes l(".:: Worker Day ::."); + //mesc l("Scheduled: April 27th - May 3rd"); + mes l("No victor appliable."); + next; + mes l(".:: Ched's Summer 2021 ::."); + //mesc l("June 21st - September 21st"); + mesc l("WARNING: Data may have been corrupted."), 1; + mes l("1. andulkaT - 702"); + mes l("2. Gogo VII - 103"); + mes l("3. caslu_jpg - 50"); + mes ""; + mes l(".:: Chocolate Day ::."); + //mesc l("Scheduled: July 7th"); + mes l("No victor appliable."); + mes ""; + mes l(".:: Free Software Day ::."); + //mesc l("Scheduled: September 9th"); + mes l("No victor appliable."); + mes ""; + mes l(".:: International Coffee Day ::."); + //mesc l("Scheduled: October 1st"); + mes l("No victor appliable."); + mes ""; + mes l(".:: Hasan Scorpion Killing Challenge 2021 ::."); + //mesc l("September 22nd - December 20th"); + mes l("1. Duke M - 16,016"); + mes l("2. andulkaT - 6,000"); + mes l("3. Sharli - 4,419"); + mesc l("4. poppet - 3,826"); + mesc l("5. girl flapper - 2,487"); + mes ""; + mes l(".:: Thanksgiving 2021 ::."); + //mesc l("November 1st - November 30th"); + mes l("No victor appliable."); + mes ""; + mes l(".:: Christmas 2021 ::."); + //mesc l("December 19th - January 2nd"); + mes l("1. Alcyone - 5,942"); + mes l("2. andulkaT - 438"); + mes l("3. poppet - 71"); + mes ""; + mesc l("1. andulkaT - 100,001 GP"); + mesc l("2. Alcyone - 53,057 GP"); + mesc l("3. poppet - 20,010 GP"); + return; +} + +function script HallOf2022 { + mes ""; + if ($GEMINI_WINNER$ != "") { + mes l(".:: FIRST PLAYER TO COMPLETE GEMINI SISTERS QUEST ::."); + mes $GEMINI_WINNER$; + mes ""; + } + mes l(".:: TMW-2 Anniversary ::."); + mesc l("Scheduled: January 13rd"); + //mes l("No victor appliable."); + mesc l("Anniversary marks the project birthdate. Do not mistake with TMW2 Day."); + mes ""; + mes l(".:: Valentine Day ::."); + //mesc l("Scheduled: February 13th - 16th"); + mes l("1. poppet - 3056"); + mes l("2. andulkaT - 1018"); + mes l("3. Jesusalva - 310"); + next; + mes l(".:: TMW2 Day ::."); + mesc l("Scheduled: March 2nd"); + //mes l("No victor appliable."); + mesc l("TMW2 Day marks the server birthdate. Do not mistake with TMW2 Anniversary."); + mes ""; + mes l(".:: Easter 2022 ::."); + mesc l("Scheduled: April 17th - 24th"); + mes ""; + mes l(".:: Worker Day ::."); + mesc l("Scheduled: April 27th - May 3rd"); + //mes l("No victor appliable."); + next; + mes l(".:: Ched's Summer 2022 ::."); + mesc l("June 21st - September 21st"); + mes ""; + mes l(".:: Chocolate Day ::."); + mesc l("Scheduled: July 7th"); + //mes l("No victor appliable."); + mes ""; + mes l(".:: Free Software Day ::."); + mesc l("Scheduled: September 9th"); + //mes l("No victor appliable."); + mes ""; + mes l(".:: International Coffee Day ::."); + mesc l("Scheduled: October 1st"); + //mes l("No victor appliable."); + mes ""; + mes l(".:: Hasan Scorpion Killing Challenge 2022 ::."); + mesc l("September 22nd - December 20th"); + mes ""; + mes l(".:: Thanksgiving 2022 ::."); + mesc l("November 1st - November 30th"); + //mes l("No victor appliable."); + mes ""; + mes l(".:: Christmas 2022 ::."); + mesc l("Scheduled: December 19th - January 2nd"); + return; +} + +- script @scoreboard NPC_HIDDEN,{ + end; + +OnCall: + do { + clear; + HallOfSponsor(true); + mes ""; + select + l("Hall of Sponsors"), + l("Hall Of Fortune"), + l("Hall Of Base Level"), + l("Hall Of Job Level"), + l("Hall Of Strange Coins"), + l("Hall Of GM Commands"), + l("Hall Of Guilds"), + l("Hall Of Crazyfefe Battle"), + l("Hall Of Candor Bloodbath"), + l("Hall Of Doppelganger Arena"), + l("Hall Of AF King"), + l("Hall Of Deaths"), + rif(season() == SUMMER || season() == AUTUMN, l("Hall Of Ched")), + l("Hall Of Fame"), + l("Hall Of Academics"), + l("2018 Event Winners"), + l("2019 Event Winners"), + l("2020 Event Winners"), + l("2021 Event Winners"), + l("2022 Event Winners"), + l("Game Statistics"), + l("Quit"); + mes ""; + switch (@menu) { + case 1: + HallOfSponsor(); + next; + break; + case 2: + HallOfFortune(); + next; + break; + case 3: + HallOfLevel(); + next; + break; + case 4: + HallOfJob(); + next; + break; + case 5: + HallOfCoins(); + next; + break; + case 6: + HallOfGMLog(); + next; + break; + case 7: + HallOfGuild(); + next; + break; + case 8: + HallOfCandor(); + next; + break; + case 9: + HallOfBloodbath(); + next; + break; + case 10: + HallOfUDT(); + next; + break; + case 11: + HallOfAFK(); + next; + break; + case 12: + HallOfDeath(); + next; + break; + case 13: + mes b(l("Top 10 - Summer Ched's Event")); + mes("1."+$@ched_name$[0]+" ("+$@ched_value[0]+")"); + mes("2."+$@ched_name$[1]+" ("+$@ched_value[1]+")"); + mes("3."+$@ched_name$[2]+" ("+$@ched_value[2]+")"); + mes("4."+$@ched_name$[3]+" ("+$@ched_value[3]+")"); + mes("5."+$@ched_name$[4]+" ("+$@ched_value[4]+")"); + mes("6."+$@ched_name$[5]+" ("+$@ched_value[5]+")"); + mes("7."+$@ched_name$[6]+" ("+$@ched_value[6]+")"); + mes("8."+$@ched_name$[7]+" ("+$@ched_value[7]+")"); + mes("9."+$@ched_name$[8]+" ("+$@ched_value[8]+")"); + mes("10."+$@ched_name$[9]+" ("+$@ched_value[9]+")"); + next; + break; + case 14: + mes l("Hero: ")+$MOST_HEROIC$; + mes ""; + HallOfHonor(); + next; + break; + case 15: + HallOfAcademic(); + next; + break; + case 16: + HallOf2018(); + next; + break; + case 17: + HallOf2019(); + next; + break; + case 18: + HallOf2020(); + next; + break; + case 19: + HallOf2021(); + next; + break; + case 20: + HallOf2022(); + next; + break; + case 21: + HallOfGame(); + next; + break; + default: + close; + } + } while (true); + end; + +OnInit: + bindatcmd "scoreboard", "@scoreboard::OnCall", 0, 100, 0; + bindatcmd "scoreboards", "@scoreboard::OnCall", 0, 100, 0; + bindatcmd "leaderboard", "@scoreboard::OnCall", 0, 100, 0; + end; +} + diff --git a/npc/functions/seasons.txt b/npc/functions/seasons.txt new file mode 100644 index 0000000..dc41128 --- /dev/null +++ b/npc/functions/seasons.txt @@ -0,0 +1,595 @@ +// TMW2 Script. +// Authors: +// Jesusalva +// Description: +// Season functions + +// Function authored by Reid and edited by Jesusalva +// season({day, month}) +// SQuest_Summer +// returns the current season (approximation) +// WINTER: Winter, 21/12 +// SPRING: Spring, 20/03 +// SUMMER: Summer, 21/06 +// AUTUMN: Autumn, 22/09 + +function script season { + .@current_month = getarg(1, gettime(GETTIME_MONTH)); + + if (.@current_month % 3 == 0) { + .@current_day = getarg(0, gettime(GETTIME_DAYOFMONTH)); + + switch (.@current_month) { + case MARCH: .@season_day = 20; break; + case JUNE: .@season_day = 21; break; + case SEPTEMBER: .@season_day = 22; break; + case DECEMBER: .@season_day = 21; break; + default: break; + } + + .@is_after_season_day = .@current_day >= .@season_day ? 0 : -1; + } + + return (.@current_month / 3 + .@is_after_season_day) % 4; +} + + +// Event seasons +// Christmas cannot be on GlobalEventMenu because it affects seasons system +function script sChristmas { + // Determine the drop rates based on month, and Christmas proximity + if (gettime(GETTIME_MONTH) == DECEMBER) { + if (gettime(GETTIME_DAYOFMONTH) <= 26) + .@m=10; + else + .@m=8; + } else { + .@m=5; + } + + // Add Christmas drops + addmonsterdrop(Moggun, XmasCake, 80*.@m); + addmonsterdrop(AlphaMouboo, XmasCake, 92*.@m); + addmonsterdrop(BlueSlime, XmasCake, 100*.@m); + addmonsterdrop(SantaSlime, XmasCake, 120*.@m); + addmonsterdrop(Pollet, XmasCake, 130*.@m); + addmonsterdrop(IcedFluffy, XmasCake, 150*.@m); + addmonsterdrop(Yeti, XmasCake, 500*.@m); + + addmonsterdrop(Bandit, XmasCandyCane, 30*.@m); + addmonsterdrop(Mouboo, XmasCandyCane, 48*.@m); + addmonsterdrop(WhiteSlime, XmasCandyCane, 50*.@m); + addmonsterdrop(RudolphSlime, XmasCandyCane, 100*.@m); + addmonsterdrop(Fluffy, XmasCandyCane, 200*.@m); + addmonsterdrop(AzulSlime, XmasCandyCane, 200*.@m); + + addmonsterdrop(Duck, GingerBreadMan, 36*.@m); + addmonsterdrop(WaterFairy, GingerBreadMan, 100*.@m); + + // Event drop rates, multiplied by 10 during Christmas (see .@m) + addmonsterdrop(Yeti, ClosedChristmasBox, 350*.@m); + addmonsterdrop(WaterFairy, ClosedChristmasBox, 108*.@m); + addmonsterdrop(Pollet, ClosedChristmasBox, 97*.@m); + addmonsterdrop(AlphaMouboo, ClosedChristmasBox, 83*.@m); + addmonsterdrop(IcedFluffy, ClosedChristmasBox, 67*.@m); + addmonsterdrop(BlueSlime, ClosedChristmasBox, 42*.@m); + addmonsterdrop(Moggun, ClosedChristmasBox, 40*.@m); + addmonsterdrop(SantaSlime, ClosedChristmasBox, 36*.@m); + addmonsterdrop(AzulSlime, ClosedChristmasBox, 20*.@m); + addmonsterdrop(Fluffy, ClosedChristmasBox, 20*.@m); + addmonsterdrop(RudolphSlime, ClosedChristmasBox, 8*.@m); + addmonsterdrop(WhiteSlime, ClosedChristmasBox, 3*.@m); + addmonsterdrop(GiantMaggot, ClosedChristmasBox, 2*.@m); + + // This is not dropped outside December + if (gettime(GETTIME_MONTH) == DECEMBER) { + // Bugfix + if (gettime(GETTIME_YEAR) == 2018) + .@m+=10; + addmonsterdrop(WaterFairy, XmasGift, 6*.@m); + addmonsterdrop(Pollet, XmasGift, 5*.@m); + addmonsterdrop(AlphaMouboo, XmasGift, 5*.@m); + addmonsterdrop(IcedFluffy, XmasGift, 4*.@m); + addmonsterdrop(SantaSlime, XmasGift, 3*.@m); + addmonsterdrop(Fluffy, XmasGift, 2*.@m); + addmonsterdrop(AzulSlime, XmasGift, 2*.@m); + } + + // Change maps for Christmas Season (Specially LoF maps) + addmapmask "003-1", MASK_CHRISTMAS; + addmapmask "005-1", MASK_CHRISTMAS; + addmapmask "009-1", MASK_CHRISTMAS; + addmapmask "012-1", MASK_CHRISTMAS; + addmapmask "017-2", MASK_CHRISTMAS; + addmapmask "017-2-1", MASK_CHRISTMAS; + addmapmask "017-3", MASK_CHRISTMAS; + addmapmask "020-2", MASK_CHRISTMAS; + + // Enable event + set $EVENT$, "Christmas"; + //logmes "Enabled CHRISTMAS event.", LOGMES_ATCOMMAND; + return; +} +// Valentine Day is handled by @event, this is only for @reloadmobdb +function script sValentine { + // Add Valentine drops + addmonsterdrop(WhirlyBird, LoveLetter, 10000); + addmonsterdrop(RedMushroom, LoveLetter, 8); + addmonsterdrop(ChocolateSlime, LoveLetter, 4); + return; +} + +function script sEaster { + // Enable event + set $EVENT$, "Easter"; + addmonsterdrop(Forain, DarkEggshellHat, 2); + if (playerattached()) + logmes "Enabled EASTER event.", LOGMES_ATCOMMAND; + return; +} + +// This allows GMs to change seasons if needed +function script SeasonControl { + do + { + select + "Summer Start", + "Summer End", + "Autumn Start", + "Autumn End", + "Winter Start", + "Winter End", + "Spring Start", + "Spring End", + "SPECIAL - Christmas", + "SPECIAL - Valentine", + "SPECIAL - Easter", + "FORCED DAYLIGHT", + "FORCED NIGHTTIME", + "Abort"; + + switch (@menu) { + case 1: donpcevent("#SeasonCore::OnSummerStart"); break; + case 2: donpcevent("#SeasonCore::OnSummerEnd"); break; + case 3: donpcevent("#SeasonCore::OnAutumnStart"); break; + case 4: donpcevent("#SeasonCore::OnAutumnEnd"); break; + case 5: donpcevent("#SeasonCore::OnWinterStart"); break; + case 6: donpcevent("#SeasonCore::OnWinterEnd"); break; + case 7: donpcevent("#SeasonCore::OnSpringStart"); break; + case 8: donpcevent("#SeasonCore::OnSpringEnd"); break; + case 9: sChristmas(); break; + case 10: sValentine(); break; + case 11: sEaster(); break; + case 12: $@WEATHER_NIGHT=false; doevent("#WeatherCore::OnMinute45"); break; + case 13: $@WEATHER_NIGHT=true; doevent("#WeatherCore::OnMinute45"); break; + } + } while (@menu != 13); + return; +} + +// If skip_checks is set, it'll ignore $@SEASON control. +// SeasonReload( {skip_checks} ) +function script SeasonReload { + // Proccess skip_checks + if (getarg(0,0)) + $@SEASON=99; + + // Summer extra drops + if (season() == SUMMER && $@SEASON != SUMMER) { + donpcevent("#SeasonCore::OnSummerStart"); + } + // Summer end delete drops + if (season() == AUTUMN && $@SEASON == SUMMER) { + donpcevent("#SeasonCore::OnSummerEnd"); + } + // Autumn extra drops + if (season() == AUTUMN && $@SEASON != AUTUMN) { + donpcevent("#SeasonCore::OnAutumnStart"); + } + // Autumn end delete drops + if (season() == WINTER && $@SEASON == AUTUMN) { + donpcevent("#SeasonCore::OnAutumnEnd"); + } + // Winter extra drops + if (season() == WINTER && $@SEASON != WINTER) { + donpcevent("#SeasonCore::OnWinterStart"); + } + // Winter end delete drops + if (season() == SPRING && $@SEASON == WINTER) { + donpcevent("#SeasonCore::OnWinterEnd"); + } + // Spring extra drops + if (season() == SPRING && $@SEASON != SPRING) { + donpcevent("#SeasonCore::OnSpringStart"); + } + // Spring end delete drops + if (season() == SUMMER && $@SEASON == SPRING) { + donpcevent("#SeasonCore::OnSpringEnd"); + } + + // Non-season, but season-related + // Christmas have a special feature + if ($EVENT$ == "Christmas") + sChristmas(); + if ($EVENT$ == "Valentine") + sValentine(); + + $@SEASON=season(); + initnpctimer("#SeasonCore"); + return; +} + +000-0,0,0,0 script #SeasonCore NPC_HIDDEN,{ + end; + +OnSummerStart: + addmonsterdrop(SaxsoGhost, CherryCocktail, 450); + addmonsterdrop(DesertBandit, CherryCocktail, 410); // INVALID + addmonsterdrop(Duck, CherryCocktail, 360); + addmonsterdrop(Croc, CherryCocktail, 180); + addmonsterdrop(RedButterfly, CherryCocktail, 100); + + addmonsterdrop(Centaur, CactusCocktail, 1000); + addmonsterdrop(GiantMaggot, CactusCocktail, 290); // INVALID + addmonsterdrop(FireGoblin, CactusCocktail, 220); // INVALID + addmonsterdrop(DesertMaggot, CactusCocktail, 190); + addmonsterdrop(Scorpion, CactusCocktail, 165); + addmonsterdrop(Maggot, CactusCocktail, 140); + + addmonsterdrop(AlphaMouboo, AppleCocktail, 850); + addmonsterdrop(OceanCroc, AppleCocktail, 480); + addmonsterdrop(Mouboo, AppleCocktail, 280); + addmonsterdrop(RedScorpion, AppleCocktail, 120); + addmonsterdrop(Pinkie, AppleCocktail, 70); + + + addmonsterdrop(Duck, Sunglasses, 1); + addmonsterdrop(Croc, Sunglasses, 1); + addmonsterdrop(SaxsoGhost, Sunglasses, 1); + addmonsterdrop(DesertMaggot, Sunglasses, 1); // INVALID + addmonsterdrop(Scorpion, Sunglasses, 1); + addmonsterdrop(GiantMaggot, Sunglasses, 1); // INVALID + addmonsterdrop(Centaur, Sunglasses, 3); + addmonsterdrop(AlphaMouboo, Sunglasses, 1); + addmonsterdrop(OceanCroc, Sunglasses, 1); + addmonsterdrop(Mouboo, Sunglasses, 1); + addmonsterdrop(Pinkie, Sunglasses, 1); + addmonsterdrop(Moonshroom, Sunglasses, 2); + addmonsterdrop(RedButterfly, Sunglasses, 1); + end; + +OnSummerEnd: + // Inform the top 5 Ched winners to gather their prizes + .@nb = query_sql("SELECT c.name, i.count2, c.char_id FROM `quest` AS i, `char` AS c WHERE i.quest_id=305 AND i.char_id=c.char_id AND i.count1="+(gettime(GETTIME_YEAR)-2000)+" ORDER BY i.count2 DESC LIMIT 10", $@ched_name$, $@ched_value, $@ched_charid); + for (.@i=0;.@i < getarraysize($@ched_charid);.@i++) { + rodex_sendmail($@ched_charid[.@i], "Ched", "Summer Drinks", "Final Ranking: #"+(.@i+1)+". Rewards are available with Ched!"); + } + + delmonsterdrop(Duck, CherryCocktail); + delmonsterdrop(Croc, CherryCocktail); + delmonsterdrop(SaxsoGhost, CherryCocktail); + delmonsterdrop(RedButterfly, CherryCocktail); + delmonsterdrop(DesertBandit, CherryCocktail); + delmonsterdrop(Maggot, CactusCocktail); + delmonsterdrop(DesertMaggot, CactusCocktail); + delmonsterdrop(Scorpion, CactusCocktail); + delmonsterdrop(GiantMaggot, CactusCocktail); + delmonsterdrop(Centaur, CactusCocktail); + delmonsterdrop(FireGoblin, CactusCocktail); + delmonsterdrop(AlphaMouboo, AppleCocktail); + delmonsterdrop(OceanCroc, AppleCocktail); + delmonsterdrop(Mouboo, AppleCocktail); + delmonsterdrop(Pinkie, AppleCocktail); + delmonsterdrop(RedScorpion, AppleCocktail); + delmonsterdrop(Duck, Sunglasses); + delmonsterdrop(Croc, Sunglasses); + delmonsterdrop(SaxsoGhost, Sunglasses); + delmonsterdrop(DesertMaggot, Sunglasses); + delmonsterdrop(Scorpion, Sunglasses); + delmonsterdrop(GiantMaggot, Sunglasses); + delmonsterdrop(Centaur, Sunglasses); + delmonsterdrop(AlphaMouboo, Sunglasses); + delmonsterdrop(OceanCroc, Sunglasses); + delmonsterdrop(Mouboo, Sunglasses); + delmonsterdrop(Pinkie, Sunglasses); + delmonsterdrop(Moonshroom, Sunglasses); + delmonsterdrop(RedButterfly, Sunglasses); + end; + +OnAutumnStart: + // Fancy trees + addmapmask "012-1", MASK_AUTUMN; + + // Autumn's Drop + addmonsterdrop(FafiDragon, PumpkandySeed, 10000); + addmonsterdrop(BlackMamba, PumpkandySeed, 8000); + addmonsterdrop(AlphaMouboo, PumpkandySeed, 5000); + addmonsterdrop(PoisonSpikyMushroom, PumpkandySeed, 3000); + addmonsterdrop(Mouboo, PumpkandySeed, 2000); + addmonsterdrop(Bandit, PumpkandySeed, 1600); + addmonsterdrop(Fluffy, PumpkandySeed, 1500); + addmonsterdrop(LogHead, PumpkandySeed, 1400); + addmonsterdrop(CaveSnake, PumpkandySeed, 1380); + addmonsterdrop(CaveMaggot, PumpkandySeed, 850); + addmonsterdrop(GreenSlime, PumpkandySeed, 750); + addmonsterdrop(Piou, PumpkandySeed, 600); + addmonsterdrop(Squirrel, PumpkandySeed, 500); + end; + +OnAutumnEnd: + // Ched's rewards can't be claimed anymore. Delete that from all players. + DelQuestFromEveryPlayer(SQuest_Ched); + + // Inform the top 5 Hasan winners to gather their prizes + .@nb = query_sql("SELECT c.name, i.count2, c.char_id FROM `quest` AS i, `char` AS c WHERE i.quest_id="+SQuest_Autumn+" AND i.char_id=c.char_id ORDER BY i.count2 DESC LIMIT 5", $@hasn_name$, $@hasn_value, $@hasn_charid); + for (.@i=0;.@i < getarraysize($@hasn_charid);.@i++) { + rodex_sendmail($@hasn_charid[.@i], "Hasan", "Scorpion Hunting", "Final Ranking: #"+(.@i+1)+". Rewards are available with Hasan!"); + } + + + removemapmask "012-1", MASK_AUTUMN; + delmonsterdrop(FafiDragon, PumpkandySeed); + delmonsterdrop(Mouboo, PumpkandySeed); + delmonsterdrop(AlphaMouboo, PumpkandySeed); + delmonsterdrop(Fluffy, PumpkandySeed); + delmonsterdrop(Piou, PumpkandySeed); + delmonsterdrop(CaveSnake, PumpkandySeed); + delmonsterdrop(CaveMaggot, PumpkandySeed); + delmonsterdrop(Bandit, PumpkandySeed); + delmonsterdrop(Squirrel, PumpkandySeed); + delmonsterdrop(PoisonSpikyMushroom, PumpkandySeed); + delmonsterdrop(LogHead, PumpkandySeed); + delmonsterdrop(GreenSlime, PumpkandySeed); + delmonsterdrop(BlackMamba, PumpkandySeed); + end; + +OnWinterStart: + // Nearly all Winterlands + Woodlands + ducks drop winter items. (Summer is Desert shining, Winter is the opposite) + // Winterland Area Mobs + // Moggun AlphaMouboo BlueSlime SantaSlime IcedFluffy Yeti WaterFairy AzulSlime Fluffy + // RudolphSlime WhiteSlime AngryBat Wolvern WindFairy + + // Woodlands Area Mobs + // Tipiu Piousse Silkworm Squirrel Blub CobaltPlant MauvePlant GambogePlant AlizarinPlant + // Loghead Mouboo ForestMushroom SeaSlime Centaur Pinkie CloverField PoisonSpikyMushroom + // ChagashroomField PlushroomField ManaPiou Bluepar LivingPotato RedMushroom RedButterfly + // ManaBug TrainingDummy + + // LoF Area Mobs + // ChocolateSlime Lavern ShadowPlant CyanButterfly + + // Items: Snowflake CaramelCandy GingerBreadMan ChocolateBiscuit + // Quest Requires All Of The Above + + addmonsterdrop(SantaSlime, ChocolateBiscuit, 100); + + addmonsterdrop(WaterFairy, GingerBreadMan, 1000); + addmonsterdrop(RedMushroom, GingerBreadMan, 300); + addmonsterdrop(Duck, GingerBreadMan, 280); + addmonsterdrop(ManaPiou, GingerBreadMan, 240); + addmonsterdrop(AngryBat, GingerBreadMan, 140); + addmonsterdrop(AzulSlime, GingerBreadMan, 110); + addmonsterdrop(Lavern, GingerBreadMan, 90); + + addmonsterdrop(Tipiu, CaramelCandy, 8000); + addmonsterdrop(WindFairy, CaramelCandy, 2000); + addmonsterdrop(IcedFluffy, CaramelCandy, 1200); + addmonsterdrop(Wolvern, CaramelCandy, 1000); + addmonsterdrop(LivingPotato, CaramelCandy, 800); + addmonsterdrop(ChocolateSlime, CaramelCandy, 400); + addmonsterdrop(SeaSlime, CaramelCandy, 400); + addmonsterdrop(Pinkie, CaramelCandy, 200); + addmonsterdrop(Fluffy, CaramelCandy, 150); + addmonsterdrop(SlimeBlast, CaramelCandy, 100); + addmonsterdrop(SilkWorm, CaramelCandy, 30); + + addmonsterdrop(ForestMushroom, Snowflake, 3000); + addmonsterdrop(Blub, Snowflake, 3000); + addmonsterdrop(LogHead, Snowflake, 600); + addmonsterdrop(RedButterfly, Snowflake, 400); + addmonsterdrop(CyanButterfly, Snowflake, 400); + addmonsterdrop(Dummy, Snowflake, 300); + addmonsterdrop(ManaBug, Snowflake, 200); + addmonsterdrop(Piousse, Snowflake, 100); + addmonsterdrop(Squirrel, Snowflake, 100); + addmonsterdrop(CloverPatch, Snowflake, 100); + addmonsterdrop(CobaltPlant, Snowflake, 90); + addmonsterdrop(GambogePlant, Snowflake, 90); + addmonsterdrop(MauvePlant, Snowflake, 90); + addmonsterdrop(AlizarinPlant, Snowflake, 90); + + + addmonsterdrop(AlphaMouboo, ReinbooWand, 3); + addmonsterdrop(BloodyMouboo, ReinbooWand, 2); + addmonsterdrop(Centaur, ReinbooWand, 2); + addmonsterdrop(Mouboo, ReinbooWand, 1); + addmonsterdrop(EasterMouboo, ReinbooWand, 1); + addmonsterdrop(MoubooSlime, ReinbooWand, 1); + end; + +OnWinterEnd: + // Hasan's rewards can't be claimed anymore. Delete that from all players. + DelQuestFromEveryPlayer(SQuest_Autumn); + + delmonsterdrop(WaterFairy, GingerBreadMan); + delmonsterdrop(RedMushroom, GingerBreadMan); + delmonsterdrop(Duck, GingerBreadMan); + delmonsterdrop(ManaPiou, GingerBreadMan); + delmonsterdrop(AngryBat, GingerBreadMan); + delmonsterdrop(AzulSlime, GingerBreadMan); + delmonsterdrop(Lavern, GingerBreadMan); + delmonsterdrop(Tipiu, CaramelCandy); + delmonsterdrop(WindFairy, CaramelCandy); + delmonsterdrop(IcedFluffy, CaramelCandy); + delmonsterdrop(Wolvern, CaramelCandy); + delmonsterdrop(LivingPotato, CaramelCandy); + delmonsterdrop(ChocolateSlime, CaramelCandy); + delmonsterdrop(SeaSlime, CaramelCandy); + delmonsterdrop(Pinkie, CaramelCandy); + delmonsterdrop(SlimeBlast, CaramelCandy); + delmonsterdrop(SilkWorm, CaramelCandy); + delmonsterdrop(ForestMushroom, Snowflake); + delmonsterdrop(Blub, Snowflake); + delmonsterdrop(LogHead, Snowflake); + delmonsterdrop(RedButterfly, Snowflake); + delmonsterdrop(CyanButterfly, Snowflake); + delmonsterdrop(Dummy, Snowflake); + delmonsterdrop(ManaBug, Snowflake); + delmonsterdrop(Piousse, Snowflake); + delmonsterdrop(Squirrel, Snowflake); + delmonsterdrop(CloverPatch, Snowflake); + delmonsterdrop(CobaltPlant, Snowflake); + delmonsterdrop(GambogePlant, Snowflake); + delmonsterdrop(MauvePlant, Snowflake); + delmonsterdrop(AlizarinPlant, Snowflake); + + delmonsterdrop(AlphaMouboo, ReinbooWand); + delmonsterdrop(BloodyMouboo, ReinbooWand); + delmonsterdrop(Centaur, ReinbooWand); + delmonsterdrop(Mouboo, ReinbooWand); + delmonsterdrop(EasterMouboo, ReinbooWand); + delmonsterdrop(MoubooSlime, ReinbooWand); + end; + +OnSpringStart: + // All Woodlands drop spring items. + + // Items: Tulip Rose Blueberries (+ GrassSeeds and AlizarinHerb ) + // Quest Requires All Of The Above (+herbs which are common drop) + // All mobs in same group drops the same thing + + // Boos (GrassSeeds) + addmonsterdrop(Centaur, GrassSeeds, 2000); + addmonsterdrop(AlphaMouboo, GrassSeeds, 800); + addmonsterdrop(Mouboo, GrassSeeds, 300); + addmonsterdrop(MoubooSlime, GrassSeeds, 100); + + // Shrooms (AlizarinHerb) + addmonsterdrop(WickedMushroom, AlizarinHerb, 9000); + addmonsterdrop(ForestMushroom, AlizarinHerb, 6000); + addmonsterdrop(RedMushroom, AlizarinHerb, 2000); + addmonsterdrop(PoisonSpikyMushroom, AlizarinHerb, 1000); + + // Pious (AlizarinHerb) + addmonsterdrop(Tipiu, AlizarinHerb, 8000); + addmonsterdrop(ManaPiou, AlizarinHerb, 740); + addmonsterdrop(Piousse, AlizarinHerb, 400); + + // Mystical & Fairies (Tulip) + addmonsterdrop(GreenDragon, Tulip, 3000); + addmonsterdrop(WindFairy, Tulip, 1000); + addmonsterdrop(PoisonFairy, Tulip, 1000); + addmonsterdrop(WaterFairy, Tulip, 1000); + addmonsterdrop(EarthFairy, Tulip, 1000); + + // Slimes (AlizarinHerb) + addmonsterdrop(BlackSlime, AlizarinHerb, 220); + addmonsterdrop(SeaSlime, AlizarinHerb, 210); + addmonsterdrop(RedSlime, AlizarinHerb, 200); + addmonsterdrop(ChocolateSlime, AlizarinHerb, 50); + + // Snakes (Rose) + addmonsterdrop(BlackMamba, Rose, 1800); + addmonsterdrop(MountainSnake, Rose, 1500); + addmonsterdrop(GrassSnake, Rose, 700); + + // Butterflies (Blueberries) + addmonsterdrop(CyanButterfly, Blueberries, 400); + addmonsterdrop(RedButterfly, Blueberries, 400); + addmonsterdrop(ManaBug, Blueberries, 200); + + // Scorpions (Blueberries) + addmonsterdrop(GoldenScorpion, Blueberries, 4000); + addmonsterdrop(NightScorpion, Blueberries, 3000); + + // Underground mobs (Rose) + addmonsterdrop(Troll, Rose, 3000); + addmonsterdrop(Lavern, Rose, 900); + addmonsterdrop(DarkLizard, Rose, 400); + addmonsterdrop(AngryBat, Rose, 100); + addmonsterdrop(SilkWorm, Rose, 30); + + // Standard Greenary (Tulip) + addmonsterdrop(LogHead, Tulip, 600); + addmonsterdrop(LivingPotato, Tulip, 400); + addmonsterdrop(Pinkie, Tulip, 200); + addmonsterdrop(Squirrel, Tulip, 100); + + // Special mobs and drop + // 1- No mob dropping spring-exclusive (Tulip/Rose/Blueberries) + // 2- Preference to mobs you wouldn't otherwise bother + // 3- Keep a certain level threshold for rarity + addmonsterdrop(Centaur, BrimmedFlowerHat, 4); + addmonsterdrop(Tipiu, BrimmedFlowerHat, 4); + addmonsterdrop(AlphaMouboo, BrimmedFlowerHat, 2); + addmonsterdrop(BlackSlime, BrimmedFlowerHat, 1); + addmonsterdrop(SeaSlime, BrimmedFlowerHat, 1); + addmonsterdrop(RedSlime, BrimmedFlowerHat, 1); + addmonsterdrop(MoubooSlime, BrimmedFlowerHat, 1); + addmonsterdrop(ManaPiou, BrimmedFlowerHat, 1); + + end; + +OnSpringEnd: + delmonsterdrop(Centaur, GrassSeeds); + delmonsterdrop(AlphaMouboo, GrassSeeds); + delmonsterdrop(Mouboo, GrassSeeds); + delmonsterdrop(MoubooSlime, GrassSeeds); + delmonsterdrop(WickedMushroom, AlizarinHerb); + delmonsterdrop(ForestMushroom, AlizarinHerb); + delmonsterdrop(RedMushroom, AlizarinHerb); + delmonsterdrop(PoisonSpikyMushroom, AlizarinHerb); + delmonsterdrop(Tipiu, AlizarinHerb); + delmonsterdrop(ManaPiou, AlizarinHerb); + delmonsterdrop(Piousse, AlizarinHerb); + delmonsterdrop(BlackSlime, AlizarinHerb); + delmonsterdrop(SeaSlime, AlizarinHerb); + delmonsterdrop(RedSlime, AlizarinHerb); + delmonsterdrop(ChocolateSlime, AlizarinHerb); + delmonsterdrop(GreenDragon, Tulip); + delmonsterdrop(WindFairy, Tulip); + delmonsterdrop(PoisonFairy, Tulip); + delmonsterdrop(WaterFairy, Tulip); + delmonsterdrop(EarthFairy, Tulip); + delmonsterdrop(LogHead, Tulip); + delmonsterdrop(LivingPotato, Tulip); + delmonsterdrop(Pinkie, Tulip); + delmonsterdrop(Squirrel, Tulip); + delmonsterdrop(CyanButterfly, Blueberries); + delmonsterdrop(RedButterfly, Blueberries); + delmonsterdrop(ManaBug, Blueberries); + delmonsterdrop(GoldenScorpion, Blueberries); + delmonsterdrop(NightScorpion, Blueberries); + delmonsterdrop(BlackMamba, Rose); + delmonsterdrop(MountainSnake, Rose); + delmonsterdrop(GrassSnake, Rose); + delmonsterdrop(Troll, Rose); + delmonsterdrop(Lavern, Rose); + delmonsterdrop(DarkLizard, Rose); + delmonsterdrop(AngryBat, Rose); + delmonsterdrop(SilkWorm, Rose); + delmonsterdrop(Centaur, BrimmedFlowerHat); + delmonsterdrop(Tipiu, BrimmedFlowerHat); + delmonsterdrop(AlphaMouboo, BrimmedFlowerHat); + delmonsterdrop(BlackSlime, BrimmedFlowerHat); + delmonsterdrop(SeaSlime, BrimmedFlowerHat); + delmonsterdrop(RedSlime, BrimmedFlowerHat); + delmonsterdrop(MoubooSlime, BrimmedFlowerHat); + delmonsterdrop(ManaPiou, BrimmedFlowerHat); + end; + +OnInit: + SeasonReload(1); + end; + +OnHour00: + if ($@SEASON != season()) { + SeasonReload(); + } + end; + +OnTimer700: + charcommand("@refreshall"); + stopnpctimer; + end; +} diff --git a/npc/functions/shake.txt b/npc/functions/shake.txt new file mode 100644 index 0000000..2c3c58a --- /dev/null +++ b/npc/functions/shake.txt @@ -0,0 +1,53 @@ +// Author: Jesusalva + +// Shake player screen +// Usage: +// set @max_shake then call OnShake + +// sshake( {max_shake=6,10}, {closedialog=true} ) +function script sshake { + @max_shake=abs(getarg(0, rand2(6,10))); + for (.@s=0; .@s < @max_shake; .@s++) { + movecam rand(-20,20), rand(-20,20); + sleep2(50); + } + restorecam; + if (getarg(1,false)) + closedialog; + return; +} + +- script shake 32767,{ + end; + +// Loops until @shake is @max_shake +OnShake: + @shake+=1; + movecam rand(-20,20), rand(-20,20); + //closeclientdialog; + + if (@shake < @max_shake) { + addtimer(50, "shake::OnShake"); + } else { + @shake=0; + restorecam; + closedialog; + } + end; + +// Called by GM Command +OnGM: + @shake=0; + @max_shake=rand2(6,10); + addtimer(50, "shake::OnShake"); + end; + +OnCall: + getmapxy(.@m$,.@x,.@y,0); + areatimer(.@m$, .@x-15, .@y-15, .@x+15, .@y+15, 10, "shake::OnGM"); + end; + +OnInit: + bindatcmd "shake", "shake::OnCall", 80, 80, 1; + end; +} diff --git a/npc/functions/shops.txt b/npc/functions/shops.txt new file mode 100644 index 0000000..39db86a --- /dev/null +++ b/npc/functions/shops.txt @@ -0,0 +1,43 @@ +// Evol functions. +// Author: +// 4144 +// Jesusalva +// Description: +// Shops utility functions +// Variables: +// Item ID, Item Price, Item Amount + +// restoreshopitem(amount) or restoreshopitem(price, amount) +function script restoreshopitem { + if (getarg(2, -1337) == -1337) { + .@amount=getarg(1); + .@price=-1; + } else { + .@amount=getarg(2); + .@price=getarg(1); + } + + if (shopcount(getarg(0)) < .@amount) + sellitem getarg(0), .@price, .@amount; + return; +} + + +// shop2({NPC Name, {showmenu=true}}) +function script shop2 { + npctalk3 any( + l("The best wares!"), + l("Buy cheap, sell expensive!") + ); + .@n$=getarg(0, strnpcinfo(0)); + + npcshopattach(.@n$); + + if (getarg(1,true)) + shop .@n$; + else + openshop .@n$; + + closeclientdialog; + return; +} diff --git a/npc/functions/siege.txt b/npc/functions/siege.txt new file mode 100644 index 0000000..33375d3 --- /dev/null +++ b/npc/functions/siege.txt @@ -0,0 +1,547 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Town Siege utilities + +// Siege Spawn +// Can be used anywhere to spawn on the whole map, margins respected. +// siege_spawn( map, mobID, Amount, eventID ) +function script siege_spawn { + .@mp$=getarg(0); + .@mid=getarg(1); + .@qnt=getarg(2); + .@ev$=getarg(3); + + areamonster(.@mp$, 20, 20, getmapinfo(MAPINFO_SIZE_X, .@mp$)-20, getmapinfo(MAPINFO_SIZE_Y, .@mp$)-20, strmobinfo(1, .@mid), .@mid, .@qnt, .@ev$); + return; +} + +// Calculate player average level +// flag 1 - Don't count dead players +// flag 2 - Return highest player level, instead of average. +// flag 4 - Return the total sum of levels instead. +// siege_calcdiff ( map{, flags} ) +function script siege_calcdiff { + .@bsum=0; + if (getarg(1,0) & 1) + .@deadcount=true; + if (getarg(1,0) & 2) + .@onlyhighest=true; + if (getarg(1,0) & 4) + .@onlytotal=true; + .@c = getunits(BL_PC, .@players, false, getarg(0)); + .@skip=0; + + // There is at least one player, do things properly + for (.@i = 0; .@i < .@c; .@i++) { + // Dead players are not counted + if (.@deadcount) { + if (ispcdead(strcharinfo(0, "", .@players[.@i]))) { + .@skip+=1; + continue; + } + } + + .@b=readparam(BaseLevel, .@players[.@i]); + + // GMs are not counted + if (getgroupid(.@players[.@i]) >= 60) { + .@skip+=1; + .@b=0; + } + .@bsum+=.@b; + if (.@b > .@highest) + .@highest=.@b; + } + + // Sanitize and fallback if needed + .@c-=.@skip; + if (!.@c) + .@c=1; + + //debugmes "calcdiff: Total %d Average %d Highest %d", .@bsum, (.@bsum/.@c), .@highest; + + if (.@onlyhighest) + return .@highest; + else if (.@onlytotal) + return .@bsum; + else + return (.@bsum/.@c); +} + +// push to $@SIEGE_TMPMOBS the <mobID> if their level is within a x levels range +// above or below <level> +// siege_push ( mobID, {level{, variation}} ) +function script siege_push { + .@mi=getarg(0); + .@lv=getarg(1,0); + .@var=getarg(2,14); // Old Default: 15, and then 10 + + if ( is_between(.@lv-.@var, .@lv+(.@var/2), strmobinfo(3, .@mi)) ) { + //debugmes "Monster %s (%d) level %d ~= %d", strmobinfo(1, .@mi), .@mi, strmobinfo(3, .@mi), .@lv; + array_push($@SIEGE_TMPMOBS, .@mi); + } + return; +} + +// Selects a monster based on player average strength and base difficulty +// It'll select monsters from 20 levels below to 20 levels above +// tp_mask is the same as TP_* constants and changes mobs present. Only TP_TULIM, +// TP_HURNS and TP_NIVAL are supported. You can use them with the "|" operand, eg., +// TP_TULIM | TP_HURNS. +// +// It currently only creates $@SIEGE_TMPMOBS, you need to use any_of() manually. +// siege_selectmob ( blvl, difficulty{, tp_mask} ) +function script siege_selectmob { + .@blv=getarg(0); + .@dif=getarg(1, 0); + .@tp=getarg(2, 0); + + // We don't need .@dif, so we convert difficulty to levels + .@blv+=(.@dif/any(2,2,3)); + + deletearray $@SIEGE_TMPMOBS; + setarray $@SIEGE_TMPMOBS, ManaGhost, CandiedSlime, Bif, SlimeBlast; + // Now we must select mobs, using array_push() to $@SIEGE_TMPMOBS + // First, mobs on all envs + siege_push(BlackScorpion, .@blv); + siege_push(GreenSlime, .@blv); + siege_push(CaveMaggot, .@blv); + siege_push(MagicGoblin, .@blv); + siege_push(AngryBat, .@blv); + siege_push(BlackSlime, .@blv); + siege_push(BlackScorpion, .@blv); + siege_push(Forain, .@blv); + siege_push(Terranite, .@blv); + siege_push(JackO, .@blv); + siege_push(BlackMamba, .@blv); + siege_push(TerraniteProtector, .@blv); + siege_push(Reaper, .@blv); + + // What if we are trying to select a boss and they're... overleveled? + // We need a level 105, 120 and a level 135 monsters + siege_push(FallenKing2, .@blv); + siege_push(TerraniteKing, .@blv); + + // Now, mobs on only certain envs + if (.@tp & TP_TULIM) { + siege_push(AngryScorpion, .@blv); + siege_push(AngryRedScorpion, .@blv); + siege_push(DesertBandit, .@blv); + siege_push(LavaSlime, .@blv); + siege_push(OldSnake, .@blv); + siege_push(Snake, .@blv); + } + if (.@tp & TP_HURNS) { + siege_push(RedSlime, .@blv); + siege_push(Bandit, .@blv); + siege_push(RedMushroom, .@blv); + siege_push(RobinBandit, .@blv); + siege_push(AngryYellowSlime, .@blv); + siege_push(GrassSnake, .@blv); + siege_push(WickedMushroom, .@blv); + siege_push(GreenDragon, .@blv); + } + if (.@tp & TP_NIVAL) { + siege_push(Bluepar, .@blv); + siege_push(Wolvern, .@blv); + siege_push(Yeti, .@blv); + siege_push(Moggun, .@blv); // PS. Not aggressive + } + + // Removed + //siege_push(DarkLizard, .@blv); + //siege_push(Crafty, .@blv); + + return; +} + +///////////////////////////////////////////////////////////// +// Prepare a siege with optional announce +// siege_setup ( map ) +function script siege_setup { + .@m$=getarg(0); + + // Save old map zone + if (getmapinfo(MAPINFO_ID, .@m$) < 1) { + return Exception("SIEGE ERROR, INVALID MAP ID: "+.@m$, RB_ISFATAL|RB_DEBUGMES|RB_IRCBROADCAST|RB_GLOBALANNOUNCE); + } + //$@MZONE$[getmapinfo(MAPINFO_ID, .@m$)]=getmapinfo(MAPINFO_ZONE, .@m$); + + // Apply changes + addmapmask .@m$, MASK_MATTACK; + changemusic .@m$, any("mythica.ogg", "eric_matyas_ghouls.ogg", "misuse.ogg", "Arabesque.ogg"); + disablenpc("Mana Stone"); + if (.@m$ != "003-1") + pvpon(.@m$); + setmapflag(.@m$,mf_zone,"MMO"); // MMO Zone: Overrides GM Commands + setmapflag(.@m$,mf_bexp,rand2(135,142)); // 35~42% EXP UP on siege maps + .@tn$ = MapToLoc(.@m$, false); + if (getd("$"+.@tn$+"_SIEGEXP")) { + setmapflag(.@m$, mf_bexp, 300); // Triple EXP is on + kamibroadcast("Experience for siege map has been set to 3×!", "INFORMATION"); + setd("$"+.@tn$+"_SIEGEXP", 0); + } + return; +} + +// Check if boss was killed or not +// siege_check ( map ) +function script siege_check { + .@m$=getarg(0); + .@mb=0; + + .@mb+=mobcount(.@m$, "#SiegeCtrl::OnSergeantDeath"); + .@mb+=mobcount(.@m$, "#SiegeCtrl::OnLieutenantDeath"); + .@mb+=mobcount(.@m$, "#SiegeCtrl::OnCaptainDeath"); + .@mb+=mobcount(.@m$, "#SiegeCtrl::OnColonelDeath"); + .@mb+=mobcount(.@m$, "#SiegeCtrl::OnGeneralDeath"); + + // Players failed, so reduce score in 1~5 (like Sergeant~General). + // In future, it could be inverse proportion (-9 for sergeant, -1 for general) + if (.@mb) { + if ($GAME_STORYLINE == 2) + $MK_TEMPVAR-=rand2(1, 5); + + kamibroadcast("Players failed to defend the city!!"); + debugmes "Number of boss grade monsters found: %d", .@mb; + $SIEGE_DIFFICULTY=max(1, ($SIEGE_DIFFICULTY/2)); + // Lower the town exports in 5% (but never more than 25 GP) + .@var$="$"+strtoupper(MapToLoc(.@m$))+"_EXPORT"; + .@pen=min(25, getd(.@var$)/20); + setd(.@var$, getd(.@var$)-.@pen); + } else { + kamibroadcast("The city was defended with success! GG, everyone!"); + $SIEGE_DIFFICULTY+=1; + } + return; +} + +// Revert what siege_setup did +// siege_revert ( map ) +function script siege_revert { + .@m$=getarg(0); + + // Revert map zone (to town, or to blank) and delete backup + removemapflag(.@m$,mf_zone); + //setmapflag(.@m$,mf_zone,$@MZONE$[getmapinfo(MAPINFO_ID, .@m$)]); + setmapflag(.@m$,mf_zone,"Normal2"); // Normal doesn't works... + //setmapflag(.@m$,mf_zone,"All"); + //$@MZONE$[getmapinfo(MAPINFO_ID, .@m$)]=""; + + removemapmask .@m$, MASK_MATTACK; + changemusic .@m$, "caketown.ogg"; // :> + pvpoff(.@m$); + removemapflag(.@m$,mf_bexp); + removemapflag(.@m$,mf_nosave); + setmapflag(.@m$,mf_bexp,100); + killmonsterall(.@m$); + return; +} + +// Create the Siege Boss for #SiegeCtrl utility, DO NEVER CAST TWICE +// siege_boss ( map, difficulty ) +function script siege_boss { + .@m$=getarg(0); + .@s=getarg(1,0); + + // We now select based on player average level and a seed of randomness + .@val=siege_calcdiff(.@m$)+.@s; + + // Nobody is on map: Be TRULY random + if (.@val < 10) { + .@val=rand(20,97)+.@s; + } + + // Switch an adequate boss, almost always stronger + if (.@val <= 25) { + .@mobId=MonsterSergeant; + .@ts$="Sergeant"; + } else if (.@val <= 45) { + .@mobId=MonsterLieutenant; + .@ts$="Lieutenant"; + } else if (.@val <= 67) { + .@mobId=MonsterCaptain; + .@ts$="Captain"; + } else if (.@val <= 90) { + .@mobId=MonsterColonel; + .@ts$="Colonel"; + } else { + .@mobId=MonsterGeneral; + .@ts$="General"; + } + + // We want spawn point to be fixed + .@lx=array_find($@LOCMASTER_MAP$, .@m$); + .@xm=$@LOCMASTER_X[.@lx]; + .@ym=$@LOCMASTER_Y[.@lx]; + + .@xm=.@xm+rand(-1,1); + .@ym=.@ym+rand(-1,1); + + // Announce and spawn + .@mg=monster(.@m$, .@xm, .@ym, strmobinfo(1, .@mobId), .@mobId, 1, "#SiegeCtrl::On"+.@ts$+"Death"); + + // Boost the boss stats based on difficulty and nº of players online + .@bhp=getunitdata(.@mg, UDT_MAXHP); + .@bat=getunitdata(.@mg, UDT_ATKMAX); + .@bai=getunitdata(.@mg, UDT_ATKMIN); + .@bdf=getunitdata(.@mg, UDT_DEF); + .@bcr=getunitdata(.@mg, UDT_CRIT); + + .@s+=getusers(1); + + setunitdata(.@mg, UDT_MAXHP, .@bhp+(.@s*250)+.@val*5*getmapusers(.@m$)); + setunitdata(.@mg, UDT_HP, .@bhp+(.@s*250)+.@val*5*getmapusers(.@m$)); + + setunitdata(.@mg, UDT_ATKMAX, .@bat+(.@s*5)); + setunitdata(.@mg, UDT_DEF, .@bdf+(.@s*4)); + setunitdata(.@mg, UDT_CRIT, .@bcr+(.@s*3)); + setunitdata(.@mg, UDT_ATKMIN, .@bai+(.@s*2)); + + // Spawn some scouts + areamonster(.@m$, .@xm-1, .@ym-1, .@xm+1, .@ym+1, "Scout", any(GreenSlime,RedSlime,AngryYellowSlime), 2); + areamonster(.@m$, .@xm-1, .@ym-1, .@xm+1, .@ym+1, "Scout", any(GreenSlime,RedSlime,AngryYellowSlime), 2); + areamonster(.@m$, .@xm-1, .@ym-1, .@xm+1, .@ym+1, "Scout", any(GreenSlime,RedSlime,AngryYellowSlime), 2); + + announce("##1The Monster "+.@ts$+" arrived! Watch out!", bc_all); + return; +} + +// Spawn some monsters +// siege_cast ( map, NPCName, {, difficulty{, tpflag}} ) +function script siege_cast { + // mz - map ; n - name ; d - difficulty ; tp - teleport + // a - ammount ; e - mobId + .@mz$=getarg(0); + .@n$=getarg(1); + .@d=getarg(2,0); + .@tp=getarg(3,0); + + // Difficulty doesn't applies to Tulimshar + if (.@mz$ == "003-1") + .@d=0; + + siege_selectmob(siege_calcdiff(.@mz$), .@d, .@tp); + + // How many monsters? This value is multiplied by 3; + // 1 per player (= 3 mobs), 1 each 5 difficulty, 1 always present. + .@a=getmapusers(.@mz$); + .@a+=(.@d/5)+1; + + .@e=any_of($@SIEGE_TMPMOBS); + array_remove($@SIEGE_TMPMOBS, .@e); + siege_spawn(.@mz$, .@e, .@a, "#SiegeCtrl::OnRespawn"); + + .@e=any_of($@SIEGE_TMPMOBS); + array_remove($@SIEGE_TMPMOBS, .@e); + siege_spawn(.@mz$, .@e, .@a, "#SiegeCtrl::OnRespawn"); + + .@e=any_of($@SIEGE_TMPMOBS); + array_remove($@SIEGE_TMPMOBS, .@e); + siege_spawn(.@mz$, .@e, .@a, "#SiegeCtrl::OnRespawn"); + return; +} + + +//////////////////////////////////////////// +// Utility Function +// do_siege ( town, outskirts, varcode, flag, npc, timer ) +function script do_siege { + .@m$=getarg(0); + .@o$=getarg(1); + .@c$=getarg(2); + .@tp=getarg(3); + .@n$=getarg(4); + .@t=getarg(5); + + // Dry run + if ($@SIEGE_ABORTED) + return; + + // If no active player, KILL THE SCRIPT + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + .@idle = 0; + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + if (checkidle() < 450) + .@idle++; + detachrid(); + } + + // No one is active, cancel the event + if (!.@idle) { + kamibroadcast(col(b("EVENT CANCELLED DUE TO PLAYER INACTIVITY"),1)); + $@MK_AGGRO=$@MK_AGGRO/5; // Lower aggro bar to 20% + + // Cleanup and Garbage Collection + siege_revert(.@m$); + siege_revert(.@o$); + enablenpc("Mana Stone"); + setd("$@SIEGE_"+.@c$, 0); + setd("$@SIEGE_ABORTED", true); + setd("$@MK_SCENE", MK_NONE); + return; + } + + // In past, we had a $@SIEGE_<town> to determine difficulty + // This behavior is now deprecated, we use a global $SIEGE_DIFFICULTY + // Which raises in 1 every victory and lowers in 1 every defeat (capped at 1) + // But this behavior can be overriden + if (!getd("$@SIEGE_"+.@c$)) { + .@difc=max(1, getd("$SIEGE_DIFFICULTY")); + + // And then, we reset $@SIEGE_<town> so it can be manipulated + // And set .@difc to this value + setd("$@SIEGE_"+.@c$, .@difc); + } + + // Set difficulty based on previous value + .@difc=getd("$@SIEGE_"+.@c$); + + switch (.@t) { + // Warmup + case 70: + case 95: + siege_cast(.@m$, .@n$, .@difc, .@tp); + case 35: + siege_cast(.@o$, .@n$, .@difc, .@tp); + break; + // Setup and casts + case 0: + siege_setup(.@o$); + siege_cast(.@o$, .name$, 0, TP_HURNS); + break; + case 60: + siege_setup(.@m$); + siege_cast(.@o$, .@n$, 0, .@tp); + mapannounce(.@m$, "##2Message to all NPCs in town: Take shelter!", bc_map); + break; + case 90: + siege_cast(.@m$, .@n$, .@difc, .@tp); + siege_cast(.@o$, .@n$, .@difc, .@tp); + break; + // Boss stage + case 280: + siege_boss(.@m$, .@difc); + siege_cast(.@m$, .@n$, .@difc, .@tp); + break; + // Difficulty Raisers + case 310: + case 520: + case 640: + .@varsig=.@difc; + setd("$@SIEGE_"+.@c$, .@varsig+1); + // Regular flow + case 195: + case 220: + case 265: + //case 280: BOSS WAVE + //case 310: difficulty raiser + case 355: + case 400: + case 445: + case 490: + //case 520: difficulty raiser + case 535: + case 580: + case 625: + //case 640: difficulty raiser + case 670: + siege_cast(.@m$, .@n$, .@difc, .@tp); + break; + // Ending flow + // TODO: It would be better to make these values relative to MK_SIEGE_DURATION + case 700: + mapannounce(.@m$, "##1The Monster Army is planning to retreat soon!", bc_map); + siege_cast(.@m$, .@n$, .@difc, .@tp); + break; + case 760: + mapannounce(.@m$, "##1The Monster Army is withdrawing within 30 seconds!", bc_map); + $@MK_SCENE=MK_NONE; + $@MK_AGGRO=$@MK_AGGRO/20; + break; + // Check db/constants.conf to change this value! + case MK_SIEGE_DURATION: + siege_check(.@m$); + siege_revert(.@m$); + siege_revert(.@o$); + enablenpc("Mana Stone"); + setd("$@SIEGE_"+.@c$, 0); + break; + } + + return; +} + +// Utility NPC +- script #SiegeCtrl NPC_HIDDEN,{ + end; + +OnRespawn: + if (playerattached()) { + getmapxy(.@m$,.@x,.@y,0); + if (rand(10000) <= $coinsrate) + makeitem StrangeCoin, 1, .@m$, .@x, .@y; + } + end; + +// Boss Death Labels +OnSergeantDeath: + if ($GAME_STORYLINE == 2) + $MK_TEMPVAR+=1; + getitem StrangeCoin, rand2(1,5); + announce("##2The Monster Sergeant was defeated by "+strcharinfo(0)+"!", bc_all); + $@EXP_EVENT=rand2(1, 3); + $@EXP_EVENT_TIME=1; + donpcevent "@exprate::OnPlayerCall"; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +OnLieutenantDeath: + if ($GAME_STORYLINE == 2) + $MK_TEMPVAR+=3; + getitem StrangeCoin, rand2(5,10); + announce("##2The Monster Lieutenant was defeated by "+strcharinfo(0)+"!", bc_all); + $@EXP_EVENT=rand2(4, 6); + $@EXP_EVENT_TIME=1; + donpcevent "@exprate::OnPlayerCall"; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +OnCaptainDeath: + if ($GAME_STORYLINE == 2) + $MK_TEMPVAR+=5; + getitem StrangeCoin, rand2(10,15); + announce("##2The Monster Captain was defeated by "+strcharinfo(0)+"!", bc_all); + $@EXP_EVENT=rand2(7, 9); + $@EXP_EVENT_TIME=1; + donpcevent "@exprate::OnPlayerCall"; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +OnColonelDeath: + if ($GAME_STORYLINE == 2) + $MK_TEMPVAR+=7; + getitem StrangeCoin, rand2(15,20); + announce("##2The Monster Colonel was defeated by "+strcharinfo(0)+"!", bc_all); + $@EXP_EVENT=rand2(10, 12); + $@EXP_EVENT_TIME=1; + donpcevent "@exprate::OnPlayerCall"; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +OnGeneralDeath: + if ($GAME_STORYLINE == 2) + $MK_TEMPVAR+=9; + getitem StrangeCoin, rand2(20,25); + announce("##2The Monster General was defeated by "+strcharinfo(0)+"!", bc_all); + $@EXP_EVENT=rand2(13, 15); + $@EXP_EVENT_TIME=1; + donpcevent "@exprate::OnPlayerCall"; + specialeffect(FX_FANFARE, AREA, getcharid(3)); + end; + +} + diff --git a/npc/functions/soul_menhir.txt b/npc/functions/soul_menhir.txt new file mode 100644 index 0000000..fa41e9d --- /dev/null +++ b/npc/functions/soul_menhir.txt @@ -0,0 +1,133 @@ +// TMW2 Script +// Author: Jesusalva +// With parts from The Mana World. + +function script SoulMenhir { + // Create @x and @y for this script + @n = rand2(getarraysize(@Xs)); + @x = @Xs[@n]; + @y = @Ys[@n]; + deletearray @Xs; + deletearray @Ys; + @n = 0; + + mes l("[Soul Menhir]"); + mes l("(A mystical aura surrounds this stone. You feel mysteriously attracted to it. Something tells you to touch it. What do you do?)"); + + menu + rif(@x && @y, l("Touch it.")), L_Bind, + rif($@GM_EVENT, l("Send soul to the Mana Plane for GM events")), L_Aeros, + rif($EVENT$ == "Valentine", l("[Valentine Day] Send soul to the Valentine Highlands!")), L_Valentine, + rif($EVENT$ == "Easter", l("[Easter] Send soul to the Mana Forest!")), L_Easter, + rif($EVENT$ == "Worker", l("[Worker Day] Send soul to the Contributor Cave!")), L_Worker, + rif($EVENT$ == "Christmas" && BaseLevel >= 20, l("[Christmas] Send soul to the Christmas Workshop!")), L_Xmas, // TODO: In future there'll be an event map + rif($EVENT$ == "Tower" && countitem(EventDreamTicket), l("Dream Tower")), L_Tower, + rif($EVENT$ == "Raid", l("Boss Raid")), L_Raid, + rif($EVENT$ == "Olympics", l("[Magic Olympics] Send soul to Porthos")), L_Porthos, + l("Leave it alone."), -; + + return; + +L_Bind: + if (@map$ == "003-1" && !(TELEPORTERS & TP_TULIM)) + TELEPORTERS=TELEPORTERS|TP_TULIM; + if (@map$ == "012-1" && !(TELEPORTERS & TP_HURNS)) + TELEPORTERS=TELEPORTERS|TP_HURNS; + if (@map$ == "020-1" && !(TELEPORTERS & TP_NIVAL)) + TELEPORTERS=TELEPORTERS|TP_NIVAL; + + if (Menhir_Activated == 1) + goto L_Shortversion; + + mes l("[Soul Menhir]"); + mes l("(You touch the mysterious stone. Somehow it feels warm and cold at the same time.)"); + mes l("(Suddenly a strange sensation flows through you. It feels like your soul leaves your body and becomes one with the stone.)"); + mes l("(As suddenly as the feeling started it stops. The strange attraction is away from one moment to the next and the menhir feels like just an ordinary stone.)"); + Menhir_Activated = 1; + goto L_Save; + +L_Shortversion: + mes l("[Soul Menhir]"); + mes l("(A strange sensation flows through you. It feels like your soul leaves your body and becomes one with the stone. As suddenly as the feeling started it stops.)"); + goto L_Save; + +L_Aeros: + if (!$@GM_EVENT) goto L_DontPanic; + .@gt=$@AEROS_SPWN; + if (.@gt == 2) { + select + l("East Aeros"), + l("West Aeros"); + .@gt=@menu-1; + } + switch (.@gt) { + case 0: + warp "001-1", 235, 26; break; + case 1: + warp "001-1", 23, 108; break; + } + specialeffect(63, AREA, getcharid(3)); + message strcharinfo(0), l("You are now at the Mana Plane of Existence, at the Floating Island of Aeros."); + close; + +L_Valentine: + if ($EVENT$ != "Valentine") goto L_DontPanic; + warp "001-11", 38, 32; + message strcharinfo(0), l("You are now at the Valentine Highlands."); + close; + +L_Easter: + if ($EVENT$ != "Easter") goto L_DontPanic; + warp "001-4", 151, 157; + message strcharinfo(0), l("You are now at the Mana Plane of Existence, at the Magical Forest."); + close; + +L_Worker: + if ($EVENT$ != "Worker") goto L_DontPanic; + warp "001-5", 22, 79; + message strcharinfo(0), l("You are now at the Mana Plane of Existence, at the Contributor's Cave."); + close; + +L_Xmas: + if ($EVENT$ != "Christmas") goto L_DontPanic; + warp "019-4-1", 32, 36; + message strcharinfo(0), l("You are now at the Christmas Workshop."); + close; + +L_Tower: + if ($EVENT$ != "Tower") goto L_DontPanic; + if (!countitem(EventDreamTicket)) goto L_DontPanic; + doevent "sDreamTower::OnWarpTo"; + close; + +L_Raid: + if ($EVENT$ != "Raid") goto L_DontPanic; + callfunc("FYRaid_Select"); + close; + +L_Porthos: + if ($EVENT$ != "Olympics") goto L_DontPanic; + callfunc("FYE_Olympics_TO"); + closeclientdialog; + close; + +L_DontPanic: + message strcharinfo(0), l("(A strange barrier keeps you from touching the stone at this time.)"); + return; + +L_Save: + if (GSET_SOULMENHIR_MANUAL) { + .@v$=MapToLoc(@map$); + .@tp=POL_LocToTP(strtoupper(.@v$)); + if (!(#EXILED & .@tp)) { + savepoint @map$, @x, @y; + specialeffect(4, SELF, getcharid(3)); + } else { + mesc l("You are exiled from this town and therefore, unable to use the Menhir."), 1; + } + } else { + EnterTown(MapToLoc(@map$)); + dispbottom col(l("Your position is auto-saved when entering a town - use @ucp to change this behavior."), 1); + } + return; +} diff --git a/npc/functions/string.txt b/npc/functions/string.txt new file mode 100644 index 0000000..2a38d90 --- /dev/null +++ b/npc/functions/string.txt @@ -0,0 +1,211 @@ +// Evol Script +// Author: Gumi + +// safe string manipulation functions +// ** does not require PCRE + + +// str(<int>) +// returns whatever is passed, converted to string + +function script str { + return "" + getarg(0); +} + + + +// startswith("<string>", "<search>") +// returns true if <string> begins with <search> + +function script startswith { + return substr(getarg(0), 0, getstrlen(getarg(1)) - 1) == getarg(1); +} + + + +// endswith("<string>", "<search>") +// returns true if <string> ends with <search> + +function script endswith { + .@t = getstrlen(getarg(0)); // total length + .@n = getstrlen(getarg(1)); // substring length + return substr(getarg(0), .@t - .@n, .@t - 1) == getarg(1); +} + + + +// capitalize("<string>") +// returns <string> with its first letter capitalized + +function script capitalize { + return setchar(getarg(0), strtoupper(charat(getarg(0), 0)), 0); +} + + + +// titlecase("<string>" {, "<delimiter>" {, <camel>}}) +// returns <string> with the first letter of each word capitalized +// if <camel> is true, the string is joined in a camelCase fashion + +function script titlecase { + .@delimiter$ = getarg(1, " "); + .@c = getarg(2, 0); + explode(.@words$, getarg(0), .@delimiter$); + + for (.@i = (.@c ? 1 : 0); .@i < 255; ++.@i) + { + if (.@words$[.@i] == "") + { + break; + } + + .@words$[.@i] = setchar(.@words$[.@i], strtoupper(charat(.@words$[.@i], 0)), 0); + } + + return implode(.@words$, (.@c ? "" : .@delimiter$)); +} + + + +// camelcase("<string" {, "<delimiter>"}) + +function script camelcase { + return titlecase(getarg(0), getarg(1, " "), true); +} + + + +// zfill("<string>" {, <width> {, "<padding>"}}) +// returns <string> padded to the left with <padding> up to width + +function script zfill { + .@str$ = getarg(0); + .@width = getarg(1, 8); + .@padding$ = getarg(2, "0"); + + for (.@s = getstrlen(.@str$); .@s < .@width; ++.@s) + { + .@str$ = .@padding$ + .@str$; + } + + return .@str$; +} + + + +// format_number(<integer> {, "<separator>"}) +// formats a number properly + +function script format_number { + .@number$ = str(getarg(0)); + .@len = getstrlen(.@number$); + .@separator$ = getarg(1, ","); + + if (getargcount() < 2 && playerattached()) { + // get from user language + switch (Lang) { + case LANG_FR: .@separator$ = " "; break; // French + case LANG_DE: .@separator$ = "."; break; // Germanic + case LANG_PTBR: .@separator$ = "."; break; // Brazilian + default: .@separator$ = ","; // English (default) + } + } + + for (.@i = .@len - 3; .@i > 0; .@i -= 3) { + .@number$ = insertchar(.@number$, .@separator$, .@i); + } + + return .@number$; +} + + + +// fnum(<integer>) +// alias for format_number + +function script fnum { + return format_number(getarg(0)); +} + + + +// strip("<string>") +// removes spaces at the start and end + +function script strip { + .@s$ = getarg(0); + if (.@s$ == "") { + return ""; + } + .@start = 0; + .@end = getstrlen(.@s$) - 1; + for (.@i = .@start; .@i < .@end; .@i++) + { + if (charat(.@s$, .@i) != " ") { + break; + } else { + .@start++; + } + } + for (.@i = .@end; .@i >= .@start; .@i--) + { + if (charat(.@s$, .@i) != " ") { + break; + } else { + .@end--; + } + } + //debugmes "STRIP.DEBUG MODE ENABLED BY JESUSALVA. PASSING SUBSTRING PARAMS"; + //debugmes "String \""+.@s$+"\" from "+str(.@start)+" to "+str(.@end); + return substr(.@s$, .@start, .@end); +} + + + +// reverse("<string>") +// returns <string> reversed + +function script reverse { + .@str$ = getarg(0); + .@len = getstrlen(.@str$); + + for (.@i = 0; .@i < (.@len / 2); ++.@i) { + .@tmp$ = charat(.@str$, .@i); + .@str$ = setchar(.@str$, charat(.@str$, (.@len - 1 - .@i)), .@i); // a <= b + .@str$ = setchar(.@str$, .@tmp$, (.@len - 1 - .@i)); // b <= a + } + + return .@str$; +} + + + +// repeat("<string>", <multiplier>) +// repeats <string> many times and returns it + +function script repeat { + .@mul = getarg(1); + + for (.@i = 0; .@i < .@mul; ++.@i) { + .@str$ += getarg(0); + } + + return .@str$; +} + + + +// shuffle("<string>") +// returns <string> shuffled + +function script shuffle { + .@str$ = getarg(0); + + for (.@len = getstrlen(.@str$); .@len > 0; --.@len) { + .@rnd = rand(.@len); + .@out$ += charat(.@str$, .@rnd); + .@str$ = delchar(.@str$, .@rnd); + } + + return .@out$; +} diff --git a/npc/functions/teleporter.txt b/npc/functions/teleporter.txt new file mode 100644 index 0000000..fc6afd9 --- /dev/null +++ b/npc/functions/teleporter.txt @@ -0,0 +1,58 @@ +// TMW2 Script +// Authors: +// Jesusalva +// Description: +// Link portals to soul menhirs like the teleporters from old +// The price is temporary. This feature got in because no ship in Nivalis Port +// PS. Anise => “Aisen” Anagram + +// TeleporterGate(TP_CURRENT) +function script TeleporterGate { + .@TP=getarg(0); + + // Validate + .@x=array_find($@LOCMASTER_TP, .@TP); + if (.@x < 0) + return Exception("Invalid Teleport Point: "+.@TP); + + // Obtain current Teleport Point (.@IF) + .@x=array_find($@LOCMASTER_MAP$, getmap()); + .@IF=$@LOCMASTER_TP[.@x]; + + if (!(TELEPORTERS & .@TP)) { + TELEPORTERS=TELEPORTERS|.@TP; + mesn "Anise Inc."; + mesc l("Location Registered. You are now capable to use this warp gate."); + next; + } + mesc l("Where should I warp to?"); + mesc l("Cost: 1 @@", getitemlink(PileOfAsh)), 1; + if (!countitem(PileOfAsh)) + close; + next; + menuint + rif(TELEPORTERS & TP_FROST && .@IF != TP_FROST, l("Frostia")), TP_FROST, + rif(TELEPORTERS & TP_HALIN && .@IF != TP_HALIN, l("Halinarzo")), TP_HALIN, + rif(TELEPORTERS & TP_LILIT && .@IF != TP_LILIT, l("Lilit")), TP_LILIT, + rif(TELEPORTERS & TP_TULIM && .@IF != TP_TULIM, l("Tulimshar")), TP_TULIM, + rif(TELEPORTERS & TP_HURNS && .@IF != TP_HURNS, l("Hurnscald")), TP_HURNS, + rif(TELEPORTERS & TP_NIVAL && .@IF != TP_NIVAL, l("Nivalis")), TP_NIVAL, + rif(TELEPORTERS & TP_LOF && .@IF != TP_LOF, l("Land Of Fire")), TP_LOF, + rif(TELEPORTERS & TP_FORT && .@IF != TP_FORT, l("Fortress Island")), TP_FORT, + rif(TELEPORTERS & TP_BOSSR && .@IF != TP_BOSSR, l("Moubootaur Maze")), TP_BOOSR, + l("None"), -1; + mes ""; + + // Proccess menu + if (@menuret != -1) + delitem PileOfAsh, 1; + else + close; + + // Retrieve location specifics and warp + .@x=array_find($@LOCMASTER_TP, @menuret); + closeclientdialog; + warp $@LOCMASTER_MAP$[.@x], $@LOCMASTER_X[.@x], $@LOCMASTER_Y[.@x]; + return; +} + diff --git a/npc/functions/time.txt b/npc/functions/time.txt new file mode 100644 index 0000000..e6e4c70 --- /dev/null +++ b/npc/functions/time.txt @@ -0,0 +1,117 @@ +// Evol Script +// Authors: Gumi, Jesusalva + +function script now { + return gettimetick(2); +} + +// Returns current time. A SQL update superseeded this. +// santime( ) +function script santime { + return gettimetick(2); +} + +function script time_from_ms { + return now() + (getarg(0) / 1000); +} + +function script time_from_seconds { + return now() + getarg(0); +} + +function script time_from_minutes { + return now() + (getarg(0) * 60); +} + +function script time_from_hours { + return now() + (getarg(0) * 3600); +} + +function script time_from_days { + return now() + (getarg(0) * 86400); +} + + +// FuzzyTime(<unix timestamp>{, <options>{, <precision>}}) +// gives time in a human-readable format +// +// <options> is bitmasked: +// 1 do not show "ago" when in past +// 2 do not show "in" when in the future +// 4 show "from now" instead of "in" when in the future +// +// <precision> is the number of units to show, +// by default uses two (eg. 2m30s or 1h20m). +// Use '99' for max precision + +function script FuzzyTime { + .@future = getarg(0, now()); + .@options = getarg(1, 3); + .@precision = getarg(2, 2); + .@diff = (.@future - now()); + + // check if in the past, or in the future + if (.@diff < 0) { + .@diff *= -1; + .@past = true; + } + + .@diff = max(1, .@diff); + + if (.@diff >= 31536000) { + .@years = (.@diff / 31536000); + .@diff = (++.@s == .@precision ? 0 : (.@diff % 31536000)); + .@ret$ += sprintf("%d %s", .@years, (.@years > 1 ? "years" : "year")); + } + + if (.@diff >= 86400) { + .@days = (.@diff / 86400); + .@diff = (++.@s == .@precision ? 0 : (.@diff % 86400)); + + if (.@s > 1) { + .@ret$ += (.@diff > 0 ? ", " : " and "); + } + + .@ret$ += sprintf("%d %s", .@days, (.@days > 1 ? "days" : "day")); + } + + if (.@diff >= 3600) { + .@hours = (.@diff / 3600); + .@diff = (++.@s == .@precision ? 0 : (.@diff % 3600)); + + if (.@s > 1) { + .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and "); + } + + .@ret$ += sprintf("%d %s", .@hours, (.@hours > 1 ? "hours" : "hour")); + } + + if (.@diff >= 60) { + .@minutes = (.@diff / 60); + .@diff = (++.@s == .@precision ? 0 : (.@diff % 60)); + + if (.@s > 1) { + .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and "); + } + + .@ret$ += sprintf("%d %s", .@minutes, (.@minutes > 1 ? "minutes" : "minute")); + } + + if (.@diff >= 1) { + if (++.@s > 1) { + .@ret$ += (.@s >= 3 ? ", " : " ") + "and "; + } + + .@ret$ += sprintf("%d %s", .@diff, (.@diff > 1 ? "seconds" : "second")); + } + + if (.@past && !(.@options & 1)) { + .@ret$ += " ago"; + } + + if (!(.@past) && !(.@options & 2)) { + .@ret$ = ((.@options & 4) ? sprintf("%s from now", .@ret$) : sprintf("in %s", .@ret$)); + } + + return .@ret$; +} diff --git a/npc/functions/timer.txt b/npc/functions/timer.txt new file mode 100644 index 0000000..ee96b21 --- /dev/null +++ b/npc/functions/timer.txt @@ -0,0 +1,78 @@ +// Evol Script +// Authors: Gumi, Jesusalva + +// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>") +function script areatimer { + .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4)); + for (.@i = 0; .@i < .@c; .@i++) { + addtimer(getarg(5), getarg(6), .@players[.@i]); + } + return .@i; +} + +// areadeltimer("<map>", <x1>, <y1>, <x2>, <y2>, "<npc>::<event>") +function script areadeltimer { + .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4)); + for (.@i = 0; .@i < .@c; .@i++) { + deltimer(getarg(5), .@players[.@i]); + } + return .@i; +} + +// areatimer2("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>") +function script areatimer2 { + .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4)); + for (.@i = 0; .@i < .@c; .@i++) { + deltimer(getarg(6), .@players[.@i]); + addtimer(getarg(5), getarg(6), .@players[.@i]); + } + return .@i; +} + +// addtimer2(<tick>, "<npc>::<event>") +function script addtimer2 { + deltimer(getarg(1)); + addtimer(getarg(0), getarg(1)); + return; +} + + +// maptimer("<map>", <tick>, "<npc>::<event>") +function script maptimer { + .@c = getunits(BL_PC, .@players, false, getarg(0)); + for (.@i = 0; .@i < .@c; .@i++) { + addtimer(getarg(1), getarg(2), .@players[.@i]); + } + return .@i; +} + +// Same as maptimer() but deletes any previously running timer +// maptimer2("<map>", <tick>, "<npc>::<event>") +function script maptimer2 { + .@c = getunits(BL_PC, .@players, false, getarg(0)); + for (.@i = 0; .@i < .@c; .@i++) { + deltimer(getarg(2), .@players[.@i]); + addtimer(getarg(1), getarg(2), .@players[.@i]); + } + return .@i; +} + +// mapdeltimer("<map>", "<npc>::<event>") +function script mapdeltimer { + .@c = getunits(BL_PC, .@players, false, getarg(0)); + for (.@i = 0; .@i < .@c; .@i++) { + deltimer(getarg(1), .@players[.@i]); + } + return .@i; +} + +// partytimer("<map>", <tick>, "<npc>::<event>", partyid) +function script partytimer { + .@c = getunits(BL_PC, .@players, false, getarg(0)); + for (.@i = 0; .@i < .@c; .@i++) { + if (getcharid(1, strcharinfo(0,"",.@players[.@i]) ) == getarg(3)) + addtimer(getarg(1), getarg(2), .@players[.@i]); + } + return .@i; +} + diff --git a/npc/functions/treasure.txt b/npc/functions/treasure.txt new file mode 100644 index 0000000..d9bc42b --- /dev/null +++ b/npc/functions/treasure.txt @@ -0,0 +1,76 @@ +// TMW2 functions. +// Author: +// Jesusalva +// Description: +// Random Treasure Box Utils + +// TreasureBox ( { bonus chance{, TreasureKey} } ) +function script TreasureBox { + .@id=getnpcid(); + if (RNGTREASURE_DATE[.@id] > gettimetick(2)) { + mesc l("The chest is unlocked and empty."); + close; + } + + .@key=getarg(1, TreasureKey); + mesc l("Open the chest?"); + mesc l("Cost: 1 @@", getitemlink(.@key)), 1; + if (!countitem(.@key)) + close; + next; + if (askyesno() == ASK_NO) + close; + + delitem .@key, 1; + mesc l("You open the chest!"); + RNGTREASURE_DATE[.@id]=gettimetick(2)+CHEST_WAITTIME; // Minimum 15 minutes + + .@empty=getvariableofnpc(.empty, strnpcinfo(0)); + if (!.@empty) { + TREASURE_OPEN=TREASURE_OPEN+1; + .@t=TREASURE_OPEN; + .@r=rand(0,10000)-(readparam2(bLuk)*2); + + // Some chests may have different rates + // Note that rare is used as 300 instead of 200 + // This is to normalize with SR/UR formula + if (.@r > 1600) // UC and C (100% ~ 150% bonus) + .@r-=max(getarg(0, 0), 1200)+min((.@r-1600), getarg(0, 0)/2); + else if (.@r > 300) // Rare (67% ~ 120% bonus) + .@r-=max(getarg(0, 0), 285)+min((.@r-300), getarg(0, 0)/5); + else if (.@r <= 300) // SR and UR (67% Bonus) + .@r-=getarg(0, 0)*2/3; + + // Select treasure list + // You're warranted an ultra rare (0.1%) every 149 open chests + // You're warranted a super rare (2%) every 50 open chests + // There's also rares (14%), uncommons (36%) and commons (48%) + .@ur_rate=min(15, (TREASURE_OPEN/10)); + if (.@t % 149 == 0 || .@r < .@ur_rate) + .@loot=any(ScrollMagnusHealC, SaviorBlueprint, DivineApple, MercBoxE, ScrollSDragon, Shemagh, Shemagh, EverburnPowder, IridiumOre, PlatinumOre, SacredImmortalityPotion, MagicApple, ElixirOfLife); + else if (.@t % 50 == 0 || .@r < 200) + .@loot=any(MercBoxC, ScrollMagnusHealB, SnakeEgg, LachesisBrew, ArrowAmmoBox, GoldPieces, SilverGift, TerraniteOre, LeadOre, TinOre, SilverOre, GoldOre, TitaniumOre, FluoPowder, Lockpicks, EquipmentBlueprintC, AlchemyBlueprintC, AlchemyBlueprintD, AncientBlueprint, YerbaMate, JasmineTea, DeathPotion, SacredLifePotion, SacredManaPotion, BrokenWarpCrystal, PurificationPotion, GoldenApple); + else if (.@r < 1600 || .@t == 0) + .@loot=any(MercBoxB, MoubooSteak, SmokeGrenade, ClothoLiquor, Coal, RedPlushWine, PrecisionPotion, CoinBag, DodgePotion, MoveSpeedPotion, Dagger, BronzeGift, IronOre, CopperOre, BlueDye, EquipmentBlueprintB, AlchemyBlueprintB, AlchemyBlueprintC, OolongTea); + else if (.@r < 5200) + .@loot=any(MercBoxA, Croconut, Potatoz, MoubooSteak, ClothoLiquor, Coal, SmallMushroom, HastePotion, StrengthPotion, WoodenLog, LeatherPatch, Beer, StrangeCoin, EquipmentBlueprintA, EquipmentBlueprintB, AlchemyBlueprintA, SpearmintTea, TreasureMap, DungeonMap, IcedBottle); + else + .@loot=any(FatesPotion, PiberriesInfusion, EmptyBottle, ChocolateBar, Plushroom, Chagashroom, RawLog, LeatherPatch, BugLeg, ScorpionStinger, SmallKnife, ChamomileTea, EquipmentBlueprintA); + + + inventoryplace .@loot, 1; + mesc l("You find @@ inside!", getitemlink(.@loot)); + getitem .@loot, 1; + // Get Monster points for treasure hunting (20% from job level) + if (MPQUEST) + Mobpt+=(JobLevel/5); + + // World Expo Event + if ($EVENT$ == "Expo") + FYE_Expo(); + } else { + mesc l("You find @@ inside!", l("nothing")); + } + return; +} + diff --git a/npc/functions/util.txt b/npc/functions/util.txt new file mode 100644 index 0000000..b39c412 --- /dev/null +++ b/npc/functions/util.txt @@ -0,0 +1,1330 @@ +// TMW2 Script. +// Authors: +// Jesusalva +// Description: +// Util functions + +///////////////////////////////////////////////////////////////////////////////// +// Delete item ID on inventories, storages, guild storages and carts. Also affects mails. +// WARNING, irreversible and dangerous! +// DelItemFromEveryPlayer( ID ) +function script DelItemFromEveryPlayer { + if (getarg(0, -1) < 0) + return; + + query_sql("DELETE FROM `inventory` WHERE `nameid`="+getarg(0)); + query_sql("DELETE FROM `cart_inventory` WHERE `nameid`="+getarg(0)); + query_sql("DELETE FROM `storage` WHERE `nameid`="+getarg(0)); + query_sql("DELETE FROM `guild_storage` WHERE `nameid`="+getarg(0)); + query_sql("DELETE FROM `rodex_items` WHERE `nameid`="+getarg(0)); + query_sql("DELETE FROM `auction` WHERE `nameid`="+getarg(0)); + consolewarn "Deleting item %d", getarg(0); + + // Del items which SQL can't reach + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + .@am=countitem(getarg(0), .@players[.@i]); + if (.@am) debugmes "DELETE %d items from ACC %d", .@am, .@players[.@i]; + if (.@am) + delitem(getarg(0), .@am, .@players[.@i]); + } + return; +} + +// Delete an acc_reg entry from all players. Full arrays only. Affect num and str db. +// WARNING, irreversible and dangerous! +// DelAccRegFromEveryPlayer( KEY ) +function script DelAccRegFromEveryPlayer { + if (getarg(0, "error") == "error") + return; + + query_sql("DELETE FROM `acc_reg_num_db` WHERE `key`='"+getarg(0)+"'"); + query_sql("DELETE FROM `acc_reg_str_db` WHERE `key`='"+getarg(0)+"'"); + + if (playerattached()) + detachrid(); + + // Del variables which SQL can't reach + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + if (compare(getarg(0), "$")) + setd(getarg(0), ""); + else + setd(getarg(0), 0); + detachrid(); + } + return; +} + +// Delete an char_reg entry from all players. Full arrays only. Affect num and str db. +// WARNING, irreversible and dangerous! +// DelChrRegFromEveryPlayer( KEY ) +function script DelChrRegFromEveryPlayer { + if (getarg(0, "error") == "error") + return; + + query_sql("DELETE FROM `char_reg_num_db` WHERE `key`='"+getarg(0)+"'"); + query_sql("DELETE FROM `char_reg_str_db` WHERE `key`='"+getarg(0)+"'"); + + // Del variables which SQL can't reach + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + if (compare(getarg(0), "$")) + setd(getarg(0), ""); + else + setd(getarg(0), 0); + detachrid(); + } + return; +} + +// Delete a quest entry from all players. This includes all counters. Use with caution. +// WARNING, irreversible and dangerous! +// DelQuestFromEveryPlayer( ID ) +function script DelQuestFromEveryPlayer { + if (getarg(0, -1) < 0) + return; + + query_sql("DELETE FROM `quest` WHERE `quest_id`="+getarg(0)); + + // Del quests which SQL can't reach + .@a=playerattached(); + if (.@a) detachrid(); + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + setq(getarg(0), 0, 0, 0, 0); + detachrid(); + } + if (.@a) attachrid(.@a); + return; +} + +// Transforms an item in something else. +// ReplaceItemFromEveryPlayer( OldID, NewID ) +function script ReplaceItemFromEveryPlayer { + if (getarg(0, -1) < 0) + return; + consoleinfo("* Server update: %d item was replaced by %d", getarg(0), getarg(1)); + query_sql("UPDATE `inventory` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0)); + query_sql("UPDATE `cart_inventory` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0)); + query_sql("UPDATE `storage` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0)); + query_sql("UPDATE `guild_storage` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0)); + query_sql("UPDATE `rodex_items` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0)); + query_sql("UPDATE `auction` SET `nameid`='"+getarg(1)+"' WHERE `nameid`="+getarg(0)); + return; +} + +// Replaces a skill with another ID. +// ReplaceSkillFromEveryPlayer( OldID, NewID ) +function script ReplaceSkillFromEveryPlayer { + if (getarg(0, -1) < 0) + return; + consoleinfo("* Server update: skill %d was replaced by %d", getarg(0), getarg(1)); + // If new ID already exists, it will skip + query_sql("UPDATE IGNORE `skill` SET `id`='"+getarg(1)+"' WHERE `id`="+getarg(0)); + return; +} + + +///////////////////////////////////////////////////////////////////////////////// +// Returns Nard reputation for discounts +// Currently ranges from 0 to 16. +function script nard_reputation { + .@nr=0; // Base reputation + + // Valon Quest (+1 rep) + if (getq(CandorQuest_Trainer) >= 14) + .@nr=.@nr+1; + + // Zegas Quest (+1 rep) + if (getq(CandorQuest_Barrel) >= 4) + .@nr=.@nr+1; + + // Hide And Seek Quest (+1 rep) + if (getq(CandorQuest_HAS) >= 4) + .@nr=.@nr+1; + + // Sailors Quest (+1 rep) + if (getq(CandorQuest_Sailors) >= 3) + .@nr=.@nr+1; + + // Sailors Quest, part 2 (+1 rep) + if (getq(CandorQuest_SailorCure) >= 1) + .@nr=.@nr+1; + + // Vincent Quest (+1 rep) + if (getq(CandorQuest_Vincent) >= 2) + .@nr=.@nr+1; + + // Tolchi Quest (+1 rep) + if (getq(CandorQuest_Tolchi) >= 4) + .@nr=.@nr+1; + + // Maya Quest (+1 rep) + if (getq(CandorQuest_Maya) >= 4) + .@nr=.@nr+1; + + // Rosen Quest (+1 rep) + if (getq(CandorQuest_Rosen) >= 3) + .@nr=.@nr+1; + + // Ship Crew Quests + // Dan Quest (+1 rep) + if (getq(ShipQuests_Dan) >= 3) + .@nr=.@nr+1; + + // Chef Gado Quest (+1 rep) + if (getq(ShipQuests_ChefGado) >= 2) + .@nr=.@nr+1; + + // Peter Quest (+1 rep) + if (getq(ShipQuests_Peter) >= 7) + .@nr=.@nr+1; + + // Tulimshar Quests + // Swezanne Quest (+1 rep) + if (getq(TulimsharQuest_Swezanne) >= 1) + .@nr=.@nr+1; + + // Sailors Quest (+1 rep) + if (getq(TulimsharQuest_Sailors) >= 2) + .@nr=.@nr+1; + + // Hasan Quest (+1 rep) + if (getq(TulimsharQuest_Hasan) >= 5) + .@nr=.@nr+1; + + // Dausen Quest (+1 rep) + if (getq(TulimsharQuest_WaterForGuard) >= 3) + .@nr=.@nr+1; + + //debugmes "Reputation: "+str(.@nr); + return .@nr; + +} + +// Returns reputation based on quests completion for discounts. Returns 0~100 int. +// Takes one argument (PC_DEST$). Grep for "getarg". +function script reputation { + .@nr=0; // Base reputation + + // Tulimshar Quests (16 points) + if (getarg(0) == "Tulim") { + // Eugene Quest (+1 rep) + if (getq(TulimsharQuests_Fishman) >= 2) + .@nr=.@nr+1; + + // Sarah Quest (+1 rep) + if (getq(TulimsharQuest_Sarah) >= 1) + .@nr=.@nr+1; + + // Dausen Quest (+1 rep) + if (getq(TulimsharQuest_WaterForGuard) >= 3) + .@nr=.@nr+1; + + // Dausen Quest II (+1 rep) + if (getq(TulimsharQuest_MobTutorial) >= 8) + .@nr=.@nr+1; + + // Swezanne Quest (+1 rep) + if (getq(TulimsharQuest_Swezanne) >= 1) + .@nr=.@nr+1; + + // Silvia Quest (+1 rep) + if (getq(TulimsharQuest_Lifestone) >= 2) + .@nr=.@nr+1; + + /* + // Eisten Quest (+1 rep) + if (getq(TulimsharQuest_Eistein) >= 6) + .@nr=.@nr+1; + */ + + // Hasan Quest (+1 rep) + if (getq(TulimsharQuest_Hasan) >= 5) + .@nr=.@nr+1; + + // Devoir Quest (+1 rep) + if (getq(TulimsharQuest_Devoir) >= 1) + .@nr=.@nr+1; + + // Sailors Quest (+1 rep) + if (getq(TulimsharQuest_Sailors) >= 2) + .@nr=.@nr+1; + + // Zarkor Quest (+1 rep) + if (getq(TulimsharQuest_DarkInvocator) >= 7) + .@nr=.@nr+1; + + // Anwar Quest (+1 rep) + if (getq(TulimsharQuest_AnwarField) >= 10) + .@nr=.@nr+1; + + // Neko Quest (+1 rep) + if (getq(TulimsharQuest_Neko) >= 2) + .@nr=.@nr+1; + + // Tycoon Quest (+1 rep) + if (getq(MineQuest_Tycoon) >= 15) + .@nr=.@nr+1; + + // Dracoula Quest (+1 rep) + if (getq(MineQuest_Dracoula) >= 1) + .@nr=.@nr+1; + + // Caelum Quest (+1 rep) + if (getq(MineQuest_Caelum) >= 2) + .@nr=.@nr+1; + + // Naem Quest (+1 rep) + if (getq(MineQuest_Naem) >= 3) + .@nr=.@nr+1; + + // Veteran Officer Quest (+1 rep) + if (getq(TulimsharQuest_WoodenSword) >= 2) + .@nr=.@nr+1; + + // TULIMSHAR Magical Forumula + .@nr=.@nr*100/16; + + + + + + + + // Hurnscald Quests (11 points) + } else if (getarg(0) == "Hurns") { + + // Alan Quest (+1 rep) + if (getq(HurnscaldQuest_ForestBow) >= 2) + .@nr=.@nr+1; + + // Gwendolyn Quest (+1 rep) + if (getq(HurnscaldQuest_HarkEye) >= 6) + .@nr=.@nr+1; + + // Celestia Quest (+1 rep) + if (getq(HurnscaldQuest_TeaParty) >= 2) + .@nr=.@nr+1; + + /* + // Yeti King Quest (+1 rep) + // Please note that if you challenge it again and lose, it'll reset + if (getq(HurnscaldQuest_Celestia) == 6) + .@nr=.@nr+1; + */ + + // Farmers Quest (+1 rep) + if (getq(HurnscaldQuest_Farmers) >= 5) + .@nr=.@nr+1; + + // Helena Quest (+1 rep) + if (getq(HurnscaldQuest_Bandits) >= 8) + .@nr=.@nr+1; + + // Injuried Mouboo Quest (+1 rep) + if (getq(HurnscaldQuest_InjuriedMouboo) >= 2) + .@nr=.@nr+1; + + // Blood Donor Quest (+1 rep) + if (getq(HurnscaldQuest_BloodDonor) >= 1) + .@nr=.@nr+1; + + // Woody Quest (+1 rep) + if (getq(HurnscaldQuest_Woody) >= 5) + .@nr=.@nr+1; + + // Lieutenant Quest (+1 rep) + if (getq(HurnscaldQuest_Lieutenant) >= 10) + .@nr=.@nr+1; + + // Thorn Quest (+1 rep) + if (getq(HurnscaldQuest_Thorn) >= 1) + .@nr=.@nr+1; + + // Blossom Quest (+1 rep) + if (getq(HurnscaldQuest_Blossom) >= 1) + .@nr=.@nr+1; + + // LOF Bot Quest (+1 rep) + if (getq(HurnscaldQuest_LOFPass) >= 3) + .@nr=.@nr+1; + + // HURNSCALD Magical Forumula + .@nr=.@nr*100/12; + + + + + + + + // Land Of Fire Quests (6 points) + } else if (getarg(0) == "LoF") { + + // The EPISODE + //// Tea For Two (+1 rep) + if (getq(LoFQuest_EPISODE) >= 2) + .@nr=.@nr+1; + //// Early Christmas (+1 rep) + if (getq(LoFQuest_EPISODE) >= 6) + .@nr=.@nr+1; + //// Order's Request (+1 rep) + if (getq(LoFQuest_EPISODE) >= 13) + .@nr=.@nr+1; + + // George Quest (+1 rep) + if (getq(LoFQuest_George) >= 5) + .@nr=.@nr+1; + + // Fairy Quest (+1 rep) + if (getq(LoFQuest_Fairy) >= 3) + .@nr=.@nr+1; + + // Pet Detective Quest (+1 rep) + if (getq(LoFQuest_Pets) >= 1) + .@nr=.@nr+1; + + // LAND OF FIRE Magical Forumula + .@nr=.@nr*100/6; + + + + + + + // Nivalis Quests (6 points) + } else if (getarg(0) == "Nival") { + + // Nivalis Well Quest (+1 rep) + if (getq(NivalisQuest_Well) >= 2) + .@nr=.@nr+1; + + // Nivalis Cindy Quest (+1 rep) + if (getq(NivalisQuest_Cindy) >= 5) + .@nr=.@nr+1; + + // Blue Sage: Investigation Quest (+1 rep) + if (getq(NivalisQuest_BlueSage) >= 12) + .@nr=.@nr+1; + + // Blue Sage: Slime Hunting Quest (+1 rep) + if (getq(NivalisQuest_BlueSageSlimes) >= 2) + .@nr=.@nr+1; + + // Blue Sage: Page Makers Quest (+1 rep) + if (getq(NivalisQuest_BlueSagePagemaker) >= 1) + .@nr=.@nr+1; + + // Blue Sage: Page Finders Quest (+1 rep) + if (getq(NivalisQuest_BlueSagePagefinder) >= 1) + .@nr=.@nr+1; + + // Nivalis Henry Quest (+1 rep) + if (getq(NivalisQuest_Henry) >= 2) + .@nr=.@nr+1; + + // NIVALIS Magical Forumula + .@nr=.@nr*100/7; + + + + + + + + // Halinarzo Quests (5 points) + } else if (getarg(0) == "Halin") { + + // Foxhound Famine Quest (+1 rep) + if (getq(HalinarzoQuest_Foxhound) >= 6) + .@nr=.@nr+1; + + // Charles Quest (+1 rep) + if (getq(HalinarzoQuest_TraderKing) >= 2) + .@nr=.@nr+1; + + // Joaquim & Yumi Quest (+1 rep) + if (getq(HalinarzoQuest_SickWife) >= 5) + .@nr=.@nr+1; + + // Life Delight Quest (+1 rep) + if (getq(HalinarzoQuest_LifeDelight) >= 2) + .@nr=.@nr+1; + + // Fisherman Quest (crossover) (+1 rep) + if (getq(HurnscaldQuest_ForestBow) >= 2) + .@nr=.@nr+1; + + // Alvasus Quest (weekly) (+1 rep) + if (getq(HalinarzoQuest_Alvasus) >= 1) + .@nr=.@nr+1; + + // Sawis Quest (+1 rep) + if (getq(HalinarzoQuest_Sawis) >= 2) + .@nr=.@nr+1; + + // Mirio Quest (+1 rep) + if (getq(HalinarzoQuest_Speed) >= 1) + .@nr=.@nr+1; + + // HALINARZO Magical Forumula + .@nr=.@nr*100/8; + + + + + + + + // Frostia Quests (3 points) + } else if (getarg(0) == "Frostia") { + + // Homunculus Quest (+1 rep) + if (getq(FrostiaQuest_Homunculus) >= 4) + .@nr=.@nr+1; + + // AFK Cap Quest (+1 rep) + if (getq(FrostiaQuest_AFKCap) >= 2) + .@nr=.@nr+1; + + // Jeremy Quest (+1 rep) + if (getq(FrostiaQuest_Jeremy) >= 2) + .@nr=.@nr+1; + + // Erlan Quest (+1 rep) + if (getq(FrostiaQuest_Erlan) >= 2) + .@nr=.@nr+1; + + // Rydel Quest (+1 rep) + if (getq(FrostiaQuest_Rydel) >= 2) + .@nr=.@nr+1; + + // Meriel Quest (+1 rep) + if (getq(FrostiaQuest_Meriel) >= 2) + .@nr=.@nr+1; + + // Taenya Quest (+1 rep) + if (getq(FrostiaQuest_Taenya) >= 2) + .@nr=.@nr+1; + + // Talindra Quest (+1 rep) + if (getq(FrostiaQuest_Talindra) >= 2) + .@nr=.@nr+1; + + // Gaelira Quest (+1 rep) + if (getq(FrostiaQuest_Gaelira) >= 2) + .@nr=.@nr+1; + + // Saevel Quest (+1 rep) + if (getq(FrostiaQuest_Saevel) >= 2) + .@nr=.@nr+1; + + // Jhon H's Quest (+1 rep) + if (getq(FrostiaQuest_JohnH) >= 1) + .@nr=.@nr+1; + + // FROSTIA Magical Forumula + .@nr=.@nr*100/11; + + + + + + + + // Candor Quests (10 points) + } else if (getarg(0) == "Candor") { + + // Valon Quest (+1 rep) + if (getq(CandorQuest_Trainer) >= 14) + .@nr=.@nr+1; + + // Zegas Quest (+1 rep) + if (getq(CandorQuest_Barrel) >= 4) + .@nr=.@nr+1; + + // Hide And Seek Quest (+1 rep) + if (getq(CandorQuest_HAS) >= 4) + .@nr=.@nr+1; + + // Sailors Quest (+1 rep) + if (getq(CandorQuest_Sailors) >= 3) + .@nr=.@nr+1; + + // Sailors Quest, part 2 (+1 rep) + if (getq(CandorQuest_SailorCure) >= 1) + .@nr=.@nr+1; + + // Vincent Quest (+1 rep) + if (getq(CandorQuest_Vincent) >= 2) + .@nr=.@nr+1; + + // Tolchi Quest (+1 rep) + if (getq(CandorQuest_Tolchi) >= 3) + .@nr=.@nr+1; + + // Maya Quest (+1 rep) + if (getq(CandorQuest_Maya) >= 4) + .@nr=.@nr+1; + + // Rosen Quest (+1 rep) + if (getq(CandorQuest_Rosen) >= 3) + .@nr=.@nr+1; + + // Marggo Quest (+1 rep) + if (getq(CandorQuest_Marggo) >= 1) + .@nr=.@nr+1; + + // CANDOR Magical Forumula + .@nr=.@nr*100/10; + + + + + + + + // Fortress Town Quests (1 point/special) + } else if (getarg(0) == "Fortress") { + + // Who am I? (+2 rep) + if (getq(General_Narrator) >= 22) + .@nr=.@nr+2; + + // Cadis: Great Slime Hunt (+1 rep) + if (getq(FortressQuest_SlimeHunter) >= 2) + .@nr=.@nr+1; + + // Cadis: Great Over100 Hunt (+1 rep) + if (getq(FortressQuest_Over100) >= 2) + .@nr=.@nr+1; + + // Cadis: Great Ranged Hunt (+1 rep) + if (getq(FortressQuest_RangedHunt) >= 2) + .@nr=.@nr+1; + + // FORTRESS TOWN Magical Formula + .@nr=.@nr*100/5; + + + // Final + } + + //debugmes "Reputation: "+str(.@nr); + return .@nr; + +} + + +// Returns time for ship travel. +// Can be modified by a factor. +function script nard_time { + // Estimates time to move by ship from LOCATION$ to getarg(0) + + // From Candor + if (LOCATION$ == "Candor") { + if (getarg(0) == "Tulim") + return 22000; + if (getarg(0) == "Artis") + return 60000; + + } + // From Tulimshar + if (LOCATION$ == "Tulim") { + if (getarg(0) == "Candor") + return 22000; + if (getarg(0) == "Hurns") + return 28000; + if (getarg(0) == "Nival") + return 52000; + if (getarg(0) == "Artis") + return 60000; + if (getarg(0) == "Tulim") + return 12000; // Script-Controlled + + } + // From Hurnscald + if (LOCATION$ == "Hurns") { + if (getarg(0) == "Candor") + return 22000; + if (getarg(0) == "Tulim") + return 28000; + if (getarg(0) == "Nival") + return 28000; + + } + // From Nivalis + if (LOCATION$ == "Nival") { + if (getarg(0) == "Candor") + return 46000; + if (getarg(0) == "Tulim") + return 52000; + if (getarg(0) == "Hurns") + return 28000; + + } + // From Artis + if (LOCATION$ == "Artis") { + if (getarg(0) == "Tulim") + return 10000; + if (getarg(0) == "Candor") + return 10000; + + } + + // Error + consolewarn "ERROR, INVALID LOCATION AND DESTINATION"; + consolewarn("%s -> %s", LOCATION$, getarg(0)); + dispbottom l("An error on your travel time happened. Please report."); + return INT_MAX; +} + +// alignment() → 1 if Good, -1 if Evil, 0 if Neutral +function script alignment { + .@m=getq(HurnscaldQuest_InjuriedMouboo); + + // Mouboo was slain: EVIL + if (.@m >= 9) + return -1; + + // Mouboo was saved and Sagratha rescued: GOOD + if (SAGRATHA_FRIENDSHIP >= 2) + return 1; + + // N/A: NEUTRAL + return 0; +} + +// Returns if you are a legendary weapon holder +// islegendary( {strcharinfo} ) +function script islegendary { + .@you$ = getarg(0, strcharinfo(0)); + // TODO: countitem(Lightbringer) => Because rent time? + return (.@you$ == $LIGHT_HOLDER$ || + .@you$ == $AEGIS_HOLDER$ || + .@you$ == $TYRAN_HOLDER$ || + .@you$ == $RUNES_HOLDER$ || + .@you$ == $DEMUR_HOLDER$); +} + +// Returns if an event is a ranked Aurora Event or not +// (Had to be moved from functions/aurora.txt) +function script FYEventUsesRanking { + setarray .@av$, "Expo", "Fishing", "Mining", "Tower", "Raid", "Olympics"; + if (array_find(.@av$, $EVENT$) >= 0) { + return true; + } + return false; +} + +// Determines if player is still in range. +// eg. +// if (reachable(.x, .y, .distance)) { +function script reachable { + .@x=getarg(0); + .@y=getarg(1); + .@z=getarg(2); + getmapxy(.@mp$, .@xp, .@yp, 0); + + if (distance(.@x, .@y, .@xp, .@yp) <= .@z) + return 1; + else + return 0; +} + +// Determines if party exp sharing is enabled +// ( Party ID ) +function script party_expon { + .@nb = query_sql("SELECT exp FROM `party` WHERE party_id="+escape_sql(getarg(0))+" LIMIT 2", .@value); + return .@value[0]; +} + +// Special rif for books +// rif2(<menu_id>, <condition>, <text>) +function script rif2 { + return rif( getarg(1) ,rif(@menu == getarg(0), "► ") + getarg(2)); +} + + +// Prepare Mana Stone +// mstone( lvl ) +function script mstone { + // Fill variable + .@v=getarg(0); + + // Determine how much stats you need, this is based on players + // and change based on $Global Variables + .int=7; + .lvl=15; + .jlv=10; + + return ( + MAGIC_LVL == .@v && + readparam2(bInt) >= $MANA_BINT+(.int*.@v) && + BaseLevel >= $MANA_BLVL+(.lvl*.@v) && + JobLevel >= $MANA_JLVL+(.jlv*.@v) && + readparam(Sp) == readparam(MaxSp)); +} + +// MAGIC_PTS → Amount of used Magic Skill Points +// sk_maxpoints() → Max Magic Skill Points you may use +// Returns how many points you can use +// Current maximum as of 2020-06-21: (pratic) 30 ~ 43 (theoric) +function script sk_maxpoints { + // 2 points per magic level + .@val=(MAGIC_LVL)*2; + // 1 point every twice magic level + .@val+=(MAGIC_LVL/2); + // Excluding first 15, 1 point every 12 job levels (Up to JL 75) + .@val+=min(5, ((JobLevel-15)/12)); + // 1 point per being a player + .@val+=1; + // 2 points per Rebirth + .@val+=(REBIRTH*2); + // 1 point per skill permit level + .@val+=getskilllv(TMW2_SKILLPERMIT); + // Sacrificing the Mouboo: +1 MSP + .@val+=(alignment() < 0 ? 1 : 0); + return .@val; +} + +// Returns how many points you can allocate +function script sk_points { + return sk_maxpoints()-MAGIC_PTS; +} + +// Returns true if a skill can be leveled up. +// sk_canlvup( {cost=1} ) +function script sk_canlvup { + return ((MAGIC_PTS+getarg(0,1)) <= sk_maxpoints()); +} + +// Level up a skill in 1 level +// TODO: Return the point if leveling about Max Level +// sk_lvup( sk{, cost=0} ) +function script sk_lvup { + .@lvl=getskilllv(getarg(0)); + getexp 0, 50*(.@lvl+1); + addtoskill(getarg(0),.@lvl+1,0); + if (getarg(1,0)) { + MAGIC_PTS+=getarg(1,0); + } + return; +} + +// LEGACY Magic School Learning Interface +// mlearn( skill, MAX_LV, MSP cost, item, amount{, AP cost} ) +// returns false if cheater +function script mlearn { + .@sk=getarg(0); + .@ff=getarg(1); + .@msp=getarg(2); + .@it=getarg(3); + .@am=getarg(4); + .@ap=getarg(5, 0); + // Max level reached + if (getskilllv(.@sk) >= .@ff) { + return true; + } + // Not enough items + if (countitem(.@it) < .@am && !(countitem(ScholarshipBadge))) + return false; + // Not enough MSP + if (!sk_canlvup(.@msp)) + return false; + // Not enough AP + if (MAGIC_RP < .@ap) { + return false; + } + + // Payment + if (countitem(.@it) < .@am) + delitem ScholarshipBadge, 1; + else + delitem .@it, .@am; + + // Level up + sk_lvup(.@sk, .@msp); + MAGIC_RP-=.@ap; + return true; +} + +// NEW Magic School Learning Interface +// learn_magic(Skill) +function script learn_magic { + .@ski=getarg(0); + .@learn$=l("Learning"); + + // Check if skill is valid + .@mlv=$@MSK_MAXLV[.@ski]; + if (.@mlv < 1) { + return Exception("ERROR: The skill "+.@ski+" is not valid!"); + } + + // Load a few temporary variables + .@pre=$@MSK_PREREQ[.@ski]; + .@it=$@MSK_ITEM[.@ski]; + .@am=$@MSK_AMOUNT[.@ski]; + .@msp=$@MSK_MSPCOST[.@ski]; + .@ap=($@MSK_COST[.@ski]*getskilllv(.@ski)*100); + + // Pre-requisite check + if (.@pre) { + if (getskilllv(.@pre) < 1) { + mesc l("Pre-requisites not met!"), 1; + mesc l("The following skill is needed: %s%s (Lv. %d)", + "##9", getskillname(.@pre), 1), 1; + next; + return false; + } + } + + // Max level reached + if (getskilllv(.@ski) >= .@mlv) { + mesc l("You've reached the maximum level for this skill."), 1; + next; + return true; + } + + // Skill level check + if (getskilllv(.@ski)) { + .@learn$=l("Upgrading"); + // New MSP ruleset + if (getskilllv(.@ski) > 5) + .@msp = 1; + else + .@msp = 0; + } else if (.@msp <= 1) { + // 1 MSP skills take no Research Points + .@ap=0; + } + + // Discount from usage [Mathias] + .@ap=max(0, .@ap-skillInvoke[.@ski]); + + mesc l("%s %s will require:", .@learn$, getskillname(.@ski)); + mes l("* %d/%d MSP (Magic Skill Points)", sk_points(), .@msp); + mes l("* %s/%s RP (Research Points)", fnum(MAGIC_RP), fnum(.@ap)); + if (countitem(.@it) < .@am) { + mesc l("~~%d/%d %s~~", countitem(.@it), .@am, getitemlink(.@it)), 8; + mes l("* %d/%d %s", countitem(ScholarshipBadge), 1, getitemlink(ScholarshipBadge)); + } else { + mes l("* %d/%d %s", countitem(.@it), .@am, getitemlink(.@it)); + } + mes ""; + mesc l("Really learn this skill?"); + if (askyesno() == ASK_NO) + return true; + + return mlearn(.@ski, .@mlv, .@msp, .@it, .@am, .@ap); +} + +// transcheck( {item 1, amount 1}, {item 2, amount 2}... ) +// returns true upon success +function script transcheck { + if (getargcount() < 2 || getargcount() % 2 != 0) + return Exception("Faulty learning skill command invoked - error"); + + // Count items + for (.@i=0;.@i < getargcount(); .@i++) { + if (countitem(getarg(.@i)) < getarg(.@i+1)) + return false; + .@i++; + } + + // Delete Items + for (.@i=0;.@i < getargcount(); .@i++) { + delitem getarg(.@i), getarg(.@i+1); + .@i++; + } + return true; +} + +// Returns a value defining your current magic control (affects success ratio, higher is better) +// A value of '5' means perfect control, and a value of '0' means overwhelm. +// abizit() +function script abizit { + if (!MAGIC_LVL) return 0; + .@base=((MAGIC_LVL*2)**3); + return min(MAGIC_EXP/.@base, 5); +} + +// anyloot( {item 1, amount 1, chance 1}, {item 2, amount 2, chance 2}... ) +// Give chance (standard 1~10000 roll) to obtain item, capped at amount. +// TODO: Fill an array, then inventoryplace() and getitem() +function script anyloot { + if (getargcount() < 3 || getargcount() % 3 != 0) + return Exception("Faulty anyloot skill command invoked - error"); + + // Get Items + for (.@i=0;.@i < getargcount(); .@i+=3) { + if (rand2(10000) < getarg(.@i+2)) + getitem getarg(.@i), rand2(1, getarg(.@i+1)); + } + return true; +} + + +// Returns, based on a 1-5 range, the title for both thief and merc ranks +// thiefrank() / mercrank() +function script thiefrank { + switch (THIEF_RANK) { + case 5: return l("Bandit Lord"); + case 4: return l("Assassin"); + case 3: return l("Rogue"); + case 2: return l("Bandit"); + case 1: return l("Thief"); + case 0: return l("Citizen"); + default: return l("Error"); + } +} +function script mercrank { + switch (MERC_RANK) { + case 5: return l("Constable"); + case 4: return l("Guardian"); + case 3: return l("Merchant"); + case 2: return l("Trader"); + case 1: return l("Fair Person"); + default: return l("Error"); + } +} +function script academicrank { + switch (getarg(0, ACADEMIC_RANK)) { + case 8: return l("Grand Master"); // Reserved for GM Team + case 7: return l("Sage"); + case 6: return l("Ph.D"); + case 5: return l("Doctor"); + case 4: return l("Master"); + case 3: return l("Bachelor"); + case 2: return l("Technician"); + case 1: return l("Student"); + case 0: return l("Layman"); + default: return l("Banned from Academy"); + } +} + +// alias to readbattleparam(getcharid(3), ?? ) +function script battleparam { + return readbattleparam(getcharid(3), getarg(0)); +} + +// gettimeparam(GETTIME_X) +// Returns the number of seconds/minutes/hours/days/months/years since 01/01/1970 +function script gettimeparam { + .@p=getarg(0, GETTIME_MINUTE); + + // Seconds + .@t=gettimetick(2); + if (.@p == GETTIME_SECOND) + return .@t; + + // Minutes (default) + .@t=.@t/60; + if (.@p == GETTIME_MINUTE) + return .@t; + + // Hours + .@t=.@t/60; + if (.@p == GETTIME_HOUR) + return .@t; + + // Days + .@t=.@t/24; + if (.@p == GETTIME_DAYOFMONTH) + return .@t; + + // Weeks (estimative) + .@a=.@t+4; // 01/01/1970 was a Thursday. So this will make it float at sunday. + .@a=.@a/7; + if (.@p == GETTIME_WEEKDAY) + return .@a; + + // Months (estimative. FIXME - use (gettime(YEAR)-1970)*12 + gettime(MONTH)) + .@t=.@t/30; + if (.@p == GETTIME_MONTH) + return .@t; + + // Years (estimative, unused, fallback) + .@t=.@t/12; + return .@t; +} + + +// Convert LOC (uppercase) to a TP variable +// POL_LocToTP( {TOWNCODE} ) +function script POL_LocToTP { + .@tw$=strtoupper(getarg(0, LOCATION$)); + + if (.@tw$ == "TULIM") + return TP_TULIM; + + if (.@tw$ == "HALIN") + return TP_HALIN; + + if (.@tw$ == "HURNS") + return TP_HURNS; + + if (.@tw$ == "LOF") + return TP_LOF; + + if (.@tw$ == "NIVAL") + return TP_NIVAL; + + if (.@tw$ == "ARTIS") + return TP_ARTIS; + + if (.@tw$ == "CANDOR") + return TP_CANDOR; + + if (.@tw$ == "LILIT") + return TP_LILIT; + + // TODO: Change this to use npc/config/location.txt instead + if (.@tw$ == "FROSTIA") + return TP_FROST; + + return Exception("Invalid town requested / POL_LocToTP", RB_DEFAULT|RB_SPEECH, -1); +} + + +// Upon entering a town +// EnterTown( LocName ) +function script EnterTown { + // Fill variable + .@v$=getarg(0); + + // Validade variable, see npc/config/location.txt first + if (array_find($@LOCMASTER_LOC$, .@v$) < 0) + return Exception("Invalid location passed to EnterTown: "+.@v$); + + // Do not save if you're exiled + .@tpcode=POL_LocToTP(strtoupper(.@v$)); + if (!(#EXILED & .@tpcode)) + LOCATION$=.@v$; + return; +} + +// Convert map name to location id +// LocToMap( LocName ) +function script LocToMap { + // Fill variable + .@v$=getarg(0); + + // Error code + if (playerattached()) + .@err=RB_DEFAULT; + else + .@err=RB_DEBUGMES; + + // Validade variable, see npc/config/location.txt first + .@lx=array_find($@LOCMASTER_LOC$, .@v$); + if (.@lx < 0) + return Exception("Invalid location passed to LocToMap: "+.@v$, .@err); + + return $@LOCMASTER_MAP$[.@lx]; +} + +// Convert map name to location id +// MapToLoc( MapName, report=True ) +function script MapToLoc { + // Fill variable + .@v$=getarg(0); + + // Error code + if (playerattached()) + .@err=RB_DEFAULT; + else + .@err=RB_DEBUGMES; + + // Validate variable, see npc/config/location.txt first + .@lx=array_find($@LOCMASTER_MAP$, .@v$); + if (.@lx < 0) { + if (getarg(1, true)) + return Exception("Invalid map passed to MapToLoc: "+.@v$, .@err); + else + return ""; + } + + return $@LOCMASTER_LOC$[.@lx]; +} + +// Gets the location code for TP code +function script TPToLoc { + .@i=array_find($@LOCMASTER_TP, getarg(0)); + return $@LOCMASTER_MAP$[.@i]; + return; +} + +// Warps home and updates LOCATION$ +function script teleporthome { + warp "Save", 0, 0; + .@i=array_find($@LOCMASTER_MAP$, getmap()); + if (.@i >= 0) + EnterTown($@LOCMASTER_LOC$[.@i]); + else + consolewarn("[ERROR] Invalid Town Map for Time Flask: %s", getmap()); + return; +} + +// Returns TOP 3 Average Level +// TOP3AVERAGELVL( - ) +function script TOP3AVERAGELVL { + return ($@hoblvl_value[0]+$@hoblvl_value[1]+$@hoblvl_value[2])/3; +} + +// Grants newcomers exp boost. Returns bonus % +// NewcomerEXPDROPUP( - ) +function script NewcomerEXPDROPUP { + // Event System Override + if ($EVENT$ == "Anniversary") { + if (BaseLevel < 10) + BaseLevel=10; + } + // Newbies + if (!REBIRTH) { + .@AVG_LEVEL=($@hoblvl_value[0]+$@hoblvl_value[1]+$@hoblvl_value[2])/3; + .@BONUS=min(50, .@AVG_LEVEL/2); + .@BONUS-=BaseLevel; + .@BONUS=max(1, .@BONUS); + // Rebirth + } else { + .@BONUS=REBIRTH*2; + } + // Defaults to 24 hours + sc_end SC_CASH_PLUSEXP; + sc_end SC_CASH_RECEIVEITEM; + sc_start SC_CASH_PLUSEXP, 86400000, (REBIRTH ? .@BONUS : .@BONUS*2/3); + sc_start SC_CASH_RECEIVEITEM, 86400000, .@BONUS; + // This does not belong here, but... + if (getq(LoFQuest_EPISODE) == 13) { + sc_start SC_POISON, 86400000, 1, 10000, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK; + } + return .@BONUS; +} + +// Frostia Util +// frally( ) +function script frally { + return (Class == Elven); // or something +} + +// Easter Egg +// RegEasterEgg(EE_CODE, {CoinsAmount=3}) +function script RegEasterEgg { + .@code=getarg(0); + .@coin=getarg(1,3); + .@q=getq2(General_EasterEggs); + + if (!(.@q & .@code)) { + setq1 General_EasterEggs, 1; + setq2 General_EasterEggs, .@q|.@code; + dispbottom l("For finding an Easter Egg, you got Strange Coins!"); + getitem StrangeCoin, .@coin; + setq3 General_EasterEggs, bitmask_count(.@q|.@code); + } + return; +} + +// Makes a monster aggro +// set_aggro( monster{, mode=MD_AGGRESSIVE} ) +function script set_aggro { + .@m=getarg(0); + .@x=getarg(1, MD_AGGRESSIVE); + .@op=getunitdata(.@m, UDT_MODE); + .@op=.@op|.@x; + setunitdata(.@m, UDT_MODE, .@op); + return; +} + +function script immortal { + .@u=getarg(0); + setunitdata(.@u, UDT_HP, 2147483647); + setunitdata(.@u, UDT_MAXHP, 2147483647); + return; +} + +// Special function which makes a date as a number +// numdate( - ) +function script numdate { + .@strdate$=sprintf("%04d%02d%02d", gettime(GETTIME_YEAR), gettime(GETTIME_MONTH), gettime(GETTIME_DAYOFMONTH)); + // Debug payload + if ($@OVERRIDE_NUMDATE) + return $@OVERRIDE_NUMDATE; + return atoi(.@strdate$); +} + +// Same as numdate() but SQL format. +// sqldate({day variation, month variation}) +function script sqldate { + .@d=gettime(GETTIME_DAYOFMONTH)+getarg(0, 0); + .@m=gettime(GETTIME_MONTH)+getarg(1, 0); + .@y=gettime(GETTIME_YEAR); + // Overflow prevention + if (.@d <= 0) { + .@d=1; + } + while (.@m > 12) { + .@y+=1; + .@m-=12; + } + while (.@m < 1) { + .@y-=1; + .@m+=12; + } + .@strdate$=sprintf("%04d-%02d-%02d %02d:%02d:%02d", .@y, .@m, .@d, gettime(GETTIME_HOUR), gettime(GETTIME_MINUTE), gettime(GETTIME_SECOND)); + return .@strdate$; +} + +// json_encode( {varname, varvalue}, {varname 2, varvalue 2}... ) +// returns string +function script json_encode { + if (getargcount() < 2 || getargcount() % 2 != 0) + return Exception("json_encode arguments must be paired"); + + .@json$="{"; + .@tab=true; + + // For arguments + for (.@i=0;.@i < getargcount(); .@i++) { + // Close previous item + if (.@tab) + .@tab=false; + else + .@json$+=","; + + // Input variable name + .@json$+="\""+getarg(.@i)+"\": "; + + // Input variable value + if (isstr(getarg(.@i+1))) + .@json$+="\""+getarg(.@i+1)+"\""; + else + .@json$+=getarg(.@i+1); + + // Advance + .@i++; + } + + // Close the JSON + .@json$+="}"; + return .@json$; +} + + +// api_send( code, data ) +// sends to API +function script api_send { + .@cde=getarg(0); + if (!apicall(.@cde, getarg(1))) { + debugmes "[API] Fallback in use."; + .@fm$=escape_sql(getarg(1)); + query_sql("INSERT INTO `api_export` (`type`, `data`) VALUES ('"+.@cde+"', \""+.@fm$+"\")"); + } + return; +} + diff --git a/npc/functions/vault.txt b/npc/functions/vault.txt new file mode 100644 index 0000000..78eb5e7 --- /dev/null +++ b/npc/functions/vault.txt @@ -0,0 +1,41 @@ +// TMW-2 Script +// Author: +// Jesusalva +// Description: +// Vault Utilities + +function script getvaultid { + // FIXME: Make this False + if ($BETASERVER && !debug) + return ##VAULT; + else + return 0; +} + +function script getvaultexp { + .@exp=getarg(0); + if (.@exp > 100) + Exception("ILLEGAL VAULT EXPERIENCE, FIXME URGENTLY. STOPPING SCRIPT BY FORCE WHILE DOING NOTHING.", + RB_DEBUGMES | RB_IRCBROADCAST | RB_GLOBALANNOUNCE | RB_ISFATAL); + if (getvaultid()) { + ##VAULT_EXP+=.@exp; + consoleinfo("Granting %d Soul Exp to %d under the Moubootaur's authority.", + .@exp, ##VAULT); + } + return; +} + +// MirrorLakeSendTo(World, Lake) +function script MirrorLakeSendTo { + .@w=getarg(0); + .@t=getarg(1); + ##VAULT_GOTO=.@w; + ##VAULT_MLTO=.@t; + closeclientdialog; + dispbottom l("Darkness fills your vision..."); + sleep2(1000); + kick(getcharid(3), 7); // 7 is not a valid kick reason + //atcommand("@kick "+strcharinfo(0)); + end; +} + diff --git a/npc/functions/weather.txt b/npc/functions/weather.txt new file mode 100644 index 0000000..5c369e1 --- /dev/null +++ b/npc/functions/weather.txt @@ -0,0 +1,352 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Controls world seasons. RESPECT MASK_* VARS ON CONSTANTS DB + +// is_night(set=False) +function script is_night { + // If we're not configuring, retrieve the variable (cache) + // It is more efficient this way + .@set=getarg(0, false); + if (!.@set) + return $@WEATHER_NIGHT; + + /* + // Night time depends on season. + // Summer: Day > Night + // Winter: Night > Day + // Autumn/Spring: Day = Night + // + // But we have TWO SUNS, meaning night is always smaller. + // + // 2 = GETTIME_MINUTE + // 3 = GETTIME_HOUR + // Summer: 5h day. 1h night. (4 cycles, 8 updates) + // Others: 3h day. 1h night. (6 cycles, 12 updates) + // Winter: 2h day. 1h night. (8 cycles, 16 updates) + */ + // NEW Unified Rule: Day last 3 hours. Night lasts 1 hour. Always. + .@t=(season() == SUMMER ? 6 : (season() == WINTER ? 3 : 4)); + return (gettime(3) % .@t == 1); +} + +000-0,0,0,0 script #WeatherCore NPC_HIDDEN,{ + end; + +/* + * removemapflag("<map name>", <flag>) + * setmapflag("<map name>", <flag>{, <val>}) + * getmapflag("<map name>", <flag>) + + mf_snow: 16 + + mf_jexp: 39 + mf_bexp: 40 +*/ + +OnInit: + // This is weather startup + .@init=true; + $@WEATHER_NIGHT=is_night(true); + .tpc=0; + .tcl=0; + // Bind commands + bindatcmd "wsnow", "#WeatherCore::OnSnow", 80, 80, 1; + bindatcmd "wrain", "#WeatherCore::OnRain", 80, 80, 1; + bindatcmd "wsand", "#WeatherCore::OnSand", 80, 80, 1; + bindatcmd "wevil", "#WeatherCore::OnEvil", 80, 80, 1; + bindatcmd "wnight", "#WeatherCore::OnNight", 80, 80, 1; + bindatcmd "wclear", "#WeatherCore::OnClear", 80, 80, 1; + bindatcmd "wreset", "#WeatherCore::OnReset", 99, 99, 1; + bindatcmd "wset", "#WeatherCore::OnManual", 99, 99, 1; + + + // Determine which maps are subject to weather, and how weather works: + // eg. it will never snow on a desert, or a sandstorm on icelands. + .wcore = htnew; + + // Deserts + htput(.wcore, "003-1", "desert"); + htput(.wcore, "004-1", "desert"); + htput(.wcore, "004-2", "desert"); + htput(.wcore, "009-1", "desert"); + htput(.wcore, "010-1", "desert"); + htput(.wcore, "010-2", "desert"); + htput(.wcore, "032-1", "desert"); + + // Woodlands + htput(.wcore, "001-4", "woodland"); + htput(.wcore, "001-11", "woodland"); + htput(.wcore, "001-12", "woodland"); + htput(.wcore, "005-1", "woodland"); + htput(.wcore, "012-1", "woodland"); + htput(.wcore, "014-1", "woodland"); + htput(.wcore, "014-2", "woodland"); + htput(.wcore, "014-3", "woodland"); + htput(.wcore, "014-4", "woodland"); + htput(.wcore, "014-5", "woodland"); + htput(.wcore, "017-1", "woodland"); + htput(.wcore, "018-1", "woodland"); + htput(.wcore, "018-2", "woodland"); + htput(.wcore, "018-4", "woodland"); + htput(.wcore, "018-5", "woodland"); + htput(.wcore, "027-1", "woodland"); + + // Icelands + htput(.wcore, "001-7", "iceland"); + htput(.wcore, "019-1", "iceland"); + htput(.wcore, "019-2", "iceland"); + htput(.wcore, "019-3", "iceland"); + htput(.wcore, "019-4", "iceland"); + htput(.wcore, "019-5", "iceland"); + htput(.wcore, "019-6", "iceland"); + htput(.wcore, "020-1", "iceland"); + htput(.wcore, "022-1", "iceland"); + htput(.wcore, "024-1", "iceland"); + htput(.wcore, "031-1", "iceland"); + + // Special + htput(.wcore, "011-3", "special"); + + + consoleinfo "[Weather.sys] Total Maps = " + htsize(.wcore); + // No "end" here, so server starts with weather +OnMinute00: +OnMinute15: +OnMinute30: +OnMinute45: + // There is no weather in test servers + if (debug && !$@GM_OVERRIDE) + end; + + //debugmes "[Weather.sys] Starting to regen"; + .tpc+=getusers(1); + .tcl+=1; + + .@hti = htiterator(.wcore); + for(.@key$ = htinextkey(.@hti); hticheck(.@hti); .@key$ = htinextkey(.@hti)) { + // Local variables: .@key$ .@type .@r + .@type$=htget(.wcore, .@key$); + .@r=rand(0,10000); + + // Clear weather map masks + .@mk=getmapmask(.@key$); + + if (.@mk & MASK_RAIN) + .@mk=.@mk^MASK_RAIN; + if (.@mk & MASK_SANDSTORM) + .@mk=.@mk^MASK_SANDSTORM; + if (.@mk & MASK_SNOW) + .@mk=.@mk^MASK_SNOW; + if (.@mk & MASK_NIGHT) + .@mk=.@mk^MASK_NIGHT; + if (.@mk & MASK_EVILSANCTUM) + .@mk=.@mk^MASK_EVILSANCTUM; + + // Monster King Sieges (can be weird, siege last 10mn and mask last 15) + if ($@MK_SCENE) + .@mk=.@mk|MASK_EVILSANCTUM; + if (.@mk & (MASK_MATTACK|MASK_EVILSANCTUM)) + .@mk=.@mk^MASK_EVILSANCTUM; + + // Remove all current masks, and add rain/snow/sand + if (.@type$ == "desert") { + if (.@r < 10) + .@mk=.@mk|MASK_RAIN; + else if (.@r < 300) + .@mk=.@mk|MASK_SANDSTORM; + + } else if (.@type$ == "woodland") { + if (.@r < 300) + .@mk=.@mk|MASK_RAIN; + + if ($EVENT$ == "Christmas") + .@mk=.@mk|MASK_SNOW; + else if (season() == WINTER && .@r >= 9250) + .@mk=.@mk|MASK_SNOW; + + } else if (.@type$ == "iceland") { + if (.@r < 30) + .@mk=.@mk|MASK_RAIN; + else if (.@r < 300) + .@mk=.@mk|MASK_SNOW; + + } else if (.@type$ == "special") { + // This biom is too hot for snow. But it may rain. + if (.@r < 300) + .@mk=.@mk|MASK_RAIN; + + // Fog? (Evil Sanctum and Monster Attack) Or Sandstorm? + if (.@r % 4 == 2) + .@mk=.@mk|MASK_EVILSANCTUM; + else if (.@r % 4 == 1) + .@mk=.@mk|MASK_SANDSTORM; + else if (.@r % 4 == 3) + .@mk=.@mk|MASK_MATTACK; + + } else { + consolebug "Warning warning, blame Saulc! Weather system error on map "+.@key$; + announce("ERROR BLAME SAULC! WEATHER SYSTEM CORRUPTED.", bc_all); + } + + // Bugfix + if (!(.@mk & MASK_NONE)) + .@mk=.@mk|MASK_NONE; + + setmapmask .@key$, .@mk; + + // Is it night time? + if (is_night()) { + setmapflag(.@key$, mf_nightenabled); + addmapmask .@key$, MASK_NIGHT; + } else if (getmapmask(.@key$) & MASK_NIGHT) { + removemapflag(.@key$, mf_nightenabled); + removemapmask .@key$, MASK_NIGHT; + } + } + htidelete(.@hti); + //debugmes "[Weather.sys] Regenerated"; + + // Hardcore: Skip day/night cycles + if ($HARDCORE || $@GM_OVERRIDE) { + $@WEATHER_NIGHT=is_night(true); + end; + } + + // During night, normal monsters respawn 30% faster. (Bifs and Bosses are immune) + // + // Also, announce to players about day/night cycle changes + // The fastest you'll get is 30 minutes for night cycle. + // It's 2 messages every 3 hours. (r7.5) + // Player might be on cave, and this will help them tracking time. + .@current=is_night(true); + .@update = (.@current != $@WEATHER_NIGHT); + if (.@update) { + $@WEATHER_NIGHT=.@current; + if (.tcl > 0) { + .tpc = .tpc / .tcl; + // Monsters spawn faster based on average player count of previous + // cycle, 2% faster per player, capped at 2/3 of the lowest rate + // Default lowest is 70%, so it can go down up to 45% reduction + // For a total of (70-45 = 25%) or monsters spawning 4× faster + // when server is at "full" load (22 players or so) + .@bon = min(.tpc * 2, min($BCONFN_SPAWN, $BCONFD_SPAWN) * 2 / 3); + } + .tpc=0; + .tcl=0; + } + if (is_night() && .@update) { + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + callfunc("SC_Bonus", 180, SC_INCMHPRATE, 10, 10); + percentheal 100, 100; + message(.@players[.@i], "The night falls."); + detachrid(); + } + setbattleflag("mob_spawn_delay", $BCONFN_SPAWN - .@bon); + setbattleflag("monster_hp_rate", $BCONFN_MOBHP); + //charcommand("@reloadbattleconf"); // Careful! + donpcevent("@exprate::OnInheirtedReload"); + //donpcevent("@droprate::OnReload"); + } else if (!is_night() && .@update) { + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + callfunc("SC_Bonus", 180, SC_INCMHPRATE, 10, 10); + percentheal 100, 100; + message(.@players[.@i], "The day rises."); + detachrid(); + } + setbattleflag("mob_spawn_delay", $BCONFD_SPAWN - .@bon); + setbattleflag("monster_hp_rate", $BCONFD_MOBHP); + //charcommand("@reloadbattleconf"); // Careful! + donpcevent("@exprate::OnInheirtedReload"); + //donpcevent("@droprate::OnReload"); + } + + callfunc "FYE_Normalize"; + debugmes "[Weather.sys] Weather reloaded"; + end; + + // Function to check stuff + // WeatherSwitch ( MASK, MAP ) + function WeatherSwitch { + // Get map + getmapxy(.@key$,.@a,.@b,0); + + // Sanitize + .@m$ = htget(.wcore, .@key$, "Not found"); + + // Change Weather or abort + if (.@m$ == "Not found") + dispbottom l("Command not permitted on this map! Check npc/functions/weather.conf"); + else + addmapmask(.@key$, getarg(0)); + return; + } + +// Some commands, for GMs manually override weather +OnRain: + WeatherSwitch(MASK_RAIN); + end; + +OnSand: + WeatherSwitch(MASK_SANDSTORM); + end; + +OnSnow: + WeatherSwitch(MASK_SNOW); + end; + +OnNight: + WeatherSwitch(MASK_NIGHT); + end; + +OnEvil: + WeatherSwitch(MASK_EVILSANCTUM); + end; + +OnManual: + if (!.@atcmd_numparameters) { + dispbottom l("Syntax: @wset <map_mask>"); + end; + } + + // Never allow negative numbers, or to disable map mask 1 (never, EVER, do such insane thing) + .@rq = atoi(.@atcmd_parameters$[0]); + if (.@rq <= 1 || .@rq % 2 == 1) { + dispbottom l("Invalid map mask"); + end; + } + + // <Insert a helpful comment here> + getmapxy(.@key$,.@a,.@b,0); + .@mk=getmapmask(.@key$); + .@mk=.@mk^.@rq; + setmapmask(.@key$, .@mk); + end; + +// Clear works on any map +OnClear: + getmapxy(.@key$,.@a,.@b,0); + .@mk=getmapmask(.@key$); + if (.@mk & MASK_RAIN) + .@mk=.@mk^MASK_RAIN; + if (.@mk & MASK_SANDSTORM) + .@mk=.@mk^MASK_SANDSTORM; + if (.@mk & MASK_SNOW) + .@mk=.@mk^MASK_SNOW; + if (.@mk & MASK_NIGHT) + .@mk=.@mk^MASK_NIGHT; + setmapmask(.@key$, .@mk); + end; + +// Reset the whole map, including season, event and weather masks +OnReset: + getmapxy(.@key$,.@a,.@b,0); + setmapmask(.@key$, MASK_NONE); + end; + +} diff --git a/npc/guilds/_import.txt b/npc/guilds/_import.txt new file mode 100644 index 0000000..43098ee --- /dev/null +++ b/npc/guilds/_import.txt @@ -0,0 +1,10 @@ +// Map guilds: Guild Hall +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/guilds/_warps.txt", +"npc/guilds/alchemy.txt", +"npc/guilds/bank.txt", +"npc/guilds/exchange.txt", +"npc/guilds/forge.txt", +"npc/guilds/logs.txt", +"npc/guilds/management.txt", +"npc/guilds/storage.txt", diff --git a/npc/guilds/_warps.txt b/npc/guilds/_warps.txt new file mode 100644 index 0000000..6e8c8f6 --- /dev/null +++ b/npc/guilds/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map guilds: Guild Hall warps +guilds,35,49,0 warp #guilds_35_49 1,0,017-1,57,34 diff --git a/npc/guilds/alchemy.txt b/npc/guilds/alchemy.txt new file mode 100644 index 0000000..99da224 --- /dev/null +++ b/npc/guilds/alchemy.txt @@ -0,0 +1,30 @@ +// Moubootaur Legends Script +// Author: +// Jesusalva +// Description: +// Guild Facility - Alchemy Table + +guilds,24,35,0 script Guild Chemistry NPC_NO_SPRITE,{ + do + { + mesc l("What will you brew today?"); + mesc l("Note: Items brewed here will use a Guild Recipe instead!"); + if (AlchemySystem(CRAFT_GUILD)) + { + mesc l("Success!"), 3; + next; + } + else + { + mesc l("That didn't work!"), 1; + next; + } + mesc l("Try again?"); + } while (askyesno() == ASK_YES); + close; + +OnInit: + .distance=2; + end; +} + diff --git a/npc/guilds/bank.txt b/npc/guilds/bank.txt new file mode 100644 index 0000000..dadd527 --- /dev/null +++ b/npc/guilds/bank.txt @@ -0,0 +1,58 @@ +// Moubootaur Legends Script +// Author: +// Jesusalva +// Description: +// Guild Facility - Guild Vault + +guilds,35,35,0 script Guild Vault NPC_NO_SPRITE,{ + .@gid=getcharid(2); + .@role=getguildrole(.@gid, getcharid(3)); + do + { + mesn; + mesc l("This vault currently have @@ GP inside.", format_number($GUILD_BANK[.@gid])); + select + l("Okay, laters"), + l("Donate GP"), + rif(.@role == GPOS_TREASURER || .@role <= GPOS_VICELEADER, l("Withdraw GP")); + mes ""; + switch (@menu) + { + case 1: + close; + case 2: + input .@mx; + if (Zeny < .@mx || .@mx < 0) + { + mesc l("Invalid amount!"), 1; + } + else + { + Zeny=Zeny-.@mx; + $GUILD_BANK[.@gid]+=.@mx; + mesc l("Donation successful!"), 3; + } + break; + case 3: + input .@mx; + if ($GUILD_BANK[.@gid] < .@mx || .@mx < 0) + { + mesc l("Invalid amount!"), 1; + } + else + { + $GUILD_BANK[.@gid]-=.@mx; + Zeny=Zeny+.@mx; + mesc l("Money withdrawn!"), 3; + } + break; + } + + } while (true); + close; + +OnInit: + .distance=2; + end; +} + diff --git a/npc/guilds/exchange.txt b/npc/guilds/exchange.txt new file mode 100644 index 0000000..168dc93 --- /dev/null +++ b/npc/guilds/exchange.txt @@ -0,0 +1,155 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Trades Guild Coins for useful items +// Variable: +// #GUILDSHOP (bitmask) + +guilds,29,24,0 script Guild Coin Exchange NPC_NO_SPRITE,{ + do + { + mes l("You have %d %s", countitem(GuildCoin), getitemlink(GuildCoin)); + next; + // Calculate fruit cost + if (!(#GUILDSHOP & GEX_fruit1)) { + .@fruitcost=200; .@fruitId=GEX_fruit1; + } else if (!(#GUILDSHOP & GEX_fruit2)) { + .@fruitcost=350; .@fruitId=GEX_fruit2; + } else if (!(#GUILDSHOP & GEX_fruit3)) { + .@fruitcost=500; .@fruitId=GEX_fruit3; + } else if (!(#GUILDSHOP & GEX_fruit4)) { + .@fruitcost=750; .@fruitId=GEX_fruit4; + } else if (!(#GUILDSHOP & GEX_fruit5)) { + .@fruitcost=1000; .@fruitId=GEX_fruit5; + } else { + .@fruitcost=false; .@fruitId=GEX_fruitMAX; + } + + // Calculate mercbox cost + if (!(#GUILDSHOP & GEX_merc1)) { + .@merccost=50; .@mercId=GEX_merc1; + } else if (!(#GUILDSHOP & GEX_merc2)) { + .@merccost=100; .@mercId=GEX_merc2; + } else if (!(#GUILDSHOP & GEX_merc3)) { + .@merccost=175; .@mercId=GEX_merc3; + } else if (!(#GUILDSHOP & GEX_merc4)) { + .@merccost=250; .@mercId=GEX_merc4; + } else if (!(#GUILDSHOP & GEX_merc5)) { + .@merccost=350; .@mercId=GEX_merc5; + } else { + .@merccost=false; .@mercId=GEX_mercMAX; + } + + // Calculate blueprint cost + if (!(#GUILDSHOP & GEX_blue1)) { + .@bluecost=75; .@blueId=GEX_blue1; + } else if (!(#GUILDSHOP & GEX_blue2)) { + .@bluecost=100; .@blueId=GEX_blue2; + } else if (!(#GUILDSHOP & GEX_blue3)) { + .@bluecost=150; .@blueId=GEX_blue3; + } else if (!(#GUILDSHOP & GEX_blue4)) { + .@bluecost=200; .@blueId=GEX_blue4; + } else if (!(#GUILDSHOP & GEX_blue5)) { + .@bluecost=250; .@blueId=GEX_blue5; + } else { + .@bluecost=300; .@blueId=GEX_blueMAX; + } + + // Calculate arcmage boxset cost + if (!(#GUILDSHOP & GEX_arcmage1)) { + .@arcmagecost=30; .@arcmageId=GEX_arcmage1; + } else if (!(#GUILDSHOP & GEX_arcmage2)) { + .@arcmagecost=60; .@arcmageId=GEX_arcmage2; + } else if (!(#GUILDSHOP & GEX_arcmage3)) { + .@arcmagecost=90; .@arcmageId=GEX_arcmage3; + } else if (!(#GUILDSHOP & GEX_arcmage4)) { + .@arcmagecost=120; .@arcmageId=GEX_arcmage4; + } else if (!(#GUILDSHOP & GEX_arcmage5)) { + .@arcmagecost=150; .@arcmageId=GEX_arcmage5; + } else { + .@arcmagecost=150; .@arcmageId=GEX_arcmageMAX; + } + + menuint + l("Close"), 0, + rif(.@fruitcost, .@fruitcost+l(" Coins - Mysterious Fruit")), .@fruitId, + rif(.@merccost, .@merccost+l(" Coins - Mercenary Boxset")), .@mercId, + rif(.@bluecost, .@bluecost+l(" Coins - Ancient Blueprint")), .@blueId, + rif(.@arcmagecost, .@arcmagecost+l(" Coins - Arcmage Boxset")), .@arcmageId; + + mes ""; + switch (@menuret) { + case GEX_fruit1: + case GEX_fruit2: + case GEX_fruit3: + case GEX_fruit4: + case GEX_fruit5: + case GEX_fruitMAX: + if (countitem(GuildCoin) < .@fruitcost) + close; + delitem GuildCoin, .@fruitcost; + getitem MysteriousFruit, 1; + if (@menuret > 0) + #GUILDSHOP=#GUILDSHOP|@menuret; + break; + + case GEX_merc1: + case GEX_merc2: + case GEX_merc3: + case GEX_merc4: + case GEX_merc5: + case GEX_mercMAX: + if (countitem(GuildCoin) < .@merccost) + close; + delitem GuildCoin, .@merccost; + getitem (#GUILDSHOP & GEX_merc3 ? MercBoxE : MercBoxD), 1; + if (@menuret > 0) + #GUILDSHOP=#GUILDSHOP|@menuret; + break; + + case GEX_blue1: + case GEX_blue2: + case GEX_blue3: + case GEX_blue4: + case GEX_blue5: + case GEX_blueMAX: + if (countitem(GuildCoin) < .@bluecost) + close; + delitem GuildCoin, .@bluecost; + getitem AncientBlueprint, 1; + if (@menuret > 0) + #GUILDSHOP=#GUILDSHOP|@menuret; + break; + + case GEX_arcmage1: + case GEX_arcmage2: + case GEX_arcmage3: + case GEX_arcmage4: + case GEX_arcmage5: + case GEX_arcmageMAX: + if (countitem(GuildCoin) < .@arcmagecost) + close; + delitem GuildCoin, .@arcmagecost; + getitem ArcmageBoxset, 1; + if (@menuret > 0) + #GUILDSHOP=#GUILDSHOP|@menuret; + break; + } + + } while (@menuret != 0); + close; + +OnInit: + .@npcId = getnpcid(.name$); + setunitdata(.@npcId, UDT_HEADTOP, TopHat); + setunitdata(.@npcId, UDT_HEADMIDDLE, GoldenWarlordPlate); + setunitdata(.@npcId, UDT_WEAPON, JeansChaps); + setunitdata(.@npcId, UDT_HEADBOTTOM, AssassinBoots); + setunitdata(.@npcId, UDT_HAIRSTYLE, 25); + setunitdata(.@npcId, UDT_HAIRCOLOR, 4); + + .sex = G_MALE; + .distance = 5; + end; +} diff --git a/npc/guilds/forge.txt b/npc/guilds/forge.txt new file mode 100644 index 0000000..a558c4c --- /dev/null +++ b/npc/guilds/forge.txt @@ -0,0 +1,33 @@ +// Moubootaur Legends Script +// Author: +// Jesusalva +// Description: +// Guild Facility - Blacksmith Area + +guilds,24,27,0 script Guild Blacksmith NPC_NO_SPRITE,{ + // Script begins here + do + { + mesc l("What will you forge today?"); + //mesc l("You might need to have a @@ equipped for some recipes!", getitemlink(Knife)); + mesc l("Note: Items forged here will be guild-bound, but will use player recipes and bonuses!"); + mesc b(l("\"WHAT IS CRAFTED IN GUILD BELONGS TO GUILD.\"")), 1; + if (SmithSystem(CRAFT_GUILD)) + { + mesc l("Success!"), 3; + next; + } + else + { + mesc l("That didn't work!"), 1; + next; + } + mesc l("Try again?"); + } while (askyesno() == ASK_YES); + close; + +OnInit: + .distance=2; + end; +} + diff --git a/npc/guilds/logs.txt b/npc/guilds/logs.txt new file mode 100644 index 0000000..4c225f0 --- /dev/null +++ b/npc/guilds/logs.txt @@ -0,0 +1,422 @@ +// Moubootaur Legends Script +// Author: +// Jesusalva +// Description: +// Guild Facility - Contains recipe list + +guilds,47,39,0 script Guild Logs NPC_NO_SPRITE,{ + function hudRecipe; + function showRecipe; + function calcRecipe; + function clearRecipe; + function GDSkillLearn; + .@gid=getcharid(2); + .@s=0; // How many was displayed, to use next(); if needed + + mes ".:: " + l("Alchemy Recipes") + " ::."; + // Healing + .@s+=showRecipe(CraftPiberriesInfusion, PiberriesInfusion, + 5, Piberries, 1, Curshroom); + + if (.@s > 1) + next; + + // General Boosts + .@s+=showRecipe(CraftHastePotion, HastePotion, + 15, Plushroom); + .@s+=showRecipe(CraftStrengthPotion, StrengthPotion, + 15, Chagashroom); + .@s+=showRecipe(CraftResetPotion, StatusResetPotion, + 90, ManaPiouFeathers, 10, Curshroom); + .@s+=showRecipe(CraftSpeedPotion, MoveSpeedPotion, + 1, GemPowder, 5, FluoPowder); + .@s+=showRecipe(CraftPrecisionPotion, PrecisionPotion, + 3, Piberries, 1, MountainSnakeEgg); + .@s+=showRecipe(CraftDodgePotion, DodgePotion, + 3, Piberries, 1, SnakeEgg); + + if (.@s > 5) + next; + + // Stats Boosts + .@s+=showRecipe(CraftLukPotionA, LukPotionA, + 1, EmeraldPowder, 1, HerbalTea); + .@s+=showRecipe(CraftLukPotionB, LukPotionB, + 1, Emerald, 2, HerbalTea); + .@s+=showRecipe(CraftLukPotionC, LukPotionC, + 1, PolishedEmerald, 3, HerbalTea); + + .@s+=showRecipe(CraftDexPotionA, DexPotionA, + 1, AmethystPowder, 1, HerbalTea); + .@s+=showRecipe(CraftDexPotionB, DexPotionB, + 1, Amethyst, 2, HerbalTea); + .@s+=showRecipe(CraftDexPotionC, DexPotionC, + 1, PolishedAmethyst, 3, HerbalTea); + + .@s+=showRecipe(CraftIntPotionA, IntPotionA, + 1, SapphirePowder, 1, HerbalTea); + .@s+=showRecipe(CraftIntPotionB, IntPotionB, + 1, Sapphire, 2, HerbalTea); + .@s+=showRecipe(CraftIntPotionC, IntPotionC, + 1, PolishedSapphire, 3, HerbalTea); + + .@s+=showRecipe(CraftVitPotionA, VitPotionA, + 1, DiamondPowder, 1, HerbalTea); + .@s+=showRecipe(CraftVitPotionB, VitPotionB, + 1, Diamond, 2, HerbalTea); + .@s+=showRecipe(CraftVitPotionC, VitPotionC, + 1, PolishedDiamond, 3, HerbalTea); + + .@s+=showRecipe(CraftAgiPotionA, AgiPotionA, + 1, TopazPowder, 1, HerbalTea); + .@s+=showRecipe(CraftAgiPotionB, AgiPotionB, + 1, Topaz, 2, HerbalTea); + .@s+=showRecipe(CraftAgiPotionC, AgiPotionC, + 1, PolishedTopaz, 3, HerbalTea); + + if (.@s > 14) + next; + + // Limit Boosts + .@s+=showRecipe(CraftSacredManaPotion, SacredManaPotion, + 1, GoldenApple, 10, CelestiaTea); + .@s+=showRecipe(CraftSacredLifePotion, SacredLifePotion, + 1, GoldenApple, 1, ElixirOfLife); + .@s+=showRecipe(CraftSacredImmortalityPotion, SacredImmortalityPotion, + 1, SacredLifePotion, 1, SacredManaPotion); + + if (.@s == 0) + mesc l("Your guild doesn't knows any recipes!"), 1; + + .@role=getguildrole(.@gid, getcharid(3)); + do + { + select + l("Do nothing"), + rif(strcharinfo(0) == getguildmaster(.@gid), l("Raise max members")), + rif(.@role <= GPOS_MEMBER, l("Learn individual guild skills")), + rif(.@role <= GPOS_VICELEADER, l("Learn Alchemy Recipes")); + mes ""; + + switch (@menu) { + case 2: + .@cur_lv=getguildlvl(.@gid); + .@min_lv=(getskilllv(GD_EXTENSION)+1)*4; + .@price=.@min_lv*274; + if (.@cur_lv < .@min_lv) + { + mesc l("Guild Level is not enough: @@/@@", .@cur_lv, .@min_lv); + next; + break; + } + mesc l("Raising this skill will allow to recruit 4 more members."); + mesc l("The cost for Guild Vault is @@ GP.", .@price); + next; + select + rif($GUILD_BANK[.@gid] >= .@price, l("Upgrade it")), + l("Don't upgrade it"); + mes ""; + if (@menu == 1 && $GUILD_BANK[.@gid] >= .@price) + { + $GUILD_BANK[.@gid]-=.@price; + // guildskill()? + skill GD_EXTENSION, getskilllv(GD_EXTENSION)+1, 0; + break; + } + break; + case 3: + mes ".:: " + l("Guild Magic") + " ::."; + mesc l("IMPORTANT: All guild magic needs %s to be cast!", getitemlink(GuildCoin)), 3; + do + { + select + l("Return"), + rif(strcharinfo(0) == getguildmaster(.@gid), l("Increase all stats")), + l("Guild Area Regeneration"), + l("Guild Area DEF UP"), + l("Guild's Battle Plan"), + l("Guild Area ATK UP"), + l("Guild Area CRIT UP"), + l("Guild Area Autorevive"), + l("Weapon Maximum Damage"), + l("Friendly SP Regeneration"); + + mes ""; + switch (@menu) { + case 2: + GDSkillLearn(TMW2_GD_INCALL, 2580, + l("Guild's Power"), + l("Increase all stats from every guild member in area."), + 8, 12, 16, 20, 24, 30, 36, 42, 50); + break; + case 3: + GDSkillLearn(TMW2_GD_REGEN, 1740, + l("Angel Light"), + l("Causes a healing effect in area to guild members."), + 4, 6, 9, 12, 15, 20, 25, 30, 35); + break; + case 4: + GDSkillLearn(TMW2_GD_DEFUP, 640, + l("Blessing of Defense"), + l("Increase defense to all friends in radius."), + 3, 5, 7, 9, 11, 14, 17, 20, 25); + break; + case 5: + GDSkillLearn(TMW2_GD_BATTLEPLAN, 1015, + l("Battle Plans"), + l("Increase STR/INT/DEX in range for all guild allies."), + 5, 8, 11, 15, 20, 24, 30, 37, 42); + break; + case 6: + GDSkillLearn(TMW2_GD_ATKUP, 1500, + l("Damage Improvement"), + l("Increase damage dealt by weapon for all guildies."), + 2, 4, 6, 8, 10, 12, 15, 18, 21); + break; + case 7: + GDSkillLearn(TMW2_GD_CRITUP, 1500, + l("Critical Fortune"), + l("Increase critical chance of all guild allies."), + 6, 9, 13, 19, 22, 27, 33, 39, 45); + break; + case 8: + GDSkillLearn(TMW2_GD_AUTOREVIVE, 3000, + l("Blessing of Immortality"), + l("If a guild mate dies, they will revive. Don't work on self."), + 7, 11, 17, 23, 26, 29, 31, 34, 38); + break; + case 9: + GDSkillLearn(TMW2_GDP_MAXPOWER, 3000, + l("Maximize Damage"), + l("PARTY AND GUILD friends: Weapon always deal max damage."), + 1, 5, 10, 15, 20, 25, 30, 35, 40); + break; + case 10: + GDSkillLearn(TMW2_GDP_SPREGEN, 3000, + l("Improved MP Regen"), + l("PARTY AND GUILD friends: Temporaly regen MP faster."), + 5, 10, 15, 22, 28, 32, 37, 41, 44); + break; + default: + break; + } + } while (@menu != 1); + @menu=99; + break; + case 4: + mes ".:: " + l("Alchemy Recipes") + " ::."; + do { + clearRecipe(); + + // Healing Recipes + if (!showRecipe(CraftPiberriesInfusion, false)) + calcRecipe(CraftPiberriesInfusion, 3, 10000, l("Piberries Infusion")); + + // General Boosts + if (!showRecipe(CraftHastePotion, false)) + calcRecipe(CraftHastePotion, 2, 5000, l("Haste Potion")); + if (!showRecipe(CraftStrengthPotion, false)) + calcRecipe(CraftStrengthPotion, 2, 5000, l("Strength Potion")); + + if (!showRecipe(CraftResetPotion, false)) + calcRecipe(CraftResetPotion, 4, 50000, l("Status Reset Potion")); + if (!showRecipe(CraftSpeedPotion, false)) + calcRecipe(CraftSpeedPotion, 4, 50000, l("Movement Speed Potion")); + + if (!showRecipe(CraftPrecisionPotion, false)) + calcRecipe(CraftPrecisionPotion, 5, 20000, l("Precision Potion")); + if (!showRecipe(CraftDodgePotion, false)) + calcRecipe(CraftDodgePotion, 5, 20000, l("Dodge Potion")); + + // Stats Boosts + if (!showRecipe(CraftLukPotionA, false)) + calcRecipe(CraftLukPotionA, 4, 15000, l("Luck Potion")); + if (!showRecipe(CraftLukPotionB, false)) + calcRecipe(CraftLukPotionB, 6, 25000, l("Luck+ Potion")); + if (!showRecipe(CraftLukPotionC, false)) + calcRecipe(CraftLukPotionC, 8, 35000, l("Luck++ Potion")); + + if (!showRecipe(CraftDexPotionA, false)) + calcRecipe(CraftDexPotionA, 4, 15000, l("Dex Potion")); + if (!showRecipe(CraftDexPotionB, false)) + calcRecipe(CraftDexPotionB, 6, 25000, l("Dex+ Potion")); + if (!showRecipe(CraftDexPotionC, false)) + calcRecipe(CraftDexPotionC, 8, 35000, l("Dex++ Potion")); + + if (!showRecipe(CraftIntPotionA, false)) + calcRecipe(CraftIntPotionA, 4, 15000, l("Int Potion")); + if (!showRecipe(CraftIntPotionB, false)) + calcRecipe(CraftIntPotionB, 6, 25000, l("Int+ Potion")); + if (!showRecipe(CraftIntPotionC, false)) + calcRecipe(CraftIntPotionC, 8, 35000, l("Int++ Potion")); + + if (!showRecipe(CraftVitPotionA, false)) + calcRecipe(CraftVitPotionA, 4, 15000, l("Vit Potion")); + if (!showRecipe(CraftVitPotionB, false)) + calcRecipe(CraftVitPotionB, 6, 25000, l("Vit+ Potion")); + if (!showRecipe(CraftVitPotionC, false)) + calcRecipe(CraftVitPotionC, 8, 35000, l("Vit++ Potion")); + + if (!showRecipe(CraftAgiPotionA, false)) + calcRecipe(CraftAgiPotionA, 4, 15000, l("Agi Potion")); + if (!showRecipe(CraftAgiPotionB, false)) + calcRecipe(CraftAgiPotionB, 6, 25000, l("Agi+ Potion")); + if (!showRecipe(CraftAgiPotionC, false)) + calcRecipe(CraftAgiPotionC, 8, 35000, l("Agi++ Potion")); + + // Limit Boosts + if (!showRecipe(CraftSacredManaPotion, false)) + calcRecipe(CraftSacredManaPotion, 7, 100000, l("Sacred Mana Potion")); + if (!showRecipe(CraftSacredLifePotion, false)) + calcRecipe(CraftSacredLifePotion, 7, 100000, l("Sacred Life Potion")); + if (!showRecipe(CraftSacredImmortalityPotion, false)) + calcRecipe(CraftSacredImmortalityPotion, 10, 500000, l("Sacred Immortality Potion")); + + } while (!hudRecipe()); + break; + } + + } while (@menu != 1); + close; + +// showRecipe (Craft, Bonus, Req1No, Req1Id, Req2No, Req2Id) +// Bonus must NOT be zero to display text +function showRecipe { + /* + debugmes "Exist: %d", getd("$RECIPES_ALCHEMY_"+getcharid(2)+"["+getarg(0)+"]"); + debugmes "Seeking for slot %d on guild %d", getarg(0), getcharid(2); + */ + if (getd("$RECIPES_ALCHEMY_"+getcharid(2)+"["+getarg(0)+"]")) { + //debugmes "Hooray! It exists! We have %d defined", getarg(1); + if (getarg(1)) { + mesn l("Craft @@", getitemlink(getarg(1))); + if (getarg(2,0)) + mesc l("* @@ @@", getarg(2), getitemlink(getarg(3))); + if (getarg(4,0)) + mesc l("* @@ @@", getarg(4), getitemlink(getarg(5))); + mes ""; + } + //debugmes "You got it: %d (global: %d)", getarg(0), $RECIPES_ALCHEMY[getcharid(2)]; + return 1; + } + //debugmes "Nope, nothing here"; + return 0; +} + +// calcRecipe (CraftID, GLV, GGP, TXT) +function calcRecipe { + array_push(@tmp_alcrep_id, getarg(0)); + array_push(@tmp_alcrep_glv, getarg(1)); + array_push(@tmp_alcrep_ggp, getarg(2)); + array_push(@tmp_alcrep_txt$, getarg(3) + " - GLV "+getarg(1)+", "+format_number(getarg(2))+" GP"); + return; +} + +// clearRecipe () +function clearRecipe { + deletearray(@tmp_alcrep_id); + deletearray(@tmp_alcrep_glv); + deletearray(@tmp_alcrep_ggp); + deletearray(@tmp_alcrep_txt$); + @tmp_alcrep_id[0]=-1; + @tmp_alcrep_glv[0]=-1; + @tmp_alcrep_ggp[0]=-1; + @tmp_alcrep_txt$[0]=l("Learn Nothing"); + return; +} + +// hudRecipe () +function hudRecipe { + .@gid=getcharid(2); + // Select + select (implode(@tmp_alcrep_txt$, ":")); + @menu=@menu-1; + + /* DEBUG prints + debugmes "You choose: %d", @menu; + debugmes getd("$RECIPES_ALCHEMY_"+.@gid); + copyarray(.@v, getd("$RECIPES_ALCHEMY_"+.@gid), getarraysize(getd("$RECIPES_ALCHEMY_"+.@gid)) ); + debugmes "Width: %d (out of %d)", getarraysize(.@v), getarraysize(getd("$RECIPES_ALCHEMY_"+.@gid)); + debugmes "Slot 44: %d", .@v[44]; + debugmes "Slot 43: %d", .@v[43]; + debugmes "Slot 42: %d", .@v[42]; + debugmes "GLVL Options: %d-%d-%d-%d", @tmp_alcrep_glv[0], @tmp_alcrep_glv[1], @tmp_alcrep_glv[2], @tmp_alcrep_glv[3]; + */ + + if (@tmp_alcrep_glv[@menu] <= 0) + return 1; + + if (getguildlvl(.@gid) < @tmp_alcrep_glv[@menu]) { + mesc l("Insufficient Guild Level! (@@/@@)", getguildlvl(.@gid), @tmp_alcrep_glv[@menu]), 1; + next; + return 0; + } + if ($GUILD_BANK[.@gid] < @tmp_alcrep_ggp[@menu]) { + mesc l("Insufficient Guild Money! (Guild has: @@ GP)", format_number($GUILD_BANK[.@gid])), 1; + next; + return 0; + } + // All fine, proceed + $GUILD_BANK[.@gid] -= @tmp_alcrep_ggp[@menu]; + setd("$RECIPES_ALCHEMY_"+getcharid(2)+"["+@tmp_alcrep_id[@menu]+"]", true); + mesc l("Skill learnt!"), 2; + next; + return 0; +} + +// GDSkillLearn(SKID, cost-per-guild-level, title, desc, lv1, {lv2, lv3...}) +function GDSkillLearn { + .@id=getarg(0); + .@p=getarg(1); + .@t$=getarg(2); + .@d$=getarg(3); + + .@gid=getcharid(2); + .@gid_lv=getguildlvl(.@gid); + .@cur_lv=getskilllv(.@id); + .@min_lv=getarg(4+.@cur_lv, -1); + + .@price=.@min_lv*.@p; + + // Max level reached + if (.@min_lv < 0) + { + mesc l("This skill cannot be upgraded further."); + next; + return 0; + } + + // Guild level insuffice + if (.@gid_lv < .@min_lv) + { + mesc l("Guild Level is not enough: @@/@@", .@gid_lv, .@min_lv); + next; + return 0; + } + + mes ".:: "+.@t$+" ::."; + mes .@d$; + mesc l("The cost for Guild Vault is @@ GP.", format_number(.@price)); + mesc l("Current Guild Balance: %s GP", format_number($GUILD_BANK[.@gid])); + next; + select + rif($GUILD_BANK[.@gid] >= .@price, l("Upgrade it")), + l("Don't upgrade it"); + mes ""; + if (@menu == 1 && $GUILD_BANK[.@gid] >= .@price) + { + $GUILD_BANK[.@gid]-=.@price; + // guildskill()? + skill .@id, .@cur_lv+1, 0; + mesc l("Skill improved"), 2; + @menu=99; + return 1; + } + return 0; +} + +OnInit: + .distance=2; + end; +} + diff --git a/npc/guilds/management.txt b/npc/guilds/management.txt new file mode 100644 index 0000000..5e0fd0a --- /dev/null +++ b/npc/guilds/management.txt @@ -0,0 +1,115 @@ +// Moubootaur Legends Script +// Author: +// Jesusalva +// Description: +// Guild Facility - Guild Settings (for guildmaster) + +guilds,35,30,0 script Guild Management NPC_NO_SPRITE,{ + function gmaster; + .@gid=getcharid(2); + .@admin=(getguildrole(.@gid, getcharid(3)) == GPOS_GUILDMASTER); + .@pos=getguildrole(.@gid, getcharid(3)); + + if (is_admin() && strcharinfo(0) == "Monster King") { + mesc ".:: Jesusalva's Interface ::.", 1; + mesc "Write Guild ID to modify - DO NOT INPUT IF YOU DON'T KNOW EXACTLY WHAT YOU ARE DOING", 1; + mesc "THAT IS IRREVERSIBLE, DON'T BE A DUMMY", 1; + next; + mesc("Correct gid: " + .@gid); + input .@gid; + if (.@gid <= 0) + close; + .@admin=true; + logmes(strcharinfo(0)+" logged in as super user in guild: "+getguildname(.@gid), LOGMES_ATCOMMAND); + } + + mesc (".:: "+getguildname(.@gid)+" ::."), 1; + mesc l("Guild Master: @@", getguildmaster(.@gid)), 3; + if (getguildnxp(.@gid) > 0) + mesc l("Guild Lv @@, @@/@@ EXP to level up", getguildlvl(.@gid), fnum(getguildexp(.@gid)), fnum(getguildnxp(.@gid))); + else + mesc l("Guild Lv @@, @@/@@ EXP to level up", getguildlvl(.@gid), fnum(getguildexp(.@gid)), "???"); + + // mesc l("The average player level is @@", getguildavg(.@gid)); + //mesc l("Your position on the guild: @@", getguildrole(.@gid, getcharid(3), true)); + mesc l("You are the guild's \"%s\", and you contribute with %02d%% EXP.", + getguildpostitle(.@gid, .@pos), + getguildpostax(.@gid, .@pos)); + //getguildpostitle(.@gid, .@pos) + + // I forgot a function to get player title and allow any guildmaster to change this + if (.@admin) + { + next; + do + { + gmaster(); + next; + mesc l("Do you want to do something else?"); + } while (askyesno() == ASK_YES); + } + close; + + function gmaster { + .@gid=getcharid(2); + mesn; + mesc l("Please select the Guild Position you want to change."); + menuint + l("Abort"), -1, + l("Guild Master"), GPOS_GUILDMASTER, + l("Vice Leader"), GPOS_VICELEADER, + l("Recruiter"), GPOS_RECRUITER, + l("Treasurer"), GPOS_TREASURER, + l("Normal Member"), GPOS_MEMBER, + l("Newbie"), GPOS_NEWBIE; + mes ""; + .@idx=0+@menuret; + if (.@idx < 0) + return; + mesc l("The guild's \"%s\" currently contribute with %02d%% EXP.", getguildpostitle(.@gid, .@pos), getguildpostax(.@gid, .@idx)); + mesc l("Please write how much in percent is EXP Tax for member (range: 0~50)"), 1; + input .@exptax; + if (.@exptax < 0 || .@exptax > 50) + return; + mesc l("Applying..."); + switch (.@idx) { + case GPOS_GUILDMASTER: + .@name$="GuildMaster"; + .@perm=GPERM_ALL; + break; + case GPOS_VICELEADER: + .@name$="ViceLeader"; + .@perm=GPERM_ALL; + break; + case GPOS_RECRUITER: + .@name$="Recruiter"; + .@perm=GPERM_INVITE; + break; + case GPOS_TREASURER: + .@name$="Treasurer"; + .@perm=0; + break; + case GPOS_MEMBER: + .@name$="Member"; + .@perm=0; + break; + case GPOS_NEWBIE: + .@name$="Newbie"; + .@perm=0; + break; + default: + .@name$="Position "+(.@idx+1); + .@perm=0; + break; + } + //debugmes "setguildrole(%d,%d,%d,%d,\"%s\")", .@gid, .@idx, .@perm, .@exptax, .@name$; + setguildrole(.@gid, .@idx, .@perm, .@exptax, .@name$); + mesc l("Done!"), 3; + return; + } + +OnInit: + .distance=1; + end; +} + diff --git a/npc/guilds/storage.txt b/npc/guilds/storage.txt new file mode 100644 index 0000000..2f8db6d --- /dev/null +++ b/npc/guilds/storage.txt @@ -0,0 +1,33 @@ +// Moubootaur Legends Script +// Author: +// Jesusalva +// Description: +// Guild Facility - Guild Storage + +guilds,33,28,0 script Guild Storage NPC_NO_SPRITE,{ + .@gid=getcharid(2); + if (.@gid < 1) + end; + if (getguildlvl(.@gid) < 2) + { + npctalkonce l("Your guild need to have at least level 2 to use storage feature."); + } + else + { + if (getguildrole(.@gid, getcharid(3)) > GPOS_MEMBER) + { + dispbottom l("Your position must be the one of a regular member or above."); + end; + } + if (guildopenstorage()) + { + npctalkonce l("Someone else is using the guild storage right now. Please wait."); + } + } + end; + +OnInit: + .distance=2; + end; +} + diff --git a/npc/items/alcohol.txt b/npc/items/alcohol.txt new file mode 100644 index 0000000..374a4be --- /dev/null +++ b/npc/items/alcohol.txt @@ -0,0 +1,124 @@ +// TMW-2 Script. +// Author: +// Crush +// Jesusalva +// Description: +// Alcohol effects +// TODO: Retroactive, weakens every hour... +// +// Variables: +// @taste Alcohol taste (0~100) - influences exp up +// @Alcohol Alcoholic rating (0~100) - influences Attack Speed Malus, Min. Vit and duration +// ALC_DELAYTIME For how long you are drunk (the delay) - gettimetick(2) +// ALC_THRESHOLD How drunk you are (the bonus) +// +// When drunk, attack speed is lowered but exp gain is increased. +// Attack Speed Reductor: SC_ATTHASTE_INFINITY (reset upon death), SC_ATTHASTE_POTION2 (not reset upon death) +// Max HP Reductor: SC_INCMHPRATE +// EXP Increaser: SC_CASH_PLUSEXP (not reset upon death), SC_OVERLAPEXPUP (reset upon death) + +function script ALCReset { + if (ALC_DELAYTIME < gettimetick(2)) + ALC_THRESHOLD=0; + return; +} + +- script alcohol_sc -1,{ + + // Stack remaning bonuses if the last one hasn't finished + // remaining_bonuses(sc, type) + // type 0: delay + // type 1: value + function remaining_bonus + { + if (getstatus(getarg(0))) + { + if (getarg(1)) + return getstatus(getarg(0), 1); + else + return getstatus(getarg(0), 5); // Shouldn't it be 5? + } + return 0; + } + +OnUse: + if (@Alcohol <= 0 || @taste <= 0) { + Exception("Invalid alcoholic item, deleting without any effect."); + end; + } + // Just to be sure + ALCReset(); + + // Do you have enough vitality to hold your beer? + // Skip this check on the first drink + if (ALC_THRESHOLD) { + .@vit=readparam2(bVit); + if (@Alcohol+ALC_THRESHOLD > .@vit) { + if (GSET_ALCOHOL_NOOVERDRINK) dispbottom l("You vomit, you are too drunk for this to have effect anymore."); + else dispbottom l("You vomit, you are too drunk and drinking is harmful."); + dispbottom l("Raise vitality to be able to drink even more."); + sc_start SC_CONFUSION, 5000, 0, 10000, SCFLAG_NOAVOID; // Warning, forces user to use @resync! + if (GSET_ALCOHOL_NOOVERDRINK) end; + percentheal -@Alcohol, -@Alcohol; + } + } + .@deltatime=2*60*1000; // How long (in ms) each Alcohol point works? (max. 100 points) + // Default value is 2 minutes per alcohol point - you'll be somber after at most four hours. + // Beer (7): 14 minutes + // Wine (16): 32 minutes + // Sake (25): 50 minutes + // Rum (40) : 80 minutes (1h20) + // Ale (70) : 140 minutes (2h20) + + // Taste is affected by users near you. + // Each user raises exp bonus in 1.5%, capped to the beverage taste + // If you are with many people, drink a better beverage! ;-) + getmapxy(.@m$, .@x, .@y, 0); + .@bonus=getareausers(.@m$, .@x-12, .@y-12, .@x+12, .@y+12)-1; + .@bonus=.@bonus*15/10; + @taste+=min(@taste, .@bonus); + + // Alcohol EXP Bonus - ponderate average, so having more VIT doesn't means + // more experience - only more time (be careful when mixing alcohol!) + .@v=remaining_bonus(SC_OVERLAPEXPUP, true); + .@t=remaining_bonus(SC_OVERLAPEXPUP, false)/1000; + + if (.@t) .@val1 = ponderate_avg(@taste, @Alcohol, .@v, .@t); + else .@val1 = @taste; + + // Put the delay in ms. + .@delay = remaining_bonus(SC_OVERLAPEXPUP, false); + .@delay = .@t + @Alcohol*.@deltatime; + + // Reset EXP Bonus based on the new cumulative delay and average exp bonus + sc_end SC_OVERLAPEXPUP; + sc_start SC_OVERLAPEXPUP, .@delay, .@val1; + + // Recalculate Alcohol Threshold and time + ALC_THRESHOLD+=@Alcohol; + if (ALC_DELAYTIME < gettimetick(2)) { + ALC_DELAYTIME=gettimetick(2); + ALC_THRESHOLD=@Alcohol; + } + ALC_DELAYTIME+=@Alcohol*.@deltatime; + + // Debug comment if you need to check stuff + //debugmes "%d %d | %d %d | f t ", remaining_bonus(SC_OVERLAPEXPUP, false), remaining_bonus(SC_OVERLAPEXPUP, true), remaining_bonus(SC_ATTHASTE_INFINITY, false), remaining_bonus(SC_ATTHASTE_INFINITY, true); + + // For debuff I'll use inc_sc_bonus utilities (exp gain = atk speed loss) + .@delay=@Alcohol*(.@deltatime/1000); + .@min=-(remaining_bonus(SC_OVERLAPEXPUP, true)*2); + .@max=-(remaining_bonus(SC_OVERLAPEXPUP, true)*2); + // Sanitization, and nerf the debuff + .@min=(.@min/2)+1; + .@max=(.@max/2)+2; + // DEX and VIT may affect a tiny bit (there's caps) + .@pam=readparam2(bVit)+readparam2(bDex); + .@min=min(0, .@min*limit(50, 500-.@pam, 500)/500); + .@max=min(0, .@min*limit(100, 500-.@pam, 500)/500); + SC_Bonus(.@delay, SC_ATTHASTE_INFINITY, .@min, .@max); + if (debug || $@GM_OVERRIDE) + debugmes "Alcohol penalty: %d ~ %d for %d s", .@min, .@max, .@delay; + close; +} + diff --git a/npc/items/arcmage.txt b/npc/items/arcmage.txt new file mode 100644 index 0000000..038be82 --- /dev/null +++ b/npc/items/arcmage.txt @@ -0,0 +1,51 @@ +// TMW-2 script. +// Author: +// Jesusalva +// Description: +// Card boxsets, shout out for arcmage.org + +// Create a random card, with rares +function script MakeRandomArcmageCard { + array_push(.@arcmagecards, NatureCard); + array_push(.@arcmagecards, NinjaCard); + array_push(.@arcmagecards, MageCard); + array_push(.@arcmagecards, DruidCard); + array_push(.@arcmagecards, ClericCard); + array_push(.@arcmagecards, KnightCard); + array_push(.@arcmagecards, HeroCard); + array_push(.@arcmagecards, NecromancerCard); + + .@r=rand(0,10000); + // 5% chances of a rare card + // Actually only 1/3 of this rate is effective + // Therefore real chances are of about 1.67% + if (.@r < 500+(JobLevel*2)) { + array_push(.@arcmagecards, SpeedCard); + array_push(.@arcmagecards, ReflectCard); + array_push(.@arcmagecards, PowerCard); + array_push(.@arcmagecards, WallCard); + } + // You may get the S Card if you are lucky. + // However, less than 1/2 of this rate is + // effective, and it is not so flexible. + // 0.05% at start and +0.1% per rebirth + if (.@r < 5+(REBIRTH*10)) { + array_push(.@arcmagecards, NatureCardS); + array_push(.@arcmagecards, NinjaCardS); + array_push(.@arcmagecards, MageCardS); + array_push(.@arcmagecards, DruidCardS); + array_push(.@arcmagecards, ClericCardS); + array_push(.@arcmagecards, KnightCardS); + array_push(.@arcmagecards, HeroCardS); + array_push(.@arcmagecards, NecromancerCardS); + // These cards does not have S version for drop + array_push(.@arcmagecards, SpeedCard); + array_push(.@arcmagecards, ReflectCard); + array_push(.@arcmagecards, PowerCard); + array_push(.@arcmagecards, WallCard); + } + + getitem any_of(.@arcmagecards), 1; + return; +} + diff --git a/npc/items/books.txt b/npc/items/books.txt new file mode 100644 index 0000000..1c50486 --- /dev/null +++ b/npc/items/books.txt @@ -0,0 +1,737 @@ +// TMW-2 script. +// Author: +// Jesusalva +// gumi +// Tirifto +// Description: +// Books used by TMW-2. Some are from evol. + +function script FishingBook { + narrator S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("To get started with fishing, you'll need two things: a fishing rod and a bait."), + l("You just need one fishing rod, although you should take more than one single bait."); + + @menu = 0; // reset for the rif + + do + { + narrator S_NO_NPC_NAME, + l("Please select a chapter:"); + + mes ""; + + select + rif2(1, true, l("Ch 1 — Fishing apparatus")), + rif2(2, true, l("Ch 2 — Baits")), + rif2(3, true, l("Ch 3 — Location")), + rif2(4, true, l("Ch 4 — Casting")), + rif2(5, true, l("Ch 5 — Reeling")), + l("Close"); + mes ""; + + switch(@menu) + { + case 1: + narrator S_LAST_NEXT, + l("You'll want your fishing rod to be flexible but solid."), + l("Comfortable grip is important especially for newcomers, since they'll be holding it for quite a while."); + break; + case 2: + narrator S_LAST_NEXT, + l("You can use many diverse items to lure fishes."), + l("Most common and widely popular in the fish realm are @@ and pieces of @@.", + getitemlink(SmallTentacles), getitemlink(Bread)), + l("Some types of fish also enjoy @@ quite a bit.", + getitemlink(Aquada)), + l("Some people, however, prefer to fish with more unorthodox baits, such as @@ or @@.", + getitemlink(RoastedMaggot), getitemlink(CaveSnakeTongue)), + l("Other food can be used as a bait, too."); + break; + case 3: + narrator S_LAST_NEXT, + l("Find yourself a nice dry spot on a coast where you can easily reach into deep water."), + l("Fishing next to shallow water is not going to work well, because fishes seldom go there."), + l("You can easily identify fishing spots, small bubbles and fishes are visible from the surface."), + l("Don't forget to come as close as possible to these spots!"); + break; + case 4: + narrator S_LAST_NEXT, + l("Toss the hook into deep water by clicking on where you want to cast it."), + l("Make sure to put on a bait after you click, though!"), + l("After that, stay still and be patient, but also alert!"); + break; + case 5: + narrator S_LAST_NEXT, + l("To successfully catch a fish, you need to pull up your hook by clicking it, right after it submerges."), + l("Should you be too quick or wait too long, you will most likely fail."); + break; + } + } while (@menu != 6); + return; +} + +- script #Book-Fishing1 NPC_HIDDEN,{ + + function read_book { + setnpcdialogtitle l(.book_name$); + FishingBook(); + closeclientdialog; + end; + } + +OnShelfUse: + if (openbookshelf()) + read_book; + bye; + +OnUse: + if (openbook()) + read_book; + bye; + +OnInit: + .book_name$ = getitemname(FishingGuideVolI); + .sex = G_OTHER; + .distance = 1; + end; +} + + + +function script PetcaringBook { + select + l("General Information"), + l("Pet Summary"); + mes ""; + if (@menu == 1) { + narrator 1, + l("So you have now a pet, who is loyal to you. It'll follow you everywhere, but there are two things you must know."), + l("Do not let intimacy and hunger get to zero. If any of those get to zero, it'll leave you forever."), + l("Pets must keep a strict diet. Pious eats Piberries, Bhoppers eat Aquadas, and Maggots eats Bug Legs."), + l("White Cats drink Milk, Forest Mushroom eats Moss, Black Cats eats marshmallow. Keep in mind whatever they eat."), + l("However, you should only give food when it's hungry, otherwise it'll believe you're a bad owner and intimacy will decrease."), + l("Dying will also decrease the pet intimacy, and there are bonuses when your intimacy is high!"), + l("To perform most actions, like feeding and renaming, just right-click it. You can even put it back on the egg if its following gets too annoying. When in the egg, they will not feel hunger."), + l("Give your pet a nice name, and keep it healthy, and you'll be a successful pet owner!"), + l("Some pets will also collect loot for you, right click on it so it drop whatever it is holding for you."), + l("...And if you're still trying to check your pet stats, just hover it with your mouse. Thanks."), + l("-- Animals Protection Agency of Hurnscald"); + } else { + mes l("%s", getitemlink(PiouEgg)); + mesc b(l("Acquisition: ")) + l("Login Bonus"); + mesc b(l("Food: ")) + getitemlink(Piberries); + mesc b(l("Bonus: ")) + l("Loot 10, Luck +2, Luck Dance"); + + dnext; + mes ""; + mes l("%s", getitemlink(Ratte)); + mesc b(l("Acquisition: ")) + l("Unobtanium"); + mesc b(l("Food: ")) + getitemlink(Cheese); + mesc b(l("Bonus: ")) + l("Loot 3, Dex +5"); + + dnext; + mes ""; + mes l("%s", getitemlink(DuckEgg)); + mesc b(l("Acquisition: ")) + l("Grand Hunter Quest"); + mesc b(l("Food: ")) + getitemlink(CherryCake); + mesc b(l("Bonus: ")) + l("Loot 3, Steal +15%, Passive HP Regen (1)"); + + dnext; + mes ""; + mes l("%s", getitemlink(FluffyEgg)); + mesc b(l("Acquisition: ")) + l("Grand Hunter Quest"); + mesc b(l("Food: ")) + getitemlink(LettuceLeaf); + mesc b(l("Bonus: ")) + l("Loot 3, Max MP +250"); + + dnext; + mes ""; + mes l("%s", getitemlink(MaggotCocoon)); + mesc b(l("Acquisition: ")) + l("Grand Hunter Quest"); + mesc b(l("Food: ")) + getitemlink(BugLeg); + mesc b(l("Bonus: ")) + l("Loot 3, Max HP +250"); + + dnext; + mes ""; + mes l("%s", getitemlink(BatEgg)); + mesc b(l("Acquisition: ")) + l("Grand Hunter Quest"); + mesc b(l("Food: ")) + getitemlink(RoastedMaggot); + mesc b(l("Bonus: ")) + l("Loot 3, ASPD +5%"); + + dnext; + mes ""; + mes l("%s", getitemlink(ForestShroomEgg)); + mesc b(l("Acquisition: ")) + l("Grand Hunter Quest"); + mesc b(l("Food: ")) + getitemlink(Moss); + mesc b(l("Bonus: ")) + l("Loot 3, STR +4, AGI +1"); + + dnext; + mes ""; + mes l("%s", getitemlink(MoggunEgg)); + mesc b(l("Acquisition: ")) + l("Grand Hunter Quest"); + mesc b(l("Food: ")) + getitemlink(MoubooSteak); + mesc b(l("Bonus: ")) + l("Loot 3, DEF +5"); + + dnext; + mes ""; + mes l("%s", getitemlink(TamedSnakeEgg)); + mesc b(l("Acquisition: ")) + l("Grand Hunter Quest"); + mesc b(l("Food: ")) + getitemlink(MoubooSteak); + mesc b(l("Bonus: ")) + l("Loot 3, Evasion +7"); + + dnext; + mes ""; + mes l("%s", getitemlink(DragonHorn)); + mesc b(l("Acquisition: ")) + l("Grand Hunter Quest"); + mesc b(l("Food: ")) + getitemlink(Dragonfruit); + mesc b(l("Bonus: ")) + l("Loot 4, Str +1, Gold Drop (4%)"); + + dnext; + mes ""; + mes l("%s", getitemlink(BhopEgg)); + mesc b(l("Acquisition: ")) + l("Easter Top 1 Prize"); + mesc b(l("Food: ")) + getitemlink(Aquada); + mesc b(l("Bonus: ")) + l("Loot 3, Luck +5, Luck Dance, Passive MP Regen (1)"); + + dnext; + mes ""; + mes l("%s", getitemlink(DoggyDog)); + mesc b(l("Acquisition: ")) + l("Valentine Top 1 Prize"); + mesc b(l("Food: ")) + getitemlink(AnimalBones); + mesc b(l("Bonus: ")) + l("Loot 3, STR +5, Vitality Dance, Passive HP Regen (1)"); + + dnext; + mes ""; + mes l("%s", getitemlink(CattyCat)); + mesc b(l("Acquisition: ")) + l("Christmas Top 1 Prize"); + mesc b(l("Food: ")) + getitemlink(Milk); + mesc b(l("Bonus: ")) + l("Loot 3, Agi +5, Agility Dance, Passive MP Regen (1)"); + + dnext; + mes ""; + mes l("%s", getitemlink(BlackyCat)); + mesc b(l("Acquisition: ")) + l("Magic Olympics Top 1 Prize"); + mesc b(l("Food: ")) + getitemlink(Mashmallow); + mesc b(l("Bonus: ")) + l("Loot 3, Int +5, Agi +1"); + + dnext; + mes ""; + mes l("%s", getitemlink(PinkieCrystal)); + mesc b(l("Acquisition: ")) + l("Ultra Rare Drop"); + mesc b(l("Food: ")) + getitemlink(CherryCake); + mesc b(l("Bonus: ")) + l("Loot 3, Agi +5"); + + next; + mes l("-- Animals Protection Agency of Hurnscald"); + } + return; +} + +- script #Book-Petcaring NPC_HIDDEN,{ + function read_book { + PetcaringBook(); + close; + } + +OnShelfUse: + @book_name$ = .bookname$; + if (openbookshelf ()) + read_book; + close; +OnUse: + @book_name$ = .bookname$; + if (openbook ()) + read_book; + close; +OnInit: + .bookname$ = "Fluffy Animals who Love Their Owners"; + .sex = G_OTHER; + .distance = 1; + end; +} + + + + + + + + + + + + + + + + + + + + + + + +- script #Book-JGrimorium NPC_HIDDEN,{ + end; + +function myself { + // TODO: Save the variables in temp vars + // If getarg(1) is not your charid, detach + // Then attach the getarg(1) instead + // Display then using the temp vars + // For scrying, myself(false, .@me) + // While still using attachrid + .@all = getarg(0, true); + .@who = getarg(1, getcharid(3)); + .@why = getcharid(3); + .@adm = is_admin(); + + /* Basic Data */ + detachrid(); + attachrid(.@who); + // No scrying + if (GSET_NOSCRY && .@who != .@why && !.@adm) { + detachrid(); + attachrid(.@why); + return 1; + } + // Basic data + .@name$ = strcharinfo(0, "error", .@who); + .@staff = is_staff(); + .@sponsor = is_sponsor(); + .@party$ = (getcharid(1) ? strcharinfo(1) : ""); + .@guild$ = (getcharid(2) ? strcharinfo(2) : ""); + .@clan$ = (getcharid(5) ? strcharinfo(4) : ""); + .@married = getpartnerid(); + .@legend = islegendary(); + if (getpetinfo(0)) { + .@pet = true; + .@pet_name$ = getpetinfo(2); + .@pet_type$ = getpetinfo(1); + } else { + .@pet = false; + } + if (gethominfo(0)) { + .@homun = true; + .@hc_name$ = gethominfo(2); + } else { + .@homun = false; + } + .@born = #REG_DATE; + detachrid(); + attachrid(.@why); + + mes ".:: " + .@name$ + " ::."; + if (.@staff) + mesc l("%s is currently a staff member.", .@name$), 3; + else if (.@sponsor) + mesc l("%s is currently sponsoring the High Alliance.", .@name$), 3; + mes ""; + if (.@party$ != "") + mesc l("Party Name: @@", .@party$); + if (.@guild$ != "") + mesc l("Guild Name: @@", .@guild$); + if (.@clan$ != "") + mesc l("Clan Name: @@", .@clan$); + if (.@married) + mesc l("Civil status: Married"); + else + mesc l("Civil status: Single"); + if (.@legend) + mesc l("%s is a legendary hero.", .@name$), 2; + if (.@pet) + mesc l("Proud owner of %s the %s.", .@pet_name$, .@pet_type$); + if (.@homun) + mesc l("Proud owner of %s the Homunculus.", .@hc_name$); + mesc l("Born %s ago", FuzzyTime(.@born)); + dnext; + + /* Magic Data */ + detachrid(); + attachrid(.@who); + .@lvl = MAGIC_LVL; + .@rank$ = academicrank(); + if (.@all) { + .@rp$ = fnum(MAGIC_RP); + .@sp = sk_points(); + .@msp = sk_maxpoints(); + } else { + .@rp$ = "?"; + } + detachrid(); + attachrid(.@why); + + mes ".:: " + l("Magic Status") + " ::."; + mesc l("Current magic rank: %d", .@lvl); + if (.@all) + mesc l("You have @@/@@ magic skill points available.", + b(.@sp), .@msp); + mesc l("Your current scholar rank: %s (%s Research Points)", + .@rank$, .@rp$); + if (.@who == .@why) + ShowAbizit(true); + dnext; + + + /* Rogue Data */ + detachrid(); + attachrid(.@who); + .@rank$ = thiefrank(); + if (.@all) { + .@exp = THIEF_EXP; + .@rank= THIEF_RANK; + } + detachrid(); + attachrid(.@why); + + mes ".:: " + l("Rogue Status") + " ::."; + mesc l("Your current rank: %s", .@rank$); + if (.@all && .@exp > (.@rank*2)**5) + mesc l("An upgrade is available."), 2; + dnext; + + + /* Craft Data */ + detachrid(); + attachrid(.@who); + .@skill = getskilllv(TMW2_CRAFT); + .@score = CRAFTING_SCORE_COMPLETE/40; + .@milis = CRAFTING_SCORE_COMPLETE%40*100; // Broken? + detachrid(); + attachrid(.@why); + + mes ".:: " + l("Crafting Status") + " ::."; + mesc l("Skill Level: %d", .@skill); + mesc l("Crafting Score: %d.%02d", .@score, .@milis); + dnext; + + + + + /* Misc Data */ + detachrid(); + attachrid(.@who); + if (.@all) { + .@mpt = Mobpt; + .@gp = (Zeny+BankVault); + } + .@die = PC_DIE_COUNTER; + .@reborn = REBIRTH; + .@honor = HONOR; + .@kills = MONSTERS_KILLED; + .@gid = getcharid(2); + .@afk_h = AFKING/1200; + .@afk_m = AFKING%1200/60*3; + detachrid(); + attachrid(.@why); + + mes ".:: " + l("Miscellaneous Status") + " ::."; + if (.@all) + mesc l("Monster Points: %s", fnum(.@mpt)); + mesc l("Times died: %s", fnum(.@die)); + mesc l("Times reborn: %d", .@reborn); + if (.@all) + mesc l("Total Gold: %s", fnum(.@gp)); + mesc l("Honor Points: %s", fnum(.@honor)); + mesc l("Monsters killed: %s", fnum(.@kills)); + if (.@gid > 0) { + .@pos=getguildrole(.@gid, .@who); + mesc l("Current Guild: %s", getguildname(.@gid)); + mesc l("Guild Master: @@", getguildmaster(.@gid)); + if (.@all) + mesc l("You are the guild's \"%s\", and you contribute with %02d%% EXP.", + getguildpostitle(.@gid, .@pos), + getguildpostax(.@gid, .@pos)); + } + mesc l("Total time AFK'ed in Tulimshar: %d hours and %d minutes", + .@afk_h, .@afk_m); + dnext; + + + /* Records Data */ + detachrid(); + attachrid(.@who); + .@candor = CRAZYPOINTS; + .@bloodbath = gettimetick(2)+SCANDORPTS; + .@ctf = CAPTURE_FLAG; + .@cod = getq2(LoFQuest_COD); + .@merc = MERCENARY_DAILYQUEST; + .@udt = UDTRANK; + .@egg = getq3(General_EasterEggs); + detachrid(); + attachrid(.@why); + mes ".:: " + l("Personal Records") + " ::."; + mesc l("Candor Battle Score: %s", fnum(.@candor)); + mesc l("Candor Bloodbath Score: %s", FuzzyTime(.@bloodbath)); + mesc l("Times won Capture the Flag: %s", fnum(.@ctf)); + mesc l("Times won Call of Dusty: %s", fnum(.@cod)); + mesc l("Mercenary Quests completed: %s", fnum(.@merc)); + mesc l("Doppelganger Waves Won: %s", fnum(.@udt)); + mesc l("Easter Eggs found: %d", .@egg); + dnext; + + + /* Feat Data */ + detachrid(); + attachrid(.@who); + .@yeti = YETIKING_WINNER; + .@hh = HEROESHOLD_WINNER; + .@reborn = REBIRTH_WINNER; + .@quirin = QUIRINO_WINNER; + .@gemini = GEMINI_WINNER; + .@ghq = GHQ_WINNER; + .@doct = EPISODE_WINNER; + .@fort = FORT_1ST_VISIT; + .@seal = MOUBOOTAUR_WINNER; + detachrid(); + attachrid(.@why); + mes ".:: " + l("Personal Feats") + " ::."; + if (.@yeti) + mesc l("Cleared the Yeti King Challenge %s ago", FuzzyTime(.@yeti)); + if (.@hh) + mesc l("Cleared Heroes Hold %s ago", FuzzyTime(.@hh)); + if (.@gemini) + mesc l("Cleared Gemini Sisters Quest %s ago", FuzzyTime(.@gemini)); + if (.@reborn) + mesc l("First reborn %s ago", FuzzyTime(.@reborn)); + if (.@quirin) + mesc l("Won Quirino Voraz Arena %s ago", FuzzyTime(.@quirin)); + if (.@ghq) + mesc l("First Grand Hunter challenge cleared %s ago", FuzzyTime(.@ghq)); + if (.@doct) + mesc l("Completed The Episode of Ozthokk %s ago", FuzzyTime(.@doct)); + if (.@fort) + mesc l("First visit to Fortress Is. %s ago", FuzzyTime(.@fort)); + if (.@seal) + mesc l("Defeated the Moubootaur (Sealed) %s ago", FuzzyTime(.@seal)); + + /* Heroic Data */ + detachrid(); + attachrid(.@who); + .@candor = (reputation("Candor") >= 100); + .@tulim = (reputation("Tulim") >= 100); + .@halin = (reputation("Halin") >= 100); + .@hurns = (reputation("Hurns") >= 100); + .@lof = (reputation("LoF") >= 100); + .@nival = (reputation("Nival") >= 100); + .@frost = (reputation("Frostia") >= 100); + .@forte = (reputation("Fortress") >= 100); + detachrid(); + attachrid(.@why); + if (.@candor) + mesc l("%s Hero", l("Candor")); + if (.@tulim) + mesc l("%s Hero", l("Tulimshar")); + if (.@halin) + mesc l("%s Hero", l("Halinarzo")); + if (.@hurns) + mesc l("%s Hero", l("Hurnscald")); + if (.@lof) + mesc l("%s Hero", l("Land Of Fire")); + if (.@nival) + mesc l("%s Hero", l("Nivalis")); + if (.@frost) + mesc l("%s Hero", l("Frostia")); + if (.@forte) + mesc l("%s Hero", l("Fortress Town")); + // TODO: Total players invited to ML + // TODO: Houses owned + // TODO: Times elected + // TODO: Admin of how many towns? + // TODO: First election won date + // TODO: Most used skill + // TODO: Remember the position attained on previous events + //mes ".:: " + l("Achievements") + " ::."; + return 0; +} + +OnScry: + if (!countitem(JesusalvaGrimorium) && !is_staff()) + end; + .@w$ = implode(.@atcmd_parameters$, " "); + if (.@w$ == "" || .@w$ == "NULL") + .@w$ = strcharinfo(0); + .@me = getcharid(3); + .@id = getcharid(3, .@w$); + if (!.@id) { + mesc l("The requested char \"%s\" is not online or does not exist.", .@w$), 1; + close; + } + /* attachrid() + mes() is a no-go for this */ + setnpcdialogtitle sprintf("@scry %s", .@w$); + if (myself(is_admin(), .@id)) { + mesc l("%s has protected themselves from prying eyes. Your scry attempt failed.", .@w$), 1; + } + close; + +function read_book { + + setnpcdialogtitle l(.book_name$); + + narrator S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("I, second sage of Fate, write this book. The knowledge on it shall guide you to the Secret Of Mana."); + + @menu = 0; // reset for the rif + + do + { + narrator S_NO_NPC_NAME, + l("Please select a chapter:"); + + mes ""; + + select + rif2(1, MAGIC_LVL, l("Ch 1 — Prologue")), + rif2(2, MAGIC_EXP, l("Ch 2 — About Magic Skills")), + rif2(3, MAGIC_LVL, l("Ch 3 — Status Ailments")), + rif2(4, true, l("Ch 4 — Information About You")), + rif2(5, true, l("Ch 5 — Information About Others")), + rif2(6, true, l("Open Fishing Book")), + rif2(7, true, l("Open Petcaring Book")), + rif2(8, getq(LoFQuest_Pets), l("List of Unlocked Pets and Food")), + rif2(9, CRAFTQUEST, l("Open Recipe Book")), + rif2(10, true, l("Read Rules")), + l("Close"); + mes ""; + + switch(@menu) + { + case 1: + mesc l("You have @@/@@ magic skill points available.", b(sk_points()), sk_maxpoints()); + mesc l("Your current scholar rank: %s (%d Research Points)", + academicrank(), fnum(MAGIC_RP)); + next; + narrator S_LAST_NEXT, + l("Mana is something which existed since the being, but nobody knows much about."), + l("This book will write itself, and reveal you the Secret Of Mana."), + l("Give it time, increase your magic power, and you'll find out the truth."), + l("You are a @@º degree mage. This book allows you many new possibilities.", MAGIC_LVL); + break; + case 2: + narrator S_LAST_NEXT, + l("Re-casting the same magic spell or skill won't give you magic experience."), + l("Summoning and Homunculus (H) skills can be raised from skill window directly."); + // TODO: We could show you all the skills via loop? Meh + + ShowAbizit(true); + next; + break; + case 3: + mes l("There are several minor status conditions, which may buff or debuff you."); + mes l("An example is dec agi, which lowers your agility."); + mes l("The most aggressive and main ones are:"); + next; + mesf("##B%s##b - %s", l("Blind"), l("Acc. and Evade -25%%")); + mesf("##B%s##b - %s", l("Burning"), l("Damage over time, MDF -25%%")); + mesf("##B%s##b - %s", l("Curse"), l("ATK = 25%%, LUK = 0, Slow down")); + mesf("##B%s##b - %s", l("Freeze"), l("Can't move, DEF-, no evade, Water element")); + mesf("##B%s##b - %s", l("Poison"), l("DEF-, Damage over time, no MP regen")); + mesf("##B%s##b - %s", l("Silence"), l("Can't use skills")); + mesf("##B%s##b - %s", l("Sleep"), l("Can't move nor attack, crit def -100%")); + mesf("##B%s##b - %s", l("Stone"), l("Can't move, DEF-, Damage over time, Earth element")); + mesf("##B%s##b - %s", l("Stun"), l("Can't move nor evade.")); + next; + mes l("There are also less common ailments:"); + next; + mesf("##B%s##b - %s", l("Bleed"), l("Lethal damage over time, no regen.")); + mesf("##B%s##b - %s", l("Confuse"), l("Random movement and extra desync")); + mesf("##B%s##b - %s", l("Cold"), l("Total slow down, DEF-, may freeze")); + mesf("##B%s##b - %s", l("Deadly Poison"), l("Lower MAXHP, Damage over time")); + mesf("##B%s##b - %s", l("Deep Sleep"), l("Can't chat, recover HP over time")); + mesf("##B%s##b - %s", l("Fear"), l("Can't move, Acc. and Evade -20%%")); + next; + break; + case 4: + myself(); + break; + case 5: + mesc l("You can scry other players with: %s", b("@scry")); + mesc l("To scry Jesusaves, for example, you would do:"); + mes b("@scry Jesusaves"); + mes ""; + mesc l("- Players can prevent being scry'ed with %s", b("@ucp")); + mesc l("- Some information like money won't be available."); + mesc l("- Target player must be online."); + next; + break; + case 6: + FishingBook(); + break; + case 7: + PetcaringBook(); + break; + case 8: + mesf(".:: %s ::.", l("Grand Hunter Quest")); + if (PDQ_CheckGHQ(Maggot) >= 10000) + mesf("%s - %s", + getmonsterlink(Maggot), getitemlink(BugLeg)); + if (PDQ_CheckGHQ(ForestMushroom) >= 10000) + mesf("%s - %s", + getmonsterlink(ForestMushroom), getitemlink(Moss)); + if (PDQ_CheckGHQ(Fluffy) >= 10000) + mesf("%s - %s", + getmonsterlink(Fluffy), getitemlink(LettuceLeaf)); + if (PDQ_CheckGHQ(Duck) >= 10000) + mesf("%s - %s", + getmonsterlink(Duck), getitemlink(CherryCake)); + if (PDQ_CheckGHQ(Bat) >= 10000) + mesf("%s - %s", + getmonsterlink(Bat), getitemlink(RoastedMaggot)); + if (PDQ_CheckGHQ(Moggun) >= 10000) + mesf("%s - %s", + getmonsterlink(Moggun), getitemlink(MoubooSteak)); + if (#LOGIN_ALLTIME >= 6) + mesf("%s - %s", + getmonsterlink(Piou), getitemlink(Piberries)); + next; + mesf(".:: %s ::.", l("Special Event Pets")); + if (countitem(Ratte)) + mesf("%s - %s", + getmonsterlink(Ratto), getitemlink(Cheese)); + if (countitem(BhopEgg)) + mesf("%s - %s", + getmonsterlink(BhopFluffy), getitemlink(Aquada)); + if (countitem(DoggyDog)) + mesf("%s - %s", + getmonsterlink(Toto), getitemlink(AnimalBones)); + if (countitem(CattyCat)) + mesf("%s - %s", + getmonsterlink(WhiteCat), getitemlink(Milk)); + if (countitem(BlackyCat)) + mesf("%s - %s", + getmonsterlink(BlackCat), getitemlink(Mashmallow)); + next; + break; + case 9: + closeclientdialog; + doevent("#RecipeBook::OnUse"); + end; + break; + case 10: + GameRules(); + break; + default: + close; + } + } while (true); + + end; +} + +OnShelfUse: + if (openbookshelf()) + read_book; + bye; + +OnUse: + read_book; + bye; + +OnInit: + .book_name$ = getitemname(JesusalvaGrimorium); + .sex = G_OTHER; + .distance = 1; + bindatcmd "scry", "#Book-JGrimorium::OnScry", 0, 0, 0; + end; +} + diff --git a/npc/items/croconut.txt b/npc/items/croconut.txt new file mode 100644 index 0000000..eececb0 --- /dev/null +++ b/npc/items/croconut.txt @@ -0,0 +1,78 @@ +// Evol scripts. +// Authors: +// 4144 +// Reid +// Jesusalva +// Description: +// Allows to break a Croconut into multiple parts. + +- script Croconut NPC_HIDDEN,{ + close; + +OnUse: + mesc l("Do you want to break open this %s?", getitemlink(Croconut)); + + select + l("Yes."), + l("No."); + mes ""; + closeclientdialog; + switch (@menu) { + case 1: + goto L_Weapon; + case 2: + getitem Croconut, 1; + close; + } + close; + +L_Weapon: + .@r=rand2(1,5); + switch (.@r) { + case 1: + case 2: + case 3: + goto L_TooWeak; break; + case 4: + goto L_Weak; break; + case 5: + goto L_Good; break; + } + +L_TooWeak: + // Croconuts do not heal much. So opening them without fail should be possible at relatively low strength levels. + .@q = rand2(5); + if (readparam2(bStr) > 10) + .@q = .@q + 1; + if (readparam2(bStr) > 25) + .@q = .@q + 1; + if (readparam2(bStr) > 35) + .@q = .@q + 1; + + if (.@q == 0) goto L_TooWeakLost; + if ( (.@q == 1) || (.@q == 2) ) goto L_TooWeakFail; + if ( (.@q >= 3) && (.@q <= 6) ) goto L_Weak; + if ( (.@q > 6) ) goto L_Good; + +L_TooWeakLost: + dispbottom l("Oops! You destroyed your %s.", getitemlink(Croconut)); + close; + +L_TooWeakFail: + dispbottom l("Well... you did not succeed in opening this %s.", getitemlink(Croconut)); + + getitem Croconut, 1; + close; + +L_Weak: + dispbottom l("You broke the %s into two parts, but you crushed one of them.", getitemlink(Croconut)); + + getitem HalfCroconut, 1; + close; + +L_Good: + dispbottom l("You perfectly cut your %s into two edible parts.", getitemlink(Croconut)); + + getitem HalfCroconut, 2; + close; +} diff --git a/npc/items/emptybox.txt b/npc/items/emptybox.txt new file mode 100644 index 0000000..2f676b1 --- /dev/null +++ b/npc/items/emptybox.txt @@ -0,0 +1,54 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Allows to create your own fish/plushroom/croconut box + +- script Empty Box#it NPC_HIDDEN,{ + close; + +OnUse: + mesn; + mesc l("You can fill this box with the following items:"); + mesc l("- @@ @@", 7, getitemlink(GrassCarp)); + mesc l("- @@ @@", 8, getitemlink(Croconut)); + mesc l("- @@ @@", 20, getitemlink(CommonCarp)); + mesc l("- @@ @@", 87, getitemlink(Plushroom)); + mes ""; + mesc l("Fill with what?"); + select + l("Don't fill"), + rif(countitem(GrassCarp) >= 7, l("7 Grass Carps")), + rif(countitem(Croconut) >= 8, l("8 Croconuts")), + rif(countitem(CommonCarp) >= 20, l("20 Common Carps")), + rif(countitem(Aquada) >= 50, l("50 Aquadas")), + rif(countitem(Plushroom) >= 87, l("87 Plushrooms")); + mes ""; + switch (@menu) { + case 2: + delitem GrassCarp, 7; + getitem FishBox, 1; + break; + case 3: + delitem Croconut, 8; + getitem CroconutBox, 1; + break; + case 4: + delitem CommonCarp, 20; + getitem FishBox, 1; + break; + case 5: + delitem Aquada, 50; + getitem AquadaBox, 1; + break; + case 6: + delitem Plushroom, 87; + getitem PlushroomBox, 1; + break; + default: + getitem EmptyBox, 1; + } + delitem EmptyBox, 1; + closedialog; + close; +} diff --git a/npc/items/grenade.txt b/npc/items/grenade.txt new file mode 100644 index 0000000..96d952c --- /dev/null +++ b/npc/items/grenade.txt @@ -0,0 +1,122 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Grenades workaround + +// grenade(range, damage - in 0.01%, flag) - defaults to 3x3 square, with 5% damage. +// If flag is set, damage will be deemed to be absolute values. +function script grenade { + .@r=getarg(0, 3); + .@d=getarg(1, 500); + .@f$=getarg(2, "filter_notboss"); + + getmapxy(.@m$, .@x, .@y, 0); + .@c=getunits(BL_MOB, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); + for (.@i = 0; .@i < .@c; .@i++) { + .@hp=getunitdata(.@mbs[.@i], UDT_HP); + .@dm=max(1, .@hp*(10000-.@d)/10000); + if (getarg(2, false)) + .@dm=max(1, .@hp-.@d); + if (callfunc(.@f$)) { + //debugmes "Hitting monster (%d hp) for %d damage", .@hp, .@dm; + setunitdata(.@mbs[.@i], UDT_HP, .@dm); + specialeffect(FX_ATTACK, AREA, .@mbs[.@i]); + } + } + return; +} + +// areasc(range, time, sc, bl, value, filter, target, chances) +// Defaults to 3x3 square, sleep mob for 500ms. Ignores you. +// Centered on player attached, 100% success chance +// Need a player caster. Valid BL: BL_MOB | BL_PC | BL_HOM | BL_MER +function script areasc { + .@r=getarg(0, 3); + .@d=getarg(1, 500); + .@s=getarg(2, SC_SLEEP); + .@b=getarg(3, BL_MOB); + .@val=getarg(4, 1); + .@f$=getarg(5, "filter_notme"); + .@t=getarg(6, playerattached()); + .@sr=getarg(7, 10000); + + getmapxy(.@m$, .@x, .@y, getunittype(.@t), .@t); + .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); + for (.@i = 0; .@i < .@c; .@i++) { + // Filtering + if (!callfunc(.@f$, .@mbs[.@i])) + continue; + sc_start .@s, .@d, .@val, .@sr, SCFLAG_NONE, .@mbs[.@i]; + specialeffect(FX_BUFF, AREA, .@mbs[.@i]); + } + return; +} + +// areasc2(map, x, y, {range, time, sc, bl, value, filter}) - can be used by NPC +// Valid BL: BL_MOB | BL_PC | BL_HOM | BL_MER +function script areasc2 { + .@m$=getarg(0); + .@x=getarg(1); + .@y=getarg(2); + .@r=getarg(3, 3); + .@d=getarg(4, 500); + .@s=getarg(5, SC_SLEEP); + .@b=getarg(6, BL_MOB); + .@val=getarg(7, 1); + .@f$=getarg(8, "filter_always"); + + .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); + for (.@i = 0; .@i < .@c; .@i++) { + // Filtering + if (!callfunc(.@f$, .@mbs[.@i])) + continue; + sc_start .@s, .@d, .@val, 10000, SCFLAG_NONE, .@mbs[.@i]; + specialeffect(FX_BUFF, AREA, .@mbs[.@i]); + } + return; +} + +// areasc3(range, time, sc, bl, val1, val2, filter) +// Defaults to 3x3 square, sleep mob for 500ms. Ignores you. +// Need a player caster. Valid BL: BL_MOB | BL_PC | BL_HOM | BL_MER +function script areasc3 { + .@r=getarg(0, 3); + .@d=getarg(1, 500); + .@s=getarg(2, SC_SLEEP); + .@b=getarg(3, BL_MOB); + .@v1=getarg(4, 1); + .@v2=getarg(5, 1); + .@f$=getarg(6, "filter_notme"); + + getmapxy(.@m$, .@x, .@y, 0); + .@c=getunits(.@b, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); + for (.@i = 0; .@i < .@c; .@i++) { + // Filtering + if (!callfunc(.@f$, .@mbs[.@i])) + continue; + sc_start2 .@s, .@d, .@v1, .@v2, 10000, SCFLAG_NONE, .@mbs[.@i]; + specialeffect(FX_BUFF, AREA, .@mbs[.@i]); + } + return; +} + +// massprovoke(range, {map, x, y}) - player only +function script massprovoke { + getmapxy(.@m$, .@x, .@y, 0); + .@r=getarg(0, 3); + .@m$=getarg(1, .@m$); + .@x=getarg(2, .@x); + .@y=getarg(3, .@y); + + .@c=getunits(BL_MOB, .@mbs, false, .@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); + for (.@i = 0; .@i < .@c; .@i++) { + //sc_start .@s, .@d, 1, 10000, SCFLAG_NONE, .@mbs[.@i]; + aggravate .@mbs[.@i]; + specialeffect(FX_MAGIC, AREA, .@mbs[.@i]); + } + return; +} + +// TODO: Maybe we could use areasc() with a special check +// To force the implementation of guild skills... (Yet another script based) diff --git a/npc/items/inc_sc_bonus.txt b/npc/items/inc_sc_bonus.txt new file mode 100644 index 0000000..8e6b6e5 --- /dev/null +++ b/npc/items/inc_sc_bonus.txt @@ -0,0 +1,72 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Description: +// Applies effects for INC_* (STR doesn't exist) +// Valid values: INCAGI INCVIT INCINT INCDEX INCLUK INCHIT INCFLEE SC_FURY +// Doesn't works: SC_STRUP +// Works if .@min == .@max: INCMHP INCMHPRATE INCMSP INCMSPRATE +/// Untested Values: WALKSPEED (reverse logic) INVINCIBLE (broken) +// PS. SC_FURY causes crit rate to increase +// +// Variables: +// .@delay Second of buffing +// .@type SC_* +// .@min Min amount of type +// .@max Max amount of type (optional) + +// SC_Bonus(delay, SC, min{, max}) +function script SC_Bonus { + .@delay=getarg(0); + .@type=getarg(1); + .@min=getarg(2); + .@max=getarg(3, .@min); + if (.@delay <= 0) + return false; + + // Get the bonus value + if (.@min != .@max) + .@bonus=rand2(.@min, .@max); + else + .@bonus=.@min; + + // Remaining time and effect conversion + .@v=getstatus(.@type, 1); + .@t=getstatus(.@type, 5); + + // Convert remaining time to seconds, rounded down + if (.@t > 1000) + .@t=.@t/1000; + else + .@t=0; + + // If there was effect previously, get ponderate average + if (.@v > 0) + .@v=ponderate_avg(.@bonus, .@delay, .@v, .@t); + else + .@v=.@bonus; + + // Update time value to ms and to stack + .@t+=.@delay; + .@t*=1000; + + // Debug print if needed + if (debug || $@GM_OVERRIDE) + debugmes "Effect %d (+%d percent) for %d ms", .@type, .@bonus, .@t; + + // Restart the bonus + sc_end .@type; + sc_start .@type,.@t,.@v; + return true; +} + +- script inc_sc_bonus -1,{ +OnUse: + SC_Bonus(@delay, @type, @min, @max); + @delay=0; + @type=0; + @min=0; + @max=0; + end; +} + diff --git a/npc/items/legacy_heal.txt b/npc/items/legacy_heal.txt new file mode 100644 index 0000000..2beb820 --- /dev/null +++ b/npc/items/legacy_heal.txt @@ -0,0 +1,79 @@ +// TMW-2 Script. +// Evol scripts. +// Author: +// Reid +// Jesusalva +// Description: +// Legacy Healing System +// +// Variables: +// @delay Second of healing +// @min Min amount of healing +// @max Max amount of healing +// +// *getequipoption(EQI_HEAD_TOP,1,168); → Heal Bonus (should be first bonus on Chef Hat) + + +// ItHeal(delay, min, {max=min}) +function script ItHeal { + .@delay=getarg(0, @delay); + .@min=getarg(1, @min); + .@max=getarg(2, (@max ? @max : .@min)); + + if (.@delay <= 0) { + Exception("Invalid legacy healing item, deleting without healing effect."); + end; + } + + // Decide the healing bonus type. We have four types: S, L, G and M + // By default, we use 'S' + .@skill = SC_S_LIFEPOTION; + + // Vitality can improve the healing amount + .@min = min(.@max, .@min + (.@min * readparam2(bVit) / 100)); + .@max = .@max + (.@max * readparam2(bVit) / 240); + + // Obtain the real healing + @val1 = rand2(.@min, .@max) / .@delay; + //debugmes "Heal %d-%d/%d = %d", .@min, .@max, .@delay, @val1; + + .@delay *= 1000; // Put the delay in ms + + // We now have @val1 (new effect), @delay (new delay) + // But do we have .@v and .@d (old effect and delay)? + .@v=getstatus(.@skill, 1); + .@d=getstatus(.@skill, 4) * 1000; + + // If there WAS an effect previously, get ponderate average + // Note: never use double-precision ponderate averages + if (.@v > 0) { + @val1=ponderate_avg(@val1, .@delay, .@v, .@d); + // Overflow and Underflow protection + if (.@delay+.@d < .@delay*5 && .@d > 0) + .@delay=.@delay+.@d; + //.@delay=ponderate_avg(.@delay, @val1, .@d, .@v); + } + + // Apply the effect and finish + sc_end .@skill; + sc_start2 .@skill, .@delay, @val1, 1; + + // @val1 must be preserved for cross-reading + @delay=0; + @min=0; + @max=0; + // @val1=0; + return; +} + +- script legacy_heal -1,{ + +OnUse: + if (@delay <= 0) { + Exception("Invalid legacy healing item, deleting without healing effect."); + end; + } + + ItHeal(@delay, @min, @max); + end; +} diff --git a/npc/items/lofteleporter.txt b/npc/items/lofteleporter.txt new file mode 100644 index 0000000..9952d72 --- /dev/null +++ b/npc/items/lofteleporter.txt @@ -0,0 +1,123 @@ +// TMW2 scripts. +// Authors: +// Pyndragon +// Jesusalva +// Description: +// Hand Teleporter (also saves coordinates - @memo) + +- script LoF Teleporter NPC_HIDDEN,{ + close; + + // Checks if you can warp + function loftel_check { + getmapxy(.@m$, .@x, .@y, 0); + .@is_hurt=(readparam(Hp) < readparam(MaxHp)*9/10); // <90% hp + .@is_town=(getmapflag(.@m$, mf_town)); + return (.@is_hurt && !.@is_town); + } + + // Calculate time remaining + // (time, .@x) + function loftel_time { + return gettimetick(2)+max((60*getarg(0))-(getarg(1)*60), 30); + } + +L_Cooldown: + mesn; + mesc l("This teleporter is currently recharging."); + mesc l("You can use it again in @@.", FuzzyTime(TELEPORTER_TIME)); + close; + +OnUse: + if (TELEPORTER_TIME > gettimetick(2)) + goto L_Cooldown; + if (readparam(Hp) < readparam(MaxHp)) { + dispbottom l("You are hurt, and cannot use this."); + end; + } + if (BaseLevel < 20) { + dispbottom l("This is too powerful to you. Get level 20 before attempting to use."); + end; + } + + mesn; + mesc l("Ozthokk, a great sage from the Land Of Fire, holds secrets of time and space travel."); + mesc l("This is not magic, it is science!"); + mes ""; + mesc l("PS. Additional reagents may be required for warps."); + next; + + .@x=(reputation("LoF")/10)+min(15, countitem(TimeFlask)-1); // up to 10 minutes reduction from quests, and 15 from time flasks + + select + l("Don't warp"), + l("Land Of Fire Village (@@m)", 35-.@x), + l("Tulimshar (@@m)", 35-.@x), + rif(TELEPORTERS & TP_FROST, l("Frostia (@@m)", 120-.@x)), + rif(TELEPORTERS & TP_HALIN, l("Halinarzo (@@m)", 120-.@x)), + rif(TELEPORTERS & TP_LILIT, l("Lilit (@@m)", 150-.@x)), + rif(GSET_SOULMENHIR_MANUAL, l("Save Point (@@m)", 30-.@x)), + rif(getq(LoFQuest_EPISODE) >= 15, l("200 years ago, The Great Fire (%dm)", 360-(.@x*2))); + + if (@menu == 1) + close; + + switch (@menu) { + case 1: + } + if (loftel_check()) { + dispbottom l("You are hurt, and cannot use this."); + } + sshake(); + switch (@menu) { + case 2: + warp "017-1", 120, 89; + TELEPORTER_TIME=loftel_time(35, .@x); + EnterTown("LoF"); + @timer_navio_running=0; + break; + case 3: + warp "003-1", 41, 49; + TELEPORTER_TIME=loftel_time(35, .@x); + EnterTown("Tulim"); + @timer_navio_running=0; + break; + case 4: + warp "024-1", 155, 82; + TELEPORTER_TIME=loftel_time(120, .@x); + EnterTown("Frostia"); + @timer_navio_running=0; + break; + case 5: + warp "009-1", 113, 91; + TELEPORTER_TIME=loftel_time(120, .@x); + EnterTown("Halin"); + @timer_navio_running=0; + break; + case 6: + warp "018-5", 111, 53; + TELEPORTER_TIME=loftel_time(150, .@x); + EnterTown("Lilit"); + @timer_navio_running=0; + break; + case 7: + TELEPORTER_TIME=loftel_time(30, .@x); + //EnterTown("Save"); + teleporthome(); + @timer_navio_running=0; + break; + case 8: + //atcommand("@block "+strcharinfo(0)); + // Reset your bank savings (temporary) + #MerchantBank+=BankVault; + BankVault=0; + setq2 LoFQuest_EPISODE, 0; + setq3 LoFQuest_EPISODE, 0; + warp "032-1", 23, 25; + TELEPORTER_TIME=loftel_time(360, .@x*2); + @timer_navio_running=0; + break; + } + closedialog; + end; +} diff --git a/npc/items/maps.txt b/npc/items/maps.txt new file mode 100644 index 0000000..cb9b418 --- /dev/null +++ b/npc/items/maps.txt @@ -0,0 +1,17 @@ +// TMW2 Script. +// Authors: +// Jesusalva +// Description: +// World Map Items + +function script wmap { + .@loc$=getarg(0, LOCATION$); + setnpcdialogtitle l("World Map - @@", l(.@loc$)); + setskin "map_"+.@loc$; + mes "Please keep your ManaPlus updated."; + select("Ok"); + setskin ""; + closeclientdialog; + return; +} + diff --git a/npc/items/mercenary.txt b/npc/items/mercenary.txt new file mode 100644 index 0000000..140d388 --- /dev/null +++ b/npc/items/mercenary.txt @@ -0,0 +1,150 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Core functions for Mercenary boxset + +// Main loop +// merc_boxset(5★, 4★, 3★, 2★, 1★) +function script merc_boxset { + .@s5=getarg(0,0); + .@s4=getarg(1,0); + .@s3=getarg(2,0); + .@s2=getarg(3,0); + .@s1=getarg(4,0); + + .@sumup=.@s5+.@s4+.@s3+.@s2+.@s1; + /* I wonder if this is needed...? + .@s4+=.@s5; + .@s3+=.@s4; + .@s2+=.@s3; + .@s1+=.@s2; + */ + + // Make the seed. More level and luck increases odds of higher rarity + .@seed=max(0, rand(0, .@sumup)-BaseLevel-readparam(bLuk)); + + // 5 ★ + if (.@seed < .@s5) { + setarray .@r, MercCard_AndreiSakar, MercCard_Woody, MercCard_Lilanna, MercCard_Xanthem; + .@n=any_of(.@r); + // 4 ★ + } else if (.@seed < .@s4) { + setarray .@r, MercCard_Aisen, MercCard_Msawis, MercCard_Swezanne, MercCard_DragonStar; + .@n=any_of(.@r); + // 3 ★ + } else if (.@seed < .@s3) { + setarray .@r, MercCard_Saulc, MercCard_Crazyfefe, MercCard_LawnCable, MercCard_Arthur; + .@n=any_of(.@r); + // 2 ★ + } else if (.@seed < .@s2) { + setarray .@r, MercCard_Pookie, MercCard_Jesusalva, MercCard_Demure, MercCard_EarthWitch; + .@n=any_of(.@r); + // 1 ★ + } else { + setarray .@r, MercCard_Apane, MercCard_Soren, MercCard_GonzoDark, MercCard_Rosa; + .@n=any_of(.@r); + } + + getitem .@n, 1; + return; +} + +/* +// Setup a merc_boxset based on level (TODO) +function script cond_mercboxset { + return; +} +*/ + +// Get mercenary ID +// merc_randid(5★, 4★, 3★, 2★, 1★) +function script merc_randid { + .@s5=getarg(0,0); + .@s4=getarg(1,0); + .@s3=getarg(2,0); + .@s2=getarg(3,0); + .@s1=getarg(4,0); + + .@sumup=.@s5+.@s4+.@s3+.@s2+.@s1; + /* I wonder if this is needed...? + .@s4+=.@s5; + .@s3+=.@s4; + .@s2+=.@s3; + .@s1+=.@s2; + */ + + // Make the seed. More level and luck increases odds of higher rarity + .@seed=max(0, rand(0, .@sumup)-BaseLevel-readparam(bLuk)); + + // 5 ★ + if (.@seed < .@s5) { + setarray .@r, 1192, 1191, 1193, 1210; + .@n=any_of(.@r); + // 4 ★ + } else if (.@seed < .@s4) { + setarray .@r, 1194, 1195, 1209, 1205; + .@n=any_of(.@r); + // 3 ★ + } else if (.@seed < .@s3) { + setarray .@r, 1196, 1197, 1198, 1208; + .@n=any_of(.@r); + // 2 ★ + } else if (.@seed < .@s2) { + setarray .@r, 1200, 1201, 1199, 1207; + .@n=any_of(.@r); + // 1 ★ + } else { + setarray .@r, 1203, 1204, 1202, 1206; + .@n=any_of(.@r); + } + + //debugmes "Return: %d ([%d, %d, %d])", .@n, .@r[0], .@r[1], .@r[2]; + return .@n; +} + +// Read the card and return its rarity +// merc_getstar(card) +function script merc_getstar { + switch (getarg(0)) { + case MercBoxE: + case MercBoxEE: + case MercCard_AndreiSakar: + case MercCard_Lilanna: + case MercCard_Woody: + case MercCard_Xanthem: + return 5; + case MercBoxD: + case MercBoxDD: + case MercCard_Aisen: + case MercCard_Msawis: + case MercCard_DragonStar: + case MercCard_Swezanne: + return 4; + case MercBoxC: + case MercBoxCC: + case MercCard_Saulc: + case MercCard_Crazyfefe: + case MercCard_LawnCable: + case MercCard_Arthur: + return 3; + case MercBoxB: + case MercBoxBB: + case MercCard_Pookie: + case MercCard_Jesusalva: + case MercCard_Demure: + case MercCard_EarthWitch: + return 2; + case MercBoxA: + case MercBoxAA: + case MercCard_Apane: + case MercCard_Soren: + case MercCard_GonzoDark: + case MercCard_Rosa: + return 1; + default: + return 0; + } + return -1; +} + diff --git a/npc/items/miscrecipes.txt b/npc/items/miscrecipes.txt new file mode 100644 index 0000000..275d5b0 --- /dev/null +++ b/npc/items/miscrecipes.txt @@ -0,0 +1,93 @@ +// TMW-2 script. +// Author: +// Jesusalva +// Description: +// More specific Recipe Books in TMW2 + +// callfunc("TerraniteBlueprint") +function script TerraniteBlueprint { + // Lucky roll (0.25%) + if (rand2(2500) < 1) { + getitem AncientBlueprint, 1; + dispbottom l("Wait a minute... This is written in Mananese! I can't read it!"); + return; + } + + // Setup + array_push(.@recipes, CraftRockKnife); // Weapon: Rock Knife + array_push(.@recipes, CraftTerraniteArmor); // TerraniteArmor + array_push(.@recipes, CraftTerranitePants); // TerranitePants + array_push(.@recipes, CraftTerraniteHelmet); // TerraniteHelmet + //array_push(.@recipes, CraftTerranite); // TerraniteGloves (?)(X)(TODO) + //array_push(.@recipes, CraftTerranite); // TerraniteBoots (?)(X)(TODO) + + // Now you'll learn some recipe! + .@rcp=any_of(.@recipes); + + // Double precision failsafe + if (RECIPES_EQUIPMENT[.@rcp]) + .@rcp=any_of(.@recipes); + + // Maybe you already knew it? + if (RECIPES_EQUIPMENT[.@rcp]) { + .@mpot=rand2(2000, 5000); + dispbottom l("It was a recipe you already knew... (+ @@ Mobpt)", .@mpot); + getexp (BaseLevel+JobLevel)*rand2(1,.@rarity), JobLevel+rand2(1,.@rarity); + // Give you some Monster Points to use with Intense Beard + // You do NOT need to be registered with Aidan for this. + Mobpt+=.@mpot; + } else { + dispbottom l("Learned a new recipe!"); + RECIPES_EQUIPMENT[.@rcp]=true; + } + return; +} +///////////////////////////////////////////////////////////////////////////////// + +// callfunc("LegendaryBlueprint") +function script LegendaryBlueprint { + // Unlucky roll (0.25%) + if (rand2(2500) < 1) { + getitem AncientBlueprint, 1; + dispbottom l("Wait a minute... This is written in Mananese! I can't read it!"); + return; + } + // Current: 3x Mylarin & 2x Sunny Crystal (can be cheated for 2) + // Needed: 3x Mylarin & 2x Sunny Crystal + + // Setup + array_push(.@recipes, CraftSkypiercer); // Weapon: Sky Piercer + array_push(.@recipes, CraftSaviorShield); // Shield: Savior Shield + array_push(.@recipes, CraftSaviorArmor); // Savior Armor + //array_push(.@recipes, CraftSaviorPants); // Savior Pants + array_push(.@recipes, CraftSaviorBoots); // Savior Boots + array_push(.@recipes, CraftSaviorHelmet); // Savior Helmet + //array_push(.@recipes, CraftSavior); // Savior Gloves (?)(X) + + // Now you'll learn some recipe! + .@rcp=any_of(.@recipes); + + // Double precision failsafe + if (RECIPES_EQUIPMENT[.@rcp]) + .@rcp=any_of(.@recipes); + + // Triple precision failsafe + if (RECIPES_EQUIPMENT[.@rcp]) + .@rcp=any_of(.@recipes); + + // Maybe you already knew it? + if (RECIPES_EQUIPMENT[.@rcp]) { + .@mpot=rand2(40000, 80000); + dispbottom l("It was a recipe you already knew... (+ @@ Mobpt)", .@mpot); + getexp rand(100000, 200000), rand(10000, 25000); + // Give you some Monster Points to use with Intense Beard + // You do NOT need to be registered with Aidan for this. + Mobpt+=.@mpot; + } else { + dispbottom l("Learned a new recipe!"); + RECIPES_EQUIPMENT[.@rcp]=true; + } + return; +} + + diff --git a/npc/items/rand_mp_heal.txt b/npc/items/rand_mp_heal.txt new file mode 100644 index 0000000..4b6a5ca --- /dev/null +++ b/npc/items/rand_mp_heal.txt @@ -0,0 +1,63 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Description: +// UGLY WORKAROUND. Legacy Method Only. +// Variables: +// @min +// @max +// @delay + +function script MPHeal { + @delay = getarg(0); + @min = getarg(1); + @max = getarg(2); + + if (@delay <= 0) { + Exception("Invalid healing item, deleting without healing effect."); + end; + } + + // +1 max MP per 3 Int, +1 min MP per 5 int. + // Original max MP will be respected + @max = min(@max*2, @min+(readparam2(bInt)/5)); + @min = min(@max, @min+(readparam2(bInt)/3)); + + // Make these abstract % in absolute values + @min=max(1, MaxHp*@min/100); + @max=max(3, MaxHp*@max/100); + + // Save the effect + @mp_healeffect = rand2(@min, @max); + @mp_healeffect = @mp_healeffect / @delay + 1; + @mp_healdelay = @delay; + + // Apply the effect and finish + deltimer .name$+"::OnUpdate"; + addtimer 1000, .name$+"::OnUpdate"; + + // Clear stuff + // @mp_healeffect and @mp_healdelay must be preserved for cross-reading + @delay=0; + @min=0; + @max=0; +} + +- script rand_mp_heal -1,{ + +OnUse: + MPHeal(@delay, @min, @max); + end; + +// Script Heart +OnUpdate: + deltimer .name$+"::OnUpdate"; + heal 0, @mp_healeffect; + @mp_healdelay-=1; + if (@mp_healdelay >= 1) + addtimer 1000, .name$+"::OnUpdate"; + else + @mp_healeffect=0; + end; +} + diff --git a/npc/items/rand_sc_heal.txt b/npc/items/rand_sc_heal.txt new file mode 100644 index 0000000..41ea3b3 --- /dev/null +++ b/npc/items/rand_sc_heal.txt @@ -0,0 +1,112 @@ +// TMW-2 Script. +// Evol scripts. +// Author: +// Reid +// Jesusalva +// Description: +// Random heal every x seconds. +// +// Variables: +// @type +// 0 - Sweeties (lowest) +// 1 - Vegetables +// 2 - Proteins +// 3 - Proccessed +// 4 - Magical (highest) +// @delay +// Overrides the lasting time +// @rarity +// How rare the item is (how much should it effect) +// Ranges from 1 to 10. +// +// Formula: +// MinHeal %: @rarity * ((@type*1) + 1) +// MaxHeal %: @rarity * ((@type*2) + 1) +// Delay: 1 + (@type*2) +// Sweeties: 1s +// Vegetables: 3s +// Proteins: 5s +// Proccessed: 7s +// Magical: 9s +// +// *getequipoption(EQI_HEAD_TOP,1,168); → Heal Bonus (should be first bonus on Chef Hat) + +// ItHeal(type, rarity{, delay=auto}) +function script ItHeal2 { + .@type=getarg(0); + .@rarity=getarg(1); + .@delay=getarg(2, 0); + + // Calculate healing value in % + .@min=.@rarity * ((.@type*1) + 1); + .@max=.@rarity * ((.@type*2) + 1); + + // Healing items always have a bonus positive variation as you are reborn + .@max+=REBIRTH; + + // Vitality raises the minimum healing value in 1%, capped at maximum vlaue + // It also raises @max up to double + .@max = min(.@max*2, .@max+(readparam2(bVit)/50)); + .@min = min(.@max, .@min+(readparam2(bVit)/30)); + + // Make these abstract % in absolute values + .@min=max(1, MaxHp*.@min/100); + .@max=max(3, MaxHp*.@max/100); + + // Calculate how much you'll heal + @val1 = rand2(.@min, .@max); + + // Calculate delay if it was not given + if (.@delay <= 0) { + .@delay=1 + ((.@type*3)/2); + } + + // Update val1 + @val1 = (@val1 / .@delay) + 1; + + // Decide the healing bonus type. We have four types: S, L, G and M + // By default, we use 'S' + .@skill = SC_S_LIFEPOTION; + + // We now have @val1 (new effect), @delay (new delay) + // But do we have .@v and .@d (old effect and delay)? + //Meh, just override it + /* + if (getstatus(.@skill)) { + .@v=getstatus(.@skill, 1); + .@d=getstatus(.@skill, 5); + } + + // If there WAS an effect previously, get ponderate average + if (.@v > 0) { + @val1=ponderate_avg(@val1, @delay, .@v, .@d); + @delay=ponderate_avg(@delay, @val1, .@d, .@v); + } + */ + + // Put the delay in ms + .@delay *= 1000; + + // Apply the effect and finish + sc_end .@skill; + sc_start2 .@skill, .@delay, @val1, 1; + return @val1; +} + +- script rand_sc_heal -1,{ + end; +OnUse: + if (@rarity <= 0) { + Exception("Invalid healing item, deleting without healing effect."); + end; + } + ItHeal2(@type, @rarity, @delay); + + // Clear stuff + // @val1 must be preserved for cross-reading + @delay=0; + @type=0; + @rarity=0; + // @val1=0; + end; +} diff --git a/npc/items/shovel.txt b/npc/items/shovel.txt new file mode 100644 index 0000000..ed1f464 --- /dev/null +++ b/npc/items/shovel.txt @@ -0,0 +1,544 @@ +// TMW2 Script +// Evol scripts. +// Author: +// Travolta +// Jesusalva +// Description: +// NPC to use shovel (dig, bury etc) +// TODO: A function to retrieve a list of all walkable cells on a map +// would be a great improvement to shovel_* functions. As long that it doesn't +// a huge loop, of course... + +// shovel_scriptItem( map, x, y, item, {amount} ) +function script shovel_scriptItem { + .@map$=getarg(0); + .@x=getarg(1); + .@y=getarg(2); + .@id=getarg(3); + .@amount=getarg(4,1); + + // Players can't walk on this cell, why bother? + if (!checkcell(.@map$, .@x, .@y, cell_chkpass)) + return; + + // Bury the item, they're renewed daily at 00:00 + .@wtc = getarraysize($@WBT_Random_id); + $@WBT_Random_id[.@wtc] = .@id; + $@WBT_Random_amount[.@wtc] = .@amount; + $@WBT_Random_map$[.@wtc] = .@map$; + $@WBT_Random_x[.@wtc] = .@x; + $@WBT_Random_y[.@wtc] = .@y; + //debugmes "Buried"+.@amount+" "+getitemname(.@id); + return; +} + +// shovel_scatter( map, x1, y1, x2, y2, amount, item1, {item2, item3...} ) +function script shovel_scatter { + .@map$=getarg(0); + .@x1=getarg(1); + .@y1=getarg(2); + .@x2=getarg(3); + .@y2=getarg(4); + .@amount=getarg(5,1); + .@dbgamm=.@amount; // debug message + + .@wta = getarraysize($@WBT_Random_id); // wta - original + .@wtb = .@wta+.@amount; // wtb - future + .@tries=3; + do { + freeloop(true); // DANGEROUS + for (.@i = 0; .@i < .@amount; .@i++) + shovel_scriptItem(.@map$, rand(.@x1,.@x2), rand(.@y1,.@y2), getarg(rand2(getargcount()-6)+6)); + freeloop(false); + + .@wtc = getarraysize($@WBT_Random_id); // wtc - current + .@amount=.@wtb-.@wtc; + + //debugmes "WTA %d WTB %d. WTC %d, tries %d, amount %d and dbg %d", .@wta, .@wtb, .@wtc, .@tries, .@amount, .@dbgamm; + if (.@wtc >= .@wtb) + .@tries=0; + else + .@tries-=1; + } while (.@tries > 0); + debugmes "Scattered "+.@dbgamm+" items on "+.@map$+". Currently scattered: "+.@wtc; + return; + +} + +// Here we begin +- script Shovel -1,{ + function CheckDigLocation; + function AddDigRect; + function PlayerIsTired; + + + function Dig { + // First check: Did some player bury some TREASURE? O.o + getmapxy(.@map$, .@x, .@y, 0); + for (.@i = 0; .@i < getarraysize($WorldBuriedTreasures_id); .@i++) + { + if (!strcmp($WorldBuriedTreasures_map$[.@i], .@map$) && + $WorldBuriedTreasures_x[.@i] == .@x && + $WorldBuriedTreasures_y[.@i] == .@y) + { + .@id = $WorldBuriedTreasures_id[.@i]; + .@amount = $WorldBuriedTreasures_amount[.@i]; + + inventoryplace .@id, .@amount; + + deletearray $WorldBuriedTreasures_id[.@i], 1; + deletearray $WorldBuriedTreasures_amount[.@i], 1; + deletearray $WorldBuriedTreasures_map$[.@i], 1; + deletearray $WorldBuriedTreasures_x[.@i], 1; + deletearray $WorldBuriedTreasures_y[.@i], 1; + getitem .@id, .@amount; + mesn strcharinfo(0); + mesc l("You found something!"); + mesc l("It's @@ @@.", .@amount, getitemname(.@id)); + next; + closeclientdialog; + return 1; + } + } + // Second check: Perhaps here is a rare, random ore? + freeloop(true); + for (.@i = 0; .@i < getarraysize($@WBT_Random_id); .@i++) + { + if (!strcmp($@WBT_Random_map$[.@i], .@map$) && + $@WBT_Random_x[.@i] == .@x && + $@WBT_Random_y[.@i] == .@y) + { + .@id = $@WBT_Random_id[.@i]; + .@amount = $@WBT_Random_amount[.@i]; + + inventoryplace .@id, .@amount; + + deletearray $@WBT_Random_id[.@i], 1; + deletearray $@WBT_Random_amount[.@i], 1; + deletearray $@WBT_Random_map$[.@i], 1; + deletearray $@WBT_Random_x[.@i], 1; + deletearray $@WBT_Random_y[.@i], 1; + getitem .@id, .@amount; + mesn strcharinfo(0); + mesc l("You found something!"); + mesc l("It's @@ @@.", .@amount, getitemname(.@id)); + next; + closeclientdialog; + return 1; + } + } + freeloop(false); + dispbottom l("Sadly, you found nothing but dirt."); + return 0; + } + + function Bury { + narrator S_FIRST_BLANK_LINE | S_LAST_BLANK_LINE, l("What would you like to bury?"); + .@items$ = ""; + + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .@id = requestitem(); + + // If ID is invalid, there's not enough items, it is an Iron Shovel, it is bound = Cannot bury + // NOBODY bypass notrade check. (ITR_NONE is 0) + if (.@id < 1) close; + if (.@id < 1 || countitem(.@id) < 1 || .@id == IronShovel || .@id == SteelShovel || checkbound(.@id) || + (!getiteminfo(.@id, ITEMINFO_TRADE)) + ) { + @ShovelLastUsed = 0; + if (.@id == IronShovel || .@id == SteelShovel || checkbound(.@id)) + mesc l("You cannot bury this item!"); + else if (!getiteminfo(.@id, ITEMINFO_TRADE)) + mesc l("This item is too precious, you cannot part with it!"); + else + mesc l("You give up."); + close; + return; + } + + .@amount = 1; + if (countitem(.@id) > 1) { + narrator S_FIRST_BLANK_LINE | S_LAST_BLANK_LINE, l("Amount?"); + input .@amount, 0, countitem(.@id); + } + if (.@amount == 0 || .@amount > countitem(.@id)) { + close; + return; + } + + getmapxy(.@map$, .@x, .@y, 0); + delitem .@id, .@amount; + .@wtc = getarraysize($WorldBuriedTreasures_id); + $WorldBuriedTreasures_id[.@wtc] = .@id; + $WorldBuriedTreasures_amount[.@wtc] = .@amount; + $WorldBuriedTreasures_map$[.@wtc] = .@map$; + $WorldBuriedTreasures_x[.@wtc] = .@x; + $WorldBuriedTreasures_y[.@wtc] = .@y; + narrator S_FIRST_BLANK_LINE, l("You buried @@ @@.", .@amount, getitemname(.@id)); + close; + return 0; + } + + // Scope: ShovelQuests_ → x, y, map, func{tion} + function ShovelQuests { + getmapxy(.@map$, .@x, .@y, 0); + for (.@i = 0; .@i < getarraysize(ShovelQuests_func$); .@i++) + { + if (!strcmp(ShovelQuests_map$[.@i], .@map$) && + ShovelQuests_x[.@i] == .@x && + ShovelQuests_y[.@i] == .@y) + { + .@func$ = ShovelQuests_func$[.@i]; + deletearray ShovelQuests_func$[.@i], 1; + deletearray ShovelQuests_map$[.@i], 1; + deletearray ShovelQuests_x[.@i], 1; + deletearray ShovelQuests_y[.@i], 1; + callfunc(.@func$); + return 1; + } + } + return 0; + } + +OnDig: + if (!CheckDigLocation()) { + dispbottom l("I can't use the shovel here."); + end; + } + if (PlayerIsTired()) + end; + if (!ShovelQuests()) + Dig(); + end; + +OnBury: + if (!CheckDigLocation()) { + dispbottom l("I can't use the shovel here."); + end; + } + if (PlayerIsTired()) + end; + Bury(); + end; + +function CheckDigLocation { + getmapxy(.@map$, .@x, .@y, 0); + for (.@i = 0; .@i < getarraysize(.WorldDigRect_Map$); .@i++) + { + if (!strcmp(.WorldDigRect_Map$[.@i], .@map$) && + .WorldDigRect_x1[.@i] <= .@x && + .WorldDigRect_x2[.@i] >= .@x && + .WorldDigRect_y1[.@i] <= .@y && + .WorldDigRect_y2[.@i] >= .@y) + { + return 1; + } + } + return 0; +} + +function AddDigRect { + if (getargcount() < 5) { + debugmes "usage: AddDigRect(map$,x1,y1,x2,y2)"; + return 0; + } + .@map$ = str(getarg(0)); + .@x1 = getarg(1); + .@y1 = getarg(2); + .@x2 = getarg(3); + .@y2 = getarg(4); + .@size = getarraysize(.WorldDigRect_Map$); + .WorldDigRect_Map$[.@size] = .@map$; + .WorldDigRect_x1[.@size] = .@x1; + .WorldDigRect_y1[.@size] = .@y1; + .WorldDigRect_x2[.@size] = .@x2; + .WorldDigRect_y2[.@size] = .@y2; + return 1; +} + +function PlayerIsTired { + /* + // GMs can do this in an unrestricted way + if (is_gm()) + return 0; + */ + // I changed the rule to: No restriction on Aeros! (GM or not) (May be laggy) + if (getmap() == "001-1") + return 0; + + .@tick = gettimetick(2); + .@playertick = .PlayerTiredTime - (readparam(bStr)/5) - (readparam(bVit)/10); + + // Dig and Bury more with Steel Shovel + if (countitem(SteelShovel)) { + .@playertick-=(.PlayerTiredTime*2)/3; + } + + if (@ShovelLastUsed + max(5, .@playertick) > .@tick) { + .@time$=FuzzyTime(@ShovelLastUsed + max(5, .@playertick)); + dispbottom lg("You are exhausted, you should rest @@.", "You are exhausted, you should rest @@.", .@time$); + return 1; + } + @ShovelLastUsed = .@tick; + return 0; +} + +OnInit: + // Define constants + .PlayerTiredTime = 25; + + // You can bury & dig on all mines + AddDigRect("007-1", 20, 20, 180, 190); + AddDigRect("011-1", 20, 20, 180, 180); + AddDigRect("015-1", 20, 20, 180, 180); + AddDigRect("032-2", 20, 20, 180, 180); + + // Aeros can be used too (for events) + AddDigRect("001-1", 20, 20, 342, 158); + + // LoF Areas + AddDigRect("018-1", 20, 20, 80, 80); + +OnHour00: + // Clear random treasure + deletearray $@WBT_Random_id; + deletearray $@WBT_Random_amount; + deletearray $@WBT_Random_map$; + deletearray $@WBT_Random_x; + deletearray $@WBT_Random_y; + + + // Scatter Treasure. + // There are 25600 possible cells, and about 60% of them are collisions. + // As we don't prevent treasure from falling on collision, it is pretty high. + // If two treasures fall on same place, the previous treasure will be ignored. + // Theoretical chance of uncovering a treasure on an attempt is 0.12% to 0.70% + // 2019-05-27: Doubled ammount of treasures. New rates should be 0.24% ~ 1.40% + shovel_scatter("007-1", 20, 20, 180, 180, rand(60,360), + TreasureKey,CoinBag,TreasureKey,SulfurPowder,Coal,EarthPowder, + IronOre,CopperOre,LeadOre,TinOre,SilverOre,GoldOre,PlatinumOre,IridiumOre,TitaniumOre, + Diamond,Ruby,Emerald,Sapphire,Topaz,Amethyst, + CursedAmmoBox,ThornAmmoBox,SacredBullet); + shovel_scatter("011-1", 20, 20, 180, 180, rand(60,360), + TreasureKey,CoinBag,TreasureKey,SulfurPowder,Coal,EarthPowder, + IronOre,CopperOre,LeadOre,TinOre,SilverOre,GoldOre,PlatinumOre,IridiumOre,TitaniumOre, + Diamond,Ruby,Emerald,Sapphire,Topaz,Amethyst, + CursedAmmoBox,ThornAmmoBox,SacredBullet); + shovel_scatter("015-1", 20, 20, 180, 180, rand(60,360), + TreasureKey,CoinBag,TreasureKey,SulfurPowder,Coal,EarthPowder, + IronOre,CopperOre,LeadOre,TinOre,SilverOre,GoldOre,PlatinumOre,IridiumOre,TitaniumOre, + Diamond,Ruby,Emerald,Sapphire,Topaz,Amethyst, + CursedAmmoBox,ThornAmmoBox,SacredBullet); + + // Extra burried treasure (25~65 over 3600 tiles: aprox. 0.70% to 1.80%) + // New Rate: 1.40% ~ 3.60% since 2019-05-27 + shovel_scatter("018-1", 20, 20, 80, 80, rand(50,130), + TreasureKey,CoinBag,TreasureKey,SulfurPowder,Coal,EarthPowder, + IronOre,CopperOre,LeadOre,TinOre,SilverOre,GoldOre,PlatinumOre,IridiumOre,TitaniumOre, + Diamond,Ruby,Emerald,Sapphire,Topaz,Amethyst, + CursedAmmoBox,ThornAmmoBox,SacredBullet); + + // Aeros can't be forgotten, but only during Thanksgiving + if ($EVENT$ == "Thanksgiving") { + shovel_scatter("001-1", 171, 20, 340, 160, rand(160,360), + TreasureKey,CoinBag,TreasureKey,SulfurPowder,Coal,EarthPowder, + IronOre,CopperOre,LeadOre,TinOre,SilverOre,GoldOre,PlatinumOre,IridiumOre,TitaniumOre, + Diamond,Ruby,Emerald,Sapphire,Topaz,Amethyst); + shovel_scatter("001-1", 20, 20, 140, 140, rand(160,360), + TreasureKey,CoinBag,TreasureKey,SulfurPowder,Coal,EarthPowder, + IronOre,CopperOre,LeadOre,TinOre,SilverOre,GoldOre,PlatinumOre,IridiumOre,TitaniumOre, + Diamond,Ruby,Emerald,Sapphire,Topaz,Amethyst); + } + end; + +} + +function script shovel_addquest { + if (getargcount() < 4) + { + debugmes "usage: shovel_addquest(map$,x,y,func$)"; + return 0; + } + .@map$ = str(getarg(0)); + .@x = getarg(1); + .@y = getarg(2); + .@func$ = str(getarg(3)); + .@size = getarraysize(ShovelQuests_func$); + ShovelQuests_func$[.@size] = .@func$; + ShovelQuests_map$[.@size] = .@map$; + ShovelQuests_x[.@size] = .@x; + ShovelQuests_y[.@size] = .@y; + return 1; +} + +function script shovel_adddigrect { + if (getargcount() < 5) + { + debugmes "usage: shovel_adddigrect(map$,x1,y1,x2,y2)"; + return 0; + } + .@map$ = str(getarg(0)); + .@x1 = getarg(1); + .@y1 = getarg(2); + .@x2 = getarg(3); + .@y2 = getarg(4); + .@size = getarraysize(getvariableofnpc(.WorldDigRect_Map$, strnpcinfo(3))); + set getvariableofnpc(.WorldDigRect_Map$[.@size], strnpcinfo(3)), .@map$; + set getvariableofnpc(.WorldDigRect_x1[.@size], strnpcinfo(3)), .@x1; + set getvariableofnpc(.WorldDigRect_y1[.@size], strnpcinfo(3)), .@y1; + set getvariableofnpc(.WorldDigRect_x2[.@size], strnpcinfo(3)), .@x2; + set getvariableofnpc(.WorldDigRect_y2[.@size], strnpcinfo(3)), .@y2; + return 1; +} + +function script shovel_getcity { + .@a$=getarg(0); + + // else is not required (return prevails) + if (.@a$ == "007-1") + return l("Tulimshar Mines"); + if (.@a$ == "011-1") + return l("Halinarzo Mines"); + if (.@a$ == "015-1") + return l("Hurnscald Mines"); + if (.@a$ == "018-1") + return l("Sincerity Island"); + if (.@a$ == "032-2") + return l("Tree Maze (Past Tulimshar)"); + + return .@a$; +} + +// [Treasure Map] functions + +function script shovel_randomtreasure { + .@id=any(TreasureKey,CoinBag,TreasureKey,CoinBag,CoinBag,GoldPieces, + Diamond,Ruby,Emerald,Sapphire,Topaz,Amethyst, + StrangeCoin, CasinoCoins, MercBoxA, AncientBlueprint); + // Legacy Tulimshar has different items + if (getmap() == "032-2") .@id = any(CoinBag, TreasureKey, GoldPieces, + Diamond,Ruby,Emerald, + Sapphire,Topaz,Amethyst, + StrangeCoin, MercBoxB, AncientBlueprint, + TulimWarpCrystal, AlchemyBlueprintC, + EarthPowder, MercBoxC, StrangeCoin); + if (rand2(400) == 136) .@id = EarthPowder; // 0.25% chance + if (rand2(2000) == 337) .@id = PirateBandana; // 0.05% chance + delitem TreasureMap, 1; + .@amount=any(1,1,2)+(getmap() == "032-2" ? 1 : 0); + if (.@id == TreasureKey || .@id == CoinBag || .@id == StrangeCoin) + .@amount+=any(0,1,0,1,2); + if (.@id == CasinoCoins) + .@amount+=rand2(0,8); + if (.@id == MercBoxA || .@id == AncientBlueprint || + .@id == GoldPieces || .@id == EarthPowder || + .@id == PirateBandana || .@id == MercBoxB || + .@id == MercBoxC || .@id == TulimWarpCrystal || + .@id == AlchemyBlueprintC) + .@amount=1; + getitem .@id, .@amount; + ShovelQuests_AssignedMAP$=""; + ShovelQuests_AssignedX=0; + ShovelQuests_AssignedY=0; + + mesn strcharinfo(0); + mesc l("You found something!"); + mesc l("It's @@ @@.", .@amount, getitemname(.@id)); + next; + closeclientdialog; + return; +} + +function script shovel_genrandtreasure { + if (getq(LoFQuest_EPISODE) >= 16) + .@m$=any("007-1", "011-1", "015-1", "018-1", "032-2", + "007-1", "011-1", "015-1", "018-1"); + else + .@m$=any("007-1", "011-1", "015-1", "018-1"); + // Dangerous + do { + .@x=rand2(20, getmapinfo(MAPINFO_SIZE_X, .@m$)-20); + .@y=rand2(20, getmapinfo(MAPINFO_SIZE_Y, .@m$)-20); + } while (!checkcell(.@m$, .@x, .@y, cell_chkpass)); + + // Success + if (checkcell(.@m$, .@x, .@y, cell_chkpass)) { + shovel_addquest(.@m$, .@x, .@y, "shovel_randomtreasure"); + ShovelQuests_AssignedMAP$=shovel_getcity(.@m$); + ShovelQuests_AssignedX=.@x; + ShovelQuests_AssignedY=.@y; + } + return; +} + +// [Dungeon Map] functions + +function script shovel_randomdungeon { + // If you don't have the map, stop + delitem DungeonMap, 1; + + // Determine the dungeon size + if (ShovelQuests_DungeonMAP$ == "032-2" && REBIRTH) + .@mz = (MAZE_SIZE_X | MAZE_SIZE_A); + else if (ShovelQuests_DungeonMAP$ == "032-2") + .@mz = (MAZE_SIZE_G | MAZE_SIZE_X); + else if (REBIRTH) + .@mz = (MAZE_SIZE_M | MAZE_SIZE_G); + else + .@mz = (MAZE_SIZE_S | MAZE_SIZE_M); + + // Create the Maze + CreateMaze(IOT_CHAR, .@mz); + MazeMobs((BaseLevel*8/10), false, rand2(20, 24)); + + // Prepare the chest array + if (.@mz & MAZE_SIZE_A) + array_push(.@mob, SupremeChest); + if (.@mz & MAZE_SIZE_X) + array_push(.@mob, PrismChest); + if (.@mz & MAZE_SIZE_G) + array_push(.@mob, GoldenChest); + if (.@mz & MAZE_SIZE_M) + array_push(.@mob, SilverChest); + if (.@mz & MAZE_SIZE_S) + array_push(.@mob, BronzeChest); + + // Spawn & Configure the boss monster + .@mx=getmapinfo(MAPINFO_SIZE_X, MAZE_MAP$)-20; + .@my=getmapinfo(MAPINFO_SIZE_Y, MAZE_MAP$)-20; + .@mob=areamonster(MAZE_MAP$, .@mx/2, .@my/2, .@mx, .@my, "Treasure Chest", any_of(.@mob), 1, "Emergency Exit::OnTalk"); + + InitMaze(4800, true, true); // 1h30, random start, with treasure chest + + // Cleanup old variables and exit + ShovelQuests_DungeonMAP$=""; + ShovelQuests_DungeonX=0; + ShovelQuests_DungeonY=0; + dispbottom l("Now, to find the treasure chest!"); + closeclientdialog; + return; +} + +function script shovel_genranddungeon { + if (getq(LoFQuest_EPISODE) >= 16) + .@m$=any("007-1", "011-1", "015-1", "018-1", "032-2", + "007-1", "011-1", "015-1", "018-1"); + else + .@m$=any("007-1", "011-1", "015-1", "018-1"); + // Dangerous + do { + .@x=rand2(20, getmapinfo(MAPINFO_SIZE_X, .@m$)-20); + .@y=rand2(20, getmapinfo(MAPINFO_SIZE_Y, .@m$)-20); + } while (!checkcell(.@m$, .@x, .@y, cell_chkpass)); + + // Success + if (checkcell(.@m$, .@x, .@y, cell_chkpass)) { + shovel_addquest(.@m$, .@x, .@y, "shovel_randomdungeon"); + ShovelQuests_DungeonMAP$=shovel_getcity(.@m$); + ShovelQuests_DungeonX=.@x; + ShovelQuests_DungeonY=.@y; + } + return; +} + diff --git a/npc/items/teleporter.txt b/npc/items/teleporter.txt new file mode 100644 index 0000000..b7e311b --- /dev/null +++ b/npc/items/teleporter.txt @@ -0,0 +1,54 @@ +// TMW2 scripts. +// Authors: +// Pyndragon +// Jesusalva +// Description: +// Warp Crystal + +- script Warp Crystal NPC_HIDDEN,{ + close; + +function Cooldown { + mesn; + mesc l("Successive warps cause time-space distortions and are thus not allowed."); + mesc l("You can use it again in %s.", FuzzyTime(TELEPORTER_TIME)); + getitem @itemid, 1; + close; +} + +function ReturnItem { + getitem @itemid, 1; + end; +} + +OnUse: + // Receives @dest$ + if (TELEPORTER_TIME > gettimetick(2)) + Cooldown(); + if (BaseLevel < 20) { + dispbottom l("The might contained in this curious object is too powerful. You have to be at least level 20 to harness it."); + ReturnItem(); + } + // TODO: Are you already at target point? + + // The chance to break is always at least 20% + // Begins at 100% and each second will subtract 0.01% + // It will never go below 15%, which happens after x seconds + .@timet=limit(0, gettimetick(2)-TELEPORTER_TIME, 3600); + .@prop=.@timet*2777/1000; // Make it range from 0~10000 + .@adj_breakrate=limit(1500, .@prop, 9500 ); + //debugmes "Adjusted break ratio: %d", .@adj_breakrate; + if (rand(0,10000) > .@adj_breakrate) + getitem @itemid, 1; + else + getitem BrokenWarpCrystal, 1; + + // Apply Cooldown, same variable as LoF Teleporter, cancel ship travels + TELEPORTER_TIME=gettimetick(2)+300; + @timer_navio_running=0; + + // Save new location and warp you there + EnterTown(@dest$); + ReturnTown(); + end; +} diff --git a/npc/items/valentine.txt b/npc/items/valentine.txt new file mode 100644 index 0000000..79a0a96 --- /dev/null +++ b/npc/items/valentine.txt @@ -0,0 +1,31 @@ +// TMW-2 script. +// Author: +// Jesusalva +// Description: +// Valentine eating + +// Eats chocolate, returns bad ones if requested +// EatValentineChocolate() +function script EatValentineChocolate { + if ($EVENT$ != "Valentine") { + dispbottom l("Past due date."); + } else if (#VALENTINE_OPENED >= #VALENTINE_RECEIVED) { + dispbottom l("This is not meant for me."); + getitem BoxOfChocolates, 1; + } else { + .@pts=1; + if (GSET_VALENTINE_EATALL) { + .@pts=(#VALENTINE_RECEIVED-#VALENTINE_OPENED); + delitem BoxOfChocolates, .@pts-1; + } + // I know technically this is wrong grammar, but I want fixed width + #VALENTINE_OPENED=#VALENTINE_OPENED+.@pts; + #VALENTINE_POINTS=#VALENTINE_POINTS+.@pts; + if (rand2(0,1)) + dispbottom l("It's tasty ^.^"); + else + dispbottom l("It's yummy ^.^"); + } + return; +} + diff --git a/npc/magic/abizit.txt b/npc/magic/abizit.txt new file mode 100644 index 0000000..8c38a1d --- /dev/null +++ b/npc/magic/abizit.txt @@ -0,0 +1,33 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: - +// +// Abizit() reports your magic power + +- script sk#abizit 32767,{ + end; + +OnCall: + // Must have magic + if (!MAGIC_LVL) + end; + + // Check cooldown + if (@abizit_at > gettimetick(2)) { + dispbottom l("Skill is in cooldown for @@.", FuzzyTime(@abizit_at)); + end; + } + + ShowAbizit(false); + + // set cooldown + @abizit_at=gettimetick(2); + @abizit_at=@abizit_at+4; + end; + +OnInit: + bindatcmd "sk-abizit", "sk#abizit::OnCall", 0, 100, 0; + bindatcmd "abizit", "sk#abizit::OnCall", 0, 100, 0; + end; +} diff --git a/npc/magic/demure.txt b/npc/magic/demure.txt new file mode 100644 index 0000000..c885e1b --- /dev/null +++ b/npc/magic/demure.txt @@ -0,0 +1,154 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_DEMURE +// +// Let yourself rage and go out of control destroy everything around you +// Doubles attack speed but nullifies evade complety for 30 seconds. +// Each level grants +30s and +1% evade and +5% attack speed. +// After level 1, evade penalty/cooldown raises in 1s per additional level +// +// Cooldown is cast duration +/* + SC_INCAGI: 190 + SC_INCVIT: 191 + SC_INCINT: 192 + SC_INCDEX: 193 + SC_INCLUK: 194 + SC_INCHIT: 195 + SC_INCHITRATE: 196 + SC_INCFLEE: 197 + SC_INCFLEERATE: 198 + SC_INCMHPRATE: 199 + SC_INCMSPRATE: 200 + SC_INCATKRATE: 201 + SC_INCMATKRATE: 202 + SC_INCDEFRATE: 203 + + SC_WALKSPEED + SC_INVINCIBLE (?) + SC_MANA_PLUS (?) + SC_INCMHP + SC_INCMSP + + // The basic ones + SC_STONE: 0 + SC_FREEZE: 1 + SC_STUN: 2 + SC_SLEEP: 3 + SC_POISON: 4 + SC_CURSE: 5 + SC_SILENCE: 6 + SC_CONFUSION: 7 + SC_BLIND: 8 + SC_BLOODING: 9 + SC_DPOISON: 10 + SC_FEAR: 11 + SC_COLD: 12 + SC_BURNING: 13 + SC_DEEP_SLEEP: 14 + SC_PROVOKE: 20 + SC_ENDURE: 21 + + // Original potion ones + SC_ATTHASTE_POTION1: 55 + SC_ATTHASTE_POTION2: 56 + SC_ATTHASTE_POTION3: 57 + SC_ATTHASTE_INFINITY: 58 + SC_MOVHASTE_HORSE: 59 + SC_MOVHASTE_INFINITY: 60 + SC_PLUSATTACKPOWER: 61 + SC_PLUSMAGICPOWER: 62 + + // Guild Skills + SC_INCALLSTATUS + SC_SLOWPOISON + SC_BENEDICTIO (?) + SC_GDSKILL_REGENERATION (?) + SC_ANGELUS: DEF+ + SC_BLESSING: STR/DEX/INT+ + SC_IMPOSITIO: Wpn ATK+ + SC_FORTUNE: CRIT+ + + // Guild & Party skills + SC_KAIZEL: Auto Revive + SC_MAXIMIZEPOWER: Always at full power + SC_MAGNIFICAT: MP Regen × 2 + + // Tested Newer ones + SC_RICHMANKIM: 167 (Target EXP+) + + // Untested Newer ones + SC_CRITICALPERCENT: 262 + SC_RESIST_PROPERTY_WATER: 664 + SC_RESIST_PROPERTY_GROUND: 665 + SC_RESIST_PROPERTY_FIRE: 666 + SC_RESIST_PROPERTY_WIND: 667 + + » Create SC to boost skill power? + » Maybe SC to boost elemental damage + + // Potionable Skills (Scrolls?) + TMW2_OVERLOAD (Pierce Double Damage) + TMW2_MAGNUSHEAL (Area Healing based on MATK, affects homun) + TMW2_FIREARROW (A weak fire-based attack. Bleeds.) + TMW2_FROSTDIVER (A weak ice-based attack. Freeze.) + TMW2_LIGHTNINGBOLT (A strong wind-based attack) + TMW2_HOLYLIGHT (A strong holy-based attack vs tile) + TMW2_METEORSHOWER (A strong AoE earth-based attack, which also stuns) + TMW2_ARROWSHOWER (AoE attack, causes your damage to become arrows) + TMW2_ALLINONE (Xanthem's Gambit - Weak w/ shields, multi-ele multi-attack) + «Summons» + «Little Wonders - Recover/dispel ailments, but no heal» + + // Art of War: TMW2_HORIZONTALSLASH + TMW2_DIAGONALSLASH + TMW2_VERTICALSLASH + TMW2_STAB + TMW2_GRANDBLAST (possibly?) +*/ + +function script SK_Demure { + // non-melee weapons not allowed + if (getiteminfo(getequipid(EQI_HAND_R), ITEMINFO_RANGE) > 3) { + dispbottom l("Only melee weapons are permitted for use with this skill."); + end; + } + + // Determine length: 30s per skill level + .@length=getskilllv(TMW2_DEMURE)*30000; + .@effect=25+getskilllv(TMW2_DEMURE); + .@penalty=getskilllv(TMW2_DEMURE)-101; + .@malus=(getskilllv(TMW2_DEMURE)-1)*1000; + + // Add two SC_ effects: One to raise attack speed and other to drop evade + //sc_start(<effect type>, <ticks>, <value 1>{, <rate>, <flag>{, <GID>}}) + sc_start SC_ATTHASTE_POTION2, .@length, .@effect; + SC_Bonus(.@length, SC_INCHITRATE, .@effect); + SC_Bonus(.@length, SC_INCATKRATE, .@effect); + SC_Bonus((.@length+.@malus), SC_INCFLEERATE, .@penalty); + SC_Bonus((.@length+.@malus), SC_INCDEFRATE, .@penalty); + SC_Bonus((.@length+.@malus), SC_INCMHPRATE, .@penalty); + + // set cooldown and timer + @demure_at=gettimetick(2); + @demure_at=@demure_at+(.@length+.@malus)/1000; + addtimer(rand(786,1346), "sk#demure::OnTMW2DemureCheck"); + + // Get a few mana experience points (this is NOT used by Mana Stone) + GetManaExp(TMW2_DEMURE, rand(1,getskilllv(TMW2_DEMURE))); + return; +} + +- script sk#demure 32767,{ + end; + +// If you switch to a bow, you lose the effect at once and loop dies +OnTMW2DemureCheck: + if (@demure_at < gettimetick(2)) + end; + if (getiteminfo(getequipid(EQI_HAND_R), ITEMINFO_RANGE) > 3) + sc_start SC_ATTHASTE_POTION2, 100, -50; + else + addtimer(rand(786,1346), "sk#demure::OnTMW2DemureCheck"); + end; + +OnInit: + end; +} diff --git a/npc/magic/dragokin.txt b/npc/magic/dragokin.txt new file mode 100644 index 0000000..bf8344f --- /dev/null +++ b/npc/magic/dragokin.txt @@ -0,0 +1,42 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_DRAGOKIN +// +// Summons a Green Dragon because they are cool :3 +// +// Cooldown is cast duration + +function script SK_Dragokin { + // Blocked from summoning magic + if (alignment() < 0) + return; + + // Other requeriments: 5x Dragon Scale or Demure Axe + if (!(countitem(DragonScales) >= 4 || countitem(DemureAxe))) { + dispbottom l("You need 4x @@ to cast this skill.", getitemlink(DragonScales)); + return; + } + + // Setup + @sk=TMW2_DRAGOKIN; + @mp=50; + @amp=4; + + // Check if you have mana to cast + // MagicCheck(SkillID, Mana{, MP per level}) + if (!MagicCheck(@sk, @mp, @amp)) + return; + + // Destroy reagents + if (!countitem(DemureAxe)) + delitem DragonScales, 4; + + // set cooldown + @dragokin_at=gettimetick(2); + @dragokin_at=@dragokin_at+16; + + // Summon the dragons and give Mana EXP + SK_summon(GreenDragon, 5, 5); + return; +} diff --git a/npc/magic/drops.txt b/npc/magic/drops.txt new file mode 100644 index 0000000..fcfc57e --- /dev/null +++ b/npc/magic/drops.txt @@ -0,0 +1,65 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_DROPS +// +// Realm of Drops - A passive skill which drastically improves drop rates + +function script SK_drops { + .@mobId=getarg(0, killedrid); + if (getskilllv(TMW2_DROPS) <= 0) + return; + if (.@mobId <= 0) + return; + if (@skdrop[.@mobId] < 0) + return; + + // Keep in mind that it is reset on logout + @skdrop[.@mobId]+=1; + .@lv=getmonsterinfo(.@mobId, MOB_LV); + .@min=10-getskilllv(TMW2_DROPS)+(.@lv/10); + + // Maybe we are in condition for the bonus drop + if (@skdrop[.@mobId] % .@min == 0) { + // This creates .@item and .@rate with same index + deletearray($@MobDrop_item); + deletearray($@MobDrop_rate); + getmobdrops(.@mobId); + .@count = $@MobDrop_count; + copyarray(.@item[0], $@MobDrop_item[0], .@count); + copyarray(.@rate[0], $@MobDrop_rate[0], .@count); + + // .@total => sum of all drop rates + // .@array => The real array for relative_array_random() + .@total = 0; + .@array = -1; + for (.@i = 0; .@i < .@count; ++.@i) { + .@s = getarraysize(.@array); + array_push(.@array, .@item[.@i]); + array_push(.@array, .@rate[.@i]); + .@total+=.@rate[.@i]; + } + + // Now we determine if you can, or cannot, get a bonus drop + if (.@total < 500) { + if (.@total < 200) { + // Hard limit: 2% of total drop rate (-1 prevents execution) + @skdrop[.@mobId]=-1; + return; + } else { + // Soft limit: Half the efficiency + if (@skdrop[.@mobId] % (.@min*2) != 0) + return; + } + } + + // You can! So give you a random bonus drop with proper ponderation + .@drop = relative_array_random(.@array); + getmapxy(.@m$, .@x, .@y, 0); + makeitem(.@drop, 1, .@m$, .@x, .@y); + if ($@GM_OVERRIDE || debug) + debugmes("Realm of Drops: Created %d as bonus drop in (%d,%d) [TDR %d KL %d]", .@drop, .@x, .@y, .@total, @skdrop[.@mobId]); + } + return; +} + diff --git a/npc/magic/forget.txt b/npc/magic/forget.txt new file mode 100644 index 0000000..81e3cdf --- /dev/null +++ b/npc/magic/forget.txt @@ -0,0 +1,52 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: Forgetful NPCs +// Allows you to get rid of unwanted skills + +// ForgetfulNPC( NAME, SCHOOL ) +function script ForgetfulNPC { + .@n$=getarg(0); + .@school=getarg(1); + mesc l("Teaching %s your skills will make you both forget them.", .@n$), 1; + mesc l("Once your skill is forgotten, the Magic Skill Points used will be freed."), 1; + mesc l("But be careful: learning fees and research points WILL NOT be given back!"), 1; + do + { + next; + mesc l("Which skill will you FORGET permanently today?"), 1; + setarray .@forget$, l("Cancel"), "-1"; + freeloop(true); + for (.@i=0; .@i < getarraysize($@MSK_MAGIC) ; .@i++) { + .@sk=$@MSK_MAGIC[.@i]; + if (getskilllv(.@sk)) { + if ($@MSK_CLASS[.@sk] == .@school) { + array_push(.@forget$, getskillname(.@sk)); + array_push(.@forget$, str(.@sk)); + } + } + } + freeloop(false); + menuint2(.@forget$); + deletearray(.@forget$); + if (@menuret < 1) + close; + mes ""; + mesc l("Skill @@ will be permanently lost!", getskillname(@menuret)), 1; + mesc l("Continue anyway?"), 1; + if (askyesno() == ASK_YES) { + .@msp=0; + .@lv=$@MSK_MSPCOST[@menuret]+max(0, getskilllv(@menuret)-5); + skill @menuret, 0, 0; + MAGIC_PTS-=.@lv; + mesc l("Skill @@ has been lost", getskillname(@menuret)), 3; + mesc l("You recovered @@ magic skill points", .@lv), 3; + } else { + mesn l("%s, the Forgetful", .@n$); + mesq l("Uh, what?"); + } + } while (@menuret); + return; +} + + diff --git a/npc/magic/guild.txt b/npc/magic/guild.txt new file mode 100644 index 0000000..e538b71 --- /dev/null +++ b/npc/magic/guild.txt @@ -0,0 +1,166 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_GD +// +// Guild Magic +// Reserved SCs: SC_INCALLSTATUS SC_ANGELUS SC_BLESSING SC_IMPOSITIO SC_KAIZEL SC_MAGNIFICAT +// For testing: SC_TWOHANDQUICKEN +// BROKEN: SC_ASSNCROS + +/* +SC_ENCHANTPOISON +Endows a single target's equipped weapon with the Poison property temporarily. This skill also gives the chance of leaving enemies poisoned while physically attacking. + +SC_POISONREACT +Gives a 50% chance of autocasting Level 5 Envenom when the user is physically attacked for a set number of autocasts. If the user is struck with a Nature property attack, the user will retaliate with a stronger physical attack, which has a 50% chance of leaving the attacker poisoned. This skill will end after a set duration, reaching the autocast limit or performing one poison counter. + +SC_SLOWPOISON +Stops the HP drain from the Poison status effect that affects a single target. It does not nullify the defense drop from the forementioned status effect. (no params) + +SC_SUFFRAGIUM +Shortens the Variable Cast Time of a single target's next skill. (15%) + +SC_BENEDICTIO +Blesses a targeted location to endow the armor of all players within the area of effect with the Holy property. (no params) + +SC_KYRIE <sc3: hits/durability> +Creates a protective barrier on a single target that blocks every form of physical damage until its durability wears off or expires. Its durability is a portion of the target's Max HP. This skill cannot be used in conjunction with Assumptio. + +*SC_GLORIA +Temporarily boosts LUK by 30 to the user and party members. + +SC_LEXAETERNA +Weakens a single target so it can take double damage from the next incoming attack. Healing, misses, status effects and the retaliation from Shield Reflect do not trigger the effect. (PVP or used versus monsters. no params) + +SC_MAXIMIZEPOWER +Alters the damage variance of the equipped weapon to inflict the maximum of its damage. Maintaining this skill active will drain SP. (args = sp drain?) + +SC_ENERGYCOAT +Coats the caster with spiritual energy to buffer all incoming damage temporarily. The more remaining SP the caster has, the more damage is buffered and the more SP is drained. (no params?) + +SC_ASSUMPTIO +Places a temporary buff on a single target that doubles their Hard Defense and Hard Magic Defense. This skill cannot be used in conjunction with Kyrie Eleison and Kaite. + +// SC_GDSKILL_BATTLEORDER +-- SC_LEADERSHIP (aura) +-- SC_GLORYWOUNDS (aura) +-- SC_SOULCOLD (aura) +-- SC_HAWKEYES (aura) + +SC_HUMMING +Increase HIT of players in the area of effect by +2 per SkillLV. The accuracy rate increased by this skill is affected by Dancing Lesson skill level and DEX of the caster. (integer value) + +*/ + +// GD_allboost ( lv{, target} ) +// Increases all stats +function script GD_allboost { + .@lv=getarg(0, @skillLv); + .@t$=getarg(1, "filter_sameguild"); + // range, time, SC, BL, power, filter + areasc(3+.@lv, 40000+(5000*.@lv), SC_INCALLSTATUS, BL_PC, .@lv, .@t$); + return; +} + + +// GD_regeneration ( lv ) +// 12x12 HP and MP recovery +function script GD_regenerating { + .@lv=getarg(0, @skillLv); + .@t$=getarg(1, "filter_sameguild"); + // range, time, SC, BL, power, filter + areasc(12, 8000+(1000*.@lv), SC_GDSKILL_REGENERATION, BL_PC, 15+(5*.@lv), .@t$); + return; +} + + +// GD_defboost ( lv ) +// Places a temporary buff on the user and all party members in a 14x14 area around the user that increases Soft Defense. (VIT def) +function script GD_defboost { + .@lv=getarg(0, @skillLv); + .@t$=getarg(1, "filter_sameguild"); + // range, time, SC, BL, power, filter + areasc(4+.@lv, 40000+(5000*.@lv), SC_ANGELUS, BL_PC, .@lv*4, .@t$); + return; +} + + +// GD_atkboost ( lv ) +// Places a temporary buff on a single target that increases STR, DEX and INT. (The damage dealing stats) +function script GD_atkboost { + .@lv=getarg(0, @skillLv); + .@t$=getarg(1, "filter_sameguild"); + // range, time, SC, BL, power, filter + areasc(4+.@lv, 40000+(5000*.@lv), SC_BLESSING, BL_PC, .@lv*2, .@t$); + return; +} + + +// GD_atkboost2 ( lv ) +// Blesses a single target's weapon to increase its attack power +function script GD_atkboost2 { + .@lv=getarg(0, @skillLv); + .@t$=getarg(1, "filter_sameguild"); + // range, time, SC, BL, power, filter + areasc(4+.@lv, 40000+(5000*.@lv), SC_IMPOSITIO, BL_PC, 5+(5*.@lv), .@t$); + return; +} + + +// GD_critboost ( lv ) +// Boost the critical hit rate +function script GD_critboost { + .@lv=getarg(0, @skillLv); + .@t$=getarg(1, "filter_sameguild"); + // range, time, SC, BL, 1, power, filter + areasc(1+.@lv, 30000+(5000*.@lv), SC_FORTUNE, BL_PC, 3*.@lv, .@t$); + return; +} + + +// GD_autorevive ( lv ) +// Automatically revive guild mates once they die - VERY IMPORTANT SKILL +// Will not stick on you - if you die, YOU ARE DEAD. +// After being resurrected Kaizel is dispelled but you have a 2 seconds lasting Kyrie Eleison (Absolute shield) buff. +function script GD_autorevive { + .@lv=getarg(0, @skillLv); + // range, time, SC, BL, 1, power, filter + areasc(2+.@lv, 25000+10000*.@lv, SC_KAIZEL, BL_PC, 10*.@lv, "filter_sameguildnotyou"); + return; +} + + + + + + + + + + + + + + + + +// SK_maximizepower ( lv ) +// Damage always at max +function script SK_maximizepower { + .@lv=getarg(0, @skillLv); + // range, time, SC, BL, power, filter + areasc(1+.@lv, 60000*.@lv, SC_MAXIMIZEPOWER, BL_PC, 1, "filter_sameguildorparty"); + return; +} + + +// SK_spregen ( lv ) +// Temporarily doubles the SP Recovery rate of the user. +function script SK_spregen { + .@lv=getarg(0, @skillLv); + // range, time, SC, BL, 1, power, filter + areasc(.@lv, 20000+(10000*.@lv), SC_MAGNIFICAT, BL_PC, 1, "filter_sameguildorparty"); + return; +} + diff --git a/npc/magic/mpregen.txt b/npc/magic/mpregen.txt new file mode 100644 index 0000000..dc8ad2b --- /dev/null +++ b/npc/magic/mpregen.txt @@ -0,0 +1,57 @@ +// TMW2 script +// Author: Jesusalva <jesusalva@tmw2.org> +// +// Magic Script: - +// +// mpgen to make mana from HP + +function script SK_mpregen { + // Convert HP to mana (20% HP - 1) (To prevent 5 casts from killing) + .@basehp=(MaxHp/5)-1; + // How much MP is that worth? + // Well, 400HP:120MP so base formula is 4:1 + // Let's have a 50% penalty, so, 6:1 + .@lv=getskilllv(TMW2_MPREGEN); + .@ratio=max(40, 60-.@lv); + .@mpheal=.@basehp*.@ratio/100; + heal -.@basehp, .@mpheal; + // Temporarily block healing and regeneration skills + SC_Bonus(1+.@lv, SC_HALT_REGENERATION, 1); + return; +} + +function script SK_transfermp { + .@mp = Sp; + .@me = getcharid(3); + .@tg = getarg(0, @skillTarget); + detachrid(); + attachrid(.@tg); + .@mo = MaxSp - Sp; + // Heal will be the smallest from: + // Current MP or Missing Mp + .@vl = min(.@mo, .@mp); + Sp += .@vl; + detachrid(); + attachrid(.@me); + Sp -= .@vl; + return; +} + +/* +- script sk#mpgen 32767,{ + end; + +OnCall: + // Must have magic + if (!MAGIC_LVL) + end; + + unitskilluseid(getcharid(3), TMW2_MPREGEN, 1, getcharid(3)); + end; + +OnInit: + bindatcmd "sk-mpgen", "sk#mpgen::OnCall", 0, 100, 0; + end; +} +*/ + diff --git a/npc/magic/ovh.txt b/npc/magic/ovh.txt new file mode 100644 index 0000000..476be29 --- /dev/null +++ b/npc/magic/ovh.txt @@ -0,0 +1,13 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_OVHFIRE +// +// Burns down the user to a toast, but they'll survive, because they are awesome. + +function script SK_OVHFire { + specialeffect FX_HELLBLAZE, AREA, getcharid(3); + return; +} + + diff --git a/npc/magic/parum.txt b/npc/magic/parum.txt new file mode 100644 index 0000000..3fa73bc --- /dev/null +++ b/npc/magic/parum.txt @@ -0,0 +1,56 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_PARUM +// +// Attempts to transmutate Raw Logs. +// May create arrows, Mouboo Figurines, Warped Logs. +// With enough ManaExp may create WoodenLog. +// Is not powerful enough to create overly complex stuff like Wooden Sword, Wooden Shield or Wooden Bow +// Final item is random +// TODO: Using too much transmutation magic may have dire consequences! Like, uh, transmutating your head! +// Nah, it is probably just propaganda... I hope. ¬.¬ + +function script SK_parum { + // Create the stuff based on MAGIC_EXP + .@r=rand(1,41); + // Each 2 mexp reduces chance to get a fail + if (.@r < 42-(MAGIC_EXP/2)) { + getitem WarpedLog, 1; + } else { + // Proeficiency makes more likely to finish it + if (.@r > 30-abizit()) + getitem MoubooFigurine, 1; + else if (.@r > 20-abizit() && MAGIC_EXP > 82) + getitem WoodenLog, 1; + else + getitem Arrow, .@r; + } + // I know, the code is not very sane. A number from 0 to 40 is cast. + // You will get lots of useless Warped Logs until you have 82 MExp. + // If you do not get a Warped Log, you have 25% chances of getting the + // Mouboo figurine. The other will be arrows, unless you hit the 82 MExp value + // which will add 25% chances to get a Wooden Log too. These values are estimate. + + // Get a few mana experience points (this is NOT used by Mana Stone) + GetManaExp(TMW2_PARUM, rand2(1,3)); + return; +} + +- script sk#parum 32767,{ + end; + +/* +OnFriendlyDeath: + emote 4; + end; +*/ + +OnCall: + // Deprecated + dispbottom l("The usage of @sk-commands was deprecated"); + end; + +OnInit: + end; +} diff --git a/npc/magic/plantkingdom.txt b/npc/magic/plantkingdom.txt new file mode 100644 index 0000000..d6765fb --- /dev/null +++ b/npc/magic/plantkingdom.txt @@ -0,0 +1,62 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_PLANTKINGDOM +// +// Summons plants + +- script sk#plantkingdom 32767,{ + end; + +OnCall: + // Blocked from summoning magic + if (alignment() < 0) + return; + + // Other requeriments: 2x Root + if (countitem(Root) < 2) { + dispbottom l("You need 2x @@ to cast this skill.", getitemlink(Root)); + end; + } + + // Check cooldown + if (@plantkingdom_at > gettimetick(2)) { + dispbottom l("Skill is in cooldown for @@.", FuzzyTime(@plantkingdom_at)); + end; + } + + // Setup + @sk=TMW2_PLANTKINGDOM; + @mp=30; + @amp=3; + + // Check if you have mana to cast + // MagicCheck(SkillID, Mana{, MP per level}) + if (!MagicCheck(@sk, @mp, @amp)) + end; + + // Destroy reagents + delitem Root, 2; + + // set cooldown + @plantkingdom_at=gettimetick(2); + @plantkingdom_at=@plantkingdom_at+54; + + // As usual, magic profeciency affects + if (rand(1,6) < abizit()+1) { + // Summon Magic + // SummonMagic(SkillID, MobID{, SkillLevelPerMob=2{, Level Override}}) + SummonMagic(@sk, any(ShadowPlant, AlizarinPlant, CobaltPlant, MauvePlant, GambogePlant, PlushroomField, ChagashroomField, MananaTree, CrocoTree, AlizarinPlant, CobaltPlant, MauvePlant, GambogePlant), 2, MAGIC_LVL+getskilllv(@sk)-1); + } else { + dispbottom l("The spell fails!"); + } + + // Get 3~4 mana experience point (this is NOT used by Mana Stone) + GetManaExp(@sk, rand2(3,4)); + + end; + +OnInit: + bindatcmd "sk-plantkingdom", "sk#plantkingdom::OnCall", 0, 100, 0; + end; +} diff --git a/npc/magic/revive.txt b/npc/magic/revive.txt new file mode 100644 index 0000000..96620c7 --- /dev/null +++ b/npc/magic/revive.txt @@ -0,0 +1,52 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_REVIVE +// Magic Script: TMW2_RESSURECT +// +// Skill to revive players +// TODO: Reimburse EXP? + +// revive target (level, target) +function script SK_resurrect { + .@lv=getarg(0); + .@tg=getarg(1); + .@me=getcharid(3); + attachrid(.@tg); + if (ispcdead()) { + recovery(.@tg); + percentheal 100, 0; + percentheal -(100 - 10 * .@lv), 0; + } + detachrid(); + attachrid(.@me); + return; +} + +// revives getarg(0) +function script SK_revive { + .@target=getarg(0); + if (getunittype(.@target) != UNITTYPE_PC) { + dispbottom l("This skill can only be used on players!"); + return; + } + recovery(.@target); + return; +} + +// revive in getarg(0) range from caster +function script SK_ressurect { + .@r=getarg(0); + getmapxy(.@m$, .@x, .@y, 0); + recovery(.@m$, .@x-.@r, .@y-.@r, .@x+.@r, .@y+.@r); + return; +} + +// revives the whole map [ULTIMATE] +function script SK_sanctum { + BaseLevel-=1; // Maybe EXP Gain -300% for a hour? + recovery(getmap()); + //maptimer AUTOREVIVE 15 minutes + return; +} + diff --git a/npc/magic/scrolls.txt b/npc/magic/scrolls.txt new file mode 100644 index 0000000..6df45c4 --- /dev/null +++ b/npc/magic/scrolls.txt @@ -0,0 +1,36 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Scroll System + +// SummonScroll(mob, life, {mob2, minlv}) +function script SummonScroll { + .@mob = getarg(0); + .@tim = getarg(1, 60); + .@mbb = getarg(2, 0); + .@blv = getarg(3, 0); + + // Depending on alignment status, invoke superior form + if (alignment_cansummon() && alignment() > 1 && BaseLevel > .@blv && .@mbb) { + .@mids = summon("Summoned Monster", .@mbb, .@tim); + } else { + .@mids = summon("Summoned Monster", .@mob, .@tim); + } + + // Reconfigure monster modes + .@opt=getunitdata(.@mids, UDT_MODE); + // Disable looting + if (.@opt & MD_LOOTER) + .@opt=.@opt^MD_LOOTER; + // All summons can suffer knockback + if (.@opt & MD_NOKNOCKBACK) + .@opt=.@opt^MD_NOKNOCKBACK; + // Strip summons from BOSS mode and immunity + if (.@opt & MD_BOSS) + .@opt=.@opt^MD_BOSS; + // Save new options + setunitdata(.@mids, UDT_MODE, .@opt); + return; +} + diff --git a/npc/magic/study.txt b/npc/magic/study.txt new file mode 100644 index 0000000..b747f8b --- /dev/null +++ b/npc/magic/study.txt @@ -0,0 +1,48 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_STUDY +// +// Skill to study a target monster +// Will report the monster exact current stats, and is part of research + +function script SK_study { + .@mobGD=getarg(0); + if (.@mobGD <= 0) + return; + + // We want monsters + if (getunittype(.@mobGD) != UNITTYPE_MOB) { + dispbottom l("This skill can only be used on monsters!"); + return; + } + .@mobID=getunitdata(.@mobGD, UDT_CLASS); + + // Research Points + if (array_rfind(@study, .@mobGD) < 0) { + .@mult=max(1, 11-getskilllv(TMW2_STUDY)); + .@rp=getmonsterinfo(.@mobID, MOB_LV)/.@mult; + array_push(@study, .@mobGD); + if (.@rp) { + MAGIC_RP+=.@rp; + dispbottom l("Research Points +%d", .@rp); + } + } + + // Report + dispbottom l("%s - %s/%s HP, %s/%s MP", + getmonsterinfo(.@mobID, MOB_NAME), + fnum(getunitdata(.@mobGD, UDT_HP)), + fnum(getunitdata(.@mobGD, UDT_MAXHP)), + fnum(getunitdata(.@mobGD, UDT_SP)), + fnum(getunitdata(.@mobGD, UDT_MAXSP))); + + // Truncate. + // We're saving the GID so it must be "big enough" + // But not too big so rfind() is not expensive + if (getarraysize(@study) > 99) { + deletearray(@study, 30); + } + return; +} + diff --git a/npc/magic/transmigration.txt b/npc/magic/transmigration.txt new file mode 100644 index 0000000..f16f854 --- /dev/null +++ b/npc/magic/transmigration.txt @@ -0,0 +1,330 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Magic Script: TMW2_TRANSMIGRATION +// +// Attempts to make stuff from other stuff +// This is actually referred as transmutation in human-readable forms, and +// transmigration in scripts. + +- script sk#mkpot 32767,{ + end; +OnCall: + // Check cooldown + if (@mkpot_at > gettimetick(2)) { + dispbottom l("Skill is in cooldown for @@.", FuzzyTime(@mkpot_at)); + end; + } + + // Check requisites + if (!MagicCheck(TMW2_TRANSMIGRATION, 215, -5)) + end; + + .@q=getq(General_Auldsbel); + + do { + mes ".:: " + l("Transmutation Skill") + " ::."; + mesc l("What will you transmute today?"); + mes ""; + menuint + rif(@transmemo, l("Repeat ")+getitemname(@transmemo)), @transmemo, + l("Crazy Rum"), CrazyRum, + l("Coal"), 9901, // 9901 cheat code + l("Mouboo Figurine"), MoubooFigurine, + rif(.@q >= 9, l("Downgrade Snake Skin")), SnakeSkin, + rif(.@q >= 9, l("Downgrade Snake Egg")), SnakeEgg, + rif(.@q >= 9, l("Downgrade Snake Tongue")), SnakeTongue, + rif(.@q >= 7, l("Downgrade Scorpion Stinger")), ScorpionStinger, + rif(.@q >= 7, l("Downgrade Scorpion Claw")), ScorpionClaw, + l("Downgrade Ore"), IronOre; + + mes ""; + .@itemid=(@menuret == 9901 ? Coal : @menuret); + mesc l("Transmutating @@ will require:", getitemlink(@menuret)); + + // Requeriments listing + switch (@menuret) { + case CrazyRum: + mesc l("* @@/@@ @@", countitem(Plushroom), 10, getitemlink(Plushroom)); + mesc l("* @@/@@ @@", countitem(Milk), 3, getitemlink(Milk)); + break; + case 9901: // This is coal + mesc l("* @@/@@ @@", countitem(WoodenLog), 5, getitemlink(WoodenLog)); + break; + case MoubooFigurine: + mesc l("* @@/@@ @@", countitem(WoodenLog), 1, getitemlink(WoodenLog)); + break; + case SnakeSkin: + menuint + l("Black Mamba Skin -> Mountain Snake Skin"), MountainSnakeSkin, + l("Mountain Snake Skin -> Snake Skin"), SnakeSkin, + l("Snake Skin -> Cave Snake Skin"), CaveSnakeSkin, + l("Cancel"); + break; + case SnakeEgg: + menuint + l("Black Mamba Egg -> Mountain Snake Egg"), MountainSnakeEgg, + l("Mountain Snake Egg -> Snake Egg"), SnakeEgg, + l("Snake Egg -> Cave Snake Egg"), CaveSnakeEgg, + l("Cancel"); + break; + case SnakeTongue: + menuint + l("Black Mamba Tongue -> Mountain Snake Tongue"), MountainSnakeTongue, + l("Mountain Snake Tongue -> Snake Tongue"), SnakeTongue, + l("Snake Tongue -> Cave Snake Tongue"), CaveSnakeTongue, + l("Cancel"); + break; + case ScorpionStinger: + menuint + l("Black Scorpion Stinger -> Red Scorpion Stinger"), RedScorpionStinger, + l("Red Scorpion Stinger -> Scorpion Stinger"), ScorpionStinger, + l("Cancel"); + break; + case ScorpionClaw: + menuint + l("Golden Scorpion Claw -> Black Scorpion Claw"), BlackScorpionClaw, + l("Black Scorpion Claw -> Red Scorpion Claw"), RedScorpionClaw, + l("Red Scorpion Claw -> Scorpion Claw"), ScorpionClaw, + l("Cancel"); + break; + case IronOre: + menuint + l("Platinum Ore -> Iridium Ore"), IridiumOre, + l("Iridium Ore -> Titanium Ore"), TitaniumOre, + l("Titanium Ore -> Lead Ore"), LeadOre, + l("Lead Ore -> Tin Ore"), TinOre, + l("Tin Ore -> Gold Ore"), GoldOre, + l("Gold Ore -> Silver Ore"), SilverOre, + l("Silver Ore -> Copper Ore"), CopperOre, + l("Copper Ore -> Iron Ore"), IronOre, + l("Iron Ore -> Coal"), Coal, + l("Cancel"), 0; + break; + default: + Exception("ERROR, INVALID TRANSMIGRATION OPTION", RB_DEFAULT|RB_SPEECH); @menuret=0; break; + } + // Confirmation + if (@menuret) { + next; + mesc l("Transmute?!"); + .@me=@menuret; + if (askyesno() == ASK_NO) + @menuret=0; + else + @menuret=.@me; + } + } while (!@menuret); + + // Close the dialog + closeclientdialog; + + // Check and Consume the reagents + switch (@menuret) { + case CrazyRum: + if (!transcheck(Plushroom, 10, Milk, 3)) { + dispbottom l("Not enough items!"); + end; + } + break; + case MoubooFigurine: + if (!transcheck(WoodenLog, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case 9901: // Coal cheat code + if (!transcheck(WoodenLog, 5)) { + dispbottom l("Not enough items!"); + end; + } + .@me=WoodenLog; + @menuret=Coal; + break; + // Snake Skin Chain + case MountainSnakeSkin: + if (!transcheck(BlackMambaSkin, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case SnakeSkin: + if (!transcheck(MountainSnakeSkin, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case CaveSnakeSkin: + if (!transcheck(SnakeSkin, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + // Snake Egg Chain + case MountainSnakeEgg: + if (!transcheck(BlackMambaEgg, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case SnakeEgg: + if (!transcheck(MountainSnakeEgg, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case CaveSnakeEgg: + if (!transcheck(SnakeEgg, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + // Snake Tongue Chain + case MountainSnakeTongue: + if (!transcheck(BlackMambaTongue, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case SnakeTongue: + if (!transcheck(MountainSnakeTongue, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case CaveSnakeTongue: + if (!transcheck(SnakeTongue, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + // Scorpion Stinger Chain + case RedScorpionStinger: + if (!transcheck(BlackScorpionStinger, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case ScorpionStinger: + if (!transcheck(RedScorpionStinger, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + // Scorpion Claw Chain + case BlackScorpionClaw: + if (!transcheck(GoldenScorpionClaw, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case RedScorpionClaw: + if (!transcheck(BlackScorpionClaw, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case ScorpionClaw: + if (!transcheck(RedScorpionClaw, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + // Ore Chain (the biggest one) + case IridiumOre: + if (!transcheck(PlatinumOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case TitaniumOre: + if (!transcheck(IridiumOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case LeadOre: + if (!transcheck(TitaniumOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case TinOre: + if (!transcheck(LeadOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case GoldOre: + if (!transcheck(TinOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case SilverOre: + if (!transcheck(GoldOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case CopperOre: + if (!transcheck(SilverOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case IronOre: + if (!transcheck(CopperOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + case Coal: + if (!transcheck(IronOre, 1)) { + dispbottom l("Not enough items!"); + end; + } + break; + + default: + Exception("ERROR, INVALID TRANSMIGRATION REAGENTS", RB_DEFAULT|RB_SPEECH|RB_ISFATAL); break; + } + + // Do the roll from 1 to 100 + // Base success chance is 0% + // Each abizit() point gives you 18% success rate (max 90%) + // Each skill level gives you 1% success rate (max 10%) + .@r=rand2(1, 100)-abizit()*18-getskilllv(TMW2_TRANSMIGRATION); + + // Backfire chance: 30% + if (.@r >= 70) { + dispbottom l("The spell backfires!"); + percentheal -(rand2(5, 15)), 0; + // Chance for nothing to happen + } else if (.@r >= 30) { + dispbottom l("Your reagents vanish into emptiness!"); + // Chance for you getting a junk item + } else if (.@r > 0) { + dispbottom l("Your spell takes a mind of its own and shapes in something else!"); + getitem any(WarpedLog, .@me), 1; + // You were successful + } else { + dispbottom l("*plim*"); + getitem @menuret, 1; + } + + // Store to memory + @transmemo=.@me; + + // set cooldown + @mkpot_at=gettimetick(2); + @mkpot_at=@mkpot_at+6; + + // Get a few mana experience points (this is NOT used by Mana Stone) + GetManaExp(TMW2_TRANSMIGRATION, rand2(4,9)); + end; + +OnInit: + bindatcmd "sk-trans", "sk#mkpot::OnCall", 0, 100, 0; + end; +} diff --git a/npc/re/scripts_main.conf b/npc/re/scripts_main.conf new file mode 100644 index 0000000..8574cb9 --- /dev/null +++ b/npc/re/scripts_main.conf @@ -0,0 +1,4 @@ +// Only files that are included here will be read by the server +npc_global_list: ( +@include "npc/scripts.conf" +) // npc_global_list diff --git a/npc/scripts.conf b/npc/scripts.conf new file mode 100644 index 0000000..e51c7e4 --- /dev/null +++ b/npc/scripts.conf @@ -0,0 +1,156 @@ +// This is the main script import file + +// Critical functions +"npc/functions/permissions.txt", +"npc/functions/string.txt", +"npc/functions/main.txt", +"npc/functions/array.txt", +"npc/functions/bitwise.txt", +"npc/functions/math.txt", +"npc/functions/vault.txt", +"npc/functions/util.txt", + +// General-purpose Framework functions +"npc/functions/input.txt", +"npc/functions/time.txt", +"npc/functions/timer.txt", +"npc/functions/seasons.txt", +"npc/functions/weather.txt", + +// These functions need to be loaded before everything else +"npc/functions/goodbye.txt", +"npc/functions/openbook.txt", + +// Important functions +"npc/functions/asklanguage.txt", +"npc/functions/game-rules.txt", +"npc/functions/inventoryplace.txt", +"npc/functions/scoreboards.txt", +"npc/functions/random-talk.txt", +"npc/items/rand_mp_heal.txt", +"npc/items/rand_sc_heal.txt", +"npc/items/legacy_heal.txt", +"npc/items/inc_sc_bonus.txt", +"npc/commands/kami.txt", +"npc/commands/event.txt", + +// config script +"npc/config/hairstyle_config.txt", +"npc/config/location.txt", +"npc/config/traps.txt", +"npc/config/magic.txt", +"npc/functions/maze.txt", + +// Misc functions +"npc/functions/aurora.txt", +"npc/functions/estate.txt", +"npc/functions/barber.txt", +"npc/functions/bank.txt", +"npc/functions/daily.txt", +"npc/functions/doors.txt", +"npc/functions/event.txt", +"npc/functions/fishing.txt", +"npc/functions/guild.txt", +"npc/functions/hammocks.txt", +"npc/functions/honor.txt", +"npc/functions/lockpicks.txt", +"npc/functions/marriage.txt", +"npc/functions/mobpoint.txt", +"npc/functions/mobhunter.txt", +"npc/functions/mounts.txt", +"npc/functions/news.txt", +"npc/functions/npcmove.txt", +"npc/functions/npcmovegraph.txt", +"npc/functions/nurse.txt", +"npc/functions/petsales.txt", +"npc/functions/politics.txt", +"npc/functions/refine.txt", +"npc/functions/resetstatus.txt", +"npc/functions/riddle.txt", +"npc/functions/shake.txt", +"npc/functions/shops.txt", +"npc/functions/siege.txt", +"npc/functions/soul_menhir.txt", +"npc/functions/teleporter.txt", +"npc/functions/treasure.txt", + +"npc/functions/filters.txt", + +// Item functions +"npc/items/alcohol.txt", +"npc/items/arcmage.txt", +"npc/items/books.txt", +"npc/items/croconut.txt", +"npc/items/emptybox.txt", +"npc/items/grenade.txt", +"npc/items/lofteleporter.txt", +"npc/items/maps.txt", +"npc/items/mercenary.txt", +"npc/items/miscrecipes.txt", +"npc/items/shovel.txt", +"npc/items/teleporter.txt", +"npc/items/valentine.txt", + +// Crafting System +"npc/craft/options.txt", +"npc/craft/alchemy.txt", +"npc/craft/smith.txt", +"npc/craft/tweak.txt", +"npc/craft/recipes.txt", +"npc/craft/price.txt", + +// custom atcommands +"npc/commands/music.txt", +"npc/commands/exp.txt", +"npc/commands/debug-look.txt", +"npc/commands/debug-quest.txt", +"npc/commands/debug.txt", +"npc/commands/discord.txt", +"npc/commands/grantpower.txt", +"npc/commands/ipcheck.txt", +"npc/commands/language.txt", +"npc/commands/mobinfo.txt", +"npc/commands/motd.txt", +"npc/commands/python.txt", +"npc/commands/rate-management.txt", +"npc/commands/resync.txt", +"npc/commands/rent.txt", +"npc/commands/scheduled-broadcasts.txt", +"npc/commands/shroom.txt", +"npc/commands/super-menu.txt", +"npc/commands/titulate.txt", +"npc/commands/warp.txt", +"npc/commands/welcome.txt", +"npc/commands/wgm.txt", +"npc/commands/ucp.txt", + +// Magic Commands +"npc/magic/abizit.txt", +"npc/magic/demure.txt", +"npc/magic/dragokin.txt", +"npc/magic/drops.txt", +"npc/magic/forget.txt", +"npc/magic/guild.txt", +"npc/magic/mpregen.txt", +"npc/magic/ovh.txt", +"npc/magic/parum.txt", +"npc/magic/plantkingdom.txt", +"npc/magic/revive.txt", +"npc/magic/study.txt", +"npc/magic/transmigration.txt", +"npc/magic/scrolls.txt", +"npc/magic/magic_blackbox.txt", + +// These are to be proccessed later +"npc/functions/clientversion.txt", +"npc/functions/estate2.txt", +"npc/functions/hub.txt", +"npc/functions/mkbot.txt", +"npc/functions/gmbot.txt", +"npc/functions/instances.txt", +"npc/functions/captcha.txt", +"npc/functions/dungeon.txt", +"npc/config/easteregg_blackbox.txt", + +// Maps specific scripts +@include "npc/_import.txt" diff --git a/npc/sec_pri/_import.txt b/npc/sec_pri/_import.txt new file mode 100644 index 0000000..085dd0a --- /dev/null +++ b/npc/sec_pri/_import.txt @@ -0,0 +1,3 @@ +// Map sec_pri: Jesusalva's Jail +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/sec_pri/mapflags.txt", diff --git a/npc/sec_pri/mapflags.txt b/npc/sec_pri/mapflags.txt new file mode 100644 index 0000000..4876bb5 --- /dev/null +++ b/npc/sec_pri/mapflags.txt @@ -0,0 +1,2 @@ +sec_pri mapflag zone Jail +botcheck mapflag zone Jail diff --git a/npc/soren-2/_import.txt b/npc/soren-2/_import.txt new file mode 100644 index 0000000..f1e16d2 --- /dev/null +++ b/npc/soren-2/_import.txt @@ -0,0 +1,5 @@ +// Map soren-2: Soren House +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/soren-2/_warps.txt", +"npc/soren-2/main.txt", +"npc/soren-2/mapflags.txt", diff --git a/npc/soren-2/_warps.txt b/npc/soren-2/_warps.txt new file mode 100644 index 0000000..0227141 --- /dev/null +++ b/npc/soren-2/_warps.txt @@ -0,0 +1,3 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map soren-2: Soren House warps +soren-2,32,37,0 warp #soren-2_32_37 0,0,soren,105,58 diff --git a/npc/soren-2/main.txt b/npc/soren-2/main.txt new file mode 100644 index 0000000..d732bf8 --- /dev/null +++ b/npc/soren-2/main.txt @@ -0,0 +1,238 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Celestia Yeti King's quest. This controls Soren's House Indoors. It will +// handle Lobby event, and is called by Soren's House script. +// It'll then spawn monsters and/or items, but with addtimer technology. +// Once all monsters are dead, and minimum time is over, a portal will show up. +// This later control is done by npctimer. The spawn logic continues, though. +// The addtimer will use compareandsetq, and the warp will use it too, so if +// you haven't spawned your quota, you won't be able to use the portal. +// +// The use of compareandsetq HurnscaldQuest_Celestia +// Ensures you're NOT capable of skipping to final stage in order to finish +// the quest. Nice attempt, but that won't work. The scripts will advance, +// but you'll stay at the same quest state, and when it's checked, you will +// be with bound hands. +// +// OnCheck: +// Everytime you kill a monster, this checks if all mobs are dead, and you +// have finished your share of spawns. Friends can really make your life +// harder. +// OnSanctum: +// This runs every once in a while, and may either spawn things, or drop +// items. ...Needless to say, if it spawn things, everyone on the room will +// get mad at you. Luck you, here's not PVP. +// +// One last thing. If you're way past your share, you'll get warnings about +// messing other players life. Simply use the warp, or leave the house and +// come back. If you don't, you'll be kicked from the game via @kick. +// Why? Because Jesusalva did this script. + +soren-2,38,29,0 script #SorenSanctum NPC_NO_SPRITE,0,0,{ + if (@sorensanctum == 0 && $@GM_OVERRIDE && is_admin()) + doevent("#SorenSanctum::OnStart"); + end; + +OnTouch: + if (@sorensanctum >= 5 && !mobcount(.map$, "#SorenSanctum::OnCheck")) { + /* + dispbottom l("Error, contact Jesusalva! Missing warp. Healing & Reseting temporaly."); + percentheal 100, 100;*/ + @sorensanctum=0; + compareandsetq HurnscaldQuest_Celestia, 4, 5; + warp "001-7", 30, 42; + } + end; + +OnCheck: + // This doesn't works (or didn't used to), maybe because the way it's called + if (!mobcount("soren-2", "#SorenSanctum::OnCheck")) + setnpcdisplay "#SorenSanctum", NPC_SUMMONING_CIRC; + else + setnpcdisplay "#SorenSanctum", NPC_NO_SPRITE; + // Repeat if possible + if (!playerattached()) + end; + if (getmap() == "soren-2") + addtimer2(10000, "#SorenSanctum::OnCheck"); + end; + +OnSanctum: + ++@sorensanctum; + if (ispcdead() || !isin("soren-2", 24, 27, 40, 36)) + end; + + // Decide if we'll spawn or add items. Previous failures are NOT considerated. + .@odds=rand(1, 10000); + .@amount=(rand2(0, 5)/2)+1; + .@lx=24; .@ly=27; + .@ux=40; .@uy=36; + + // Core Logic, Part 1 + // Decide rarity + + // 1%: Super Rare drop + // 9%: Rare Drop + // 20%: Normal Drop + // 40%: Normal Spawn + // 10%: Rare Spawn + // 19%: Drop + Spawn + // 1%: Super Rare Spawn + + if (.@odds <= 100 || .@odds >= 9900) { + .@bonus=2; + } else if (.@odds <= 1000 || (.@odds >= 7000 && .@odds < 8000)) { + .@bonus=1; + } + + // Generate which monster/item would be dropped, depending on your (un)luck at ODDS (FIXME - Mobs vs Player level) + .@mid=rand2(1,10)+.@bonus; + switch (.@mid) { + case 1: + .@monsterId = AngryRedScorpion ; break; + case 2: + .@monsterId = IceMaggot ; break; + case 3: + .@monsterId = SeaSlime ; break; + case 4: + .@monsterId = AlphaMouboo ; break; + case 5: + .@monsterId = LavaSlime ; break; + case 6: + .@monsterId = BlackScorpion ; break; + case 7: + .@monsterId = Snake ; break; + case 8: + .@monsterId = FallenGuard2 ; break; + case 9: + .@monsterId = FallenGuard1 ; break; + case 10: + .@monsterId = Moonshroom ; break; + default: + .@monsterId = Yeti ; break; + } + // Item Gen + .@mid=rand(1,10)+.@bonus; + switch (.@mid) { + case 1: + .@itemId = Acorn ; break; + case 2: + .@itemId = Bread ; break; + case 3: + .@itemId = Aquada ; break; + case 4: + .@itemId = StrengthPotion ; break; + case 5: + .@itemId = Plushroom ; break; + case 6: + .@itemId = Piberries ; break; + case 7: + .@itemId = Croconut ; break; + case 8: + .@itemId = Chagashroom ; break; + case 9: + .@itemId = Manana ; break; + case 10: + .@itemId = HastePotion ; break; + default: + .@itemId = rand(CopperOre, TitaniumOre) ; break; + } + + // Rare drop + if (rand(1,6900) == 1337) + .@itemId=RaidTrousers; + + // Core Logic, part 3 + // Make Items or Monsters + if (.@odds <= 3333) { + makeitem(.@itemId, 1, .map$, rand(.@lx, .@ux), rand(.@ly, .@uy)); + } else if (.@odds <= 7000 || .@odds > 9900) { + areamonster .map$, .@lx, .@ly, .@ux, .@uy, strmobinfo(1, .@monsterId), .@monsterId, .@amount; + } else { + areamonster .map$, .@lx, .@ly, .@ux, .@uy, strmobinfo(1, .@monsterId), .@monsterId, .@amount; + makeitem(.@itemId, 1, .map$, rand(.@lx, .@ux), rand(.@ly, .@uy)); + } + + // Revert sprite if needed + if (.@odds > 2000) { + setnpcdisplay .name$, NPC_NO_SPRITE; + } else if (!mobcount(.map$, "#SorenSanctum::OnCheck")) { + setnpcdisplay .name$, NPC_SUMMONING_CIRC; + } + + // Handle maximum overtime you can do here: 10 cycles, or 3m20s ~ 6m40s + if (@sorensanctum == 8) { + specialeffect(28); + dispbottom col(l("WARNING: Do not spend unecessary time on Soren's House, you'll be disconnected!"), 1); + percentheal -5, -20; + } + if (@sorensanctum == 9) { + specialeffect(28); + dispbottom col(l("FINAL WARNING: Do not spend unecessary time on Soren's House, you'll be disconnected!!"), 1); + percentheal -20, -100; + } + if (@sorensanctum >= 10) { + atcommand "@kick "+strcharinfo(0); + npctalk l("The power of Soren's House kicks @@ like a game master.", strcharinfo(0)); + } + + // Random 20~40 seconds before next cycle. (Minimum 100~200 seconds, or 1m40s~3m20s) + // If you're past minimum, it'll be a little slower to don't affect other players too much. + // These values are for single player. If there are more users, the timer will tick for longer. + if (@sorensanctum < 5) + addtimer(rand(15000, 35000)+getusers(1)*5000, "#SorenSanctum::OnSanctum"); + else + addtimer(rand(25000, 35000)+getusers(1)*5000, "#SorenSanctum::OnSanctum"); + + // Enforce the policy + addtimer2(10000, "#SorenSanctum::OnCheck"); + goto OnCheck; + + + + +OnStart: + // If you're not on stage 4, skip this. I could set here to stage 5, so this + // intro won't play again. But if it plays again, your progress is reset, and + // it is harder this way, so I'm not touching on quest state :> + if (getq(HurnscaldQuest_Celestia) != 4) + end; + addtimer(200, "#SorenSanctum::OnStep1"); // Allow client to update stuff + end; + +OnStep1: + // When we reach here, we have both a player attached, and the player was already warped. + // I don't know if you walked in and out the house, though. If you do, this will loop... + // Which is actually bad for you, as that will reset your progress!! + npctalk3 l("Well well well, @@, good job in reaching Soren's House.", strcharinfo(0)); + addtimer(3000, "#SorenSanctum::OnStep2"); + end; + +OnStep2: + //setnpcdisplay .name$, NPC_YETI_KING; + npctalk3 l("You done well in getting so far. I didn't thought you would make it."); + addtimer(3000, "#SorenSanctum::OnStep3"); + end; + +OnStep3: + npctalk3 l("The Cave Of Trials, and the Soren's Village... There's only one thing left."); + addtimer(3000, "#SorenSanctum::OnStep4"); + end; + +OnStep4: + npctalk3 l("...You still have not overcome his house."); + addtimer(3000, "#SorenSanctum::OnStep5"); + end; + +OnStep5: + //setnpcdisplay .name$, NPC_NO_SPRITE; + npctalk3 l("Defeat a few waves of Soren's House. Once you're done, this will become a warp. Hurry before it closes."); + @sorensanctum=0; + addtimer(5000, "#SorenSanctum::OnSanctum"); + end; + +OnInit: + end; +} diff --git a/npc/soren-2/mapflags.txt b/npc/soren-2/mapflags.txt new file mode 100644 index 0000000..c3091c0 --- /dev/null +++ b/npc/soren-2/mapflags.txt @@ -0,0 +1 @@ +soren-2 mapflag zone MMO diff --git a/npc/soren/_import.txt b/npc/soren/_import.txt new file mode 100644 index 0000000..7721a7c --- /dev/null +++ b/npc/soren/_import.txt @@ -0,0 +1,5 @@ +// Map soren: Soren's Village +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/soren/_mobs.txt", +"npc/soren/main.txt", +"npc/soren/mapflags.txt", diff --git a/npc/soren/_mobs.txt b/npc/soren/_mobs.txt new file mode 100644 index 0000000..79e4d5d --- /dev/null +++ b/npc/soren/_mobs.txt @@ -0,0 +1,8 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map soren: Soren's Village mobs +soren,122,104,102,81 monster Moonshroom 1069,14,60000,60000 +soren,102,84,102,81 monster Forain 1061,5,60000,60000 +soren,103,91,64,40 monster 4144's Tortuga 1004,1,6600000,600000 +soren,105,87,30,23 monster Snake 1122,4,15000,15000 +soren,109,57,12,5 monster Guardians Of Soren 1130,4,5000,10000 +soren,110,97,103,81 monster Angry Red Scorpion 1130,18,5000,10000 diff --git a/npc/soren/main.txt b/npc/soren/main.txt new file mode 100644 index 0000000..845c662 --- /dev/null +++ b/npc/soren/main.txt @@ -0,0 +1,531 @@ +// TMW2 Scripts +// Author: +// Jesusalva +// Description: +// Celestia Yeti King's quest. This controls Soren's House and the three keysigns. +// Key #1 +// Perform the rite at the Fountain +// Key #2 +// Perform the rite at the south lake +// Key #3 +// Perform the rite at the Button Area +// +// If the same player performs the rite on different places, HE'LL BE PENALIZED. +// Performing the rite spawn monsters, so watch out. + +// Notes: +// I had two options: +// getvariableofnpc(<variable>, "<npc name>") +// This way, each summon point would have .caster and .lifetime +// getvariableofpc(<variable>, <account id>{, <default value>}) +// This way, I could use @cast_places to control stuff. +// +// Obviously NPC variables was more sane. +// +// The use of compareandsetq HurnscaldQuest_Celestia +// Ensures you're NOT capable of skipping to final stage in order to finish +// the quest. Nice attempt, but that won't work. The scripts will advance, +// but you'll stay at the same quest state, and when it's checked, you will +// be with bound hands. + +soren,105,57,0 script Soren's House NPC_NO_SPRITE,0,0,{ + end; + +OnTouch: + .@st1=getvariableofnpc(.lifetime, "Soren's Fountain")-gettimetick(2); + .@st2=getvariableofnpc(.lifetime, "Soren's Lake")-gettimetick(2); + .@st3=getvariableofnpc(.lifetime, "Soren's Gizmo")-gettimetick(2); + if (.@st1 > 0 && .@st2 > 0 && .@st3 > 0) { + @soren_penalty=0; + compareandsetq HurnscaldQuest_Celestia, 3, 4; + doevent("#SorenSanctum::OnStart"); + warp "soren-2", 32, 36; + end; + } else { + mesn l("Soren's House Tutorial"); + mesc l("There's a strong magic barrier. We need to disarm it in order to enter there."); + mesc l("There are three singularities on this island. If I disarm more than one, I'll have a penalty."); + mesc l("I should have full mana before attempting to disarm one."); + mesc l("Also, if I move away from the singularity during disarm process, it'll be lost."); + mesc l("The singularities keep arming themselves up again, so I have roughly five minutes between first disarm and entering here."); + mesc l("We should split our team, and have someone to protect our backs. Otherwise, we might not do it."); + // Protip: stock Elixir Of Life if you need to do this quest with less than 3 team members + close; + } + end; + +// Some cleanup might be needed to don't raise difficulty infinitely +// So every day, at 03:23 AM, if no one is trying the quest, it'll get rid +// of the non-permanent monsters on Soren Village & Soren House. +// FIXME: It also clears up Groata Grotto +OnClock0323: + if (getareausers("soren") == 0 && + getareausers("soren-2") == 0 && + getareausers("001-6") == 0 && + getareausers("001-7") == 0) { + killmonster("soren", "all"); + killmonster("soren-2", "all"); + } + if ($@GROTTO && + getareausers("006-0") == 0 && + getareausers("006-3") == 0 && + getareausers("006-5") == 0) { + killmonster("006-5", "all"); + $@GROATA = 0; + $@GROTTO = 0; + enablenpc "#006-5_49_54"; + } + end; +} + + + + + +soren,105,92,0 script Soren's Fountain NPC_NO_SPRITE,{ + // Initial Checks + if (.lifetime > gettimetick(2)) { + npctalk l("This singularity will remain disarmed for @@ more!", FuzzyTime(.lifetime, 2, 2)); + end; + } + if (.st) { + npctalk l("A disarm process is already running."); + end; + } + + // Main menu + mesc l("Attempt to disarm the singularity?"); + if (askyesno() == ASK_YES) { + .casterId=getcharid(3); + .st=1; + npctalk l("@@ started disarm process. Please stand by.", strcharinfo(0)); + initnpctimer; + if (getvariableofnpc(.casterId, "Soren's Fountain") == .casterId) + @sorenp=10; + else + @sorenp=0; + } + close; + +// Waves (total: 6 waves + 1 optional) +OnTimer5000: +OnTimer10300: +OnTimer14000: +OnTimer18000: +OnTimer25000: +OnTimer28000: +OnTimer31000: + if (!attachrid(.casterId)) { + npctalk "Disarm process aborted: Disarmer is gone."; + stopnpctimer; .st=0; + end; + } + if (!reachable(.x, .y, .distance)) { + npctalk l("Disarm process aborted: Disarmer is out of reach."); + stopnpctimer; .st=0; + end; + } + if (Sp < MaxSp/100*15) { + npctalk l("Disarm process aborted: Insufficient mana to proceed."); + stopnpctimer; .st=0; + end; + } + if (ispcdead()) { + npctalk l("Disarm process aborted: Disarmer is dead."); + stopnpctimer; .st=0; + end; + } + + // Penalty Handler. + .@val=-2; + if (getvariableofnpc(.casterId, "Soren's Lake") == .casterId) + .@val=.@val-10; + if (getvariableofnpc(.casterId, "Soren's Gizmo") == .casterId) + .@val=.@val-10; + if (@sorenp) + .@val-=@sorenp; + + percentheal (.@val/2), -13+.@val; + + // Monster Gen + .@amount=rand2(.st/3+1, .st/2+1)+getareausers("soren", 12); + .@mid=rand2(1,3)+.st; + switch (.@mid) { + case 1: + case 2: + .@monsterId = CaveMaggot ; break; + case 3: + case 4: + .@monsterId = RedSlime ; break; + case 5: + case 6: + .@monsterId = LavaSlime ; break; + case 7: + case 8: + .@monsterId = any(Snake, GrassSnake, OldSnake, MountainSnake) ; break; + default: // case 9: + .@monsterId = any(Yeti, Yeti, MountainSnake) ; break; + } + // Item Gen + .@mid=rand(1,7)+.st; + switch (.@mid) { + case 1: + case 2: + .@itemId = Acorn ; break; + case 3: + case 4: + .@itemId = any(BugLeg, ChocolateMouboo) ; break; + case 5: + case 6: + .@itemId = OrangeCupcake ; break; + case 7: + case 8: + .@itemId = CherryCake ; break; + case 9: + case 10: + .@itemId = Chagashroom ; break; + case 11: + .@itemId = GingerBreadMan ; break; + case 12: + .@itemId = rand2(Diamond, Amethyst) ; break; + default: // case 13 + .@itemId = rand2(CopperOre, TitaniumOre) ; break; + } + // Defines + .@lx=.x-.distance; + .@ly=.y-.distance; + .@ux=.x+.distance; + .@uy=.y+.distance; + + // Core function + areamonster .map$, .@lx, .@ly, .@ux, .@uy, strmobinfo(1, .@monsterId), .@monsterId, .@amount; makeitem(.@itemId, 1, .map$, rand2(.@lx, .@ux), rand2(.@ly, .@uy)); ++.st; + specialeffect(54); + + // If we're done with waves + if (.st >= 7) { + getexp rand2(1, 100), rand2(1, 10); + .lifetime=gettimetick(2)+330+rand2(0, 60); + npctalk l("Disarmed with success for: @@", FuzzyTime(.lifetime, 2, 2)); + stopnpctimer; .st=0; + specialeffect(27); + } + end; + +OnInit: + .sex = G_OTHER; + .distance = 3; + + .casterId=0; // getcharid(0) → 3 is account number!! + .lifetime=0; // When will this gate expire (six minutes) (gettimetick(2) + (60*5)) + .st=0; // Status after started + + end; +} + + + + + + + + + + + + + + + + +soren,104,143,0 script Soren's Lake NPC_NO_SPRITE,{ + // Initial Checks + if (.lifetime > gettimetick(2)) { + npctalk l("This singularity will remain disarmed for @@ more!", FuzzyTime(.lifetime, 2, 2)); + end; + } + if (.st) { + npctalk l("A disarm process is already running."); + end; + } + + // Main menu + mesc l("Attempt to disarm the singularity?"); + if (askyesno() == ASK_YES) { + .casterId=getcharid(3); + .st=1; + npctalk l("@@ started disarm process. Please stand by.", strcharinfo(0)); + initnpctimer; + if (getvariableofnpc(.casterId, "Soren's Lake") == .casterId) + @sorenp=10; + else + @sorenp=0; + } + close; + +// Waves (total: 6 waves + 1 optional) +OnTimer4500: +OnTimer11300: +OnTimer17000: +OnTimer22000: +OnTimer26200: +OnTimer29100: +OnTimer30900: + if (!attachrid(.casterId)) { + npctalk "Disarm process aborted: Disarmer is gone."; + stopnpctimer; .st=0; + end; + } + if (!reachable(.x, .y, .distance)) { + npctalk l("Disarm process aborted: Disarmer is out of reach."); + stopnpctimer; .st=0; + end; + } + if (Sp < MaxSp/100*15) { + npctalk l("Disarm process aborted: Insufficient mana to proceed."); + stopnpctimer; .st=0; + end; + } + if (ispcdead()) { + npctalk l("Disarm process aborted: Disarmer is dead."); + stopnpctimer; .st=0; + end; + } + + // Penalty Handler. + .@val=-2; + if (getvariableofnpc(.casterId, "Soren's Fountain") == .casterId) + .@val=.@val-10; + if (getvariableofnpc(.casterId, "Soren's Gizmo") == .casterId) + .@val=.@val-10; + if (@sorenp) + .@val-=@sorenp; + + percentheal (.@val/2), -13+.@val; + + // Monster Gen. Lake spawns less + .@amount=rand2(.st/3+1, .st/2+1)+getareausers("soren", 12)-1; + .@mid=rand2(1,3)+.st; + switch (.@mid) { + case 1: + case 2: + .@monsterId = CaveMaggot ; break; + case 3: + case 4: + .@monsterId = RedSlime ; break; + case 5: + case 6: + .@monsterId = LavaSlime ; break; + case 7: + case 8: + .@monsterId = any(Snake, GrassSnake, OldSnake, MountainSnake) ; break; + default: // case 9: + .@monsterId = any(Yeti, Yeti, MountainSnake) ; break; + } + // Item Gen have a different mechanic here + .@mid=rand(.st, .st*2+1); + switch (.@mid) { + case 1: + case 2: + .@itemId = Acorn ; break; + case 3: + case 4: + .@itemId = any(BugLeg, ChocolateMouboo) ; break; + case 5: + case 6: + .@itemId = OrangeCupcake ; break; + case 7: + case 8: + .@itemId = CherryCake ; break; + case 9: + case 10: + .@itemId = Chagashroom ; break; + case 11: + .@itemId = HastePotion ; break; + case 12: + .@itemId = rand2(Diamond, Amethyst) ; break; + default: // case 13 + .@itemId = rand2(CopperOre, TitaniumOre) ; break; + } + // Defines for Lake are fixed on the same spot. + .@lx=104; .@ux=104; + .@ly=141; .@uy=141; + + // Core function + areamonster .map$, .@lx, .@ly, .@ux, .@uy, strmobinfo(1, .@monsterId), .@monsterId, .@amount; makeitem(.@itemId, 1, .map$, rand2(.@lx, .@ux), rand2(.@ly, .@uy)); ++.st; + + // If we're done with waves + if (.st >= 7) { + .lifetime=gettimetick(2)+330+rand2(0, 60); + npctalk l("Disarmed with success for: @@", FuzzyTime(.lifetime, 2, 2)); + stopnpctimer; .st=0; + } + end; + +OnInit: + .sex = G_OTHER; + .distance = 2; + + .casterId=""; // getcharid(3) → Account number!! + .lifetime=0; // When will this gate expire (five~six minutes) (gettimetick(2) + (60*5)) + .st=0; // Status after started + + end; +} + + + + + + + + + + + + + + + + + + +soren,107,37,0 script Soren's Gizmo NPC_NO_SPRITE,{ + // Initial Checks + if (.lifetime > gettimetick(2)) { + npctalk l("This singularity will remain disarmed for @@ more!", FuzzyTime(.lifetime, 2, 2)); + end; + } + if (.st) { + npctalk l("A disarm process is already running."); + end; + } + + // Main menu + mesc l("Attempt to disarm the singularity?"); + if (askyesno() == ASK_YES) { + .casterId=getcharid(3); + .st=1; + npctalk l("@@ started disarm process. Please stand by.", strcharinfo(0)); + initnpctimer; + if (getvariableofnpc(.casterId, "Soren's Gizmo") == .casterId) + @sorenp=10; + else + @sorenp=0; + } + close; + +// Waves (total: 6 waves + 1 optional) +OnTimer6200: +OnTimer9300: +OnTimer13900: +OnTimer17200: +OnTimer24500: +OnTimer27400: +OnTimer31200: + if (!attachrid(.casterId)) { + npctalk "Disarm process aborted: Disarmer is gone."; + stopnpctimer; .st=0; + end; + } + if (!reachable(.x, .y, .distance)) { + npctalk l("Disarm process aborted: Disarmer is out of reach."); + stopnpctimer; .st=0; + end; + } + if (Sp < MaxSp/100*15) { + npctalk l("Disarm process aborted: Insufficient mana to proceed."); + stopnpctimer; .st=0; + end; + } + if (ispcdead()) { + npctalk l("Disarm process aborted: Disarmer is dead."); + stopnpctimer; .st=0; + end; + } + + // Penalty Handler. + .@val=-2; + if (getvariableofnpc(.casterId, "Soren's Lake") == .casterId) + .@val=.@val-10; + if (getvariableofnpc(.casterId, "Soren's Fountain") == .casterId) + .@val=.@val-10; + if (@sorenp) + .@val-=@sorenp; + + percentheal (.@val/2), -13+.@val; + + // Monster Gen. Gizmo spawns more monsters because you can move more + .@amount=rand2(.st/3+1, .st/2+1)+getareausers("soren", 12)+1; + .@mid=rand2(1,3)+.st; + switch (.@mid) { + case 1: + case 2: + .@monsterId = CaveMaggot ; break; + case 3: + case 4: + .@monsterId = RedSlime ; break; + case 5: + case 6: + .@monsterId = LavaSlime ; break; + case 7: + case 8: + .@monsterId = any(Snake, GrassSnake, OldSnake, MountainSnake) ; break; + default: // case 9: + .@monsterId = any(Yeti, Yeti, MountainSnake) ; break; + } + // Item Gen + .@mid=rand(1,7)+.st; + switch (.@mid) { + case 1: + case 2: + .@itemId = Acorn ; break; + case 3: + case 4: + .@itemId = any(BugLeg, ChocolateMouboo) ; break; + case 5: + case 6: + .@itemId = OrangeCupcake ; break; + case 7: + case 8: + .@itemId = CherryCake ; break; + case 9: + case 10: + .@itemId = Chagashroom ; break; + case 11: + .@itemId = HastePotion ; break; + case 12: + .@itemId = rand2(Diamond, Amethyst) ; break; + default: // case 13 + .@itemId = rand2(CopperOre, TitaniumOre) ; break; + } + // Defines + .@lx=.x-.distance; + .@ly=.y-.distance; + .@ux=.x+.distance; + .@uy=.y+.distance; + + // Core function + areamonster .map$, .@lx, .@ly, .@ux, .@uy, strmobinfo(1, .@monsterId), .@monsterId, .@amount; makeitem(.@itemId, 1, .map$, rand2(.@lx, .@ux), rand2(.@ly, .@uy)); ++.st; + + // If we're done with waves + if (.st >= 7) { + .lifetime=gettimetick(2)+330+rand2(0, 60); + npctalk l("Disarmed with success for: @@", FuzzyTime(.lifetime, 2, 2)); + stopnpctimer; .st=0; + } + end; + + +OnInit: + .sex = G_OTHER; + .distance = 4; + + .casterId=""; // getcharid(3) → Account number!! + .lifetime=0; // When will this gate expire (five~six minutes) (gettimetick(2) + (60*5)) + .st=0; // Status after started + + end; +} + +029-0,143,120,0 duplicate(Guild Storage) Guild Storekeeper#FoS NPC_TERRY + diff --git a/npc/soren/mapflags.txt b/npc/soren/mapflags.txt new file mode 100644 index 0000000..f38cb43 --- /dev/null +++ b/npc/soren/mapflags.txt @@ -0,0 +1 @@ +soren mapflag zone MMO diff --git a/npc/test/_import.txt b/npc/test/_import.txt new file mode 100644 index 0000000..46209c4 --- /dev/null +++ b/npc/test/_import.txt @@ -0,0 +1,12 @@ +// Map test: test +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/test/_mobs.txt", +"npc/test/karim.txt", +"npc/test/mapflags.txt", +"npc/test/npc1.txt", +"npc/test/npc3.txt", +"npc/test/npc4.txt", +"npc/test/npc5.txt", +"npc/test/rock.txt", +"npc/test/test1.txt", +"npc/test/test2.txt", diff --git a/npc/test/_mobs.txt b/npc/test/_mobs.txt new file mode 100644 index 0000000..350a38f --- /dev/null +++ b/npc/test/_mobs.txt @@ -0,0 +1,17 @@ +// This file is generated automatically. All manually added changes will be removed when running the Converter. +// Map test: test mobs +test,38,12,1,1 monster Piou 1002,1,1000,1000 +test,38,12,1,1 monster Tortuga 1004,1,1000,1000 +test,44,12,1,1 monster Piou 1002,3,1000,1000 +test,48,10,1,1 monster Tortuga 1004,3,1000,1000 +test,48,10,0,0 monster Piou 1002,1,1000,1000 +test,48,10,0,0 monster Ratto 1005,1,1000,1000 +test,54,10,1,1 monster Piou 1002,3,1000,1000 +test,68,9,0,0 monster Piou 1002,3,1000,1000 +test,68,9,0,0 monster Ratto 1005,1,1000,1000 +test,68,9,1,1 monster Tortuga 1004,3,1000,1000 +test,76,10,1,1 monster Piou 1002,3,1000,1000 +test,76,10,1,1 monster Ratto 1005,1,1000,1000 +test,78,4,1,1 monster Ratto 1005,3,1000,1000 +test,87,93,1,1 monster Green 4144 1004,1,1000,1000 +test,87,81,1,1 monster Pink 4144 1004,1,1000,1000 diff --git a/npc/test/karim.txt b/npc/test/karim.txt new file mode 100644 index 0000000..c1b74d2 --- /dev/null +++ b/npc/test/karim.txt @@ -0,0 +1,153 @@ +// Evol scripts. +// Author: +// Reid +// Description: +// Blacksmith's assistant of Artis +// Variables: +// ArtisQuests_Enora +// Values: +// 0 Default. +// 1 BlackSmith quest delivered. +// 2 Chelios Quest given. +// 3 Chelios Quest done. +// 4 BlackSmith gave the sword. + +test,2,4,0 script karim NPC_PLAYER,{ + + .Item1 = RightCraftyWing; + .Item2 = LeftCraftyWing; + .ItemReward = FlightTalisman; + + function quest_beggining{ + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("my name is karim can you help me?."); + + setq Karim_Quest, 1; + return; + } + + function quest_left { + if (countitem(.Item2) > 0) + { + delitem .Item2, 1; + if (rand(10) == 1) + { + mesq l("Yay, it worked! You get a good wing."); + getitem .ItemReward, 1; + setq Karim_Quest, 2; + close; + } + else + { + mesq l("This one is useless! Give me another @@.", getitemlink(.Item2)); + return; + } + } + else + { + mesq l("You don't have any @@, are you mocking me?", getitemlink(.Item2)); + return ; + } + } + + function quest_right { + if (countitem(.Item1) > 0) + { + delitem .Item1, 1; + if (rand(5) == 1) + { + mesq l("Yay, it worked! You get a good wing."); + getitem .ItemReward, 1; + setq Karim_Quest, 2; + close; + } + else + { + mesq l("This one is useless! Give me another @@.", getitemlink(.Item1)); + next; + return; + } + } + else + { + mesq l("You don't have any @@, are you mocking me?", getitemlink(.Item1)); + return ; + } + } + + function quest_started { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Can you give me a @@ or a @@", getitemlink(.Item1), getitemlink(.Item2)); + + do + { + select + l("a Right Crafty Wing"), + l("a Left Crafty Wing"), + menuaction(l("Quit")); + + switch (@menu) + { + case 1: + quest_right; + break; + case 2: + quest_left; + break; + } + } while (@menu != 3); + return; + } + + function quest_completed { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Quest completed."); + return; + } + + function quest_restart { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("Quest restart."); + setq Karim_Quest, 0; + return; + } + + speech S_LAST_NEXT, l("Can you help me?"); + + do + { + .@karim = getq(Karim_Quest); + select + rif(.@karim == 2, lg("quest completed.")), + rif(.@karim == 1, l("Hello again can you give you give me some tentacles.")), + rif(.@karim == 0, l("Hello")), + l("Debug"), + menuaction(l("Quit")); + + switch (@menu) + { + case 1: + quest_completed; + break; + case 2: + quest_started; + break; + case 3: + quest_beggining; + break; + case 4: + quest_restart; + break; + } + } while (@menu != 5); + + closedialog; + goodbye; + close; + +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} + diff --git a/npc/test/mapflags.txt b/npc/test/mapflags.txt new file mode 100644 index 0000000..6f3199a --- /dev/null +++ b/npc/test/mapflags.txt @@ -0,0 +1,2 @@ +test mapflag mask 5 +test mapflag town diff --git a/npc/test/npc1.txt b/npc/test/npc1.txt new file mode 100644 index 0000000..002857d --- /dev/null +++ b/npc/test/npc1.txt @@ -0,0 +1,724 @@ +// Evol scripts. +// Author: +// 4144 +// Description: +// test npc1 + +test,10,10,0 script npc1#door NPC_HIDDEN,0,1,{ +OnTouch: + warp "test", 20, 20; + close; +OnUnTouch: + doevent "npc1::OnUnTouch"; +} + +test,10,10,0 script npc1 NPC_TEST1,3,3,{ + function areatest + { + mesn; + mes "getareausers(\"test\", 7, 7, 13, 13): " + getareausers("test", 7, 7, 13, 13); + mes "getareausers(7, 7, 13, 13): " + getareausers(7, 7, 13, 13); + mes "getareausers(\"test\"): " + getareausers("test"); + mes "getareausers(): " + getareausers(); + mes "getareausers(\"test\", 1): " + getareausers("test", 1); + mes "getareausers(1): " + getareausers(1); + next; + return; + } + +L_Start: + if (!is_admin()) { + mesc "Error 13: Permission Denied", 1; + close; + } + mes "Homunculus state: "+checkhomcall(); + switch (select( + "show area", + "map", + "pc", + "change npc direction", + "quest test", + "cart", + "items", + "chat", + "wall", + "craft", + "hashtable", + "instance", + "mercenary", + "npc", + "clan")) + { + case 1: + areatest; + break; + case 2: + while(1) + { + .@mask = getmapmask("test"); + mes "current mask: " + str(.@mask); + switch (select("show sand:show grass:show fog:hide fog:back")) + { + case 1: + addremovemapmask "test", 4, 8; + break; + case 2: + addremovemapmask "test", 8, 4; + break; + case 3: + addmapmask "test", 2; + break; + case 4: + removemapmask "test", 2; + break; + case 5: + goto L_Start; + break; + } + } + break; + + case 3: + while(1) + { + switch (select("change sex:setmount 0:setmount 1:setmount 2:test command:back")) + { + case 1: + changecharsex; + break; + case 2: + setmount 0; + break; + case 3: + setmount 1; + break; + case 4: + setmount 2; + break; + case 5: + clientcommand "emote 1"; + break; + case 6: + goto L_Start; + break; + } + } + break; + case 4: + for (.@f = 0; .@f < 9; .@f ++) + { + mes "" + .@f; + .dir = .@f; + next; + } + break; + case 5: + mes "current"; + .@val1 = getq(ShipQuests_Gugli); + .@val2 = getq2(ShipQuests_Gugli); + .@val3 = getq3(ShipQuests_Gugli); + .@valTime = getqtime(ShipQuests_Gugli); + mes "val1=" + .@val1; + mes "val2=" + .@val2; + mes "val3=" + .@val3; + mes "valTime=" + .@valTime; + .@val1 ++; + .@val2 ++; + .@val3 ++; + .@valTime ++; + next; + mes "test " + str(.@val1) + ", " + str(.@val2) + ", " + str(.@val3) + ", " + str(.@valTime); + setq ShipQuests_Gugli, .@val1, .@val2, .@val3, .@valTime; + next; + .@val1 = getq(ShipQuests_Gugli); + .@val2 = getq2(ShipQuests_Gugli); + .@val3 = getq3(ShipQuests_Gugli); + .@valTime = getqtime(ShipQuests_Gugli); + mes "val1=" + .@val1; + mes "val2=" + .@val2; + mes "val3=" + .@val3; + mes "valTime=" + .@valTime; + break; + case 6: + while(1) + { + switch (select("add cart:remove cart:back")) + { + case 1: + setcart 1; + break; + case 2: + setcart 0; + break; + case 3: + goto L_Start; + break; + } + } + break; + case 7: + while(1) + { + switch (select("refine", "rent", "bound", "named", "cards", "options", "back")) + { + case 1: + .@item = -1; + while(1) + { + switch (select("set item", "refine +1", "refine -1", "refine fail", "back")) + { + case 1: + .@item = requestitemindex(); + mes "Item index selected: " + str(.@item); + break; + case 2: + successrefindex .@item, 1; + break; + case 3: + downrefindex .@item, 1; + break; + case 4: + failedrefindex .@item; + break; + case 5: + goto L_Start; + break; + } + } + break; + case 2: + while(1) + { + switch (select("rent vneck for 10 seconds", + "rent vneck for 30 seconds", + "rent vneck for 1 minute", + "rent vneck for 5 minutes", + "back")) + { + case 1: + rentitem 1301, 10; + break; + case 2: + rentitem 1301, 30; + break; + case 3: + rentitem 1301, 60; + break; + case 4: + rentitem 1301, 300; + break; + case 5: + goto L_Start; + break; + } + } + break; + case 3: + while(1) + { + switch (select("get bound vneck", + "back")) + { + case 1: + getitembound2 1301, 1, 1, 0, 0, 0, 0, 0, 0, 1; + break; + case 2: + goto L_Start; + break; + } + } + break; + case 4: + while(1) + { + switch (select("get named vneck", + "back")) + { + case 1: + getnameditem 1301, "4144"; + break; + case 2: + goto L_Start; + break; + } + } + break; + case 5: + .@item = -1; + while(1) + { + switch (select("set item", + "remove cards success", + "fail, remove both", + "fail, remove card", + "fail, remove item", + "fail, safe", + "print all", + "remove 0", + "back")) + { + case 1: + .@item = requestitemindex(); + mes "Item index selected: " + str(.@item); + break; + case 2: + successremovecardsindex .@item; + break; + case 3: + failedremovecardsindex .@item, 0; + break; + case 4: + failedremovecardsindex .@item, 1; + break; + case 5: + failedremovecardsindex .@item, 2; + break; + case 6: + failedremovecardsindex .@item, 3; + break; + case 7: + mes "slots=" + str(MAX_SLOTS); + for (.@i = 0; .@i < MAX_SLOTS; .@i++) + { + mes "slot " + str(.@i) + " = " + str(getcardbyindex(.@item, .@i)); + } + mes "item options:"; + for (.@i = 0; .@i < 5; .@i ++) + { + mes sprintf("%d: Option: %d, Value: %d", .@i, getitemoptionidbyindex(.@item, .@i), getitemoptionvaluebyindex(.@item, .@i)); + } + break; + case 8: + removecardbyindex(.@item, 0); + break; + case 9: + goto L_Start; + break; + } + } + break; + case 6: + .@item = -1; + while(1) + { + switch (select("set item", "info", "set option 1", "remove options", "back")) + { + case 1: + .@item = requestitemindex(); + mes "Item index selected: " + str(.@item); + break; + case 2: + mes "Selected item options"; + for (.@i = 0; .@i < 5; .@i ++) + { + mes sprintf("%d: Option: %d, Value: %d", .@i, getitemoptionidbyindex(.@item, .@i), getitemoptionvaluebyindex(.@item, .@i)); + } + break; + case 3: + setitemoptionbyindex(.@item, 0, VAR_MAXHPAMOUNT, 200); + setitemoptionbyindex(.@item, 1, VAR_STRAMOUNT, 10); + setitemoptionbyindex(.@item, 2, VAR_VITAMOUNT, -5); + break; + case 4: + mes "Remove item options"; + for (.@i = 0; .@i < 5; .@i ++) + { + setitemoptionbyindex(.@item, .@i, 0, 0); + } + break; + case 5: + goto L_Start; + break; + } + } + break; + case 7: + goto L_Start; + break; + } + } + break; + case 8: + chatjoin .chat; + close; + case 9: + while(1) + { + switch (select("set wall 1:set wall 2:set wall 3:delete wall 1:delete wall 2:delete wall 3:back")) + { + case 1: + setcells "test", 14, 11, 17, 11, 3, "wall1"; + break; + case 2: + setcells "test", 14, 7, 21, 8, 1, "wall2"; + break; + case 3: + setcells "test", 13, 15, 13, 21, 3, "wall3"; + break; + case 4: + delcells "wall1"; + break; + case 5: + delcells "wall2"; + break; + case 6: + delcells "wall3"; + break; + case 7: + goto L_Start; + break; + } + } + break; + case 10: + while(1) + { + switch (select("technical:normal:back")) + { + case 1: + setskin "test2"; + .@var$ = requestcraft(9); + mes .@var$; + .@craft = initcraft(.@var$); + mes "craft id: " + str(.@craft); + mes "is craft valid: " + validatecraft(.@craft); + .@id = getcraftslotid(.@craft, 0); + .@amount = getcraftslotamount(.@craft, 0); + mes "first item id: " + .@id; + mes "first item name: " + getitemlink(.@id); + mes "first item amount: " + .@amount; + + .@id = getcraftslotid(.@craft, 1); + .@amount = getcraftslotamount(.@craft, 1); + mes "second item id: " + .@id; + mes "second item name: " + getitemlink(.@id); + mes "second item amount: " + .@amount; +// dumpcraft .@craft; + .@entry = findcraftentry(.@craft, 0); + mes "found craft entry: " + .@entry; + if (.@entry < 0) + { + mes "no craft recipe found"; + } + else + { + mes "use craft"; + usecraft .@craft; + mes "return code: " + getcraftcode(.@entry); + } + deletecraft .@craft; + setskin ""; + break; + case 2: + mes "put items what you want to craft"; + setskin "test2"; + .@var$ = requestcraft(9); + setskin ""; + .@craft = initcraft(.@var$); + if (.@craft < 0) + { + mes "Craft error."; + goto L_Start; + } + mes "lets see what you can get..."; + next; + if (!validatecraft(.@craft)) + { + mes "inventory was changed."; + goto L_Start; + } + .@entry = findcraftentry(.@craft, 0); + if (.@entry < 0) + { + mes "no craft recipe found"; + } + else + { + mes "found recipe"; + usecraft .@craft; + mes "return code: " + getcraftcode(.@entry); + } + deletecraft .@craft; + break; + case 3: + goto L_Start; + break; + } + } + break; + case 11: + switch(select("Create new hashtable", + "Use existing ID")) + { + case 1: + .@ht = htnew(); + mes "ID = " + .@ht; + break; + case 2: + mes "Input ID"; + input .@ht; + break; + } + .@defval = 0; + .@defval_str$ = ""; + .@newval = 0; + .@newval_str$ = ""; + .@key$ = ""; + + while(1) + { + switch (select("Set default string value", + "Set default number value", + "Get string", + "Get number", + "Put string", + "Put number", + "Show hashtable", + "Clear hashtable", + "Delete hashtable", + "Exit")) + { + case 1: + input .@defval_str$; + break; + case 2: + input .@defval; + break; + case 3: + mes "Input key:"; + input .@key$; + mes "htget(" + .@ht + ", \"" + .@key$ + "\") = " + htget(.@ht, .@key$); + mes "htget(" + .@ht + ", \"" + .@key$ + "\", \"" + .@defval_str$ + "\") = " + htget(.@ht, .@key$, .@defval_str$); + break; + case 4: + mes "Input key:"; + input .@key$; + mes "htget(" + .@ht + ", \"" + .@key$ + "\") = " + htget(.@ht, .@key$); + mes "htget(" + .@ht + ", \"" + .@key$ + "\", " + .@defval + ") = " + htget(.@ht, .@key$, .@defval); + break; + case 5: + mes "Input key:"; + input .@key$; + mes "Input value:"; + input .@newval_str$; + htput(.@ht, .@key$, .@newval_str$); + mes "htput(" + .@ht + ", \"" + .@key$ + "\", \"" + .@newval_str$ + "\")"; + mes "htget(" + .@ht + ", \"" + .@key$ + "\") = " + htget(.@ht, .@key$); + break; + case 6: + mes "Input key:"; + input .@key$; + mes "Input value:"; + input .@newval; + htput(.@ht, .@key$, .@newval); + mes "htput(" + .@ht + ", \"" + .@key$ + "\", " + .@newval + ")"; + mes "htget(" + .@ht + ", \"" + .@key$ + "\") = " + htget(.@ht, .@key$); + break; + case 7: + mes "size = " + htsize(.@ht); + .@hti = htiterator(.@ht); + for(.@key$ = htinextkey(.@hti); hticheck(.@hti); .@key$ = htinextkey(.@hti)) + mes "key = " + .@key$ + " value = " + htget(.@ht, .@key$); + htidelete(.@hti); + break; + case 8: + htclear(.@ht); + mes "htclear(" + .@ht + ")"; + break; + case 9: + htdelete(.@ht); + mes "htdelete(" + .@ht + ")"; + break; + case 10: + goto L_Start; + } // switch + } // while + break; + case 12: + while(1) + { + switch (select("create", "warp", "info", "warp back", "delete", "back")) + { + case 1: + if (.instid >= 0 && isinstance(.instid)) + { + mes "Error: test instance already created"; + break; + } +// .instid = instance_create("test@instance", 0, 0); + .instid = instance_create("test@instance", getcharid(3), IOT_CHAR); + if (.instid < 0) + { + mes "Error: creating instance"; + break; + } + mes "new instance id: " + str(.instid); +// .@instanceMapName$ = instance_attachmap("test", .instid, 1, "test@map1"); +// .@instanceMapName$ = instance_attachmap("test", .instid, 1); + .@instanceMapName$ = instance_attachmap("test", .instid, 0, "test@map1"); + if (.@instanceMapName$ == "") + { + mes "Error: instance attach map error"; + break; + } + mes "Attached instance map name: " + .@instanceMapName$; + instance_set_timeout(1000000, 1000000, .instid); + instance_init(.instid); + break; + case 2: + warp "test@map1", 12, 12; + break; + case 3: + mes "npc name: " + .name$; + mes "npc ext name: " + .extname$; + mes "npc id: " + .id; + mes "npc parent id: " + .parent; + mes "npc src id: " + .srcId; + if (instance_id() >= 0) + { + mes "npc1 in instance named: " + instance_npcname("npc1"); + } + else + { + mes "npc1 not in instance"; + } + break; + case 4: + warp "test", 12, 12; + break; + case 5: + if (.instid < 0) + { + mes "Error: test instance was not created"; + break; + } + instance_destroy(.instid); + break; + case 6: + goto L_Start; + break; + } + } + break; + case 13: + while(1) + { + switch (select("create mercenary for 10 seconds", "create mercenary for 1 min", "create mercenary for 10 min", "Andrei Sakar", "1205 for test", "back")) + { + case 1: + mercenary_create 1191, 10000; + break; + case 2: + mercenary_create 1191, 60000; + break; + case 3: + mercenary_create 1191, 600000; + break; + case 4: + mercenary_create 1192, 300000; + break; + case 5: + mercenary_create 1205, 300000; + break; + default: + goto L_Start; + break; + } + } + break; + case 14: + while(1) + { + switch (select("set hair 1", "set hair 0", "equip vneck", "unequip vneck", "back")) + { + case 1: + setunitdata(.@npcId, UDT_HAIRSTYLE, 5); + setunitdata(.@npcId, UDT_HAIRCOLOR, 17); + break; + case 2: + .@npcId = getnpcid("npc4"); + setunitdata(.@npcId, UDT_HAIRSTYLE, 0); + break; + case 3: + .@npcId = getnpcid("npc4"); + setunitdata(.@npcId, UDT_HEADTOP, 1301); + break; + case 4: + .@npcId = getnpcid("npc4"); + setunitdata(.@npcId, UDT_HEADTOP, 0); + break; + case 5: + goto L_Start; + break; + } + } + break; + case 15: + while(1) + { + switch (select( + "join sword clan", + "join arc wand clan", + "join golden mace clan", + "join crossbox clan", + "leave clan", + "back")) + { + case 1: + mes(str(clan_join(1))); + break; + case 2: + mes(str(clan_join(2))); + break; + case 3: + mes(str(clan_join(3))); + break; + case 4: + mes(str(clan_join(4))); + break; + case 5: + mes(str(clan_leave())); + break; + case 6: + goto L_Start; + break; + } + } + break; + } + + close; + +OnTouch: + doorTouch; + +OnUnTouch: + doorUnTouch; + +OnTimer340: + doorTimer; + +OnInit: + .sex = G_MALE; + .distance = 5; + .alwaysVisible = true; + waitingroom "test room", 2, "npc1::OnReadyCheck", 1; + // test instance id + .instid = -10; + if (.parent != 0) + { + setnpcdir 0; + } + end; + +OnWhisperGlobal: + mes "cmd: " + @whispervar0$; + close; + +OnReadyCheck: + debugmes "OnReadyCheck"; + $@bgid1 = waitingroom2bg("testbg", 10, 10, "bgnpc1::OnLogout","bgnpc1:OnDie"); + debugmes "bgid=" + str($@bgid1); + setbgteam $@bgid1, 1; + bg_warp $@bgid1, "testbg", 10, 10; +} diff --git a/npc/test/npc3.txt b/npc/test/npc3.txt new file mode 100644 index 0000000..82d5fe3 --- /dev/null +++ b/npc/test/npc3.txt @@ -0,0 +1,7 @@ +// Evol scripts. +// Author: +// 4144 +// Description: +// test npc3 + +test,25,10,0 cashshop npc3 NPC_SHOP_BAG,505:100,502:-1,513:-1,509:-1,2000:-1,1800:-1,3501:-1 diff --git a/npc/test/npc4.txt b/npc/test/npc4.txt new file mode 100644 index 0000000..b85d1bf --- /dev/null +++ b/npc/test/npc4.txt @@ -0,0 +1,30 @@ +// Evol scripts. +// Author: +// 4144 +// Description: +// test npc4 + +test,25,20,0 script npc4 NPC_PLAYER,{ + close; +OnTimer1000: + domovestep; +OnInit: + .walkmask = WALK_WATER; + initpath "move", 20, 20, + "sit", 0, 0, + "dir", 6, 0, + "move", 25, 20, + "emote", 3, 0, +// "class", 104, 0, + "speed", 100, 0, + "wait", 2, 0, + "move", 22, 10, + "rmove", 3, 3, +// "class", 801, 0, + "speed", 200, 0, + "wait", 1, 0, + "warp", 22, 30, + "goto", 0, 0; + initialmove; + initnpctimer; +} diff --git a/npc/test/npc5.txt b/npc/test/npc5.txt new file mode 100644 index 0000000..faa655e --- /dev/null +++ b/npc/test/npc5.txt @@ -0,0 +1,34 @@ +// Evol scripts. +// Author: +// Travolta +// Description: +// test npc5 + +function script npc5TestFunc { + npctalk getarg(0); + return 0; +} + +test,45,25,0 script npc5 NPC_PLAYER,{ + close; + +OnTimer1000: + dographmovestep; + +OnInit: + // .debug = 1; + initmovegraph "topleft", 45, 25, + "topright", 55, 25, + "bottomright", 55, 34, + "bottomleft", 45, 34; + + setmovegraphcmd "topleft", "topright", 30, "dir 0; wait 1", + "topleft", "bottomright", 20, "class 104", + "topleft", "bottomleft", 50, "emote 8; wait 1; warp bottomright", + "topright", "bottomleft", 1, "emote 3; wait 5", + "bottomleft", "topleft", 25, "dir 2; wait 1; call npc5TestFunc Hello=)", + "bottomleft", "bottomright", 10, "speed 40", + "bottomright", "bottomleft", 1, "class 801; speed 100; dir 0; emote 1; wait 4"; + firstmove "wait 1; speed 100; dir 4; emote 7"; + initnpctimer; +} diff --git a/npc/test/rock.txt b/npc/test/rock.txt new file mode 100644 index 0000000..4f769cd --- /dev/null +++ b/npc/test/rock.txt @@ -0,0 +1,178 @@ +// Evol scripts. +// Author: +// Reid +// Description: +// Blacksmith's assistant of Artis +// Variables: +// ArtisQuests_Enora +// Values: +// 0 Default. +// 1 BlackSmith quest delivered. +// 2 Chelios Quest given. +// 3 Chelios Quest done. +// 4 BlackSmith gave the sword. + +test,2,6,0 script rock NPC_PLAYER,{ + + function quest_play { + mes l("Before start witch item do you want to play"); + mes "##B" + l("Drag and drop an item from your inventory.") + "##b"; + + .Item = requestitem(); + if (.Item < 1) + { + mes l("You didn't add a item."); + close; + } + + if (countitem(.Item) < 1) + { + mes l("You don't have the item."); + close; + } + delitem .Item, 1; + goto quest_choose; + } + + function quest_game { + if (.choose == 1){ + mes l("you choose rock."); + next; + } + else if (.choose == 2){ + mes l("you choose scissors."); + next; + } + else { + mes l("you choose paper."); + next; + } + + .npcChoose = rand(3); + if (.npcChoose == 0) + { + mes l("the npc choose rock."); + next; + if (.choose == 1) + { + mes l("draw."); + goto quest_choose; + } + else if (.choose == 2) + { + mes l("you lose"); + close; + } + else if (.choose == 3) + { + mes l("you win"); + getitem .Item, 2; + close; + } + } + else if (.npcChoose == 1) + { + mes l("the npc choose scissors."); + next; + if (.choose == 2) + { + mes l("draw."); + goto quest_choose; + } + else if (.choose == 3) + { + mes l("you lose"); + close; + } + else if (.choose == 1) + { + mes l("you win @@",getitemlink(.Item)); + getitem .Item, 2; + close; + } + } + else if (.npcChoose == 2) + { + mes l("the npc choose paper."); + next; + if (.choose == 3) + { + mes l("draw."); + goto quest_choose; + } + else if (.choose == 1) + { + mes l("you lose"); + close; + } + else if (.choose == 2) + { + mes l("you win"); + getitem .Item, 2; + close; + } + } + close; + } + + + function quest_choose { + speech S_FIRST_BLANK_LINE | S_LAST_NEXT, + l("What do you choose?"); + next; + do + { + select + l("Rock"), + l("Scissors"), + l("Paper"); + + switch (@menu) + { + case 1: + .choose = 1; + quest_game; + break; + case 2: + .choose = 2; + quest_game; + break; + case 3: + .choose = 3; + quest_game; + break; + } + } while (@menu != 4); + close; + } + + if (!is_admin()) { + mesc "Error 13: Permission Denied", 1; + close; + } + + speech S_LAST_NEXT, l("Hello do you want to play rock scissors paper?"); + do + { + select + l("Hello"), + menuaction(l("Quit")); + + switch (@menu) + { + case 1: + quest_play; + break; + } + } while (@menu != 2); + + closedialog; + goodbye; + close; + +OnInit: + .sex = G_MALE; + .distance = 3; + end; +} + diff --git a/npc/test/test1.txt b/npc/test/test1.txt new file mode 100644 index 0000000..7297f54 --- /dev/null +++ b/npc/test/test1.txt @@ -0,0 +1,96 @@ +// Evol scripts. +// Author: +// 4144 +// Description: +// script tests + +function script test1function { + .var = .walkmask; + if (.var != 3) + mes "Error: testing test1 npc variables from function."; + .var = getvariableofnpc(.walkmask, "test1"); + if (.var != 3) + mes "Error: testing test1 npc variables from function."; + .var = getvariableofnpc(.walkmask, "test2"); + if (.var != 4) + mes "Error: testing test2 npc variables from function."; + + return; +} + +test,10,5,0 script test1 NPC_HAT_BOX,{ + mes "Npc vars testing..."; + .var = .walkmask; + if (.var != 3) + mes "Error: testing test1 npc variables."; + .var = getvariableofnpc(.walkmask, "test1"); + if (.var != 3) + mes "Error: testing test1 npc variables from function."; + .var = getvariableofnpc(.walkmask, "test2"); + if (.var != 4) + mes "Error: testing test2 npc variables."; + test1function; + mes "Formatting testing for sex: " + str(Sex); + if (l("test @@", "") != "test ") + mes "Error: format string 'l(\"test @@\", \"\")': " + l("test @@", ""); + if (l("test @@", "1") != "test 1") + mes "Error: format string 'l(\"test @@\", \"1\")': " + l("test @@", "1"); + if (l("@@", "") != "") + mes "Error: format string 'l(\"@@\", \"\")': " + l("@@", ""); + if (l("@@") != "@@") + mes "Error: format string 'l(\"@@\")': " + l("@@"); + if (l("@@ @@ @@", "this", "is", "test") != "this is test") + mes "Error: format string 'l(\"@@ @@ @@\", \"this\", \"is\", \"test\") != \"this is test\"': " + l("@@ @@ @@", "this", "is", "test"); + if (l("data @@ @@ @@ data", "this", "is", "test") != "data this is test data") + mes "Error: format string 'l(\"data @@ @@ @@ data\", \"this\", \"is\", \"test\")': " + l("data @@ @@ @@ data", "this", "is", "test"); + if (l("test") != "test") + mes "Error: l(\"test\"): " + l("test"); + if (Sex == 1) + { + if (lg("test") != "test#1") + mes "Error: lg(\"test\"): " + lg("test"); + if (lg("test1", "test2") != "test2#1") + mes "Error: lg(\"test1\", \"test2\"): " + lg("test1", "test2"); + if (lg("test1 @@", "test2 @@", "line") != "test2 line#1") + mes "Error: lg(\"test1 @@\", \"test2 @@\", \"line\"): " + lg("test1 @@", "test2 @@", "line"); + } + else if (Sex == 0) + { + if (lg("test") != "test#0") + mes "Error: lg(\"test\"): " + lg("test"); + if (lg("test1", "test2") != "test1#0") + mes "Error: lg(\"test1\", \"test2\"): " + lg("test1", "test2"); + if (lg("test1 @@", "test2 @@", "line") != "test1 line#0") + mes "Error: lg(\"test1 @@\", \"test2 @@\", \"line\"): " + lg("test1 @@", "test2 @@", "line"); + } + mes "Quest vars testing..."; + .@time = 1000; + setq Test_testing1, 1, 2, 3, .@time; + if (getq(Test_testing1) != 1) + mes "Error: quest variable 1 error. Must be 1, but get " + str(getq(Test_testing1)); + if (getq2(Test_testing1) != 2) + mes "Error: quest variable 2 error. Must be 2, but get " + str(getq(Test_testing1)); + if (getq3(Test_testing1) != 3) + mes "Error: quest variable 3 error. Must be 3, but get " + str(getq(Test_testing1)); + if (getqtime(Test_testing1) != .@time) + mes "Error: quest variable 3 error. Must be 3, but get " + str(getqtime(Test_testing1)); + next; + .@time = 2000; + setq Test_testing1, 2, 3, 4, .@time; + if (getq(Test_testing1) != 2) + mes "Error: quest variable 1 error. Must be 2, but get " + str(getq(Test_testing1)); + if (getq2(Test_testing1) != 3) + mes "Error: quest variable 2 error. Must be 3, but get " + str(getq(Test_testing1)); + if (getq3(Test_testing1) != 4) + mes "Error: quest variable 3 error. Must be 4, but get " + str(getq(Test_testing1)); + if (getqtime(Test_testing1) != .@time) + mes "Error: quest variable 3 error. Must be 3, but get " + str(getqtime(Test_testing1)); + next; + setq Test_testing1, 0; + mes "Tests complete."; + close; + +OnInit: + .walkmask = 3; + end; +} diff --git a/npc/test/test2.txt b/npc/test/test2.txt new file mode 100644 index 0000000..fdc6f84 --- /dev/null +++ b/npc/test/test2.txt @@ -0,0 +1,13 @@ +// Evol scripts. +// Author: +// 4144 +// Description: +// script tests + +test,12,5,0 script test2 NPC_HAT_BOX,{ + close; + +OnInit: + .walkmask = 4; + end; +} diff --git a/npc/testbg/_import.txt b/npc/testbg/_import.txt new file mode 100644 index 0000000..c8affdf --- /dev/null +++ b/npc/testbg/_import.txt @@ -0,0 +1,3 @@ +// Map testbg: testbg +// This file is generated automatically. All manually added changes will be removed when running the Converter. +"npc/testbg/mapflags.txt", diff --git a/npc/testbg/mapflags.txt b/npc/testbg/mapflags.txt new file mode 100644 index 0000000..764a55d --- /dev/null +++ b/npc/testbg/mapflags.txt @@ -0,0 +1 @@ +testbg mapflag battleground |