Sigrid Solveig Haflínudóttirwip Plan 9 port8024a895 years agoHistory
420 lines · 9.9 KB · C
RawDownload
#include "play.h" #include "datatypes.h"#include "data.h"#include "doors.h"#include "terminals.h"#include "mazes.h"#include "chapters.h"#include "nodes.h"#include "input.h"#include "gui.h"#include "audio.h"#include "loader.h"#include "walkthrough.h"#include "prefs.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.*/ #ifdef DEBUGstatic char *orientationNames = "NESW";#endif static GameState *_state = NULL;static Node *currentNode = NULL;static Subject *currentSubject = NULL;static int begun = 0;static double _totalTime = 0;static double resetTime = -1;static double walkthroughStartTime = -1;static double walkthroughTime = -1;#ifdef DEBUGstatic double walkthroughDelay = 0.1;#elsestatic double walkthroughDelay = 0.25;#endif static voidsetupAction(void){	int hasAction = 1;	GUI_EnableFuseCursor(0); 	switch(currentSubject->type) {	case SUBJECTTYPE_DOOR: Doors_Setup(&currentSubject->data.door, _state); break;	case SUBJECTTYPE_TERMINAL: Terminals_Setup(&currentSubject->data.terminal, _state); break;	case SUBJECTTYPE_MAZE: Mazes_Setup(&currentSubject->data.maze, _state); break;	default: hasAction = 0; break;	} 	GUI_SetActionTrigger(hasAction);} static voidmoveTo(NodeSubject *node){	if(node->node == NULL) {		printf("Play error: Cannot move to nonexistent node. What were you doing?\n");		return;	} 	currentNode = node->node;	strcpy(_state->nodeName, currentNode->name);	if(node->orientation != ORIENTATION_NONE) {		_state->orientation = node->orientation;	}} static voidrefreshNode(int nudgeX, int nudgeY){	currentSubject = &currentNode->subjects[_state->orientation];	GUI_Reset();	GUI_ShowNode(_state->nodeName, _state->orientation, MODIFIER_NONE, 0, 0);	GUI_ShowMovement(nudgeX, nudgeY);	setupAction();	Audio_SetAmbience(currentNode->zone);	Audio_SetMusic(_state->chapter);#ifdef DEBUG	printf("%s %c\n", _state->nodeName, orientationNames[_state->orientation]);#endif} static voidupdateChapter(void){	if(Chapters_Update(currentSubject, _state)) {		GUI_ShowSave();		Prefs_Save();	}} static voidaction(int autoDoor){	switch(currentSubject->type) {	case SUBJECTTYPE_DOOR: {		NodeSubject *nodeSubject = Doors_Action(&currentSubject->data.door, _state, autoDoor);		if(nodeSubject != NULL) {			updateChapter();			moveTo(nodeSubject);			refreshNode(0, 1);		}	} break;	case SUBJECTTYPE_TERMINAL:		if(Terminals_Action(&currentSubject->data.terminal, _state)) {			updateChapter();			Audio_SetMusic(_state->chapter);		}		break;	case SUBJECTTYPE_MAZE:		moveTo(Mazes_Action(&currentSubject->data.maze, _state));		refreshNode(0, 1);		break;	default: break;	} 	if(_state->completed) {		int showSecret = _state->energy > 0;#ifndef DEBUG		if(_state->usedWalkthrough) {			showSecret = 0;		}#endif		GUI_ShowCredits(showSecret);	}} static voidturn(int direction){	Audio_PlayEffect(EFFECT_FOOTSTEP_TURN);	_state->orientation = (_state->orientation + NumberOfOrientations + direction) % NumberOfOrientations;	refreshNode(direction, 0);	GUI_ShowFootstep(direction == -1 ? FOOTSTEP_LEFT : FOOTSTEP_RIGHT);} static voidstepForward(void){	if(currentSubject->type == SUBJECTTYPE_NODE) {		Audio_PlayFootstep();		moveTo(&currentSubject->data.node);		refreshNode(0, 1);	} else {		Audio_PlayEffect(EFFECT_FOOTSTEP_COLLIDE);	}	GUI_ShowFootstep(FOOTSTEP_STRAIGHT);} static voidstepBackward(void){	/*		A step backwards is a "180° rotation",		followed by a step forwards or action,		followed by another "180° rotation". 		This is partly due to the fact that nodes		in Hiversaires don't necessarily agree on		which direction is north.	*/ 	int succeeded = 1;	Audio_PlayEffect(EFFECT_FOOTSTEP_TURN);	_state->orientation = (_state->orientation + 2) % NumberOfOrientations; 	refreshNode(0, 0); 	if(currentSubject->type == SUBJECTTYPE_NODE) {		moveTo(&currentSubject->data.node);	} else if(currentSubject->type == SUBJECTTYPE_DOOR || currentSubject->type == SUBJECTTYPE_MAZE) {		action(1);	} else {		succeeded = 0;	} 	_state->orientation = (_state->orientation + 2) % NumberOfOrientations;	refreshNode(0, succeeded ? -1 : 0);	GUI_ShowFootstep(FOOTSTEP_STRAIGHT);} static voidreset(void){	Data_ResetState(_state);	_state->loaded = 1;	currentNode = Nodes_Get(_state->nodeName);	refreshNode(0, 0);	GUI_ShowHomeMenu();	GUI_ShowSave();	Prefs_Save();} static voidenterGame(void){	begun = 1;	GUI_ShowHomeMenu();} voidPlay_Init(GameState *state){	_state = state; 	if(!_state->loaded || _state->completed) {		printf("\nWelcome, newcomer, to\n");		Data_ResetState(_state);		_state->loaded = 1;	} else {		printf("\nWelcome back, friend, to\n");	} 	Audio_SetActive(_state->audio);	currentNode = Nodes_Get(_state->nodeName);	refreshNode(0, 0); 	GUI_RunSplash(enterGame); 	printf(		"		\n\n"		"		HH   HH   HH\n"		"		HH   HH   HH\n"		"		HH   HH   HH\n"		"		HHHHHHHHHHHH\n"		"		HH   HH   HH\n"		"		HH   HH   HH\n"		"		HH   HH   HH\n"		"		\n"		"		HHIVERSAIRES\n\n");} voidPlay_Quit(void){	_state = NULL;} voidPlay_ProcessInput(InputType type, int down){	if(!begun) {		GUI_SkipSplash();		return;	} 	if(_state->completed) {		if(down && (type == INPUTTYPE_ACTION || type == INPUTTYPE_CENTER || type == INPUTTYPE_FORWARD)) {			GUI_LaunchCreditURL();		}		return;	} 	/*		Any incoming input during a walkthrough		cancels it part-way.	*/	walkthroughTime = -1; 	if(down) {		switch(type) {#ifdef DEBUG		case DEBUGINPUTTYPE_ENERGY:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			_state->energy = (_state->energy + 1) % 4;			printf("DEBUG: energy = %d\n", _state->energy);			GUI_ShowEnergy(_state->energy);			break;		case DEBUGINPUTTYPE_CLOCK:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			_state->clock = (_state->clock + 1) % 3;			printf("DEBUG: clock = %d\n", _state->clock);			GUI_ShowClock(_state->clock);			break;		case DEBUGINPUTTYPE_STUDIO:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			_state->studio = !_state->studio;			printf("DEBUG: studio = %d\n", _state->studio);			break;		case DEBUGINPUTTYPE_SEAL_1:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			_state->seals[0] = (_state->seals[0] + 1) % NumberOfZones;			printf("DEBUG: seal 1 = %d\n", _state->seals[0]);			GUI_ShowSeals(_state->seals[0], _state->seals[1]);			break;		case DEBUGINPUTTYPE_SEAL_2:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			_state->seals[1] = (_state->seals[1] + 1) % NumberOfZones;			printf("DEBUG: seal 2 = %d\n", _state->seals[1]);			GUI_ShowSeals(_state->seals[0], _state->seals[1]);			break;		case DEBUGINPUTTYPE_SECRET:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			_state->secret = !_state->secret;			printf("DEBUG: secret = %d\n", _state->secret);			break;		case DEBUGINPUTTYPE_TIME:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			_state->time = !_state->time;			printf("DEBUG: time = %d\n", _state->time);			break;		case DEBUGINPUTTYPE_CHAPTER:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			_state->chapter = (_state->chapter + 1) % NumberOfChapters;			Audio_SetMusic(_state->chapter);			printf("DEBUG: chapter = %d\n", _state->chapter);			break;		case DEBUGINPUTTYPE_UNLOCK_DOORS:			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			Doors_unlockAll = !Doors_unlockAll;			printf("DEBUG: unlock all doors set to = %d\n", Doors_unlockAll);			break;#endif		case INPUTTYPE_LEFT: turn(-1); break;		case INPUTTYPE_RIGHT: turn(1); break;		case INPUTTYPE_FORWARD: stepForward(); break;		case INPUTTYPE_BACK: stepBackward(); break;		case INPUTTYPE_ACTION: action(0); break;		case INPUTTYPE_CENTER:			/*				Depending on the subject, a CENTER input				is either a step forward or an action.			*/			if(currentSubject->type == SUBJECTTYPE_NODE) {				stepForward();			} else {				action(0);			}			break;		case INPUTTYPE_RESET:			/*				Unlike the other inputs above,				RESET starts a countdown as a way				to confirm the player's intention.			*/			resetTime = _totalTime;			GUI_TintRed();			Audio_PlayEffect(EFFECT_ACTION_SEAL_INACTIVE);			break;		case INPUTTYPE_WALKTHROUGH:			/*				WALKTHROUGH, like RESET, starts a countdown.			*/			walkthroughStartTime = _totalTime;			GUI_TintCyan();			Audio_PlayEffect(EFFECT_ACTION_SEAL_INIT);			break;		}	} else {		switch(type) {		case INPUTTYPE_RESET:			/* Cancel the RESET countdown */			resetTime = -1;			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			GUI_StopTint();			break;		case INPUTTYPE_WALKTHROUGH:			/* Cancel the WALKTHROUGH countdown */			walkthroughStartTime = -1;			Audio_PlayEffect(EFFECT_ACTION_ENERGY_ACTIVE);			GUI_StopTint();			break;		default: break;		}	}} voidPlay_Update(double totalTime, double deltaTime){	(void)deltaTime;	_totalTime = totalTime; 	/*		If the RESET countdown ticks to zero,		we reset the game state.	*/	if(resetTime != -1 && _totalTime - resetTime > 2) {		resetTime = -1;		walkthroughStartTime = -1;		walkthroughTime = -1;		GUI_StopTint();		Audio_PlayEffect(EFFECT_ACTION_DOOR_ACTIVE);		reset();	} 	/*		If the WALKTHROUGH countdown ticks to zero,		we reset the game state, and initialize the walkthrough.	*/	if(walkthroughStartTime != -1 && _totalTime - walkthroughStartTime > 2) {		resetTime = -1;		walkthroughStartTime = -1;		GUI_StopTint();		Audio_PlayEffect(EFFECT_ACTION_DOOR_ACTIVE);		reset();		Walkthrough_Reset();		walkthroughTime = _totalTime;	} 	/*		We run the walkthrough if it is active.	*/	if(!_state->completed && walkthroughTime != -1 && _totalTime - walkthroughTime > walkthroughDelay) {		_state->usedWalkthrough = 1;		Play_ProcessInput(Walkthrough_Next(), 1);		walkthroughTime = _totalTime;	}}

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