Sigrid Solveig Haflínudóttirwip Plan 9 port8024a895 years agoHistory
645 lines · 18.4 KB · C
RawDownload
#include "gui.h" #include <SDL2/SDL.h>#include <stdio.h>#include "datatypes.h"#include "stage.h"#include "delays.h"#include "util.h" /*Copyright (c) 2013-2021 Devine Lu Linvega2018 web port by rezmason2021 C89 port by rezmason Permission to use, copy, modify, and distribute this software for anypurpose with or without fee is hereby granted, provided that the abovecopyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIESWITH REGARD TO THIS SOFTWARE.*/ #define PATH_LENGTH 64#define NODE_RENDER_PATH "graphics/node_render/%s.%i"#define CURSOR_BORDER 32 #define NUDGE_MULT 0.025 #define loadCursor(cursor, path) success &= Loader_LoadCursor(&cursor, "graphics/cursor/" path)#define loadImage(asset, path) success &= Loader_LoadImage(Loader_CreateImage(&asset, LOADTYPE_MANUAL, "graphics/interface/" path))#define lazyLoadImage(asset, path) Loader_CreateImage(&asset, LOADTYPE_LAZY, "graphics/interface/" path)#define lazierLoadImage(asset, path) Loader_CreateImage(&asset, LOADTYPE_LAZIER, "graphics/interface/" path)#define loadLogo(asset, path) Loader_CreateImage(&asset, LOADTYPE_LAZIER, "graphics/logos/" path) static const char *modifierNames[] = {	"",	".filled",	".empty",	".on",	".open",	".secret",	".seal.1",	".seal.2",	".unlocked",	".seal.1",	".seal.2",	".seal.both",	".exit"}; static SDL_Cursor *cursorsByType[NumberOfCursorTypes][2];static CursorType currentCursorType = CURSORTYPE_FORWARD;static int cursorPressed = 0;static CursorType actionCursorType = CURSORTYPE_FORWARD;static int actionTriggerActive;static SDL_Color red = {0xFF, 0x0, 0x0, 0xFF};static SDL_Color cyan = {0x72, 0xDE, 0xC2, 0xFF}; static Image *iconsByType[NumberOfIconTypes];static Image *clocksByType[NumberOfClockTypes];static Image *mazeIconsByAxisAndInstruction[NumberOfMazeAxes][NumberOfMazeInstructions];static Image *progressPanesByChapter[NumberOfChapters];static Image *sealsByZone[NumberOfZones]; static Image *ententeProgressPane;static Image *menuControls;static Image *menuLogo;static Image *vignette;static Image *black; static Image *logos[5]; static int nodeImageIndex = 0;static Image *nodeImages[2];static char nodeImagePaths[2][PATH_LENGTH]; 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; intGUI_PreloadAssets(void){	int success = 1; 	/* Cursors get loaded immediately */	loadCursor(cursorsByType[CURSORTYPE_FUSE][0], "fuse.png");	loadCursor(cursorsByType[CURSORTYPE_FUSE][1], "fuse.png");	loadCursor(cursorsByType[CURSORTYPE_FORWARD][0], "pointer.up.png");	loadCursor(cursorsByType[CURSORTYPE_FORWARD][1], "pointer.up.pressed.png");	loadCursor(cursorsByType[CURSORTYPE_LEFT][0], "pointer.left.png");	loadCursor(cursorsByType[CURSORTYPE_LEFT][1], "pointer.left.pressed.png");	loadCursor(cursorsByType[CURSORTYPE_RIGHT][0], "pointer.right.png");	loadCursor(cursorsByType[CURSORTYPE_RIGHT][1], "pointer.right.pressed.png");	cursorsByType[CURSORTYPE_SYSTEM][0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);	cursorsByType[CURSORTYPE_SYSTEM][1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); 	/* HUD images get loaded immediately */	loadImage(iconsByType[ICONTYPE_ALERT], "alert.svg");	loadImage(iconsByType[ICONTYPE_CLOCK_0], "clock.0.svg");	loadImage(iconsByType[ICONTYPE_CLOCK_1], "clock.1.svg");	loadImage(iconsByType[ICONTYPE_CLOCK_2], "clock.2.svg");	loadImage(iconsByType[ICONTYPE_FOOTSTEP], "footstep.svg");	loadImage(iconsByType[ICONTYPE_FUSE_0], "fuse.0.svg");	loadImage(iconsByType[ICONTYPE_FUSE_1], "fuse.1.svg");	loadImage(iconsByType[ICONTYPE_FUSE_2], "fuse.2.svg");	loadImage(iconsByType[ICONTYPE_FUSE_3], "fuse.3.svg");	loadImage(iconsByType[ICONTYPE_MUSIC_OFF], "music.off.svg");	loadImage(iconsByType[ICONTYPE_MUSIC_ON], "music.on.svg");	loadImage(iconsByType[ICONTYPE_SAVE], "save.svg"); 	loadImage(sealsByZone[ZONE_ANTECHANNEL], "seal.antechannel.svg");	loadImage(sealsByZone[ZONE_CAPSULE], "seal.capsule.svg");	loadImage(sealsByZone[ZONE_CIRCULAR], "seal.circular.svg");	loadImage(sealsByZone[ZONE_ENTENTE], "seal.entente.svg");	loadImage(sealsByZone[ZONE_FOREST], "seal.forest.svg");	loadImage(sealsByZone[ZONE_METAMONDST], "seal.metamondst.svg");	loadImage(sealsByZone[ZONE_NATANIEV], "seal.nataniev.svg");	loadImage(sealsByZone[ZONE_NETHER], "seal.nether.svg");	loadImage(sealsByZone[ZONE_NONE], "seal.none.svg");	loadImage(sealsByZone[ZONE_RAINRE], "seal.rainre.svg");	loadImage(sealsByZone[ZONE_STONES], "seal.stones.svg");	loadImage(sealsByZone[ZONE_STUDIO], "seal.studio.svg");	loadImage(sealsByZone[ZONE_TERMINAL], "seal.terminal.svg"); 	/* Clock, maze and progress images can be loaded once they're needed */	lazyLoadImage(clocksByType[CLOCKTYPE_SHADOW], "dimclock.shadow.svg");	lazyLoadImage(clocksByType[CLOCKTYPE_STATE_0], "dimclock.state0.svg");	lazyLoadImage(clocksByType[CLOCKTYPE_STATE_1], "dimclock.state1.svg");	lazyLoadImage(clocksByType[CLOCKTYPE_STATE_2], "dimclock.state2.svg"); 	lazyLoadImage(mazeIconsByAxisAndInstruction[MAZEAXIS_X][MAZEINSTRUCTION_LEFT], "entente.x.left.svg");	lazyLoadImage(mazeIconsByAxisAndInstruction[MAZEAXIS_X][MAZEINSTRUCTION_RIGHT], "entente.x.right.svg");	lazyLoadImage(mazeIconsByAxisAndInstruction[MAZEAXIS_X][MAZEINSTRUCTION_STRAIGHT], "entente.x.straight.svg");	lazyLoadImage(mazeIconsByAxisAndInstruction[MAZEAXIS_Y][MAZEINSTRUCTION_LEFT], "entente.y.left.svg");	lazyLoadImage(mazeIconsByAxisAndInstruction[MAZEAXIS_Y][MAZEINSTRUCTION_RIGHT], "entente.y.right.svg");	lazyLoadImage(mazeIconsByAxisAndInstruction[MAZEAXIS_Y][MAZEINSTRUCTION_STRAIGHT], "entente.y.straight.svg"); 	lazyLoadImage(progressPanesByChapter[CHAPTER_ACT1], "progress.act1.svg");	lazyLoadImage(progressPanesByChapter[CHAPTER_ACT2], "progress.act2.svg");	lazyLoadImage(progressPanesByChapter[CHAPTER_ACT3], "progress.act3.svg");	lazyLoadImage(progressPanesByChapter[CHAPTER_ACT4], "progress.act4.svg");	lazyLoadImage(progressPanesByChapter[CHAPTER_ACT5], "progress.act5.svg"); 	lazyLoadImage(ententeProgressPane, "progress.entente.svg"); 	/* The full-screen menus are huge and rarely needed */	lazierLoadImage(menuControls, "menu.controls.svg");	lazierLoadImage(menuLogo, "menu.logo.svg"); 	/* The splash screen images only show up once */	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		radial gradient that flashes almost every time the player		performs an action. So it's loaded when the game initializes,		but it's a very small SVG that's rendered very large.	*/	loadImage(vignette, "vignette.svg");	loadImage(black, "black.svg");	return success;} voidGUI_Init(void){	Stage_SetAlpha(BILLBOARD_HUD_STEP_RIGHT, 0);	Stage_SetAlpha(BILLBOARD_HUD_STEP_FORWARD, 0);	Stage_SetAlpha(BILLBOARD_HUD_STEP_LEFT, 0);	Stage_SetAlpha(BILLBOARD_HUD_CLOCK_ALERT, 0);	Stage_SetAlpha(BILLBOARD_HUD_SEAL_ALERT, 0);	Stage_SetAlpha(BILLBOARD_HUD_FUSE_ALERT, 0);	Stage_SetAlpha(BILLBOARD_HUD_SAVE, 0);	Stage_SetAlpha(BILLBOARD_CLOCK_SHADOW, 0);	Stage_SetAlpha(BILLBOARD_VIGNETTE, 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;	Stage_SetImage(BILLBOARD_NODE, nodeImages[0]);	Stage_SetImage(BILLBOARD_LAST_NODE_FADE, nodeImages[1]);	Stage_SetImage(BILLBOARD_HUD_STEP_RIGHT, iconsByType[ICONTYPE_FOOTSTEP]);	Stage_SetImage(BILLBOARD_HUD_STEP_FORWARD, iconsByType[ICONTYPE_FOOTSTEP]);	Stage_SetImage(BILLBOARD_HUD_STEP_LEFT, iconsByType[ICONTYPE_FOOTSTEP]);	Stage_SetImage(BILLBOARD_HUD_CLOCK_ALERT, iconsByType[ICONTYPE_ALERT]);	Stage_SetImage(BILLBOARD_HUD_SEAL_ALERT, iconsByType[ICONTYPE_ALERT]);	Stage_SetImage(BILLBOARD_HUD_FUSE_ALERT, iconsByType[ICONTYPE_ALERT]);	Stage_SetImage(BILLBOARD_HUD_SAVE, iconsByType[ICONTYPE_SAVE]);	Stage_SetImage(BILLBOARD_CLOCK_SHADOW, clocksByType[CLOCKTYPE_SHADOW]);	Stage_SetImage(BILLBOARD_VIGNETTE, vignette);	Stage_SetImage(BILLBOARD_BLACK, black);	Stage_SetImage(BILLBOARD_HIVERSAIRES_LOGO, menuLogo);	Stage_SetImage(BILLBOARD_CONTROLS, menuControls); 	SDL_SetCursor(cursorsByType[currentCursorType][cursorPressed]); 	Loader_CreateImage(&nodeImages[0], LOADTYPE_LAZIER, nodeImagePaths[0]);	Loader_CreateImage(&nodeImages[1], LOADTYPE_LAZIER, nodeImagePaths[1]);} voidGUI_Quit(void){	int i, j;	for(i = 0; i < NumberOfCursorTypes; i++) {		SDL_FreeCursor(cursorsByType[i][0]);		SDL_FreeCursor(cursorsByType[i][1]);		cursorsByType[i][0] = NULL;		cursorsByType[i][1] = NULL;	} 	for(i = 0; i < NumberOfIconTypes; i++) {		Loader_DestroyImage(iconsByType[i]);		iconsByType[i] = NULL;	}	for(i = 0; i < NumberOfClockTypes; i++) {		Loader_DestroyImage(clocksByType[i]);		clocksByType[i] = NULL;	}	for(i = 0; i < NumberOfMazeAxes; i++) {		for(j = 0; j < NumberOfMazeInstructions; j++) {			Loader_DestroyImage(mazeIconsByAxisAndInstruction[i][j]);			mazeIconsByAxisAndInstruction[i][j] = NULL;		}	}	for(i = 0; i < NumberOfChapters; i++) {		Loader_DestroyImage(progressPanesByChapter[i]);		progressPanesByChapter[i] = NULL;	}	for(i = 0; i < NumberOfZones; i++) {		Loader_DestroyImage(sealsByZone[i]);		sealsByZone[i] = NULL;	} 	Loader_DestroyImage(ententeProgressPane);	ententeProgressPane = NULL;	Loader_DestroyImage(menuControls);	menuControls = NULL;	Loader_DestroyImage(menuLogo);	menuLogo = NULL;	Loader_DestroyImage(vignette);	vignette = NULL;	Loader_DestroyImage(black);	black = NULL; 	for(i = 0; i < 5; i++) {		Loader_DestroyImage(logos[i]);		logos[i] = NULL;	} 	Loader_DestroyImage(nodeImages[0]);	Loader_DestroyImage(nodeImages[1]);} voidGUI_SetActionTrigger(int active){	actionTriggerActive = active;} voidGUI_HideCursor(void){	SDL_ShowCursor(0);} voidGUI_UpdateCursor(int mouseX, int mouseY, int pressed){	CursorType type;	RegionName regionUnderMouse = GUI_GetRegionNameAt(mouseX, mouseY);	if(Stage_PointNearBorder(mouseX, mouseY, CURSOR_BORDER)) {		/*			The system cursor is shown near the edge of the window,			so that the game's cursors don't obstruct the player's			view of their cursor while they resize or move the window.		*/		type = CURSORTYPE_SYSTEM;	} else {		switch(regionUnderMouse) {		case TRIGGER_ACTION:			type = actionCursorType;			break;		case TRIGGER_MOVE_FORWARD:			type = CURSORTYPE_FORWARD;			break;		case TRIGGER_MOVE_LEFT:			type = CURSORTYPE_LEFT;			break;		case TRIGGER_MOVE_RIGHT:			type = CURSORTYPE_RIGHT;			break;		default:			type = CURSORTYPE_FORWARD;			break;		}	} 	SDL_ShowCursor(1);	if(currentCursorType != type || cursorPressed != pressed) {		currentCursorType = type;		cursorPressed = pressed;		SDL_SetCursor(cursorsByType[currentCursorType][cursorPressed]);	}} voidGUI_EnableFuseCursor(int enable){	int currentCursorIsActionCursor = currentCursorType == actionCursorType;	actionCursorType = enable ? CURSORTYPE_FUSE : CURSORTYPE_FORWARD; 	if(currentCursorIsActionCursor) {		currentCursorType = actionCursorType;		SDL_SetCursor(cursorsByType[currentCursorType][cursorPressed]);	}} RegionNameGUI_GetRegionNameAt(int mouseX, int mouseY){	int i = actionTriggerActive ? TRIGGER_ACTION : TRIGGER_MOVE_FORWARD;	for(; i <= TRIGGER_MOVE_RIGHT; i++) {		if(Stage_HitTest(i, mouseX, mouseY)) {			return i;		}	}	return TRIGGER_MOVE_FORWARD;} voidGUI_Reset(void){	GUI_HideMenu();	Stage_SetAlpha(BILLBOARD_LAST_NODE_FADE, 0);	Stage_SetAlpha(BILLBOARD_CLOCK, 0);	Stage_SetAlpha(BILLBOARD_CLOCK_SHADOW, 0);	Stage_SetAlpha(BILLBOARD_PROGRESS_PANE, 0);	Stage_SetAlpha(BILLBOARD_ENTENTE_SCREEN, 0);} voidGUI_ShowClockFace(int state, int refresh){	Stage_SetImage(BILLBOARD_CLOCK, clocksByType[CLOCKTYPE_STATE_0 + state]);	if(!refresh) {		Stage_Fade(BILLBOARD_CLOCK_SHADOW, 0, 0.23, 1.5, 0.5);		Stage_Fade(BILLBOARD_CLOCK, 0, 1, 0.5, 0.5);	} else {		Stage_SetAlpha(BILLBOARD_CLOCK_SHADOW, 0.23);		Stage_SetAlpha(BILLBOARD_CLOCK, 1);	}} voidGUI_ShowClock(int state){	Stage_SetImage(BILLBOARD_HUD_CLOCK, iconsByType[ICONTYPE_CLOCK_0 + state]);	Stage_Fade(BILLBOARD_HUD_CLOCK, 1, 0, 0.5, 3);} voidGUI_ShowClockAlert(void){	Stage_Fade(BILLBOARD_HUD_CLOCK_ALERT, 1, 0, 0.5, 0.5);} voidGUI_ShowSeals(Zone zoneP, Zone zoneQ){	/*		For aesthetic reasons, seals are always presented		in a particular order.	*/	Zone zone1, zone2;	if((zoneP > ZONE_NONE && zoneP < zoneQ) || zoneQ == ZONE_NONE) {		zone1 = zoneP;		zone2 = zoneQ;	} else {		zone1 = zoneQ;		zone2 = zoneP;	} 	Stage_SetImage(BILLBOARD_HUD_SEAL_1, sealsByZone[zone1]);	Stage_SetImage(BILLBOARD_HUD_SEAL_2, sealsByZone[zone2]); 	Stage_Fade(BILLBOARD_HUD_SEAL_1, 1, 0, 0.5, 3);	Stage_Fade(BILLBOARD_HUD_SEAL_2, 1, 0, 0.5, 3);} voidGUI_ShowSealAlert(void){	Stage_Fade(BILLBOARD_HUD_SEAL_ALERT, 1, 0, 0.5, 0.5);} voidGUI_ShowEnergy(int level){	Stage_SetImage(BILLBOARD_HUD_FUSE, iconsByType[ICONTYPE_FUSE_0 + level]);	Stage_Fade(BILLBOARD_HUD_FUSE, 1, 0, 0.5, 3);} voidGUI_ShowEnergyAlert(void){	Stage_Fade(BILLBOARD_HUD_FUSE_ALERT, 1, 0, 1.5, 0.5);} voidGUI_ShowAudio(int enabled){	Stage_SetImage(BILLBOARD_HUD_AUDIO, iconsByType[ICONTYPE_MUSIC_OFF + enabled]);	Stage_Fade(BILLBOARD_HUD_AUDIO, 1, 0, 0.5, 3);} voidGUI_HideMenu(void){	Stage_SetAlpha(BILLBOARD_BLACK, 0.);	Stage_SetAlpha(BILLBOARD_HIVERSAIRES_LOGO, 0.);	Stage_SetAlpha(BILLBOARD_CONTROLS, 0.);	Stage_SetAlpha(BILLBOARD_LOGO, 0.);} voidGUI_ShowHomeMenu(void){	Stage_SetAlpha(BILLBOARD_HUD_SEAL_1, 0);	Stage_SetAlpha(BILLBOARD_HUD_SEAL_2, 0);	Stage_SetAlpha(BILLBOARD_HUD_FUSE, 0);	Stage_SetAlpha(BILLBOARD_HUD_SAVE, 0);	Stage_SetAlpha(BILLBOARD_NODE, 1); 	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);} voidGUI_ShowMovement(int nudgeX, int nudgeY){	Stage_Nudge(nudgeX * NUDGE_MULT, nudgeY * NUDGE_MULT, 0.2);	Stage_Fade(BILLBOARD_NODE, 0.5, 1, 0.2, 0);	Stage_SetAlpha(BILLBOARD_LAST_NODE_FADE, 0);} voidGUI_ShowNode(char *nodeName, Orientation orientation, NodeModifier modifier, double fadeDuration, double fadeDelay){	/*		The main visual information in Hiversaires comes		from the images loaded here. 		The previous node image often cross-fades into the current one.		To accomplish this effect, Hiversaires takes the old image,		swaps its region with LAST_NODE_FADE, and fades out		on top of the new image. The last and current images are		reused in alternating roles, similar to a double buffering game.	*/	char path[PATH_LENGTH];	int nextIndex = !nodeImageIndex;	snprintf(path, PATH_LENGTH, NODE_RENDER_PATH "%s.jpg", nodeName, orientation, modifierNames[modifier]);	if(strcmp(nodeImagePaths[nodeImageIndex], path) == 0) {		return;	}	nodeImageIndex = nextIndex;	strcpy(nodeImagePaths[nextIndex], path);	Stage_SwapImages(BILLBOARD_LAST_NODE_FADE, BILLBOARD_NODE);	Stage_SetImage(BILLBOARD_NODE, nodeImages[nodeImageIndex]);	Stage_SetAlpha(BILLBOARD_NODE, 1);	Stage_Fade(BILLBOARD_LAST_NODE_FADE, 1, 0, fadeDuration, fadeDelay);} static voidsplashNext(void *data){	Image *image = data;	Stage_SetImage(BILLBOARD_LOGO, image);	Stage_Fade(BILLBOARD_LOGO, 1, 0, 0.125, 0.675);} static voidsplashEnd(void *data){	FuncWrapper *wrapper = data;	Stage_SetAlpha(BILLBOARD_LOGO, 0);

Lines 1–500 of 645.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t