~rabbits/hiversaires

3e1627a4c505fa9109dd8d7d107a0b9946af8e5c — Rezmason 10 months ago 48e00fc
Implementing the credits as just a slower splash, with fading in mixed in, and click-through links to the logos' corresponding sites. Added Aliceffekt logo and the secret logo. Updated the credit trigger to only show the secret if the player has an extra fuse *and* didn't use the walkthrough.
M TODO.txt => TODO.txt +4 -8
@@ 1,11 1,3 @@
- credits
  Just rasterize them
  Make an SVG, use libsvg to render it, put the command in a text file someplace, end of story
bug: audio loading hitch
  - spin up a separate SDL in a thread for audio and mixer
profile
  - any memory leaks? probably not?

deploy, test
  - Compile for multiple platforms
  - Give to testers


@@ 13,6 5,10 @@ deploy, test
    - test hacks especially, like the audio thread and resize event watcher
  - itch.io I guess

fix the audio loading hitch
  - collect data on it first tho
  - spin up a separate SDL in a thread for audio and mixer

mobile device support
  - Touch event support
  - Respond to device orientation

M media/data/data.txt => media/data/data.txt +1 -1
@@ 55,7 55,7 @@ energyTerminal("nataniev_endgame_energy_terminal", 0);
secretTerminal("rainre_secret_terminal", 0);
energyTerminal("entente_energy_box", 1);
energyTerminal("antechannel_energy_box", 1);
endgameCredit("endgame_credit", 0);
endgame("endgame_credit", 0);
ententeProgressTerminal("entente_progress_terminal", 0);
energyTerminal("capsule_nataniev_energy_terminal", 0);
/*DOORS*/

A media/graphics/logos/aliceffekt.svg => media/graphics/logos/aliceffekt.svg +14 -0
@@ 0,0 1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="512" height="512">
	<g transform="translate(0,40) scale(0.032625,0.032625)">
		<polygon fill="white" points="0,330.9 0,483.5 277,206 277,268 225,268 135,376 277,376 277,430 385,430 385,54 277,54"/>
		<polygon fill="white" points="393,54 393,376 715,376 715,268 501,268 501,54"/>
		<polygon fill="white" points="724,54 832,54 832,376 724,376"/>
		<polygon fill="white" points="841,54 841,376 1108.8,376 1162,321.6 1162,168.5 1063.5,268 949,268 949,162 1162,162 1162,54"/>
		<polygon fill="white" points="2161,376 2161,54 2482,54 2482,204 2443,244 2269,268 2374,162 2269,162 2269,268 2482,268 2482,376"/>
		<polygon fill="white" points="1831,54 1831,268 1609,268 1609,162 1714,162 1609,268 1783,244 1822,204 1822,54 1501,54 1501,268 1279,268 1279,162 1384,162 1279,268 1453,244 1492,204 1492,54 1171,54 1171,376 1501,376 1501,527 1609,419 1609,376 1831,376 1831,527 1939,419 1939,376 1981,376 2089,268 1939,268 1939,162 2044,162 1939,268 2113,244 2152,204 2152,54"/>
		<polygon fill="white" points="2934,0 2907.3,0 2724.5,162 2826,162 2826,273 2845,376 2947.8,376 3054.5,268 2934,268 2934,162 3063,162 3063,54 2934,54"/>
		<polygon fill="white" points="2712.6,171.4 2800,260 2874,485 2632,244"/>
		<polygon fill="white" points="2491,54 2491,376 2599,376 2599,268 2812,54 2661,54 2599,116 2599,54"/>
	</g>
</svg>

