ITEM_TYPE_RINGS / c_Equipment_Rings1/2, mt_rings adds the two slots beside the shoes, the accessory tooltip and the drill / jewel / switcher / adder drop targets; make_items imports the four +0..+9 ring chains. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
9.7 KiB
Python
116 lines
9.7 KiB
Python
import sys,unittest
|
|
from pathlib import Path
|
|
HERE=Path(__file__).resolve().parents[1];sys.path.insert(0,str(HERE))
|
|
import make_items as m
|
|
READER=HERE.parents[3]/'metin2-server/src/server/db/src/ProtoReader.cpp'
|
|
|
|
def row(vnum,t,st,anti='NONE',flags='NONE',wear='NONE',applies=('APPLY_NONE','0')*3,values=('0',)*6,refine=('0','0')):
|
|
return [str(vnum),'x',t,st,'1',anti,flags,wear,'NONE','10','20',refine[0],refine[1],'0','LEVEL','120','LIMIT_NONE','0',*applies,*values,'0','0','-1','']
|
|
|
|
@unittest.skipUnless(READER.is_file(),'needs ../metin2-server')
|
|
class ConvertTests(unittest.TestCase):
|
|
E=m.enums(READER) if READER.is_file() else None
|
|
def test_weapon_kept_without_unknown_flags(self):
|
|
s,c,kept=m.convert(row(320,'ITEM_WEAPON','WEAPON_SWORD','ANTI_MUDANG | ANTI_WOLFMAN','ITEM_TUNABLE','WEAR_WEAPON',
|
|
('APPLY_ATT_SPEED','18','APPLY_ATTBONUS_BOSS','5','APPLY_NONE','0'),refine=('321','81')),self.E,{320})
|
|
self.assertTrue(kept);self.assertEqual(s[5],'ANTI_MUDANG');self.assertEqual(s[7],'WEAR_WEAPON')
|
|
self.assertEqual(s[18:22],['APPLY_ATT_SPEED','18','APPLY_ATTBONUS_BOSS','5']) # ported apply kept
|
|
self.assertEqual(s[11:13],['0','0']) # the +1 item is not imported
|
|
self.assertEqual(c[1:3],[self.E['type'].index('ITEM_WEAPON'),0]);self.assertEqual(c[4],1<<self.E['anti'].index('ANTI_MUDANG'))
|
|
self.assertEqual(len(s),33);self.assertEqual(len(c),31)
|
|
def test_costume_enchant_kept_only_for_ported_targets(self):
|
|
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')): # 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<<self.E['flag'].index('ITEM_STACKABLE'))
|
|
def test_unknown_type_and_stackable_anti(self):
|
|
s,_,kept=m.convert(row(55010,'ITEM_PET','PET_SKILL','ITEM_STACKABLE','LOG','WEAR_PENDANT'),self.E,set())
|
|
self.assertFalse(kept);self.assertEqual(s[5:8],['NONE','LOG | ITEM_STACKABLE','NONE'])
|
|
def test_quest_subtype(self):
|
|
s,c,kept=m.convert(row(30400,'ITEM_QUEST','QUEST_NONE',values=('APPLY_NONE','0','0','0','0','0')),self.E,set())
|
|
self.assertTrue(kept);self.assertEqual(s[3],'0');self.assertEqual(s[24],'0')
|
|
def test_treasure_box_and_key_kept(self):
|
|
for vnum,t in ((77918,'ITEM_TREASURE_BOX'),(77921,'ITEM_TREASURE_KEY')):
|
|
s,c,kept=m.convert(row(vnum,t,'0',values=('3','0','0','0','0','0')),self.E,set())
|
|
self.assertTrue(kept);self.assertEqual(s[2:4],[t,'0']);self.assertEqual(s[24],'3') # key value0 == box value0 opens it
|
|
self.assertEqual(c[1],self.E['type'].index(t))
|
|
def test_stone_and_boss_stones_kept(self):
|
|
s,c,kept=m.convert(row(28444,'ITEM_METIN','METIN_NORMAL',flags='ITEM_STACKABLE',wear='WEAR_WEAPON',
|
|
applies=('APPLY_ATTBONUS_STONE','8','APPLY_NONE','0','APPLY_NONE','0'),values=('0','0','0','0','0','31')),self.E,set())
|
|
self.assertTrue(kept);self.assertEqual(s[7],'WEAR_WEAPON');self.assertEqual(s[18:20],['APPLY_ATTBONUS_STONE','8'])
|
|
self.assertEqual(c[17:19],[139,8]) # AVA2 numbering, shared with the server and client enums
|
|
self.assertEqual(self.E['apply'].index('APPLY_ATTBONUS_BOSS'),140);self.assertEqual(s[29],'31')
|
|
def test_dragon_armor_kept_with_refine_chain(self):
|
|
s,c,kept=m.convert(row(21210,'ITEM_ARMOR','ARMOR_BODY','ANTI_ASSASSIN | ANTI_SURA | ANTI_MUDANG | ANTI_WOLFMAN','ITEM_TUNABLE','WEAR_BODY',
|
|
('APPLY_MOV_SPEED','5','APPLY_RESIST_MAGIC','25','APPLY_MAX_HP_PCT','20'),('0','210','0','32','0','0'),('21211','81')),self.E,{21210,21211})
|
|
self.assertTrue(kept);self.assertEqual(s[3],'ARMOR_BODY');self.assertEqual(s[7],'WEAR_BODY');self.assertEqual(s[11:13],['21211','81'])
|
|
self.assertEqual(s[5],'ANTI_ASSASSIN | ANTI_SURA | ANTI_MUDANG');self.assertEqual(s[27],'32') # value3 = armor shape
|
|
models=m.shape_models();self.assertEqual(len(models),40)
|
|
self.assertIn('d:/ymir work/pc2/warrior/warrior_7_1_lod_03.gr2',models)
|
|
def test_sash_kept(self):
|
|
s,c,kept=m.convert(row(85004,'ITEM_COSTUME','COSTUME_SASH',wear='WEAR_COSTUME_BODY',
|
|
values=('4','0','0','0','0','0')),self.E,{85004})
|
|
self.assertTrue(kept);self.assertEqual(s[2:4],['ITEM_COSTUME','COSTUME_SASH']);self.assertEqual(s[24],'4') # value0 = grade
|
|
self.assertEqual(c[2],3) # COSTUME_SASH, after BODY/HAIR/MOUNT
|
|
def test_mount_and_skin_kept(self):
|
|
s,c,kept=m.convert(row(71502,'ITEM_COSTUME','COSTUME_MOUNT','ANTI_STACK',applies=('APPLY_ATTBONUS_MONSTER','30','APPLY_NONE','0','APPLY_NONE','0'),
|
|
values=('20113','0','0','0','0','0')),self.E,{71502})
|
|
self.assertTrue(kept);self.assertEqual(s[3],'COSTUME_MOUNT');self.assertEqual(s[18:20],['APPLY_ATTBONUS_MONSTER','30'])
|
|
self.assertEqual(s[24],'20113');self.assertEqual(c[2],2) # value0 = horse race
|
|
s,c,kept=m.convert(row(71234,'ITEM_COSTUME','COSTUME_MOUNT_SKIN',values=('32012','0','0','0','0','0')),self.E,{71234})
|
|
self.assertTrue(kept);self.assertEqual(s[3],'COSTUME_MOUNT_SKIN');self.assertEqual(c[2],4)
|
|
self.assertEqual(set(m.MOUNT_MOBS),{32012,32028})
|
|
def test_pendant_and_scrolls_kept(self):
|
|
s,c,kept=m.convert(row(9601,'ITEM_ARMOR','ARMOR_PENDANT',flags='ITEM_TUNABLE',wear='WEAR_PENDANT',
|
|
applies=('APPLY_ENCHANT_FIRE','1','APPLY_NONE','0','APPLY_NONE','0'),refine=('9602','441')),self.E,{9601,9602})
|
|
self.assertTrue(kept);self.assertEqual(s[3],'ARMOR_PENDANT');self.assertEqual(s[7],'NONE') # no WEAR_PENDANT flag: FindEquipCell
|
|
self.assertEqual(s[11:13],['9602','441']);self.assertEqual(s[18:20],['APPLY_ENCHANT_FIRE','1']);self.assertEqual(c[17],124)
|
|
self.assertEqual(c[2],self.E['sub'][self.E['type'].index('ITEM_ARMOR')].index('ARMOR_PENDANT'))
|
|
for vnum,st in ((53996,'USE_CHANGE_ATTRIBUTE'),(71302,'USE_CHANGE_ATTRIBUTE'),(53997,'USE_ADD_ATTRIBUTE'),(71301,'USE_ADD_ATTRIBUTE')):
|
|
s,c,kept=m.convert(row(vnum,'ITEM_USE',st,flags='ITEM_STACKABLE'),self.E,set())
|
|
self.assertTrue(kept);self.assertEqual(s[2:4],['ITEM_USE',st])
|
|
s,c,kept=m.convert(row(71303,'ITEM_USE','USE_ADD_ATTRIBUTE'),self.E,set());self.assertFalse(kept)
|
|
n=m.names();self.assertEqual([n[v] for v in (9600,9800,10950)],['火之护符+0','火之护符+200','雷之护符+200'])
|
|
def test_new_rings_kept_with_refine_chain(self):
|
|
s,c,kept=m.convert(row(19500,'ITEM_NEW_RINGS','0',
|
|
applies=('APPLY_INT','1','APPLY_RESIST_WARRIOR','1','APPLY_NONE','0'),refine=('19501','46')),self.E,{19500,19501})
|
|
self.assertTrue(kept);self.assertEqual(s[2:4],['ITEM_NEW_RINGS','0']);self.assertEqual(c[1],36)
|
|
self.assertEqual(s[11:13],['19501','46']);self.assertEqual(s[18:22],['APPLY_INT','1','APPLY_RESIST_WARRIOR','1'])
|
|
n=m.names();self.assertEqual([n[v] for v in (19500,19509,19569)],['红宝石戒指+0','红宝石戒指+9','蓝宝石戒指+9'])
|
|
def test_install_items_with_mobs(self):
|
|
import subprocess,tempfile
|
|
with tempfile.TemporaryDirectory() as d:
|
|
d=Path(d);conf=d/'share/conf';conf.mkdir(parents=True)
|
|
(conf/'item_proto.txt').write_bytes(b'VNUM\tNAME\r\n71502\told\r\n');(conf/'item_names.txt').write_bytes(b'VNUM\tNAME\r\n')
|
|
(conf/'mob_proto.txt').write_bytes(b'VNUM\tNAME\n20110\tboar\n');(conf/'mob_names.txt').write_bytes(b'20110\tboar\n')
|
|
(d/'i.tsv').write_bytes(b'71502\tnew\n');(d/'m.tsv').write_bytes(b'32012\tbari\tKING\n')
|
|
subprocess.run([sys.executable,str(HERE/'install_items.py'),str(d),str(d/'i.tsv'),str(d/'bk'),str(d/'m.tsv')],check=True,capture_output=True)
|
|
self.assertEqual((conf/'item_proto.txt').read_bytes(),b'VNUM\tNAME\r\n71502\tnew\r\n')
|
|
self.assertEqual((conf/'mob_proto.txt').read_bytes(),b'VNUM\tNAME\n20110\tboar\n32012\tbari\tKING\n')
|
|
self.assertEqual((conf/'mob_names.txt').read_bytes(),b'20110\tboar\n32012\tbari\n')
|
|
self.assertTrue((d/'bk/mob_proto.txt').is_file())
|
|
def test_sash_effect_closure(self):
|
|
import tempfile
|
|
with tempfile.TemporaryDirectory() as d:
|
|
d=Path(d);files={}
|
|
def put(name,data=b'x'):
|
|
f=d/str(len(files));f.write_bytes(data);files[name]=f
|
|
put(m.SASH_EFFECTS[0],b'MeshFileName "buff_item1.mde"\nTextureFileName "d:/ymir work/effect/a.dds"')
|
|
for q in m.SASH_EFFECTS[1:]:put(q)
|
|
put('d:/ymir work/effect/etc/buff/buff_item1.mde',b'"light010.dds"');put('d:/ymir work/effect/a.dds')
|
|
put('d:/ymir work/effect/etc/buff/light010.dds')
|
|
got=set(m.effect_files(files))
|
|
self.assertEqual(got,set(files))
|
|
del files['d:/ymir work/effect/etc/buff/light010.dds']
|
|
with self.assertRaises(SystemExit):m.effect_files(files)
|
|
|
|
def test_names_cover_ranges(self):
|
|
n=m.names();self.assertEqual(len(n),514+6*200+4*9);self.assertEqual(n[54100],'武器外观强化石')
|
|
self.assertEqual([n[v] for v in (21210,21219,21279)],['暗龙铠甲+0','暗龙铠甲+9','神龙铠甲+9'])
|
|
self.assertTrue(all(len(v.encode('gbk'))<=24 for v in n.values()))
|
|
|
|
if __name__=='__main__':unittest.main()
|