Merge branch 'lambertjamesd:master' into german-audio

This commit is contained in:
hackgrid 2023-10-08 12:19:46 +02:00 committed by GitHub
commit 7982bb408a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 462 additions and 97 deletions

View file

@ -17,7 +17,7 @@ $(SKELATOOL64):
skelatool64/setup_dependencies.sh
make -C skelatool64
OPTIMIZER := -Og
OPTIMIZER := -Os
LCDEFS := -DDEBUG -g -Isrc/ -I/usr/include/n64/nustd -Werror -Wall
N64LIB := -lultra_rom -lnustd
@ -113,6 +113,7 @@ src/models/sphere.h src/models/sphere_geo.inc.h: assets/fbx/Sphere.fbx
portal_pak_dir: vpk/portal_pak_dir.vpk
vpk -x portal_pak_dir vpk/portal_pak_dir.vpk
vpk -x portal_pak_dir vpk/hl2_sound_misc_dir.vpk
vpk -x portal_pak_dir vpk/hl2_misc_dir.vpk
TEXTURE_SCRIPTS = $(shell find assets/ -type f -name '*.ims')
TEXTURE_IMAGES = $(TEXTURE_SCRIPTS:assets/%.ims=portal_pak_modified/%.png) \
@ -276,6 +277,7 @@ build/src/menu/main_menu.o: build/src/audio/clips.h build/assets/materials/ui.h
build/src/menu/new_game_menu.o: build/src/audio/clips.h build/assets/materials/ui.h build/assets/materials/images.h build/assets/test_chambers/test_chamber_00/test_chamber_00.h
build/src/menu/options_menu.o: build/assets/materials/ui.h
build/src/menu/save_game_menu.o: build/src/audio/clips.h
build/src/scene/scene_animator.o: build/src/audio/clips.h
build/src/menu/savefile_list.o: build/assets/materials/ui.h build/src/audio/clips.h
build/src/player/player.o: build/assets/models/player/chell.h build/assets/materials/static.h
build/src/scene/ball_catcher.o: build/assets/models/props/combine_ball_catcher.h build/assets/materials/static.h build/assets/models/dynamic_animated_model_list.h
@ -387,6 +389,10 @@ $(INS_SOUNDS): portal_pak_dir
portal_pak_dir/sound/music/%.wav: portal_pak_dir/sound/music/%.mp3
build/assets/sound/vehicles/tank_turret_loop1.wav: portal_pak_dir
@mkdir -p $(@D)
sox portal_pak_dir/sound/vehicles/tank_turret_loop1.wav -b 16 $@
build/%.aifc: %.sox portal_pak_dir
@mkdir -p $(@D)
sox $(<:assets/%.sox=portal_pak_dir/%.wav) $(shell cat $<) $(@:%.aifc=%.wav)
@ -403,7 +409,7 @@ build/%.aifc: %.msox portal_pak_dir
sox $(<:assets/%.msox=portal_pak_dir/%.wav) $(shell cat $<) $(@:%.aifc=%.wav)
$(SFZ2N64) -o $@ $(@:%.aifc=%.wav)
build/assets/sound/sounds.sounds build/assets/sound/sounds.sounds.tbl: $(SOUND_CLIPS)
build/assets/sound/sounds.sounds build/assets/sound/sounds.sounds.tbl: $(SOUND_CLIPS) build/assets/sound/vehicles/tank_turret_loop1.wav
@mkdir -p $(@D)
$(SFZ2N64) -o $@ $^

View file

@ -154,10 +154,10 @@ That will generate the rom at `/build/portal64.z64`
## Current New Feature TODO List
- [ ] rumble pak support?
- [ ] Investigate crash after falling into death water on test chamber 8
- [ ] Add auto save checkpoints
- [ ] Correct elevator timing
- [ ] pausing while glados is speaking can end her speech early
- [x] Investigate crash after falling into death water on test chamber 8
- [x] wake up objects after opening a portal under them
- [x] button prompts
- [x] investigate no_portals surface under portal pedestal

Binary file not shown.

View file

@ -0,0 +1 @@
-c 1 -r 22050

View file

@ -0,0 +1,15 @@
envelope Envelope {
attackTime = 0
attackVolume = 127
decayTime = -1
decayVolume = 127
releaseTime = 0
}
sound Sound {
use("../../../build/assets/sound/vehicles/tank_turret_loop1.wav")
loopCount = -1
loopEnd = -1
pan=64
envelope = Envelope
}

View file

@ -0,0 +1 @@
-c 1 -r 22050 -b 16

View file

