diff --git a/src/platform/ScriptLib/AppliesScript.inc b/src/platform/ScriptLib/AppliesScript.inc index 27af5377..83691b3b 100644 --- a/src/platform/ScriptLib/AppliesScript.inc +++ b/src/platform/ScriptLib/AppliesScript.inc @@ -9,6 +9,12 @@ L = player.AutoHuntLocalize TEXTS = ( ("APPLY_ATTBONUS_STONE", "对陨石伤害 +%d%%"), ("APPLY_ATTBONUS_BOSS", "对 Boss 伤害 +%d%%"), + ("APPLY_ENCHANT_ELEC", "对雷属性怪物伤害 +%d%%"), + ("APPLY_ENCHANT_FIRE", "对火属性怪物伤害 +%d%%"), + ("APPLY_ENCHANT_ICE", "对冰属性怪物伤害 +%d%%"), + ("APPLY_ENCHANT_WIND", "对风属性怪物伤害 +%d%%"), + ("APPLY_ENCHANT_EARTH", "对土属性怪物伤害 +%d%%"), + ("APPLY_ENCHANT_DARK", "对暗属性怪物伤害 +%d%%"), ) diff --git a/src/platform/ScriptLib/PendantScript.inc b/src/platform/ScriptLib/PendantScript.inc new file mode 100644 index 00000000..212438c8 --- /dev/null +++ b/src/platform/ScriptLib/PendantScript.inc @@ -0,0 +1,100 @@ +// PORT: AVA2 talisman (ITEM_TALISMAN_EQUIPMENT + ELEMENT_NEW_BONUSES), as module mt_pendant. Talismans are +// ITEM_ARMOR / ARMOR_PENDANT worn in c_Equipment_Pendant (server WEAR_PENDANT, metin2-server port_pendant.cpp). +// This script adds that slot to the inventory's equipment panel (the free spot under the earring on +// equipment_base), marks unequipped talismans as drop targets of the talisman attribute items (71302 / 53996 +// reroll, 71301 / 53997 add up to 5) and nothing else, and renders the "MTPENDANT " server messages that +// replace AVA2's locale strings 1056 / 1057 / 1067. +static const char c_szPendantScript[] = R"PY(# -*- coding: utf-8 -*- +import chat +import game +import item +import player +import ui +import uiInventory + +L=player.AutoHuntLocalize +CHANGE=(71302,53996) +ADD=(71301,53997) +MAX_ATTR=5 +MESSAGES={'target':'该道具只能用于护符。','equipped':'请先卸下护符再使用。','empty':'这件护符没有可重洗的属性。', + 'changed':'护符属性已重新随机。','full':'这件护符的属性已满(最多 5 条)。','added':'护符追加属性成功。'} +# EquipmentSlot coordinates of the slot (the earring is at 114,84). +SLOT=(item.EQUIPMENT_PENDANT,(114,118)) + + +def FindChild(node,name): + if node.get("name")==name:return node + for child in node.get("children",()): + found=FindChild(child,name) + if found:return found + return None + + +def PatchInventoryWindow(window): + base=FindChild(window,"Equipment_Base");slot=FindChild(window,"EquipmentSlot") + if not base or not slot:return + index,(x,y)=SLOT + frame={"name":"PendantSlotBase","type":"image","x":slot["x"]+x,"y":slot["y"]+y,"image":"d:/ymir work/ui/public/Slot_Base.sub"} + base["children"]=(frame,)+tuple(base.get("children",())) + slot["slot"]=tuple(slot["slot"])+({"index":index,"x":x,"y":y,"width":32,"height":32},) + + +_execfile=ui.execfile + +def ExecFile(fileName,dict): + _execfile(fileName,dict) + name=fileName.replace("\\","/").split("/")[-1].lower() + if name=="inventorywindow.py" and "window" in dict:PatchInventoryWindow(dict["window"]) + +ui.execfile=ExecFile + + +_refreshEquip=uiInventory.InventoryWindow.RefreshEquipSlotWindow + +def RefreshEquipSlotWindow(self): + _refreshEquip(self) + index=SLOT[0] + self.wndEquip.SetItemSlot(index,player.GetItemIndex(index),0) + self.wndEquip.RefreshSlot() + +uiInventory.InventoryWindow.RefreshEquipSlotWindow=RefreshEquipSlotWindow + + +def IsTalisman(vnum): + if not vnum:return False + item.SelectItem(vnum) + return item.GetItemType()==item.ITEM_TYPE_ARMOR and item.GetItemSubType()==item.ARMOR_PENDANT + + +def AttrCount(slot): + return len([1 for i in xrange(player.ATTRIBUTE_SLOT_MAX_NUM) if player.GetItemAttribute(slot,i)[0]]) + + +_canUse=uiInventory.InventoryWindow._InventoryWindow__CanUseSrcItemToDstItem + +def CanUseSrcItemToDstItem(self,srcItemVNum,srcSlotPos,dstSlotPos): + scroll=srcItemVNum in CHANGE or srcItemVNum in ADD + talisman=IsTalisman(player.GetItemIndex(dstSlotPos)) + if not scroll: + # the server refuses every other attribute / socket item on a talisman + if talisman and item.GetUseType(srcItemVNum) in self.USE_TYPE_TUPLE:return False + return _canUse(self,srcItemVNum,srcSlotPos,dstSlotPos) + if srcSlotPos==dstSlotPos or not talisman or player.IsEquipmentSlot(dstSlotPos):return False + count=AttrCount(dstSlotPos) + return count>0 if srcItemVNum in CHANGE else count1 else '') + if text:chat.AppendChat(chat.CHAT_TYPE_INFO,L(text)) + return 1 + return _command(self,line) + +game.GameWindow.BINARY_ServerCommand_Run=Command +)PY"; diff --git a/src/platform/ScriptLib/PythonBoot.cpp b/src/platform/ScriptLib/PythonBoot.cpp index 67e0aa7e..cc7aeb5a 100644 --- a/src/platform/ScriptLib/PythonBoot.cpp +++ b/src/platform/ScriptLib/PythonBoot.cpp @@ -1019,6 +1019,7 @@ namespace PythonBoot #include "SashScript.inc" #include "MountScript.inc" #include "CostumeAttrScript.inc" +#include "PendantScript.inc" static void install_script_module(const char* c_szModule, const char* c_szScript) { @@ -1106,6 +1107,7 @@ bool RunMainScript(const char* lpCmdLine, std::string* error) install_script_module("mt_sash", c_szSashScript); install_script_module("mt_mount", c_szMountScript); install_script_module("mt_costume_attr", c_szCostumeAttrScript); + install_script_module("mt_pendant", c_szPendantScript); if (g_touch_input) install_script_module("mt_mobile_hud", c_szMobileHUDScript); } diff --git a/src/platform/ScriptLib/SwitchbotScript.inc b/src/platform/ScriptLib/SwitchbotScript.inc index 41722ae3..40713a89 100644 --- a/src/platform/ScriptLib/SwitchbotScript.inc +++ b/src/platform/ScriptLib/SwitchbotScript.inc @@ -188,10 +188,11 @@ class SwitchbotWindow(ui.Window): vnum=player.GetItemIndex(player.INVENTORY,cell) if not vnum:continue item.SelectItem(vnum);kind=item.GetItemType() - # 54001-54267 with a value2 mask are AVA2 costume enchants (mt_costume_attr), not switchers. - if kind==item.ITEM_TYPE_USE and item.GetItemSubType()==USE_CHANGE_ATTRIBUTE and not (54001<=vnum<=54267 and item.GetValue(2)): + # 54001-54267 with a value2 mask are AVA2 costume enchants (mt_costume_attr), 71302 / 53996 talisman + # rerolls (mt_pendant), not switchers; talismans have their own attribute pool. + if kind==item.ITEM_TYPE_USE and item.GetItemSubType()==USE_CHANGE_ATTRIBUTE and not (54001<=vnum<=54267 and item.GetValue(2)) and vnum not in (71302,53996): scrolls[vnum]=scrolls.get(vnum,0)+player.GetItemCount(player.INVENTORY,cell) - elif kind in (item.ITEM_TYPE_WEAPON,item.ITEM_TYPE_ARMOR) and any(player.GetItemAttribute(player.INVENTORY,cell,i)[0] for i in range(5)): + elif kind in (item.ITEM_TYPE_WEAPON,item.ITEM_TYPE_ARMOR) and not (kind==item.ITEM_TYPE_ARMOR and item.GetItemSubType()==item.ARMOR_PENDANT) and any(player.GetItemAttribute(player.INVENTORY,cell,i)[0] for i in range(5)): items.append((cell,vnum)) selected=self.Selected();scroll=self.Scroll();state=self.bot.state if state and state['active'] and (state['cell'],state['vnum']) in items: diff --git a/src/port/GameLib/ItemData.h b/src/port/GameLib/ItemData.h index c8e59692..cfdf2f32 100644 --- a/src/port/GameLib/ItemData.h +++ b/src/port/GameLib/ItemData.h @@ -95,6 +95,7 @@ class CItemData ARMOR_FOOTS, ARMOR_NECK, ARMOR_EAR, + ARMOR_PENDANT, // PORT: AVA2 talisman, worn in c_Equipment_Pendant ARMOR_NUM_TYPES }; @@ -353,7 +354,13 @@ class CItemData APPLY_ANTI_CRITICAL_PCT, //90 크리티컬 저항 APPLY_ANTI_PENETRATE_PCT, //91 관통타격 저항 - // PORT AVA2 bonus numbering, matching the server (common/length.h): 92-138 are not ported yet. + // PORT AVA2 bonus numbering, matching the server (common/length.h): 92-122 and 129-138 are not ported yet. + APPLY_ENCHANT_ELEC = 123, // talisman: attack +x% on monsters of that element + APPLY_ENCHANT_FIRE = 124, + APPLY_ENCHANT_ICE = 125, + APPLY_ENCHANT_WIND = 126, + APPLY_ENCHANT_EARTH = 127, + APPLY_ENCHANT_DARK = 128, APPLY_ATTBONUS_STONE = 139, APPLY_ATTBONUS_BOSS = 140, diff --git a/src/port/UserInterface/GameType.h b/src/port/UserInterface/GameType.h index 91b87f96..0cee29b0 100644 --- a/src/port/UserInterface/GameType.h +++ b/src/port/UserInterface/GameType.h @@ -108,6 +108,7 @@ enum EDragonSoulStepTypes const DWORD c_Costume_Slot_Mount = c_Equipment_Start + 25; // PORT: AVA2 mount costume, server WEAR_COSTUME_MOUNT const DWORD c_Costume_Slot_MountSkin = c_Equipment_Start + 26; // PORT: AVA2 mount skin, server WEAR_COSTUME_MOUNT_SKIN #endif + const DWORD c_Equipment_Pendant = c_Equipment_Start + 27; // PORT: AVA2 talisman, server WEAR_PENDANT // [주의] 숫자(32) 하드코딩 주의. 현재 서버에서 용혼석 슬롯은 32부터임. diff --git a/src/port/UserInterface/PythonItemModule.cpp b/src/port/UserInterface/PythonItemModule.cpp index 15a62559..a543c2ef 100644 --- a/src/port/UserInterface/PythonItemModule.cpp +++ b/src/port/UserInterface/PythonItemModule.cpp @@ -760,6 +760,8 @@ void initItem() PyModule_AddIntConstant(poModule, "ARMOR_FOOTS", CItemData::ARMOR_FOOTS); PyModule_AddIntConstant(poModule, "ARMOR_NECK", CItemData::ARMOR_NECK); PyModule_AddIntConstant(poModule, "ARMOR_EAR", CItemData::ARMOR_EAR); + PyModule_AddIntConstant(poModule, "ARMOR_PENDANT", CItemData::ARMOR_PENDANT); // PORT: AVA2 talisman + PyModule_AddIntConstant(poModule, "EQUIPMENT_PENDANT", c_Equipment_Pendant); PyModule_AddIntConstant(poModule, "ITEM_APPLY_MAX_NUM", CItemData::ITEM_APPLY_MAX_NUM); PyModule_AddIntConstant(poModule, "ITEM_SOCKET_MAX_NUM", CItemData::ITEM_SOCKET_MAX_NUM); @@ -860,4 +862,10 @@ void initItem() PyModule_AddIntConstant(poModule, "APPLY_ANTI_PENETRATE_PCT", CItemData::APPLY_ANTI_PENETRATE_PCT ); PyModule_AddIntConstant(poModule, "APPLY_ATTBONUS_STONE", CItemData::APPLY_ATTBONUS_STONE ); PyModule_AddIntConstant(poModule, "APPLY_ATTBONUS_BOSS", CItemData::APPLY_ATTBONUS_BOSS ); + PyModule_AddIntConstant(poModule, "APPLY_ENCHANT_ELEC", CItemData::APPLY_ENCHANT_ELEC ); + PyModule_AddIntConstant(poModule, "APPLY_ENCHANT_FIRE", CItemData::APPLY_ENCHANT_FIRE ); + PyModule_AddIntConstant(poModule, "APPLY_ENCHANT_ICE", CItemData::APPLY_ENCHANT_ICE ); + PyModule_AddIntConstant(poModule, "APPLY_ENCHANT_WIND", CItemData::APPLY_ENCHANT_WIND ); + PyModule_AddIntConstant(poModule, "APPLY_ENCHANT_EARTH", CItemData::APPLY_ENCHANT_EARTH ); + PyModule_AddIntConstant(poModule, "APPLY_ENCHANT_DARK", CItemData::APPLY_ENCHANT_DARK ); } diff --git a/tests/wiki/test_switchbot.py b/tests/wiki/test_switchbot.py index 5d219f7f..120a99cf 100644 --- a/tests/wiki/test_switchbot.py +++ b/tests/wiki/test_switchbot.py @@ -41,7 +41,7 @@ class SwitchbotTests(unittest.TestCase): mods['net'].SendChatPacket=self.sent.append mods['game'].GameWindow=type('GameWindow',(),dict(Open=lambda s:None,Close=lambda s:None,BINARY_ServerCommand_Run=lambda s,line:7)) grp=mods['grp'];grp.SetColor=grp.RenderBar=grp.RenderBox=lambda *a:None - it=mods['item'];it.ITEM_TYPE_WEAPON=1;it.ITEM_TYPE_ARMOR=2;it.ITEM_TYPE_USE=3;self.selected=None + it=mods['item'];it.ITEM_TYPE_WEAPON=1;it.ITEM_TYPE_ARMOR=2;it.ITEM_TYPE_USE=3;it.ARMOR_PENDANT=7;self.selected=None it.SelectItem=lambda v:setattr(self,'selected',v);it.GetItemType=lambda:protos[self.selected][0];it.GetItemSubType=lambda:protos[self.selected][1] it.GetItemName=lambda:'N%d'%self.selected pl=mods['player'];pl.INVENTORY=1;pl.INVENTORY_PAGE_SIZE=45;pl.INVENTORY_PAGE_COUNT=5 diff --git a/tools/server/ava2-items/make_items.py b/tools/server/ava2-items/make_items.py index 0638a31f..7af2ddc1 100644 --- a/tools/server/ava2-items/make_items.py +++ b/tools/server/ava2-items/make_items.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Stage the AVA2 drop items that the 203 server lacks as ordinary 40250 items (server rows + client patch pack). Only types/subtypes/flags the 40250 server understands survive; items of AVA2-only systems (pets, pendants, -new rings, weapon/sash-skin/effect costume enchants, talisman bonus items, new skill books) become plain ITEM_NONE items until those +new rings, weapon/sash-skin/effect costume enchants, new skill books) become plain ITEM_NONE items until those systems are ported. Also writes mobs-server.tsv (MOUNT_MOBS) for install_items.py. Usage: see build_zh_locale.build_ava2_items and --help. """ import argparse,json,re,shutil,subprocess @@ -11,13 +11,18 @@ RANGES=((54001,54027,'时装强化石'),(54028,54078,'发型强化石'),(54079,5 (54124,54174,'坐骑外观强化石'),(54175,54219,'披风外观强化石'),(54220,54267,'特效强化石')) # Dragon armor: +0..+9 per class (the refine chain needs every step). DRAGON={21210:'暗龙铠甲',21230:'风龙铠甲',21250:'魔龙铠甲',21270:'神龙铠甲'} +# Talismans (P7, server port_pendant.cpp): +0..+200 per element, refine sets 440-499 (../pendant/refine.sql). +PENDANT={9600:'火之护符',9830:'冰之护符',10060:'土之护符',10290:'暗之护符',10520:'风之护符',10750:'雷之护符'} +PENDANT_STEPS=201 +# Talisman attribute items: 71302 / 53996 reroll, 71301 / 53997 add (53996 / 53997 are generic on AVA2). +PENDANT_SCROLLS={53996,53997,71301,71302} # Models of the armor shapes the 40250 race msm lack (registered by the client's mt_shapes script): shape -> # model stem ('%s' = race); RACE_PATHS = the ShapeData PathName of each race msm. ARMOR_SHAPES={32:'%s_7_1'} RACE_PATHS={'warrior':('pc','pc2'),'assassin':('pc2','pc'),'sura':('pc','pc2'),'shaman':('pc2','pc')} # (male, female) # Kept with their 40250 meaning; everything else becomes ITEM_NONE (see module doc). KEEP_TYPES={'ITEM_MATERIAL','ITEM_QUEST','ITEM_GIFTBOX','ITEM_NONE','ITEM_TREASURE_BOX','ITEM_TREASURE_KEY'} -KEEP_SUBTYPES={('ITEM_WEAPON',s) for s in ('WEAPON_SWORD','WEAPON_DAGGER','WEAPON_BOW','WEAPON_TWO_HANDED','WEAPON_BELL','WEAPON_FAN')}|{('ITEM_ARMOR','ARMOR_FOOTS'),('ITEM_ARMOR','ARMOR_BODY'),('ITEM_METIN','METIN_NORMAL')}|{('ITEM_COSTUME',s) for s in ('COSTUME_SASH','COSTUME_MOUNT','COSTUME_MOUNT_SKIN')} +KEEP_SUBTYPES={('ITEM_WEAPON',s) for s in ('WEAPON_SWORD','WEAPON_DAGGER','WEAPON_BOW','WEAPON_TWO_HANDED','WEAPON_BELL','WEAPON_FAN')}|{('ITEM_ARMOR','ARMOR_FOOTS'),('ITEM_ARMOR','ARMOR_BODY'),('ITEM_ARMOR','ARMOR_PENDANT'),('ITEM_METIN','METIN_NORMAL')}|{('ITEM_COSTUME',s) for s in ('COSTUME_SASH','COSTUME_MOUNT','COSTUME_MOUNT_SKIN')} DESC={'ITEM_METIN':'可镶嵌到武器的空孔中。','ITEM_MATERIAL':'制作与升级材料。','ITEM_QUEST':'任务物品。','ITEM_GIFTBOX':'打开后可获得物品。','ITEM_TREASURE_BOX':'需要对应的钥匙才能打开。','ITEM_TREASURE_KEY':'可以打开对应的宝箱。','ITEM_WEAPON':'','ITEM_ARMOR':'','ITEM_COSTUME':'披风。可在披风工匠处合成升级,或吸收武器、铠甲的部分属性。'} # Sash system (metin2-server docs/FEATURE-AVA2-UNLOCK.md P4): effects registered by the client's mt_sash script # (files they reference are staged too), the window images, and item_scale.txt (model scale per job/sex; a row @@ -32,7 +37,11 @@ SASH_LOCALE={'locale/en/ui/acce/acce_combine.tga':'locale/zh/ui/acce/acce_combin ENCHANT_KEEP=((54001,54078),(54124,54174)) DESC_SUB={('ITEM_USE','USE_CHANGE_ATTRIBUTE'):'拖到未穿戴的对应时装上,附加一条固定属性(每件时装最多 3 条,不能重复)。', ('ITEM_COSTUME','COSTUME_MOUNT'):'坐骑时装。装备后召唤该坐骑(需要已拥有马匹),装备期间属性生效。', - ('ITEM_COSTUME','COSTUME_MOUNT_SKIN'):'坐骑外观。需先装备坐骑时装,装备后改变坐骑外观。'} + ('ITEM_COSTUME','COSTUME_MOUNT_SKIN'):'坐骑外观。需先装备坐骑时装,装备后改变坐骑外观。', + ('ITEM_ARMOR','ARMOR_PENDANT'):'护符。装备在护符栏,对相应属性的怪物伤害提高(强化等级 +1 = +1%)。可用护符强化道具重洗属性,用护符加持道具追加属性(最多 5 条)。'} +# Per-vnum descriptions (override DESC_SUB). +DESC_VNUM={53996:'拖到未穿戴的护符上,重新随机全部属性。',71302:'拖到未穿戴的护符上,重新随机全部属性。', + 53997:'拖到未穿戴的护符上,追加一条属性(最多 5 条)。',71301:'拖到未穿戴的护符上,追加一条属性(最多 5 条)。'} # Mount system (P5): races of the mount skins that 203 and the 40250 client lack -> (zh name, npc_mount folder). # Their AVA2 mob_proto rows go to mobs-server.tsv, the whole model folder into the pack; the client's mt_mount # script registers the race names (npclist.txt is read from the root pack only). @@ -64,6 +73,8 @@ def names(): for v in range(lo,hi+1):result[v]=n for base,n in DRAGON.items(): for k in range(10):result[base+k]='%s+%d'%(n,k) + for base,n in PENDANT.items(): + for k in range(PENDANT_STEPS):result[base+k]='%s+%d'%(n,k) return result def convert(cols,E,imported): @@ -71,7 +82,8 @@ def convert(cols,E,imported): c=[x.strip() for x in cols[:33]] t,st=c[2],c[3] kept=t in KEEP_TYPES or (t,st) in KEEP_SUBTYPES or ((t,st)==('ITEM_USE','USE_CHANGE_ATTRIBUTE') and - any(lo<=int(c[0])<=hi for lo,hi in ENCHANT_KEEP)) + any(lo<=int(c[0])<=hi for lo,hi in ENCHANT_KEEP)) or ( + (t,st) in (('ITEM_USE','USE_CHANGE_ATTRIBUTE'),('ITEM_USE','USE_ADD_ATTRIBUTE')) and int(c[0]) in PENDANT_SCROLLS) anti=tokens(c[5]);flags=tokens(c[6]) if 'ITEM_STACKABLE' in anti:anti.remove('ITEM_STACKABLE');flags.append('ITEM_STACKABLE') anti=[a for a in dict.fromkeys(anti) if a in E['anti']];flags=[f for f in dict.fromkeys(flags) if f in E['flag']] @@ -196,7 +208,7 @@ def prepare(ava,proto_reader,locale,output,tool,packer,wrap=lambda text:text): if filename=='item_list.txt':result.append('\t'.join(listrows[v]).encode('gbk')) else: t=rows[v][2].strip() - desc=DESC_SUB.get((t,rows[v][3].strip()),DESC.get(t,'')) if v in report['kept'] else LOCKED + desc=DESC_VNUM.get(v) or DESC_SUB.get((t,rows[v][3].strip()),DESC.get(t,'')) if v in report['kept'] else LOCKED result.append(('%d\t%s\t%s'%(v,zh[v],wrap(desc) if desc else '')).encode('gbk')) (target/filename).write_bytes(b'\n'.join(result)+b'\n') subprocess.run([str(packer),str(output/'ava2items'),str(assets),''],check=True) diff --git a/tools/server/ava2-items/tests/test_items.py b/tools/server/ava2-items/tests/test_items.py index 682be6a8..cd99bfdb 100644 --- a/tools/server/ava2-items/tests/test_items.py +++ b/tools/server/ava2-items/tests/test_items.py @@ -22,7 +22,7 @@ class ConvertTests(unittest.TestCase): for vnum,mask in ((54001,'2'),(54078,'4'),(54130,'16')): # body / hair / mount skin (port_costume_attr.cpp) s,c,kept=m.convert(row(vnum,'ITEM_USE','USE_CHANGE_ATTRIBUTE',flags='ITEM_STACKABLE',values=('23','10',mask,'0','0','0')),self.E,set()) self.assertTrue(kept);self.assertEqual(s[2:4],['ITEM_USE','USE_CHANGE_ATTRIBUTE']);self.assertEqual(s[24:27],['23','10',mask]) - for vnum,mask in ((54100,'8'),(54200,'32'),(54250,'64'),(53996,'0')): # weapon costume / sash skin / effect / talisman + for vnum,mask in ((54100,'8'),(54200,'32'),(54250,'64')): # weapon costume / sash skin / effect s,c,kept=m.convert(row(vnum,'ITEM_USE','USE_CHANGE_ATTRIBUTE',flags='ITEM_STACKABLE',values=('23','10',mask,'0','0','0')),self.E,set()) self.assertFalse(kept);self.assertEqual(s[2:4],['ITEM_NONE','0']);self.assertEqual(s[24:30],['0']*6) self.assertEqual(c[1:3],[0,0]);self.assertEqual(c[5],1< "$BACKUP/refine_440_499.tsv" +echo "backup: $BACKUP" +$MYSQL player < "$HERE/refine.sql" +[ "$($MYSQL -N player -e 'SELECT COUNT(*) FROM refine_proto WHERE id BETWEEN 440 AND 499 AND cost=50000')" -eq 60 ] || { echo "refine rows missing" >&2; exit 1; } +echo "done; restart the db" diff --git a/tools/server/pendant/refine.sql b/tools/server/pendant/refine.sql new file mode 100644 index 00000000..3346b03c --- /dev/null +++ b/tools/server/pendant/refine.sql @@ -0,0 +1,63 @@ +-- AVA2 talisman refine sets 440-499 (AVA2 player.refine_proto): element fragment x1 + 30031 x1 + 33031 x10, 50000 yang, 100 %. +-- Step n of a talisman chain uses set base + n % 10 (fire 440, elect 450, ice 460, wind 470, earth 480, dark 490). +REPLACE INTO refine_proto (id,vnum0,vnum1,vnum2,vnum3,vnum4,count0,count1,count2,count3,count4,cost,src_vnum,result_vnum,prob) VALUES +(440,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(441,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(442,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(443,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(444,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(445,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(446,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(447,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(448,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(449,96001,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(450,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(451,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(452,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(453,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(454,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(455,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(456,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(457,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(458,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(459,96006,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(460,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(461,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(462,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(463,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(464,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(465,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(466,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(467,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(468,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(469,96002,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(470,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(471,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(472,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(473,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(474,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(475,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(476,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(477,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(478,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(479,96005,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(480,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(481,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(482,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(483,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(484,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(485,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(486,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(487,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(488,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(489,96003,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(490,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(491,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(492,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(493,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(494,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(495,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(496,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(497,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(498,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100), +(499,96004,30031,33031,0,0,1,1,10,0,0,50000,0,0,100);