class Ray(Control): scale = InterpDesc('_scale') alpha = InterpDesc('_alpha') def __init__(self, x0, y0, x1, y1, *args, **kwargs): Control.__init__(self, *args, **kwargs) from math import sqrt, atan2, pi self.x, self.y = x0, y0 dx, dy = x1 - x0, y1 - y0 scale = sqrt(dx*dx+dy*dy) / L('c-ray').width self.angle = atan2(dy, dx) / pi * 180 self.scale = SineInterp(0.0, scale, 0.4) self.alpha = ChainInterp( FixedInterp(1.0, 1), CosineInterp(1.0, 0.0, 0.5), on_done=lambda self, desc: self.delete() ) def draw(self): ray = L('c-ray') glPushMatrix() glRotatef(self.angle, 0., 0., 1.) glScalef(self.scale, 1., 1.) glTranslatef(0., -ray.height/2, 0.) glColor4f(1., 1., 1., self.alpha) ray.blit(0, 0) glPopMatrix() def hit_test(self, x, y): return False
class SmallCardSprite(Control): width, height = 33, 46 x = InterpDesc('_x') y = InterpDesc('_y') def __init__(self, card, x=0.0, y=0.0, *args, **kwargs): Control.__init__(self, *args, **kwargs) self._w, self._h = 33, 46 self.x, self.y = x, y self.selected = False self.hover = False self.card = card self.img = L(card.ui_meta.image_small) self.balloon = balloon = BalloonPrompt(self) balloon.set_balloon(card.ui_meta.description) @staticmethod def batch_draw(csl): glPushMatrix() glLoadIdentity() vertices = [] for cs in csl: ax, ay = cs.abs_coords() vertices += cs.img.get_t4f_v4f_vertices(ax, ay) s = cs.card.suit n = cs.card.number ssuit = L('thb-smallsuit') snum = L('thb-smallnum') if n == 10: # special case # g[0].blit(1+g[0].vertices[0], 33+g[0].vertices[1]) # g[1].blit(5+g[1].vertices[0], 33+g[1].vertices[1]) vertices += snum[s % 2 * 14 + 10].get_t4f_v4f_vertices(ax - 1, ay + 31) vertices += snum[s % 2 * 14 + 0].get_t4f_v4f_vertices(ax + 3, ay + 31) else: vertices += snum[s % 2 * 14 + n].get_t4f_v4f_vertices(ax + 1, ay + 31) vertices += ssuit[s - 1].get_t4f_v4f_vertices(ax + 1, ay + 22) if cs.selected: vertices += L('thb-card-small-selected').get_t4f_v4f_vertices(ax, ay) else: vertices += L('thb-card-small-frame').get_t4f_v4f_vertices(ax, ay) n = len(vertices) buf = (GLfloat*n)() buf[:] = vertices glColor3f(1., 1., 1.) glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) glInterleavedArrays(GL_T4F_V4F, 0, buf) with get_atlas('card').texture: glDrawArrays(GL_QUADS, 0, n/8) glPopClientAttrib() glPopMatrix()
class GirlSelector(ImageSelector): x = InterpDesc('_x') y = InterpDesc('_y') def __init__(self, choice, group, x=0, y=0, *a, **k): self.choice = choice cc = choice.char_cls meta = cc.ui_meta pimg = L(meta.port_image) self.x = x self.y = y ImageSelector.__init__(self, pimg, group, *a, **k) self.balloon.set_balloon(char_desc(cc))
class GirlSelector(ImageSelector, BalloonPromptMixin): x = InterpDesc('_x') y = InterpDesc('_y') def __init__(self, choice, group, x=0, y=0, *a, **k): self.choice = choice cc = choice.char_cls meta = cc.ui_meta pimg = meta.port_image self.char_name = meta.char_name self.char_maxlife = cc.maxlife self.x = x self.y = y ImageSelector.__init__(self, pimg, group, *a, **k) self.init_balloon(meta.description)
class HighlightLayer(SensorLayer, BalloonPromptMixin): zindex = 0 hl_alpha = InterpDesc('_hl_alpha') def __init__(self, *a, **k): SensorLayer.__init__(self, *a, **k) BalloonPromptMixin.__init__(self) from base.baseclasses import main_window self.window = main_window self.hand_cursor = self.window.get_system_mouse_cursor('hand') self.worldmap_shadow = common_res.worldmap_shadow.get() self.disable_click = False self.highlight = None self.hldraw = None self.hl_alpha = 0 self.hlhit = False def on_mouse_motion(self, x, y, dx, dy): for s in ServerList.values(): if inpoly(x, y, s['polygon']): self.hl_alpha = 1 if self.highlight is not s: self.highlight = s self.init_balloon(s['description'], polygon=s['polygon']) x, y, w, h = s['box'] tex = self.worldmap_shadow.get_region(x, y, w, h) self.hldraw = (x, y, tex) self.window.set_mouse_cursor(self.hand_cursor) break else: if self.highlight: self.highlight = None self.hl_alpha = LinearInterp(1.0, 0, 0.3) self.window.set_mouse_cursor(None) self.init_balloon('', (0, 0, 0, 0)) def on_mouse_release(self, x, y, button, modifiers): if self.highlight and not self.disable_click: self.disable_click = True screen.do_connect(self.highlight['address']) def enable_click(self): self.disable_click = False def draw(self): hla = self.hl_alpha if hla and not self.disable_click: x, y, tex = self.hldraw glColor4f(1, 1, 1, hla) tex.blit(x, y)
class CardSprite(Control): x = InterpDesc('_x') y = InterpDesc('_y') back_scale = InterpDesc('_bs') question_scale = InterpDesc('_qs') ftanim_alpha = InterpDesc('_fta') ftanim_cardalpha = InterpDesc('_ftca') shine_alpha = InterpDesc('_shine_alpha') alpha = InterpDesc('_alpha') width, height = 91, 125 def __init__(self, card, x=0.0, y=0.0, *args, **kwargs): Control.__init__(self, *args, **kwargs) self._w, self._h = 91, 125 self.shine = False self.gray = False self.x, self.y = x, y self.shine_alpha = 0.0 self.selected = False self.alpha = 1.0 self.card = card self.ft_anim = False self.balloon = BalloonPrompt(self) self.update() @staticmethod def batch_draw(csl): glPushMatrix() glLoadIdentity() vertices = [] for cs in csl: ax, ay = cs.abs_coords() if cs.ft_anim: qs = cs.question_scale bs = cs.back_scale aa = cs.ftanim_alpha ca = cs.ftanim_cardalpha if cs.gray: c = (.66, .66, .66, ca) else: c = (1., 1., 1., ca) vertices += cs.img.get_t2c4n3v3_vertices(c, ax, ay) n, s = cs.number, cs.suit if n: vertices += L('thb-cardnum')[s % 2 * 13 + n - 1].get_t2c4n3v3_vertices(c, ax + 5, ay + 105) if s: vertices += L('thb-suit')[s - 1].get_t2c4n3v3_vertices(c, ax + 6, ay + 94) c = (1, 1, 1, aa) if qs: vertices += L('thb-card-question').get_t2c4n3v3_vertices(c, ax+(1-qs)*45, ay, 0, qs*91) if bs: vertices += L('thb-card-hidden').get_t2c4n3v3_vertices(c, ax+(1-bs)*45, ay, 0, bs*91) else: a = cs.alpha if cs.gray: c = (.66, .66, .66, a) else: c = (1., 1., 1., a) vertices += cs.img.get_t2c4n3v3_vertices(c, ax, ay) resides_in = cs.card.resides_in if resides_in and resides_in.type == 'showncards': vertices += L('thb-card-showncardtag').get_t2c4n3v3_vertices(c, ax, ay) n, s = cs.number, cs.suit if n: vertices += L('thb-cardnum')[s % 2 * 13 + n - 1].get_t2c4n3v3_vertices(c, ax + 5, ay + 105) if s: vertices += L('thb-suit')[s-1].get_t2c4n3v3_vertices(c, ax+6, ay+94) if cs.selected: c = (0., 0., 2., 1.0) else: c = (1., 1., 1., cs.shine_alpha) vertices += L('thb-card-shinesoft').get_t2c4n3v3_vertices(c, ax-6, ay-6) if vertices: n = len(vertices) buf = (GLfloat*n)() buf[:] = vertices glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) glInterleavedArrays(GL_T2F_C4F_N3F_V3F, 0, buf) with get_atlas('card').texture: glDrawArrays(GL_QUADS, 0, n/12) glPopClientAttrib() glPopMatrix() def update(self): card = self.card meta = card.ui_meta self.img = L(meta.image) self.number, self.suit = card.number, card.suit t = getattr(meta, 'description', None) t and self.balloon.set_balloon(t) def on_mouse_enter(self, x, y): self.shine_alpha = 1.0 def on_mouse_leave(self, x, y): self.shine_alpha = SineInterp(1.0, 0.0, 0.3) def do_fatetell_anim(self): self.ft_anim = True self.question_scale = ChainInterp( SineInterp(0.0, 1.0, 0.1), CosineInterp(1.0, 0.0, 0.1), FixedInterp(0.0, 0.2), SineInterp(0.0, 1.0, 0.1), CosineInterp(1.0, 0.0, 0.1), FixedInterp(0.0, 0.2), SineInterp(0.0, 1.0, 0.1) ) self.back_scale = ChainInterp( FixedInterp(0.0, 0.2), SineInterp(0.0, 1.0, 0.1), CosineInterp(1.0, 0.0, 0.1), FixedInterp(0.0, 0.2), SineInterp(0.0, 1.0, 0.1), CosineInterp(1.0, 0.0, 0.1), ) self.ftanim_alpha = ChainInterp( FixedInterp(1.0, 1.0), LinearInterp(1.0, 0.0, 2.0), on_done=self._end_ft_anim, ) self.ftanim_cardalpha = ChainInterp( FixedInterp(0.0, 1.0), FixedInterp(1.0, 0.0), ) def _end_ft_anim(self, _self, desc): self.ft_anim = False
class GameScreen(Screen): flash_alpha = InterpDesc('_flash_alpha') class InvitePanel(Panel): def __init__(self, game_id, *a, **k): Panel.__init__( self, width=550, height=340, zindex=10000, *a, **k ) self.game_id = game_id self.x = (self.overlay.width - 550) // 2 self.y = (self.overlay.height - 340) // 2 self.btncancel = btncancel = Button( u'关闭', parent=self, x=440, y=25, width=90, height=40 ) self.labels = pyglet.graphics.Batch() Label( u'邀请游戏', font_size=12, x=275, y=306, anchor_x='center', anchor_y='bottom', color=Colors.green.heavy + (255, ), shadow=(2, 207, 240, 156, 204), batch=self.labels, ) @btncancel.event def on_click(): self.delete() Executive.call('get_hallinfo', ui_message, None) def draw(self): Panel.draw(self) self.labels.draw() def on_message(self, _type, *args): if _type == 'current_users': ul = args[0] ul = [(uid, uname) for uid, uname, state in ul if state in ('hang', 'observing')] for i, (uid, uname) in enumerate(ul): y, x = divmod(i, 5) x, y = 30 + 100*x, 250 - 60*y s = Button( uname, color=Colors.orange, parent=self, x=x, y=y, width=95, height=30, ) @s.event def on_click(s=s, uid=uid, un=uname): Executive.call('invite_user', ui_message, uid) self.overlay.chat_box.append(u'|R已经邀请了%s,请等待回应……|r\n' % un) s.state = Button.DISABLED class RoomControlPanel(Control): def __init__(self, parent=None): Control.__init__(self, parent=parent, **r2d((0, 0, 820, 720))) self.btn_getready = Button( parent=self, caption=u'准备', **r2d((360, 80, 100, 35)) ) self.btn_invite = Button( parent=self, caption=u'邀请', **r2d((360, 40, 100, 35)) ) self.ready = False l = [] class MyPP(PlayerPortrait): # this class is INTENTIONALLY put here # to make cached avatars get gc'd cached_avatar = {} for x, y, color in parent.ui_class.portrait_location: l.append(MyPP('NONAME', parent=self, x=x, y=y, color=color)) self.portraits = l @self.btn_getready.event def on_click(): if self.ready: Executive.call('cancel_ready', ui_message, []) self.ready = False self.btn_getready.caption = u'准备' self.btn_getready.update() else: Executive.call('get_ready', ui_message, []) #self.btn_getready.state = Button.DISABLED self.ready = True self.btn_getready.caption = u'取消准备' self.btn_getready.update() @self.btn_invite.event # noqa def on_click(): GameScreen.InvitePanel(self.parent.game.gameid, parent=self) def draw(self): self.draw_subcontrols() def on_message(self, _type, *args): if _type == 'player_change': self.update_portrait(args[0]) elif _type == 'kick_request': u1, u2, count = args[0] self.parent.chat_box.append( u'|B|R>> |c0000ffff%s|r希望|c0000ffff|B%s|r离开游戏,已有%d人请求\n' % ( u1[1], u2[1], count ) ) elif _type == 'game_joined': self.ready = False self.btn_getready.caption = u'准备' self.btn_getready.state = Button.NORMAL def update_portrait(self, pl): def players(): return { p.account.username for p in self.portraits if p.account } orig_players = players() full = True for i, p in enumerate(pl): accdata = p['account'] acc = Account.parse(accdata) if accdata else None if not accdata: full = False port = self.portraits[i] port.account = acc port.ready = (p['state'] == 'ready') port.update() curr_players = players() for player in (orig_players - curr_players): self.parent.chat_box.append( u'|B|R>> |r玩家|c0000ffff|B%s|r已离开游戏\n' % player ) for player in (curr_players - orig_players): self.parent.chat_box.append( u'|B|R>> |r玩家|c0000ffff|B%s|r已进入游戏\n' % player ) if not self.ready and full and orig_players != curr_players: from utils import notify notify(u'东方符斗祭 - 满员提醒', u'房间已满员,请准备。') class EventsBox(Frame): def __init__(self, parent): Frame.__init__( self, parent=parent, caption=u'游戏信息', x=820, y=350, width=204, height=370, bot_reserve=0, bg=common_res.bg_eventsbox.get(), ) self.box = TextArea( parent=self, x=2, y=2, width=200, height=370-24-2 ) def append(self, v): self.box.append(v) def clear(self): self.box.text = u'\u200b' class ChatBox(ChatBoxFrame): def __init__(self, parent): ChatBoxFrame.__init__( self, parent=parent, x=820, y=0, width=204, height=352, bg=common_res.bg_chatbox.get(), ) def __init__(self, game, *args, **kwargs): Screen.__init__(self, *args, **kwargs) self.backdrop = common_res.bg_ingame.get() self.flash_alpha = 0.0 self.game = game self.ui_class = game.ui_meta.ui_class self.gameui = self.ui_class( parent=False, game=game, **r2d((0, 0, 820, 720)) ) # add when game starts self.events_box = GameScreen.EventsBox(parent=self) self.chat_box = GameScreen.ChatBox(parent=self) self.panel = GameScreen.RoomControlPanel(parent=self) self.btn_exit = Button( parent=self, caption=u'退出房间', zindex=1, **r2d((730, 670, 75, 25)) ) VolumeTuner(parent=self, x=690, y=665) @self.btn_exit.event def on_click(): box = ConfirmBox(u'真的要离开吗?', buttons=ConfirmBox.Presets.OKCancel, parent=self) @box.event def on_confirm(val): if val: Executive.call('exit_game', ui_message, []) def on_message(self, _type, *args): rst = handle_chat(_type, args) if rst: self.chat_box.append(rst) return elif _type == 'game_started': from utils import notify notify(u'东方符斗祭 - 游戏提醒', u'游戏已开始,请注意。') self.remove_control(self.panel) self.add_control(self.gameui) self.gameui.init() self.game.start() elif _type == 'end_game': self.remove_control(self.gameui) self.add_control(self.panel) g = args[0] elif _type == 'client_game_finished': g = args[0] g.ui_meta.ui_class.show_result(g) elif _type in ('game_left', 'fleed'): GameHallScreen().switch() elif _type == 'game_joined': # last game ended, this is the auto # created game self.game = args[0] self.panel.btn_getready.state = Button.NORMAL self.gameui = self.ui_class( parent=False, game=self.game, **r2d((0, 0, 820, 720)) ) SoundManager.switch_bgm(common_res.bgm_hall) self.backdrop = common_res.bg_ingame.get() self.set_color(Colors.green) self.events_box.clear() elif _type == 'game_crashed': ConfirmBox( u'游戏逻辑已经崩溃,请退出房间!\n' u'这是不正常的状态,你可以报告bug。\n' u'游戏ID:%d' % self.game.gameid, parent=self ) from __main__ import do_crashreport do_crashreport() elif _type == 'observe_request': uid, uname = args[0] box = ConfirmBox( u'玩家 %s 希望旁观你的游戏,是否允许?\n' u'旁观玩家可以看到你的手牌。' % uname, parent=self, buttons=((u'允许', True), (u'不允许', False)), default=False ) @box.event def on_confirm(val, uid=uid): Executive.call('observe_grant', ui_message, [uid, val]) elif _type == 'observer_enter': obuid, obname, uname = args[0] self.chat_box.append( u'|B|R>> |r|c0000ffff%s|r[|c9100ffff%d|r]|r趴在了|c0000ffff%s|r身后\n' % (obname, obuid, uname) ) elif _type == 'observer_leave': obuid, obname, uname = args[0] self.chat_box.append( u'|B|R>> |r|c0000ffff%s|r飘走了\n' % obname ) else: Screen.on_message(self, _type, *args) def draw(self): glColor3f(1, 1, 1) self.backdrop.blit(0, 0) glColor4f(0, 0, 0, .5) glRectf(0, 0, 1000, 138) glColor3f(0.1922, 0.2706, 0.3882) glRectf(0, 138, 1000, 140) glColor3f(1, 1, 1) self.draw_subcontrols() fa = self.flash_alpha if fa: glColor4f(1, 1, 1, fa) glRectf(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT) def set_flash(self, duration): self.flash_alpha = CosineInterp(1.0, 0.0, duration) def set_color(self, color): self.events_box.set_color(color) self.chat_box.set_color(color) def on_switch(self): SoundManager.switch_bgm(common_res.bgm_hall)