# Coverage for thb/meta/common.py : 19%

## 115 statements 29 run 86 missing 4 excluded 1 partial

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 -*-

2 from __future__ import annotations

3

4# -- stdlib --

5 from typing import Any, Dict, Optional, TYPE_CHECKING, Type, Union, Sequence

6

7# -- third party --

8# -- own --

9 from game.base import GameViralContext

10

11# -- typing --

12 if TYPE_CHECKING:

13 from thb.mode import THBattle

14 from thb.characters.base import Character # noqa: F401

15 from thb.cards.base import Card # noqa: F401

16

17

18# -- code --

19 UI_META: Dict[type, Any] = {}

20

21

22 class UIMetaAccessor(object):

23 def __init__(self, cls):

24 self.for_cls = cls

25 self.mro = cls.mro()

26

27 def __getattr__(self, name):

28 for cls in self.mro:

29 if cls not in UI_META:

30 continue

31

32 try:

33 val = getattr(UI_META[cls](), name)

34 return val

35 except AttributeError:

36 pass

37

38 raise AttributeError(f'{self.for_cls.__name__}.{name}')

39

40

41 def ui_meta(for_cls: type):

42 def decorate(cls: type):

43 name = cls.__name__

44 if name in UI_META: 44 ↛ 45 line 44 didn't jump to line 45, because the condition on line 44 was never true

45 raise Exception(f'{name} ui_meta redefinition!')

46

47 cls = type(name, (cls, UIMetaBase), {})

48

49 # Type info is handled by plugin

50 for_cls.ui_meta = UIMetaAccessor(for_cls) # type: ignore

51 UI_META[for_cls] = cls

52 return cls

53 return decorate

54

55

56# -----BEGIN COMMON FUNCTIONS-----

57 class UIMetaBase(GameViralContext):

58 game: THBattle

59

60 def __init__(self):

61 g = self.game

62 me = g.runner.core.game.theone_of(g)

63 try:

64 self.me = g.find_character(me)

65 except IndexError:

66 self.me = me

67

68 def my_turn(self):

69 g = self.game

70 me = self.me

71 try:

72 act = g.action_stack[-1]

73 except IndexError:

74 return False

75

76 from thb import actions

77 if not isinstance(act, actions.ActionStage):

78 return False

79

80 if act.target is not me: return False

81

82 if not act.in_user_input: return False

83

84 return True

85

86 def limit1_skill_used(self, tag):

87 me = self.me

88 t = me.tags

89 return t[tag] >= t['turn_count']

90

91 def clickable(self):

92 return False

93

94 def is_action_valid(self, c, tl):

95 return (False, 'BUG!')

96

97 def card_desc(self, c):

98 if isinstance(c, (list, tuple)):

99 return '、'.join([self.card_desc(i) for i in c])

100

101 from thb.cards.base import Card, HiddenCard

102 if c.is_card(HiddenCard): return '一张牌'

103

104 if c.suit == Card.SPADE:

105 suit = '|r♠'

106 elif c.suit == Card.HEART:

107 suit = '|r|cb03a11ff♥'

108 elif c.suit == Card.CLUB:

109 suit = '|r♣'

110 elif c.suit == Card.DIAMOND:

111 suit = '|r|cb03a11ff♦'

112 elif c.suit == Card.NOTSET:

113 suit = '|r '

114 else:

115 suit = '|r错误'

116

117 num = ' A23456789_JQK'[c.number]

118 if num == '_': num = '10'

119 return suit + num + ' |G%s|r' % c.ui_meta.name

120

121 def build_handcard(self, cardcls, p=None):

122 me = self.me

123 from thb.cards.base import CardList

124 cl = CardList(p or me, 'cards')

125 c = cardcls()

126 c.move_to(cl)

127 return c

128

129 def accept_cards(self, cl: Sequence[Card]):

130 g = self.game

131 try:

132 act = g.hybrid_stack[-1]

133 if act.cond(cl): # type: ignore

134 return True

135

136 except (IndexError, AttributeError):

137 pass

138

139 return False

140

141 def char_desc(self, ch: Union[Character, Type[Character]]):

142 m = ch.ui_meta

143

144 cls: Type[Character]

145 obj: Optional[Character]

146

147 if isinstance(ch, Character):

148 cls, obj = ch.__class__, ch

149 else:

150 cls, obj = ch, None

151

152 rst = []

153 rst.append('|DB%s %s 体力：%s|r' % (m.title, m.name, cls.maxlife))

154 skills = list(cls.skills)

155 if hasattr(cls, 'boss_skills'):

156 skills.extend(cls.boss_skills)

157

158 if obj:

159 skills.extend([

160 c for c in obj.skills

161 if 'character' in c.skill_category and c not in skills

162 ])

163

164 for s in skills:

165 sm = s.ui_meta

166 rst.append('|G%s|r：%s' % (sm.name, sm.description))

167

168 notes = getattr(m, 'notes', '')

169 if notes:

170 rst.append(notes)

171

172 tail = ['%s：%s' % i for i in [

173 ('画师', m.illustrator),

174 ('CV', m.cv),

175 ('人物设计', m.designer),

176 ] if i[1]]

177

178 if tail:

179 rst.append('|DB（%s）|r' % '，'.join(tail))

180

181 return '\n\n'.join(rst)