Browse Source

* Implemented buying store system (aka. reverse vending, purchase shop) together with related skill and items, without NPCs.
- For SQL apply upgrade_svn14713_log.sql to upgrade tables `picklog` and `zenylog`; for TXT no action is necessary.
- Requires 2010-04-20aRagexeRE or later and can be disabled in 'conf/battle/feature.conf'.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@14713 54d463be-8e91-2dee-dedb-b68131a5f0ec

ai4rei 14 năm trước cách đây
mục cha
commit
ce3f78556c

+ 4 - 0
Changelog-Trunk.txt

@@ -1,5 +1,9 @@
 Date	Added
 
+2011/02/19
+	* Implemented buying store system (aka. reverse vending, purchase shop) together with related skill and items, without NPCs. [Ai4rei]
+	- For SQL apply upgrade_svn14713_log.sql to upgrade tables `picklog` and `zenylog`; for TXT no action is necessary.
+	- Requires 2010-04-20aRagexeRE or later and can be disabled in 'conf/battle/feature.conf'.
 2011/02/17
 	* Merged enumeration update from renewal [14699/branches/renewal] for a future commit. [Ai4rei]
 2011/02/16

+ 3 - 0
conf/Changelog.txt

@@ -1,5 +1,8 @@
 Date	Added
 
+2011/02/19
+	* Rev. 14713 Added map-server feature settings file 'battle/feature.conf'. [Ai4rei]
+	- Added setting 'feature.buying_store' to enable/disable the buying store system.
 2011/02/15
 	* Rev. 14707 Added map-server battle setting 'gm_check_minlevel'. [Ai4rei]
 2011/02/06

+ 24 - 0
conf/battle/feature.conf

@@ -0,0 +1,24 @@
+//      ______  __    __                                 
+//     /\  _  \/\ \__/\ \                                
+//   __\ \ \L\ \ \ ,_\ \ \___      __    ___      __     
+// /'__`\ \  __ \ \ \/\ \  _ `\  /'__`\/' _ `\  /'__`\   
+///\  __/\ \ \/\ \ \ \_\ \ \ \ \/\  __//\ \/\ \/\ \L\.\_ 
+//\ \____\\ \_\ \_\ \__\\ \_\ \_\ \____\ \_\ \_\ \__/.\_\
+// \/____/ \/_/\/_/\/__/ \/_/\/_/\/____/\/_/\/_/\/__/\/_/
+//  _   _   _   _   _   _   _     _   _   _   _   _   _
+// / \ / \ / \ / \ / \ / \ / \   / \ / \ / \ / \ / \ / \ 
+//( e | n | g | l | i | s | h ) ( A | t | h | e | n | a )
+// \_/ \_/ \_/ \_/ \_/ \_/ \_/   \_/ \_/ \_/ \_/ \_/ \_/
+//
+//--------------------------------------------------------------
+// eAthena Feature Configuration File
+//--------------------------------------------------------------
+// Note 1: Value is a config switch (on/off, yes/no or 1/0)
+// Note 2: Value is in percents (100 means 100%)
+// Note 3: Value is a bit field. If no description is given,
+//         assume unit types (1: Pc, 2: Mob, 4: Pet, 8: Homun)
+//--------------------------------------------------------------
+
+// Buying store (Note 1)
+// Requires: 2010-04-20aRagexeRE or later
+feature.buying_store: on

+ 1 - 0
conf/log_athena.conf

@@ -16,6 +16,7 @@
 // 2048 - (R) Log items placed/retrieved from storage.
 // 4096 - (G) Log items placed/retrieved from guild storage.
 // 8192 - (E) Log mail system transactions.
+// 16384 - (B) Log buying store transactions
 // Example: Log trades+vending+script items+created items: 2+4+64+1024 = 1094
 enable_logs: 1
 

+ 6 - 0
db/Changelog.txt

@@ -9,6 +9,12 @@
 	13005 Angelic Wing Dagger:	NEED INFO.
 =======================
 
+2011/02/19
+	* Rev. 14713 Database updates required by buying store system implementation. [Ai4rei]
+	- Added database of items, that can be sold to a buying store (item_buyingstore.txt).
+	- Added items Buy_Stall_Permit (6377) and Shabby_Purchase_Street_Stall_License (12548).
+	- Updated packet database with buying store related packets.
+	- Added skill 'Open Buying Store' (ALL_BUYING_STORE).
 2011/02/06
 	* Rev. 14697 Added the missing restricted skills to zone 6 for Endless Tower. (bugreport:4707) [L0ne_W0lf]
 	* Adjusted the rates for item using bAddEffOnSkill bonus. 

+ 1013 - 0
db/item_buyingstore.txt

