diff options
-rw-r--r-- | conf/map/battle/feature.conf | 10 | ||||
-rw-r--r-- | conf/messages.conf | 3 | ||||
-rw-r--r-- | db/pre-re/refine_db.conf | 179 | ||||
-rw-r--r-- | db/re/refine_db.conf | 598 | ||||
-rw-r--r-- | doc/script_commands.txt | 6 | ||||
-rw-r--r-- | npc/merchants/advanced_refiner.txt | 19 | ||||
-rw-r--r-- | npc/merchants/hd_refine.txt | 15 | ||||
-rw-r--r-- | npc/merchants/refine.txt | 17 | ||||
-rw-r--r-- | npc/re/merchants/hd_refiner.txt | 11 | ||||
-rw-r--r-- | npc/re/merchants/refine.txt | 11 | ||||
-rw-r--r-- | npc/re/merchants/shadow_refiner.txt | 10 | ||||
-rw-r--r-- | src/common/mmo.h | 8 | ||||
-rw-r--r-- | src/map/atcommand.c | 17 | ||||
-rw-r--r-- | src/map/battle.c | 14 | ||||
-rw-r--r-- | src/map/battle.h | 2 | ||||
-rw-r--r-- | src/map/clif.c | 87 | ||||
-rw-r--r-- | src/map/clif.h | 6 | ||||
-rw-r--r-- | src/map/itemdb.h | 1 | ||||
-rw-r--r-- | src/map/map.c | 1 | ||||
-rw-r--r-- | src/map/packets.h | 6 | ||||
-rw-r--r-- | src/map/packets_struct.h | 70 | ||||
-rw-r--r-- | src/map/pc.h | 5 | ||||
-rw-r--r-- | src/map/refine.c | 388 | ||||
-rw-r--r-- | src/map/refine.h | 35 | ||||
-rw-r--r-- | src/map/refine.p.h | 66 | ||||
-rw-r--r-- | src/map/script.c | 20 |
26 files changed, 1582 insertions, 23 deletions
diff --git a/conf/map/battle/feature.conf b/conf/map/battle/feature.conf index 5e40f2898..0cb293d60 100644 --- a/conf/map/battle/feature.conf +++ b/conf/map/battle/feature.conf @@ -88,4 +88,14 @@ features: { // true: enable (Default) // false: disable enable_achievement_system: true + + // Enable Refinery UI (requires 2016-10-05Ragexe/RE) + // true: enable (Default) + // false: disable + enable_refinery_ui: false + + // Replace Refine NPCs with Refinery UI + // true: enable + // false: disable (default) + replace_refine_npcs: false } diff --git a/conf/messages.conf b/conf/messages.conf index 772b882c9..2788f264d 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -449,7 +449,8 @@ // Return pet to egg message 451: You can't return your pet because your inventory is full. 452: usage @camerainfo range rotation latitude -//453-497 FREE +453: Refinery UI is not available +//454-497 FREE // Messages of others (not for GM commands) // ---------------------------------------- diff --git a/db/pre-re/refine_db.conf b/db/pre-re/refine_db.conf index dd3bcdb41..5896e49f6 100644 --- a/db/pre-re/refine_db.conf +++ b/db/pre-re/refine_db.conf @@ -37,6 +37,24 @@ Armors/WeaponLevel1~4: { // Specifies weap RandomBonusStartLevel: (int) // This value specifies the start point for those levels that give a random bonus value (usually the first unsafe upgrade). // - RandomBonusStartLevel is only applied for weapons, and not displayed client-side. RandomBonusValue: (int) // A random number between 0 and (Random bonus start level - Upgrade level + 1) * this value is applied for all upgrades past. + RefineryUISettings: ( + { + Level: (int or array of int) // Holds either the individule refine level meant for this setting or an array defining a range + of Low to Max level + BlacksmithBlessing: (int) (optional) // How many Blacksmith Blessing required for this range to be safe from breaking + Items: { + AegisName: { + Type: "(string)" // The type to determine the chances used for this item, REFINE_CHANCE_TYPE_* + constants are used in here + Cost: (int) (optional) // Amount of zeny required + FailureBehavior: "(string)" (optional) // The expected behvaior on failure for this item, the following strings are used in here + Destroy (default) sets the item to be destroyed on failure + Keep keeps the item after failure + Downgrade downgrades the item by one level on failure + } + } + } + ) Rates: { // Per level configuration of the refine rates. Lv1~10: { // Lv1 ~ Lv10. NormalChance: (int) // (optional, defaults to 100) Chance of successful refine using normal ores (100 = 100%). @@ -54,6 +72,39 @@ Armors: { StatsPerLevel: 66 RandomBonusStartLevel: 0 RandomBonusValue: 0 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Elunium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 2000 + } + Enriched_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 2000 + } + } + }, + { + Level: [8, 10] + Items: { + Elunium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 2000 + } + Enriched_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 2000 + } + HD_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + ) Rates: { Lv5: { NormalChance: 60 @@ -97,6 +148,38 @@ WeaponLevel1: { StatsPerLevel: 200 RandomBonusStartLevel: 8 RandomBonusValue: 300 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Phracon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 50 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 50 + } + } + }, + { + Level: [8, 10] + Items: { + Phracon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 50 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 50 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + } + } + }, + ) Rates: { Lv8: { NormalChance: 60 @@ -122,6 +205,38 @@ WeaponLevel2: { StatsPerLevel: 300 RandomBonusStartLevel: 7 RandomBonusValue: 500 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Emveretarcon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 200 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 200 + } + } + }, + { + Level: [8, 10] + Items: { + Emveretarcon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 200 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 200 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + } + } + }, + ) Rates: { Lv7: { NormalChance: 60 @@ -153,6 +268,38 @@ WeaponLevel3: { StatsPerLevel: 500 RandomBonusStartLevel: 6 RandomBonusValue: 800 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 5000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 5000 + } + } + }, + { + Level: [8, 10] + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 5000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 5000 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + } + } + }, + ) Rates: { Lv6: { NormalChance: 60 @@ -190,6 +337,38 @@ WeaponLevel4: { StatsPerLevel: 700 RandomBonusStartLevel: 5 RandomBonusValue: 1300 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 5000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 5000 + } + } + }, + { + Level: [8, 10] + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 5000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 5000 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + } + } + }, + ) Rates: { Lv5: { NormalChance: 60 diff --git a/db/re/refine_db.conf b/db/re/refine_db.conf index 4974e0033..31335fffd 100644 --- a/db/re/refine_db.conf +++ b/db/re/refine_db.conf @@ -37,6 +37,24 @@ Armors/WeaponLevel1~4: { // Specifies weap RandomBonusStartLevel: (int) // This value specifies the start point for those levels that give a random bonus value (usually the first unsafe upgrade). // - RandomBonusStartLevel is only applied for weapons, and not displayed client-side. RandomBonusValue: (int) // A random number between 0 and (Random bonus start level - Upgrade level + 1) * this value is applied for all upgrades past. + RefineryUISettings: ( + { + Level: (int or array of int) // Holds either the individule refine level meant for this setting or an array defining a range + of Low to Max level + BlacksmithBlessing: (int) (optional) // How many Blacksmith Blessing required for this range to be safe from breaking + Items: { + AegisName: { + Type: "(string)" // The type to determine the chances used for this item, REFINE_CHANCE_TYPE_* + constants are used in here + Cost: (int) (optional) // Amount of zeny required + FailureBehavior: "(string)" (optional) // The expected behvaior on failure for this item, the following strings are used in here + Destroy (default) sets the item to be destroyed on failure + Keep keeps the item after failure + Downgrade downgrades the item by one level on failure + } + } + } + ) Rates: { // Per level configuration of the refine rates. Lv1~20: { // Lv1 ~ Lv20. NormalChance: (int) // (optional, defaults to 100) Chance of successful refine using normal ores (100 = 100%). @@ -54,6 +72,122 @@ Armors: { StatsPerLevel: 0 RandomBonusStartLevel: 0 RandomBonusValue: 0 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Elunium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 2000 + } + Enriched_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 2000 + } + } + }, + { + Level: 8 + BlacksmithBlessing: 1 + Items: { + Elunium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 2000 + } + Enriched_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 2000 + } + HD_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 9 + BlacksmithBlessing: 2 + Items: { + Elunium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 2000 + } + Enriched_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 2000 + } + HD_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 10 + BlacksmithBlessing: 4 + Items: { + Elunium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 2000 + } + Enriched_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 2000 + } + HD_Elunium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 11 + BlacksmithBlessing: 7 + Items: { + Carnium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Carnium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 12 + BlacksmithBlessing: 11 + Items: { + Carnium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Carnium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: [13, 20] + Items: { + Carnium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Carnium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + ) Rates: { Lv1: { Bonus: 100 @@ -175,6 +309,122 @@ WeaponLevel1: { StatsPerLevel: 200 RandomBonusStartLevel: 8 RandomBonusValue: 300 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Phracon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 50 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 50 + } + } + }, + { + Level: 8 + BlacksmithBlessing: 1 + Items: { + Phracon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 50 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 50 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 9 + BlacksmithBlessing: 2 + Items: { + Phracon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 50 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 50 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 10 + BlacksmithBlessing: 4 + Items: { + Phracon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 50 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 50 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 11 + BlacksmithBlessing: 7 + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 12 + BlacksmithBlessing: 11 + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: [13, 20] + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + ) Rates: { Lv8: { NormalChance: 60 @@ -255,6 +505,122 @@ WeaponLevel2: { StatsPerLevel: 300 RandomBonusStartLevel: 7 RandomBonusValue: 500 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Emveretarcon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 200 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 200 + } + } + }, + { + Level: 8 + BlacksmithBlessing: 1 + Items: { + Emveretarcon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 200 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 200 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 9 + BlacksmithBlessing: 2 + Items: { + Emveretarcon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 200 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 200 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 10 + BlacksmithBlessing: 4 + Items: { + Emveretarcon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 200 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 200 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 11 + BlacksmithBlessing: 7 + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 12 + BlacksmithBlessing: 11 + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: [13, 20] + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + ) Rates: { Lv7: { NormalChance: 60 @@ -341,6 +707,122 @@ WeaponLevel3: { StatsPerLevel: 500 RandomBonusStartLevel: 6 RandomBonusValue: 800 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 5000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 5000 + } + } + }, + { + Level: 8 + BlacksmithBlessing: 1 + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 5000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 5000 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 9 + BlacksmithBlessing: 2 + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 5000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 5000 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 10 + BlacksmithBlessing: 4 + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 5000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 5000 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 11 + BlacksmithBlessing: 7 + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 12 + BlacksmithBlessing: 11 + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: [13, 20] + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + ) Rates: { Lv6: { NormalChance: 60 @@ -433,6 +915,122 @@ WeaponLevel4: { StatsPerLevel: 700 RandomBonusStartLevel: 5 RandomBonusValue: 1400 + RefineryUISettings: ( + { + Level: [1, 7] + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 20000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + } + } + }, + { + Level: 8 + BlacksmithBlessing: 1 + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 20000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 9 + BlacksmithBlessing: 2 + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 20000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 10 + BlacksmithBlessing: 4 + Items: { + Oridecon: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 20000 + } + Enriched_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + } + HD_Oridecon: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 11 + BlacksmithBlessing: 7 + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: 12 + BlacksmithBlessing: 11 + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + { + Level: [13, 20] + Items: { + Bradium: { + Type: "REFINE_CHANCE_TYPE_NORMAL" + Cost: 100000 + } + HD_Bradium: { + Type: "REFINE_CHANCE_TYPE_ENRICHED" + Cost: 20000 + FailureBehavior: "Downgrade" + } + } + }, + ) Rates: { Lv5: { NormalChance: 60 diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 4d8053da0..516454365 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -10463,3 +10463,9 @@ Force close roulette window. Works for 20141008 main clients, 20140903 re, any zero. --------------------------------------- +*openrefineryui() + +Opens refinery user interface for the player +returns true on success and false on failure + +--------------------------------------- diff --git a/npc/merchants/advanced_refiner.txt b/npc/merchants/advanced_refiner.txt index 9632f95f7..ec263e192 100644 --- a/npc/merchants/advanced_refiner.txt +++ b/npc/merchants/advanced_refiner.txt @@ -44,14 +44,19 @@ //========================================================================= payon,157,146,6 script Suhnbi#cash 4_M_03,{ - disable_items; - mes "[Suhnbi]"; - mes "I am the Armsmith"; - mes "I can refine all kinds of weapons,"; - mes "armor and equipment, so let me"; - mes "know what you want to refine."; - next; + mes("[Suhnbi]"); + mes("I am the Armsmith"); + mes("I can refine all kinds of weapons,"); + mes("armor and equipment, so let me"); + mes("know what you want to refine."); + if (getbattleflag("features/replace_refine_npcs") == 1) { + if (openrefineryui()) + close(); + } + next(); + + disable_items; setarray .@position$[1], "Head","Body","Left hand","Right hand","Robe","Shoes","Accessory 1","Accessory 2","Head 2","Head 3"; .@menu$ = ""; for(.@i = 1; .@i<=10; ++.@i) { diff --git a/npc/merchants/hd_refine.txt b/npc/merchants/hd_refine.txt index a7fc5e922..1a5a43621 100644 --- a/npc/merchants/hd_refine.txt +++ b/npc/merchants/hd_refine.txt @@ -39,6 +39,17 @@ //== Blacksmith Mighty Hammer (+7~9) ======================= - script ::MightyHammer FAKE_NPC,{ + mes("[Blacksmith Mighty Hammer]"); + mes("I'm a blacksmith skilled in refining weapons and armors."); + mes("I can refine an item of your choice among the items you are equipped with."); + mes("Which item do you want to refine?"); + + if (getbattleflag("features/replace_refine_npcs") == 1) { + if (openrefineryui()) + close(); + } + next(); + disable_items; mes "[Blacksmith Mighty Hammer]"; mes "Unlike others, I am a blacksmith who refines a very limited number of items."; @@ -179,6 +190,10 @@ lhz_in02,280,19,3 duplicate(MightyHammer) Mighty Hammer#lhz 4_M_DWARF //== Basta (+10 and up) ==================================== - script ::Basta FAKE_NPC,{ + if (getbattleflag("features/replace_refine_npcs") == 1) { + if (openrefineryui()) + end(); + } disable_items; mes "[Basta]"; mes "I'm the best Blacksmith in the whole world, Basta."; diff --git a/npc/merchants/refine.txt b/npc/merchants/refine.txt index 7f1b4d9a3..19ebf4a8e 100644 --- a/npc/merchants/refine.txt +++ b/npc/merchants/refine.txt @@ -589,14 +589,19 @@ lhz_in02,282,20,7 script Fulerr 4_M_LGTMAN,{ // If you enable this function, be sure to edit the value of .@safe to the max // safe refine in refine_db.txt as well. function script refinemain { + mesf("[%s]", getarg(0)); + mes("I'm the Armsmith."); + mes("I can refine all kinds of weapons, armor and equipment, so let me"); + mes("know what you want me to refine."); + + if (getbattleflag("features/replace_refine_npcs") == 1) { + if (openrefineryui()) + close(); + } + next(); + disable_items; .@features = getarg(1); - mes "[" + getarg(0) + "]"; - mes "I'm the Armsmith."; - mes "I can refine all kinds of weapons, armor and equipment, so let me"; - mes "know what you want me to refine."; - next; - setarray .@position$[1], "Head","Body","Left hand","Right hand","Robe","Shoes","Accessory 1","Accessory 2","Head 2","Head 3"; .@menu$ = ""; for(.@i = 1; .@i <= 10; ++.@i) { diff --git a/npc/re/merchants/hd_refiner.txt b/npc/re/merchants/hd_refiner.txt index 17979642e..2dcc74bae 100644 --- a/npc/re/merchants/hd_refiner.txt +++ b/npc/re/merchants/hd_refiner.txt @@ -39,6 +39,17 @@ //== Blacksmith Mighty Hammer (+7~9) ======================= - script ::MightyHammer FAKE_NPC,{ + mes("[Blacksmith Mighty Hammer]"); + mes("I'm a blacksmith skilled in refining weapons and armors."); + mes("I can refine an item of your choice among the items you are equipped with."); + mes("Which item do you want to refine?"); + + if (getbattleflag("features/replace_refine_npcs") == 1) { + if (openrefineryui()) + close(); + } + next(); + disable_items; mes "[Blacksmith Mighty Hammer]"; mes "Unlike others, I am a blacksmith who refines a very limited number of items."; diff --git a/npc/re/merchants/refine.txt b/npc/re/merchants/refine.txt index 6356acfca..879e9a5f1 100644 --- a/npc/re/merchants/refine.txt +++ b/npc/re/merchants/refine.txt @@ -56,6 +56,17 @@ payon_in01,18,132,3 script Vestri#pay 4_M_DWARF,{ // On official servers, if an item is unsuccessfully refined it will break at a // 20% rate and downgrade at an 80% rate. function script refinenew { + mesf("[%s]", getarg(0)); + mes("I'm a blacksmith skilled in refining weapons and armors."); + mes("I can refine an item of your choice among the items you are equipped with."); + mes("Which item do you want to refine?"); + + if (getbattleflag("features/replace_refine_npcs") == 1) { + if (openrefineryui()) + close(); + } + next(); + disable_items; mes "["+ getarg(0) +"]"; mes "I am the best Armsmith ever!"; diff --git a/npc/re/merchants/shadow_refiner.txt b/npc/re/merchants/shadow_refiner.txt index f03d348b2..db9668b6d 100644 --- a/npc/re/merchants/shadow_refiner.txt +++ b/npc/re/merchants/shadow_refiner.txt @@ -39,11 +39,17 @@ itemmall,31,76,3 script Shadow Blacksmith#nomal 4_F_JOB_BLACKSMITH,{ .@npc_name$ = "[Shadow Blacksmith]"; .@zeny_cost = 20000; // Amount of zeny to be charged for refining. - disable_items; mesf("%s", .@npc_name$); mes("Do you want to refine a Shadow item?"); mes("Please choose the part you want to refine."); - next; + + if (getbattleflag("features/replace_refine_npcs") == 1) { + if (openrefineryui()) + close(); + } + next(); + + disable_items; setarray(.@position$[0],"Armor","Weapon","Shield","Shoes","Earring","Pendant"); for (.@i=EQI_SHADOW_ARMOR; .@i <= EQI_SHADOW_ACC_L; .@i++){ .@menu$ = .@menu$ + (getequipisequiped(.@i) ? getequipname(.@i) : ("^8C8C8C" + .@position$[.@i-EQI_SHADOW_ARMOR] + " [Not Equipped]^000000" + ":")); diff --git a/src/common/mmo.h b/src/common/mmo.h index aafa54008..eb74d62b3 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -1366,6 +1366,10 @@ enum questinfo_type { #define MAX_ITEMLIST MAX_STORAGE #endif +#ifndef MAX_REFINE_REQUIREMENTS + #define MAX_REFINE_REQUIREMENTS 4 +#endif + // sanity checks... #if MAX_ZENY > INT_MAX #error MAX_ZENY is too big @@ -1379,4 +1383,8 @@ enum questinfo_type { #error MAX_SKILL has been replaced by MAX_SKILL_DB. Please update your custom definitions. #endif +#if MAX_REFINE_REQUIREMENTS > 4 +#error MAX_REFINE_REQUIREMENTS is bigger than allowed, this is a hardcoded limit in the client +#endif + #endif /* COMMON_MMO_H */ diff --git a/src/map/atcommand.c b/src/map/atcommand.c index e97822e69..09303912b 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -9829,6 +9829,22 @@ ACMD(camerainfo) return true; } +ACMD(refineryui) +{ +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) + if (battle_config.enable_refinery_ui == 0) { + clif->message(fd, msg_fd(fd, 453)); + return false; + } + + clif->OpenRefineryUI(sd); + return true; +#else + clif->message(fd, msg_fd(fd, 453)); + return false; +#endif +} + /** * Fills the reference of available commands in atcommand DBMap **/ @@ -10112,6 +10128,7 @@ static void atcommand_basecommands(void) ACMD_DEF(reloadclans), ACMD_DEF(setzone), ACMD_DEF(camerainfo), + ACMD_DEF(refineryui), }; int i; diff --git a/src/map/battle.c b/src/map/battle.c index b06de267d..2f33e2ea9 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -7419,6 +7419,8 @@ static const struct battle_data { { "ping_time", &battle_config.ping_time, 20, 0, 99999999, }, { "option_drop_max_loop", &battle_config.option_drop_max_loop, 10, 1, 100000, }, { "drop_connection_on_quit", &battle_config.drop_connection_on_quit, 0, 0, 1, }, + {"features/enable_refinery_ui", &battle_config.enable_refinery_ui, 1, 0, 1, }, + {"features/replace_refine_npcs", &battle_config.replace_refine_npcs, 1, 0, 1, }, }; static bool battle_set_value_sub(int index, int value) @@ -7544,6 +7546,18 @@ static void battle_adjust_conf(void) } #endif +#if !(PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO)) + if (battle_config.enable_refinery_ui == 1) { + ShowWarning("conf/map/battle/feature.conf refinery ui is enabled but it requires PACKETVER 2016-11-09 RagexeRE/2016-11-30 Ragexe or newer, disabling...\n"); + battle_config.enable_refinery_ui = 0; + } + + if (battle_config.replace_refine_npcs == 1) { + ShowWarning("conf/map/battle/feature.conf replace refine npcs is enabled but it requires PACKETVER 2016-11-09 RagexeRE/2016-11-30 Ragexe or newer, disabling...\n"); + battle_config.replace_refine_npcs = 0; + } +#endif + #ifndef CELL_NOSTACK if (battle_config.custom_cell_stack_limit != 1) ShowWarning("Battle setting 'custom_cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n"); diff --git a/src/map/battle.h b/src/map/battle.h index 8743274ee..1640a4e7f 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -587,6 +587,8 @@ struct Battle_Config { int option_drop_max_loop; int drop_connection_on_quit; + int enable_refinery_ui; + int replace_refine_npcs; }; /* criteria for battle_config.idletime_critera */ diff --git a/src/map/clif.c b/src/map/clif.c index 299c69a1c..b8a54166a 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -49,6 +49,7 @@ #include "map/pet.h" #include "map/quest.h" #include "map/rodex.h" +#include "map/refine.h" #include "map/script.h" #include "map/skill.h" #include "map/status.h" @@ -22492,6 +22493,87 @@ static void clif_parse_ResetCooldown(int fd, struct map_session_data *sd) atcommand->exec(fd, sd, cmd, true); } +static void clif_OpenRefineryUI(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + + if (battle_config.enable_refinery_ui == 0) + return; + + struct PACKET_ZC_REFINE_OPEN_WINDOW p; + p.packetType = HEADER_ZC_REFINE_OPEN_WINDOW; + clif->send(&p, sizeof(p), &sd->bl, SELF); + + sd->state.refine_ui = 1; +#endif +} + +static void clif_parse_AddItemRefineryUI(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_AddItemRefineryUI(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) + if (battle_config.enable_refinery_ui == 0) + return; + + const struct PACKET_CZ_REFINE_ADD_ITEM *p = RFIFO2PTR(fd); + refine->refinery_add_item(sd, p->index - 2); +#endif +} + +static void clif_AddItemRefineryUIAck(struct map_session_data *sd, int item_index, struct s_refine_requirement *req) +{ +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + nullpo_retv(req); + Assert_retv(item_index >= 0 && item_index < sd->status.inventorySize); + + if (battle_config.enable_refinery_ui == 0) + return; + + char buf[sizeof(struct PACKET_ZC_REFINE_ADD_ITEM) + sizeof(struct PACKET_ZC_REFINE_ADD_ITEM_SUB) * MAX_REFINE_REQUIREMENTS]; + struct PACKET_ZC_REFINE_ADD_ITEM *p = (struct PACKET_ZC_REFINE_ADD_ITEM *)buf; + + p->packetType = HEADER_ZC_REFINE_ADD_ITEM; + p->packtLength = sizeof(*p) + sizeof(p->req[0]) * req->req_count; + p->itemIndex = item_index + 2; + p->blacksmithBlessing = req->blacksmith_blessing; + + int weapon_level = itemdb_wlv(sd->status.inventory[item_index].nameid); + for (int i = 0; i < req->req_count; ++i) { + p->req[i].chance = refine->get_refine_chance(weapon_level, sd->status.inventory[item_index].refine, req->req[i].type); + p->req[i].itemId = req->req[i].nameid; + p->req[i].zeny = req->req[i].cost; + } + + clif->send(p, p->packtLength, &sd->bl, SELF); +#endif +} + +static void clif_parse_RefineryUIRefine(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_RefineryUIRefine(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) + if (battle_config.enable_refinery_ui == 0) + return; + + const struct PACKET_CZ_REFINE_ITEM_REQUEST *p = RFIFO2PTR(fd); + refine->refinery_refine_request(sd, p->index - 2, p->itemId, (p->blacksmithBlessing == 1) ? true : false); +#endif +} + +static void clif_parse_RefineryUIClose(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_RefineryUIClose(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) + if (battle_config.enable_refinery_ui == 0) + return; + + sd->state.refine_ui = 0; + return; +#endif +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -23705,4 +23787,9 @@ void clif_defaults(void) clif->pResetCooldown = clif_parse_ResetCooldown; clif->loadConfirm = clif_loadConfirm; clif->send_selforarea = clif_send_selforarea; + clif->OpenRefineryUI = clif_OpenRefineryUI; + clif->pAddItemRefineryUI = clif_parse_AddItemRefineryUI; + clif->AddItemRefineryUIAck = clif_AddItemRefineryUIAck; + clif->pRefineryUIClose = clif_parse_RefineryUIClose; + clif->pRefineryUIRefine = clif_parse_RefineryUIRefine; } diff --git a/src/map/clif.h b/src/map/clif.h index aaf053274..0515fbd05 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -55,6 +55,7 @@ struct skill_unit; struct unit_data; struct view_data; struct achievement_data; // map/achievement.h +struct s_refine_requirement; enum clif_messages; enum rodex_add_item; @@ -1611,6 +1612,11 @@ struct clif_interface { void (*pResetCooldown) (int fd, struct map_session_data *sd); void (*loadConfirm) (struct map_session_data *sd); void (*send_selforarea) (int fd, struct block_list *bl, const void *buf, int len); + void (*OpenRefineryUI) (struct map_session_data *sd); + void (*pAddItemRefineryUI) (int fd, struct map_session_data *sd); + void (*AddItemRefineryUIAck) (struct map_session_data *sd, int item_index, struct s_refine_requirement *req); + void (*pRefineryUIClose) (int fd, struct map_session_data *sd); + void (*pRefineryUIRefine) (int fd, struct map_session_data *sd); }; #ifdef HERCULES_CORE diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 315787993..e032def0c 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -130,6 +130,7 @@ enum item_itemid { ITEMID_INDIGO_PTS = 6361, ITEMID_YELLOW_WISH_PTS = 6362, ITEMID_LIME_GREEN_PTS = 6363, + ITEMID_BLACKSMITH_BLESSING = 6635, ITEMID_STONE = 7049, ITEMID_FIRE_BOTTLE = 7135, ITEMID_ACID_BOTTLE = 7136, diff --git a/src/map/map.c b/src/map/map.c index d34d421d7..2b95ec27a 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -6409,6 +6409,7 @@ static void map_load_defaults(void) npc_chat_defaults(); rodex_defaults(); stylist_defaults(); + refine_defaults(); } /** * --run-once handler diff --git a/src/map/packets.h b/src/map/packets.h index 99404cbe3..bffec4f43 100644 --- a/src/map/packets.h +++ b/src/map/packets.h @@ -1805,6 +1805,12 @@ packet(0x96e,clif->ackmergeitems); packet(0x0a88,clif->pResetCooldown); #endif +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) + packet(0x0aa1, clif->pAddItemRefineryUI); + packet(0x0aa3, clif->pRefineryUIRefine); + packet(0x0aa4, clif->pRefineryUIClose); +#endif + // 2017-02-28aRagexeRE #if PACKETVER >= 20170228 // new packets diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index 45683596f..33b7759ce 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -3319,6 +3319,76 @@ struct PACKET_ZC_SE_PC_BUY_CASHITEM_RESULT { DEFINE_PACKET_HEADER(ZC_SE_PC_BUY_CASHITEM_RESULT, 0x0849); #endif +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) +struct PACKET_ZC_REFINE_OPEN_WINDOW { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_REFINE_OPEN_WINDOW, 0x0aa0); +#endif + +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) +struct PACKET_CZ_REFINE_ADD_ITEM { + int16 packetType; + int16 index; +}; +DEFINE_PACKET_HEADER(CZ_REFINE_ADD_ITEM, 0x0aa1); +#endif + +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) +struct PACKET_ZC_REFINE_ADD_ITEM_SUB { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + int8 chance; + int32 zeny; +} __attribute__((packed)); + +struct PACKET_ZC_REFINE_ADD_ITEM { + int16 packetType; + int16 packtLength; + int16 itemIndex; + int8 blacksmithBlessing; + struct PACKET_ZC_REFINE_ADD_ITEM_SUB req[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_REFINE_ADD_ITEM, 0x0aa2); +#endif + +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) +struct PACKET_CZ_REFINE_ITEM_REQUEST { + int16 packetType; + int16 index; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + int8 blacksmithBlessing; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_REFINE_ITEM_REQUEST, 0x0aa3); + +struct PACKET_CZ_REFINE_WINDOW_CLOSE { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_REFINE_WINDOW_CLOSE, 0x0aa4); +#endif + +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) +struct PACKET_ZC_REFINE_STATUS { + int16 packetType; + char name[NAME_LENGTH]; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + int8 refine_level; + int8 status; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_REFINE_STATUS, 0x0ada); +#endif + #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute #pragma pack(pop) #endif // not NetBSD < 6 / Solaris diff --git a/src/map/pc.h b/src/map/pc.h index b2069d4df..42c9d204e 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -237,6 +237,7 @@ struct map_session_data { unsigned int standalone : 1;/* [Ind/Hercules <3] */ unsigned int loggingout : 1; unsigned int warp_clean : 1; + unsigned int refine_ui : 1; } state; struct { unsigned char no_weapon_damage, no_magic_damage, no_misc_damage; @@ -660,10 +661,10 @@ END_ZEROED_BLOCK; #define pc_issit(sd) ( (sd)->vd.dead_sit == 2 ) #define pc_isidle(sd) ( (sd)->chat_id != 0 || (sd)->state.vending || (sd)->state.buyingstore || DIFF_TICK(sockt->last_tick, (sd)->idletime) >= battle->bc->idle_no_share ) #define pc_istrading(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->state.trading ) -#define pc_cant_act(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chat_id != 0 || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend ) +#define pc_cant_act(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chat_id != 0 || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend || (sd)->state.refine_ui == 1) /* equals pc_cant_act except it doesn't check for chat rooms */ -#define pc_cant_act2(sd) ( (sd)->npc_id || (sd)->state.buyingstore || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend ) +#define pc_cant_act2(sd) ( (sd)->npc_id || (sd)->state.buyingstore || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend || (sd)->state.refine_ui == 1) #define pc_setdir(sd,b,h) ( (sd)->ud.dir = (b) ,(sd)->head_dir = (h) ) #define pc_setchatid(sd,n) ( (sd)->chat_id = (n) ) diff --git a/src/map/refine.c b/src/map/refine.c index 5d0517d7d..96d37141d 100644 --- a/src/map/refine.c +++ b/src/map/refine.c @@ -22,14 +22,19 @@ #include "refine.p.h" #include "common/cbasetypes.h" -#include "common/mmo.h" #include "common/nullpo.h" +#include "common/random.h" #include "common/showmsg.h" #include "common/strlib.h" +#include "common/utils.h" +#include "map/itemdb.h" #include "map/map.h" +#include "map/pc.h" +#include "map/script.h" #include <stdio.h> #include <stdlib.h> +#include <string.h> /** @file * Implementation of the refine interface. @@ -40,6 +45,151 @@ static struct refine_interface_private refine_p; static struct refine_interface_dbs refine_dbs; struct refine_interface *refine; +/// @copydoc refine_interface::refinery_refine_request() +static void refine_refinery_refine_request(struct map_session_data *sd, int item_index, int material_id, bool use_blacksmith_blessing) +{ + nullpo_retv(sd); + + if (item_index < 0 || item_index >= sd->status.inventorySize) + return; + + if (!refine->p->is_refinable(sd, item_index)) + return; + + int weapon_level = itemdb_wlv(sd->status.inventory[item_index].nameid), + refine_level = sd->status.inventory[item_index].refine, + i = 0; + const struct s_refine_requirement *req = &refine->p->dbs->refine_info[weapon_level].refine_requirements[refine_level]; + ARR_FIND(0, req->req_count, i, req->req[i].nameid == material_id); + + if (i == req->req_count) + return; + + if (use_blacksmith_blessing && req->blacksmith_blessing == 0) + return; + + if (sd->status.zeny < req->req[i].cost) + return; + + if (use_blacksmith_blessing) { + int count = 0; + for (int k = 0; k < sd->status.inventorySize; ++k) { + if (sd->status.inventory[k].nameid == ITEMID_BLACKSMITH_BLESSING) + count += sd->status.inventory[k].amount; + } + + if (count < req->blacksmith_blessing) + return; + } + + int idx; + if ((idx = pc->search_inventory(sd, req->req[i].nameid)) == INDEX_NOT_FOUND) + return; + + if (use_blacksmith_blessing) { + int amount = req->blacksmith_blessing; + for (int k = 0; k < sd->status.inventorySize; ++k) { + if (sd->status.inventory[k].nameid != ITEMID_BLACKSMITH_BLESSING) + continue; + + int delamount = (amount < sd->status.inventory[k].amount) ? amount : sd->status.inventory[k].amount; + if (pc->delitem(sd, k, delamount, 0, DELITEM_NORMAL, LOG_TYPE_REFINE) != 0) + break; + + amount -= delamount; + if (amount == 0) + break; + } + } + + if (pc->delitem(sd, idx, 1, 0, DELITEM_NORMAL, LOG_TYPE_REFINE) != 0) + return; + + if (pc->payzeny(sd, req->req[i].cost, LOG_TYPE_REFINE, NULL) != 0) + return; + + int refine_chance = refine->get_refine_chance(weapon_level, refine_level, req->req[i].type); + if (rnd() % 100 >= refine_chance) { + clif->misceffect(&sd->bl, 2); + + int failure_behabior = (use_blacksmith_blessing) ? REFINE_FAILURE_BEHAVIOR_KEEP : req->req[i].failure_behavior; + switch (failure_behabior) { + case REFINE_FAILURE_BEHAVIOR_KEEP: + clif->refine(sd->fd, 1, 0, sd->status.inventory[item_index].refine); + refine->refinery_add_item(sd, item_index); + break; + case REFINE_FAILURE_BEHAVIOR_DOWNGRADE: + sd->status.inventory[item_index].refine -= 1; + sd->status.inventory[item_index].refine = cap_value(sd->status.inventory[item_index].refine, 0, MAX_REFINE); + clif->refine(sd->fd, 2, item_index, sd->status.inventory[item_index].refine); + logs->pick_pc(sd, LOG_TYPE_REFINE, 1, &sd->status.inventory[item_index], sd->inventory_data[item_index]); + refine->refinery_add_item(sd, item_index); + break; + case REFINE_FAILURE_BEHAVIOR_DESTROY: + default: + clif->refine(sd->fd, 1, item_index, sd->status.inventory[item_index].refine); + pc->delitem(sd, item_index, 1, 0, DELITEM_FAILREFINE, LOG_TYPE_REFINE); + break; + } + } else { + sd->status.inventory[item_index].refine += 1; + sd->status.inventory[item_index].refine = cap_value(sd->status.inventory[item_index].refine, 0, MAX_REFINE); + + clif->misceffect(&sd->bl, 3); + clif->refine(sd->fd, 0, item_index, sd->status.inventory[item_index].refine); + logs->pick_pc(sd, LOG_TYPE_REFINE, 1, &sd->status.inventory[item_index], sd->inventory_data[item_index]); + refine->refinery_add_item(sd, item_index); + } +} + +/// @copydoc refine_interface::refinery_add_item() +static void refine_refinery_add_item(struct map_session_data *sd, int item_index) +{ + nullpo_retv(sd); + + if (item_index < 0 || item_index >= sd->status.inventorySize) + return; + + if (!refine->p->is_refinable(sd, item_index)) + return; + + int weapon_level = itemdb_wlv(sd->status.inventory[item_index].nameid); + int refine_level = sd->status.inventory[item_index].refine; + clif->AddItemRefineryUIAck(sd, item_index, &refine->p->dbs->refine_info[weapon_level].refine_requirements[refine_level]); +} + +/// @copydoc refine_interface_private::is_refinable() +static bool refine_is_refinable(struct map_session_data *sd, int item_index) +{ + nullpo_retr(false, sd); + Assert_retr(false, item_index >= 0 && item_index < sd->status.inventorySize); + + if (sd->status.inventory[item_index].nameid == 0) + return false; + + struct item_data *itd = itemdb->search(sd->status.inventory[item_index].nameid); + + if (itd == &itemdb->dummy) + return false; + + if (itd->type != IT_WEAPON && itd->type != IT_ARMOR) + return false; + + if (itd->flag.no_refine == 1) + return false; + + if (sd->status.inventory[item_index].identify == 0) + return false; + + if (sd->status.inventory[item_index].refine >= MAX_REFINE || sd->status.inventory[item_index].expire_time > 0) + return false; + + if ((sd->status.inventory[item_index].attribute & ATTR_BROKEN) != 0) + return false; + + return true; +} + /// @copydoc refine_interface::get_randombonus_max() static int refine_get_randombonus_max(enum refine_type equipment_type, int refine_level) { @@ -72,6 +222,221 @@ static int refine_get_refine_chance(enum refine_type wlv, int refine_level, enum return refine->p->dbs->refine_info[wlv].chance[type][refine_level]; } +/// @copydoc refine_interface_private::failure_behavior_string2enum() +static bool refine_failure_behavior_string2enum(const char *str, enum refine_ui_failure_behavior *result) +{ + nullpo_retr(false, str); + nullpo_retr(false, result); + + if (strcasecmp(str, "Destroy") == 0) + *result = REFINE_FAILURE_BEHAVIOR_DESTROY; + else if (strcasecmp(str, "Keep") == 0) + *result = REFINE_FAILURE_BEHAVIOR_KEEP; + else if (strcasecmp(str, "Downgrade") == 0) + *result = REFINE_FAILURE_BEHAVIOR_DOWNGRADE; + else + return false; + + return true; +} + +/// @copydoc refine_interface_private::readdb_refinery_ui_settings_items() +static bool refine_readdb_refinery_ui_settings_items(const struct config_setting_t *elem, struct s_refine_requirement *req, const char *name, const char *source) +{ + nullpo_retr(false, elem); + nullpo_retr(false, req); + nullpo_retr(false, name); + nullpo_retr(false, source); + Assert_retr(false, req->req_count < MAX_REFINE_REQUIREMENTS); + + const char *aegis_name = config_setting_name(elem); + struct item_data *itd; + + if ((itd = itemdb->search_name(aegis_name)) == NULL) { + ShowWarning("refine_readdb_requirements_items: Invalid item '%s' passed to requirements of '%s' in \"%s\" skipping...\n", aegis_name, name, source); + return false; + } + + for (int i = 0; i < req->req_count; ++i) { + if (req->req[i].nameid == itd->nameid) { + ShowWarning("refine_readdb_requirements_items: Duplicated item '%s' passed to requirements of '%s' in \"%s\" skipping...\n", aegis_name, name, source); + return false; + } + } + + const char *type_string = NULL; + if (libconfig->setting_lookup_string(elem, "Type", &type_string) == CONFIG_FALSE) { + ShowWarning("refine_readdb_requirements_items: no type passed to item '%s' of requirements of '%s' in \"%s\" skipping...\n", aegis_name, name, source); + return false; + } + + int type; + if (!script->get_constant(type_string, &type)) { + ShowWarning("refine_readdb_requirements_items: invalid type '%s' passed to item '%s' of requirements of '%s' in \"%s\" skipping...\n", type_string, aegis_name, name, source); + return false; + } + + int cost = 0; + if (libconfig->setting_lookup_int(elem, "Cost", &cost) == CONFIG_TRUE) { + if (cost < 1) { + ShowWarning("refine_readdb_requirements_items: invalid cost value %d passed to item '%s' of requirements of '%s' in \"%s\" defaulting to 0...\n", cost, aegis_name, name, source); + cost = 0; + } + } + + enum refine_ui_failure_behavior behavior = REFINE_FAILURE_BEHAVIOR_DESTROY; + const char *behavior_string = NULL; + if (libconfig->setting_lookup_string(elem, "FailureBehavior", &behavior_string) != CONFIG_FALSE) { + if (!refine->p->failure_behavior_string2enum(behavior_string, &behavior)) { + ShowWarning("refine_readdb_requirements_items: invalid failure behavior value %s passed to item '%s' of requirements of '%s' in \"%s\" defaulting to 'Destroy'...\n", behavior_string, aegis_name, name, source); + } + } + + req->req[req->req_count].nameid = itd->nameid; + req->req[req->req_count].type = type; + req->req[req->req_count].cost = cost; + req->req[req->req_count].failure_behavior = behavior; + req->req_count++; + + return true; +} + +/// @copydoc refine_interface_private::readdb_refinery_ui_settings_sub() +static bool refine_readdb_refinery_ui_settings_sub(const struct config_setting_t *elem, int type, const char *name, const char *source) +{ + nullpo_retr(false, elem); + nullpo_retr(false, name); + nullpo_retr(false, source); + Assert_retr(0, type >= REFINE_TYPE_ARMOR && type < REFINE_TYPE_MAX); + + struct config_setting_t *level_t; + bool levels[MAX_REFINE] = {0}; + + if ((level_t = libconfig->setting_get_member(elem, "Level")) == NULL) { + ShowWarning("refine_readdb_requirements_sub: a requirements element missing level field for entry '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + if (config_setting_is_scalar(level_t)) { + if (!config_setting_is_number(level_t)) { + ShowWarning("refine_readdb_requirements_sub: expected 'Level' field to be an integer '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + int refine_level = libconfig->setting_get_int(level_t); + if (refine_level < 1 || refine_level > MAX_REFINE) { + ShowWarning("refine_readdb_requirements_sub: Invalid 'Level' given value %d expected a value between %d and %d '%s' in \"%s\" skipping...\n", refine_level, 1, MAX_REFINE, name, source); + return false; + } + + levels[refine_level - 1] = true; + } else if (config_setting_is_aggregate(level_t)) { + if (libconfig->setting_length(level_t) != 2) { + ShowWarning("refine_readdb_requirements_sub: invalid length for Level array, expected 2 found %d for entry '%s' in \"%s\" skipping...\n", libconfig->setting_length(level_t), name, source); + return false; + } + + int levels_range[2]; + const struct config_setting_t *level_entry = NULL; + int i = 0, + k = 0; + while ((level_entry = libconfig->setting_get_elem(level_t, i++)) != NULL) { + if (!config_setting_is_number(level_entry)) { + ShowWarning("refine_readdb_requirements_sub: expected 'Level' array field to be an integer '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + levels_range[k] = libconfig->setting_get_int(level_entry); + if (levels_range[k] < 1 || levels_range[k] > MAX_REFINE) { + ShowWarning("refine_readdb_requirements_sub: Invalid 'Level' given value %d expected a value between %d and %d in entry'%s' in \"%s\" skipping...\n", levels_range[k], 1, MAX_REFINE, name, source); + return false; + } + + ++k; + } + + if (!(levels_range[0] < levels_range[1])) { + ShowWarning("refine_readdb_requirements_sub: Invalid 'Level' range was given low %d high %d in entry'%s' in \"%s\" skipping...\n", levels_range[0], levels_range[1], name, source); + return false; + } + + for (i = levels_range[0] - 1; i < levels_range[1]; ++i) { + levels[i] = true; + } + } + + struct s_refine_requirement req = {0}; + if (libconfig->setting_lookup_int(elem, "BlacksmithBlessing", &req.blacksmith_blessing) == CONFIG_TRUE) { + if (req.blacksmith_blessing < 1 || req.blacksmith_blessing > INT8_MAX) { + ShowWarning("refine_readdb_requirements_sub: Invalid 'BlacksmithBlessing' amount was given value %d expected a value between %d and %d in entry'%s' in \"%s\" defaulting to 0...\n", req.blacksmith_blessing, 1, INT8_MAX, name, source); + req.blacksmith_blessing = 0; + } + } + + struct config_setting_t *items_t; + if ((items_t = libconfig->setting_get_member(elem, "Items")) == NULL) { + ShowWarning("refine_readdb_requirements_sub: a requirements element missing Items element for entry '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + if (libconfig->setting_length(items_t) < 1) { + ShowWarning("refine_readdb_requirements_sub: an Items element containing no items passed for entry '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + int loaded_items = 0; + for (int i = 0; i < libconfig->setting_length(items_t); ++i) { + if (req.req_count >= MAX_REFINE_REQUIREMENTS) { + ShowWarning("refine_readdb_requirements_sub: Too many items passed to requirements maximum possible items is %d entry '%s' in \"%s\" skipping...\n", MAX_REFINE_REQUIREMENTS, name, source); + continue; + } + + struct config_setting_t *item_t = libconfig->setting_get_elem(items_t, i); + + if (!refine->p->readdb_refinery_ui_settings_items(item_t, &req, name, source)) + continue; + + loaded_items++; + } + + if (loaded_items == 0) { + ShowWarning("refine_readdb_requirements_sub: no valid items for requirements is passed for entry '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + for (int i = 0; i < MAX_REFINE; ++i) { + if (!levels[i]) + continue; + + refine->p->dbs->refine_info[type].refine_requirements[i] = req; + } + + return true; +} + +/// @copydoc refine_interface_private::readdb_refinery_ui_settings() +static int refine_readdb_refinery_ui_settings(const struct config_setting_t *r, int type, const char *name, const char *source) +{ + nullpo_retr(0, r); + nullpo_retr(0, name); + nullpo_retr(0, source); + Assert_retr(0, type >= REFINE_TYPE_ARMOR && type < REFINE_TYPE_MAX); + + int i = 0; + const struct config_setting_t *elem = NULL; + while ((elem = libconfig->setting_get_elem(r, i++)) != NULL) { + refine->p->readdb_refinery_ui_settings_sub(elem, type, name, source); + } + + int retval = 0; + for (i = 0; i < MAX_REFINE; ++i) { + if (refine->p->dbs->refine_info[type].refine_requirements[i].req_count > 0) + retval++; + } + + return retval; +} + /// @copydoc refine_interface_private::readdb_refine_libconfig_sub() static int refine_readdb_refine_libconfig_sub(struct config_setting_t *r, const char *name, const char *source) { @@ -92,6 +457,18 @@ static int refine_readdb_refine_libconfig_sub(struct config_setting_t *r, const ShowError("status_readdb_refine_libconfig_sub: Out of range level for entry '%s' in \"%s\", skipping.\n", name, source); return 0; } + + struct config_setting_t *refinery_ui_settings; + if ((refinery_ui_settings = libconfig->setting_get_member(r, "RefineryUISettings")) == NULL) { + ShowWarning("status_readdb_refine_libconfig_sub: Missing Requirements for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + + if (refine->p->readdb_refinery_ui_settings(refinery_ui_settings, type, name, source) != MAX_REFINE) { + ShowWarning("status_readdb_refine_libconfig_sub: Not all refine levels have requrements entry for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + if (!libconfig->setting_lookup_int(r, "StatsPerLevel", &bonus_per_level)) { ShowWarning("status_readdb_refine_libconfig_sub: Missing StatsPerLevel for entry '%s' in \"%s\", skipping.\n", name, source); return 0; @@ -191,6 +568,8 @@ static int refine_readdb_refine_libconfig_sub(struct config_setting_t *r, const /// @copydoc refine_interface_private::readdb_refine_libconfig() static int refine_readdb_refine_libconfig(const char *filename) { + nullpo_retr(0, filename); + bool duplicate[REFINE_TYPE_MAX]; struct config_t refine_db_conf; struct config_setting_t *r; @@ -244,9 +623,16 @@ void refine_defaults(void) refine->p->readdb_refine_libconfig = refine_readdb_refine_libconfig; refine->p->readdb_refine_libconfig_sub = refine_readdb_refine_libconfig_sub; + refine->p->failure_behavior_string2enum = refine_failure_behavior_string2enum; + refine->p->readdb_refinery_ui_settings_items = refine_readdb_refinery_ui_settings_items; + refine->p->readdb_refinery_ui_settings_sub = refine_readdb_refinery_ui_settings_sub; + refine->p->readdb_refinery_ui_settings = refine_readdb_refinery_ui_settings; + refine->p->is_refinable = refine_is_refinable; refine->init = refine_init; refine->final = refine_final; + refine->refinery_refine_request = refine_refinery_refine_request; + refine->refinery_add_item = refine_refinery_add_item; refine->get_refine_chance = refine_get_refine_chance; refine->get_bonus = refine_get_bonus; refine->get_randombonus_max = refine_get_randombonus_max; diff --git a/src/map/refine.h b/src/map/refine.h index 850f6143a..100d2c6b2 100644 --- a/src/map/refine.h +++ b/src/map/refine.h @@ -25,6 +25,7 @@ * Refine Interface. **/ #include "common/hercules.h" +#include "common/mmo.h" /* Defines */ /** @@ -60,6 +61,24 @@ enum refine_chance_type { REFINE_CHANCE_TYPE_MAX }; +enum refine_ui_failure_behavior { + REFINE_FAILURE_BEHAVIOR_DESTROY, + REFINE_FAILURE_BEHAVIOR_KEEP, + REFINE_FAILURE_BEHAVIOR_DOWNGRADE +}; + +/* Structure */ +struct s_refine_requirement { + int blacksmith_blessing; + int req_count; + struct { + int nameid; + int cost; + enum refine_chance_type type; + enum refine_ui_failure_behavior failure_behavior; + } req[MAX_REFINE_REQUIREMENTS]; +}; + /** * Refine Interface **/ @@ -101,6 +120,22 @@ struct refine_interface { * @return returns the bonus from refine db **/ int(*get_randombonus_max) (enum refine_type equipment_type, int refine_level); + + /** + * Validates and send Item addition packet to the client for refinery UI + * @param sd player session data. + * @param item_index the requested item index in inventory. + **/ + void (*refinery_add_item) (struct map_session_data *sd, int item_index); + + /** + * Processes an refine request through Refinery UI + * @param sd player session data + * @param item_index the index of the requested item + * @param material_id the refine material chosen by player + * @param use_blacksmith_blessing sets either if blacksmith blessing is requested to be used or not + **/ + void (*refinery_refine_request) (struct map_session_data *sd, int item_index, int material_id, bool use_blacksmith_blessing); }; #ifdef HERCULES_CORE diff --git a/src/map/refine.p.h b/src/map/refine.p.h index d47944315..e1a24f6b7 100644 --- a/src/map/refine.p.h +++ b/src/map/refine.p.h @@ -28,11 +28,11 @@ #include "refine.h" #include "common/conf.h" -/* Structures */ struct s_refine_info { - int chance[REFINE_CHANCE_TYPE_MAX][MAX_REFINE]; // success chance - int bonus[MAX_REFINE]; // cumulative fixed bonus damage - int randombonus_max[MAX_REFINE]; // cumulative maximum random bonus damage + int chance[REFINE_CHANCE_TYPE_MAX][MAX_REFINE]; //< success chance + int bonus[MAX_REFINE]; //< cumulative fixed bonus damage + int randombonus_max[MAX_REFINE]; //< cumulative maximum random bonus damage + struct s_refine_requirement refine_requirements[MAX_REFINE]; //< The requirements used for refinery UI }; struct refine_interface_dbs { @@ -66,6 +66,64 @@ struct refine_interface_private { * @return The number of found entries. **/ int (*readdb_refine_libconfig) (const char *filename); + + /** + * Converts refine database failure behvaior string to enum refine_ui_failure_behavior + * @param str the string to convert + * @param result pointer to where the converted value will be held + * @return true on success, false otherwise. + **/ + bool (*failure_behavior_string2enum) (const char *str, enum refine_ui_failure_behavior *result); + + /** + * Processes a refine_db.conf RefineryUISettings items entry. + * + * @param elem Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param req a pointer to requirements struct to fill with the item data + * @param name the current element name + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return true on success, false otherwise. + **/ + bool (*readdb_refinery_ui_settings_items) (const struct config_setting_t *elem, struct s_refine_requirement *req, const char *name, const char *source); + + /** + * Processes a refine_db.conf RefineryUISettings entry. + * + * @param elem Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param type the type index in refine database to fill the data + * @param name the current element name + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return true on success, false otherwise. + **/ + bool (*readdb_refinery_ui_settings_sub) (const struct config_setting_t *elem, int type, const char *name, const char *source); + + /** + * Reads a refine_db.conf RefineryUISettings entry and sends it to be processed. + * + * @param r Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param type the type index in refine database to fill the data + * @param name the current element name + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return true on success, false otherwise. + **/ + int (*readdb_refinery_ui_settings) (const struct config_setting_t *r, int type, const char *name, const char *source); + + /** + * Checks if a given item in player's inventory is refineable. + * @param sd player session data. + * @param item_index the item index in player's inventory. + * @return true if item is refineable, false otherwise. + **/ + bool (*is_refinable) (struct map_session_data *sd, int item_index); }; #endif diff --git a/src/map/script.c b/src/map/script.c index 1cc1d3ad2..5843ac292 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -25286,6 +25286,25 @@ static BUILDIN(closeroulette) return true; } +static BUILDIN(openrefineryui) +{ + struct map_session_data *sd = script_rid2sd(st); + + if (sd == NULL) { + script_pushint(st, 0); + return true; + } + + if (battle_config.enable_refinery_ui == 0) { + script_pushint(st, 0); + return true; + } + + clif->OpenRefineryUI(sd); + script_pushint(st, 1); + return true; +} + /** * Adds a built-in script function. * @@ -26039,6 +26058,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(getInventorySize, ""), BUILDIN_DEF(closeroulette, ""), + BUILDIN_DEF(openrefineryui, ""), }; int i, len = ARRAYLENGTH(BUILDIN); RECREATE(script->buildin, char *, script->buildin_count + len); // Pre-alloc to speed up |