@ -12,13 +12,13 @@ cutscenes:
- delay 2
- show_prompt CutscenePromptTypeJump
- delay 3
- q_sound SOUNDS_00_PART1_ENTRY_1 CH_GLADOS
- q_sound SOUNDS_00_PART1_ENTRY_2 CH_GLADOS
- q_sound SOUNDS_00_PART1_ENTRY_3 CH_GLADOS
- q_sound SOUNDS_00_PART1_ENTRY_4 CH_GLADOS
- q_sound SOUNDS_00_PART1_ENTRY_5 CH_GLADOS
- q_sound SOUNDS_00_PART1_ENTRY_6 CH_GLADOS
- q_sound SOUNDS_00_PART1_ENTRY_7 CH_GLADOS
- 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
- 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
- q_sound SOUNDS_00_PART1_SUCCESS_2 CH_GLADOS
- q_sound SOUNDS_00_PART1_SUCCESS_3 CH_GLADOS
- 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
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
- q_sound SOUNDS_00_PART2_ENTRY_1 CH_GLADOS SUBTITLE_00_PART2_ENTRY_1
- wait_for_signal success_1
- q_sound SOUNDS_00_PART2_SUCCESS_1 CH_GLADOS
- q_sound SOUNDS_00_PART2_SUCCESS_1 CH_GLADOS SUBTITLE_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
- q_sound SOUNDS_01_PART1_ENTRY_2 CH_GLADOS
- 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
- 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
- q_sound 01_PART1_GET_PORTAL_GUN_2 CH_GLADOS
- q_sound 01_PART1_GET_PORTAL_GUN_3 CH_GLADOS
- q_sound 01_PART1_GET_PORTAL_GUN_4 CH_GLADOS
- q_sound 01_PART1_GET_PORTAL_GUN_5 CH_GLADOS
- q_sound 01_PART1_GET_PORTAL_GUN_6 CH_GLADOS
- q_sound 01_PART1_GET_PORTAL_GUN_7 CH_GLADOS
- q_sound 01_PART1_GET_PORTAL_GUN_8 CH_GLADOS
- 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
MIND_THE_GAP:
- open_portal portal_gap 0
- q_sound SOUNDS_01_PART2_ENTRY_1 CH_GLADOS
- q_sound SOUNDS_01_PART2_ENTRY_1 CH_GLADOS SUBTITLE_01_PART2_ENTRY_1
FINISH_03:
- q_sound SOUNDS_01_PART2_SUCCESS_1 CH_GLADOS
- q_sound SOUNDS_01_PART2_SUCCESS_1 CH_GLADOS SUBTITLE_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
- q_sound SOUNDS_02_PART1_ENTRY_1 CH_GLADOS SUBTITLE_02_PART1_ENTRY_1
- wait_for_channel CH_GLADOS
- open_portal portal_0 0
- q_sound SOUNDS_02_PART1_ENTRY_2 CH_GLADOS
- q_sound SOUNDS_02_PART1_ENTRY_2 CH_GLADOS SUBTITLE_02_PART1_ENTRY_2
- wait_for_signal success
- q_sound SOUNDS_02_PART1_SUCCESS_1 CH_GLADOS
- q_sound SOUNDS_02_PART1_SUCCESS_2 CH_GLADOS
- 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
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
- q_sound SOUNDS_ESCAPE_01_PART1_NAG01_1 CH_GLADOS SUBTITLE_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
- q_sound SOUNDS_02_PART2_SUCCESS_2 CH_GLADOS
- 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
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
- q_sound SOUNDS_03_PART2_ENTRY_1 CH_GLADOS SUBTITLE_03_PART2_ENTRY_1
- wait_for_signal horizontal_activiate 2
- q_sound 03_PART2_PLATFORM_ACTIVATED_1 CH_GLADOS
- q_sound 03_PART2_PLATFORM_ACTIVATED_1 CH_GLADOS SUBTITLE_03_PART2_PLATFORM_ACTIVATED_1
INTRO:
- save_checkpoint 0
- open_portal ground_portal 0
- q_sound SOUNDS_03_PART1_ENTRY_1 CH_GLADOS
- q_sound SOUNDS_03_PART1_ENTRY_2 CH_GLADOS
- 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
- wait_for_signal exit_activate 2
- q_sound SOUNDS_03_PART1_SUCCESS_1 CH_GLADOS
- q_sound SOUNDS_03_PART1_SUCCESS_1 CH_GLADOS SUBTITLE_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
- q_sound SOUNDS_04_PART1_SUCCESS_1 CH_GLADOS SUBTITLE_04_PART1_SUCCESS_1
INTRO_CUTSCENE:
- q_sound SOUNDS_04_PART1_ENTRY_1 CH_GLADOS
- q_sound SOUNDS_04_PART1_ENTRY_1 CH_GLADOS SUBTITLE_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
- q_sound SOUNDS_05_PART1_ENTRY_2 CH_GLADOS
- 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
- start_cutscene NAG
- wait_for_signal success
- stop_cutscene NAG
- q_sound SOUNDS_05_PART1_SUCCESS_1 CH_GLADOS
- q_sound SOUNDS_05_PART1_SUCCESS_1 CH_GLADOS SUBTITLE_05_PART1_SUCCESS_1
NAG:
- delay 28
- q_sound SOUNDS_05_PART1_NAG1_1 CH_GLADOS
- q_sound SOUNDS_05_PART1_NAG1_1 CH_GLADOS SUBTITLE_05_PART1_NAG1_1
- delay 20
- q_sound SOUNDS_05_PART1_NAG2_1 CH_GLADOS
- q_sound SOUNDS_05_PART1_NAG2_1 CH_GLADOS SUBTITLE_05_PART1_NAG2_1
- delay 25
- q_sound SOUNDS_05_PART1_NAG3_1 CH_GLADOS
- q_sound SOUNDS_05_PART1_NAG3_1 CH_GLADOS SUBTITLE_05_PART1_NAG3_1
- delay 25.001
- q_sound SOUNDS_05_PART1_NAG4_1 CH_GLADOS
- q_sound SOUNDS_05_PART1_NAG4_1 CH_GLADOS SUBTITLE_05_PART1_NAG4_1
- delay 26
- q_sound SOUNDS_05_PART1_NAG5_1 CH_GLADOS
- q_sound SOUNDS_05_PART1_NAG5_1 CH_GLADOS SUBTITLE_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
- q_sound SOUNDS_06_PART1_ENTRY_1 CH_GLADOS SUBTITLE_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
- q_sound SOUNDS_06_PART1_SUCCESS_2_1 CH_GLADOS SUBTITLE_06_PART1_SUCCESS_2_1
- play_animation piston_top piston_top_1
FIRST_SUCCESS:
- q_sound SOUNDS_06_PART1_SUCCESS_1_1 CH_GLADOS
- q_sound SOUNDS_06_PART1_SUCCESS_1_1 CH_GLADOS SUBTITLE_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
- q_sound 07_PART1_ENTRY_2 CH_GLADOS
- q_sound 07_PART1_ENTRY_3 CH_GLADOS
- 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
TILT:
- delay 4
- set_signal cube_dropper
- play_animation tilt tilt
START_1:
- q_sound 07_PART2_ENTRY_1 CH_GLADOS
- q_sound 07_PART2_ENTRY_1 CH_GLADOS SUBTITLE_07_PART2_ENTRY_1
- wait_for_signal weeeee
- q_sound 07_PART2_SUCCESS_1 CH_GLADOS
- q_sound 07_PART2_SUCCESS_1 CH_GLADOS SUBTITLE_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
- q_sound 07_PART1_GET_DEVICE_COMPONENT_2 CH_GLADOS
- q_sound 07_PART1_GET_DEVICE_COMPONENT_3 CH_GLADOS
- 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
- wait_for_signal trapped
- q_sound 07_PART1_TRAPPED_1 CH_GLADOS
- q_sound 07_PART1_TRAPPED_2 CH_GLADOS
- 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
- wait_for_channel CH_GLADOS
- set_signal trap_door
PROMPT_SWITCH:

