diff --git a/.gitignore b/.gitignore index eddaa9f..ef4bed0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,9 +15,13 @@ vpk/ portal_pak_dir/ portal_pak_modified/ +resource/closecaption_* + tools/skeletool64 tools/vtf2png skelatool64/*.zip src/controls/controller-data.h +src/audio/subtitles.c +src/audio/subtitles.h diff --git a/Makefile b/Makefile index 0643219..5af9547 100644 --- a/Makefile +++ b/Makefile @@ -67,21 +67,26 @@ LDFLAGS = -L/usr/lib/n64 $(N64LIB) -L$(N64_LIBGCCDIR) -lgcc default: english_audio english_audio: portal_pak_dir + @python tools/level_scripts/subtitle_generate.py @$(MAKE) buildgame german_audio: vpk/portal_sound_vo_german_dir.vpk vpk/portal_sound_vo_german_000.vpk portal_pak_dir + @python tools/level_scripts/subtitle_generate.py vpk -x portal_pak_dir vpk/portal_sound_vo_german_dir.vpk @$(MAKE) buildgame french_audio: vpk/portal_sound_vo_french_dir.vpk vpk/portal_sound_vo_french_000.vpk portal_pak_dir + @python tools/level_scripts/subtitle_generate.py vpk -x portal_pak_dir vpk/portal_sound_vo_french_dir.vpk @$(MAKE) buildgame russian_audio: vpk/portal_sound_vo_russian_dir.vpk vpk/portal_sound_vo_russian_000.vpk portal_pak_dir + @python tools/level_scripts/subtitle_generate.py vpk -x portal_pak_dir vpk/portal_sound_vo_russian_dir.vpk @$(MAKE) buildgame spanish_audio: vpk/portal_sound_vo_spanish_dir.vpk vpk/portal_sound_vo_spanish_000.vpk portal_pak_dir + @python tools/level_scripts/subtitle_generate.py vpk -x portal_pak_dir vpk/portal_sound_vo_spanish_dir.vpk @$(MAKE) buildgame @@ -509,4 +514,8 @@ clean-assets: fix: wine tools/romfix64.exe build/portal.z64 + +subtitles: tools/level_scripts/subtitle_generate.py + python3 tools/level_scripts/subtitle_generate.py + .SECONDARY: diff --git a/README.md b/README.md index afc34ad..033263a 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,12 @@ sudo apt install nodejs
+Optionally, for closed captions you will need to add the following files from where Portal is installed to the folder `resource/`. If no files are placed here Portal64 will still build without them, it simply wont have an option for close captions in the game menu. 2-3 languages seems to work best. +``` +portal/resource/closecaption_.txt +portal/resource/closecaption_.txt +``` + You then need to add the following files from where Portal is installed to the folder `vpk`. (see vpk/add_vpk_here.md for more details!) ``` portal/portal_pak_000.vpk diff --git a/assets/closecaption_english.txt b/assets/closecaption_english.txt deleted file mode 100644 index f5c38c4..0000000 Binary files a/assets/closecaption_english.txt and /dev/null differ diff --git a/assets/test_chambers/test_chamber_00/test_chamber_00.yaml b/assets/test_chambers/test_chamber_00/test_chamber_00.yaml index 4898fde..5e2d40d 100644 --- a/assets/test_chambers/test_chamber_00/test_chamber_00.yaml +++ b/assets/test_chambers/test_chamber_00/test_chamber_00.yaml @@ -12,13 +12,13 @@ cutscenes: - delay 2 - show_prompt CutscenePromptTypeJump - delay 3 - - q_sound SOUNDS_00_PART1_ENTRY_1 CH_GLADOS SUBTITLE_00_PART1_ENTRY_1 - - q_sound SOUNDS_00_PART1_ENTRY_2 CH_GLADOS SUBTITLE_00_PART1_ENTRY_2 - - q_sound SOUNDS_00_PART1_ENTRY_3 CH_GLADOS SUBTITLE_00_PART1_ENTRY_3 - - q_sound SOUNDS_00_PART1_ENTRY_4 CH_GLADOS SUBTITLE_00_PART1_ENTRY_4 - - q_sound SOUNDS_00_PART1_ENTRY_5 CH_GLADOS SUBTITLE_00_PART1_ENTRY_5 - - q_sound SOUNDS_00_PART1_ENTRY_6 CH_GLADOS SUBTITLE_00_PART1_ENTRY_6 - - q_sound SOUNDS_00_PART1_ENTRY_7 CH_GLADOS SUBTITLE_00_PART1_ENTRY_7 + - q_sound SOUNDS_00_PART1_ENTRY_1 CH_GLADOS PORTAL_00_PART1_ENTRY_1 + - q_sound SOUNDS_00_PART1_ENTRY_2 CH_GLADOS PORTAL_00_PART1_ENTRY_2 + - q_sound SOUNDS_00_PART1_ENTRY_3 CH_GLADOS PORTAL_00_PART1_ENTRY_3 + - q_sound SOUNDS_00_PART1_ENTRY_4 CH_GLADOS PORTAL_00_PART1_ENTRY_4 + - q_sound SOUNDS_00_PART1_ENTRY_5 CH_GLADOS PORTAL_00_PART1_ENTRY_5 + - q_sound SOUNDS_00_PART1_ENTRY_6 CH_GLADOS PORTAL_00_PART1_ENTRY_6 + - q_sound SOUNDS_00_PART1_ENTRY_7 CH_GLADOS PORTAL_00_PART1_ENTRY_7 - delay 7 - show_prompt CutscenePromptTypeNone - wait_for_channel CH_GLADOS @@ -30,9 +30,9 @@ cutscenes: - set_signal cube_dropper - show_prompt CutscenePromptTypePickup - wait_for_signal success - - q_sound SOUNDS_00_PART1_SUCCESS_1 CH_GLADOS SUBTITLE_00_PART1_SUCCESS_1 - - q_sound SOUNDS_00_PART1_SUCCESS_2 CH_GLADOS SUBTITLE_00_PART1_SUCCESS_2 - - q_sound SOUNDS_00_PART1_SUCCESS_3 CH_GLADOS SUBTITLE_00_PART1_SUCCESS_3 + - q_sound SOUNDS_00_PART1_SUCCESS_1 CH_GLADOS PORTAL_00_PART1_SUCCESS_1 + - q_sound SOUNDS_00_PART1_SUCCESS_2 CH_GLADOS PORTAL_00_PART1_SUCCESS_2 + - q_sound SOUNDS_00_PART1_SUCCESS_3 CH_GLADOS PORTAL_00_PART1_SUCCESS_3 START_SECOND_ROOM: - start_cutscene SECOND_ROOM_INSTRUCTIONS - open_portal stationary_portal 1 @@ -45,8 +45,8 @@ cutscenes: - delay 5 - goto portal_loop SECOND_ROOM_INSTRUCTIONS: - - q_sound SOUNDS_00_PART2_ENTRY_1 CH_GLADOS SUBTITLE_00_PART2_ENTRY_1 + - q_sound SOUNDS_00_PART2_ENTRY_1 CH_GLADOS PORTAL_00_PART2_ENTRY_1 - wait_for_signal success_1 - - q_sound SOUNDS_00_PART2_SUCCESS_1 CH_GLADOS SUBTITLE_00_PART2_SUCCESS_1 + - q_sound SOUNDS_00_PART2_SUCCESS_1 CH_GLADOS PORTAL_00_PART2_SUCCESS_1 HOVER_CUBE_PROMPT: - show_prompt CutscenePromptTypeDrop diff --git a/assets/test_chambers/test_chamber_01/test_chamber_01.yaml b/assets/test_chambers/test_chamber_01/test_chamber_01.yaml index 8841791..445347a 100644 --- a/assets/test_chambers/test_chamber_01/test_chamber_01.yaml +++ b/assets/test_chambers/test_chamber_01/test_chamber_01.yaml @@ -6,8 +6,8 @@ cutscenes: - start_cutscene portal_loop - set_signal FIRST_ELEVATOR INTRO_CUTSCENE: - - q_sound SOUNDS_01_PART1_ENTRY_1 CH_GLADOS SUBTITLE_01_PART1_ENTRY_1 - - q_sound SOUNDS_01_PART1_ENTRY_2 CH_GLADOS SUBTITLE_01_PART1_ENTRY_2 + - q_sound SOUNDS_01_PART1_ENTRY_1 CH_GLADOS PORTAL_01_PART1_ENTRY_1 + - q_sound SOUNDS_01_PART1_ENTRY_2 CH_GLADOS PORTAL_01_PART1_ENTRY_2 - wait_for_channel CH_GLADOS - set_signal room_0_entrance GET_GUN: @@ -16,19 +16,19 @@ cutscenes: - hide_pedestal - stop_cutscene portal_loop - show_prompt CutscenePromptTypePortal1 - - q_sound 01_PART1_GET_PORTAL_GUN_1 CH_GLADOS SUBTITLE_01_PART1_GET_PORTAL_GUN_1 - - q_sound 01_PART1_GET_PORTAL_GUN_2 CH_GLADOS SUBTITLE_01_PART1_GET_PORTAL_GUN_2 - - q_sound 01_PART1_GET_PORTAL_GUN_3 CH_GLADOS SUBTITLE_01_PART1_GET_PORTAL_GUN_3 - - q_sound 01_PART1_GET_PORTAL_GUN_4 CH_GLADOS SUBTITLE_01_PART1_GET_PORTAL_GUN_4 - - q_sound 01_PART1_GET_PORTAL_GUN_5 CH_GLADOS SUBTITLE_01_PART1_GET_PORTAL_GUN_5 - - q_sound 01_PART1_GET_PORTAL_GUN_6 CH_GLADOS SUBTITLE_01_PART1_GET_PORTAL_GUN_6 - - q_sound 01_PART1_GET_PORTAL_GUN_7 CH_GLADOS SUBTITLE_01_PART1_GET_PORTAL_GUN_7 - - q_sound 01_PART1_GET_PORTAL_GUN_8 CH_GLADOS SUBTITLE_01_PART1_GET_PORTAL_GUN_8 + - q_sound 01_PART1_GET_PORTAL_GUN_1 CH_GLADOS PORTAL_01_PART1_GET_PORTAL_GUN_1 + - q_sound 01_PART1_GET_PORTAL_GUN_2 CH_GLADOS PORTAL_01_PART1_GET_PORTAL_GUN_2 + - q_sound 01_PART1_GET_PORTAL_GUN_3 CH_GLADOS PORTAL_01_PART1_GET_PORTAL_GUN_3 + - q_sound 01_PART1_GET_PORTAL_GUN_4 CH_GLADOS PORTAL_01_PART1_GET_PORTAL_GUN_4 + - q_sound 01_PART1_GET_PORTAL_GUN_5 CH_GLADOS PORTAL_01_PART1_GET_PORTAL_GUN_5 + - q_sound 01_PART1_GET_PORTAL_GUN_6 CH_GLADOS PORTAL_01_PART1_GET_PORTAL_GUN_6 + - q_sound 01_PART1_GET_PORTAL_GUN_7 CH_GLADOS PORTAL_01_PART1_GET_PORTAL_GUN_7 + - q_sound 01_PART1_GET_PORTAL_GUN_8 CH_GLADOS PORTAL_01_PART1_GET_PORTAL_GUN_8 MIND_THE_GAP: - open_portal portal_gap 0 - - q_sound SOUNDS_01_PART2_ENTRY_1 CH_GLADOS SUBTITLE_01_PART2_ENTRY_1 + - q_sound SOUNDS_01_PART2_ENTRY_1 CH_GLADOS PORTAL_01_PART2_ENTRY_1 FINISH_03: - - q_sound SOUNDS_01_PART2_SUCCESS_1 CH_GLADOS SUBTITLE_01_PART2_SUCCESS_1 + - q_sound SOUNDS_01_PART2_SUCCESS_1 CH_GLADOS PORTAL_01_PART2_SUCCESS_1 portal_loop: - open_portal portal_exit 0 - label loop_start diff --git a/assets/test_chambers/test_chamber_02/test_chamber_02.yaml b/assets/test_chambers/test_chamber_02/test_chamber_02.yaml index 41b476b..0401685 100644 --- a/assets/test_chambers/test_chamber_02/test_chamber_02.yaml +++ b/assets/test_chambers/test_chamber_02/test_chamber_02.yaml @@ -1,25 +1,25 @@ cutscenes: INTRO: - - q_sound SOUNDS_02_PART1_ENTRY_1 CH_GLADOS SUBTITLE_02_PART1_ENTRY_1 + - q_sound SOUNDS_02_PART1_ENTRY_1 CH_GLADOS PORTAL_02_PART1_ENTRY_1 - wait_for_channel CH_GLADOS - open_portal portal_0 0 - - q_sound SOUNDS_02_PART1_ENTRY_2 CH_GLADOS SUBTITLE_02_PART1_ENTRY_2 + - q_sound SOUNDS_02_PART1_ENTRY_2 CH_GLADOS PORTAL_02_PART1_ENTRY_2 - wait_for_signal success - - q_sound SOUNDS_02_PART1_SUCCESS_1 CH_GLADOS SUBTITLE_02_PART1_SUCCESS_1 - - q_sound SOUNDS_02_PART1_SUCCESS_2 CH_GLADOS SUBTITLE_02_PART1_SUCCESS_2 + - q_sound SOUNDS_02_PART1_SUCCESS_1 CH_GLADOS PORTAL_02_PART1_SUCCESS_1 + - q_sound SOUNDS_02_PART1_SUCCESS_2 CH_GLADOS PORTAL_02_PART1_SUCCESS_2 DROP_CUBE: - set_signal cube_dropper ENTER_BUTTON_ROOM: - open_portal portal_1 BAD_PERSON: - - q_sound SOUNDS_ESCAPE_01_PART1_NAG01_1 CH_GLADOS SUBTITLE_ESCAPE_01_PART1_NAG01_1 + - q_sound SOUNDS_ESCAPE_01_PART1_NAG01_1 CH_GLADOS PORTAL_ESCAPE_01_PART1_NAG01_1 - wait_for_channel CH_GLADOS - set_signal bad_person ENTER_SMALL_ROOM: - set_signal in_room - open_portal portal_2 0 - - q_sound SOUNDS_02_PART2_SUCCESS_1 CH_GLADOS SUBTITLE_02_PART2_SUCCESS_1 - - q_sound SOUNDS_02_PART2_SUCCESS_2 CH_GLADOS SUBTITLE_02_PART2_SUCCESS_2 + - q_sound SOUNDS_02_PART2_SUCCESS_1 CH_GLADOS PORTAL_02_PART2_SUCCESS_1 + - q_sound SOUNDS_02_PART2_SUCCESS_2 CH_GLADOS PORTAL_02_PART2_SUCCESS_2 operators: - should_open_door = exit_2_0 and exit_2_1 - should_open_door = should_open_door or bad_person diff --git a/assets/test_chambers/test_chamber_03/test_chamber_03.yaml b/assets/test_chambers/test_chamber_03/test_chamber_03.yaml index e5817e7..4669681 100644 --- a/assets/test_chambers/test_chamber_03/test_chamber_03.yaml +++ b/assets/test_chambers/test_chamber_03/test_chamber_03.yaml @@ -13,16 +13,16 @@ cutscenes: - save_checkpoint 0.001 - open_portal ground_portal_2 - start_cutscene BALL_2 - - q_sound SOUNDS_03_PART2_ENTRY_1 CH_GLADOS SUBTITLE_03_PART2_ENTRY_1 + - q_sound SOUNDS_03_PART2_ENTRY_1 CH_GLADOS PORTAL_03_PART2_ENTRY_1 - wait_for_signal horizontal_activiate 2 - - q_sound 03_PART2_PLATFORM_ACTIVATED_1 CH_GLADOS SUBTITLE_03_PART2_PLATFORM_ACTIVATED_1 + - q_sound 03_PART2_PLATFORM_ACTIVATED_1 CH_GLADOS PORTAL_03_PART2_PLATFORM_ACTIVATED_1 INTRO: - save_checkpoint 0 - open_portal ground_portal 0 - - q_sound SOUNDS_03_PART1_ENTRY_1 CH_GLADOS SUBTITLE_03_PART1_ENTRY_1 - - q_sound SOUNDS_03_PART1_ENTRY_2 CH_GLADOS SUBTITLE_03_PART1_ENTRY_2 + - q_sound SOUNDS_03_PART1_ENTRY_1 CH_GLADOS PORTAL_03_PART1_ENTRY_1 + - q_sound SOUNDS_03_PART1_ENTRY_2 CH_GLADOS PORTAL_03_PART1_ENTRY_2 - wait_for_signal exit_activate 2 - - q_sound SOUNDS_03_PART1_SUCCESS_1 CH_GLADOS SUBTITLE_03_PART1_SUCCESS_1 + - q_sound SOUNDS_03_PART1_SUCCESS_1 CH_GLADOS PORTAL_03_PART1_SUCCESS_1 START_BALL: - set_signal launch_ball - wait_for_signal exit_activate diff --git a/assets/test_chambers/test_chamber_04/test_chamber_04.yaml b/assets/test_chambers/test_chamber_04/test_chamber_04.yaml index ea758c8..200a67b 100644 --- a/assets/test_chambers/test_chamber_04/test_chamber_04.yaml +++ b/assets/test_chambers/test_chamber_04/test_chamber_04.yaml @@ -1,8 +1,8 @@ cutscenes: SUCCESS: - - q_sound SOUNDS_04_PART1_SUCCESS_1 CH_GLADOS SUBTITLE_04_PART1_SUCCESS_1 + - q_sound SOUNDS_04_PART1_SUCCESS_1 CH_GLADOS PORTAL_04_PART1_SUCCESS_1 INTRO_CUTSCENE: - - q_sound SOUNDS_04_PART1_ENTRY_1 CH_GLADOS SUBTITLE_04_PART1_ENTRY_1 + - q_sound SOUNDS_04_PART1_ENTRY_1 CH_GLADOS PORTAL_04_PART1_ENTRY_1 DROWN_PLAYER: - kill_player water OPEN_PORTAL: diff --git a/assets/test_chambers/test_chamber_05/test_chamber_05.yaml b/assets/test_chambers/test_chamber_05/test_chamber_05.yaml index 6eb1ea8..71b2db6 100644 --- a/assets/test_chambers/test_chamber_05/test_chamber_05.yaml +++ b/assets/test_chambers/test_chamber_05/test_chamber_05.yaml @@ -4,20 +4,20 @@ cutscenes: INTRO_CUTSCENE: - set_signal cube_dropper - open_portal portal_0 0 - - q_sound SOUNDS_05_PART1_ENTRY_1 CH_GLADOS SUBTITLE_05_PART1_ENTRY_1 - - q_sound SOUNDS_05_PART1_ENTRY_2 CH_GLADOS SUBTITLE_05_PART1_ENTRY_2 + - q_sound SOUNDS_05_PART1_ENTRY_1 CH_GLADOS PORTAL_05_PART1_ENTRY_1 + - q_sound SOUNDS_05_PART1_ENTRY_2 CH_GLADOS PORTAL_05_PART1_ENTRY_2 - start_cutscene NAG - wait_for_signal success - stop_cutscene NAG - - q_sound SOUNDS_05_PART1_SUCCESS_1 CH_GLADOS SUBTITLE_05_PART1_SUCCESS_1 + - q_sound SOUNDS_05_PART1_SUCCESS_1 CH_GLADOS PORTAL_05_PART1_SUCCESS_1 NAG: - delay 28 - - q_sound SOUNDS_05_PART1_NAG1_1 CH_GLADOS SUBTITLE_05_PART1_NAG1_1 + - q_sound SOUNDS_05_PART1_NAG1_1 CH_GLADOS PORTAL_05_PART1_NAG1_1 - delay 20 - - q_sound SOUNDS_05_PART1_NAG2_1 CH_GLADOS SUBTITLE_05_PART1_NAG2_1 + - q_sound SOUNDS_05_PART1_NAG2_1 CH_GLADOS PORTAL_05_PART1_NAG2_1 - delay 25 - - q_sound SOUNDS_05_PART1_NAG3_1 CH_GLADOS SUBTITLE_05_PART1_NAG3_1 + - q_sound SOUNDS_05_PART1_NAG3_1 CH_GLADOS PORTAL_05_PART1_NAG3_1 - delay 25.001 - - q_sound SOUNDS_05_PART1_NAG4_1 CH_GLADOS SUBTITLE_05_PART1_NAG4_1 + - q_sound SOUNDS_05_PART1_NAG4_1 CH_GLADOS PORTAL_05_PART1_NAG4_1 - delay 26 - - q_sound SOUNDS_05_PART1_NAG5_1 CH_GLADOS SUBTITLE_05_PART1_NAG5_1 \ No newline at end of file + - q_sound SOUNDS_05_PART1_NAG5_1 CH_GLADOS PORTAL_05_PART1_NAG5_1 \ No newline at end of file diff --git a/assets/test_chambers/test_chamber_06/test_chamber_06.yaml b/assets/test_chambers/test_chamber_06/test_chamber_06.yaml index 1e51c6c..55f93d8 100644 --- a/assets/test_chambers/test_chamber_06/test_chamber_06.yaml +++ b/assets/test_chambers/test_chamber_06/test_chamber_06.yaml @@ -8,16 +8,16 @@ cutscenes: - play_animation piston_top piston_top_0 - play_animation piston_bottom piston_bottom_0 FIRST_ROOM: - - q_sound SOUNDS_06_PART1_ENTRY_1 CH_GLADOS SUBTITLE_06_PART1_ENTRY_1 + - q_sound SOUNDS_06_PART1_ENTRY_1 CH_GLADOS PORTAL_06_PART1_ENTRY_1 - delay 4 - open_portal first_room_portal 0 OPEN_FIRST_DOOR: - set_signal room_0_exit SECOND_SUCCESS: - - q_sound SOUNDS_06_PART1_SUCCESS_2_1 CH_GLADOS SUBTITLE_06_PART1_SUCCESS_2_1 + - q_sound SOUNDS_06_PART1_SUCCESS_2_1 CH_GLADOS PORTAL_06_PART1_SUCCESS_2_1 - play_animation piston_top piston_top_1 FIRST_SUCCESS: - - q_sound SOUNDS_06_PART1_SUCCESS_1_1 CH_GLADOS SUBTITLE_06_PART1_SUCCESS_1_1 + - q_sound SOUNDS_06_PART1_SUCCESS_1_1 CH_GLADOS PORTAL_06_PART1_SUCCESS_1_1 SECOND_ROOM: - close_portal 0 - close_portal 1 diff --git a/assets/test_chambers/test_chamber_07/test_chamber_07.yaml b/assets/test_chambers/test_chamber_07/test_chamber_07.yaml index d6f2cc1..5fcfb2a 100644 --- a/assets/test_chambers/test_chamber_07/test_chamber_07.yaml +++ b/assets/test_chambers/test_chamber_07/test_chamber_07.yaml @@ -25,17 +25,17 @@ cutscenes: - start_cutscene portal_loop - start_cutscene platform_ferry - set_signal launch_ball - - q_sound 07_PART1_ENTRY_1 CH_GLADOS SUBTITLE_07_PART1_ENTRY_1 - - q_sound 07_PART1_ENTRY_2 CH_GLADOS SUBTITLE_07_PART1_ENTRY_2 - - q_sound 07_PART1_ENTRY_3 CH_GLADOS SUBTITLE_07_PART1_ENTRY_3 + - q_sound 07_PART1_ENTRY_1 CH_GLADOS PORTAL_07_PART1_ENTRY_1 + - q_sound 07_PART1_ENTRY_2 CH_GLADOS PORTAL_07_PART1_ENTRY_2 + - q_sound 07_PART1_ENTRY_3 CH_GLADOS PORTAL_07_PART1_ENTRY_3 TILT: - delay 4 - set_signal cube_dropper - play_animation tilt tilt START_1: - - q_sound 07_PART2_ENTRY_1 CH_GLADOS SUBTITLE_07_PART2_ENTRY_1 + - q_sound 07_PART2_ENTRY_1 CH_GLADOS PORTAL_07_PART2_ENTRY_1 - wait_for_signal weeeee - - q_sound 07_PART2_SUCCESS_1 CH_GLADOS SUBTITLE_07_PART2_SUCCESS_1 + - q_sound 07_PART2_SUCCESS_1 CH_GLADOS PORTAL_07_PART2_SUCCESS_1 portal_loop: - label loop_start - open_portal portal_0 0 @@ -60,12 +60,12 @@ cutscenes: - start_cutscene platform_loop - close_portal 0 - show_prompt CutscenePromptTypePortal0 - - q_sound 07_PART1_GET_DEVICE_COMPONENT_1 CH_GLADOS SUBTITLE_07_PART1_GET_DEVICE_COMPONENT_1 - - q_sound 07_PART1_GET_DEVICE_COMPONENT_2 CH_GLADOS SUBTITLE_07_PART1_GET_DEVICE_COMPONENT_2 - - q_sound 07_PART1_GET_DEVICE_COMPONENT_3 CH_GLADOS SUBTITLE_07_PART1_GET_DEVICE_COMPONENT_3 + - q_sound 07_PART1_GET_DEVICE_COMPONENT_1 CH_GLADOS PORTAL_07_PART1_GET_DEVICE_COMPONENT_1 + - q_sound 07_PART1_GET_DEVICE_COMPONENT_2 CH_GLADOS PORTAL_07_PART1_GET_DEVICE_COMPONENT_2 + - q_sound 07_PART1_GET_DEVICE_COMPONENT_3 CH_GLADOS PORTAL_07_PART1_GET_DEVICE_COMPONENT_3 - wait_for_signal trapped - - q_sound 07_PART1_TRAPPED_1 CH_GLADOS SUBTITLE_07_PART1_TRAPPED_1 - - q_sound 07_PART1_TRAPPED_2 CH_GLADOS SUBTITLE_07_PART1_TRAPPED_2 + - q_sound 07_PART1_TRAPPED_1 CH_GLADOS PORTAL_07_PART1_TRAPPED_1 + - q_sound 07_PART1_TRAPPED_2 CH_GLADOS PORTAL_07_PART1_TRAPPED_2 - wait_for_channel CH_GLADOS - set_signal trap_door PROMPT_SWITCH: diff --git a/resource/.gitignore b/resource/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/levels/level_definition.h b/src/levels/level_definition.h index c47246a..2b7f419 100644 --- a/src/levels/level_definition.h +++ b/src/levels/level_definition.h @@ -10,6 +10,7 @@ #include "../sk64/skelatool_clip.h" #include "../sk64/skelatool_armature.h" #include "../physics/collision_box.h" +#include "../audio/subtitles.h" #define NO_TRANSFORM_INDEX 0xFF @@ -63,68 +64,6 @@ enum CutscenePromptType { CutscenePromptTypeJump, }; -enum CutsceneSubtitleType { - CutsceneSubtitleTypeNone, - SUBTITLE_00_PART1_ENTRY_1, - SUBTITLE_00_PART1_ENTRY_2, - SUBTITLE_00_PART1_ENTRY_3, - SUBTITLE_00_PART1_ENTRY_4, - SUBTITLE_00_PART1_ENTRY_5, - SUBTITLE_00_PART1_ENTRY_6, - SUBTITLE_00_PART1_ENTRY_7, - SUBTITLE_00_PART1_SUCCESS_1, - SUBTITLE_00_PART1_SUCCESS_2, - SUBTITLE_00_PART1_SUCCESS_3, - SUBTITLE_00_PART2_ENTRY_1, - SUBTITLE_00_PART2_SUCCESS_1, - SUBTITLE_01_PART1_ENTRY_1, - SUBTITLE_01_PART1_ENTRY_2, - SUBTITLE_01_PART1_GET_PORTAL_GUN_1, - SUBTITLE_01_PART1_GET_PORTAL_GUN_2, - SUBTITLE_01_PART1_GET_PORTAL_GUN_3, - SUBTITLE_01_PART1_GET_PORTAL_GUN_4, - SUBTITLE_01_PART1_GET_PORTAL_GUN_5, - SUBTITLE_01_PART1_GET_PORTAL_GUN_6, - SUBTITLE_01_PART1_GET_PORTAL_GUN_7, - SUBTITLE_01_PART1_GET_PORTAL_GUN_8, - SUBTITLE_01_PART2_ENTRY_1, - SUBTITLE_01_PART2_SUCCESS_1, - SUBTITLE_02_PART1_ENTRY_1, - SUBTITLE_02_PART1_ENTRY_2, - SUBTITLE_02_PART1_SUCCESS_1, - SUBTITLE_02_PART1_SUCCESS_2, - SUBTITLE_ESCAPE_01_PART1_NAG01_1, - SUBTITLE_02_PART2_SUCCESS_1, - SUBTITLE_02_PART2_SUCCESS_2, - SUBTITLE_03_PART2_ENTRY_1, - SUBTITLE_03_PART2_PLATFORM_ACTIVATED_1, - SUBTITLE_03_PART1_ENTRY_1, - SUBTITLE_03_PART1_ENTRY_2, - SUBTITLE_03_PART1_SUCCESS_1, - SUBTITLE_04_PART1_SUCCESS_1, - SUBTITLE_04_PART1_ENTRY_1, - SUBTITLE_05_PART1_ENTRY_1, - SUBTITLE_05_PART1_ENTRY_2, - SUBTITLE_05_PART1_SUCCESS_1, - SUBTITLE_05_PART1_NAG1_1, - SUBTITLE_05_PART1_NAG2_1, - SUBTITLE_05_PART1_NAG3_1, - SUBTITLE_05_PART1_NAG4_1, - SUBTITLE_05_PART1_NAG5_1, - SUBTITLE_06_PART1_ENTRY_1, - SUBTITLE_06_PART1_SUCCESS_2_1, - SUBTITLE_06_PART1_SUCCESS_1_1, - SUBTITLE_07_PART1_ENTRY_1, - SUBTITLE_07_PART1_ENTRY_2, - SUBTITLE_07_PART1_ENTRY_3, - SUBTITLE_07_PART2_ENTRY_1, - SUBTITLE_07_PART2_SUCCESS_1, - SUBTITLE_07_PART1_GET_DEVICE_COMPONENT_1, - SUBTITLE_07_PART1_GET_DEVICE_COMPONENT_2, - SUBTITLE_07_PART1_GET_DEVICE_COMPONENT_3, - SUBTITLE_07_PART1_TRAPPED_1, - SUBTITLE_07_PART1_TRAPPED_2, -}; #define CH_NONE 0xFF diff --git a/src/menu/audio_options.c b/src/menu/audio_options.c index 740672e..f1fc282 100644 --- a/src/menu/audio_options.c +++ b/src/menu/audio_options.c @@ -4,6 +4,7 @@ #include "../savefile/savefile.h" #include "../font/dejavusans.h" #include "../audio/soundplayer.h" +#include "../audio/subtitles.h" #include "../build/assets/materials/ui.h" #include "../build/src/audio/clips.h" @@ -12,11 +13,57 @@ #define GAMEPLAY_HEIGHT 124 #define GAMEPLAY_X ((SCREEN_WD - GAMEPLAY_WIDTH) / 2) +#define SCROLL_TICKS NUM_SUBTITLE_LANGUAGES +#define SCROLL_INTERVALS (SCROLL_TICKS - 1) +#define FULL_SCROLL_TIME 2.0f +#define SCROLL_MULTIPLIER (int)(0xFFFF * FIXED_DELTA_TIME / (80 * FULL_SCROLL_TIME)) +#define SCROLL_CHUNK_SIZE (0x10000 / SCROLL_INTERVALS) + void audioOptionsInit(struct AudioOptions* audioOptions) { audioOptions->selectedItem = AudioOptionSubtitlesEnabled; - audioOptions->subtitlesEnabled = menuBuildCheckbox(&gDejaVuSansFont, "Closed Captions", GAMEPLAY_X + 8, GAMEPLAY_Y + 8); - audioOptions->subtitlesEnabled.checked = (gSaveData.controls.flags & ControlSaveSubtitlesEnabled) != 0; + if (NUM_SUBTITLE_LANGUAGES){ + audioOptions->subtitlesEnabled = menuBuildCheckbox(&gDejaVuSansFont, "Closed Captions", GAMEPLAY_X + 8, GAMEPLAY_Y + 8); + audioOptions->subtitlesEnabled.checked = (gSaveData.controls.flags & ControlSaveSubtitlesEnabled) != 0; + + audioOptions->subtitlesLanguageText = menuBuildText(&gDejaVuSansFont, "Captions Language: ", GAMEPLAY_X + 8, GAMEPLAY_Y + 28); + audioOptions->subtitlesLanguageDynamicText = menuBuildText(&gDejaVuSansFont, SubtitleLanguages[gSaveData.controls.subtitleLanguage], GAMEPLAY_X + 150, GAMEPLAY_Y + 28); + + audioOptions->subtitlesLanguage= menuBuildSlider(GAMEPLAY_X + 8, GAMEPLAY_Y + 48, 200, SCROLL_TICKS); + audioOptions->subtitles_language_temp = (0xFFFF/SCROLL_TICKS)* gSaveData.controls.subtitleLanguage; + audioOptions->subtitlesLanguage.value = (int)gSaveData.controls.subtitleLanguage * (0xFFFF/SCROLL_TICKS); + } +} + +void audioOptionsHandleSlider(unsigned short* settingValue, float* sliderValue) { + OSContPad* pad = controllersGetControllerData(0); + + int newValue = (int)*settingValue + pad->stick_x * SCROLL_MULTIPLIER; + + if (controllerGetButtonDown(0, A_BUTTON | R_JPAD)) { + if (newValue >= 0xFFFF && controllerGetButtonDown(0, A_BUTTON)) { + newValue = 0; + } else { + newValue = newValue + SCROLL_CHUNK_SIZE; + newValue = newValue - (newValue % SCROLL_CHUNK_SIZE); + } + } + + if (controllerGetButtonDown(0, L_JPAD)) { + newValue = newValue - 1; + newValue = newValue - (newValue % SCROLL_CHUNK_SIZE); + } + + if (newValue < 0) { + newValue = 0; + } + + if (newValue > 0xFFFF) { + newValue = 0xFFFF; + } + + *settingValue = newValue; + *sliderValue = (float)newValue / 0xFFFF; } enum MenuDirection audioOptionsUpdate(struct AudioOptions* audioOptions) { @@ -42,6 +89,7 @@ enum MenuDirection audioOptionsUpdate(struct AudioOptions* audioOptions) { } } + if (NUM_SUBTITLE_LANGUAGES){ switch (audioOptions->selectedItem) { case AudioOptionSubtitlesEnabled: if (controllerGetButtonDown(0, A_BUTTON)) { @@ -54,16 +102,32 @@ enum MenuDirection audioOptionsUpdate(struct AudioOptions* audioOptions) { gSaveData.controls.flags &= ~ControlSaveSubtitlesEnabled; } } - + break; + case AudioOptionSubtitlesLanguage: + audioOptionsHandleSlider(&audioOptions->subtitles_language_temp, &audioOptions->subtitlesLanguage.value); + int temp = (int)((audioOptions->subtitles_language_temp * (1.0f/0xFFFF) * NUM_SUBTITLE_LANGUAGES)); + temp = (int)minf(maxf(0.0, temp), NUM_SUBTITLE_LANGUAGES-1); + gSaveData.controls.subtitleLanguage = temp; + audioOptions->subtitlesLanguageDynamicText = menuBuildText(&gDejaVuSansFont, SubtitleLanguages[gSaveData.controls.subtitleLanguage], GAMEPLAY_X + 150, GAMEPLAY_Y + 28); break; } - - if ((controllerDir & ControllerDirectionLeft || controllerGetButtonDown(0, L_TRIG) || controllerGetButtonDown(0, Z_TRIG))) { - return MenuDirectionLeft; } - if ((controllerDir & ControllerDirectionRight || controllerGetButtonDown(0, R_TRIG))) { - return MenuDirectionRight; + if (audioOptions->selectedItem == AudioOptionSubtitlesLanguage){ + if ((controllerGetButtonDown(0, L_TRIG) || controllerGetButtonDown(0, Z_TRIG))) { + return MenuDirectionLeft; + } + if ((controllerGetButtonDown(0, R_TRIG))) { + return MenuDirectionRight; + } + } + else{ + if (controllerDir & ControllerDirectionLeft || controllerGetButtonDown(0, L_TRIG) || controllerGetButtonDown(0, Z_TRIG)) { + return MenuDirectionLeft; + } + if (controllerDir & ControllerDirectionRight || controllerGetButtonDown(0, R_TRIG)) { + return MenuDirectionRight; + } } return MenuDirectionStay; @@ -72,16 +136,31 @@ enum MenuDirection audioOptionsUpdate(struct AudioOptions* audioOptions) { void audioOptionsRender(struct AudioOptions* audioOptions, struct RenderState* renderState, struct GraphicsTask* task) { gSPDisplayList(renderState->dl++, ui_material_list[SOLID_ENV_INDEX]); - gSPDisplayList(renderState->dl++, audioOptions->subtitlesEnabled.outline); - renderState->dl = menuCheckboxRender(&audioOptions->subtitlesEnabled, renderState->dl); + if (NUM_SUBTITLE_LANGUAGES){ + gSPDisplayList(renderState->dl++, audioOptions->subtitlesEnabled.outline); + renderState->dl = menuCheckboxRender(&audioOptions->subtitlesEnabled, renderState->dl); + + gSPDisplayList(renderState->dl++, audioOptions->subtitlesLanguage.back); + renderState->dl = menuSliderRender(&audioOptions->subtitlesLanguage, renderState->dl); + } gSPDisplayList(renderState->dl++, ui_material_revert_list[SOLID_ENV_INDEX]); gSPDisplayList(renderState->dl++, ui_material_list[DEJAVU_SANS_INDEX]); - gDPPipeSync(renderState->dl++); - menuSetRenderColor(renderState, audioOptions->selectedItem == AudioOptionSubtitlesEnabled, &gSelectionGray, &gColorWhite); - gSPDisplayList(renderState->dl++, audioOptions->subtitlesEnabled.text); + if (NUM_SUBTITLE_LANGUAGES){ + gDPPipeSync(renderState->dl++); + menuSetRenderColor(renderState, audioOptions->selectedItem == AudioOptionSubtitlesEnabled, &gSelectionGray, &gColorWhite); + gSPDisplayList(renderState->dl++, audioOptions->subtitlesEnabled.text); + + gDPPipeSync(renderState->dl++); + menuSetRenderColor(renderState, audioOptions->selectedItem == AudioOptionSubtitlesLanguage, &gSelectionGray, &gColorWhite); + gSPDisplayList(renderState->dl++, audioOptions->subtitlesLanguageText); + + gDPPipeSync(renderState->dl++); + menuSetRenderColor(renderState, audioOptions->selectedItem == AudioOptionSubtitlesLanguage, &gSelectionGray, &gColorWhite); + gSPDisplayList(renderState->dl++, audioOptions->subtitlesLanguageDynamicText); + } gSPDisplayList(renderState->dl++, ui_material_revert_list[DEJAVU_SANS_INDEX]); } \ No newline at end of file diff --git a/src/menu/audio_options.h b/src/menu/audio_options.h index 2b5b8e8..45e34c0 100644 --- a/src/menu/audio_options.h +++ b/src/menu/audio_options.h @@ -6,12 +6,17 @@ enum AudioOption { AudioOptionSubtitlesEnabled, + AudioOptionSubtitlesLanguage, AudioOptionCount, }; struct AudioOptions { struct MenuCheckbox subtitlesEnabled; + struct MenuSlider subtitlesLanguage; + Gfx* subtitlesLanguageText; + Gfx* subtitlesLanguageDynamicText; + unsigned short subtitles_language_temp; short selectedItem; }; diff --git a/src/menu/controls.c b/src/menu/controls.c index 11c743a..b2019bd 100644 --- a/src/menu/controls.c +++ b/src/menu/controls.c @@ -461,8 +461,8 @@ void controlsMenuRender(struct ControlsMenu* controlsMenu, struct RenderState* r #define CONTROL_PROMPT_HEIGHT 24 #define CONTROL_PROMPT_PADDING 6 -#define SUBTITLE_LEFT_MARGIN 10 -#define SUBTITLE_BOTTOM_MARGIN 10 +#define SUBTITLE_SIDE_MARGIN 17 +#define SUBTITLE_BOTTOM_MARGIN 11 #define SUBTITLE_PADDING 5 @@ -521,22 +521,22 @@ void controlsRenderSubtitle(char* message, float opacity, struct RenderState* re opacityAsInt = 0; } - int textPositionX = (SUBTITLE_LEFT_MARGIN + SUBTITLE_PADDING); + int textPositionX = (SUBTITLE_SIDE_MARGIN + SUBTITLE_PADDING); int textPositionY = (SCREEN_HT - SUBTITLE_BOTTOM_MARGIN - SUBTITLE_PADDING) - size.y; gSPDisplayList(renderState->dl++, ui_material_list[SOLID_TRANSPARENT_OVERLAY_INDEX]); - gDPSetEnvColor(renderState->dl++, 0, 0, 0, opacityAsInt / 3); + gDPSetEnvColor(renderState->dl++, 0, 0, 0, opacityAsInt / 2); gDPFillRectangle( renderState->dl++, textPositionX - CONTROL_PROMPT_PADDING, textPositionY - CONTROL_PROMPT_PADDING, - textPositionX + size.x + CONTROL_PROMPT_PADDING, + SCREEN_WD - SUBTITLE_SIDE_MARGIN, SCREEN_HT - SUBTITLE_BOTTOM_MARGIN ); gSPDisplayList(renderState->dl++, ui_material_revert_list[SOLID_TRANSPARENT_OVERLAY_INDEX]); gSPDisplayList(renderState->dl++, ui_material_list[DEJAVU_SANS_INDEX]); - gDPSetEnvColor(renderState->dl++, 255, 60, 60, opacityAsInt); + gDPSetEnvColor(renderState->dl++, 255, 140, 155, opacityAsInt); renderState->dl = fontRender( &gDejaVuSansFont, message, diff --git a/src/savefile/savefile.c b/src/savefile/savefile.c index 7f9ca61..4b72764 100755 --- a/src/savefile/savefile.c +++ b/src/savefile/savefile.c @@ -95,6 +95,7 @@ void savefileNew() { gSaveData.controls.acceleration = 0x4000; gSaveData.controls.deadzone = 0x4000; gSaveData.controls.portalRenderDepth = 2; + gSaveData.controls.subtitleLanguage = 0; gSaveData.audio.soundVolume = 0xFF; gSaveData.audio.musicVolume = 0xFF; diff --git a/src/savefile/savefile.h b/src/savefile/savefile.h index 814049f..3bcc00f 100755 --- a/src/savefile/savefile.h +++ b/src/savefile/savefile.h @@ -3,6 +3,7 @@ #include "./checkpoint.h" #include "../controls/controller_actions.h" +#include "../audio/subtitles.h" #define SRAM_START_ADDR 0x08000000 #define SRAM_SIZE 0x8000 @@ -51,6 +52,7 @@ struct ControlSaveState { unsigned short acceleration; unsigned short deadzone; unsigned char portalRenderDepth; + int subtitleLanguage; }; struct AudioSettingsSaveState { diff --git a/src/scene/hud.c b/src/scene/hud.c index d7eabb9..0df6484 100644 --- a/src/scene/hud.c +++ b/src/scene/hud.c @@ -32,12 +32,13 @@ #define RETICLE_HEIGHT 16 #define PROMPT_FADE_TIME 2.0f +#define SUBTITLE_FADE_TIME 0.75f void hudInit(struct Hud* hud) { hud->promptType = CutscenePromptTypeNone; hud->promptOpacity = 0.0f; hud->subtitleOpacity = 0.0f; - + hud->chosenLanguage = gSaveData.controls.subtitleLanguage; hud->flags = 0; hud->resolvedPrompts = 0; hud->lastPortalIndexShot = -1; @@ -59,15 +60,17 @@ void hudUpdate(struct Hud* hud) { } } + hud->chosenLanguage = gSaveData.controls.subtitleLanguage; + float targetPromptOpacity = (hud->flags & HudFlagsShowingPrompt) ? 1.0 : 0.0f; - float targetSubtitleOpacity = (hud->flags & HudFlagsShowingSubtitle) ? 0.7: 0.0f; + float targetSubtitleOpacity = (hud->flags & HudFlagsShowingSubtitle) ? 0.85: 0.0f; if (targetPromptOpacity != hud->promptOpacity) { hud->promptOpacity = mathfMoveTowards(hud->promptOpacity, targetPromptOpacity, FIXED_DELTA_TIME / PROMPT_FADE_TIME); } if (targetSubtitleOpacity != hud->subtitleOpacity) { - hud->subtitleOpacity = mathfMoveTowards(hud->subtitleOpacity, targetSubtitleOpacity, FIXED_DELTA_TIME / PROMPT_FADE_TIME); + hud->subtitleOpacity = mathfMoveTowards(hud->subtitleOpacity, targetSubtitleOpacity, FIXED_DELTA_TIME / SUBTITLE_FADE_TIME); } if (targetPromptOpacity && (hud->resolvedPrompts & (1 << hud->promptType)) != 0) { @@ -125,71 +128,6 @@ char* gPromptText[] = { "TO JUMP", }; -char* gSubtitleText[] = { - "", - "Hello and, again, welcome to the Aperture Science \ncomputer-aided enrichment center.", - "We hope your brief detention in the relaxation \nvault has been a pleasant one.", - "Your specimen has been processed and we are now\nready to begin the test proper.", - "Before we start, however, keep in mind that \nalthough fun and learning are the primary goals of\nall enrichment center activities, serious\ninjuries may occur.", - "For your own safety and the safety of others, \nplease refrain from t-*bzzzzzt*", - "Por favor bordón de fallar. Muchos gracias de \nfallar gra-*bzzt*", - "Stand back. The portal will open in three, two, one.", - "Excellent. Please proceed into the chamberlock\nafter completing each test.", - "First, however, note the incandescent particle\nfield across the exit.", - "This Aperture Science Material Emancipation Grid\nwill vaporize any unauthorized equipment that\npasses through it - for instance, the Aperture\nScience Weighted Storage Cube.", - "Please place the Weighted Storage Cube on the \nFifteen Hundred Megawatt Aperture Science Heavy \nDuty Super-Colliding Super Button.", - "Perfect. Please move quickly to the chamberlock,\nas the effects of prolonged exposure to the \nbutton are not part of this test.", - "You're doing very well!", - "Please be advised that a noticeable taste of blood\nis not part of any test protocol but is an \nunintended side effect of the Aperture Science \nMaterial Emancipation Grill, which may,\nin semi-rare cases, emancipate dental \nfillings, crowns, tooth enamel and teeth.", - "Very good! You are now in possession of the \nAperture Science Handheld Portal Device.", - "With it, you can create your own portals.", - "These intra dimensional gates have proven to \nbe completely safe.", - "The device, however, has not.", - "Do not touch the operational end of the device.", - "Do not look directly at the operational end of the\ndevice.", - "Do not submerge the device in liquid, even partially.", - "Most importantly, under no circumstances should \nyou-*bzzzpt*", - "Please proceed to the chamberlock. Mind the gap.", - "Well done! Remember: The Aperture Science Bring \nYour Daughter to Work Day is the perfect time to\nhave her tested.", - "Welcome to test chamber four.", - "You're doing quite well.", - "Once again, excellent work.", - "As part of a required test protocol, we will not \nmonitor the next test chamber. You will be \nentirely on your own.Good luck.", - "You're not a good person. You know that, right?", - "As part of a required test protocol, our previous\nstatement suggesting that we would not monitor \nthis chamber was an outright fabrication.", - "Good job! As part of a required test protocol, we\nwill stop enhancing the truth in three, two, o-\n*bzzt*", - "Warning devices are required on all mobile \nequipment. However, alarms and flashing hazard lights \nhave been found to agitate the high energy pellet\nand have therefore been disabled for \nyour safety.", - "Good. Now use the Aperture Science Unstationary\nScaffold to reach the chamberlock.", - "While safety is one of many Enrichment Center goals,\nthe Aperture Science High Energy Pellet, seen \nto the left of the chamber, can and has \ncaused permanent disabilities such as \nvaporization.", - "Please be careful.", - "Unbelievable! You, Subject Name Here, must be\nthe pride of Subject Hometown Here.", - "Very impressive. Please note that any appearance of\ndanger is merely a device to enhance your \ntesting experience.", - "Please note that we have added a consequence for \nfailure. Any contact with the chamber floor will \nresult in an 'unsatisfactory' mark on your \nofficial testing record followed by \ndeath. Good luck!", - "The Enrichment Center regrets to inform you that\nthis next test is impossible.", - "Make no attempt to solve it.", - "Fantastic! You remained resolute and resourceful in\nan atmosphere of extreme pessimism.", - "The Enrichment Center apologizes for this clearly \nbroken test chamber.", - "Once again, the Enrichment Center offers its most \nsincere apologies on the occasion of this \nunsolvable test environment.", - "Frankly, this chamber was a mistake. If we were you,\nwe would quit now.", - "No one will blame you for giving up. In fact, quitting\nat this point is a perfectly reasonable \nresponse.", - "Quit now and cake will be served immediately.", - "Hello again. To reiterate our previous warning: This\ntest (garbled) -ard momentum.", - "Momentum, a function of mass and velocity, is\nconserved between portals. In layman's terms: speedy\nthing goes in, speedy thing comes out.", - "Spectacular. You appear to understand how a portal\naffects forward momentum, or to be more precise,\nhow it does not.", - "The Enrichment Center promises to always provide a\nsafe testing environment.", - "In dangerous testing environments, the Enrichment \nCenter promises to always provide useful advice.", - "For instance, the floor here will kill you - try to \navoid it.", - "Get ot ydaer f-f-fling yourself. F-Fling into sp\n-*bzzt*", - "Weeeeeeeeeeeeeeeeeeeeee-*bzzt*", - "The device has been modified so that it can now \nmanufacture two linked portals at once.", - "As part of an optional test protocol, we are pleased\nto present an amusing fact:", - "The device is now more valuable than the organs \nand combined incomes of everyone in Subject \nHometown Here.", - "Through no fault of the Enrichment Center, you \nhave managed to trap yourself in this room.", - "An escape hatch will open in three, two, one." -}; - - - void hudShowActionPrompt(struct Hud* hud, enum CutscenePromptType promptType) { if (promptType == CutscenePromptTypeNone) { hud->flags &= ~HudFlagsShowingPrompt; @@ -200,8 +138,8 @@ void hudShowActionPrompt(struct Hud* hud, enum CutscenePromptType promptType) { hud->promptType = promptType; } -void hudShowSubtitle(struct Hud* hud, enum CutsceneSubtitleType subtitleType) { - if (subtitleType == CutsceneSubtitleTypeNone) { +void hudShowSubtitle(struct Hud* hud, enum SubtitleKey subtitleType) { + if (subtitleType == SubtitleKeyNone) { hud->flags &= ~HudFlagsShowingSubtitle; return; } @@ -334,6 +272,6 @@ void hudRender(struct Hud* hud, struct Player* player, struct RenderState* rende } if (hud->subtitleOpacity > 0.0f && gSaveData.controls.flags & ControlSaveSubtitlesEnabled) { - controlsRenderSubtitle(gSubtitleText[hud->subtitleType], hud->subtitleOpacity, renderState); + controlsRenderSubtitle(SubtitleLanguageValues[hud->chosenLanguage][hud->subtitleType], hud->subtitleOpacity, renderState); } } \ No newline at end of file diff --git a/src/scene/hud.h b/src/scene/hud.h index cc57766..064b3e1 100644 --- a/src/scene/hud.h +++ b/src/scene/hud.h @@ -4,6 +4,9 @@ #include "../graphics/renderstate.h" #include "../player/player.h" #include "../controls/controller_actions.h" +#include "../audio/subtitles.h" + + #define INTRO_BLACK_TIME 3.0f #define INTRO_FADE_TIME 1.0f @@ -17,8 +20,9 @@ enum HudFlags { }; struct Hud { + int chosenLanguage; enum CutscenePromptType promptType; - enum CutsceneSubtitleType subtitleType; + enum SubtitleKey subtitleType; float promptOpacity; float subtitleOpacity; @@ -38,7 +42,7 @@ void hudUpdatePortalIndicators(struct Hud* hud, struct Ray* raycastRay, struct void hudPortalFired(struct Hud* hud, int index); void hudShowActionPrompt(struct Hud* hud, enum CutscenePromptType promptType); void hudResolvePrompt(struct Hud* hud, enum CutscenePromptType promptType); -void hudShowSubtitle(struct Hud* hud, enum CutsceneSubtitleType subtitleType); +void hudShowSubtitle(struct Hud* hud, enum SubtitleKey subtitleType); void hudResolveSubtitle(struct Hud* hud); void hudRender(struct Hud* hud, struct Player* player, struct RenderState* renderState); diff --git a/src/scene/security_camera.c b/src/scene/security_camera.c index f7e4ada..9c23a2b 100644 --- a/src/scene/security_camera.c +++ b/src/scene/security_camera.c @@ -171,7 +171,7 @@ void securityCamerasCheckPortal(struct SecurityCamera* securityCameras, int came if (!cutsceneRunnerIsChannelPlaying(CH_GLADOS)) { short clipIndex = randomInRange(0, sizeof(gCameraDestroyClips) / sizeof(*gCameraDestroyClips)); - cutsceneQueueSoundInChannel(gCameraDestroyClips[clipIndex], 1.0f, CH_GLADOS, CutsceneSubtitleTypeNone); + cutsceneQueueSoundInChannel(gCameraDestroyClips[clipIndex], 1.0f, CH_GLADOS, SubtitleKeyNone); } } } diff --git a/src/scene/security_camera.h b/src/scene/security_camera.h index d5bd693..890de80 100644 --- a/src/scene/security_camera.h +++ b/src/scene/security_camera.h @@ -4,6 +4,7 @@ #include "../physics/collision_object.h" #include "../levels/level_definition.h" #include "../sk64/skelatool_armature.h" +#include "../audio/subtitles.h" struct SecurityCamera { struct CollisionObject collisionObject; diff --git a/tools/level_scripts/subtitle_generate.py b/tools/level_scripts/subtitle_generate.py new file mode 100644 index 0000000..29c081c --- /dev/null +++ b/tools/level_scripts/subtitle_generate.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +import os +import re + +def get_caption_keys_values_language(lines): + language = "English" + keys = [] + values = [] + + found_language = False + found_tokens = False + for line in lines: + if not found_language: + if not '"Language"' in line: + continue + found_language = True + line = line.replace('"', "") + key, val = line.split() + language = val + continue + if not found_tokens: + if not '"Tokens"' in line: + continue + found_tokens = True + continue + if ("{" in line) or ("}" in line) : + continue + keyval= line.split('"\t"') + if len(keyval) != 2: + keyval= line.split('" "') + if len(keyval) != 2: + continue + if "[english]" in keyval[0]: + continue + key = keyval[0].replace('"', "").replace(".", "_").replace("-", "_").replace('\\', "_").replace('#', "").upper() + val = keyval[1].replace('"', "").replace("\n", "").replace("\\", "") + val = re.sub(r'\','',val) + val = re.sub(r'\','',val) + val = val.replace("", "") + val = re.sub(r'\','',val) + val = val.replace("", "") + newval = "" + last_space = 0 + addition = 0 + for i,ch in enumerate(val): + if (i%40 == 0) and (i != 0): + newval = newval[:last_space+addition] + '\\n' + newval[last_space+addition+1:] + addition += 1 + newval = newval + ch + else: + if ch == " ": + last_space = i + newval = newval + ch + keys.append(key) + values.append(newval) + + return keys, values, language + +def make_overall_subtitles_header(all_header_lines, languages_list): + header_lines = [] + header_lines.append("#ifndef __SUBTITLES_H__\n") + header_lines.append("#define __SUBTITLES_H__\n") + header_lines.append("\n") + header_lines.append(f"#define NUM_SUBTITLE_LANGUAGES {len(languages_list)}\n") + header_lines.append("\n") + header_lines.append("extern char* SubtitleLanguages[];\n") + header_lines.append("extern char* SubtitleLanguageValues[][508];\n") + header_lines.append("\n") + if len(languages_list) > 0: + header_lines.extend(all_header_lines) + else: + header_lines.append(f"enum SubtitleKey\n") + header_lines.append("{\n") + header_lines.append(' SubtitleKeyNone,\n') + header_lines.append("};\n") + header_lines.append("\n") + + header_lines.append("#endif") + + with open("src/audio/subtitles.h", "w") as f: + f.writelines(header_lines) + +def make_SubtitleKey_headerlines(keys): + header_lines = [] + header_lines.append("\n") + header_lines.append(f"enum SubtitleKey\n") + header_lines.append("{\n") + header_lines.append(' SubtitleKeyNone,\n') + for key in keys: + header_lines.append(f' {key},\n') + header_lines.append("};\n") + header_lines.append("\n") + return header_lines + +def make_SubtitleLanguageValues(values_list): + sourcefile_lines = [] + if len(values_list) <= 0: + sourcefile_lines.append("\n") + sourcefile_lines.append("char * SubtitleLanguageValues[][508] =\n") + sourcefile_lines.append("{\n") + sourcefile_lines.append(" {\n") + for val in range(508): + sourcefile_lines.append(f' "",\n') + sourcefile_lines.append(" },\n") + sourcefile_lines.append("};\n") + sourcefile_lines.append("\n") + return sourcefile_lines + sourcefile_lines.append("\n") + sourcefile_lines.append("char * SubtitleLanguageValues[][508] =\n") + sourcefile_lines.append("{\n") + for lang in values_list: + sourcefile_lines.append(" {\n") + sourcefile_lines.append(' "",\n') + for value in lang: + sourcefile_lines.append(f' "{value}",\n') + sourcefile_lines.append(" },\n") + sourcefile_lines.append("};\n") + sourcefile_lines.append("\n") + return sourcefile_lines + +def make_overall_subtitles_sourcefile(other_sourcefile_lines, language_list): + sourcefile_lines = [] + sourcefile_lines.append('#include "subtitles.h"\n') + + sourcefile_lines.append("\n") + sourcefile_lines.append("char* SubtitleLanguages[] =\n") + sourcefile_lines.append("{\n") + if len(language_list) <= 0: + sourcefile_lines.append(f' "",\n') + else: + for language in language_list: + sourcefile_lines.append(f' "{language.upper()}",\n') + sourcefile_lines.append("};\n") + sourcefile_lines.append("\n") + sourcefile_lines.extend(other_sourcefile_lines) + + with open("src/audio/subtitles.c", "w") as f: + f.writelines(sourcefile_lines) + +def process_all_closecaption_files(dir): + values_list = [] + header_lines = [] + sourcefile_lines = [] + language_list = [] + SubtitleKey_generated = False + for filename in os.listdir(dir): + if "closecaption_" not in filename: + continue + try: + filepath = os.path.join(dir, filename) + lines = [] + + with open(filepath, "r", encoding='cp1252') as f: + lines = f.readlines() + + new_lines = [] + for line in lines: + line = line.replace("\x00", "") + if "\n" != line: + new_lines.append(line) + + k,v,l = get_caption_keys_values_language(new_lines) + values_list.append(v) + if not SubtitleKey_generated: + header_lines = make_SubtitleKey_headerlines(k) + SubtitleKey_generated = True + language_list.append(l) + print(filename, " - PASSED") + except: + print(filename, " - FAILED") + continue + sourcefile_lines = make_SubtitleLanguageValues(values_list) + make_overall_subtitles_header(header_lines, language_list) + make_overall_subtitles_sourcefile(sourcefile_lines, language_list) + + + +process_all_closecaption_files("resource/") \ No newline at end of file