From 055ae0a6bb07db30f81236b34ae9ecea56249bbe Mon Sep 17 00:00:00 2001 From: Jesusaves Date: Fri, 10 Jul 2020 16:32:39 +0000 Subject: Implements Crafting Core system Recipes etc. not included --- db/constants.conf | 13 +- db/craft_db.conf | 27 +++ db/re/skill_db.conf | 9 + db/re/skill_tree.conf | 5 + npc/008-2-16/stove.txt | 8 +- npc/008-2-16/yannika.txt | 13 +- npc/012-2-5/toichi.txt | 60 ++++++- npc/functions/crafting.txt | 94 ++++++++++ npc/functions/main.txt | 19 ++ npc/functions/quest-debug/055-General_Cooking.txt | 14 +- npc/items/recipes.txt | 208 ++++++++++++++++++---- npc/scripts.conf | 1 + 12 files changed, 413 insertions(+), 58 deletions(-) create mode 100644 npc/functions/crafting.txt diff --git a/db/constants.conf b/db/constants.conf index 59dce74b..2c0fb424 100644 --- a/db/constants.conf +++ b/db/constants.conf @@ -4187,7 +4187,18 @@ constants_db: { RUSTYPICK_INN: 2 comment__: "CRAFT enum" - CRAFT_SANDWICH: 4 + CRAFT_COOKING: 4 + CRAFT_ALCHEMY: 8 + CRAFT_SMITHERY: 16 + CRAFT_TAILORING: 32 + CRAFT_JEWELERY: 64 + + comment__: "CRAFT Rarity enum" + CRAFT_BASIC: 1 + CRAFT_INTERMEDIARY: 2 + CRAFT_ADVANCED: 4 + CRAFT_EXPERT: 8 + CRAFT_MASTER: 16 comment__: "Being actions" ACTION_STAND: 0 diff --git a/db/craft_db.conf b/db/craft_db.conf index 2c338390..b8cdfc56 100644 --- a/db/craft_db.conf +++ b/db/craft_db.conf @@ -391,4 +391,31 @@ craft_db: ( } Priority: 10 }, +// Example recipe, incl for testing, do not use on live +{ + Id: 8 + Name: "CraftInfantryHelmet" + Flag: 16 + ReturnCode: 2906 + // InfantryHelmet + SourceItems: + ( + { + IronOre: 12 + Coal: 3 + Moss: 2 + Dagger: 1 + }, + ) + CreateItems: + ( + { + PileOfAsh: 1 + }, + ) + RequiredItems: { + RecipeBook: 1 + } + Priority: 10 +}, ) diff --git a/db/re/skill_db.conf b/db/re/skill_db.conf index 30cc7466..6749fcd3 100644 --- a/db/re/skill_db.conf +++ b/db/re/skill_db.conf @@ -38652,4 +38652,13 @@ skill_db: ( } CoolDown: 3000 }, +{ + Id: 20004 + Name: "EVOL_CRAFTING" + Description: "Crafting" + MaxLevel: 10 + SkillType: { + Passive: true + } +}, ) diff --git a/db/re/skill_tree.conf b/db/re/skill_tree.conf index 20a4dd64..95fc3daa 100644 --- a/db/re/skill_tree.conf +++ b/db/re/skill_tree.conf @@ -27,6 +27,7 @@ Job_Name: { // Job names as in src/map/pc.c (they are hardcoded at the moment so Viro: { skills: { + EVOL_CRAFTING: 0 SM_SWORD: 0 SM_TWOHAND: 0 AC_OWL: 0 @@ -91,6 +92,7 @@ Viro: { CaveUkar: { skills: { + EVOL_CRAFTING: 0 SM_SWORD: 0 SM_TWOHAND: 0 AC_OWL: 0 @@ -155,6 +157,7 @@ CaveUkar: { FireKralog: { skills: { + EVOL_CRAFTING: 0 SM_SWORD: 0 SM_TWOHAND: 0 AC_OWL: 0 @@ -219,6 +222,7 @@ FireKralog: { LightRaijin: { skills: { + EVOL_CRAFTING: 0 SM_SWORD: 0 SM_TWOHAND: 0 AC_OWL: 0 @@ -283,6 +287,7 @@ LightRaijin: { SeaTritan: { skills: { + EVOL_CRAFTING: 0 SM_SWORD: 0 SM_TWOHAND: 0 AC_OWL: 0 diff --git a/npc/008-2-16/stove.txt b/npc/008-2-16/stove.txt index 91036c44..40dfeca9 100644 --- a/npc/008-2-16/stove.txt +++ b/npc/008-2-16/stove.txt @@ -25,14 +25,14 @@ setskin "craft4"; .@var$ = requestcraft(4); // Limit: 4 items .@craft = initcraft(.@var$); - .@entry = findcraftentry(.@craft, CRAFT_SANDWICH); + .@entry = findcraftentry(.@craft, CRAFT_COOKING); setskin ""; // Does the recipe exist and is a sandwich? if (.@entry < 0) { narrator - l("You don't know how to make a sandwich with that."), + l("You don't know how any recipe with that."), l("Do you want to try again?"); if (askyesno() == ASK_YES) .@tryAgain=true; @@ -52,7 +52,7 @@ { // Even if the recipe is right, if you don't have it on your // recipe book, it should be deemed invalid. - if (COOKING_RECIPES[.@entry]) + if (RECIPES[.@entry]) { usecraft .@craft; narrator @@ -62,7 +62,7 @@ else { narrator - l("You don't know how to make a sandwich with that."), + l("You don't know how any recipe with that."), l("Do you want to try again?"); } diff --git a/npc/008-2-16/yannika.txt b/npc/008-2-16/yannika.txt index 7223b70c..9599a5d9 100644 --- a/npc/008-2-16/yannika.txt +++ b/npc/008-2-16/yannika.txt @@ -10,7 +10,7 @@ // 0: Doesn't knows the quest // 1: Received the quest for the Cookbook // 2: Completed the quest for the Cookbook -// COOKING_RECIPES +// RECIPES // Controls which recipes you know and doesn't. // It is an array with Craft Constants. @@ -101,7 +101,7 @@ } delitem .@item1, .@ammo1; delitem .@item2, .@ammo2; - COOKING_RECIPES[.@craft]=true; + RECIPES[.@craft]=true; speech l("This is how you do it! HAAH!"); next; @@ -112,6 +112,7 @@ } // Yannika can make sandwiches for players + // FIXME: Actually, Yannika will cook anything for players >__> function sudo_make_sandwich { speech @@ -128,7 +129,7 @@ setskin "craft4"; .@var$ = requestcraft(4); // Limit: 4 items .@craft = initcraft(.@var$); - .@entry = findcraftentry(.@craft, CRAFT_SANDWICH); + .@entry = findcraftentry(.@craft, CRAFT_COOKING); setskin ""; // Does the recipe exist and is a sandwich? @@ -211,7 +212,7 @@ { case CommonCarp: case GrassCarp: - if (COOKING_RECIPES[CraftCarpSandwich]) + if (RECIPES[CraftCarpSandwich]) { speech lg("Haha, silly you! You already know the recipe! Read the @@ if you forgot!", getitemlink(RecipeBook)); @@ -223,7 +224,7 @@ break; case Manana: - if (COOKING_RECIPES[CraftMananaSandwich]) + if (RECIPES[CraftMananaSandwich]) { speech lg("Haha, silly you! You already know the recipe! Read the @@ if you forgot!", getitemlink(RecipeBook)); @@ -235,7 +236,7 @@ break; case PiouLegs: - if (COOKING_RECIPES[CraftPioulegSandwich]) + if (RECIPES[CraftPioulegSandwich]) { speech lg("Haha, silly you! You already know the recipe! Read the @@ if you forgot!", getitemlink(RecipeBook)); diff --git a/npc/012-2-5/toichi.txt b/npc/012-2-5/toichi.txt index e1759e04..c2ff6c78 100644 --- a/npc/012-2-5/toichi.txt +++ b/npc/012-2-5/toichi.txt @@ -6,17 +6,69 @@ // THIS IS A PLACEHOLDER! 012-2-5,40,35,0 script Toichi NPC_TOICHI,{ + function askCrafting; speech l("Hi there."), l("My name is Toichi, and it seems I am the only one working on this lazy island."); - next; - mesq l("So I kindly request you not to listen to that stupid Rosen, saying 'Toichi is lazy'."); - next; - mesq l("At least I am doing something!"); + next; + mesq l("So I kindly request you not to listen to that stupid Rosen, saying 'Toichi is lazy'."); + next; + mesq l("At least I am doing something!"); + // TODO: Add here a check for crafting skills + askCrafting(); close; +function askCrafting { + next; + select + l("Okay..."), + l("Hey, do you mind if I use your equipment?"), + rif(is_dev(), "[Debug]"); + mes ""; + if (@menu == 1) + return; + + // Debug + if (debug && @menu == 3) + RECIPES[CraftInfantryHelmet]=!RECIPES[CraftInfantryHelmet]; + if (debug || @menu == 3) + mesf("[DEBUG] Knows the infantry helmet recipe? %s", + (RECIPES[CraftInfantryHelmet] ? "YES" : "NO")); + + mesn; + mesq l("Sure, go ahead. But I'll charge you %d E per craft as commission!", .price); + next; + do { + mesc l("What will you craft today?"); + mesc l("It costs %d E to use.", .price), 1; + if (Zeny < .price) + close; + + if (SmithSystem()) { + // This should NEVER, EVER happen. + if (Zeny < .price) { + mesc l("WARNING, you have been detected cheating and thus, violating Gasaron Anti-Theft Policy."), 1; + mesc l("You were jailed and now need a GM to get you out of there."), 1; + // At this point I just c/p the code + logmes "WARNING, "+strcharinfo(0)+" found out cheating, only had "+Zeny+" Esperins for craft table. Jailed.", LOGMES_ATCOMMAND; + atcommand("@jail "+strcharinfo(0)); + Zeny=0; + close; + } + Zeny-=.price; + mesc l("Success!"), 3; + } else { + mesc l("That didn't work!"), 1; + } + next; + mesc l("Try again?"); + } while (askyesno() == ASK_YES); + return; +} + OnInit: .sex = G_MALE; .distance = 4; + .price = 6000; end; } diff --git a/npc/functions/crafting.txt b/npc/functions/crafting.txt new file mode 100644 index 00000000..ba032f41 --- /dev/null +++ b/npc/functions/crafting.txt @@ -0,0 +1,94 @@ +// Moubootaur Legends Script +// Author: +// Jesusalva +// Description: +// Smith System (Unified) +// Notes: +// Modified for The Mana World: rEvolt +// +// 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) + +// Usage: SmithSystem ({scope=CRAFT_SMITHERY, checks=True}) +// Returns true on success, false on failure +function script SmithSystem { + .@scope=getarg(0, CRAFT_SMITHERY); + 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, .@scope); + 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 (!getarg(1, true) || RECIPES[.@entry]) { + // Player craft item and setup base variables + usecraft .@craft; + .@it=getcraftcode(.@entry); + .@lv=getiteminfo(.@it, ITEMINFO_ELV); + .@skill=getskilllv(EVOL_CRAFTING); + + // Update your CRAFTING_SCORE + CRAFTING_SCORE+=.@lv; + + // Obtain the item. No bounds or restrictions applied. + getitem(.@it, 1); + + // This is where we add options - this is oversimplified + // delinventorylist() is needed, because we'll rely on rfind() + delinventorylist(); + getinventorylist(); + .@index=array_rfind(@inventorylist_id, .@it); + + // Prepare the options + .@isweapon=(getiteminfo(.@it, ITEMINFO_TYPE) == IT_WEAPON); + .@bonus=(.@isweapon ? VAR_ATTPOWER : VAR_ITEMDEFPOWER); + .@magic=(.@isweapon ? VAR_ATTMPOWER : VAR_MDEFPOWER); + .@buffs=(.@isweapon ? VAR_HITSUCCESSVALUE : VAR_AVOIDSUCCESSVALUE); + .@heals=(.@isweapon ? VAR_MAXSPAMOUNT : VAR_MAXHPAMOUNT); + + // Now we need to randomly decide what will be .@opt1 and .@opt2 + // Would be better to not rely on rand here, though. + .@opt1=any(.@bonus, .@magic); + .@opt2=any(.@buffs, .@heals); + + // Apply the bonuses. They're capped by equip level and based on: + // Equip level, crafting experience and crafting skill + .@val=min(.@lv, (.@skill*CRAFTING_SCORE)/100+.@skill); + + // MDEF rule range is 99 while DEF rule range is 399 + // This is a really hackish way, for the record + if (.@opt1 == VAR_MDEFPOWER) + .@val=.@val/4; + + // First option will always succeed + .@val1=rand2(.@val); + setitemoptionbyindex(.@index, 0, .@opt1, .@val1); + + // Lucky roll (5% per skill level, capped at 50%) + // The lucky roll will add the extra attributes + if (rand2(20) < .@skill) { + .@val2=rand2(.@val); + setitemoptionbyindex(.@index, 1, .@opt2, .@val2); + } + + // Get experience for the craft + // I'm using the same EXP formula from Moubootaur Legends + // Which is based on the item sell price + .@xp=getiteminfo(.@it, ITEMINFO_SELLPRICE); + getexp .@xp+BaseLevel, (.@xp/3)+BaseLevel+JobLevel; + .success=true; + } else { + .success=false; + } + } + deletecraft .@craft; + setskin ""; + return .success; +} + + diff --git a/npc/functions/main.txt b/npc/functions/main.txt index 9618e47e..e4c4cd88 100644 --- a/npc/functions/main.txt +++ b/npc/functions/main.txt @@ -269,3 +269,22 @@ function script get_race { return .@allskins$[.@g] + " " + .@allraces$[.@g]; } +// Clear output of getinventorylist() +// delinventorylist() +function script delinventorylist { + deletearray @inventorylist_id; + deletearray @inventorylist_amount; + deletearray @inventorylist_equip; + deletearray @inventorylist_refine; + deletearray @inventorylist_identify; + deletearray @inventorylist_attribute; + deletearray @inventorylist_card1; + deletearray @inventorylist_card2; + deletearray @inventorylist_card3; + deletearray @inventorylist_card4; + deletearray @inventorylist_expire; + deletearray @inventorylist_bound; + @inventorylist_count=0; + return; +} + diff --git a/npc/functions/quest-debug/055-General_Cooking.txt b/npc/functions/quest-debug/055-General_Cooking.txt index ed9fb685..76e23b17 100644 --- a/npc/functions/quest-debug/055-General_Cooking.txt +++ b/npc/functions/quest-debug/055-General_Cooking.txt @@ -10,7 +10,7 @@ function script QuestDebug55 { mes "General_Cooking"; mes "---"; mes l("Quest state: @@", getq(General_Cooking)); - mes l("Known Recipes: @@", array_entries(COOKING_RECIPES)); + mes l("Known Recipes: @@", array_entries(RECIPES)); next; select @@ -33,14 +33,14 @@ function script QuestDebug55 { getitem RecipeBook, 1; break; case 5: - COOKING_RECIPES[CraftCarpSandwich]=true; - COOKING_RECIPES[CraftMananaSandwich]=true; - COOKING_RECIPES[CraftPioulegSandwich]=true; + RECIPES[CraftCarpSandwich]=true; + RECIPES[CraftMananaSandwich]=true; + RECIPES[CraftPioulegSandwich]=true; break; case 6: - COOKING_RECIPES[CraftCarpSandwich]=false; - COOKING_RECIPES[CraftMananaSandwich]=false; - COOKING_RECIPES[CraftPioulegSandwich]=false; + RECIPES[CraftCarpSandwich]=false; + RECIPES[CraftMananaSandwich]=false; + RECIPES[CraftPioulegSandwich]=false; break; } diff --git a/npc/items/recipes.txt b/npc/items/recipes.txt index 3fb3309c..485336e3 100644 --- a/npc/items/recipes.txt +++ b/npc/items/recipes.txt @@ -5,50 +5,114 @@ // Description: // Contains recipe books for Evol Online -- script #RecipeBook NPC_HIDDEN,{ +// showRecipe( Craft, Bonus, {amount 1, item 1}, {amount 2, item 2}... ) +function script showRecipe { + if (getargcount() < 3 || getargcount() % 2 != 0) + return false;//Exception("Faulty recipe skill command invoked - error"); - function read_book { - - setnpcdialogtitle l(.book_name$); - - mesc l("Eating is a necessity, but cooking is an art."); - mesc l("(All items must be placed exactly in this order for cooking work.)"); - next; - mesc l("List of known cooking recipes:"); - mes ""; - mes ".:: " + l("Sandwiches") + " ::."; - mes ""; - if (COOKING_RECIPES[CraftCarpSandwich]) { - mes l("@@", getitemlink(CarpSandwich)); - mesc l("* @@ @@", 1, getitemlink(Bread)); - mesc l("* @@ @@", 3, getitemlink(LettuceLeaf)); - mesc l("* @@ @@", 2, getitemlink(Cheese)); - mesc l("* @@ @@", 1, getitemlink(CommonCarp)); - mes ""; - } - if (COOKING_RECIPES[CraftPioulegSandwich]) { - mes l("@@", getitemlink(PioulegSandwich)); - mesc l("* @@ @@", 1, getitemlink(Bread)); - mesc l("* @@ @@", 3, getitemlink(LettuceLeaf)); - mesc l("* @@ @@", 2, getitemlink(Cheese)); - mesc l("* @@ @@", 1, getitemlink(PiouLegs)); - mes ""; - } - if (COOKING_RECIPES[CraftMananaSandwich]) { - mes l("@@", getitemlink(MananaSandwich)); - mesc l("* @@ @@", 1, getitemlink(Bread)); - mesc l("* @@ @@", 3, getitemlink(LettuceLeaf)); - mesc l("* @@ @@", 2, getitemlink(Cheese)); - mesc l("* @@ @@", 1, getitemlink(Manana)); + if (RECIPES[getarg(0)]) { + if (getarg(1)) { + mes l(".:: %s Recipe ::.", getitemlink(getarg(1))); + + for (.@i=2;.@i < getargcount(); .@i++) { + mesc l("%d/%d %s", countitem(getarg(.@i+1)), getarg(.@i), getitemlink(getarg(.@i+1))); + .@i++; + } mes ""; } + return true; + } + return false; +} + +- script #RecipeBook NPC_HIDDEN,{ + function read_book; + function read_cooking; + function read_smithery; + end; - close; +function read_book { + setnpcdialogtitle l(.book_name$); + mesc l("This book has several bookmarks. Which one will you open?"); + next; + menuint + l("Cooking"), CRAFT_COOKING, + l("Alchemy"), CRAFT_ALCHEMY, + l("Smithery"), CRAFT_SMITHERY, + l("Tailoring"), CRAFT_TAILORING, + l("Jewelery"), CRAFT_JEWELERY; + mes ""; + switch (@menuret) { + case CRAFT_COOKING: + read_cooking(); break; + case CRAFT_SMITHERY: + read_smithery(); break; + default: + mesc l("Unfortunately, there is nothing on this bookmark."); + mesc l("Perhaps, in future, someone adds it to this world."); + break; } + next; + return; +} + +/////////////////////////////////////////////////////////////////////////////// +function read_cooking { + + setnpcdialogtitle l("Cooking Recipes"); + + mesc l("Eating is a necessity, but cooking is an art."); + mesc l("(All items must be placed exactly in this order for cooking work.)"); + next; + mesc l("List of known cooking recipes:"); + mes ""; + mes ".:: " + l("Sandwiches") + " ::."; + mes ""; + showRecipe(CraftCarpSandwich, CarpSandwich, + 1, Bread, + 3, LettuceLeaf, + 2, Cheese, + 1, CommonCarp); + showRecipe(CraftPioulegSandwich, PioulegSandwich, + 1, Bread, + 3, LettuceLeaf, + 2, Cheese, + 1, PiouLegs); + showRecipe(CraftMananaSandwich, MananaSandwich, + 1, Bread, + 3, LettuceLeaf, + 2, Cheese, + 1, Manana); + + return; +} + +/////////////////////////////////////////////////////////////////////////////// +function read_smithery { + + setnpcdialogtitle l("Smithery Recipes"); + + mesc l("You will trust your life to this, so you better do a good job!"); + mesc l("(All items must be placed exactly in this order.)"); + next; + mesc l("List of known smithery recipes:"); + + mes ""; + mes ".:: " + l("Helmets") + " ::."; + mes ""; + showRecipe(CraftInfantryHelmet, InfantryHelmet, + 12, IronOre, + 3, Coal, + 2, Moss, + 1, Dagger); + + return; +} + OnUse: if (openbook()) - read_book; + read_book(); closeclientdialog(); close; @@ -58,3 +122,75 @@ OnInit: .distance = 1; end; } + +////////////////////////////////////////////////////// +// Below this line are utils for Gacha. We use callfunc() on itemDB. +// Types: see constants.db - everything is a bitwise here +// Rarity: 1 - basic, 2 - intermediary, 4 - advanced, 8 - expert, 16 - master +// Rarity: 1 - training, 2 - basic, 4 - advanced, 8 - expert, 16 - legendary +// Keep in mind! Expert and Master blueprints must be restricted! +// MakeBlueprint(type, rarity) +function script MakeBlueprint { + .@type=getarg(0, -1); + .@rarity=getarg(1, 1); + + if (.@type & CRAFT_COOKING) { + + // ---------------------------------- + if (.@rarity & CRAFT_BASIC) { + } + if (.@rarity & CRAFT_INTERMEDIARY) { + } + if (.@rarity & CRAFT_ADVANCED) { + } + if (.@rarity & CRAFT_EXPERT) { + } + if (.@rarity & CRAFT_MASTER) { + } + // ---------------------------------- + + } + else if (.@type & CRAFT_SMITHERY) + { + + // ---------------------------------- + if (.@rarity & CRAFT_BASIC) { + } + if (.@rarity & CRAFT_INTERMEDIARY) { + array_push(.@recipes, CraftInfantryHelmet); + } + if (.@rarity & CRAFT_ADVANCED) { + } + if (.@rarity & CRAFT_EXPERT) { + } + if (.@rarity & CRAFT_MASTER) { + } + // ---------------------------------- + + } + + // We don't have a .@recipes array D: + if (array_entries(.@recipes) <= 0) { + dispbottom l("This blueprint was blank."); + return; + } + + // Select a recipe randomly + .@rcp=any_of(.@recipes); + + // Double precision failsafe + if (RECIPES[.@rcp]) + .@rcp=any_of(.@recipes); + + // Learn the recipe or lose the item (and gain some EXP) + if (RECIPES_[.@rcp]) { + dispbottom l("It was a recipe you already knew..."); + getexp (BaseLevel+JobLevel)*rand2(1,.@rarity), JobLevel+rand2(1,.@rarity); + } else { + dispbottom l("Learned a new recipe!"); + RECIPES[.@rcp]=true; + } + return; +} + + diff --git a/npc/scripts.conf b/npc/scripts.conf index 4ad30e0e..4e144bf6 100644 --- a/npc/scripts.conf +++ b/npc/scripts.conf @@ -56,6 +56,7 @@ "npc/functions/manhole.txt", "npc/functions/skills.txt", "npc/functions/lockpicks.txt", +"npc/functions/crafting.txt", // quest debug "npc/functions/quest-debug/functions.txt", -- cgit v1.2.3-60-g2f50