From f471f48d74c9b01cbd9b99d919cf7aded839b2e8 Mon Sep 17 00:00:00 2001 From: Spencer Brown Date: Tue, 11 Jun 2024 08:33:03 +1000 Subject: [PATCH] Fix #2054: Export no voicelines being set correctly --- CHANGELOG.md | 3 +++ src/exporting/quote_pack.py | 41 +++++++++++++++++++++++++------ src/packages/item.py | 10 +++++++- src/precomp/conditions/monitor.py | 21 +++++++++++----- src/precomp/voice_line.py | 5 +++- src/quote_pack.py | 2 +- 6 files changed, 66 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 180ff1388..a899ba6d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ # Version (dev) * #573: Show filename in tooltip for custom screenshots. +### Bugfixes: +* The case where no voicelines are set is once again handled correctly. + ------------------------------------------ # Version 4.45.2 diff --git a/src/exporting/quote_pack.py b/src/exporting/quote_pack.py index e296b38be..c30bda96f 100644 --- a/src/exporting/quote_pack.py +++ b/src/exporting/quote_pack.py @@ -1,25 +1,55 @@ """Export quote pack configurations.""" -from typing import Optional +from typing import TYPE_CHECKING import pickle import shutil import srctools import trio +from srctools import Vec import utils from exporting import ExportData, STEPS, StepResource from packages import QuotePack +from quote_pack import QuoteInfo +if TYPE_CHECKING: + from app.gameMan import Game + + +__all__ = [] LOGGER = srctools.logger.get_logger(__name__) +async def write_data(game: 'Game', data: QuoteInfo | None) -> None: + """Write the quote data.""" + pickle_data = await trio.to_thread.run_sync(pickle.dumps, data, pickle.HIGHEST_PROTOCOL) + await trio.Path(game.abs_path('bin/bee2/voice.bin')).write_bytes(pickle_data) + + @STEPS.add_step(prereq=[], results=[StepResource.VCONF_DATA]) async def step_quote_pack(exp_data: ExportData) -> None: """Export the quotepack.""" - sel_id: Optional[str] = exp_data.selected[QuotePack] + sel_id: str | None = exp_data.selected[QuotePack] if sel_id is None: - return # No quote pack selected at all, don't write anything. + # No quote pack selected at all. Write a blank definition. + await write_data(exp_data.game, QuoteInfo( + id='', + cave_skin=None, + use_dings=False, + use_microphones=False, + global_bullseye='', + chars=set(), + base_inst='', + position=Vec(), + groups={}, + events={}, + response_use_dings=False, + responses={}, + midchamber=[], + monitor=None, + )) + return try: voice = exp_data.packset.obj_by_id(QuotePack, sel_id) @@ -27,10 +57,7 @@ async def step_quote_pack(exp_data: ExportData) -> None: raise Exception(f"Selected voice ({sel_id}) doesn't exist?") from None exp_data.vbsp_conf += voice.config - - pickle_data = await trio.to_thread.run_sync(pickle.dumps, voice.data, pickle.HIGHEST_PROTOCOL) - await trio.Path(exp_data.game.abs_path('bin/bee2/voice.bin')).write_bytes(pickle_data) - del pickle_data + await write_data(exp_data.game, voice.data) # Copy the config files for this voice line... for prefix, pretty in [ diff --git a/src/packages/item.py b/src/packages/item.py index 0e433408d..0f218e5cb 100644 --- a/src/packages/item.py +++ b/src/packages/item.py @@ -12,11 +12,12 @@ from collections.abc import Sequence, Iterable, Iterator from enum import Enum -from pathlib import PurePosixPath as FSPath +from pathlib import PurePosixPath as FSPath, Path import re import copy from srctools import FileSystem, Keyvalues, VMF, logger +from srctools.filesys import RawFileSystem from srctools.tokenizer import Tokenizer, Token import attrs import trio @@ -862,6 +863,13 @@ def parse_props(path: str) -> Keyvalues: editor_vmf = editor_vmf_res() if editor_vmf is not None: editoritems_vmf.load(first_item, editor_vmf) + # elif isinstance(filesystem, RawFileSystem): + # # Write out editoritems.vmf. + # editor_vmf = editoritems_vmf.save(first_item) + # with Path(filesystem.path, vmf_path).open('w') as f: + # LOGGER.info('Writing {}', f.name) + # await trio.to_thread.run_sync(editor_vmf.export, f) + del editor_vmf, editor_vmf_res first_item.generate_collisions() diff --git a/src/precomp/conditions/monitor.py b/src/precomp/conditions/monitor.py index 00cf23798..0a29366a6 100644 --- a/src/precomp/conditions/monitor.py +++ b/src/precomp/conditions/monitor.py @@ -1,7 +1,7 @@ import math from precomp import instanceLocs, connections, conditions, options, faithplate -from srctools import Matrix, Keyvalues, Vec, Entity, VMF, Output, Angle +from srctools import FrozenVec, Matrix, Keyvalues, Vec, Entity, VMF, Output, Angle import srctools.logger from typing import List, NamedTuple, Literal, Optional @@ -28,15 +28,22 @@ class Camera(NamedTuple): # If non-emtpy we have monitors to shoot by turrets. MONITOR_RELATIONSHIP_ENTS: List[Entity] = [] +# The location of arrival_departure_transition_ents, which has toolsblack. +BLACK_SCREEN_LOC = FrozenVec(-2500, -2500, 0) -def get_studio_pose(voice: QuoteInfo) -> Vec: + +def get_studio_pose(voice: QuoteInfo) -> Vec | FrozenVec: """Return the position of the studio camera.""" - return voice.position + options.VOICE_STUDIO_CAM_LOC() + if voice.id: + return voice.position + options.VOICE_STUDIO_CAM_LOC() + else: + # No voice line set, no studio. + return BLACK_SCREEN_LOC def scriptvar_set( targ: Entity, - pos: Vec, + pos: Vec | FrozenVec, varname: str, value: object = '', *, @@ -145,7 +152,7 @@ def res_camera(vmf: VMF, res: Keyvalues) -> conditions.ResultCallable: pitch_range = srctools.conv_int(res['PitchRange', ''], 90) def add_camera(inst: Entity) -> None: - + """Add the camera.""" normal = Vec(z=1) @ Angle.from_str(inst['angles']) if abs(normal.z) > 0.1: # Can't be on floor/ceiling! @@ -257,6 +264,7 @@ def mon_camera_link(vmf: VMF, voice: QuoteInfo) -> None: f'CamDisable({index})', ), ) + start_pos: Vec | FrozenVec for is_act, cam in zip(active_counts, ALL_CAMERAS, strict=True): if is_act: start_pos = cam.cam_pos @@ -277,7 +285,7 @@ def mon_camera_link(vmf: VMF, voice: QuoteInfo) -> None: relation['StartActive'] = '1' else: # Start in arrival_departure_transition_ents... - start_pos = Vec(-2500, -2500, 0) + start_pos = BLACK_SCREEN_LOC start_angles = Angle(0, 90, 0) cam_ent = vmf.create_ent( @@ -365,6 +373,7 @@ def make_voice_studio(vmf: VMF, voice: QuoteInfo) -> bool: This is either an instance (if monitors are present), or a nodraw room. """ + assert voice.id, "No voiceline defined?" studio_file = options.VOICE_STUDIO_INST() loc = voice.position diff --git a/src/precomp/voice_line.py b/src/precomp/voice_line.py index 6d279558f..80571dddf 100644 --- a/src/precomp/voice_line.py +++ b/src/precomp/voice_line.py @@ -353,7 +353,10 @@ def add_voice( use_priority: bool = True, ) -> None: """Add a voice line to the map.""" - LOGGER.info('Adding Voice Lines!') + if not voice.id: + LOGGER.info('No voiceline set!') + return + LOGGER.info('Adding Voice Line: {}', voice.id) norm_config = ConfigFile('bee2/voice.cfg', in_conf_folder=False) mid_config = ConfigFile('bee2/mid_voice.cfg', in_conf_folder=False) diff --git a/src/quote_pack.py b/src/quote_pack.py index f268d43d9..305ab4e7e 100644 --- a/src/quote_pack.py +++ b/src/quote_pack.py @@ -362,7 +362,7 @@ class Monitor: @attrs.define(kw_only=True) class QuoteInfo: """The data that is saved for the compiler to use.""" - id: str + id: str # The ID of the pack, or '' if no line is set. cave_skin: int | None use_dings: bool use_microphones: bool