Files
metin2-client/tests/wiki/test_display.py
T
shenleiandClaude Opus 5.5 aeb67f3c9c Hide near trees drawn as PC blockers when trees are switched off
A tree close to the camera is drawn by RenderPCBlocker, not RenderTree,
so background.SetVisiblePart(PART_TREE, 0) left it on screen. Skip tree
blockers while PART_TREE is hidden. Live screenshots on the 203 test
core confirm the tree, the GM mark and the weapon glow now disappear.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:24:53 +09:00

95 lines
5.0 KiB
Python

import os
import re
import sys
import tempfile
import types
import unittest
from pathlib import Path
ROOT=Path(__file__).resolve().parents[2]
def script(name):return (ROOT/'src/platform/ScriptLib'/name).read_text().split('R"PY(',1)[1].rsplit(')PY";',1)[0]
class Widget(object):
def __init__(self,*a,**k):self.shown=False;self.text='';self.pos=(0,0);self.size=(0,0)
def __getattr__(self,name):return lambda *a,**k:None
def Show(self):self.shown=True
def Hide(self):self.shown=False
def IsShow(self):return self.shown
def SetText(self,text):self.text=text
def SetPosition(self,x,y):self.pos=(x,y)
def GetGlobalPosition(self):return self.pos
def SetSize(self,w,h):self.size=(w,h)
def GetWidth(self):return self.size[0]
def GetHeight(self):return self.size[1]
class Action(Widget):
def __init__(self,parent,x,y,width,text,event):
Widget.__init__(self);self.pos=(x,y);self.event=event;self.text=text;self.shown=True
class DisplayTests(unittest.TestCase):
def setUp(self):
self.cwd=os.getcwd();self.tmp=tempfile.TemporaryDirectory();os.chdir(self.tmp.name)
self.state=dict(effect=None,name=True,damage=True,parts={});self.opened=[]
mods={n:types.ModuleType(n) for n in ('background','game','grp','systemSetting','ui','mt_biolog','mt_wiki')}
bg=mods['background'];bg.PART_TREE=3;bg.PART_CLOUD=2;bg.SetVisiblePart=lambda part,on:self.state['parts'].__setitem__(part,on)
ss=mods['systemSetting'];ss.SetEffectLevel=lambda v:self.state.__setitem__('effect',v)
ss.IsAlwaysShowName=lambda:self.state['name'];ss.SetAlwaysShowNameFlag=lambda v:self.state.__setitem__('name',bool(v))
ss.IsShowDamage=lambda:self.state['damage'];ss.SetShowDamageFlag=lambda v:self.state.__setitem__('damage',bool(v))
mods['game'].GameWindow=type('GameWindow',(),dict(Open=lambda s:self.opened.append(1),Close=lambda s:None))
grp=mods['grp'];grp.SetColor=grp.RenderBar=grp.RenderBox=lambda *a:None
mods['ui'].Window=Widget
mods['mt_biolog'].Action=Action;mods['mt_biolog'].Text=lambda p,x,y,t='',c=0:Widget();mods['mt_biolog'].L=lambda t:t if isinstance(t,str) else t.decode('utf-8')
mods['mt_wiki'].B=lambda t,limit=0:t
self.previous={n:sys.modules.get(n) for n in mods}
sys.modules.update(mods)
def load(self):
self.ns={'__name__':'mt_display'};exec(script('DisplayScript.inc'),self.ns);return self.ns
def tearDown(self):
os.chdir(self.cwd);self.tmp.cleanup()
for name,value in self.previous.items():
if value is None:sys.modules.pop(name,None)
else:sys.modules[name]=value
def test_defaults_and_config(self):
ns=self.load();self.assertEqual(self.state['effect'],0)
o=ns['Options']();self.assertEqual(o.Dump(),'0 1 1')
for bad in ('5 1 1','1 2 1','a 1 1','1 1','10 1 1','-1 1 1'):
o=ns['Options']();o.Load(bad);self.assertEqual(o.Dump(),'0 1 1')
Path('mt_display.cfg').write_text('3 0 1')
self.load();self.assertEqual(self.state['effect'],3) # applied at boot, before any map
def test_game_window_applies_and_saves(self):
Path('mt_display.cfg').write_text('2 0 1')
ns=self.load();game=sys.modules['game'];gw=game.GameWindow();gw.interface=types.SimpleNamespace(wndMiniMap=Widget())
game.GameWindow.Open(gw);self.assertEqual(self.opened,[1])
self.assertEqual(self.state['parts'],{3:0,2:1});self.assertEqual(ns['g_entry'].pos,(4,252))
w=ns['g_window'];w.Open()
self.assertIn('【',w.levelButtons[2].text);self.assertNotIn('【',w.levelButtons[0].text)
self.assertNotIn('【',w.treeButton.text);self.assertIn('【',w.cloudButton.text)
w.levelButtons[4].event();self.assertEqual(self.state['effect'],4)
w.ToggleTree();w.ToggleCloud();self.assertEqual(self.state['parts'],{3:1,2:0})
self.assertEqual(Path('mt_display.cfg').read_text(),'4 1 0')
w.ToggleName();w.ToggleDamage();self.assertEqual((self.state['name'],self.state['damage']),(False,False))
self.assertNotIn('【',w.nameButton.text)
game.GameWindow.Close(gw);self.assertIsNone(ns['g_window']);self.assertIsNone(ns['g_entry'])
def test_levels_match_native_rule(self):
ns=self.load();text=(ROOT/'src/port/UserInterface/InstanceBase.cpp').read_text(encoding='latin1')
body=text.split('void CInstanceBase::__UpdateEffectShown()',1)[1].split('\n}\n',1)[0]
self.assertEqual(len(ns['LEVELS']),5);self.assertIn('iLevel == 4',body)
self.assertIn('IsEnemy() || IsStone()',body);self.assertIn('IsPC()',body)
system=(ROOT/'src/port/UserInterface/PythonSystem.h').read_text(encoding='latin1')
self.assertIn('iLevel > 4',system)
render=(ROOT/'src/port/GameLib/MapOutdoorRender.cpp').read_text(encoding='latin1')
blocker=render.split('void CMapOutdoor::RenderPCBlocker()',1)[1].split('\n}\n',1)[0]
self.assertIn('TREE_OBJECT != pInstance->GetType()',blocker) # near trees are drawn as PC blockers
if __name__=='__main__':unittest.main()