From 014b2ea9eb969da68295fb841579e3305ee5f184 Mon Sep 17 00:00:00 2001 From: wkta-tom Date: Fri, 7 Oct 2022 19:02:36 +0200 Subject: [PATCH] (demolib/polarbear)dirty refactor to destroy bugs in web Ctx --- .../looparts/demolib/dialogue.py | 238 ++++++++++-------- .../looparts/demolib/rpgmenu.py | 79 +++++- .../looparts/polarbear/image.py | 22 +- 3 files changed, 213 insertions(+), 126 deletions(-) diff --git a/src/katagames_engine/looparts/demolib/dialogue.py b/src/katagames_engine/looparts/demolib/dialogue.py index efb9d2b..575ef52 100644 --- a/src/katagames_engine/looparts/demolib/dialogue.py +++ b/src/katagames_engine/looparts/demolib/dialogue.py @@ -1,24 +1,23 @@ import json from . import rpgmenu -from ... import _hub as kengi +from ... import _hub from ... import event from ... import pal +from ...compo import gfx -# - aliases -frects = kengi.polarbear.frects -kengi.polarbear.draw_text -draw_text = kengi.polarbear.draw_text -pygame = kengi.pygame +frects = _hub.polarbear.frects +pygame = _hub.pygame EngineEvTypes = event.EngineEvTypes -EventReceiver = event.EventReceiver -class Offer(object): - # An Offer is a single line spoken by the NPC. - # "effect" is a callable with no parameters. - # "replies" is a list of replies. +class Offer: + """ + An Offer is a single line spoken by the NPC, "effect" is + a callable with no parameters. + "replies" is a list of replies. + """ def __init__(self, msg, effect=None, replies=()): self.msg = msg self.effect = effect @@ -29,8 +28,8 @@ def __str__(self): @classmethod def from_json(cls, jdict): - # We spoke about not needing a json loader yet. But, in terms of hardcoding a conversation, it was just as - # easy to write this as to hand-code a dialogue tree. + # We spoke about not needing a json loader yet. But, in terms of hardcoding + # a conversation, it was just as easy to write this as to hand-code a dialogue tree. msg = jdict.get("msg", "Hello there!") effect = None replies = list() @@ -43,8 +42,10 @@ def load_jsondata(cls, jsondata): return cls.from_json(json.loads(jsondata)) -class Reply(object): - # A Reply is a single line spoken by the PC, leading to a new offer +class Reply: + """ + A Reply is a single line spoken by the PC, leading to a new offer + """ def __init__(self, msg, destination=None): self.msg = msg self.destination = destination @@ -64,16 +65,114 @@ def from_json(cls, jdict): return cls(msg, destination) -class ConversationView(EventReceiver): - # The visualizer is a class used by the conversation when conversing. - # It has a "text" property and "render", "get_menu" methods. +class ConversationView(event.EventReceiver): + """ + The View is used by the conversation when conversing. + It has a "text" property and "render", "get_menu" methods. + """ TEXT_AREA = frects.Frect(-75, -100, 300, 100) MENU_AREA = frects.Frect(-75, 30, 300, 80) PORTRAIT_AREA = frects.Frect(-240, -110, 150, 225) + BGCOL = (11, 24, 11) # pal.c64['darkgrey'] # pal.punk['nightblue'] + MENU_BORDER_COL = pal.punk['gray'] + DEBUG = True - BGCOL = pal.punk['nightblue'] # '#221031' + def __init__(self, root_offer, chosen_font, ft_size, portrait_fn=None, pre_render=None, li_alt_font_obj=None): + # -------------------- + # constructor + # -------------------- + super().__init__() - DEBUG = True + # n.b: need to use EmbeddedCfont, or no? + if li_alt_font_obj: # defaults to capello ft + self.capfont = li_alt_font_obj + # self.capfont = [ + # gfx.JsonBasedCfont(capfont_path_prfix + '-b'), # blue-ish + # gfx.JsonBasedCfont(capfont_path_prfix+'-a'), # orange + # ] + else: + self.capfont = None + + # - slight optim: + self.text_rect = None + self.menu_rect = None + self.portrait_rect = None + + # can be used to make things faster for webctx + self._primitive_style = False + self.zombie = False + self.text = '' + self.root_offer = root_offer + self.pre_render = pre_render + self.font = pygame.font.Font(chosen_font, ft_size) + if portrait_fn: + self.portrait = pygame.image.load(portrait_fn).convert_alpha() + else: + self.portrait = None + self.curr_offer = root_offer + self.dialog_upto_date = False + self.existing_menu = None + + def proc_event(self, ev, source): + if self.existing_menu: # forward event to menu if it exists + self.existing_menu.proc_event(ev, None) + + if ev.type == EngineEvTypes.LOGICUPDATE: # TODO: using L.u. isnt great! + self.update_dialog() + + elif ev.type == EngineEvTypes.PAINT: + if self.primitive_style: + self._primitiv_render(ev.screen) + else: + self._reg_render(ev.screen) + + elif ev.type == EngineEvTypes.CONVCHOICE: # ~ iterate over the conversation... + print('ConvChoice event received by', self.__class__.__name__) + self.dialog_upto_date = False + self.curr_offer = ev.value + + def _primitiv_render(self, refscreen): + pygame.draw.rect(refscreen, self.BGCOL, self.glob_rect) # fond de fenetre + pygame.draw.rect(refscreen, self.BGCOL, self.text_rect) + + #if not self.capfont: + # old fashion + + _hub.polarbear.image.draw_text( + self.capfont[0] if self.capfont else self.font, self.text, self.text_rect) + + #else: # we've overriden the basic behavior + # signatur is: + # text_to_surf(self, w, refsurf, start_pos, spacing=0, bgcolor=None) + # self.capfont[0].text_to_surf(self.text, refscreen, (self.text_rect[0], self.text_rect[1])) + + if self.portrait: + refscreen.blit(self.portrait, self.portrait_rect) + pygame.draw.rect(refscreen, self.BGCOL, self.menu_rect) + + if self.existing_menu: # draw what the player can SAY + if not self.capfont: + # old fashion + self.existing_menu.render(refscreen) + else: # we've overriden the basic behavior + self.existing_menu.alt_render(refscreen, self.capfont[0], self.capfont[1]) + # self.existing_menu.alt_render(refscreen, self.capfont[0], self.capfont[1]) + + # pourtour menu + pygame.draw.rect(refscreen, self.MENU_BORDER_COL, (self.taquet_portrait, 148, self.dim_menux, 88), 2) + + def _reg_render(self, refscreen): + if self.pre_render: + self.pre_render() + text_rect = self.TEXT_AREA.get_rect() + dborder = _hub.polarbear.default_border + dborder.render(text_rect) + _hub.polarbear.draw_text(self.font, self.text, text_rect) + dborder.render(self.MENU_AREA.get_rect()) + if self.existing_menu: + self.existing_menu.render(refscreen) + if self.portrait: + refscreen.blit(self.portrait, self.PORTRAIT_AREA.get_rect()) @property def primitive_style(self): @@ -84,7 +183,7 @@ def primitive_style(self, v): self._primitive_style = v # modify locations as we know that (Primitive style => upscaling is set to x3) - x = 48 #58 + x = 48 w = 192 self.refxxx = x @@ -101,53 +200,6 @@ def primitive_style(self, v): self.taquet_portrait = self.portrait_rect[0] self.glob_rect = pygame.Rect(self.portrait_rect[0], 53, self.dim_menux, 182) - - def _primitiv_render(self, refscreen): - pygame.draw.rect(refscreen, self.BGCOL, self.glob_rect) # fond de fenetre - - pygame.draw.rect(refscreen, self.BGCOL, self.text_rect) - draw_text(self.font, self.text, self.text_rect) - - if self.portrait: - refscreen.blit(self.portrait, self.portrait_rect) - - pygame.draw.rect(refscreen, self.BGCOL, self.menu_rect) - if self.existing_menu: - self.existing_menu.render(refscreen) - # pourtour menu - pygame.draw.rect(refscreen, pal.punk['darkpurple'], (self.taquet_portrait, 148, self.dim_menux, 88), 2) - - def __init__(self, root_offer, chosen_font, ft_size, portrait_fn=None, pre_render=None): - super().__init__() - # - slight optim: - self.text_rect = None - self.menu_rect = None - self.portrait_rect = None - - self._primitive_style = False # can be used to make things faster for webctx - self.zombie = False - - self.text = '' - self.root_offer = root_offer - self.pre_render = pre_render - self.font = pygame.font.Font(chosen_font, ft_size) - if portrait_fn: - self.portrait = pygame.image.load(portrait_fn).convert_alpha() - else: - self.portrait = None - - self.curr_offer = root_offer - self.dialog_upto_date = False - self.existing_menu = None - - # def turn_off(self): # because the conversation view can be closed from "outside" i.e. the main program - # if self.DEBUG: - # print('Conversation view closed') - # - # if self.existing_menu: - # self.existing_menu.turn_off() - # super().turn_off() - def update_dialog(self): if self.zombie: return @@ -155,19 +207,27 @@ def update_dialog(self): if self.curr_offer is None: # auto-close everything self.pev(EngineEvTypes.CONVENDS) - self.turn_off() + # let the full program remove the listener + # self.turn_off() self.zombie = True return if self.dialog_upto_date: return + self.dialog_upto_date = True self.text = self.curr_offer.msg # create a new Menu inst. + if self.capfont: + adhocft = self.capfont[0] + else: + adhocft = self.font self.existing_menu = rpgmenu.Menu( self.MENU_AREA.dx, self.MENU_AREA.dy, self.MENU_AREA.w, self.MENU_AREA.h, - border=None, predraw=None, font=self.font) # predraw: self.render + border=None, predraw=None, font=adhocft + ) + # predraw: self.render mymenu = self.existing_menu for i in self.curr_offer.replies: @@ -176,44 +236,6 @@ def update_dialog(self): mymenu.add_item("[Continue]", None) else: mymenu.sort() - nextfx = self.curr_offer.effect if nextfx: nextfx() - - def proc_event(self, ev, source): - if self.existing_menu: - self.existing_menu.proc_event(ev, None) # forward event to menu if it exists - - if ev.type == EngineEvTypes.PAINT: - self.render(ev.screen) - - elif ev.type == EngineEvTypes.LOGICUPDATE: - self.update_dialog() - - elif ev.type == EngineEvTypes.CONVCHOICE: # ~ iterate over the conversation... - print('ConvChoice event received by', self.__class__.__name__) - self.dialog_upto_date = False - self.curr_offer = ev.value - - def _reg_render(self, refscreen): - if self.pre_render: - self.pre_render() - text_rect = self.TEXT_AREA.get_rect() - dborder = kengi.polarbear.default_border - - dborder.render(text_rect) - draw_text(self.font, self.text, text_rect) - dborder.render(self.MENU_AREA.get_rect()) - - if self.existing_menu: - self.existing_menu.render(refscreen) - - if self.portrait: - refscreen.blit(self.portrait, self.PORTRAIT_AREA.get_rect()) - - def render(self, scr): - if self.primitive_style: - self._primitiv_render(scr) - else: - self._reg_render(scr) diff --git a/src/katagames_engine/looparts/demolib/rpgmenu.py b/src/katagames_engine/looparts/demolib/rpgmenu.py index 5be6790..6bff81b 100644 --- a/src/katagames_engine/looparts/demolib/rpgmenu.py +++ b/src/katagames_engine/looparts/demolib/rpgmenu.py @@ -28,7 +28,7 @@ class MenuItem(object): def __init__(self, msg, value, desc, menu): self.value = value self.desc = desc - self.font = menu.font + self._font = menu.font self.width = menu.w self.justify = -1 self.menuitem = menu.menuitem @@ -36,16 +36,30 @@ def __init__(self, msg, value, desc, menu): self.item_image = None self.select_image = None self.height = 0 - self.msg = msg + self._msg = msg + + @property + def font(self): + return self._font + + @font.setter + def font(self, v): + self._font = v + self._set_msg(self._msg) # force refresh def _get_msg(self): return self._msg def _set_msg(self, msg): self._msg = msg - self.item_image = render_text(self.font, self._msg, self.width, justify=self.justify, color=self.menuitem) - self.select_image = render_text(self.font, self._msg, self.width, justify=self.justify, color=self.menuselect) - self.height = self.select_image.get_height() + # - remove the pre-computation so the webctx accepts this as well + #self.item_image = render_text(self._font, self._msg, self.width, justify=self.justify, color=self.menuitem) + #self.select_image = render_text(self._font, self._msg, self.width, justify=self.justify, color=self.menuselect) + tmp = render_text(self._font, self._msg, self.width, justify=self.justify, color=self.menuselect) + self.height = tmp.get_size()[1] + + # before tinkenig web ctx: + # self.select_image.get_height() msg = property(_get_msg, _set_msg) @@ -62,10 +76,17 @@ def __str__(self): return self._msg def render(self, screen, dest, selected=False): - if selected: - screen.blit(self.select_image, dest) - else: - screen.blit(self.item_image, dest) + # signature draw_text(font, text, rect, color=TEXT_COLOR, justify=-1, antialias=True, dest_surface=None): + #if selected: + draw_text(self._font, self._msg, pygame.Rect(dest[0], dest[1], self.width, 256), justify=self.justify, dest_surface=screen) + #else: + # draw_text(self._font, self._msg, (dest[0], dest[1], self.width, 256), justify=self.justify, color=self.menuitem) + + # - before tinkering for the web ctx compat... + # if selected: + # screen.blit(self.select_image, dest) + # else: + # screen.blit(self.item_image, dest) # The DescBox is the default MenuDesc. It takes a string stored in the menu @@ -96,7 +117,12 @@ def __call__(self, menu_item): screen.blit(img, mydest) -class Menu(event.CogObj, Frect): # N.B (tom) it would be better to inherit from CogObj +have a Frect attribute +class Menu(event.CogObj, Frect): + """ + N.B (tom) it would be much better to inherit from CogObj + + have a Frect attribute + """ + # TODO refactor def __init__(self, dx, dy, w=300, h=100, anchor=ANCHOR_CENTER, menuitem=MENU_ITEM_COLOR, menuselect=MENU_SELECT_COLOR, border=default_border, predraw=None, font=None, padding=0, @@ -166,22 +192,49 @@ def arrange(self): item_num -= 1 y -= self.padding - def render(self, scr, do_extras=True): + def alt_render(self, scr, capft, select_capft, do_extras=True): mydest = self.get_rect() if do_extras: if self.predraw: self.predraw(scr) - if self.border: self.border.render(mydest) scr.set_clip(mydest) self.arrange() for item_num, area in list(self._item_rects.items()): - self.items[item_num].render(scr, area, (item_num == self.selected_item) and do_extras) + # THIS IS THE LINE THAT's rly different + # signatur is: + # text_to_surf(self, w, refsurf, start_pos, spacing=0, bgcolor=None) + # debug - print(type(self.items[item_num])) + + #TODO if selected then use the select_capft to render txt ! + if item_num == self.selected_item: + pfont = select_capft + else: + pfont = capft + self.items[item_num].font = pfont + self.items[item_num].render(scr, area, False) + # pfont.text_to_surf(self.items[item_num].msg, scr, (area[0], area[1])) scr.set_clip(None) + # tom: dunno what its for? + # if self.descobj: + # self.descobj(self.get_current_item()) + + def render(self, scr, do_extras=True): + mydest = self.get_rect() + if do_extras: + if self.predraw: + self.predraw(scr) + if self.border: + self.border.render(mydest) + scr.set_clip(mydest) + self.arrange() + for item_num, area in list(self._item_rects.items()): + self.items[item_num].render(scr, area, (item_num == self.selected_item) and do_extras) + scr.set_clip(None) if self.descobj: self.descobj(self.get_current_item()) diff --git a/src/katagames_engine/looparts/polarbear/image.py b/src/katagames_engine/looparts/polarbear/image.py index d272836..b424c80 100644 --- a/src/katagames_engine/looparts/polarbear/image.py +++ b/src/katagames_engine/looparts/polarbear/image.py @@ -52,7 +52,9 @@ def wrap_multi_line(text, font, maxwidth): return list(lines) -def render_text(font, text, width, color=TEXT_COLOR, justify=-1, antialias=True): +def render_text(font, text, width, color=TEXT_COLOR, justify=-1, antialias=True, dsuu=None, moff=None): + if moff: + moffx, moffy = moff # Return an image with prettyprinted text. lines = wrap_multi_line(text, font, width) @@ -68,10 +70,16 @@ def render_text(font, text, width, color=TEXT_COLOR, justify=-1, antialias=True) x = width - i.get_width() else: x = 0 - s.blit(i, (x, o)) + + if dsuu: + dsuu.blit(i, (moffx+x, moffy+o)) + else: + s.blit(i, (x, o)) o += i.get_height() - s.set_colorkey((0, 0, 0), pygame.RLEACCEL) - return s + + if moff is None: + s.set_colorkey((0, 0, 0), pygame.RLEACCEL) + return s def draw_text(font, text, rect, color=TEXT_COLOR, justify=-1, antialias=True, dest_surface=None): @@ -81,13 +89,17 @@ def draw_text(font, text, rect, color=TEXT_COLOR, justify=-1, antialias=True, de else: dsu = core.get_screen() - myimage = render_text(font, text, rect.width, color, justify, antialias) + # myimage = render. ... + render_text(font, text, rect.width, color, justify, antialias, dsuu=dsu, moff=rect.topleft) + return + if justify == 0: myrect = myimage.get_rect(midtop=rect.midtop) elif justify > 0: myrect = myimage.get_rect(topleft=rect.topleft) else: myrect = rect + dsu.set_clip(rect) dsu.blit(myimage, myrect) dsu.set_clip(None)