def _on_continue_press(self) -> None: from bastd.ui import getcurrency # Disallow for first second. if self._start_count - self._count < 2: ba.playsound(ba.getsound('error')) else: # If somehow we got signed out... if _ba.get_v1_account_state() != 'signed_in': ba.screenmessage(ba.Lstr(resource='notSignedInText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return # If it appears we don't have enough tickets, offer to buy more. tickets = _ba.get_v1_account_ticket_count() if tickets < self._cost: # FIXME: Should we start the timer back up again after? self._counting_down = False ba.textwidget(edit=self._counter_text, text='') ba.playsound(ba.getsound('error')) getcurrency.show_get_tickets_prompt() return if not self._transitioning_out: ba.playsound(ba.getsound('swish')) self._transitioning_out = True ba.containerwidget(edit=self._root_widget, transition='out_scale') self._continue_call()
def _update(self) -> None: try: t_str = str(_ba.get_v1_account_ticket_count()) except Exception: t_str = '?' if self._tickets_text is not None: ba.textwidget(edit=self._tickets_text, text=ba.Lstr( resource='getTicketsWindow.youHaveShortText', subs=[('${COUNT}', ba.charstr(ba.SpecialChar.TICKET) + t_str) ])) # Once we've kicked off an upgrade attempt and all transactions go # through, we're done. if (self._status == 'upgrading' and not _ba.have_outstanding_transactions()): self._status = 'exiting' ba.containerwidget(edit=self._root_widget, transition='out_right') edit_profile_window = self._edit_profile_window() if edit_profile_window is None: print('profile upgrade transition out:' ' original edit window gone') return ba.playsound(ba.getsound('gunCocking')) edit_profile_window.reload_window()
def _tick(self) -> None: # if our target activity is gone or has ended, go away activity = self._activity() if activity is None or activity.has_ended(): self._on_cancel() return if _ba.get_v1_account_state() == 'signed_in': sval = (ba.charstr(ba.SpecialChar.TICKET) + str(_ba.get_v1_account_ticket_count())) else: sval = '?' if self._tickets_text is not None: assert self._tickets_text_base is not None ba.textwidget(edit=self._tickets_text, text=self._tickets_text_base.replace( '${COUNT}', sval)) if self._counting_down: self._count -= 1 ba.playsound(ba.getsound('tick')) if self._count <= 0: self._on_cancel() else: ba.textwidget(edit=self._counter_text, text=str(self._count))
def on_boost_press(self) -> None: """Boost was pressed.""" from bastd.ui import account from bastd.ui import getcurrency if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() return if _ba.get_v1_account_ticket_count() < self._boost_tickets: ba.playsound(ba.getsound('error')) getcurrency.show_get_tickets_prompt() return ba.playsound(ba.getsound('laserReverse')) _ba.add_transaction( { 'type': 'PARTY_QUEUE_BOOST', 't': self._boost_tickets, 'q': self._queue_id }, callback=ba.WeakCall(self.on_update_response)) # lets not run these immediately (since they may be rapid-fire, # just bucket them until the next tick) # the transaction handles the local ticket change, but we apply our # local boost vis manually here.. # (our visualization isn't really wired up to be transaction-based) our_dude = self._dudes_by_id.get(-1) if our_dude is not None: our_dude.boost(self._boost_strength, self._smoothing)
def update(self) -> None: """Update!""" if not self._root_widget: return # Update boost-price. if self._boost_price is not None: ba.textwidget(edit=self._boost_price, text=ba.charstr(ba.SpecialChar.TICKET) + str(self._boost_tickets)) # Update boost button color based on if we have enough moola. if self._boost_button is not None: can_boost = ( (_ba.get_v1_account_state() == 'signed_in' and _ba.get_v1_account_ticket_count() >= self._boost_tickets)) ba.buttonwidget(edit=self._boost_button, color=(0, 1, 0) if can_boost else (0.7, 0.7, 0.7)) # Update ticket-count. if self._tickets_text is not None: if self._boost_button is not None: if _ba.get_v1_account_state() == 'signed_in': val = ba.charstr(ba.SpecialChar.TICKET) + str( _ba.get_v1_account_ticket_count()) else: val = ba.charstr(ba.SpecialChar.TICKET) + '???' ba.textwidget(edit=self._tickets_text, text=val) else: ba.textwidget(edit=self._tickets_text, text='') current_time = ba.time(ba.TimeType.REAL) if (self._last_transaction_time is None or current_time - self._last_transaction_time > 0.001 * _ba.get_v1_account_misc_read_val('pqInt', 5000)): self._last_transaction_time = current_time _ba.add_transaction( { 'type': 'PARTY_QUEUE_QUERY', 'q': self._queue_id }, callback=ba.WeakCall(self.on_update_response)) _ba.run_transactions() # step our dudes for dude in self._dudes: dude.step(self._smoothing)
def _start_stop_button_press(self) -> None: if (self._waiting_for_start_stop_response or self._waiting_for_initial_state): return if _ba.get_v1_account_state() != 'signed_in': ba.screenmessage(ba.Lstr(resource='notSignedInErrorText')) ba.playsound(ba.getsound('error')) self._refresh_sub_tab() return if self._hostingstate.unavailable_error is not None: ba.playsound(ba.getsound('error')) return ba.playsound(ba.getsound('click01')) # If we're not hosting, start. if self._hostingstate.party_code is None: # If there's a ticket cost, make sure we have enough tickets. if self._hostingstate.tickets_to_host_now > 0: ticket_count: Optional[int] try: ticket_count = _ba.get_v1_account_ticket_count() except Exception: # FIXME: should add a ba.NotSignedInError we can use here. ticket_count = None ticket_cost = self._hostingstate.tickets_to_host_now if ticket_count is not None and ticket_count < ticket_cost: getcurrency.show_get_tickets_prompt() ba.playsound(ba.getsound('error')) return self._last_action_send_time = time.time() _ba.add_transaction( { 'type': 'PRIVATE_PARTY_START', 'config': dataclass_to_dict(self._hostingconfig), 'region_pings': ba.app.net.zone_pings, 'expire_time': time.time() + 20, }, callback=ba.WeakCall(self._hosting_state_response)) _ba.run_transactions() else: self._last_action_send_time = time.time() _ba.add_transaction( { 'type': 'PRIVATE_PARTY_STOP', 'expire_time': time.time() + 20, }, callback=ba.WeakCall(self._hosting_state_response)) _ba.run_transactions() ba.playsound(ba.getsound('click01')) self._waiting_for_start_stop_response = True self._refresh_sub_tab()
def _refresh_tickets_text(self) -> None: if self._tickets_text is None: return try: tc_str = str(_ba.get_v1_account_ticket_count()) except Exception: ba.print_exception() tc_str = '-' ba.textwidget(edit=self._tickets_text, text=ba.Lstr(resource=self._r + '.ticketsText', subs=[('${COUNT}', tc_str)]))
def _update_tickets_text(self) -> None: from ba import SpecialChar if not self._root_widget: return sval: Union[str, ba.Lstr] if _ba.get_v1_account_state() == 'signed_in': sval = (ba.charstr(SpecialChar.TICKET) + str(_ba.get_v1_account_ticket_count())) else: sval = ba.Lstr(resource='getTicketsWindow.titleText') ba.buttonwidget(edit=self._get_tickets_button, label=sval)
def _update_currency_ui(self) -> None: # Keep currency count up to date if applicable. try: t_str = str(_ba.get_v1_account_ticket_count()) except Exception: t_str = '?' if self._get_tickets_button: ba.buttonwidget(edit=self._get_tickets_button, label=ba.charstr(ba.SpecialChar.TICKET) + t_str) if self._ticket_count_text: ba.textwidget(edit=self._ticket_count_text, text=ba.charstr(ba.SpecialChar.TICKET) + t_str)
def _on_pay_with_tickets_press(self) -> None: from bastd.ui import getcurrency # If we're already entering, ignore. if self._entering: return if not self._have_valid_data: ba.screenmessage(ba.Lstr(resource='tournamentCheckingStateText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return # If we don't have a price. if self._purchase_price is None: ba.screenmessage(ba.Lstr(resource='tournamentCheckingStateText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return # Deny if it looks like the tourney has ended. if self._seconds_remaining == 0: ba.screenmessage(ba.Lstr(resource='tournamentEndedText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return # Deny if we don't have enough tickets. ticket_count: Optional[int] try: ticket_count = _ba.get_v1_account_ticket_count() except Exception: # FIXME: should add a ba.NotSignedInError we can use here. ticket_count = None ticket_cost = self._purchase_price if ticket_count is not None and ticket_count < ticket_cost: getcurrency.show_get_tickets_prompt() ba.playsound(ba.getsound('error')) return cur_time = ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) self._last_ticket_press_time = cur_time assert isinstance(ticket_cost, int) _ba.in_game_purchase(self._purchase_name, ticket_cost) self._entering = True _ba.add_transaction({ 'type': 'ENTER_TOURNAMENT', 'fee': self._fee, 'tournamentID': self._tournament_id }) _ba.run_transactions() self._launch()
def buy(self, item: str) -> None: """Attempt to purchase the provided item.""" from ba.internal import (get_available_sale_time, get_store_item_name_translated) from bastd.ui import account from bastd.ui.confirm import ConfirmWindow from bastd.ui import getcurrency # Prevent pressing buy within a few seconds of the last press # (gives the buttons time to disable themselves and whatnot). curtime = ba.time(ba.TimeType.REAL) if self._last_buy_time is not None and (curtime - self._last_buy_time) < 2.0: ba.playsound(ba.getsound('error')) else: if _ba.get_v1_account_state() != 'signed_in': account.show_sign_in_prompt() else: self._last_buy_time = curtime # Pro is an actual IAP; the rest are ticket purchases. if item == 'pro': ba.playsound(ba.getsound('click01')) # Purchase either pro or pro_sale depending on whether # there is a sale going on. self._do_purchase_check('pro' if get_available_sale_time( 'extras') is None else 'pro_sale') else: price = _ba.get_v1_account_misc_read_val( 'price.' + item, None) our_tickets = _ba.get_v1_account_ticket_count() if price is not None and our_tickets < price: ba.playsound(ba.getsound('error')) getcurrency.show_get_tickets_prompt() else: def do_it() -> None: self._do_purchase_check(item, is_ticket_purchase=True) ba.playsound(ba.getsound('swish')) ConfirmWindow( ba.Lstr(resource='store.purchaseConfirmText', subs=[ ('${ITEM}', get_store_item_name_translated(item)) ]), width=400, height=120, action=do_it, ok_text=ba.Lstr(resource='store.purchaseText', fallback_resource='okText'))
def _purchase(self) -> None: from bastd.ui import getcurrency if self._items == ['pro']: _ba.purchase('pro') else: ticket_count: Optional[int] try: ticket_count = _ba.get_v1_account_ticket_count() except Exception: ticket_count = None if ticket_count is not None and ticket_count < self._price: getcurrency.show_get_tickets_prompt() ba.playsound(ba.getsound('error')) return def do_it() -> None: _ba.in_game_purchase(self._items[0], self._price) ba.playsound(ba.getsound('swish')) do_it()
def get_available_purchase_count(tab: str = None) -> int: """(internal)""" try: if _ba.get_v1_account_state() != 'signed_in': return 0 count = 0 our_tickets = _ba.get_v1_account_ticket_count() store_data = get_store_layout() if tab is not None: tabs = [(tab, store_data[tab])] else: tabs = list(store_data.items()) for tab_name, tabval in tabs: if tab_name == 'icons': continue # too many of these; don't show.. count = _calc_count_for_tab(tabval, our_tickets, count) return count except Exception: from ba import _error _error.print_exception('error calcing available purchases') return 0
def _purchase(self) -> None: from ba.internal import get_store_item_name_translated from bastd.ui import getcurrency from bastd.ui import confirm if self._offer['item'] == 'pro': _ba.purchase('pro_sale') elif self._offer['item'] == 'pro_fullprice': _ba.purchase('pro') elif self._is_bundle_sale: # With bundle sales, the price is the name of the IAP. _ba.purchase(self._offer['price']) else: ticket_count: Optional[int] try: ticket_count = _ba.get_v1_account_ticket_count() except Exception: ticket_count = None if (ticket_count is not None and ticket_count < self._offer['price']): getcurrency.show_get_tickets_prompt() ba.playsound(ba.getsound('error')) return def do_it() -> None: _ba.in_game_purchase('offer:' + str(self._offer['id']), self._offer['price']) ba.playsound(ba.getsound('swish')) confirm.ConfirmWindow(ba.Lstr( resource='store.purchaseConfirmText', subs=[('${ITEM}', get_store_item_name_translated(self._offer['item']))]), width=400, height=120, action=do_it, ok_text=ba.Lstr( resource='store.purchaseText', fallback_resource='okText'))
def _on_upgrade_press(self) -> None: from bastd.ui import getcurrency if self._status is None: # If it appears we don't have enough tickets, offer to buy more. tickets = _ba.get_v1_account_ticket_count() if tickets < self._cost: ba.playsound(ba.getsound('error')) getcurrency.show_get_tickets_prompt() return ba.screenmessage(ba.Lstr(resource='purchasingText'), color=(0, 1, 0)) self._status = 'pre_upgrading' # Now we tell the original editor to save the profile, add an # upgrade transaction, and then sit and wait for everything to # go through. edit_profile_window = self._edit_profile_window() if edit_profile_window is None: print('profile upgrade: original edit window gone') return success = edit_profile_window.save(transition_out=False) if not success: print('profile upgrade: error occurred saving profile') ba.screenmessage(ba.Lstr(resource='errorText'), color=(1, 0, 0)) ba.playsound(ba.getsound('error')) return _ba.add_transaction({ 'type': 'UPGRADE_PROFILE', 'name': self._name }) _ba.run_transactions() self._status = 'upgrading' self._upgrade_start_time = time.time() else: ba.playsound(ba.getsound('error'))
def _update(self) -> None: import datetime # if we somehow get signed out, just die.. if _ba.get_v1_account_state() != 'signed_in': self._back() return self._ticket_count = _ba.get_v1_account_ticket_count() # update our incentivized ad button depending on whether ads are # available if self._ad_button is not None: next_reward_ad_time = _ba.get_v1_account_misc_read_val_2( 'nextRewardAdTime', None) if next_reward_ad_time is not None: next_reward_ad_time = datetime.datetime.utcfromtimestamp( next_reward_ad_time) now = datetime.datetime.utcnow() if (_ba.have_incentivized_ad() and (next_reward_ad_time is None or next_reward_ad_time <= now)): self._ad_button_greyed = False ba.buttonwidget(edit=self._ad_button, color=(0.65, 0.5, 0.7)) ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 1.0)) ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 1)) ba.imagewidget(edit=self._ad_image, opacity=0.6) ba.textwidget(edit=self._ad_time_text, text='') else: self._ad_button_greyed = True ba.buttonwidget(edit=self._ad_button, color=(0.5, 0.5, 0.5)) ba.textwidget(edit=self._ad_label, color=(0.7, 0.9, 0.7, 0.2)) ba.textwidget(edit=self._ad_free_text, color=(1, 1, 0, 0.2)) ba.imagewidget(edit=self._ad_image, opacity=0.6 * 0.25) sval: Union[str, ba.Lstr] if (next_reward_ad_time is not None and next_reward_ad_time > now): sval = ba.timestring( (next_reward_ad_time - now).total_seconds() * 1000.0, centi=False, timeformat=ba.TimeFormat.MILLISECONDS) else: sval = '' ba.textwidget(edit=self._ad_time_text, text=sval) # if this is our first update, assign immediately; otherwise kick # off a smooth transition if the value has changed if self._smooth_ticket_count is None: self._smooth_ticket_count = float(self._ticket_count) self._smooth_update() # will set the text widget elif (self._ticket_count != int(self._smooth_ticket_count) and self._smooth_update_timer is None): self._smooth_update_timer = ba.Timer(0.05, ba.WeakCall( self._smooth_update), repeat=True, timetype=ba.TimeType.REAL) diff = abs(float(self._ticket_count) - self._smooth_ticket_count) self._smooth_increase_speed = (diff / 100.0 if diff >= 5000 else diff / 50.0 if diff >= 1500 else diff / 30.0 if diff >= 500 else diff / 15.0)
def _update(self) -> None: # We may outlive our widgets. if not self.root_widget: return # If we've been foregrounded/backgrounded we need to re-grab data. if self._fg_state != ba.app.fg_state: self._fg_state = ba.app.fg_state self._have_valid_data = False # If we need to run another tournament query, do so. if not self._running_query and ( (self._last_query_time is None) or (not self._have_valid_data) or (ba.time(ba.TimeType.REAL) - self._last_query_time > 30.0)): _ba.tournament_query(args={ 'source': 'entry window' if self._tournament_activity is None else 'retry entry window' }, callback=ba.WeakCall( self._on_tournament_query_response)) self._last_query_time = ba.time(ba.TimeType.REAL) self._running_query = True # Grab the latest info on our tourney. self._tournament_info = ba.app.accounts_v1.tournament_info[ self._tournament_id] # If we don't have valid data always show a '-' for time. if not self._have_valid_data: ba.textwidget(edit=self._time_remaining_text, text='-') else: if self._seconds_remaining is not None: self._seconds_remaining = max(0, self._seconds_remaining - 1) ba.textwidget(edit=self._time_remaining_text, text=ba.timestring( self._seconds_remaining * 1000, centi=False, timeformat=ba.TimeFormat.MILLISECONDS)) # Keep price up-to-date and update the button with it. self._purchase_price = _ba.get_v1_account_misc_read_val( self._purchase_price_name, None) ba.textwidget( edit=self._ticket_cost_text, text=(ba.Lstr(resource='getTicketsWindow.freeText') if self._purchase_price == 0 else ba.Lstr( resource='getTicketsWindow.ticketsText', subs=[('${COUNT}', str(self._purchase_price) if self._purchase_price is not None else '?')])), position=self._ticket_cost_text_position_free if self._purchase_price == 0 else self._ticket_cost_text_position, scale=1.0 if self._purchase_price == 0 else 0.6) ba.textwidget( edit=self._free_plays_remaining_text, text='' if (self._tournament_info['freeTriesRemaining'] in [None, 0] or self._purchase_price != 0) else '' + str(self._tournament_info['freeTriesRemaining'])) ba.imagewidget(edit=self._ticket_img, opacity=0.2 if self._purchase_price == 0 else 1.0, position=self._ticket_img_pos_free if self._purchase_price == 0 else self._ticket_img_pos) if self._do_ad_btn: enabled = _ba.have_incentivized_ad() have_ad_tries_remaining = ( self._tournament_info['adTriesRemaining'] is not None and self._tournament_info['adTriesRemaining'] > 0) ba.textwidget(edit=self._ad_text, position=self._ad_text_position_remaining if have_ad_tries_remaining else self._ad_text_position, color=(0, 1, 0) if enabled else (0.5, 0.5, 0.5)) ba.imagewidget(edit=self._pay_with_ad_img, opacity=1.0 if enabled else 0.2) ba.buttonwidget(edit=self._pay_with_ad_btn, color=(0.5, 0.7, 0.2) if enabled else (0.5, 0.5, 0.5)) ad_plays_remaining_text = ( '' if not have_ad_tries_remaining else '' + str(self._tournament_info['adTriesRemaining'])) ba.textwidget(edit=self._ad_plays_remaining_text, text=ad_plays_remaining_text, color=(0, 0.8, 0) if enabled else (0.4, 0.4, 0.4)) try: t_str = str(_ba.get_v1_account_ticket_count()) except Exception: t_str = '?' if self._get_tickets_button: ba.buttonwidget(edit=self._get_tickets_button, label=ba.charstr(ba.SpecialChar.TICKET) + t_str) if self._ticket_count_text: ba.textwidget(edit=self._ticket_count_text, text=ba.charstr(ba.SpecialChar.TICKET) + t_str)
def update_buttons(self) -> None: """Update our buttons.""" # pylint: disable=too-many-statements # pylint: disable=too-many-branches # pylint: disable=too-many-locals from ba.internal import get_available_sale_time from ba import SpecialChar if not self._root_widget: return import datetime sales_raw = _ba.get_v1_account_misc_read_val('sales', {}) sales = {} try: # Look at the current set of sales; filter any with time remaining. for sale_item, sale_info in list(sales_raw.items()): to_end = (datetime.datetime.utcfromtimestamp(sale_info['e']) - datetime.datetime.utcnow()).total_seconds() if to_end > 0: sales[sale_item] = { 'to_end': to_end, 'original_price': sale_info['op'] } except Exception: ba.print_exception('Error parsing sales.') assert self.button_infos is not None for b_type, b_info in self.button_infos.items(): if b_type in ['upgrades.pro', 'pro']: purchased = _ba.app.accounts_v1.have_pro() else: purchased = _ba.get_purchased(b_type) sale_opacity = 0.0 sale_title_text: Union[str, ba.Lstr] = '' sale_time_text: Union[str, ba.Lstr] = '' if purchased: title_color = (0.8, 0.7, 0.9, 1.0) color = (0.63, 0.55, 0.78) extra_image_opacity = 0.5 call = ba.WeakCall(self._print_already_own, b_info['name']) price_text = '' price_text_left = '' price_text_right = '' show_purchase_check = True description_color: Sequence[float] = (0.4, 1.0, 0.4, 0.4) description_color2: Sequence[float] = (0.0, 0.0, 0.0, 0.0) price_color = (0.5, 1, 0.5, 0.3) else: title_color = (0.7, 0.9, 0.7, 1.0) color = (0.4, 0.8, 0.1) extra_image_opacity = 1.0 call = b_info['call'] if 'call' in b_info else None if b_type in ['upgrades.pro', 'pro']: sale_time = get_available_sale_time('extras') if sale_time is not None: priceraw = _ba.get_price('pro') price_text_left = (priceraw if priceraw is not None else '?') priceraw = _ba.get_price('pro_sale') price_text_right = (priceraw if priceraw is not None else '?') sale_opacity = 1.0 price_text = '' sale_title_text = ba.Lstr(resource='store.saleText') sale_time_text = ba.timestring( sale_time, centi=False, timeformat=ba.TimeFormat.MILLISECONDS) else: priceraw = _ba.get_price('pro') price_text = priceraw if priceraw is not None else '?' price_text_left = '' price_text_right = '' else: price = _ba.get_v1_account_misc_read_val( 'price.' + b_type, 0) # Color the button differently if we cant afford this. if _ba.get_v1_account_state() == 'signed_in': if _ba.get_v1_account_ticket_count() < price: color = (0.6, 0.61, 0.6) price_text = ba.charstr(ba.SpecialChar.TICKET) + str( _ba.get_v1_account_misc_read_val( 'price.' + b_type, '?')) price_text_left = '' price_text_right = '' # TESTING: if b_type in sales: sale_opacity = 1.0 price_text_left = ba.charstr(SpecialChar.TICKET) + str( sales[b_type]['original_price']) price_text_right = price_text price_text = '' sale_title_text = ba.Lstr(resource='store.saleText') sale_time_text = ba.timestring( int(sales[b_type]['to_end'] * 1000), centi=False, timeformat=ba.TimeFormat.MILLISECONDS) description_color = (0.5, 1.0, 0.5) description_color2 = (0.3, 1.0, 1.0) price_color = (0.2, 1, 0.2, 1.0) show_purchase_check = False if 'title_text' in b_info: ba.textwidget(edit=b_info['title_text'], color=title_color) if 'purchase_check' in b_info: ba.imagewidget(edit=b_info['purchase_check'], opacity=1.0 if show_purchase_check else 0.0) if 'price_widget' in b_info: ba.textwidget(edit=b_info['price_widget'], text=price_text, color=price_color) if 'price_widget_left' in b_info: ba.textwidget(edit=b_info['price_widget_left'], text=price_text_left) if 'price_widget_right' in b_info: ba.textwidget(edit=b_info['price_widget_right'], text=price_text_right) if 'price_slash_widget' in b_info: ba.imagewidget(edit=b_info['price_slash_widget'], opacity=sale_opacity) if 'sale_bg_widget' in b_info: ba.imagewidget(edit=b_info['sale_bg_widget'], opacity=sale_opacity) if 'sale_title_widget' in b_info: ba.textwidget(edit=b_info['sale_title_widget'], text=sale_title_text) if 'sale_time_widget' in b_info: ba.textwidget(edit=b_info['sale_time_widget'], text=sale_time_text) if 'button' in b_info: ba.buttonwidget(edit=b_info['button'], color=color, on_activate_call=call) if 'extra_backings' in b_info: for bck in b_info['extra_backings']: ba.imagewidget(edit=bck, color=color, opacity=extra_image_opacity) if 'extra_images' in b_info: for img in b_info['extra_images']: ba.imagewidget(edit=img, opacity=extra_image_opacity) if 'extra_texts' in b_info: for etxt in b_info['extra_texts']: ba.textwidget(edit=etxt, color=description_color) if 'extra_texts_2' in b_info: for etxt in b_info['extra_texts_2']: ba.textwidget(edit=etxt, color=description_color2) if 'descriptionText' in b_info: ba.textwidget(edit=b_info['descriptionText'], color=description_color)