diff options
author | Jesusaves <cpntb1@ymail.com> | 2022-10-23 21:44:22 -0300 |
---|---|---|
committer | Jesusaves <cpntb1@ymail.com> | 2022-10-23 21:44:22 -0300 |
commit | a7c45a192268da2601cef47a4cdba987ae2327ca (patch) | |
tree | c5fb5b97db109fe7106496dd96498c475881046b /npc/items | |
download | serverdata-a7c45a192268da2601cef47a4cdba987ae2327ca.tar.gz serverdata-a7c45a192268da2601cef47a4cdba987ae2327ca.tar.bz2 serverdata-a7c45a192268da2601cef47a4cdba987ae2327ca.tar.xz serverdata-a7c45a192268da2601cef47a4cdba987ae2327ca.zip |
Initial commit (Moubootaur Legends fork)
Diffstat (limited to 'npc/items')
-rw-r--r-- | npc/items/alcohol.txt | 124 | ||||
-rw-r--r-- | npc/items/arcmage.txt | 51 | ||||
-rw-r--r-- | npc/items/books.txt | 737 | ||||
-rw-r--r-- | npc/items/croconut.txt | 78 | ||||
-rw-r--r-- | npc/items/emptybox.txt | 54 | ||||
-rw-r--r-- | npc/items/grenade.txt | 122 | ||||
-rw-r--r-- | npc/items/inc_sc_bonus.txt | 72 | ||||
-rw-r--r-- | npc/items/legacy_heal.txt | 79 | ||||
-rw-r--r-- | npc/items/lofteleporter.txt | 123 | ||||
-rw-r--r-- | npc/items/maps.txt | 17 | ||||
-rw-r--r-- | npc/items/mercenary.txt | 150 | ||||
-rw-r--r-- | npc/items/miscrecipes.txt | 93 | ||||
-rw-r--r-- | npc/items/rand_mp_heal.txt | 63 | ||||
-rw-r--r-- | npc/items/rand_sc_heal.txt | 112 | ||||
-rw-r--r-- | npc/items/shovel.txt | 544 | ||||
-rw-r--r-- | npc/items/teleporter.txt | 54 | ||||
-rw-r--r-- | npc/items/valentine.txt | 31 |
17 files changed, 2504 insertions, 0 deletions
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; +} + |