@@ -0,0 +1,1013 @@
+// Buying Store Item List
+// List of items, that can be sold to buying stores.
+// Format: <item id>
+// Example:
+//  512  // Apple
+// Note:
+//  Items are in same order as data\buyingstoreitemlist.txt, which
+//  must be edited as well for the client to accept added items.
+
+// items from 2010-03-12 update
+601  // Wing_Of_Fly
+602  // Wing_Of_Butterfly
+603  // Old_Blue_Box
+604  // Branch_Of_Dead_Tree
+605  // Anodyne
+606  // Aloebera
+607  // Yggdrasilberry
+608  // Seed_Of_Yggdrasil
+609  // Amulet
+610  // Leaf_Of_Yggdrasil
+611  // Spectacles
+612  // Portable_Furnace
+613  // Iron_Hammer
+614  // Golden_Hammer
+615  // Oridecon_Hammer
+616  // Old_Card_Album
+617  // Old_Violet_Box
+618  // Worn_Out_Scroll
+619  // Unripe_Apple
+620  // Orange_Juice
+621  // Bitter_Herb
+622  // Rainbow_Carrot
+623  // Earthworm_The_Dude
+624  // Rotten_Fish
+625  // Lusty_Iron
+626  // Monster_Juice
+627  // Sweet_Milk
+628  // Well_Dried_Bone
+629  // Singing_Flower
+630  // Dew_Laden_Moss
+631  // Deadly_Noxious_Herb
+632  // Fatty_Chubby_Earthworm
+633  // Baked_Yam
+634  // Tropical_Banana
+635  // Horror_Of_Tribe
+636  // No_Recipient
+637  // Old_Broom
+638  // Silver_Knife_Of_Chaste
+639  // Armlet_Of_Obedience
+640  // Shining_Stone
+641  // Contracts_In_Shadow
+642  // Book_Of_Devil
+643  // Pet_Incubator
+644  // Gift_Box
+645  // Center_Potion
+656  // Awakening_Potion
+657  // Berserk_Potion
+658  // Union_Of_Tribe
+659  // Heart_Of_Her
+660  // Prohibition_Red_Candle
+661  // Sway_Apron
+662  // Inspector_Certificate
+663  // Korea_Rice_Cake
+664  // Gift_Box_1
+665  // Gift_Box_2
+666  // Gift_Box_3
+667  // Gift_Box_4
+668  // Handsei
+669  // Rice_Cake_Soup
+678  // Poison_Bottle
+679  // Gold_Pill
+681  // Memory_Of_Wedding
+682  // Realgar_Wine
+683  // Exorcize_Herb
+684  // Durian
+686  // Earth_Scroll_1_3
+687  // Earth_Scroll_1_5
+688  // Cold_Scroll_1_3
+689  // Cold_Scroll_1_5
+690  // Fire_Scroll_1_3
+691  // Fire_Scroll_1_5
+692  // Wind_Scroll_1_3
+693  // Wind_Scroll_1_5
+694  // Ghost_Scroll_1_3
+695  // Ghost_Scroll_1_5
+696  // Fire_Scroll_2_1
+697  // Fire_Scroll_2_5
+698  // Fire_Scroll_3_1
+699  // Fire_Scroll_3_5
+700  // Cold_Scroll_2_1
+12000  // Cold_Scroll_2_5
+12001  // Holy_Scroll_1_3
+12002  // Holy_Scroll_1_5
+12003  // Holy_Scroll_2_1
+12004  // Arrow_Container
+12005  // Iron_Arrow_Container
+12006  // Steel_Arrow_Container
+12007  // Ori_Arrow_Container
+12008  // Fire_Arrow_Container
+12009  // Silver_Arrow_Container
+12010  // Wind_Arrow_Container
+12011  // Stone_Arrow_Container
+12012  // Crystal_Arrow_Container
+12013  // Shadow_Arrow_Container
+12014  // Imma_Arrow_Container
+12015  // Rusty_Arrow_Container
+12016  // Speed_Up_Potion
+12017  // Slow_Down_Potion
+12018  // Fire_Cracker
+12020  // Water_Of_Darkness
+12027  // Giggling_Box
+12028  // Box_Of_Thunder
+12029  // Gloomy_Box
+12030  // Box_Of_Grudge
+12031  // Sleepy_Box
+12032  // Box_Of_Storm
+12033  // Box_Of_Sunlight
+12034  // Painting_Box
+12040  // Stone_Of_Intelligence_
+12041  // Str_Dish01
+12042  // Str_Dish02
+12043  // Str_Dish03
+12044  // Str_Dish04
+12045  // Str_Dish05
+12046  // Int_Dish01
+12047  // Int_Dish02
+12048  // Int_Dish03
+12049  // Int_Dish04
+12050  // Int_Dish05
+12051  // Vit_Dish01
+12052  // Vit_Dish02
+12053  // Vit_Dish03
+12054  // Vit_Dish04
+12055  // Vit_Dish05
+12056  // Agi_Dish01
+12057  // Agi_Dish02
+12058  // Agi_Dish03
+12059  // Agi_Dish04
+12060  // Agi_Dish05
+12061  // Dex_Dish01
+12062  // Dex_Dish02
+12063  // Dex_Dish03
+12064  // Dex_Dish04
+12065  // Dex_Dish05
+12066  // Luk_Dish01
+12067  // Luk_Dish02
+12068  // Luk_Dish03
+12069  // Luk_Dish04
+12070  // Luk_Dish05
+12071  // Str_Dish06
+12072  // Str_Dish07
+12073  // Str_Dish08
+12074  // Str_Dish09
+12075  // Str_Dish10
+12076  // Int_Dish06
+12077  // Int_Dish07
+12078  // Int_Dish08
+12079  // Int_Dish09
+12080  // Int_Dish10
+12081  // Vit_Dish06
+12082  // Vit_Dish07
+12083  // Vit_Dish08
+12084  // Vit_Dish09
+12085  // Vit_Dish10
+12086  // Agi_Dish06
+12087  // Agi_Dish07
+12088  // Agi_Dish08
+12089  // Agi_Dish09
+12090  // Agi_Dish10
+12091  // Dex_Dish06
+12092  // Dex_Dish07
+12093  // Dex_Dish08
+12094  // Dex_Dish09
+12095  // Dex_Dish10
+12096  // Luk_Dish06
+12097  // Luk_Dish07
+12098  // Luk_Dish08
+12099  // Luk_Dish09
+12100  // Luk_Dish10
+12101  // Citron
+12102  // Meat_Skewer
+12103  // Bloody_Dead_Branch
+12104  // Random_Quiver
+12105  // Set_Of_Taiming_Item
+12106  // Accessory_Box
+12107  // Wrapped_Mask
+12108  // Bundle_Of_Magic_Scroll
+12109  // Poring_Box
+12110  // First_Aid_Kit
+12111  // Food_Package
+12112  // Tropical_Sograt
+12113  // Vermilion_The_Beach
+12114  // Elemental_Fire
+12115  // Elemental_Water
+12116  // Elemental_Earth
+12117  // Elemental_Wind
+12118  // Resist_Fire
+12119  // Resist_Water
+12120  // Resist_Earth
+12121  // Resist_Wind
+12122  // Sesame_Pastry
+12123  // Honey_Pastry
+12124  // Rainbow_Cake
+12125  // Outdoor_Cooking_Kits
+12126  // Indoor_Cooking_Kits
+12127  // High_end_Cooking_Kits
+12128  // Imperial_Cooking_Kits
+12129  // Fantastic_Cooking_Kits
+12130  // Cookie_Bag
+12132  // Red_Bag
+12144  // Sphere_Case_Wind
+12145  // Sphere_Case_Darkness
+12146  // Sphere_Case_Poison
+12147  // Sphere_Case_Water
+12148  // Sphere_Case_Fire
+12149  // Bullet_Case
+12150  // Bullet_Case_Blood
+12151  // Bullet_Case_Silver
+12183  // Holy_Arrow_Quiver
+12184  // Mercenary_Red_Potion
+12185  // Mercenary_Blue_Potion
+12194  // Hometown_Gift
+12195  // Plain_Rice_Cake
+12196  // Hearty_Rice_Cake
+12197  // Salty_Rice_Cake
+12198  // Lucky_Rice_Cake
+12241  // M_Center_Potion
+12242  // M_Awakening_Potion
+12243  // M_Berserk_Potion
+12246  // Magic_Card_Album
+12260  // Cool_Summer_Outfit
+12290  // Mysterious_Can
+12291  // Mysterious_PET_Bottle
+12292  // Unripe_Fruit
+12293  // Dried_Yggdrasilberry
+12341  // Special_Alloy_Trap_Box
+12346  // Unripe_Acorn
+12347  // Acorn_Jelly
+12353  // Tiny_Waterbottle
+12358  // Fan_Of_Wind
+12359  // Very_Soft_Plant
+12360  // Very_Red_Juice
+12362  // Kuloren
+12364  // Staff_Of_Leader
+12365  // Charming_Lotus
+12366  // Gril_Doll
+12367  // Luxury_Whisky_Bottle
+12368  // Splendid_Mirror
+12369  // Coconut
+12371  // Magical_Lithography
+12372  // Hell_Contract
+12373  // Boy's_Naivety
+12374  // Flaming_Ice
+12376  // Mysterious_Can2
+12377  // Mysterious_PET_Bottle2
+12379  // Pope's_Cookie
+12383  // Vulcan_Bullet_Magazine
+12392  // RepairA
+12393  // RepairB
+12394  // RepairC
+12395  // Tantanmen
+//12414
+12717  // Poison_Paralysis
+12718  // Poison_Leech
+12719  // Poison_Oblivion
+12720  // Poison_Contamination
+12721  // Poison_Numb
+12722  // Poison_Fever
+12723  // Poison_Laughing
+12724  // Poison_Fatigue
+12734  // Runstone_Quality
+12735  // Runstone_Ancient
+12736  // Runstone_Mystic
+12737  // Runstone_Ordinary
+12738  // Runstone_Rare
+506  // Green_Potion
+507  // Red_Herb
+508  // Yellow_Herb
+509  // White_Herb
+510  // Blue_Herb
+511  // Green_Herb
+512  // Apple
+513  // Banana
+514  // Grape
+515  // Carrot
+516  // Sweet_Potato
+517  // Meat
+518  // Honey
+519  // Milk
+520  // Leaflet_Of_Hinal
+521  // Leaflet_Of_Aloe
+522  // Fruit_Of_Mastela
+523  // Holy_Water
+525  // Panacea
+526  // Royal_Jelly
+528  // Monster's_Feed
+529  // Candy
+530  // Candy_Striper
+531  // Apple_Juice
+532  // Banana_Juice
+533  // Grape_Juice
+534  // Carrot_Juice
+535  // Pumpkin
+536  // Ice_Cream
+537  // Pet_Food
+538  // Well_Baked_Cookie
+539  // Piece_Of_Cake
+544  // Fish_Slice
+548  // Cheese
+549  // Nice_Sweet_Potato
+550  // Popped_Rice
+551  // Shusi
+553  // Bun
+564  // Rice_Ball
+566  // Tomyumkung
+567  // Prawn
+568  // Lemon
+569  // Novice_Potion
+570  // Lucky_Candy
+571  // Lucky_Candy_Cane
+572  // Lucky_Cookie
+574  // Egg
+576  // Prickly_Fruit
+577  // Grain
+578  // Strawberry
+579  // Delicious_Fish
+580  // Bread
+581  // Mushroom
+582  // Orange
+584  // Fish_Ball_Soup
+587  // Prickly_Fruit_
+591  // Caviar_Pancake
+592  // Jam_Pancake
+593  // Honey_Pancake
+594  // Sour_Cream_Pancake
+595  // Mushroom_Pancake
+//11513
+11515  // Coconut
+11516  // Acai_Fruit
+//11517
+701  // Ora_Ora
+702  // Animal_Blood
+703  // Hinalle
+704  // Aloe
+705  // Clover
+706  // Four_Leaf_Clover
+707  // Singing_Plant
+708  // Ment
+709  // Izidor
+710  // Illusion_Flower
+711  // Shoot
+712  // Flower
+713  // Empty_Bottle
+715  // Yellow_Gemstone
+716  // Red_Gemstone
+717  // Blue_Gemstone
+718  // Dark_Red_Jewel
+719  // Violet_Jewel
+720  // Skyblue_Jewel
+721  // Azure_Jewel
+722  // Scarlet_Jewel
+723  // Cardinal_Jewel
+724  // Cardinal_Jewel_
+725  // Red_Jewel
+726  // Blue_Jewel
+727  // White_Jewel
+728  // Golden_Jewel
+729  // Bluish_Green_Jewel
+730  // Crystal_Jewel
+731  // Crystal_Jewel_
+732  // Crystal_Jewel__
+733  // Crystal_Jewel___
+734  // Red_Frame
+735  // Blue_Porcelain
+736  // White_Platter
+737  // Black_Ladle
+738  // Pencil_Case
+739  // Rouge
+740  // Stuffed_Doll
+741  // Poring_Doll
+742  // Chonchon_Doll
+743  // Spore_Doll
+744  // Bunch_Of_Flowers
+745  // Wedding_Bouquet
+746  // Glass_Bead
+747  // Crystal_Mirror
+748  // Witherless_Rose
+749  // Frozen_Rose
+750  // Baphomet_Doll
+751  // Osiris_Doll
+752  // Grasshopper_Doll
+753  // Monkey_Doll
+754  // Raccoondog_Doll
+756  // Oridecon_Stone
+757  // Elunium_Stone
+901  // Danggie
+902  // Tree_Root
+903  // Reptile_Tongue
+904  // Scorpion's_Tail
+905  // Stem
+906  // Pointed_Scale
+907  // Resin
+908  // Spawn
+909  // Jellopy
+910  // Garlet
+911  // Scell
+912  // Zargon
+913  // Tooth_Of_Bat
+914  // Fluff
+915  // Chrysalis
+916  // Feather_Of_Birds
+917  // Talon
+918  // Sticky_Webfoot
+919  // Animal's_Skin
+920  // Claw_Of_Wolves
+921  // Mushroom_Spore
+922  // Orcish_Cuspid
+923  // Evil_Horn
+924  // Powder_Of_Butterfly
+925  // Bill_Of_Birds
+926  // Scale_Of_Snakes
+928  // Insect_Feeler
+929  // Immortal_Heart
+930  // Rotten_Bandage
+931  // Orcish_Voucher
+932  // Skel_Bone
+934  // Mementos
+935  // Shell
+936  // Scales_Shell
+937  // Posionous_Canine
+938  // Sticky_Mucus
+939  // Bee_Sting
+940  // Grasshopper's_Leg
+941  // Nose_Ring
+942  // Yoyo_Tail
+943  // Solid_Shell
+944  // Horseshoe
+945  // Raccoon_Leaf
+946  // Snail's_Shell
+947  // Horn
+948  // Bear's_Foot
+949  // Feather
+950  // Heart_Of_Mermaid
+951  // Fin
+952  // Cactus_Needle
+953  // Stone_Heart
+954  // Shining_Scales
+955  // Worm_Peelings
+956  // Gill
+957  // Decayed_Nail
+958  // Horrendous_Mouth
+959  // Rotten_Scale
+960  // Nipper
+961  // Conch
+962  // Tentacle
+963  // Sharp_Scale
+964  // Crap_Shell
+965  // Clam_Shell
+966  // Flesh_Of_Clam
+967  // Turtle_Shell
+968  // Voucher_Of_Orcish_Hero
+969  // Gold
+971  // Detrimindexta
+972  // Karvodailnirol
+973  // Counteragent
+974  // Mixture
+975  // Scarlet_Dyestuffs
+976  // Lemon_Dyestuffs
+978  // Cobaltblue_Dyestuffs
+979  // Darkgreen_Dyestuffs
+980  // Orange_Dyestuffs
+981  // Violet_Dyestuffs
+982  // White_Dyestuffs
+983  // Black_Dyestuffs
+984  // Oridecon
+985  // Elunium
+986  // Anvil
+987  // Oridecon_Anvil
+988  // Golden_Anvil
+989  // Emperium_Anvil
+990  // Boody_Red
+991  // Crystal_Blue
+992  // Wind_Of_Verdure
+993  // Yellow_Live
+994  // Flame_Heart
+995  // Mistic_Frozen
+996  // Rough_Wind
+997  // Great_Nature
+998  // Iron
+999  // Steel
+1000  // Star_Crumb
+1001  // Sparkling_Dust
+1002  // Iron_Ore
+1003  // Coal
+1004  // Patriotism_Marks
+1005  // Hammer_Of_Blacksmith
+1006  // Old_Magic_Book
+1007  // Penetration
+1008  // Frozen_Heart
+1009  // Sacred_Marks
+1010  // Phracon
+1011  // Emveretarcon
+1012  // Lizard_Scruff
+1013  // Colorful_Shell
+1014  // Jaws_Of_Ant
+1015  // Thin_N'_Long_Tongue
+1016  // Rat_Tail
+1017  // Moustache_Of_Mole
+1018  // Nail_Of_Mole
+1019  // Wooden_Block
+1020  // Long_Hair
+1021  // Dokkaebi_Horn
+1022  // Fox_Tail
+1023  // Fish_Tail
+1024  // Chinese_Ink
+1025  // Spiderweb
+1026  // Acorn
+1027  // Porcupine_Spike
+1028  // Wild_Boar's_Mane
+1029  // Tiger's_Skin
+1030  // Tiger_Footskin
+1031  // Limb_Of_Mantis
+1032  // Blossom_Of_Maneater
+1033  // Root_Of_Maneater
+1034  // Cobold_Hair
+1035  // Dragon_Canine
+1036  // Dragon_Scale
+1037  // Dragon_Train
+1038  // Petite_DiablOfs_Horn
+1039  // Petite_DiablOfs_Wing
+1040  // Elder_Pixie's_Beard
+1041  // Lantern
+1042  // Short_Leg
+1043  // Nail_Of_Orc
+1044  // Tooth_Of_
+1045  // Sacred_Masque
+1046  // Tweezer
+1047  // Head_Of_Medusa
+1048  // Slender_Snake
+1049  // Skirt_Of_Virgin
+1050  // Tendon
+1051  // Detonator
+1052  // Single_Cell
+1053  // Tooth_Of_Ancient_Fish
+1054  // Lip_Of_Ancient_Fish
+1055  // Earthworm_Peeling
+1056  // Grit
+1057  // Moth_Dust
+1058  // Wing_Of_Moth
+1059  // Transparent_Cloth
+1060  // Golden_Hair
+1061  // Starsand_Of_Witch
+1062  // Pumpkin_Head
+1063  // Sharpened_Cuspid
+1064  // Reins
+1065  // Booby_Trap
+1066  // Tree_Of_Archer_1
+1067  // Tree_Of_Archer_2
+1068  // Tree_Of_Archer_3
+1088  // Morocc_Potion
+1089  // Payon_Potion
+1092  // Empty_Cylinder
+1093  // Empty_Potion
+1094  // Short_Daenggie
+1095  // Needle_Of_Alarm
+1096  // Round_Shell
+1097  // Worn_Out_Page
+1098  // Manacles
+1099  // Worn_Out_Prison_Uniform
+6001  // Essence_Of_Fire
+6002  // Token_Of_Apostle
+6003  // Soul_Pendant
+6004  // Bapho_Doll
+6008  // Wood
+6010  // Pickaxe
+6020  // Fur
+6021  // Peaked_Hat
+6022  // Hard_Skin
+6023  // Mystic_Horn
+6032  // Horn_Of_Hilsrion
+6033  // Horn_Of_Tendrilion
+6073  // Dragon's_Mane
+6075  // Crystalized_Teardrop
+6086  // Withered_Flower
+6087  // Crystal_Of_Soul_01
+6088  // Crystal_Of_Soul_02
+6089  // Piece_Of_Darkness
+6090  // Purified_Bradium
+6091  // Dark_Red_Scale
+6095  // Flavored_Alcohol
+6096  // Fish_With_Blue_Back
+6097  // Pumpkin_Pie_
+6098  // Small_Snow_Flower
+6099  // Grilled_Rice_Cake
+6100  // Damp_Darkness
+6104  // Big_Cell
+6105  // Morning_Dew
+6106  // Well_Ripened_Berry
+6107  // Sunset_On_The_Rock
+6108  // Apple_Pudding
+6109  // Plant_Neutrient
+6110  // Vital_Flower
+6111  // Mystic_Stone
+6112  // Fresh_Plant
+6113  // Vital_Flower_
+6114  // Flame_Gemstone
+6115  // Bun_
+6120  // Face_Paint
+6123  // Surface_Paint
+6128  // Guillotine_Antidote
+6144  // Heartbroken_Tears
+6145  // Vulcan_Bullet
+6146  // Magic_Gear_Fuel
+6147  // Liquid_Condensed_Bullet
+6186  // Monkey_Wrench
+//6189
+//6190
+//6191
+//6192
+//6193
+//6194
+//6195
+//6196
+//6197
+//6198
+//6199
+//6200
+//6201
+//6202
+//6203
+//6204
+//6205
+//6210  // Seed_Of_Thorny_Plant
+//6211  // Bloodsuck_Plant_Seed
+//6212  // Bomb_Mushroom_Spore
+//6213  // Explosive_Powder
+//6214  // Smokes_Powder
+//6215  // Tear_Gas
+//6216  // Oil_Bottle
+//6217  // Mandragora_Flowerpot
+6223  // Carnium
+6224  // Bradium
+//6244  // Dark_Powder
+//6245  // Black_Powder
+//6246  // Yellow_Powder
+//6247  // White_Powder
+//6248  // Chowder_Pot
+//6249  // Savage_Meat
+//6250  // Iron_Cooking_Skewer
+//6251  // Black_Charcoal
+//6252  // Wolf's_Blood
+//6253  // Cold_Ice
+//6254  // Seasoned_Tough_Meat
+//6255  // Large_Pan
+//6256  // Powdered_Ice
+//6257  // Ice_Crystal
+//6258  // Comodo_Tropical_Fruit
+//6259  // Drosera_Feeler
+//6260  // Petite_Tail
+//6261  // Fine_Noodles
+//6262  // Cold_Broth
+//6263  // Coconut
+//6264  // Melon
+//6265  // Pineapple
+//6279  // Apple_Bomb_Guidebook
+//6280  // Pineapple_Bomb_Guidebook
+//6281  // Coconut_Bomb_Guidebook
+//6282  // Melon_Bomb_Guidebook
+//6283  // Banana_Bomb_Guidebook
+//6284  // Plant_Genetic_Cultivation_Guide
+//6285  // Improved_Potion_Creation_Manual
+6297  // Empty_Potion_Bottle
+//6321  // Rake_Helmet
+//6322  // Antler_Helmet
+//6323  // Two-Horn_Helmet
+//6324  // One-Horn_Helmet
+//6325  // White_Spider_Legs
+//6326  // Queen_Scraba_Shell
+//6360  // Scarlet_Point
+//6361  // Indigo_Point
+//6362  // Yellow_Wish_Point
+//6363  // Lime_Green_Point
+7001  // Mould_Powder
+7002  // Ogre_Tooth
+7003  // Anolian_Skin
+7004  // Mud_Lump
+7005  // Skull
+7006  // Wing_Of_Red_Bat
+7007  // Claw_Of_Rat
+7008  // Stiff_Horn
+7009  // Glitter_Shell
+7010  // Tail_Of_Steel_Scorpion
+7011  // Claw_Of_Monkey
+7012  // Tough_Scalelike_Stem
+7013  // Coral_Reef
+7014  // Old_Portrait
+7015  // Bookclip_In_Memory
+7016  // Spoon_Stub
+7017  // Executioner's_Mitten
+7018  // Young_Twig
+7019  // Loki's_Whispers
+7020  // Mother's_Nightmare
+7021  // Foolishness_Of_Blind
+7022  // Old_Hilt
+7023  // Blade_Lost_In_Darkness
+7024  // Bloody_Edge
+7026  // Key_Of_Clock_Tower
+7027  // Underground_Key
+7030  // Claw_Of_Desert_Wolf
+7031  // Old_Frying_Pan
+7032  // Piece_Of_Egg_Shell
+7033  // Poison_Spore
+7034  // Red_Socks_With_Holes
+7035  // Matchstick
+7036  // Fang_Of_Garm
+7038  // Yarn
+7041  // Fine_Grit
+7043  // Fine_Sand
+7047  // Alice's_Apron
+7048  // Talon_Of_Griffin
+7049  // Stone
+7053  // Cyfar
+7054  // Brigan
+7055  // Animal_Pooopoo
+7056  // Payroll_Of_Kafra
+7057  // Gallar_Horn
+7058  // Gullraifnir
+7063  // Soft_Feather
+7064  // Dragon_Fly_Wing
+7065  // Sea_Otter_Leather
+7066  // Ice_Piece
+7067  // Stone_Piece
+7068  // Burn_Tree
+7069  // Broken_Armor_Piece
+7070  // Broken_Shell
+7071  // Tatters_Clothes
+7072  // Rust_Suriken
+7073  // Jewel_Of_Prayer
+7074  // Iron_Glove
+7075  // Iron_Maiden
+7076  // Mystery_Wheel
+7077  // Silver_Fancy
+7078  // Anger_Of_Valkurye
+7079  // Feather_Of_Angel
+7080  // Foot_Step_Of_Cat
+7081  // Beard_Of_Women
+7082  // Root_Of_Stone
+7083  // Soul_Of_Fish
+7084  // Saliva_Of_Bird
+7085  // Tendon_Of_Bear
+7086  // Symbol_Of_Sun
+7087  // Breath_Of_Soul
+7088  // Crystal_Of_Snow
+7089  // Indication_Of_Tempest
+7090  // Slilince_Wave
+7091  // Rough_Billows
+7092  // Air_Stream
+7093  // Wheel
+7094  // Mystery_Piece
+7095  // Broken_Steel_Piece
+7096  // Cold_Magma
+7097  // Burning_Heart
+7098  // Live_Coal
+7099  // Old_Magic_Circle
+7100  // Sharp_Leaf
+7101  // Peco_Wing_Feather
+7102  // Hideous_Dream
+7103  // Unknown_Liquid_Bottle
+7104  // Fake_Angel_Wing
+7105  // Fake_Angel_Loop
+7106  // Goat's_Horn
+7107  // Gaoat's_Skin
+7108  // Boroken_Shiled_Piece
+7109  // Shine_Spear_Blade
+7110  // Vroken_Sword
+7111  // Smooth_Paper
+7112  // Fright_Paper_Blade
+7113  // Broken_Pharaoh_Symbol
+7114  // Tutankhamen's_Mask
+7115  // Harpy's_Feather
+7116  // Harpy's_Claw
+7117  // Rent_Spell_Book
+7118  // Rent_Scroll
+7119  // Spawns
+7120  // Burning_Horse_Shoe
+7121  // Honey_Jar
+7122  // Hot_Hair
+7123  // Dragon's_Skin
+7124  // Sand_Lump
+7125  // Scropion's_Nipper
+7126  // Large_Jellopy
+7127  // Alcol_Create_Book
+7128  // FireBottle_Create_Book
+7129  // Acid_Create_Book
+7130  // Plant_Create_Book
+7131  // Mine_Create_Book
+7132  // Coating_Create_Book
+7133  // Slim_Potion_Create_Book
+7134  // Medicine_Bowl
+7140  // Seed_Of_Life
+7141  // Yggdrasilberry_Dew
+7143  // Life_Force_Pot
+7144  // Normal_Potion_Book
+7147  // Jasmin
+7149  // Yellow_Plate
+7150  // Bamboo_Cut
+7151  // Oil_Paper
+7152  // Glossy_Hair
+7153  // Old_Japaness_Clothes
+7154  // Poison_Powder
+7155  // Poison_Toad's_Skin
+7156  // Broken_Shuriken
+7157  // Black_Mask
+7158  // Broken_Wine_Vessel
+7159  // Tengu's_Nose
+7160  // Lord's_Passable_Ticket
+7161  // Black_Bear's_Skin
+7162  // Cloud_Piece
+7163  // Sharp_Feeler
+7164  // Hard_Peach
+7165  // Limpid_Celestial_Robe
+7166  // Soft_Silk_Cloth
+7167  // Mystery_Iron_Bit
+7168  // Great_Wing
+7169  // Taegeuk_Plate
+7170  // Tuxedo
+7171  // Leopard_Skin
+7172  // Leopard_Talon
+7174  // Packing_Ribbon
+7175  // Packing_Paper
+7182  // Cacao
+7186  // Thin_Stem
+7187  // Festival_Mask
+7188  // Browny_Root
+7189  // Heart_Of_Tree
+7190  // Solid_Peeling
+7191  // Lamplight
+7192  // Blade_Of_Pinwheel
+7193  // Germinating_Sprout
+7194  // Soft_Leaf
+7195  // Air_Rifle
+7196  // Shoulder_Protection
+7197  // Tough_Vines
+7198  // Great_Leaf
+7199  // Coupon
+7200  // Flexible_String
+7201  // Log
+7202  // Beetle_Nipper
+7203  // Solid_Twig
+7204  // Gunpowder
+7205  // Piece_Of_Black_Cloth
+7206  // Black_Kitty_Doll
+7207  // Old_Manteau
+7208  // Rusty_Cleaver
+7209  // Dullahan's_Helm
+7210  // Dullahan_Armor
+7211  // Rojerta_Piece
+7212  // Hanging_Doll
+7213  // Needle_Pouch
+7214  // Bat_Cage
+7215  // Broken_Needle
+7216  // Red_Scarf
+7217  // Spool
+7218  // Rotten_Rope
+7219  // Striped_Socks
+7220  // Ectoplasm
+7221  // Tangled_Chain
+7222  // Tree_Knot
+7223  // Distorted_Portrait
+7225  // Pumpkin_Bucket
+7226  // Pill
+7262  // Fan
+7263  // Cat_Eyed_Stone
+7264  // Dried_Sand
+7265  // Dragon_Horn
+7266  // Dragon_Fang
+7267  // Tiger_Skin_Panties
+7268  // Little_Blacky_Ghost
+7269  // Bib
+7270  // Milk_Bottle
+7277  // Munak_Doll
+7286  // Chilli
+7289  // Olivine
+7290  // Phlogopite
+7291  // Agate
+7292  // Muscovite
+7293  // Rose_Quartz
+7294  // Turquoise
+7295  // Citrine
+7296  // Pyroxene
+7297  // Biotite
+7298  // Leaf_Clothes
+7299  // Bamboo_Basket
+7300  // Gemstone
+7301  // Sword_Accessory
+7303  // Bag_Of_Rice
+7312  // Jubilee
+7315  // Dark_Crystal_Fragment
+7316  // Long_Limb
+7317  // Screw
+7318  // Old_Pick
+7319  // Old_Steel_Plate
+7320  // Air_Pollutant
+7321  // Fragment_Of_Crystal
+7322  // Poisonous_Gas
+7323  // Battered_Kettle
+7325  // Tube
+7326  // Fluorescent_Liquid
+7327  // Headlamp
+7340  // Will_Of_Darkness
+7345  // Armlet_Of_Prisoner
+7352  // Transparent_Plate01
+7353  // Transparent_Plate02
+7354  // Transparent_Plate03
+7355  // Transparent_Plate04
+7356  // Piece_Of_Crest1
+7357  // Piece_Of_Crest2
+7358  // Piece_Of_Crest3
+7359  // Piece_Of_Crest4
+7419  // Embryo_HandBook
+7433  // Scroll
+7434  // Elemental_Potion_Book
+7435  // Golden_Bracelet
+7436  // Piece_Of_Memory_Green
+7437  // Piece_Of_Memory_Purple
+7438  // Piece_Of_Memory_Blue
+7439  // Piece_Of_Memory_Red
+7440  // Red_Feather
+7441  // Blue_Feather
+7442  // Cursed_Seal
+7443  // Tri_Headed_Dragon_Head
+7444  // Treasure_Box
+7445  // Dragonball_Green
+7446  // Dragonball_Blue
+7447  // Dragonball_Red
+7448  // Dragonball_Yellow
+7449  // Bloody_Page
+7450  // Piece_Of_Bone_Armor
+7451  // Scale_Of_Red_Dragon
+7452  // Yellow_Spice
+7453  // Sweet_Sauce
+7454  // Plain_Sauce
+7455  // Hot_Sauce
+7456  // Red_Spice
+7457  // Cooking_Oil
+7472  // Cookbook01
+7473  // Cookbook02
+7474  // Cookbook03
+7475  // Cookbook04
+7476  // Cookbook05
+7477  // Cookbook06
+7478  // Cookbook07
+7479  // Cookbook08
+7480  // Cookbook09
+7481  // Cookbook10
+7482  // Pot
+7507  // Sturdy_Iron_Piece
+7510  // Valhalla_Flower
+7511  // Rune_Of_Darkness
+7512  // Burnt_Parts
+7513  // Pocket_Watch
+7521  // Flame_Stone
+7522  // Ice_Stone
+7523  // Wind_Stone
+7524  // Shadow_Orb
+7561  // Ice_Heart
+7562  // Ice_Scale
+7563  // Bloody_Rune
+7564  // Rotten_Meat
+7565  // Sticky_Poison
+7566  // Will_Of_Darkness_
+7567  // Suspicious_Hat
+7568  // White_Mask
+7574  // Ice_Particle
+7751  // Old_White_Cloth
+7752  // Clattering_Skull
+7753  // Broken_Farming_Utensil
+7754  // Broken_Crown
+7830  // Goddess_Tear
+7831  // Valkyrie_Token
+7832  // Brynhild_Armor_Piece
+7833  // Hero_Remains
+7834  // Andvari_Ring
+7835  // Dusk_Glow
+7836  // Dawn_Essence
+7837  // Cold_Moonlight
+7838  // Hazy_Starlight
+7931  // Poison_Kit
+7932  // Poison_Herb_Nerium
+7933  // Poison_Herb_Rantana
+7934  // Poison_Herb_Makulata
+7935  // Poison_Herb_Seratum
+7936  // Poison_Herb_Scopolia
+7937  // Poison_Herb_Amoena
+7938  // Light_Granule
+7939  // Elder_Branch
+7940  // Special_Alloy_Trap
+11000  // Prontera_Book_01
+11001  // Adventure_Story01
+11002  // Great_Chef_Orleans01
+11003  // Legend_Of_Kafra01
+11004  // Mercenary_Rebellion
+11005  // Tyrant_Schmidt
+11006  // Blood_Flower01
+11007  // Blood_Flower02
+11008  // Barmund
+11009  // Adventure_Story02
+//11020
+//11021
+//11022  // Mix_Cooking_Ingredient_Book_1
+//11023  // Vitality_Boost_Research_Book
+//11024  // Energy_Drink_Formula
+