A media/graphics/logos/hiversaires_source.svg => media/graphics/logos/hiversaires_source.svg +12 -0
@@ 0,0 1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="512" height="512">
	<g transform="translate(0,25) scale(0.3425,0.3425)">
	  <path fill="white" d="M203.1,10.9v99h-14.5V67.7h-27.8v42.2h-14.5V67.7h-27.8v42.2h-14.5v-99h14.5v42.2h27.8V10.9h14.5v42.2h27.8
			V10.9H203.1z M32.2,96.8c-2.4-9.4-3.5-19.2-3.5-29.3c0-10.1,1.2-19.8,3.5-29.3c2.4-9.4,5.6-18.6,9.9-27.3h-5.6
			C29.1,19.1,23.6,28.2,20,38.2S14.5,58,14.5,67.6c0,9.6,1.8,19.3,5.5,29.4s9.1,19.1,16.5,27.2H42C37.8,115.4,34.5,106.3,32.2,96.8z
			 M73.5,52.1V38.6L84.6,45l4.2-6.4l-11.6-7.1l11.6-7.1L84.6,18l-11.1,6.4V10.9h-8.4v13.5L54,18l-4.2,6.4l11.6,7.1l-11.6,7.1L54,45
			l11.1-6.4v13.5H73.5z M226.1,38.3c2.4,9.4,3.6,19.2,3.6,29.3c0,10.1-1.2,19.9-3.6,29.3s-5.6,18.5-9.8,27.3h5.6
			c7.3-8.2,12.8-17.2,16.4-27.2c3.7-10,5.5-19.8,5.5-29.4c0-9.5-1.8-19.3-5.5-29.3c-3.7-10-9.2-19.1-16.4-27.3h-5.6
			C220.5,19.7,223.7,28.9,226.1,38.3z M276,78.4V64.2h-14.2v14.2H276z M276,109.9V95.8h-14.2v24.4L276,109.9z"/>
	</g>
</svg>

M src/delays.c => src/delays.c +23 -12
@@ 38,8 38,8 @@ Delays_Quit()
	delays = NULL;
}

int
Delays_Add(double duration, void (*onComplete)(void *data), void *data)
void
Delays_Add(int *uid, double duration, void (*onComplete)(void *data), void *data)
{
	int i;
	for(i = 0; i < _numDelays; i++) {


@@ 47,20 47,30 @@ Delays_Add(double duration, void (*onComplete)(void *data), void *data)
			(*delays)[i].endTime = _totalTime + duration;
			(*delays)[i].onComplete = onComplete;
			(*delays)[i].data = data;
			return i;
			*uid = i;
			return;
		}
	}
	return -1;
	*uid = -1;
}

