Coverage for thb/meta/common.py : 27%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2from __future__ import annotations
4# -- stdlib --
5from typing import Any, Dict, Optional, TYPE_CHECKING, Type, Union
7# -- third party --
8# -- own --
9from game.base import GameViralContext
11# -- typing --
12if TYPE_CHECKING:
13 from thb.mode import THBattle
14 from thb.characters.base import Character # noqa: F401
17# -- code --
18UI_META: Dict[type, Any] = {}
21class UIMetaAccessor(object):
22 def __init__(self, cls):
23 self.for_cls = cls
24 self.mro = cls.mro()
26 def __getattr__(self, name):
27 for cls in self.mro: 27 ↛ 34line 27 didn't jump to line 34, because the loop on line 27 didn't complete
28 try:
29 val = getattr(UI_META[cls](), name)
30 return val
31 except AttributeError:
32 pass
34 raise AttributeError(f'{self.cls.__name__}.{name}')
37def ui_meta(for_cls: type):
38 def decorate(cls: type):
39 name = cls.__name__
40 if name in UI_META: 40 ↛ 41line 40 didn't jump to line 41, because the condition on line 40 was never true
41 raise Exception(f'{name} ui_meta redefinition!')
43 cls = type(name, (cls, UIMetaBase), {})
45 # Type info is handled by plugin
46 for_cls.ui_meta = UIMetaAccessor(for_cls) # type: ignore
47 UI_META[for_cls] = cls
48 return cls
49 return decorate
52# -----BEGIN COMMON FUNCTIONS-----
53class UIMetaBase(GameViralContext):
54 game: THBattle
56 def __init__(self):
57 g = self.game
58 me = g.runner.core.game.theone_of(g)
59 try:
60 self.me = g.find_character(me)
61 except IndexError:
62 self.me = me
64 def my_turn(self):
65 g = self.game
66 try:
67 act = g.action_stack[-1]
68 except IndexError:
69 return False
71 from thb import actions
72 if not isinstance(act, actions.ActionStage):
73 return False
75 if act.target is not g.me: return False
77 if not act.in_user_input: return False
79 return True
81 def limit1_skill_used(self, tag):
82 g = self.game
83 t = g.me.tags
84 return t[tag] >= t['turn_count']
86 def clickable(self):
87 return False
89 def is_action_valid(self, c, tl):
90 return (False, 'BUG!')
92 def card_desc(self, c):
93 if isinstance(c, (list, tuple)):
94 return '、'.join([self.card_desc(i) for i in c])
96 from thb.cards.base import Card, HiddenCard
97 if c.is_card(HiddenCard): return '一张牌'
99 if c.suit == Card.SPADE:
100 suit = '|r♠'
101 elif c.suit == Card.HEART:
102 suit = '|r|cb03a11ff♥'
103 elif c.suit == Card.CLUB:
104 suit = '|r♣'
105 elif c.suit == Card.DIAMOND:
106 suit = '|r|cb03a11ff♦'
107 elif c.suit == Card.NOTSET:
108 suit = '|r '
109 else:
110 suit = '|r错误'
112 num = ' A23456789_JQK'[c.number]
113 if num == '_': num = '10'
114 return suit + num + ' |G%s|r' % c.ui_meta.name
116 def build_handcard(self, cardcls, p=None):
117 g = self.game
118 from thb.cards.base import CardList
119 cl = CardList(p or g.me, 'cards')
120 c = cardcls()
121 c.move_to(cl)
122 return c
124 def char_desc(self, ch: Union[Character, Type[Character]]):
125 m = ch.ui_meta
127 cls: Type[Character]
128 obj: Optional[Character]
130 if isinstance(ch, Character):
131 cls, obj = ch.__class__, ch
132 else:
133 cls, obj = ch, None
135 rst = []
136 rst.append('|DB%s %s 体力:%s|r' % (m.title, m.name, cls.maxlife))
137 skills = list(cls.skills)
138 if hasattr(cls, 'boss_skills'):
139 skills.extend(cls.boss_skills)
141 if obj:
142 skills.extend([
143 c for c in obj.skills
144 if 'character' in c.skill_category and c not in skills
145 ])
147 for s in skills:
148 sm = s.ui_meta
149 rst.append('|G%s|r:%s' % (sm.name, sm.description))
151 notes = getattr(m, 'notes', '')
152 if notes:
153 rst.append(notes)
155 tail = ['%s:%s' % i for i in [
156 ('画师', m.illustrator),
157 ('CV', m.cv),
158 ('人物设计', m.designer),
159 ] if i[1]]
161 if tail:
162 rst.append('|DB(%s)|r' % ','.join(tail))
164 return '\n\n'.join(rst)