+ 2 - 2
db/item_db.txt

@@ -3293,7 +3293,7 @@
 //6362,Yellow_Wish_Point,Yellow Wish Point,3,100,,5,,,,,,,,,,,,,{},{},{}
 //6363,Lime_Green_Point,Lime Green Point,3,100,,5,,,,,,,,,,,,,{},{},{}
 //6376,KVM_Badge
-//6377,Buy_Stall_Permit,Purchase Street Stall License,3,200,,10,,,,,,,,,,,,,{},{},{}
+6377,Buy_Stall_Permit,Purchase Street Stall License,3,200,,10,,,,,,,,,,,,,{},{},{}
 //6378
 //6379
 //6380
@@ -4823,7 +4823,7 @@
 //12471,LV5_Adrenaline_Scroll,
 //12472,Convex_Mirror,
 //12475,Cure_Free,
-//12548,Shabby_Purchase_Street_Stall_License,
+12548,Shabby_Purchase_Street_Stall_License,Shabby Purchase Street Stall License,2,500,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ buyingstore 2; },{},{}
 //12553,Brysingamen_Piece_Box,
 //12554,Asprika_Piece_Box,
 //12555,Brynhild_Piece_Box,

+ 14 - 14
db/packet_db.txt

@@ -1488,8 +1488,8 @@ packet_ver: 25
 0x080F,20
 
 //2010-03-03aRagexeRE