void
Delays_Cancel(int uid, int complete)
Delays_Cancel(int *uid, int complete)
{
	void (*onComplete)() = (*delays)[uid].onComplete;
	void *data = (*delays)[uid].data;
	(*delays)[uid].onComplete = NULL;
	(*delays)[uid].data = NULL;
	(*delays)[uid].endTime = -1;
	Delay *delay;
	void (*onComplete)();
	void *data;

	if(*uid < 0 || *uid >= _numDelays) {
		return;
	}
	delay = &(*delays)[*uid];
	onComplete = delay->onComplete;
	data = delay->data;
	delay->onComplete = NULL;
	delay->data = NULL;
	delay->endTime = -1;
	*uid = -1;
	if(complete && onComplete != NULL) {
		onComplete(data);
	}


@@ 69,11 79,12 @@ Delays_Cancel(int uid, int complete)
void
Delays_Update(double totalTime)
{
	int i;
	int i, uid;
	_totalTime = totalTime;
	for(i = 0; i < _numDelays; i++) {
		if((*delays)[i].endTime > 0 && (*delays)[i].endTime < _totalTime) {
			Delays_Cancel(i, 1);
			uid = i;
			Delays_Cancel(&uid, 1);
		}
	}
}

M src/delays.h => src/delays.h +2 -2
@@ 26,6 26,6 @@ typedef struct {

void Delays_Init(int numDelays);
void Delays_Quit();
int Delays_Add(double duration, void (*onComplete)(), void *data);
void Delays_Cancel(int uid, int complete);
void Delays_Add(int *uid, double duration, void (*onComplete)(), void *data);
void Delays_Cancel(int *uid, int complete);
void Delays_Update(double totalTime);

M src/gui.c => src/gui.c +93 -54
@@ 67,18 67,22 @@ static Image *menuLogo;
static Image *vignette;
static Image *black;

static Image *hundredRabbitsLogo;
static Image *xxiivvLogo;
static Image *rezmasonLogo;
static Image *logos[5];

static int nodeImageIndex = 0;
static Image *nodeImages[2];
static char nodeImagePaths[2][PATH_LENGTH];

static int splash1Delay;
static int splash2Delay;
static int splash3Delay;
static int splash4Delay;
static int logoDelays[10];

static char *creditURLs[5] = {
	"https://xxiivv.com",
	"https://rezmason.net",
	"https://aliceffekt.bandcamp.com",
	"https://100r.co",
	"https://git.sr.ht/~rabbits/hiversaires"};
static int creditIndex;
static int showedURL = 0;

int
GUI_PreloadAssets()


@@ 151,9 155,11 @@ GUI_PreloadAssets()
	lazierLoadImage(menuLogo, "menu.logo.svg");

	/* The splash screen images only show up once */
	loadLogo(hundredRabbitsLogo, "100R.svg");
	loadLogo(xxiivvLogo, "xxiivv.svg");
	loadLogo(rezmasonLogo, "rm.svg");
	loadLogo(logos[0], "100R.svg");
	loadLogo(logos[1], "xxiivv.svg");
	loadLogo(logos[2], "rm.svg");
	loadLogo(logos[3], "aliceffekt.svg");
	loadLogo(logos[4], "hiversaires_source.svg");

	/*
		This one's interesting: the vignette is a full-screen


@@ 178,9 184,9 @@ GUI_Init()
	Stage_SetAlpha(BILLBOARD_HUD_SAVE, 0);
	Stage_SetAlpha(BILLBOARD_CLOCK_SHADOW, 0);
	Stage_SetAlpha(BILLBOARD_VIGNETTE, 0);
	Stage_SetAlpha(BILLBOARD_MENU_LOGO, 0);
	Stage_SetAlpha(BILLBOARD_MENU_CONTROLS, 0);
	Stage_SetAlpha(BILLBOARD_SPLASH, 0);
	Stage_SetAlpha(BILLBOARD_HIVERSAIRES_LOGO, 0);
	Stage_SetAlpha(BILLBOARD_CONTROLS, 0);
	Stage_SetAlpha(BILLBOARD_LOGO, 0);

	/* Most HUD elements occupy a Stage region for the whole game */
	nodeImageIndex = 0;


@@ 195,9 201,9 @@ GUI_Init()
	Stage_SetImage(BILLBOARD_HUD_SAVE, iconsByType[ICONTYPE_SAVE]);
	Stage_SetImage(BILLBOARD_CLOCK_SHADOW, clocksByType[CLOCKTYPE_SHADOW]);
	Stage_SetImage(BILLBOARD_VIGNETTE, vignette);
	Stage_SetImage(BILLBOARD_MENU_BLACK, black);
	Stage_SetImage(BILLBOARD_MENU_LOGO, menuLogo);
	Stage_SetImage(BILLBOARD_MENU_CONTROLS, menuControls);
	Stage_SetImage(BILLBOARD_BLACK, black);
	Stage_SetImage(BILLBOARD_HIVERSAIRES_LOGO, menuLogo);
	Stage_SetImage(BILLBOARD_CONTROLS, menuControls);

	SDL_SetCursor(cursorsByType[currentCursorType][cursorPressed]);



@@ 250,12 256,10 @@ GUI_Quit()
	Loader_DestroyImage(black);
	black = NULL;

	Loader_DestroyImage(hundredRabbitsLogo);
	hundredRabbitsLogo = NULL;
	Loader_DestroyImage(rezmasonLogo);
	rezmasonLogo = NULL;
	Loader_DestroyImage(xxiivvLogo);
	xxiivvLogo = NULL;
	for(i = 0; i < 5; i++) {
		Loader_DestroyImage(logos[i]);
		logos[i] = NULL;
	}

	Loader_DestroyImage(nodeImages[0]);
	Loader_DestroyImage(nodeImages[1]);


@@ 426,14 430,10 @@ GUI_ShowAudio(int enabled)
void
GUI_HideMenu()
{
	Stage_SetAlpha(BILLBOARD_MENU_BLACK, 0.);
	Stage_SetAlpha(BILLBOARD_MENU_CREDIT1, 0.);
	Stage_SetAlpha(BILLBOARD_MENU_CREDIT2, 0.);
	Stage_SetAlpha(BILLBOARD_MENU_CREDIT3, 0.);
	Stage_SetAlpha(BILLBOARD_MENU_CREDIT4, 0.);
	Stage_SetAlpha(BILLBOARD_MENU_LOGO, 0.);
	Stage_SetAlpha(BILLBOARD_MENU_CONTROLS, 0.);
	Stage_SetAlpha(BILLBOARD_SPLASH, 0.);
	Stage_SetAlpha(BILLBOARD_BLACK, 0.);
	Stage_SetAlpha(BILLBOARD_HIVERSAIRES_LOGO, 0.);
	Stage_SetAlpha(BILLBOARD_CONTROLS, 0.);
	Stage_SetAlpha(BILLBOARD_LOGO, 0.);
}

void


@@ 445,9 445,9 @@ GUI_ShowHomeMenu()
	Stage_SetAlpha(BILLBOARD_HUD_SAVE, 0);
	Stage_SetAlpha(BILLBOARD_NODE, 1);

	Stage_Fade(BILLBOARD_MENU_BLACK, 1, 0, 2, 0);
	Stage_Fade(BILLBOARD_MENU_LOGO, 1, 0, 2, 3);
	Stage_Fade(BILLBOARD_MENU_CONTROLS, 1, 0, 1, 8);
	Stage_Fade(BILLBOARD_BLACK, 1, 0, 2, 0);
	Stage_Fade(BILLBOARD_HIVERSAIRES_LOGO, 1, 0, 2, 3);
	Stage_Fade(BILLBOARD_CONTROLS, 1, 0, 1, 8);
}

void


@@ 489,15 489,15 @@ static void
splashNext(void *data)
{
	Image *image = data;
	Stage_SetImage(BILLBOARD_SPLASH, image);
	Stage_Fade(BILLBOARD_SPLASH, 1, 0, 0.125, 0.875);
	Stage_SetImage(BILLBOARD_LOGO, image);
	Stage_Fade(BILLBOARD_LOGO, 1, 0, 0.125, 0.675);
}

static void
splashEnd(void *data)
{
	FuncWrapper *wrapper = data;
	Stage_SetAlpha(BILLBOARD_SPLASH, 0);
	Stage_SetAlpha(BILLBOARD_LOGO, 0);
	wrapper->f();
	free(wrapper);
}


@@ 505,37 505,76 @@ splashEnd(void *data)
void
GUI_RunSplash(void (*onComplete)())
{
	Stage_SetAlpha(BILLBOARD_MENU_BLACK, 1);
	Stage_SetAlpha(BILLBOARD_SPLASH, 1);
	splash1Delay = Delays_Add(0.5, splashNext, hundredRabbitsLogo);
	splash2Delay = Delays_Add(1.5, splashNext, xxiivvLogo);
	splash3Delay = Delays_Add(2.5, splashNext, rezmasonLogo);
	splash4Delay = Delays_Add(3.5, splashEnd, wrapFunction(onComplete));
	Stage_SetAlpha(BILLBOARD_BLACK, 1);
	Stage_SetAlpha(BILLBOARD_LOGO, 1);
	Delays_Add(&logoDelays[0], 0.5 + 0.8 * 0, splashNext, logos[0]);
	Delays_Add(&logoDelays[1], 0.5 + 0.8 * 1, splashNext, logos[1]);
	Delays_Add(&logoDelays[2], 0.5 + 0.8 * 2, splashNext, logos[2]);
	Delays_Add(&logoDelays[3], 0.5 + 0.8 * 3, splashNext, logos[3]);
	Delays_Add(&logoDelays[4], 0.5 + 0.8 * 4, splashEnd, wrapFunction(onComplete));
}

void
GUI_SkipSplash()
{
	Stage_SetAlpha(BILLBOARD_MENU_BLACK, 1);
	Stage_SetAlpha(BILLBOARD_SPLASH, 0);
	Stage_SetAlpha(BILLBOARD_BLACK, 1);
	Stage_SetAlpha(BILLBOARD_LOGO, 0);

	Delays_Cancel(&logoDelays[0], 0);
	Delays_Cancel(&logoDelays[1], 0);
	Delays_Cancel(&logoDelays[2], 0);
	Delays_Cancel(&logoDelays[3], 0);
	Delays_Cancel(&logoDelays[4], 1);
}

	Delays_Cancel(splash1Delay, 0);
	Delays_Cancel(splash2Delay, 0);
	Delays_Cancel(splash3Delay, 0);
	Delays_Cancel(splash4Delay, 1);
static void
creditIn(void *data)
{
	Image *image = data;
	creditIndex++;
	showedURL = 0;
	Stage_SetImage(BILLBOARD_LOGO, image);
	Stage_Fade(BILLBOARD_LOGO, 0, 1, 0.5, 0);
}

static void
creditOut(void *data)
{
	(void)data;
	Stage_Fade(BILLBOARD_LOGO, 1, 0, 0.5, 0);
}

void
GUI_ShowCreditsMenu(int showSecret)
GUI_ShowCredits(int showSecret)
{
	Stage_Fade(BILLBOARD_MENU_BLACK, 0, 1, 1, 1);
	Stage_Fade(BILLBOARD_MENU_CREDIT1, 0, 1, 1, 6);
	Stage_Fade(BILLBOARD_MENU_CREDIT2, 0, 1, 1, 10);
	Stage_Fade(BILLBOARD_MENU_CREDIT3, 0, 1, 1, 16);
	creditIndex = -1;
	Stage_Fade(BILLBOARD_BLACK, 0, 1, 3, 0);

	Delays_Add(&logoDelays[0], 3 + (0.5 + 5) * 0 + 0.5 * 0, creditIn, logos[1]);
	Delays_Add(&logoDelays[1], 3 + (0.5 + 5) * 1 + 0.5 * 0, creditOut, logos[1]);

	Delays_Add(&logoDelays[2], 3 + (0.5 + 5) * 1 + 0.5 * 1, creditIn, logos[2]);
	Delays_Add(&logoDelays[3], 3 + (0.5 + 5) * 2 + 0.5 * 1, creditOut, logos[2]);

	Delays_Add(&logoDelays[4], 3 + (0.5 + 5) * 2 + 0.5 * 2, creditIn, logos[3]);
	Delays_Add(&logoDelays[5], 3 + (0.5 + 5) * 3 + 0.5 * 2, creditOut, logos[3]);

	Delays_Add(&logoDelays[6], 3 + (0.5 + 5) * 3 + 0.5 * 3, creditIn, logos[0]);

	if(showSecret) {
		Stage_Fade(BILLBOARD_MENU_CREDIT4, 0, 1, 1, 24);
		Delays_Add(&logoDelays[7], 3 + (0.5 + 5) * 4 + 0.5 * 3, creditOut, logos[0]);
		Delays_Add(&logoDelays[8], 3 + (0.5 + 5) * 4 + 0.5 * 4, creditIn, logos[4]);
	}
}

void
GUI_LaunchCreditURL()
{
	if(showedURL) {
		return;
	}
	showedURL = 1;
	SDL_OpenURL(creditURLs[creditIndex]);
}

void

M src/gui.h => src/gui.h +2 -1
@@ 105,7 105,8 @@ void GUI_HideMenu();
void GUI_ShowHomeMenu();
void GUI_ShowMovement(int nudgeX, int nudgeY);
void GUI_ShowNode(char *nodeName, Orientation orientation, NodeModifier modifier, double fadeDuration, double fadeDelay);
void GUI_ShowCreditsMenu(int showSecret);
void GUI_ShowCredits(int showSecret);
void GUI_LaunchCreditURL();
void GUI_RunSplash(void (*onComplete)());
void GUI_SkipSplash();
void GUI_FlashVignette();

M src/play.c => src/play.c +7 -0
@@ 119,6 119,10 @@ action(int autoDoor)
		break;
	default: break;
	}

	if(_state->completed) {
		GUI_ShowCredits(_state->energy > 0 && !Walkthrough_PlayerUsedWalkthrough());
	}
}

