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