Skip to content

Commit

Permalink
Change unlock mandatory stylevar to be a widget config instead
Browse files Browse the repository at this point in the history
  • Loading branch information
TeamSpen210 committed Sep 9, 2024
1 parent 2d6953e commit 4bcd920
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 84 deletions.
35 changes: 0 additions & 35 deletions src/app/StyleVarPane.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,6 @@
desc=TransToken.ui('Disable all voicelines other than entry and exit lines.'),
),

StyleVar.unstyled(
id='UnlockDefault',
name=TransToken.ui('Unlock Default Items'),
default=False,
desc=TransToken.ui(
'Allow placing and deleting the mandatory Entry/Exit Doors and '
'Large Observation Room. Use with caution, this can have weird '
'results!'
),
),

StyleVar.unstyled(
id='AllowGooMist',
name=TransToken.ui('Allow Adding Goo Mist'),
Expand Down Expand Up @@ -113,14 +102,6 @@ class _WidgetsDict(TypedDict):
TRANS_COMMA = TransToken.ui(', ')


def mandatory_unlocked() -> bool:
"""Return whether mandatory items are unlocked currently."""
try:
return tk_vars['UnlockDefault'].get() != 0
except KeyError: # Not loaded yet
return False


def export_data(chosen_style: Style) -> dict[str, bool]:
"""Construct a dict containing the current stylevar settings."""
return {
Expand Down Expand Up @@ -234,19 +215,6 @@ async def make_stylevar_pane(
), TransToken.ui('None!'))
VAR_LIST[:] = sorted(packset.all_obj(StyleVar), key=operator.attrgetter('id'))

filter_event = trio.Event()

async def update_filter_task() -> None:
"""A special case. UnlockDefault needs to refresh the filter when changed.
That ensures the items disappear or reappear.
"""
nonlocal filter_event
while True:
await filter_event.wait()
filter_event = trio.Event()
await config.APP.apply_conf(FilterConf)

async def state_sync_task(
var_id: str,
tk_var: IntVar,
Expand All @@ -257,8 +225,6 @@ async def state_sync_task(
def cmd_func() -> None:
"""When clicked, store configuration."""
config.APP.store_conf(State(tk_var.get() != 0), var_id)
if var_id == 'UnlockDefault':
filter_event.set()

cmd = frame.register(cmd_func)
for check in checks:
Expand All @@ -270,7 +236,6 @@ def cmd_func() -> None:
tk_var.set(state.value)

async with trio.open_nursery() as nursery:
nursery.start_soon(update_filter_task)
all_pos = 0
for all_pos, var in enumerate(styleOptions):
# Add the special stylevars which apply to all styles
Expand Down
24 changes: 17 additions & 7 deletions src/app/UI.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from loadScreen import MAIN_UI as LOAD_UI
import packages
from packages.item import ItemVariant, InheritKind, SubItemRef
from packages.widgets import mandatory_unlocked
import utils
from config.filters import FilterConf
from config.gen_opts import GenOptions, AfterExport
Expand Down Expand Up @@ -904,7 +905,7 @@ def drag_fast(drag_item: PalItem, e: tk.Event[tk.Misc]) -> None:
flow_preview()


async def set_palette(chosen_pal: paletteUI.Palette) -> None:
async def set_palette(chosen_pal: paletteUI.Palette, flow_picker: Callable[[], object]) -> None:
"""Select a palette."""
pal_clear()
for coord in paletteUI.COORDS:
Expand Down Expand Up @@ -932,12 +933,17 @@ async def set_palette(chosen_pal: paletteUI.Palette) -> None:
is_pre=True,
))

old_mandatory = mandatory_unlocked()

if chosen_pal.settings is not None:
conf = await packages.get_loaded_packages().migrate_conf(chosen_pal.settings)
LOGGER.info('Settings: {}', conf)
await config.APP.apply_multi(conf)

flow_preview()
if old_mandatory is not mandatory_unlocked():
# Changed, update the preview.
flow_picker()


def pal_clear() -> None:
Expand All @@ -949,7 +955,7 @@ def pal_clear() -> None:

def pal_shuffle() -> None:
"""Set the palette to a list of random items."""
mandatory_unlocked = StyleVarPane.mandatory_unlocked()
include_mandatory = mandatory_unlocked()

if len(pal_picked) == 32:
return
Expand All @@ -966,7 +972,7 @@ def pal_shuffle() -> None:
# obey the mandatory item lock and filters.
for item in pal_items
if item.id not in palette_set
if mandatory_unlocked or not item.needs_unlock
if include_mandatory or not item.needs_unlock
if cur_filter is None or (item.id, item.subKey) in cur_filter
if len(item_list[item.id].item.visual_subtypes) # Check there's actually sub-items to show.
})
Expand Down Expand Up @@ -1266,7 +1272,7 @@ async def _flow_picker(filter_conf: FilterConf) -> None:
occur.
"""
frmScroll['width'] = pal_canvas.winfo_width()
mandatory_unlocked = StyleVarPane.mandatory_unlocked()
hide_mandatory = not mandatory_unlocked()

width = (pal_canvas.winfo_width() - 10) // 65
if width < 1:
Expand All @@ -1275,7 +1281,7 @@ async def _flow_picker(filter_conf: FilterConf) -> None:
i = 0
# If cur_filter is None, it's blank and so show all of them.
for pal_item, should_checkpoint in zip(pal_items, itertools.cycle('YNNNN')):
if pal_item.needs_unlock and not mandatory_unlocked:
if hide_mandatory and pal_item.needs_unlock:
visible = False
elif filter_conf.compress:
# Show if this is the first, and any in this item are visible.
Expand Down Expand Up @@ -1495,6 +1501,10 @@ def update_filter(new_filter: set[tuple[str, int]] | None) -> None:
windows['pal'].columnconfigure(0, weight=1)
windows['pal'].rowconfigure(0, weight=1)

async def set_items(pal: paletteUI.Palette) -> None:
"""Pass along the reflow-picker function to set_palette."""
await set_palette(pal, flow_picker)

pal_ui = paletteUI.PaletteUI(
pal_frame, menu_bar.pal_menu,
tk_img=tk_img,
Expand All @@ -1506,7 +1516,7 @@ def update_filter(new_filter: set[tuple[str, int]] | None) -> None:
pos: (it.id, it.subKey)
for pos, it in zip(paletteUI.COORDS, pal_picked, strict=False)
},
set_items=set_palette,
set_items=set_items,
)

TK_ROOT.bind_all(tk_tools.KEY_SAVE, lambda e: pal_ui.event_save(DIALOG))
Expand Down Expand Up @@ -1719,6 +1729,6 @@ async def style_select_callback() -> None:
nursery.start_soon(enable_export)
nursery.start_soon(style_select_callback)
await first_select.wait()
await set_palette(pal_ui.selected)
await set_palette(pal_ui.selected, flow_picker)
pal_ui.is_dirty.set()
task_status.started()
21 changes: 17 additions & 4 deletions src/app/itemconfig/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Customizable configuration for specific items or groups of them."""
from __future__ import annotations