-//0x0810,3
-//0x0811,-1
+0x0810,3
+0x0811,-1,reqopenbuyingstore,2:4:8:9:89
 //0x0812,86
 //0x0813,6
 //0x0814,6
@@ -1499,11 +1499,11 @@ packet_ver: 25
 //0x0819,4
 
 //2010-03-09aRagexeRE
-//0x0813,-1
+0x0813,-1
 //0x0814,2
 //0x0815,6
-//0x0816,6
-//0x0818,-1
+0x0816,6
+0x0818,-1
 //0x0819,10
 //0x081A,4
 //0x081B,4
@@ -1528,15 +1528,15 @@ packet_ver: 25
 //0x081B,8
 
 //2010-04-20aRagexeRE
-//0x0812,8
-//0x0814,86
-//0x0815,2
-//0x0817,6
-//0x0819,-1
-//0x081a,4
-//0x081b,10
-//0x081c,10
-//0x0824,6
+0x0812,8
+0x0814,86
+0x0815,2,reqclosebuyingstore,0
+0x0817,6,reqclickbuyingstore,2
+0x0819,-1,reqtradebuyingstore,2:4:8:12
+0x081a,4
+0x081b,10
+0x081c,10
+0x0824,6
 
 //2010-06-01aRagexeRE
 //0x0825,-1

+ 3 - 0
db/skill_db.txt

@@ -938,6 +938,9 @@
 // Episode 13.3
 //2534,0,0,0,0,0,0,9,0,no,0,0,0,none,0		RETURN_TO_ELDICASTES,
 
+// Buying Store
+2535,0,0,4,0,0x1,0,1,0,no,0,0x1,0,none,0,	ALL_BUYING_STORE,Open Buying Store
+
 8001,9,6,4,0,0x1,0,5,1,no,0,0,0,magic,0,	HLIF_HEAL,Healing Touch
 8002,0,6,4,0,0x3,-1,5,1,no,0,0,0,none,0,	HLIF_AVOID,Avoid
 8003,0,0,0,0,0,1,5,0,no,0,0,0,none,0,		HLIF_BRAIN,Brain Surgery

+ 2 - 0
db/skill_require_db.txt

@@ -454,6 +454,8 @@
 1018,0,0,30,0,0,0,99,0,0,none,0,12114,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//SA_ELEMENTFIRE
 1019,0,0,30,0,0,0,99,0,0,none,0,12117,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//SA_ELEMENTWIND
 
+2535,0,0,1,0,0,0,99,0,0,none,0,6377,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//ALL_BUYING_STORE#Open Buying Store
+
 10010,0,0,1,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//GD_BATTLEORDER##
 10011,0,0,1,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//GD_REGENERATION##
 10012,0,0,1,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	//GD_RESTORE##

+ 11 - 0
db/skill_tree.txt

@@ -86,6 +86,7 @@
 5,155,1,0,0,0,0,0,0,0,0,0,0 //MC_LOUD#Crazy Uproar#
 5,410,1,0,0,0,0,0,0,0,0,0,0 //WE_CALLBABY#Call Baby#
 5,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+5,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Thief
 6,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 6,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -240,6 +241,7 @@
 10,410,1,0,0,0,0,0,0,0,0,0,0 //WE_CALLBABY#Call Baby#
 10,459,1,111,5,0,0,0,0,0,0,0,0	//BS_ADRENALINE2#Full Adrenaline Rush#
 10,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+10,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Hunter
 11,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 11,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -511,6 +513,7 @@
 18,497,1,228,10,0,0,0,0,0,0,0,0	//AM_TWILIGHT2#Twilight Alchemy 2#
 18,498,1,228,10,0,0,0,0,0,0,0,0	//AM_TWILIGHT3#Twilight Alchemy 3#
 18,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+18,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Bard
 19,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 19,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -668,6 +671,7 @@
 23,53,1,52,3,0,0,0,0,0,0,0,0 //TF_DETOXIFY#Detoxify#
 23,410,1,0,0,0,0,0,0,0,0,0,0 //WE_CALLBABY#Call Baby#
 23,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+23,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Gunslinger
 24,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 24,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -810,6 +814,7 @@
 4006,155,1,0,0,0,0,0,0,0,0,0,0 //MC_LOUD#Crazy Uproar#
 4006,410,1,0,0,0,0,0,0,0,0,0,0 //WE_CALLBABY#Call Baby#
 4006,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+4006,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //ThiefHigh
 4007,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 4007,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -989,6 +994,7 @@
 4011,410,1,0,0,0,0,0,0,0,0,0,0 //WE_CALLBABY#Call Baby#
 4011,459,1,111,5,0,0,0,0,0,0,0,0	//BS_ADRENALINE2#Full Adrenaline Rush#
 4011,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+4011,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Sniper
 4012,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 4012,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -1303,6 +1309,7 @@
 4019,497,1,228,10,0,0,0,0,0,0,0,0	//AM_TWILIGHT2#Twilight Alchemy 2#
 4019,498,1,228,10,0,0,0,0,0,0,0,0	//AM_TWILIGHT3#Twilight Alchemy 3#
 4019,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+4019,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Clown
 4020,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 4020,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -1508,6 +1515,7 @@
 4028,408,1,0,0,0,0,0,0,0,0,0,0 //WE_BABY#Baby#
 4028,409,1,0,0,0,0,0,0,0,0,0,0 //WE_CALLPARENT#Call Parent#
 4028,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+4028,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Baby Thief
 4029,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 4029,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -1666,6 +1674,7 @@
 4033,1013,1,0,0,0,0,0,0,0,0,0,0 //BS_GREED#Greed#
 4033,459,1,111,5,0,0,0,0,0,0,0,0	//BS_ADRENALINE2#Full Adrenaline Rush#
 4033,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+4033,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Baby Hunter
 4034,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 4034,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -1945,6 +1954,7 @@
 4041,497,1,228,10,0,0,0,0,0,0,0,0	//AM_TWILIGHT2#Twilight Alchemy 2#
 4041,498,1,228,10,0,0,0,0,0,0,0,0	//AM_TWILIGHT3#Twilight Alchemy 3#
 4041,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+4041,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Baby Bard
 4042,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 4042,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#
@@ -2099,6 +2109,7 @@
 4045,408,1,0,0,0,0,0,0,0,0,0,0 //WE_BABY#Baby#
 4045,409,1,0,0,0,0,0,0,0,0,0,0 //WE_CALLPARENT#Call Parent#
 4045,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
+4045,2535,1,41,1,0,0,0,0,0,0,0,0 //ALL_BUYING_STORE#Open Buying Store#
 //Taekwon
 4046,1,9,0,0,0,0,0,0,0,0,0,0 //NV_BASIC#Basic Skill#
 4046,142,1,0,0,0,0,0,0,0,0,0,0 //NV_FIRSTAID#First Aid#

+ 16 - 1
doc/script_commands.txt

@@ -4,7 +4,7 @@
 //= A reference manual for the eAthena scripting language.
 //= Commands are sorted depending on their functionality.
 //===== Version ===========================================
-//= 3.35.20110106
+//= 3.36.20110219
 //=========================================================
 //= 1.0 - First release, filled will as much info as I could
 //=       remember or figure out, most likely there are errors,
@@ -155,6 +155,8 @@
 //=       Spellcheck. [Ai4rei]
 //= 3.35.20110106
 //=       Removed bug warning from 'deletearray'. [Ai4rei]
+//= 3.36.20110219
+//=       Added 'buyingstore' command. [Ai4rei]
 //=========================================================
 
 This document is a reference manual for all the scripting commands and functions 
@@ -4423,6 +4425,19 @@ Example(s):
 //The invoked character will no longer automatically equip a falchion.
 	autoequip 1104,0;
 
+---------------------------------------
+
+*buyingstore <slots>;
+
+Invokes buying store preparation window like the skill 'Open Buying Store',
+without the item requirement. Amount of slots is limited by the server to
+a maximum of 5 slots by default.
+
+Example:
+
+	// Gives the player oppurtunity to buy 4 different kinds of items.
+	buyingstore 4;
+
 ---------------------------------------
 //
 4,1.- End of item-related commands

+ 4 - 4
sql-files/logs.sql

@@ -1,7 +1,7 @@
 #PickLog types (M)onsters Drop, (P)layers Drop/Take, Mobs Drop (L)oot Drop/Take,
 # Players (T)rade Give/Take, Players (V)ending Sell/Take, (S)hop Sell/Take, (N)PC Give/Take,
 # (C)onsumable Items, (A)dministrators Create/Delete, Sto(R)age, (G)uild Storage,
-# (E)mail attachment
+# (E)mail attachment,(B)uying Store
 
 #Database: log
 #Table: picklog