static void


@@ 241,6 245,9 @@ Play_ProcessInput(InputType type, int down)
	}

	if(_state->completed) {
		if(down && (type == INPUTTYPE_ACTION || type == INPUTTYPE_CENTER || type == INPUTTYPE_FORWARD)) {
			GUI_LaunchCreditURL();
		}
		return;
	}


M src/stage.h => src/stage.h +4 -9
@@ 72,15 72,10 @@ typedef enum {
	BILLBOARD_HUD_STEP_LEFT,
	BILLBOARD_HUD_STEP_RIGHT,

	BILLBOARD_MENU_BLACK,
	BILLBOARD_MENU_CREDIT1,
	BILLBOARD_MENU_CREDIT2,
	BILLBOARD_MENU_CREDIT3,
	BILLBOARD_MENU_CREDIT4,
	BILLBOARD_MENU_LOGO,
	BILLBOARD_MENU_CONTROLS,

	BILLBOARD_SPLASH,
	BILLBOARD_BLACK,
	BILLBOARD_HIVERSAIRES_LOGO,
	BILLBOARD_CONTROLS,
	BILLBOARD_LOGO,

	TRIGGER_ACTION,
	TRIGGER_MOVE_FORWARD,

M src/stageregions.c => src/stageregions.c +4 -16
@@ 96,28 96,16 @@ Region regions[] =
		/* BILLBOARD_HUD_STEP_RIGHT */
		region(0.8, HUD_ALERT_Y, 0.2, HUD_HEIGHT, LAYOUTMODE_STRETCH),

		/* BILLBOARD_MENU_BLACK */
		/* BILLBOARD_BLACK */
		region(0., 0., 1., 1., LAYOUTMODE_STRETCH),

		/* BILLBOARD_MENU_CREDIT1 */
		/* BILLBOARD_HIVERSAIRES_LOGO */
		region(0., 0., 1., 1., LAYOUTMODE_CONTAIN),

		/* BILLBOARD_MENU_CREDIT2 */
		/* BILLBOARD_CONTROLS */
		region(0., 0., 1., 1., LAYOUTMODE_CONTAIN),

		/* BILLBOARD_MENU_CREDIT3 */
		region(0., 0., 1., 1., LAYOUTMODE_CONTAIN),

		/* BILLBOARD_MENU_CREDIT4 */
		region(0., 0., 1., 1., LAYOUTMODE_CONTAIN),

		/* BILLBOARD_MENU_LOGO */
		region(0., 0., 1., 1., LAYOUTMODE_CONTAIN),

		/* BILLBOARD_MENU_CONTROLS */
		region(0., 0., 1., 1., LAYOUTMODE_CONTAIN),

		/* BILLBOARD_SPLASH */
		/* BILLBOARD_LOGO */
		region(0.3, 0.3, 0.4, 0.4, LAYOUTMODE_CONTAIN),

		/* TRIGGER_ACTION */

M src/terminals.c => src/terminals.c +3 -4
@@ 331,12 331,11 @@ setupEntenteProgress(TerminalData *data, int *isOpen, GameState *state)
}

