Merge pull request #275 from westonCoder/subtitles-dynamic-enhancements

Dynamic Captions Building and Enhancements
This commit is contained in:
lambertjamesd 2023-10-10 20:33:37 -06:00 committed by GitHub
commit b88d4d1192
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1715 additions and 1107 deletions

4
.gitignore vendored
View file

@ -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

View file

@ -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:

View file

@ -87,6 +87,12 @@ sudo apt install nodejs
<br />
You will need to add at least one of the following files from where Portal is installed to the folder `resource/`. You can add multiple languages if desired.
```
portal/resource/closecaption_<your desired language 1>.txt
portal/resource/closecaption_<your desired language 2>.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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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
- q_sound SOUNDS_05_PART1_NAG5_1 CH_GLADOS PORTAL_05_PART1_NAG5_1

View file

@ -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

View file

@ -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:

0
resource/.gitignore vendored Normal file
View file

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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 (int)maxf(NUM_SUBTITLE_LANGUAGES, 1)
#define SCROLL_INTERVALS (int)maxf((SCROLL_TICKS - 1), 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]);
}

View file

@ -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;
};

View file

@ -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
@ -510,33 +510,41 @@ void controlsRenderPrompt(enum ControllerAction action, char* message, float opa
gSPDisplayList(renderState->dl++, ui_material_revert_list[BUTTON_ICONS_INDEX]);
}
void controlsRenderSubtitle(char* message, float opacity, struct RenderState* renderState) {
void controlsRenderSubtitle(char* message, float textOpacity, float backgroundOpacity, struct RenderState* renderState) {
struct Vector2s16 size = fontMeasure(&gDejaVuSansFont, message);
int opacityAsInt = (int)(255 * opacity);
int textOpacityAsInt = (int)(255 * textOpacity);
if (opacityAsInt > 255) {
opacityAsInt = 255;
} else if (opacityAsInt < 0) {
opacityAsInt = 0;
if (textOpacityAsInt > 255) {
textOpacityAsInt = 255;
} else if (textOpacityAsInt < 0) {
textOpacityAsInt = 0;
}
int textPositionX = (SUBTITLE_LEFT_MARGIN + SUBTITLE_PADDING);
int backgroundOpacityAsInt = (int)(255 * backgroundOpacity);
if (backgroundOpacityAsInt > 255) {
backgroundOpacityAsInt = 255;
} else if (backgroundOpacityAsInt < 0) {
backgroundOpacityAsInt = 0;
}
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, backgroundOpacityAsInt);
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, textOpacityAsInt);
renderState->dl = fontRender(
&gDejaVuSansFont,
message,

View file

@ -42,6 +42,6 @@ enum MenuDirection controlsMenuUpdate(struct ControlsMenu* controlsMenu);
void controlsMenuRender(struct ControlsMenu* controlsMenu, struct RenderState* renderState, struct GraphicsTask* task);
void controlsRenderPrompt(enum ControllerAction action, char* message, float opacity, struct RenderState* renderState);
void controlsRenderSubtitle(char* message, float opacity, struct RenderState* renderState);
void controlsRenderSubtitle(char* message, float textOpacity, float backgroundOpacity, struct RenderState* renderState);
#endif

View file

@ -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;

View file

@ -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 {

View file

@ -32,12 +32,15 @@
#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->backgroundOpacity = 0.0f;
hud->subtitleFadeTime = 0.75;
hud->chosenLanguage = gSaveData.controls.subtitleLanguage;
hud->flags = 0;
hud->resolvedPrompts = 0;
hud->lastPortalIndexShot = -1;
@ -59,15 +62,28 @@ 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) && !(hud->flags & HudFlagsSubtitleQueued)) ? 0.85: 0.0f;
float targetBackgroundOpacity = (hud->flags & HudFlagsShowingSubtitle && !(hud->flags & HudFlagsSubtitleQueued)) ? 0.45: 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 / hud->subtitleFadeTime);
}
if ((hud->subtitleOpacity == 0.0f) && (hud->flags & HudFlagsSubtitleQueued)){
hud->flags &= ~HudFlagsSubtitleQueued;
hud->subtitleType = hud->queuedSubtitleType;
hud->queuedSubtitleType = SubtitleKeyNone;
}
if (targetBackgroundOpacity != hud->backgroundOpacity) {
hud->backgroundOpacity = mathfMoveTowards(hud->backgroundOpacity, targetBackgroundOpacity, FIXED_DELTA_TIME / hud->subtitleFadeTime);
}
if (targetPromptOpacity && (hud->resolvedPrompts & (1 << hud->promptType)) != 0) {
@ -125,71 +141,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, <B>Subject Name Here<B>, must be\nthe pride of <B>Subject Hometown Here<B>.",
"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 <B>Subject \nHometown Here<B>.",
"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,14 +151,30 @@ void hudShowActionPrompt(struct Hud* hud, enum CutscenePromptType promptType) {
hud->promptType = promptType;
}
void hudShowSubtitle(struct Hud* hud, enum CutsceneSubtitleType subtitleType) {
if (subtitleType == CutsceneSubtitleTypeNone) {
hud->flags &= ~HudFlagsShowingSubtitle;
void hudShowSubtitle(struct Hud* hud, enum SubtitleKey subtitleType) {
if (subtitleType == hud->subtitleType){
return;
}
hud->flags |= HudFlagsShowingSubtitle;
hud->subtitleType = subtitleType;
if (subtitleType == SubtitleKeyNone) {
hud->flags &= ~HudFlagsShowingSubtitle;
hud->flags &= ~HudFlagsSubtitleQueued;
hud->subtitleFadeTime = 0.75;
return;
}
if (hud->flags & HudFlagsShowingSubtitle){
hud->flags |= HudFlagsSubtitleQueued;
hud->queuedSubtitleType = subtitleType;
hud->subtitleFadeTime = 0.3;
}
else{
hud->flags |= HudFlagsShowingSubtitle;
hud->subtitleType = subtitleType;
hud->subtitleFadeTime = 0.75;
}
}
void hudResolvePrompt(struct Hud* hud, enum CutscenePromptType promptType) {
@ -334,6 +301,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, hud->backgroundOpacity, renderState);
}
}

View file

@ -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
@ -14,14 +17,19 @@ enum HudFlags {
HudFlagsLookedPortalable1 = (1 << 1),
HudFlagsShowingPrompt = (1 << 2),
HudFlagsShowingSubtitle= (1 << 3),
HudFlagsSubtitleQueued= (1 << 4),
};
struct Hud {
int chosenLanguage;
enum CutscenePromptType promptType;
enum CutsceneSubtitleType subtitleType;
enum SubtitleKey subtitleType;
enum SubtitleKey queuedSubtitleType;
float promptOpacity;
float subtitleOpacity;
float backgroundOpacity;
float subtitleFadeTime;
float fadeInTimer;
u16 flags;
@ -38,7 +46,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);

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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'\<clr.+\>','',val)
val = re.sub(r'\<norepeat.+\>','',val)
val = val.replace("<sfx>", "")
val = re.sub(r'\<len:.+\>','',val)
val = val.replace("<len>", "")
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/")