from contextlib import aclosing
from typing import Any, Protocol

from collections.abc import Awaitable, Callable, Mapping, Iterator
Expand All @@ -16,7 +18,8 @@
from app import UI, sound, StyleVarPane
from app.SubPane import SubPane
from app.mdown import MarkdownData
from async_util import EdgeTrigger, run_as_task
from async_util import EdgeTrigger, run_as_task
from config.filters import FilterConf
from packages.signage import ITEM_ID as SIGNAGE_ITEM_ID
from transtoken import TransToken, CURRENT_LANG
from ui_tk import TK_ROOT, tk_tools
Expand All @@ -33,8 +36,8 @@
)
from packages.widgets import (
CLS_TO_KIND, ConfigGroup, ConfigProto, ItemVariantConf,
WidgetType,
WidgetTypeWithConf,
UNLOCK_DEFAULT_ID,
WidgetType, WidgetTypeWithConf,
)

LOGGER = logger.get_logger(__name__)
Expand Down Expand Up @@ -175,6 +178,13 @@ def wrapper(
return deco


async def unlock_default_ui_listener(holder: AsyncValue[str]) -> None:
"""When Unlock Default Items are changed, refresh the item list."""
async with aclosing(holder.eventual_values()) as agen:
async for value in agen:
await config.APP.apply_conf(FilterConf)


async def create_group(
master: ttk.Frame,
nursery: trio.Nursery,
Expand Down Expand Up @@ -236,12 +246,15 @@ async def create_group(
else:
widget.grid(row=0, column=0, columnspan=2, sticky='ew')
if s_wid.has_values:
wid_id = f'{s_wid.group_id}:{s_wid.id}'
nursery.start_soon(
s_wid.load_conf_task,
config.APP.get_ui_channel(
WidgetConfig, f'{s_wid.group_id}:{s_wid.id}',
WidgetConfig, wid_id,
)
)
if wid_id == UNLOCK_DEFAULT_ID:
nursery.start_soon(unlock_default_ui_listener, s_wid.holder)
nursery.start_soon(s_wid.state_store_task)
if s_wid.tooltip:
add_tooltip(widget, s_wid.tooltip)
Expand Down
31 changes: 30 additions & 1 deletion src/exporting/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
from app import lazy_conf
from config.gen_opts import GenOptions
from config.item_defaults import ItemDefault
from config.widgets import WidgetConfig
from connections import INDICATOR_CHECK_ID
from editoritems import InstCount, Item as EditorItem
from consts import DefaultItems
from editoritems import InstCount, Item as EditorItem, DesiredFacing
from packages.item import Item, ItemConfig
from transtoken import TransToken
import config
Expand All @@ -30,6 +32,15 @@
'Style includes old definition for {item}. Override the item package object instead.'
)

# Mandatory items to unlock.
UNLOCK_ITEMS = {
DefaultItems.door_sp_entry.id,
DefaultItems.door_coop_entry.id,
DefaultItems.door_sp_entry.id,
DefaultItems.door_sp_exit.id,
DefaultItems.obs_room_large.id,
}


def apply_replacements(conf: Keyvalues, item_id: str) -> Keyvalues:
"""Apply a set of replacement values to a config file, returning a new copy.
Expand Down Expand Up @@ -123,6 +134,24 @@ def get_export_data(
)


@STEPS.add_step(prereq=[StepResource.EI_ITEMS], results=[StepResource.EI_DATA])
async def step_unlock_defaults(exp_data: ExportData) -> None:
"""If the unlock defaults option is set, unlock the default items."""
await trio.lowlevel.checkpoint()

for i, item in enumerate(exp_data.all_items):
# If the Unlock Default Items option is enabled, we
# want to force the corridors and obs room to be
# deletable and copyable.
# Also add DESIRES_UP, so they place in the correct orientation.
# That would have already been done for vertical-enabled corridors, but that's
# fine.
if item.id in UNLOCK_ITEMS:
exp_data.all_items[i] = item = copy.copy(item)
item.deletable = item.copiable = True
item.facing = DesiredFacing.UP


@STEPS.add_step(
prereq=[StepResource.STYLE],
results=[StepResource.EI_ITEMS, StepResource.EI_DATA, StepResource.VCONF_DATA],
Expand Down
40 changes: 4 additions & 36 deletions src/exporting/stylevar.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,21 @@
from typing import Dict
import copy
"""Export stylevar selections."""
from srctools import Keyvalues, bool_as_int

import srctools
from srctools import Keyvalues

import editoritems
from consts import DefaultItems
from . import ExportData, STEPS, StepResource
from packages import StyleVar


UNLOCK_ITEMS = {
DefaultItems.door_sp_entry.id,
DefaultItems.door_coop_entry.id,
DefaultItems.door_sp_entry.id,
DefaultItems.door_sp_exit.id,
DefaultItems.obs_room_large.id,
}


@STEPS.add_step(prereq=[], results=[StepResource.VCONF_DATA])
async def step_add_stylevars(exp_data: ExportData) -> None:
"""Export style var selections into the config.
The .selected attribute is a dict mapping ids to the boolean value.
"""
style_vars: Dict[str, bool] = exp_data.selected[StyleVar]
style_vars: dict[str, bool] = exp_data.selected[StyleVar]
# Add the StyleVars block, containing each style_var.
exp_data.vbsp_conf.append(Keyvalues('StyleVars', [
Keyvalues(key, srctools.bool_as_int(val))
Keyvalues(key, bool_as_int(val))
for key, val in
style_vars.items()
]))


@STEPS.add_step(prereq=[StepResource.EI_ITEMS], results=[StepResource.EI_DATA])
async def step_unlock_defaults(exp_data: ExportData) -> None:
"""If the unlock defaults stylevar is set, unlock the default items."""
if not exp_data.selected[StyleVar]['UnlockDefault']:
return

for i, item in enumerate(exp_data.all_items):
# If the Unlock Default Items stylevar is enabled, we
# want to force the corridors and obs room to be
# deletable and copyable.
# Also add DESIRES_UP, so they place in the correct orientation.
# That would have already been done for vertical-enabled corridors, but that's
# fine.
if item.id in UNLOCK_ITEMS:
exp_data.all_items[i] = item = copy.copy(item)
item.deletable = item.copiable = True
item.facing = editoritems.DesiredFacing.UP
13 changes: 12 additions & 1 deletion src/packages/widgets.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Customizable configuration for specific items or groups of them."""
from __future__ import annotations

from typing import Any, Protocol, Self, override
from typing import Any, Final, Protocol, Self, override
from collections.abc import Iterable, Iterator
from contextlib import AbstractContextManager, aclosing
import itertools
Expand Down Expand Up @@ -34,6 +34,8 @@ def parse(cls, data: packages.ParseData, conf: Keyvalues, /) -> Self:
"""Parse keyvalues into a widget config."""


# This has to reload items when changed.
UNLOCK_DEFAULT_ID: Final = 'VALVE_MANDATORY:unlockdefault'
LEGACY_CONFIG = BEE2_config.ConfigFile('item_cust_configs.cfg', legacy=True)
LOGGER = logger.get_logger(__name__)

Expand Down Expand Up @@ -96,6 +98,15 @@ def register_no_conf(*names: str, wide: bool = False) -> WidgetType:
return kind


def mandatory_unlocked() -> bool:
"""Check if mandatory items should be shown."""
option = config.APP.get_cur_conf(WidgetConfig, UNLOCK_DEFAULT_ID).values
if not isinstance(option, str):
LOGGER.warning('Unlock Default option is a timer?')
return False
return conv_bool(option)


@attrs.define
class Widget:
"""Common logic for both kinds of widget that can appear on a ConfigGroup."""
Expand Down

0 comments on commit 4bcd920

Please sign in to comment.