@@ -9,7 +9,7 @@ CREATE TABLE `picklog` (
   `id` int(11) NOT NULL auto_increment,
   `time` datetime NOT NULL default '0000-00-00 00:00:00',
   `char_id` int(11) NOT NULL default '0',
-  `type` enum('M','P','L','T','V','S','N','C','A','R','G','E') NOT NULL default 'P',
+  `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B') NOT NULL default 'P',
   `nameid` int(11) NOT NULL default '0',
   `amount` int(11) NOT NULL default '1',
   `refine` tinyint(3) unsigned NOT NULL default '0',
@@ -22,7 +22,7 @@ CREATE TABLE `picklog` (
   INDEX (`type`)
 ) ENGINE=MyISAM AUTO_INCREMENT=1 ;
 
-#ZenyLog types (M)onsters,(T)rade,(V)ending Sell/Buy,(S)hop Sell/Buy,(N)PC Change amount,(A)dministrators,(E)Mail
+#ZenyLog types (M)onsters,(T)rade,(V)ending Sell/Buy,(S)hop Sell/Buy,(N)PC Change amount,(A)dministrators,(E)Mail,(B)uying Store
 #Database: log
 #Table: zenylog
 CREATE TABLE `zenylog` (
@@ -30,7 +30,7 @@ CREATE TABLE `zenylog` (
   `time` datetime NOT NULL default '0000-00-00 00:00:00',
   `char_id` int(11) NOT NULL default '0',
   `src_id` int(11) NOT NULL default '0',
-  `type` enum('M','T','V','S','N','A','E') NOT NULL default 'S',
+  `type` enum('M','T','V','S','N','A','E','B') NOT NULL default 'S',
   `amount` int(11) NOT NULL default '0',
   `map` varchar(11) NOT NULL default '',
   PRIMARY KEY  (`id`),

+ 4 - 0
sql-files/upgrade_svn14713_log.sql

@@ -0,0 +1,4 @@
+-- Adds 'B' to `type` in `picklog` and `zenylog`
+
+ALTER TABLE `picklog` MODIFY `type` ENUM('M','P','L','T','V','S','N','C','A','R','G','E','B') NOT NULL DEFAULT 'P';
+ALTER TABLE `zenylog` MODIFY `type` ENUM('M','T','V','S','N','A','E','B') NOT NULL DEFAULT 'S';

+ 1 - 1
src/common/mmo.h

@@ -84,7 +84,7 @@
 #define MAX_ZENY 1000000000
 #define MAX_FAME 1000000000
 #define MAX_CART 100
-#define MAX_SKILL 1020
+#define MAX_SKILL 2536
 #define GLOBAL_REG_NUM 256
 #define ACCOUNT_REG_NUM 64
 #define ACCOUNT_REG2_NUM 16

+ 4 - 2
src/map/Makefile.in

@@ -17,7 +17,8 @@ MAP_OBJ = map.o chrif.o clif.o pc.o status.o npc.o \
 	npc_chat.o chat.o path.o itemdb.o mob.o script.o \
 	storage.o skill.o atcommand.o battle.o battleground.o \
 	intif.o trade.o party.o vending.o guild.o pet.o \
-	log.o mail.o date.o unit.o homunculus.o mercenary.o quest.o instance.o
+	log.o mail.o date.o unit.o homunculus.o mercenary.o quest.o instance.o \
+	buyingstore.o
 MAP_TXT_OBJ = $(MAP_OBJ:%=obj_txt/%) \
 	obj_txt/mapreg_txt.o
 MAP_SQL_OBJ = $(MAP_OBJ:%=obj_sql/%) \
@@ -26,7 +27,8 @@ MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \
 	chat.h itemdb.h mob.h script.h path.h \
 	storage.h skill.h atcommand.h battle.h battleground.h \
 	intif.h trade.h party.h vending.h guild.h pet.h \
-	log.h mail.h date.h unit.h homunculus.h mercenary.h quest.h instance.h mapreg.h
+	log.h mail.h date.h unit.h homunculus.h mercenary.h quest.h instance.h mapreg.h \
+	buyingstore.h
 
 HAVE_MYSQL=@HAVE_MYSQL@
 ifeq ($(HAVE_MYSQL),yes)

+ 2 - 2
src/map/atcommand.c

@@ -1150,7 +1150,7 @@ ACMD_FUNC(storage)
 {
 	nullpo_retr(-1, sd);
 	
-	if (sd->npc_id || sd->vender_id || sd->state.trading || sd->state.storage_flag)
+	if (sd->npc_id || sd->vender_id || sd->state.buyingstore || sd->state.trading || sd->state.storage_flag)
 		return -1;
 
 	if (storage_storageopen(sd) == 1)
@@ -1177,7 +1177,7 @@ ACMD_FUNC(guildstorage)
 		return -1;
 	}
 
-	if (sd->npc_id || sd->vender_id || sd->state.trading)
+	if (sd->npc_id || sd->vender_id || sd->state.buyingstore || sd->state.trading)
 		return -1;
 
 	if (sd->state.storage_flag == 1) {

+ 1 - 0
src/map/battle.c

@@ -4007,6 +4007,7 @@ static const struct _battle_data {
 	{ "client_reshuffle_dice",              &battle_config.client_reshuffle_dice,           0,      0,      1,              },
 	{ "client_sort_storage",                &battle_config.client_sort_storage,             0,      0,      1,              },
 	{ "gm_check_minlevel",                  &battle_config.gm_check_minlevel,               60,     0,      100,            },
+	{ "feature_buying_store",               &battle_config.feature_buying_store,            1,      0,      1,              },
 // BattleGround Settings
 	{ "bg_update_interval",                 &battle_config.bg_update_interval,              1000,   100,    INT_MAX,        },
 	{ "bg_short_attack_damage_rate",        &battle_config.bg_short_damage_rate,            80,     0,      INT_MAX,        },

+ 1 - 0
src/map/battle.h

@@ -481,6 +481,7 @@ extern struct Battle_Config
 	int client_reshuffle_dice;  // Reshuffle /dice
 	int client_sort_storage;
 	int gm_check_minlevel;  // min GM level for /check
+	int feature_buying_store;
 
 	// [BattleGround Settings]
 	int bg_update_interval;

+ 359 - 0
src/map/buyingstore.c

@@ -0,0 +1,359 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/db.h"  // ARR_FIND
+#include "../common/showmsg.h"  // ShowWarning
+#include "../common/socket.h"  // RBUF*
+#include "../common/strlib.h"  // safestrncpy
+#include "atcommand.h"  // msg_txt
+#include "battle.h"  // battle_config.*
+#include "buyingstore.h"  // struct s_buyingstore
+#include "clif.h"  // clif_buyingstore_*
+#include "log.h"  // log_pick_pc, log_zeny
+#include "pc.h"  // struct map_session_data
+
+
+/// constants (client-side restrictions)
+#define BUYINGSTORE_MAX_PRICE 99990000
+#define BUYINGSTORE_MAX_AMOUNT 9999
+
+
+/// failure constants for clif functions
+enum e_buyingstore_failure
+{
+	BUYINGSTORE_CREATE               = 1,  // "Failed to open buying store."
+	BUYINGSTORE_CREATE_OVERWEIGHT    = 2,  // "Total amount of then possessed items exceeds the weight limit by %d. Please re-enter."
+	BUYINGSTORE_TRADE_BUYER_ZENY     = 3,  // "All items within the buy limit were purchased."
+	BUYINGSTORE_TRADE_BUYER_NO_ITEMS = 4,  // "All items were purchased."
+	BUYINGSTORE_TRADE_SELLER_FAILED  = 5,  // "The deal has failed."
+	BUYINGSTORE_TRADE_SELLER_COUNT   = 6,  // "The trade failed, because the entered amount of item %s is higher, than the buyer is willing to buy."
+	BUYINGSTORE_TRADE_SELLER_ZENY    = 7,  // "The trade failed, because the buyer is lacking required balance."
+	BUYINGSTORE_CREATE_NO_INFO       = 8,  // "No sale (purchase) information available."
+};
+
+
+static unsigned int buyingstore_nextid = 0;
+
+
+/// Returns unique buying store id
+static unsigned int buyingstore_getuid(void)
+{
+	return buyingstore_nextid++;
+}
+
+
+bool buyingstore_setup(struct map_session_data* sd, unsigned char slots)
+{
+	if( !battle_config.feature_buying_store || sd->vender_id || sd->state.buyingstore || sd->state.trading || slots == 0 )
+	{
+		return false;
+	}
+
+	if( slots > MAX_BUYINGSTORE_SLOTS )
+	{
+		ShowWarning("buyingstore_setup: Requested %d slots, but server supports only %d slots.\n", (int)slots, MAX_BUYINGSTORE_SLOTS);
+		slots = MAX_BUYINGSTORE_SLOTS;
+	}
+
+	sd->buyingstore.slots = slots;
+	clif_buyingstore_open(sd);
+
+	return true;
+}
+
+
+void buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count)
+{
+	unsigned int i, weight, listidx;
+	struct item_data* id;
+
+	if( !result || count == 0 )
+	{// canceled, or no items
+		return;
+	}
+
+	if( !battle_config.feature_buying_store || pc_istrading(sd) || sd->buyingstore.slots == 0 || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || !storename[0] )
+	{// disabled or invalid input
+		sd->buyingstore.slots = 0;
+		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
+		return;
+	}
+
+	if( !pc_can_give_items(pc_isGM(sd)) )
+	{// custom: GM is not allowed to buy (give zeny)
+		sd->buyingstore.slots = 0;
+		clif_displaymessage(sd->fd, msg_txt(246));
+		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
+		return;
+	}
+
+	weight = sd->weight;
+
+	// check item list
+	for( i = 0; i < count; i++ )
+	{// itemlist: <name id>.W <amount>.W <price>.L
+		unsigned short nameid, amount;
+		int price, idx;
+
+		nameid = RBUFW(itemlist,i*8+0);
+		amount = RBUFW(itemlist,i*8+2);
+		price  = RBUFL(itemlist,i*8+4);
+
+		if( ( id = itemdb_exists(nameid) ) == NULL || amount == 0 )
+		{// invalid input
+			break;
+		}
+
+		if( price <= 0 || price > BUYINGSTORE_MAX_PRICE )
+		{// invalid price: unlike vending, items cannot be bought at 0 Zeny
+			break;
+		}
+
+		if( !id->flag.buyingstore || !itemdb_cantrade_sub(id, pc_isGM(sd), pc_isGM(sd)) || ( idx = pc_search_inventory(sd, nameid) ) == -1 )
+		{// restrictions: allowed, no character-bound items and at least one must be owned
+			break;
+		}
+
+		if( sd->status.inventory[idx].amount+amount > BUYINGSTORE_MAX_AMOUNT )
+		{// too many items of same kind
+			break;
+		}
+
+		if( i )
+		{// duplicate check. as the client does this too, only malicious intent should be caught here
+			ARR_FIND( 0, i, listidx, sd->buyingstore.items[i].nameid == nameid );
+			if( listidx != i )
+			{// duplicate
+				ShowWarning("buyingstore_create: Found duplicate item on buying list (nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", nameid, amount, sd->status.account_id, sd->status.char_id);
+				break;
+			}
+		}
+
+		weight+= id->weight*amount;
+		sd->buyingstore.items[i].nameid = nameid;
+		sd->buyingstore.items[i].amount = amount;
+		sd->buyingstore.items[i].price  = price;
+	}
+
+	if( i != count )
+	{// invalid item/amount/price
+		sd->buyingstore.slots = 0;
+		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
+		return;
+	}
+
+	if( (sd->max_weight*90)/100 < weight )
+	{// not able to carry all wanted items without getting overweight (90%)
+		sd->buyingstore.slots = 0;
+		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE_OVERWEIGHT, weight);
+		return;
+	}
+
+	// success
+	sd->state.buyingstore = true;
+	sd->buyer_id = buyingstore_getuid();
+	sd->buyingstore.zenylimit = zenylimit;
+	sd->buyingstore.slots = i;  // store actual amount of items
+	safestrncpy(sd->message, storename, sizeof(sd->message));
+	clif_buyingstore_myitemlist(sd);
+	clif_buyingstore_entry(sd);
+}
+
+
+void buyingstore_close(struct map_session_data* sd)
+{
+	if( sd->state.buyingstore )
+	{
+		// invalidate data
+		sd->state.buyingstore = false;
+		memset(&sd->buyingstore, 0, sizeof(sd->buyingstore));
+
+		// notify other players
+		clif_buyingstore_disappear_entry(sd);
+	}
+}
+
+
+void buyingstore_open(struct map_session_data* sd, int account_id)
+{
+	struct map_session_data* pl_sd;
+
+	if( !battle_config.feature_buying_store || pc_istrading(sd) )
+	{// not allowed to sell
+		return;
+	}
+
+	if( !pc_can_give_items(pc_isGM(sd)) )
+	{// custom: GM is not allowed to sell
+		clif_displaymessage(sd->fd, msg_txt(246));
+		return;
+	}
+
+	if( ( pl_sd = map_id2sd(account_id) ) == NULL || !pl_sd->state.buyingstore )
+	{// not online or not buying
+		return;
+	}
+
+	// success
+	clif_buyingstore_itemlist(sd, pl_sd);
+}
+
+
+void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count)
+{
+	short blankslots[MAX_SLOTS];  // used when checking whether or not an item's card slots are blank
+	int zeny = 0;
+	unsigned int i, weight, listidx, k;
+	struct map_session_data* pl_sd;
+
+	if( count == 0 )
+	{// nothing to do
+		return;
+	}
+
+	if( !battle_config.feature_buying_store || pc_istrading(sd) )
+	{// not allowed to sell
+		clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0);
+		return;
+	}
+
+	if( !pc_can_give_items(pc_isGM(sd)) )
+	{// custom: GM is not allowed to sell
+		clif_displaymessage(sd->fd, msg_txt(246));
+		clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0);
+		return;
+	}
+
+	if( ( pl_sd = map_id2sd(account_id) ) == NULL || !pl_sd->state.buyingstore || pl_sd->buyer_id != buyer_id )
+	{// not online, not buying or not same store
+		clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0);
+		return;
+	}
+
+	if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit )
+	{// buyer lost zeny in the mean time? fix the limit
+		pl_sd->buyingstore.zenylimit = pl_sd->status.zeny;
+	}
+	weight = pl_sd->weight;
+	memset(blankslots, 0, sizeof(blankslots));
+
+	// check item list
+	for( i = 0; i < count; i++ )
+	{// itemlist: <index>.W <name id>.W <amount>.W
+		unsigned short nameid, amount;
+		int index;
+
+		index  = RBUFW(itemlist,i*6+0)-2;
+		nameid = RBUFW(itemlist,i*6+2);
+		amount = RBUFW(itemlist,i*6+4);
+
+		if( i )
+		{// duplicate check. as the client does this too, only malicious intent should be caught here
+			ARR_FIND( 0, i, k, RBUFW(itemlist,k*6+0)-2 == index );
+			if( k != i )
+			{// duplicate
+				ShowWarning("buyingstore_trade: Found duplicate item on selling list (prevnameid=%hu, prevamount=%hu, nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n",
+					RBUFW(itemlist,k*6+2), RBUFW(itemlist,k*6+4), nameid, amount, sd->status.account_id, sd->status.char_id);
+				clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
+				return;
+			}
+		}
+
+		if( index < 0 || index >= ARRAYLENGTH(sd->status.inventory) || sd->inventory_data[index] == NULL || sd->status.inventory[index].nameid != nameid || sd->status.inventory[index].amount < amount )
+		{// invalid input
+			clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
+			return;
+		}
+
+		if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_isGM(sd), pc_isGM(pl_sd)) || memcmp(sd->status.inventory[index].card, blankslots, sizeof(blankslots)) )
+		{// non-tradable item
+			clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
+			return;
+		}
+
+		ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid );
+		if( listidx == pl_sd->buyingstore.slots || pl_sd->buyingstore.items[listidx].amount == 0 )
+		{// there is no such item or the buyer has already bought all of them
+			clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
+			return;
+		}
+
+		if( pl_sd->buyingstore.items[listidx].amount < amount )
+		{// buyer does not need that much of the item
+			clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_COUNT, nameid);
+			return;
+		}
+
+		if( pc_checkadditem(pl_sd, nameid, amount) == ADDITEM_OVERAMOUNT )
+		{// buyer does not have enough space for this item
+			clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
+			return;
+		}
+
+		if( amount*(unsigned int)sd->inventory_data[index]->weight > pl_sd->max_weight-weight )
+		{// normally this is not supposed to happen, as the total weight is
+		 // checked upon creation, but the buyer could have gained items
+			clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
+			return;
+		}
+		weight+= amount*sd->inventory_data[index]->weight;
+
+		if( amount*pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit-zeny )
+		{// buyer does not have enough zeny
+			clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_ZENY, nameid);
+			return;
+		}
+		zeny+= amount*pl_sd->buyingstore.items[listidx].price;
+	}
+
+	// process item list
+	for( i = 0; i < count; i++ )
+	{// itemlist: <index>.W <name id>.W <amount>.W
+		unsigned short nameid, amount;
+		int index;
+
+		index  = RBUFW(itemlist,i*6+0)-2;
+		nameid = RBUFW(itemlist,i*6+2);
+		amount = RBUFW(itemlist,i*6+4);
+
+		ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid );
+		zeny = amount*pl_sd->buyingstore.items[listidx].price;
+
+		// log
+		if( log_config.enable_logs&LOG_BUYING_STORE )
+		{
+			log_pick_pc(sd, "B", nameid, -((int)amount), &sd->status.inventory[index]);
+			log_pick_pc(pl_sd, "B", nameid, amount, &sd->status.inventory[index]);
+		}
+		if( log_config.zeny )
+			log_zeny(sd, "B", pl_sd, zeny);
+
+		// move item
+		pc_additem(pl_sd, &sd->status.inventory[index], amount);
+		pc_delitem(sd, index, amount, 1, 0);
+		pl_sd->buyingstore.items[listidx].amount-= amount;
+
+		// pay up
+		pc_payzeny(pl_sd, zeny);
+		pc_getzeny(sd, zeny);
+		pl_sd->buyingstore.zenylimit-= zeny;
+
+		// notify clients
+		clif_buyingstore_delete_item(sd, index, amount, pl_sd->buyingstore.items[listidx].price);
+		clif_buyingstore_update_item(pl_sd, nameid, amount);
+	}
+
+	// check whether or not there is still something to buy
+	ARR_FIND( 0, pl_sd->buyingstore.slots, i, pl_sd->buyingstore.items[i].amount != 0 );
+	if( i == pl_sd->buyingstore.slots )
+	{// everything was bought
+		clif_buyingstore_trade_failed_buyer(pl_sd, BUYINGSTORE_TRADE_BUYER_NO_ITEMS);
+		buyingstore_close(pl_sd);
+	}
+	else if( pl_sd->buyingstore.zenylimit == 0 )
+	{// zeny limit reached
+		clif_buyingstore_trade_failed_buyer(pl_sd, BUYINGSTORE_TRADE_BUYER_ZENY);
+		buyingstore_close(pl_sd);
+	}
+}

+ 29 - 0
src/map/buyingstore.h

@@ -0,0 +1,29 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _BUYINGSTORE_H_
+#define _BUYINGSTORE_H_
+
+#define MAX_BUYINGSTORE_SLOTS 5
+
+struct s_buyingstore_item
+{
+	int price;
+	unsigned short amount;
+	unsigned short nameid;
+};
+
+struct s_buyingstore
+{
+	struct s_buyingstore_item items[MAX_BUYINGSTORE_SLOTS];
+	int zenylimit;
+	unsigned char slots;
+};
+
+bool buyingstore_setup(struct map_session_data* sd, unsigned char slots);
+void buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count);
+void buyingstore_close(struct map_session_data* sd);
+void buyingstore_open(struct map_session_data* sd, int account_id);
+void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count);
+
+#endif  // _BUYINGSTORE_H_

+ 6 - 1
src/map/chat.c

@@ -69,6 +69,11 @@ int chat_createpcchat(struct map_session_data* sd, const char* title, const char
 	if( sd->chatID )
 		return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex]
 
+	if( sd->vender_id || sd->state.buyingstore )
+	{// not chat, when you already have a store open
+		return 0;
+	}
+
 	if( map[sd->bl.m].flag.nochat )
 	{
 		clif_displaymessage(sd->fd, msg_txt(281));
@@ -108,7 +113,7 @@ int chat_joinchat(struct map_session_data* sd, int chatid, const char* pass)
 	nullpo_ret(sd);
 	cd = (struct chat_data*)map_id2bl(chatid);
 
-	if( cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->users >= cd->limit )
+	if( cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->vender_id || sd->state.buyingstore || sd->chatID || cd->users >= cd->limit )
 	{
 		clif_joinchatfail(sd,0);
 		return 0;

+ 319 - 4
src/map/clif.c

@@ -56,7 +56,10 @@ struct Clif_Config {
 struct s_packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB + 1];
 
 //Converts item type in case of pet eggs.
-#define itemtype(a) (a == IT_PETEGG)?IT_WEAPON:a
+inline int itemtype(int type)
+{
+	return ( type == IT_PETEGG ) ? IT_WEAPON : type;
+}
 
 #define WBUFPOS(p,pos,x,y,dir) \
 	do { \
@@ -3644,6 +3647,9 @@ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_d
 	if(dstsd->vender_id)
 		clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd);
 
+	if( dstsd->state.buyingstore )
+		clif_buyingstore_entry_single(sd, dstsd);
+
 	if(dstsd->spiritball > 0)
 		clif_spiritball_single(sd->fd, dstsd);
 
@@ -4072,6 +4078,8 @@ int clif_outsight(struct block_list *bl,va_list ap)
 			}
 			if(sd->vender_id)
 				clif_closevendingboard(bl,tsd->fd);
+			if( sd->state.buyingstore )
+				clif_buyingstore_disappear_entry_single(tsd, sd);
 			break;
 		case BL_ITEM:
 			clif_clearflooritem((struct flooritem_data*)bl,tsd->fd);
@@ -12851,7 +12859,7 @@ void clif_Auction_openwindow(struct map_session_data *sd)
 {
 	int fd = sd->fd;
 
-	if( sd->state.storage_flag || sd->vender_id || sd->state.trading )
+	if( sd->state.storage_flag || sd->vender_id || sd->state.buyingstore || sd->state.trading )
 		return;
 
 	WFIFOHEAD(fd,12);
@@ -14033,6 +14041,308 @@ void clif_showdigit(struct map_session_data* sd, unsigned char type, int value)
 	WFIFOSET(sd->fd, packet_len(0x1b1));
 }
 
+
+/// Buying Store System
+///
+
+/// Opens preparation window for buying store (ZC_OPEN_BUYING_STORE)
+/// 0810 <slots>.B
+void clif_buyingstore_open(struct map_session_data* sd)
+{
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0x810));
+	WFIFOW(fd,0) = 0x810;
+	WFIFOB(fd,2) = sd->buyingstore.slots;
+	WFIFOSET(fd,packet_len(0x810));
+}
+
+
+/// Request to create a buying store (CZ_REQ_OPEN_BUYING_STORE)
+/// 0811 <packet len>.W <limit zeny>.L <result>.B <store name>.80B { <name id>.W <amount>.W <price>.L }*
+/// result:
+///     0 = cancel
+///     1 = open
+static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data* sd)
+{
+	const unsigned int blocksize = 8;
+	uint8* itemlist;
+	char storename[MESSAGE_SIZE];
+	unsigned char result;
+	int zenylimit;
+	unsigned int count, packet_len;
+	struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+	packet_len = RFIFOW(fd,info->pos[0]);
+
+	// TODO: Make this check global for all variable length packets.
+	if( packet_len < 89 )
+	{// minimum packet length
+		ShowError("clif_parse_ReqOpenBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 89, packet_len, sd->bl.id);
+		return;
+	}
+
+	zenylimit = RFIFOL(fd,info->pos[1]);
+	result    = RFIFOL(fd,info->pos[2]);
+	safestrncpy(storename, (const char*)RFIFOP(fd,info->pos[3]), sizeof(storename));
+	itemlist  = RFIFOP(fd,info->pos[4]);
+
+	// so that buyingstore_create knows, how many elements it has access to
+	packet_len-= info->pos[4];
+
+	if( packet_len%blocksize )
+	{
+		ShowError("clif_parse_ReqOpenBuyingStore: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize);
+		return;
+	}
+	count = packet_len/blocksize;
+
+	buyingstore_create(sd, zenylimit, result, storename, itemlist, count);
+}
+
+
+/// Notification, that the requested buying store could not be created (ZC_FAILED_OPEN_BUYING_STORE_TO_BUYER)
+/// 0812 <result>.W <total weight>.L
+/// result:
+///     1 = "Failed to open buying store." (0x6cd, MSI_BUYINGSTORE_OPEN_FAILED)
+///     2 = "Total amount of then possessed items exceeds the weight limit by <weight/10-maxweight*90%>. Please re-enter." (0x6ce, MSI_BUYINGSTORE_OVERWEIGHT)
+///     8 = "No sale (purchase) information available." (0x705)
+/// other = nothing
+void clif_buyingstore_open_failed(struct map_session_data* sd, unsigned short result, unsigned int weight)
+{
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0x812));
+	WFIFOW(fd,0) = 0x812;
+	WFIFOW(fd,2) = result;
+	WFIFOL(fd,4) = weight;
+	WFIFOSET(fd,packet_len(0x812));
+}
+
+
+/// Notification, that the requested buying store was created (ZC_MYITEMLIST_BUYING_STORE)
+/// 0813 <packet len>.W <account id>.L <limit zeny>.L { <price>.L <count>.W <type>.B <name id>.W }*
+void clif_buyingstore_myitemlist(struct map_session_data* sd)
+{
+	int fd = sd->fd;
+	unsigned int i;
+
+	WFIFOHEAD(fd,12+sd->buyingstore.slots*9);
+	WFIFOW(fd,0) = 0x813;
+	WFIFOW(fd,2) = 12+sd->buyingstore.slots*9;
+	WFIFOL(fd,4) = sd->bl.id;
+	WFIFOL(fd,8) = sd->buyingstore.zenylimit;
+
+	for( i = 0; i < sd->buyingstore.slots; i++ )
+	{
+		WFIFOL(fd,12+i*9) = sd->buyingstore.items[i].price;
+		WFIFOW(fd,16+i*9) = sd->buyingstore.items[i].amount;
+		WFIFOB(fd,18+i*9) = itemtype(itemdb_type(sd->buyingstore.items[i].nameid));
+		WFIFOW(fd,19+i*9) = sd->buyingstore.items[i].nameid;
+	}
+
+	WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notifies clients in area of a buying store (ZC_BUYING_STORE_ENTRY)
+/// 0814 <account id>.L <store name>.80B
+void clif_buyingstore_entry(struct map_session_data* sd)
+{
+	uint8 buf[86];
+
+	WBUFW(buf,0) = 0x814;
+	WBUFL(buf,2) = sd->bl.id;
+	memcpy(WBUFP(buf,6), sd->message, MESSAGE_SIZE);
+
+	clif_send(buf, packet_len(0x814), &sd->bl, AREA_WOS);
+}
+void clif_buyingstore_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0x814));
+	WFIFOW(fd,0) = 0x814;
+	WFIFOL(fd,2) = pl_sd->bl.id;
+	memcpy(WFIFOP(fd,6), pl_sd->message, MESSAGE_SIZE);
+	WFIFOSET(fd,packet_len(0x814));
+}
+
+
+/// Request to close own buying store (CZ_REQ_CLOSE_BUYING_STORE)
+/// 0815
+static void clif_parse_ReqCloseBuyingStore(int fd, struct map_session_data* sd)
+{
+	buyingstore_close(sd);
+}
+
+
+/// Notifies clients in area that a buying store was closed (ZC_DISAPPEAR_BUYING_STORE_ENTRY)
+/// 0816 <account id>.L
+void clif_buyingstore_disappear_entry(struct map_session_data* sd)
+{
+	uint8 buf[6];
+
+	WBUFW(buf,0) = 0x816;
+	WBUFL(buf,2) = sd->bl.id;
+
+	clif_send(buf, packet_len(0x816), &sd->bl, AREA_WOS);
+}
+void clif_buyingstore_disappear_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0x816));
+	WFIFOW(fd,0) = 0x816;
+	WFIFOL(fd,2) = pl_sd->bl.id;
+	WFIFOSET(fd,packet_len(0x816));
+}
+
+
+/// Request to open someone else's buying store (CZ_REQ_CLICK_TO_BUYING_STORE)
+/// 0817 <account id>.L
+static void clif_parse_ReqClickBuyingStore(int fd, struct map_session_data* sd)
+{
+	int account_id;
+
+	account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+	buyingstore_open(sd, account_id);
+}
+
+
+/// Sends buying store item list (ZC_ACK_ITEMLIST_BUYING_STORE)
+/// 0818 <packet len>.W <account id>.L <store id>.L <limit zeny>.L { <price>.L <amount>.W <type>.B <name id>.W }*
+void clif_buyingstore_itemlist(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+	int fd = sd->fd;
+	unsigned int i;
+
+	WFIFOHEAD(fd,16+pl_sd->buyingstore.slots*9);
+	WFIFOW(fd,0) = 0x818;
+	WFIFOW(fd,2) = 16+pl_sd->buyingstore.slots*9;
+	WFIFOL(fd,4) = pl_sd->bl.id;
+	WFIFOL(fd,8) = pl_sd->buyer_id;
+	WFIFOL(fd,12) = pl_sd->buyingstore.zenylimit;
+
+	for( i = 0; i < pl_sd->buyingstore.slots; i++ )
+	{
+		WFIFOL(fd,16+i*9) = pl_sd->buyingstore.items[i].price;
+		WFIFOW(fd,20+i*9) = pl_sd->buyingstore.items[i].amount;  // TODO: Figure out, if no longer needed items (amount == 0) are listed on official.
+		WFIFOB(fd,22+i*9) = itemtype(itemdb_type(pl_sd->buyingstore.items[i].nameid));
+		WFIFOW(fd,23+i*9) = pl_sd->buyingstore.items[i].nameid;
+	}
+
+	WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Request to sell items to a buying store (CZ_REQ_TRADE_BUYING_STORE)
+/// 0819 <packet len>.W <account id>.L <store id>.L { <index>.W <name id>.W <amount>.W }*
+static void clif_parse_ReqTradeBuyingStore(int fd, struct map_session_data* sd)
+{
+	const unsigned int blocksize = 6;
+	uint8* itemlist;
+	int account_id;
+	unsigned int count, packet_len, buyer_id;
+	struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+	packet_len = RFIFOW(fd,info->pos[0]);
+
+	if( packet_len < 12 )
+	{// minimum packet length
+		ShowError("clif_parse_ReqTradeBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 12, packet_len, sd->bl.id);
+		return;
+	}
+
+	account_id = RFIFOL(fd,info->pos[1]);
+	buyer_id   = RFIFOL(fd,info->pos[2]);
+	itemlist   = RFIFOP(fd,info->pos[3]);
+
+	// so that buyingstore_trade knows, how many elements it has access to
+	packet_len-= info->pos[3];
+
+	if( packet_len%blocksize )
+	{
+		ShowError("clif_parse_ReqTradeBuyingStore: Unexpected item list size %u (account_id=%d, buyer_id=%d, block size=%u)\n", packet_len, sd->bl.id, account_id, blocksize);
+		return;
+	}
+	count = packet_len/blocksize;
+
+	buyingstore_trade(sd, account_id, buyer_id, itemlist, count);
+}
+
+
+/// Notifies the buyer, that the buying store has been closed due to a post-trade condition (ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER)
+/// 081a <result>.W
+/// result:
+///     3 = "All items within the buy limit were purchased." (0x6cf, MSI_BUYINGSTORE_TRADE_OVERLIMITZENY)
+///     4 = "All items were purchased." (0x6d0, MSI_BUYINGSTORE_TRADE_BUYCOMPLETE)
+/// other = nothing
+void clif_buyingstore_trade_failed_buyer(struct map_session_data* sd, short result)
+{
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0x81a));
+	WFIFOW(fd,0) = 0x81a;
+	WFIFOW(fd,2) = result;
+	WFIFOSET(fd,packet_len(0x81a));
+}
+
+
+/// Updates the zeny limit and an item in the buying store item list (ZC_UPDATE_ITEM_FROM_BUYING_STORE)
+/// 081b <name id>.W <amount>.W <limit zeny>.L
+void clif_buyingstore_update_item(struct map_session_data* sd, unsigned short nameid, unsigned short amount)
+{
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0x81b));
+	WFIFOW(fd,0) = 0x81b;
+	WFIFOW(fd,2) = nameid;
+	WFIFOW(fd,4) = amount;  // amount of nameid received
+	WFIFOW(fd,6) = sd->buyingstore.zenylimit;
+	WFIFOSET(fd,packet_len(0x81b));
+}
+
+
+/// Deletes item from inventory, that was sold to a buying store (ZC_ITEM_DELETE_BUYING_STORE)
+/// 081c <index>.W <amount>.W <price>.L
+/// message:
+///     "%s (%d) were sold at %dz." (0x6d2, MSI_BUYINGSTORE_TRADE_SELLCOMPLETE)
+///
+/// @note   This function has to be called _instead_ of clif_delitem/clif_dropitem.
+void clif_buyingstore_delete_item(struct map_session_data* sd, short index, unsigned short amount, int price)
+{
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0x81c));
+	WFIFOW(fd,0) = 0x81c;
+	WFIFOW(fd,2) = index+2;
+	WFIFOW(fd,4) = amount;
+	WFIFOL(fd,6) = price;  // price per item, client calculates total Zeny by itself
+	WFIFOSET(fd,packet_len(0x81c));
+}
+
+
+/// Notifies the seller, that a buying store trade failed (ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER)
+/// 0824 <result>.W <name id>.W
+/// result:
+///     5 = "The deal has failed." (0x39, MSI_DEAL_FAIL)
+///     6 = "The trade failed, because the entered amount of item %s is higher, than the buyer is willing to buy." (0x6d3, MSI_BUYINGSTORE_TRADE_OVERCOUNT)
+///     7 = "The trade failed, because the buyer is lacking required balance." (0x6d1, MSI_BUYINGSTORE_TRADE_LACKBUYERZENY)
+/// other = nothing
+void clif_buyingstore_trade_failed_seller(struct map_session_data* sd, short result, unsigned short nameid)
+{
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0x824));
+	WFIFOW(fd,0) = 0x824;
+	WFIFOW(fd,2) = result;
+	WFIFOW(fd,4) = nameid;
+	WFIFOSET(fd,packet_len(0x824));
+}
+
+
 /*==========================================
  * ƒpƒPƒbƒgƒfƒoƒbƒO
  *------------------------------------------*/
@@ -14435,8 +14745,8 @@ static int packetdb_readdb(void)
 #else // for Party booking ( PACKETVER >= 20091229 )
 	   -1, -1, 18,  4,  8,  6,  2,  4, 14, 50, 18,  6,  2,  3, 14, 20,
 #endif
-	    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-	    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	    3, -1,  8, -1,  86, 2,  6,  6, -1, -1,  4, 10, 10,  0,  0,  0,
+	    0,  0,  0,  0,  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 	    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 	};
 	struct {
@@ -14628,6 +14938,11 @@ static int packetdb_readdb(void)
 		{clif_parse_PartyBookingDeleteReq,"bookingdelreq"},
 #endif
 		{clif_parse_PVPInfo,"pvpinfo"},
+		// Buying Store
+		{clif_parse_ReqOpenBuyingStore,"reqopenbuyingstore"},
+		{clif_parse_ReqCloseBuyingStore,"reqclosebuyingstore"},
+		{clif_parse_ReqClickBuyingStore,"reqclickbuyingstore"},
+		{clif_parse_ReqTradeBuyingStore,"reqtradebuyingstore"},
 		{NULL,NULL}
 	};
 

+ 14 - 0
src/map/clif.h

@@ -605,4 +605,18 @@ void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_boo
 
 void clif_showdigit(struct map_session_data* sd, unsigned char type, int value);
 
+/// Buying Store System
+void clif_buyingstore_open(struct map_session_data* sd);
+void clif_buyingstore_open_failed(struct map_session_data* sd, unsigned short result, unsigned int weight);
+void clif_buyingstore_myitemlist(struct map_session_data* sd);
+void clif_buyingstore_entry(struct map_session_data* sd);
+void clif_buyingstore_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd);
+void clif_buyingstore_disappear_entry(struct map_session_data* sd);
+void clif_buyingstore_disappear_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd);
+void clif_buyingstore_itemlist(struct map_session_data* sd, struct map_session_data* pl_sd);
+void clif_buyingstore_trade_failed_buyer(struct map_session_data* sd, short result);
+void clif_buyingstore_update_item(struct map_session_data* sd, unsigned short nameid, unsigned short amount);
+void clif_buyingstore_delete_item(struct map_session_data* sd, short index, unsigned short amount, int price);
+void clif_buyingstore_trade_failed_seller(struct map_session_data* sd, short result, unsigned short nameid);
+
 #endif /* _CLIF_H_ */

+ 28 - 0
src/map/itemdb.c

@@ -663,6 +663,33 @@ static bool itemdb_read_itemdelay(char* str[], int columns, int current)
 	return true;
 }
 
+
+/// Reads items allowed to be sold in buying stores
+static bool itemdb_read_buyingstore(char* fields[], int columns, int current)
+{// <nameid>
+	int nameid;
+	struct item_data* id;
+
+	nameid = atoi(fields[0]);
+
+	if( ( id = itemdb_exists(nameid) ) == NULL )
+	{
+		ShowWarning("itemdb_read_buyingstore: Invalid item id %d.\n", nameid);
+		return false;
+	}
+
+	if( !itemdb_isstackable2(id) )
+	{
+		ShowWarning("itemdb_read_buyingstore: Non-stackable item id %d cannot be enabled for buying store.\n", nameid);
+		return false;
+	}
+
+	id->flag.buyingstore = true;
+
+	return true;
+}
+
+
 /*======================================
  * Applies gender restrictions according to settings. [Skotlex]
  *======================================*/
@@ -979,6 +1006,7 @@ static void itemdb_read(void)
 	sv_readdb(db_path, "item_noequip.txt", ',', 2, 2, -1,             &itemdb_read_noequip);
 	sv_readdb(db_path, "item_trade.txt",   ',', 3, 3, -1,             &itemdb_read_itemtrade);
 	sv_readdb(db_path, "item_delay.txt",   ',', 2, 2, MAX_ITEMDELAYS, &itemdb_read_itemdelay);
+	sv_readdb(db_path, "item_buyingstore.txt", ',', 1, 1, -1,         &itemdb_read_buyingstore);
 }
 
 /*==========================================

+ 1 - 0
src/map/itemdb.h

@@ -76,6 +76,7 @@ struct item_data {
 		unsigned delay_consume : 1;	// Signifies items that are not consumed immediately upon double-click [Skotlex]
 		unsigned trade_restriction : 7;	//Item restrictions mask [Skotlex]
 		unsigned autoequip: 1;
+		unsigned buyingstore : 1;
 	} flag;
 	short gm_lv_trade_override;	//GM-level to override trade_restriction
 };

+ 4 - 3
src/map/log.h

@@ -36,9 +36,10 @@ typedef enum log_what {
 	LOG_USED_ITEMS          = 0x0100, // used by player
 	LOG_MVP_PRIZE           = 0x0200,
 	LOG_COMMAND_ITEMS       = 0x0400, // created/deleted through @/# commands
-	LOG_STORAGE_ITEMS	= 0x0800, // placed/retrieved from storage
-	LOG_GSTORAGE_ITEMS	= 0x1000, // placed/retrieved from guild storage
-	LOG_MAILS		= 0x2000  // mail system transactions
+	LOG_STORAGE_ITEMS       = 0x0800, // placed/retrieved from storage
+	LOG_GSTORAGE_ITEMS      = 0x1000, // placed/retrieved from guild storage
+	LOG_MAILS               = 0x2000, // mail system transactions
+	LOG_BUYING_STORE        = 0x4000, // buying store transactions
 } log_what;
 
 extern struct Log_Config {

+ 1 - 1
src/map/mail.c

@@ -162,7 +162,7 @@ int mail_openmail(struct map_session_data *sd)
 {
 	nullpo_ret(sd);
 
-	if( sd->state.storage_flag || sd->vender_id || sd->state.trading )
+	if( sd->state.storage_flag || sd->vender_id || sd->state.buyingstore || sd->state.trading )
 		return 0;
 
 	clif_Mail_window(sd->fd, 0);

+ 10 - 5
src/map/pc.h

@@ -7,6 +7,7 @@
 #include "../common/mmo.h" // JOB_*, MAX_FAME_LIST, struct fame_list, struct mmo_charstatus
 #include "../common/timer.h" // INVALID_TIMER
 #include "battle.h" // battle_config
+#include "buyingstore.h"  // struct s_buyingstore
 #include "itemdb.h" // MAX_ITEMGROUP
 #include "map.h" // RC_MAX
 #include "pc.h" // struct map_session_data
@@ -128,6 +129,7 @@ struct map_session_data {
 		unsigned doridori : 1;
 		unsigned ignoreAll : 1;
 		unsigned debug_remove_map : 1; // temporary state to track double remove_map's [FlavioJS]
+		unsigned buyingstore : 1;
 		unsigned short autoloot;
 		unsigned short autolootid; // [Zephyrus]
 		unsigned noks : 3; // [Zeph Kill Steal Protection]
@@ -356,6 +358,9 @@ struct map_session_data {
 	char message[MESSAGE_SIZE];
 	struct s_vending vending[MAX_VENDING];
 
+	unsigned int buyer_id;  // uid of open buying store
+	struct s_buyingstore buyingstore;
+
 	struct pet_data *pd;
 	struct homun_data *hd;	// [blackhole89]
 	struct mercenary_data *md;
@@ -413,8 +418,8 @@ struct map_session_data {
 	const char* debug_func;
 };
 
-//Update this max as necessary. 54 is the value needed for Super Baby currently
-#define MAX_SKILL_TREE 54
+//Update this max as necessary. 55 is the value needed for Super Baby currently
+#define MAX_SKILL_TREE 55
 //Total number of classes (for data storage)
 #define CLASS_COUNT (JOB_MAX - JOB_NOVICE_HIGH + JOB_MAX_BASIC)
 
@@ -515,9 +520,9 @@ extern int duel_count;
 #define pc_setsit(sd)         ( (sd)->state.dead_sit = (sd)->vd.dead_sit = 2 )
 #define pc_isdead(sd)         ( (sd)->state.dead_sit == 1 )
 #define pc_issit(sd)          ( (sd)->vd.dead_sit == 2 )
-#define pc_isidle(sd)         ( (sd)->chatID || (sd)->vender_id || DIFF_TICK(last_tick, (sd)->idletime) >= battle_config.idle_no_share )
-#define pc_istrading(sd)      ( (sd)->npc_id || (sd)->vender_id || (sd)->state.trading )
-#define pc_cant_act(sd)       ( (sd)->npc_id || (sd)->vender_id || (sd)->chatID || (sd)->sc.opt1 || (sd)->state.trading || (sd)->state.storage_flag )
+#define pc_isidle(sd)         ( (sd)->chatID || (sd)->vender_id || (sd)->state.buyingstore || DIFF_TICK(last_tick, (sd)->idletime) >= battle_config.idle_no_share )
+#define pc_istrading(sd)      ( (sd)->npc_id || (sd)->vender_id || (sd)->state.buyingstore || (sd)->state.trading )
+#define pc_cant_act(sd)       ( (sd)->npc_id || (sd)->vender_id || (sd)->state.buyingstore || (sd)->chatID || (sd)->sc.opt1 || (sd)->state.trading || (sd)->state.storage_flag )
 #define pc_setdir(sd,b,h)     ( (sd)->ud.dir = (b) ,(sd)->head_dir = (h) )
 #define pc_setchatid(sd,n)    ( (sd)->chatID = n )
 #define pc_ishiding(sd)       ( (sd)->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) )

+ 18 - 0
src/map/script.c

@@ -14807,6 +14807,23 @@ BUILDIN_FUNC(pushpc)
 	return 0;
 }
 
+
+/// Invokes buying store preparation window
+/// buyingstore <slots>;
+BUILDIN_FUNC(buyingstore)
+{
+	struct map_session_data* sd;
+
+	if( ( sd = script_rid2sd(st) ) == NULL )
+	{
+		return 0;
+	}
+
+	buyingstore_setup(sd, script_getnum(st,2));
+	return 0;
+}
+
+
 // declarations that were supposed to be exported from npc_chat.c
 #ifdef PCRE_SUPPORT
 BUILDIN_FUNC(defpattern);
@@ -15168,6 +15185,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(areamobuseskill,"siiiiviiiii"),
 	BUILDIN_DEF(progressbar,"si"),
 	BUILDIN_DEF(pushpc,"ii"),
+	BUILDIN_DEF(buyingstore,"i"),
 	// WoE SE
 	BUILDIN_DEF(agitstart2,""),
 	BUILDIN_DEF(agitend2,""),

+ 6 - 0
src/map/skill.c

@@ -5692,6 +5692,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	case ALL_WEWISH:
 		clif_skill_nodamage(src,bl,skillid,skilllv,1);
 		break;
+	case ALL_BUYING_STORE:
+		if( sd )
+		{// players only, skill allows 5 buying slots
+			clif_skill_nodamage(src, bl, skillid, skilllv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS));
+		}
+		break;
 	default:
 		ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skillid);
 		clif_skill_nodamage(src,bl,skillid,skilllv,1);

+ 2 - 2
src/map/trade.c

@@ -138,8 +138,8 @@ void trade_tradeack(struct map_session_data *sd, int type)
 	}
 
 	//Check if you can start trade.
-	if (sd->npc_id || sd->vender_id || sd->state.storage_flag ||
-		tsd->npc_id || tsd->vender_id || tsd->state.storage_flag)
+	if (sd->npc_id || sd->vender_id || sd->state.buyingstore || sd->state.storage_flag ||
+		tsd->npc_id || tsd->vender_id || tsd->state.buyingstore || tsd->state.storage_flag)
 	{	//Fail
 		clif_tradestart(sd, 2);
 		clif_tradestart(tsd, 2);

+ 2 - 0
src/map/unit.c

@@ -803,6 +803,7 @@ int unit_can_move(struct block_list *bl)
 	if (sd && (
 		pc_issit(sd) ||
 		sd->vender_id ||
+		sd->state.buyingstore ||
 		sd->state.blockedmove
 	))
 		return 0; //Can't move
@@ -1872,6 +1873,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
 			trade_tradecancel(sd);
 		if(sd->vender_id)
 			vending_closevending(sd);
+		buyingstore_close(sd);
 		if(sd->state.storage_flag == 1)
 			storage_storage_quit(sd,0);
 		else if (sd->state.storage_flag == 2)

+ 2 - 0
vcproj-10/map-server_sql.vcxproj

@@ -146,6 +146,7 @@
     <ClInclude Include="..\src\map\atcommand.h" />
     <ClInclude Include="..\src\map\battle.h" />
     <ClInclude Include="..\src\map\battleground.h" />
+    <ClInclude Include="..\src\map\buyingstore.h" />
     <ClInclude Include="..\src\map\chat.h" />
     <ClInclude Include="..\src\map\chrif.h" />
     <ClInclude Include="..\src\map\clif.h" />
@@ -195,6 +196,7 @@
     <ClCompile Include="..\src\map\atcommand.c" />
     <ClCompile Include="..\src\map\battle.c" />
     <ClCompile Include="..\src\map\battleground.c" />
+    <ClCompile Include="..\src\map\buyingstore.c" />
     <ClCompile Include="..\src\map\chat.c" />
     <ClCompile Include="..\src\map\chrif.c" />
     <ClCompile Include="..\src\map\clif.c" />

+ 2 - 0
vcproj-10/map-server_txt.vcxproj

@@ -125,6 +125,7 @@
     <ClCompile Include="..\src\map\atcommand.c" />
     <ClCompile Include="..\src\map\battle.c" />
     <ClCompile Include="..\src\map\battleground.c" />
+    <ClCompile Include="..\src\map\buyingstore.c" />
     <ClCompile Include="..\src\map\chat.c" />
     <ClCompile Include="..\src\map\chrif.c" />
     <ClCompile Include="..\src\map\clif.c" />
@@ -174,6 +175,7 @@
     <ClInclude Include="..\src\map\atcommand.h" />
     <ClInclude Include="..\src\map\battle.h" />
     <ClInclude Include="..\src\map\battleground.h" />
+    <ClInclude Include="..\src\map\buyingstore.h" />
     <ClInclude Include="..\src\map\chat.h" />
     <ClInclude Include="..\src\map\chrif.h" />
     <ClInclude Include="..\src\map\clif.h" />

+ 8 - 0
vcproj-6/map-server_sql.dsp

@@ -231,6 +231,14 @@ SOURCE=..\src\map\battleground.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\src\map\buyingstore.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\map\buyingstore.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\map\chat.c
 # End Source File
 # Begin Source File

+ 8 - 0
vcproj-6/map-server_txt.dsp

@@ -211,6 +211,10 @@ SOURCE=..\src\map\battleground.c
 # End Source File
 # Begin Source File
 
+SOURCE=..\src\map\buyingstore.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\map\chat.c
 # End Source File
 # Begin Source File
@@ -335,6 +339,10 @@ SOURCE=..\src\map\battleground.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\src\map\buyingstore.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\map\chat.h
 # End Source File
 # Begin Source File

+ 6 - 0
vcproj-7.1/map-server_sql.vcproj

@@ -166,6 +166,12 @@
 			<File
 				RelativePath="..\src\map\battleground.h">
 			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.c">
+			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.h">
+			</File>
 			<File
 				RelativePath="..\src\map\chat.c">
 			</File>

+ 6 - 0
vcproj-7.1/map-server_txt.vcproj

@@ -166,6 +166,12 @@
 			<File
 				RelativePath="..\src\map\battleground.h">
 			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.c">
+			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.h">
+			</File>
 			<File
 				RelativePath="..\src\map\chat.c">
 			</File>

+ 7 - 0
vcproj-8/map-server_sql.vcproj

@@ -374,6 +374,13 @@
 			<File
 				RelativePath="..\src\map\battleground.h"
 				>
+			<File
+				RelativePath="..\src\map\buyingstore.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.h"
+				>
 			</File>
 			<File
 				RelativePath="..\src\map\chat.c"

+ 8 - 0
vcproj-8/map-server_txt.vcproj

@@ -226,6 +226,14 @@
 				RelativePath="..\src\map\battleground.h"
 				>
 			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.h"
+				>
+			</File>
 			<File
 				RelativePath="..\src\map\chat.c"
 				>

+ 8 - 0
vcproj-9/map-server_sql.vcproj

@@ -374,6 +374,14 @@
 				RelativePath="..\src\map\battleground.c"
 				>
 			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.h"
+				>
+			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.c"
+				>
+			</File>
 			<File
 				RelativePath="..\src\map\chat.c"
 				>

+ 8 - 0
vcproj-9/map-server_txt.vcproj

@@ -225,6 +225,14 @@
 				RelativePath="..\src\map\battleground.h"
 				>
 			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.c"
+				>
+			</File>
+			<File
+				RelativePath="..\src\map\buyingstore.h"
+				>
+			</File>
 			<File
 				RelativePath="..\src\map\chat.c"
 				>