summaryrefslogtreecommitdiff
path: root/npc/craft
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2022-10-23 21:44:22 -0300
committerJesusaves <cpntb1@ymail.com>2022-10-23 21:44:22 -0300
commita7c45a192268da2601cef47a4cdba987ae2327ca (patch)
treec5fb5b97db109fe7106496dd96498c475881046b /npc/craft
downloadserverdata-a7c45a192268da2601cef47a4cdba987ae2327ca.tar.gz
serverdata-a7c45a192268da2601cef47a4cdba987ae2327ca.tar.bz2
serverdata-a7c45a192268da2601cef47a4cdba987ae2327ca.tar.xz
serverdata-a7c45a192268da2601cef47a4cdba987ae2327ca.zip
Initial commit (Moubootaur Legends fork)
Diffstat (limited to 'npc/craft')
-rw-r--r--npc/craft/alchemy.txt147
-rw-r--r--npc/craft/options.txt1198
-rw-r--r--npc/craft/price.txt232
-rw-r--r--npc/craft/recipes.txt605
-rw-r--r--npc/craft/smith.txt91
-rw-r--r--npc/craft/tweak.txt128
6 files changed, 2401 insertions, 0 deletions
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);
+}