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

## 106 statements 79 run 27 missing 3 excluded 5 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

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

16

17# -- code --

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

19

20

21 class UIMetaAccessor(object):

22 def __init__(self, cls):

23 self.for_cls = cls

24 self.mro = cls.mro()

25

26 def __getattr__(self, name):

27 for cls in self.mro:

28 if cls not in UI_META:

29 continue

30

31 try:

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

33 return val

34 except AttributeError:

35 pass

36

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

38

39

40 def ui_meta(for_cls: type):

41 def decorate(cls: type):

42 name = cls.__name__

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

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

45

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

47

48 # Type info is handled by plugin

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

50 UI_META[for_cls] = cls

51 return cls

52 return decorate

53

54

55# -----BEGIN COMMON FUNCTIONS-----

56 class UIMetaBase(GameViralContext):

57 game: THBattle

58

59 def __init__(self):

60 g = self.game

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

62 try:

63 self.me = g.find_character(me)

64 except IndexError:

65 self.me = me

66

67 def my_turn(self):

68 g = self.game

69 me = self.me

70 try:

71 act = g.action_stack[-1]

72 except IndexError:

73 return False

74

75 from thb import actions

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

77 return False

78

79 if act.target is not me: return False 79 ↛ exit line 79 didn't return from function 'my_turn', because the return on line 79 wasn't executed

80

81 if not act.in_user_input: return False 81 ↛ exit line 81 didn't return from function 'my_turn', because the return on line 81 wasn't executed

82

83 return True

84

85 def limit1_skill_used(self, tag):

86 me = self.me

87 t = me.tags

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

89

90 def clickable(self):

91 return False

92

93 def is_action_valid(self, c, tl):

94 return (False, 'BUG!')

95

96 def card_desc(self, c):

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

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

99

100 from thb.cards.base import Card, HiddenCard

101 if c.is_card(HiddenCard): return '一张牌' 101 ↛ exit line 101 didn't return from function 'card_desc', because the return on line 101 wasn't executed

102

103 if c.suit == Card.SPADE:

104 suit = '|r♠'

105 elif c.suit == Card.HEART:

106 suit = '|r|cb03a11ff♥'

107 elif c.suit == Card.CLUB:

108 suit = '|r♣'

109 elif c.suit == Card.DIAMOND: 109 ↛ 111 line 109 didn't jump to line 111, because the condition on line 109 was never false

110 suit = '|r|cb03a11ff♦'

111 elif c.suit == Card.NOTSET:

112 suit = '|r '

113 else:

114 suit = '|r错误'

115

116 num = ' A23456789_JQK'[c.number]

117 if num == '_': num = '10'

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

119

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

121 me = self.me

122 from thb.cards.base import CardList

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

124 c = cardcls()

125 c.move_to(cl)

126 return c

127

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

129 m = ch.ui_meta

130

131 cls: Type[Character]

132 obj: Optional[Character]

133

134 if isinstance(ch, Character):

135 cls, obj = ch.__class__, ch

136 else:

137 cls, obj = ch, None

138

139 rst = []

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

141 skills = list(cls.skills)

142 if hasattr(cls, 'boss_skills'):

143 skills.extend(cls.boss_skills)

144

145 if obj:

146 skills.extend([

147 c for c in obj.skills

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

149 ])

150

151 for s in skills:

152 sm = s.ui_meta

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

154

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

156 if notes:

157 rst.append(notes)

158

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

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

161 ('CV', m.cv),

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

163 ] if i[1]]

164

165 if tail:

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

167

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