View file

@ -3,13 +3,11 @@
#include "../../build/src/audio/clips.h"
unsigned short soundsSkippable[10] = {
unsigned short soundsSkippable[8] = {
SOUNDS_PORTAL_ENTER1,
SOUNDS_PORTAL_ENTER2,
SOUNDS_PORTAL_EXIT1,
SOUNDS_PORTAL_EXIT2,
SOUNDS_PORTALGUN_SHOOT_RED1,
SOUNDS_PORTALGUN_SHOOT_BLUE1,
SOUNDS_CONCRETE1, //left foot
SOUNDS_CONCRETE2, //right foot
SOUNDS_CONCRETE3, //land

View file

@ -1,7 +1,7 @@
#ifndef __AUDIO_CLIPS_H__
#define __AUDIO_CLIPS_H__
extern unsigned short soundsSkippable[10];
extern unsigned short soundsSkippable[8];
extern unsigned short soundsPortalEnter[2];
extern unsigned short soundsPortalExit[2];

View file

@ -42,10 +42,13 @@ unsigned short gActionSourceButtonMask[ControllerActionSourceCount] = {
};
int gActionState = 0;
int gMutedActions = 0;
struct Vector2 gDirections[2];
#define ACTION_TO_BITMASK(action) (1 << (action))
void controllerActionApply(enum ControllerAction action) {
gActionState |= (1 << action);
gActionState |= ACTION_TO_BITMASK(action);
}
#define DEADZONE_SIZE 5
@ -126,6 +129,8 @@ void controllerActionRead() {
gDirections[0] = gZeroVec2;
gDirections[1] = gZeroVec2;
int nextMutedState = 0;
for (int controllerIndex = 0; controllerIndex < 2; ++controllerIndex) {
for (int sourceIndex = 0; sourceIndex < ControllerActionSourceCount; ++sourceIndex) {
enum ControllerAction action = gSaveData.controls.controllerSettings[controllerIndex][sourceIndex];
@ -136,6 +141,12 @@ void controllerActionRead() {
if (sourceIndex == ControllerActionSourceCUpButton || sourceIndex == ControllerActionSourceDUpButton) {
sourceIndex += 3;
}
} else if (IS_HOLDABLE_ACTION(action) && controllerGetButton(controllerIndex, gActionSourceButtonMask[sourceIndex])) {
if (ACTION_TO_BITMASK(action) & gMutedActions) {
nextMutedState |= ACTION_TO_BITMASK(action);
} else {
controllerActionApply(action);
}
} else if (controllerGetButtonDown(controllerIndex, gActionSourceButtonMask[sourceIndex])) {
controllerActionApply(action);
}
@ -146,6 +157,8 @@ void controllerActionRead() {
gDirections[i].x = clampf(gDirections[i].x, -1.0f, 1.0f);
gDirections[i].y = clampf(gDirections[i].y, -1.0f, 1.0f);
}
gMutedActions = nextMutedState;
}
struct Vector2 controllerDirectionGet(enum ControllerAction direction) {
@ -160,6 +173,10 @@ int controllerActionGet(enum ControllerAction action) {
return (gActionState & (1 << action)) != 0;
}
void controllerActionMuteActive() {
gMutedActions = gActionState;
}
int controllerSourcesForAction(enum ControllerAction action, struct ControllerSourceWithController* sources, int maxSources) {
int index = 0;

View file

@ -46,6 +46,7 @@ enum ControllerAction {
};
#define IS_DIRECTION_ACTION(action) ((action) >= ControllerActionMove && (action) <= ControllerActionRotate)
#define IS_HOLDABLE_ACTION(action) ((action) >= ControllerActionOpenPortal0 && (action) <= ControllerActionOpenPortal1)
#define IS_VALID_SOURCE(source) ((source) >= 0 && (source) < ControllerActionSourceCount)
#define MAX_DEADZONE 0.25f
@ -60,6 +61,7 @@ void controllerActionRead();
void controllerSetDeadzone(float percent);
struct Vector2 controllerDirectionGet(enum ControllerAction direction);
int controllerActionGet(enum ControllerAction action);
void controllerActionMuteActive();
int controllerSourcesForAction(enum ControllerAction action, struct ControllerSourceWithController* sources, int maxSources);

View file

@ -18,6 +18,7 @@ u64 gTriggeredCutscenes;
struct QueuedSound {
struct QueuedSound* next;
u16 soundId;
u16 subtitleId;
float volume;
};
@ -94,7 +95,7 @@ void cutsceneRunnerCancel(struct CutsceneRunner* runner) {
runner->currentCutscene = NULL;
}
void cutsceneQueueSound(int soundId, float volume, int channel) {
void cutsceneQueueSound(int soundId, float volume, int channel, int subtitleId) {
struct QueuedSound* next = gCutsceneNextFreeSound;
if (!next) {
@ -106,6 +107,7 @@ void cutsceneQueueSound(int soundId, float volume, int channel) {
next->next = NULL;
next->soundId = soundId;
next->volume = volume;
next->subtitleId = subtitleId;
struct QueuedSound* tail = gCutsceneSoundQueues[channel];
@ -140,7 +142,7 @@ void cutsceneRunnerStartStep(struct CutsceneRunner* runner) {
break;
case CutsceneStepTypeQueueSound:
{
cutsceneQueueSoundInChannel(step->queueSound.soundId, step->queueSound.volume * (1.0f / 255.0f), step->queueSound.channel);
cutsceneQueueSoundInChannel(step->queueSound.soundId, step->queueSound.volume * (1.0f / 255.0f), step->queueSound.channel, step->queueSound.subtitleId);
break;
}
case CutsceneStepTypeDelay:
@ -395,6 +397,8 @@ void cutscenesUpdateSounds() {
struct QueuedSound* curr = gCutsceneSoundQueues[i];
gCutsceneCurrentSound[i] = soundPlayerPlay(curr->soundId, curr->volume, gCutsceneChannelPitch[i], NULL, NULL);
hudShowSubtitle(&gScene.hud, curr->subtitleId);
gCutsceneSoundQueues[i] = curr->next;
curr->next = gCutsceneNextFreeSound;
@ -402,6 +406,7 @@ void cutscenesUpdateSounds() {
} else {
if (gCutsceneCurrentSound[i] != SOUND_ID_NONE) {
soundPlayerPlay(soundsIntercom[1], 1.0f, gCutsceneChannelPitch[i], NULL, NULL);
hudResolveSubtitle(&gScene.hud);
}
gCutsceneCurrentSound[i] = SOUND_ID_NONE;
@ -564,19 +569,22 @@ void cutsceneSerializeRead(struct Serializer* serializer) {
s16 nextId;
serializeRead(serializer, &nextId, sizeof(nextId));
while (nextId != SOUND_ID_NONE) {
s16 nextSubtitleId;
serializeRead(serializer, &nextSubtitleId, sizeof(nextSubtitleId));
u8 volume;
serializeRead(serializer, &volume, sizeof(volume));
cutsceneQueueSound(nextId, volume * (1.0f / 255.0f), i);
cutsceneQueueSound(nextId, volume * (1.0f / 255.0f), i, nextSubtitleId);
serializeRead(serializer, &nextId, sizeof(nextId));
}
}
}
void cutsceneQueueSoundInChannel(int soundId, float volume, int channel) {
void cutsceneQueueSoundInChannel(int soundId, float volume, int channel, int subtitleId) {
if (!gCutsceneSoundQueues[channel] && !soundPlayerIsPlaying(gCutsceneCurrentSound[channel])) {
cutsceneQueueSound(soundsIntercom[0], volume, channel);
cutsceneQueueSound(soundsIntercom[0], volume, channel, subtitleId);
}
cutsceneQueueSound(soundId, volume, channel);
cutsceneQueueSound(soundId, volume, channel, subtitleId);
}

View file

@ -43,6 +43,6 @@ void cutsceneSerializeWrite(struct Serializer* serializer, SerializeAction actio
void cutsceneSerializeRead(struct Serializer* serializer);
int cutsceneRunnerIsChannelPlaying(int channel);
void cutsceneQueueSoundInChannel(int soundId, float volume, int channel);
void cutsceneQueueSoundInChannel(int soundId, float volume, int channel, int subtitleId);
#endif

View file

@ -63,6 +63,70 @@ 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
#define CH_GLADOS 0
@ -80,6 +144,7 @@ struct CutsceneStep {
struct {
u16 soundId;
u8 channel;
u16 subtitleId;
u8 volume;
} queueSound;
struct {
@ -241,6 +306,7 @@ enum AnimationSoundType {
AnimationSoundTypeNone,
AnimationSoundTypeLightRail,
AnimationSoundTypePiston,
AnimationSoundTypeArm,
};
struct AnimationInfo {

View file

@ -235,6 +235,9 @@ static void gameProc(void* arg) {
levelLoadWithCallbacks(levelGetQueued());
cutsceneRunnerReset();
dynamicAssetsReset();
// if a portal fire button is being held
// don't fire portals until it is released
controllerActionMuteActive();
gSceneCallbacks->initCallback(gSceneCallbacks->data);
}

View file

@ -1,9 +1,22 @@
#include "audio_options.h"
#include "../controls/controller.h"
#include "../savefile/savefile.h"
#include "../font/dejavusans.h"
#include "../audio/soundplayer.h"
#include "../build/assets/materials/ui.h"
#include "../build/src/audio/clips.h"
#define GAMEPLAY_Y 54
#define GAMEPLAY_WIDTH 252
#define GAMEPLAY_HEIGHT 124
#define GAMEPLAY_X ((SCREEN_WD - GAMEPLAY_WIDTH) / 2)
void audioOptionsInit(struct AudioOptions* audioOptions) {
audioOptions->selectedItem = 0;
audioOptions->selectedItem = AudioOptionSubtitlesEnabled;
audioOptions->subtitlesEnabled = menuBuildCheckbox(&gDejaVuSansFont, "Closed Captions", GAMEPLAY_X + 8, GAMEPLAY_Y + 8);
audioOptions->subtitlesEnabled.checked = (gSaveData.controls.flags & ControlSaveSubtitlesEnabled) != 0;
}
enum MenuDirection audioOptionsUpdate(struct AudioOptions* audioOptions) {
@ -13,6 +26,38 @@ enum MenuDirection audioOptionsUpdate(struct AudioOptions* audioOptions) {
return MenuDirectionUp;
}
if (controllerDir & ControllerDirectionDown) {
++audioOptions->selectedItem;
if (audioOptions->selectedItem == AudioOptionCount) {
audioOptions->selectedItem = 0;
}
}
if (controllerDir & ControllerDirectionUp) {
if (audioOptions->selectedItem == 0) {
audioOptions->selectedItem = AudioOptionCount - 1;
} else {
--audioOptions->selectedItem;
}
}
switch (audioOptions->selectedItem) {
case AudioOptionSubtitlesEnabled:
if (controllerGetButtonDown(0, A_BUTTON)) {
audioOptions->subtitlesEnabled.checked = !audioOptions->subtitlesEnabled.checked;
soundPlayerPlay(SOUNDS_BUTTONCLICKRELEASE, 1.0f, 0.5f, NULL, NULL);
if (audioOptions->subtitlesEnabled.checked) {
gSaveData.controls.flags |= ControlSaveSubtitlesEnabled;
} else {
gSaveData.controls.flags &= ~ControlSaveSubtitlesEnabled;
}
}
break;
}
if ((controllerDir & ControllerDirectionLeft || controllerGetButtonDown(0, L_TRIG) || controllerGetButtonDown(0, Z_TRIG))) {
return MenuDirectionLeft;
}
@ -25,5 +70,18 @@ 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);
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);
gSPDisplayList(renderState->dl++, ui_material_revert_list[DEJAVU_SANS_INDEX]);
}

View file

@ -4,7 +4,14 @@
#include "./menu.h"
#include "../graphics/graphics.h"
enum AudioOption {
AudioOptionSubtitlesEnabled,
AudioOptionCount,
};
struct AudioOptions {
struct MenuCheckbox subtitlesEnabled;
short selectedItem;
};

View file

@ -461,6 +461,11 @@ 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_PADDING 5
void controlsRenderPrompt(enum ControllerAction action, char* message, float opacity, struct RenderState* renderState) {
struct Vector2s16 size = fontMeasure(&gDejaVuSansFont, message);
@ -504,3 +509,40 @@ void controlsRenderPrompt(enum ControllerAction action, char* message, float opa
renderState->dl = controlsRenderIcons(renderState->dl, action, textPositionX - CONTROL_PROMPT_PADDING, textPositionY);
gSPDisplayList(renderState->dl++, ui_material_revert_list[BUTTON_ICONS_INDEX]);
}
void controlsRenderSubtitle(char* message, float opacity, struct RenderState* renderState) {
struct Vector2s16 size = fontMeasure(&gDejaVuSansFont, message);
int opacityAsInt = (int)(255 * opacity);
if (opacityAsInt > 255) {
opacityAsInt = 255;
} else if (opacityAsInt < 0) {
opacityAsInt = 0;
}
int textPositionX = (SUBTITLE_LEFT_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);
gDPFillRectangle(
renderState->dl++,
textPositionX - CONTROL_PROMPT_PADDING,
textPositionY - CONTROL_PROMPT_PADDING,
textPositionX + size.x + CONTROL_PROMPT_PADDING,
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);
renderState->dl = fontRender(
&gDejaVuSansFont,
message,
textPositionX,
textPositionY,
renderState->dl
);
gSPDisplayList(renderState->dl++, ui_material_revert_list[DEJAVU_SANS_INDEX]);
}

View file

@ -42,5 +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);
#endif

View file

@ -38,6 +38,8 @@ enum ControlSaveFlags {
ControlSaveFlagsInvertYaw = (1 << 1),
ControlSaveTankControls = (1 << 2),
ControlSaveSubtitlesEnabled = (1 << 5),
ControlSaveMoveablePortals = (1 << 8),
ControlSaveWideScreen = (1 << 9),
};

View file

@ -115,7 +115,7 @@ void ballInit(struct Ball* ball, struct Vector3* position, struct Vector3* veloc
dynamicSceneSetRoomFlags(ball->dynamicId, ROOM_FLAG_FROM_INDEX(startingRoom));
ball->soundLoopId = soundPlayerPlay(soundsBallLoop, 1.0f, 1.0f, &ball->rigidBody.transform.position, &ball->rigidBody.velocity);
ball->soundLoopId = soundPlayerPlay(soundsBallLoop, 1.5f, 1.0f, &ball->rigidBody.transform.position, &ball->rigidBody.velocity);
}
void ballTurnOnCollision(struct Ball* ball) {
@ -134,7 +134,11 @@ void ballInitBurn(struct Ball* ball, struct ContactManifold* manifold) {
vector3Negate(&normal, &normal);
}
effectsSplashPlay(&gScene.effects, &gBallBounce, &ball->rigidBody.transform.position, &manifold->normal);
soundPlayerPlay(soundsBallBounce, 1.0f, 1.0f, &manifold->contacts[0].contactAWorld, &gZeroVec);
struct Vector3 position = manifold->contacts[0].contactAWorld;
if (manifold->shapeA->body) {
transformPoint(&manifold->shapeA->body->transform, &position, &position);
}
soundPlayerPlay(soundsBallBounce, 1.5f, 1.0f, &position, &gZeroVec);
ball->flags |= BallJustBounced;
}

View file

@ -6,6 +6,7 @@
#include "../util/time.h"
#include "../levels/levels.h"
#include "./scene.h"
#include "../savefile/savefile.h"
#define HUD_CENTER_WIDTH 6
#define HUD_CENTER_HEIGHT 8
@ -35,6 +36,7 @@
void hudInit(struct Hud* hud) {
hud->promptType = CutscenePromptTypeNone;
hud->promptOpacity = 0.0f;
hud->subtitleOpacity = 0.0f;
hud->flags = 0;
hud->resolvedPrompts = 0;
@ -58,14 +60,20 @@ void hudUpdate(struct Hud* hud) {
}
float targetPromptOpacity = (hud->flags & HudFlagsShowingPrompt) ? 1.0 : 0.0f;
float targetSubtitleOpacity = (hud->flags & HudFlagsShowingSubtitle) ? 0.7: 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);
}
if (targetPromptOpacity && (hud->resolvedPrompts & (1 << hud->promptType)) != 0) {
hudShowActionPrompt(hud, CutscenePromptTypeNone);
}
}
void hudUpdatePortalIndicators(struct Hud* hud, struct Ray* raycastRay, struct Vector3* playerUp) {
@ -117,6 +125,71 @@ 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;
@ -127,10 +200,24 @@ 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;
return;
}
hud->flags |= HudFlagsShowingSubtitle;
hud->subtitleType = subtitleType;
}
void hudResolvePrompt(struct Hud* hud, enum CutscenePromptType promptType) {
hud->resolvedPrompts |= (1 << promptType);
}
void hudResolveSubtitle(struct Hud* hud) {
hud->flags &= ~HudFlagsShowingSubtitle;
}
void hudRender(struct Hud* hud, struct Player* player, struct RenderState* renderState) {
if (player->flags & PlayerIsDead) {
gSPDisplayList(renderState->dl++, hud_death_overlay);
@ -245,4 +332,8 @@ void hudRender(struct Hud* hud, struct Player* player, struct RenderState* rende
if (hud->promptOpacity > 0.0f && hud->promptType != CutscenePromptTypeNone) {
controlsRenderPrompt(gPromptActions[hud->promptType], gPromptText[hud->promptType], hud->promptOpacity, renderState);
}
if (hud->subtitleOpacity > 0.0f && gSaveData.controls.flags & ControlSaveSubtitlesEnabled) {
controlsRenderSubtitle(gSubtitleText[hud->subtitleType], hud->subtitleOpacity, renderState);
}
}

View file

@ -13,11 +13,14 @@ enum HudFlags {
HudFlagsLookedPortalable0 = (1 << 0),
HudFlagsLookedPortalable1 = (1 << 1),
HudFlagsShowingPrompt = (1 << 2),
HudFlagsShowingSubtitle= (1 << 3),
};
struct Hud {
enum CutscenePromptType promptType;
enum CutsceneSubtitleType subtitleType;
float promptOpacity;
float subtitleOpacity;
float fadeInTimer;
@ -35,6 +38,8 @@ 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 hudResolveSubtitle(struct Hud* hud);
void hudRender(struct Hud* hud, struct Player* player, struct RenderState* renderState);

View file

@ -12,7 +12,7 @@
#include "../../build/assets/models/portal_gun/v_portalgun.h"
#include "../../build/assets/materials/static.h"
#define PORTAL_GUN_RECOIL_TIME (0.18f)
#define PORTAL_GUN_RECOIL_TIME (0.22f)
#define PORTAL_GUN_NEAR_PLANE 0.05f
@ -52,6 +52,7 @@ void portalGunInit(struct PortalGun* portalGun, struct Transform* at){
portalGun->rigidBody.angularVelocity = gZeroVec;
portalGun->portalGunVisible = 0;
portalGun->shootAnimationTimer = 0.0;
portalGun->shootTotalAnimationTimer = 0.0;
portalGunCalcTargetPosition(portalGun, at, &portalGun->rigidBody.transform.position, 0);
@ -180,6 +181,7 @@ void portalGunUpdate(struct PortalGun* portalGun, struct Player* player) {
if (player->flags & PlayerJustShotPortalGun && portalGun->shootAnimationTimer <= 0.0f) {
portalGun->shootAnimationTimer = PORTAL_GUN_RECOIL_TIME;
portalGun->shootTotalAnimationTimer = PORTAL_GUN_RECOIL_TIME * 2.0f;
}
if (portalGun->shootAnimationTimer >= 0.0f) {
@ -189,6 +191,12 @@ void portalGunUpdate(struct PortalGun* portalGun, struct Player* player) {
player->flags &= ~PlayerJustShotPortalGun;
}
}
if (portalGun->shootTotalAnimationTimer >= 0.0f) {
portalGun->shootTotalAnimationTimer -= FIXED_DELTA_TIME;
if (portalGun->shootTotalAnimationTimer <= 0.0f){
portalGun->shootTotalAnimationTimer = 0.0f;
}
}
for (int i = 0; i < 2; ++i) {
struct PortalGunProjectile* projectile = &portalGun->projectiles[i];
@ -259,3 +267,9 @@ void portalGunFire(struct PortalGun* portalGun, int portalIndex, struct Ray* ray
portalTrailPlay(&projectile->trail, &fireFrom, &hit.at);
}
int portalGunIsFiring(struct PortalGun* portalGun){
if (portalGun->shootTotalAnimationTimer > 0.0f){
return 1;
}
return 0;
}

View file

@ -31,6 +31,7 @@ struct PortalGun {
struct RigidBody rigidBody;
int portalGunVisible;
float shootAnimationTimer;
float shootTotalAnimationTimer;
struct PortalGunProjectile projectiles[2];
};
@ -41,5 +42,6 @@ void portalGunUpdate(struct PortalGun* portalGun, struct Player* player);
void portalGunRenderReal(struct PortalGun* portalGun, struct RenderState* renderState, struct Camera* fromCamera);
void portalGunFire(struct PortalGun* portalGun, int portalIndex, struct Ray* ray, struct Vector3* playerUp, int roomIndex);
int portalGunIsFiring(struct PortalGun* portalGun);
#endif

View file

@ -347,14 +347,14 @@ void sceneCheckPortals(struct Scene* scene) {
sceneFirePortal(scene, &scene->savedPortal.ray, &scene->savedPortal.transformUp, scene->savedPortal.portalIndex, scene->savedPortal.roomIndex, 0, 0);
}
if (fireOrange && hasOrange && !playerIsGrabbing(&scene->player)) {
if (fireOrange && !fireBlue && hasOrange && !playerIsGrabbing(&scene->player) && !portalGunIsFiring(&scene->portalGun)) {
portalGunFire(&scene->portalGun, 0, &raycastRay, &playerUp, scene->player.body.currentRoom);
scene->player.flags |= PlayerJustShotPortalGun;
hudPortalFired(&scene->hud, 0);
soundPlayerPlay(soundsPortalgunShoot[0], 1.0f, 1.0f, NULL, NULL);
}
if ((fireBlue || (!hasOrange && fireOrange)) && hasBlue && !playerIsGrabbing(&scene->player)) {
if (((fireBlue && !fireOrange) || (!hasOrange && fireOrange)) && hasBlue && !playerIsGrabbing(&scene->player) && !portalGunIsFiring(&scene->portalGun)) {
portalGunFire(&scene->portalGun, 1, &raycastRay, &playerUp, scene->player.body.currentRoom);
scene->player.flags |= PlayerJustShotPortalGun;
hudPortalFired(&scene->hud, 1);

View file

@ -9,14 +9,17 @@
#include "../build/src/audio/clips.h"
struct AnimatedAudioInfo {
short soundId;
short startSoundId;
short loopSoundId;
short endSoundId;
float pitch;
};
struct AnimatedAudioInfo gAnimatedAudioInfo[] = {
{.soundId = SOUND_ID_NONE},
{.soundId = SOUNDS_BEAM_PLATFORM_LOOP1, .pitch = 0.5f},
{.soundId = SOUNDS_BEAM_PLATFORM_LOOP1, .pitch = 0.5f},
{.startSoundId = SOUND_ID_NONE, .loopSoundId = SOUND_ID_NONE, .endSoundId = SOUND_ID_NONE},
{.startSoundId = SOUND_ID_NONE, .loopSoundId = SOUNDS_BEAM_PLATFORM_LOOP1, .endSoundId = SOUND_ID_NONE, .pitch = 0.5f},
{.startSoundId = SOUNDS_DOORMOVE1, .loopSoundId = SOUND_ID_NONE, .endSoundId = SOUND_ID_NONE, .pitch = 0.4f},
{.startSoundId = SOUNDS_TANK_TURRET_START1, .loopSoundId = SOUNDS_TANK_TURRET_LOOP1, .endSoundId = SOUNDS_ELEVATOR_STOP1, .pitch = 0.5f},
};
void sceneAnimatorInit(struct SceneAnimator* sceneAnimator, struct AnimationInfo* animationInfo, int animatorCount) {
@ -34,6 +37,7 @@ void sceneAnimatorInit(struct SceneAnimator* sceneAnimator, struct AnimationInfo
skAnimatorInit(&sceneAnimator->animators[i], animationInfo[i].armature.numberOfBones);
sceneAnimator->state[i].playbackSpeed = 1.0f;
sceneAnimator->state[i].soundId = SOUND_ID_NONE;
sceneAnimator->state[i].flags = 0;
vector3Scale(&sceneAnimator->armatures[i].pose[0].position, &sceneAnimator->state[i].lastPosition, 1.0f / SCENE_SCALE);
sceneAnimator->boneCount += animationInfo[i].armature.numberOfBones;
@ -48,24 +52,36 @@ void sceneAnimatorUpdate(struct SceneAnimator* sceneAnimator) {
struct AnimatedAudioInfo* audioInfo = &gAnimatedAudioInfo[sceneAnimator->animationInfo[i].soundType];
if (audioInfo->soundId == SOUND_ID_NONE) {
continue;
}
struct Vector3 currentPos;
vector3Scale(&sceneAnimator->armatures[i].pose[0].position, &currentPos, 1.0f / SCENE_SCALE);
int isMoving = currentPos.x != state->lastPosition.x || currentPos.y != state->lastPosition.y || currentPos.z != state->lastPosition.z;
int wasMoving = (state->flags & SceneAnimatorStateWasMoving) != 0;
if (isMoving && state->soundId == SOUND_ID_NONE) {
state->soundId = soundPlayerPlay(audioInfo->soundId, 1.0f, audioInfo->pitch, &currentPos, &gZeroVec);
} else if (isMoving && state->soundId != SOUND_ID_NONE) {
soundPlayerUpdatePosition(state->soundId, &currentPos, &gZeroVec);
} else if (!isMoving && state->soundId != SOUND_ID_NONE) {
soundPlayerStop(state->soundId);
state->soundId = SOUND_ID_NONE;
if (audioInfo->loopSoundId != SOUND_ID_NONE) {
if (isMoving && state->soundId == SOUND_ID_NONE) {
state->soundId = soundPlayerPlay(audioInfo->loopSoundId, 1.0f, audioInfo->pitch, &currentPos, &gZeroVec);
} else if (isMoving && state->soundId != SOUND_ID_NONE) {
soundPlayerUpdatePosition(state->soundId, &currentPos, &gZeroVec);
} else if (!isMoving && state->soundId != SOUND_ID_NONE) {
soundPlayerStop(state->soundId);
state->soundId = SOUND_ID_NONE;
}
}
if (isMoving && !wasMoving && audioInfo->startSoundId != SOUND_ID_NONE) {
soundPlayerPlay(audioInfo->startSoundId, 1.0f, audioInfo->pitch, &currentPos, &gZeroVec);
}
if (!wasMoving && isMoving && audioInfo->endSoundId != SOUND_ID_NONE) {
soundPlayerPlay(audioInfo->endSoundId, 1.0f, audioInfo->pitch, &currentPos, &gZeroVec);
}
state->lastPosition = currentPos;
state->flags &= ~SceneAnimatorStateWasMoving;
if (isMoving) {
state->flags |= SceneAnimatorStateWasMoving;
}
}
}

View file

@ -8,9 +8,14 @@
#include "../graphics/renderstate.h"
enum SceneAnimatorStateFlags {
SceneAnimatorStateWasMoving = (1 << 0),
};
struct SceneAnimatorState {
float playbackSpeed;
ALSndId soundId;
short flags;
struct Vector3 lastPosition;
};

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);
cutsceneQueueSoundInChannel(gCameraDestroyClips[clipIndex], 1.0f, CH_GLADOS, CutsceneSubtitleTypeNone);
}
}
}

View file

@ -99,12 +99,13 @@ local function generate_cutscene_step(cutscene_name, step, step_index, label_loc
tonumber(step.args[2] or "1") * 255,
math.floor(tonumber(step.args[3] or "1") * 64 + 0.5),
}
elseif step.command == "q_sound" and #step.args >= 2 then
elseif step.command == "q_sound" and #step.args >= 3 then
result.type = sk_definition_writer.raw('CutsceneStepTypeQueueSound')
result.queueSound = {
sk_definition_writer.raw(string_starts_with(step.args[1], "SOUNDS_") and step.args[1] or ("SOUNDS_" .. step.args[1])),
sk_definition_writer.raw(step.args[2]),
tonumber(step.args[3] or "1") * 255,
sk_definition_writer.raw(step.args[3]),
tonumber(step.args[4] or "1") * 255,
}
elseif step.command == "wait_for_channel" and #step.args >= 1 then
result.type = sk_definition_writer.raw('CutsceneStepTypeWaitForChannel')