static int
changeEndgameCredit(TerminalData *data, int *isOpen, GameState *state)
changeEndgame(TerminalData *data, int *isOpen, GameState *state)
{
	(void)data;
	(void)isOpen;
	state->completed = 1;
	GUI_ShowCreditsMenu(state->energy > 0);
	return 1;
}



@@ 464,8 463,8 @@ parseTerminal(Terminal *terminal, char *line, int lineNumber)
	} else if(strcmp(type, "secretTerminal") == 0) {
		terminal->setup = setupSecret;
		terminal->change = changeSecret;
	} else if(strcmp(type, "endgameCredit") == 0) {
		terminal->change = changeEndgameCredit;
	} else if(strcmp(type, "endgame") == 0) {
		terminal->change = changeEndgame;
	} else {
		printf("Terminal data error: unknown terminal type on line %d: %s\n", lineNumber, type);
	}

M src/walkthrough.c => src/walkthrough.c +9 -0
@@ 63,6 63,15 @@ Walkthrough_Reset()
	stepIndex = 0;
}

int
Walkthrough_PlayerUsedWalkthrough()
{
#ifdef DEBUG
	return 0;
#endif
	return stepIndex > 0;
}

InputType
Walkthrough_Next()
{

M src/walkthrough.h => src/walkthrough.h +1 -0
@@ 25,4 25,5 @@ WITH REGARD TO THIS SOFTWARE.
int Walkthrough_Init(char *data);
void Walkthrough_Quit();
void Walkthrough_Reset();
int Walkthrough_PlayerUsedWalkthrough();
InputType Walkthrough_Next();