/* Copyright (C) 2006 Parallel Realities This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "cutscene.h" CutsceneCamera *addCutsceneCamera(Properties *props) { char *name = props->getString("name", "unknown"); CutsceneCamera *camera = entityManager->spawnCutsceneCamera(name); camera->load(props); return camera; } void activateInGameCutscene(char *name, float delay) { debug(("activateInGameCutscene - %s, %.2f\n", name, delay)); for (CutsceneCamera *cutsceneCamera = (CutsceneCamera*)entityManager->cameraList.getFirstElement() ; cutsceneCamera != NULL ; cutsceneCamera = (CutsceneCamera*)cutsceneCamera->next) { if (cutsceneCamera->name == name) { if (cutsceneCamera->custom != 0) { continue; } camera->targetCamera = cutsceneCamera; camera->targetCamera->custom = delay; return; } } } CutsceneCamera *getCutsceneCamera(char *name) { for (CutsceneCamera *cutsceneCamera = (CutsceneCamera*)entityManager->cameraList.getFirstElement() ; cutsceneCamera != NULL ; cutsceneCamera = (CutsceneCamera*)cutsceneCamera->next) { if (cutsceneCamera->name == name) { if (cutsceneCamera->custom != 0) { continue; } return cutsceneCamera; } } return NULL; } void haltObjectsForCutscene() { entityManager->bulletList.clear(); for (Unit *unit = (Unit*)entityManager->blobList.getFirstElement() ; unit != NULL ; unit = (Unit*)unit->next) { unit->velocity.x = unit->velocity.y = 0; } for (Unit *unit = (Unit*)entityManager->enemyList.getFirstElement() ; unit != NULL ; unit = (Unit*)unit->next) { unit->velocity.x = unit->velocity.y = 0; } } void doInGameCutscene() { engine->clearInput(); engine->flushInput(); bool doneTriggers = false; CutsceneCamera *targetCamera = camera->targetCamera; targetCamera->velocity = targetCamera->destination; targetCamera->velocity -= targetCamera->position; targetCamera->velocity.normalize(); targetCamera->velocity = targetCamera->velocity * targetCamera->speed; while (targetCamera->custom > -targetCamera->timeout) { engine->doTimeDifference(); graphics->updateScreen(); engine->getInput(); graphics->clearScreen(); doGameObjects(); drawGameObjects(); haltObjectsForCutscene(); targetCamera->custom -= engine->getTimeDifference(TD_LOGIC); targetCamera->position += (targetCamera->velocity * engine->getTimeDifference(TD_LOGIC)); if (targetCamera->custom <= 0) { game->cutsceneType = CUTSCENE_INGAME; if ((targetCamera->custom <= -25) && (!doneTriggers)) { if (targetCamera->deferredSwitch != NULL) { handleStructureStateChange(targetCamera->deferredSwitch->getName(), targetCamera->deferredSwitch->active); toggleLaserTrap(targetCamera->deferredSwitch->getName()); fireTriggers(targetCamera->deferredSwitch->getName(), TRIGGER_TYPE_ANY); } fireTriggers(targetCamera->getName(), TRIGGER_TYPE_ANY); if (mission->allObjectiveComplete()) { fireTriggers("MISSION_COMPLETE", TRIGGER_TYPE_MISSION_COMPLETE); } doneTriggers = true; } } if (engine->userAccepts()) { targetCamera->custom = -targetCamera->timeout; } } game->cutsceneType = CUTSCENE_NONE; camera->targetCamera = NULL; engine->clearInput(); engine->flushInput(); resetEnemyThinkTimes(); } void loadCutscene(char *filename, List *data) { if (!engine->loadData(filename)) { graphics->showErrorAndExit(ERR_FILE, filename); } data->clear(); int i = -1; char *token = strtok((char*)engine->dataBuffer, "\n"); CutsceneCommand *command; while (true) { i++; //printf("%d) %s\n", i, token); if (strcmp(token, "@END@") == 0) { break; } // ignore blank lines if (strcmp(token, "") == 0) { token = strtok(NULL, "\n"); continue; } // looks like a comment. if (token[0] == '/') { token = strtok(NULL, "\n"); continue; } // ignore non command lines if (!strstr(token, ";")) { token = strtok(NULL, "\n"); continue; } command = new CutsceneCommand(); command->lineNumber = i; command->command = token; data->add(command); token = strtok(NULL, "\n"); } debug(("Loaded Cutscene '%s'. %d lines, %d commands\n", filename, i, data->getSize())); } void entityDoNothing() { } void cameraPositionAt(char *args) { static char name[128]; sscanf(args, "%*c%[^\"]", name); camera->targetCamera->position = entityManager->getEntityPosition(name); } void cameraStop(char *args) { camera->targetCamera->velocity.set(0, 0, 0); } void cameraLookAt(char *args) { static char name[128]; sscanf(args, "%*c%[^\"]", name); camera->targetCamera->destination = entityManager->getEntityPosition(name); } void cameraMoveTo(char *args) { static char name[128]; sscanf(args, "%*c%[^\"]", name); camera->targetCamera->velocity = entityManager->getEntityPosition(name); camera->targetCamera->velocity -= camera->targetCamera->position; camera->targetCamera->velocity.normalize(); } void entityPositionAt(char *args) { static char entityName[128], target[128]; sscanf(args, "%*c%[^\"]%*c%*c%*c%[^\"]", entityName, target); Unit *unit = (Unit*)entityManager->getOwnerByName(entityName); Vector v = entityManager->getEntityPosition(target); unit->position = v; unit->velocity.set(0, 0, 0); } void entityMoveTo(char *args) { static char entityName[128], target[128]; float speed; sscanf(args, "%*c%[^\"]%*c%*c%*c%[^\"]%*c%f", entityName, target, &speed); Unit *unit = (Unit*)entityManager->getOwnerByName(entityName); Vector v = entityManager->getEntityPosition(target); faceLocation(unit, v); unit->velocity = v; unit->velocity -= unit->position; unit->velocity.normalize(); unit->velocity = unit->velocity * speed; } void entityTurn(char *args) { } void entityLookAt(char *args) { static char entityName[128], target[128]; sscanf(args, "%*c%[^\"]%*c%*c%*c%[^\"]", entityName, target); Unit *unit = (Unit*)entityManager->getOwnerByName(entityName); Vector v = entityManager->getEntityPosition(target); faceLocation(unit, v); unit->action = entityDoNothing; unit->thinkTime = 0; } void unitLookRandom() { self->rotation.x = Math::rrand(0, 360); self->thinkTime = Math::rrand(50, 125); } void entityLookRandom(char *args) { static char entityName[128]; sscanf(args, "%*c%[^\"]", entityName); Unit *unit = (Unit*)entityManager->getOwnerByName(entityName); unit->action = &unitLookRandom; unit->thinkTime = 0; } void unitWatch() { Unit *unit = (Unit*)self; faceLocation(unit, unit->target->position); } void entityWatch(char *args) { static char entityName[128], target[128]; sscanf(args, "%*c%[^\"]%*c%*c%*c%[^\"]", entityName, target); Unit *unit = (Unit*)entityManager->getOwnerByName(entityName); unit->action = &unitWatch; unit->target = entityManager->getOwnerByName(target); unit->thinkTime = 0; } void entityJump(char *args) { } void entityVanish(char *args) { static char entityName[128], target[128]; sscanf(args, "%*c%[^\"]%*c%*c%*c%[^\"]", entityName, target); Entity *entity = (Entity*)entityManager->getAnyEntityByName(entityName); Vector v = entityManager->getEntityPosition(target); audio->playSound(SND_TELEPORT3, CH_ANY, camera->getSoundDistance(entity->position)); addTeleportParticles(entity->position); entity->position = v; entity->velocity.set(0, 0, 0); } void entityReappear(char *args) { } void entityStop(char *args) { static char unitName[128]; sscanf(args, "%*c%[^\"]", unitName); Unit *unit = (Unit*)entityManager->getOwnerByName(unitName); unit->velocity.x = unit->velocity.y = 0; } void buildCutsceneNarrative(char *filename, Texture **text) { // we need to do this for locale purposes... char fullFilePath[4096]; sprintf(fullFilePath, _("data/cutscenes/text/en/%s"), filename); char data[1024]; strcpy(data, ""); if (!engine->loadData(fullFilePath)) { graphics->showErrorAndExit(ERR_FILE, fullFilePath); } SDL_Surface *s; int i = -2; char *token = strtok((char*)engine->dataBuffer, "\n"); bool done = false; while (!done) { if (strcmp(token, "[END]") == 0) { done = true; } if (strcmp(token, "") == 0) { token = strtok(NULL, "\n"); continue; } if (token[0] == '/') { token = strtok(NULL, "\n"); continue; } if (token[0] == '[') { i++; if (i >= 0) { s = graphics->createSurface(750, 100); SDL_FillRect(s, NULL, graphics->black); graphics->setTransparent(s); graphics->setFont(FONT_NORMAL); graphics->setFontColor(255, 255, 255, 0, 0, 0); graphics->writeWrappedText(s, true, "%s", data); text[i] = graphics->createTexture(s); text[i]->iw = graphics->textWidth + 15; text[i]->ih = graphics->textHeight + 15; textureManager->addTexture(token, text[i]); } strcpy(data, ""); token = strtok(NULL, "\n"); continue; } sprintf(data, "%s %s", data, token); token = strtok(NULL, "\n"); } } void playAnimatedCutscene(float delay) { int tenPercent = (int)(graphics->screen->h * 0.05); int y = graphics->screen->h - tenPercent; while (delay > 0) { engine->doTimeDifference(); graphics->updateScreen(); engine->getInput(); graphics->clearScreen(); if (engine->keyState[SDLK_ESCAPE]) { delay = 0; } doGameObjects(); drawGameObjects(); camera->targetCamera->position += (camera->targetCamera->velocity * engine->getTimeDifference(TD_LOGIC)); delay -= engine->getTimeDifference(TD_LOGIC); graphics->setMode(MODE_2D); graphics->drawRect(0, 0, graphics->screen->w, tenPercent, GLColor::black, false); graphics->drawRect(0, y, graphics->screen->w, tenPercent, GLColor::black, false); graphics->drawLine(0, tenPercent, graphics->screen->w, tenPercent, GLColor::grey); graphics->drawLine(0, y, graphics->screen->w, y, GLColor::grey); } } void processAnimatedCutscene(List *data) { char function[1024], args[1024]; CutsceneCommand *cc = (CutsceneCommand*)data->getFirstElement(); game->cutsceneType = CUTSCENE_MOVIE; bool recognisedCommand = false; graphics->delay(1000, false); engine->resetTimeDifference(); bool done = false; while (!done) { recognisedCommand = false; sscanf(cc->command.getText(), "%s%*c%[^;]", function, args); debug(("%d) Command = '%s' '%s'\n", cc->lineNumber, function, args)); if (strcmp(function, "WAIT") == 0) { recognisedCommand = true; playAnimatedCutscene(atoi(args)); } else { for (int i = 0 ; i < 256 ; i++) { if (sceneInstruction[i].name == function) { recognisedCommand = true; sceneInstruction[i].function(args); break; } } } if (!recognisedCommand) { printf("ERROR: Cutscene command '%s' not recognised!\n", function); exit(1); } if (engine->keyState[SDLK_ESCAPE]) { done = true; } cc = (CutsceneCommand*)cc->next; if (cc == NULL) { done = true; } } for (Entity *ent = (Entity*)entityManager->blobList.getFirstElement() ; ent != NULL ; ent = (Entity*)ent->next) { ent->action = ent->walk; ent->thinkTime = 0; } for (Entity *ent = (Entity*)entityManager->enemyList.getFirstElement() ; ent != NULL ; ent = (Entity*)ent->next) { ent->action = ent->walk; ent->thinkTime = 0; } game->cutsceneType = CUTSCENE_NONE; entityManager->cameraList.remove(camera->targetCamera); camera->targetCamera = NULL; player->action = NULL; } void processStaticCutscene(List *data) { Texture *scene[5]; Texture *text[20]; Texture *currentScene[2]; Texture *currentText[2]; float currentSceneFade[2]; float currentTextFade[2]; for (int i = 0 ; i < 2 ; i++) { currentScene[i] = NULL; currentText[i] = NULL; currentTextFade[i] = 0; currentSceneFade[i] = 0; } for (int i = 0 ; i < 5 ; i++) { scene[i] = NULL; } for (int i = 0 ; i < 20 ; i++) { text[i] = NULL; } CutsceneCommand *cc = (CutsceneCommand*)data->getFirstElement(); char function[1024], args[1024], tmp[1024]; float delay = 0; int tx, ty; bool skip = false; bool done = false; glClearColor(0.0f, 0.0f, 0.0f, 0.0f); graphics->clearScreen(); graphics->delay(1000, false); tx = graphics->getOffsetX() + 25; ty = graphics->screen->h - 90; int sceneIndex = 0; int textIndex = 0; while (!done) { engine->doTimeDifference(); graphics->updateScreen(); engine->getInput(); graphics->clearScreen(); graphics->setMode(MODE_2D); if (engine->keyState[SDLK_ESCAPE]) { done = true; skip = true; audio->stopMusic(); } for (int f = 0 ; f < 2 ; f++) { if (currentScene[f] != NULL) { glColor4f(1.0, 1.0, 1.0, currentSceneFade[f]); renderScaledTexture(currentScene[f]); } if (f == 0) { graphics->fade(0.85, tx - 10, ty - 10, 770, 80); graphics->outlineRect(tx - 10, ty - 10, 770, 80, GLColor::grey); } if (currentText[f] != NULL) { glColor4f(1.0, 1.0, 1.0, currentTextFade[f]); graphics->blit(currentText[f], tx, ty, false); } } if (currentSceneFade[0] < 1) { currentSceneFade[0] += engine->getTimeDifference(TD_LOGIC) * 0.005; } if (currentSceneFade[1] > 0) { currentSceneFade[1] -= engine->getTimeDifference(TD_LOGIC) * 0.005; } if (currentTextFade[0] < 1) { currentTextFade[0] += engine->getTimeDifference(TD_LOGIC) * 0.005; } if (currentTextFade[1] > 0) { currentTextFade[1] -= engine->getTimeDifference(TD_LOGIC) * 0.005; } delay -= engine->getTimeDifference(TD_LOGIC); if (delay <= 0) { cc = (CutsceneCommand*)cc->next; if (cc == NULL) { done = true; } if (cc != NULL) { sscanf(cc->command.getText(), "%s%*c%[^;]", function, args); if (function[strlen(function) - 1] == ';') { function[strlen(function) - 1] = '\0'; } debug(("%s (%s)\n", function, args)); if (strcmp(function, "LOAD_IMAGE") == 0) { sscanf(args, "%s", tmp); scene[sceneIndex++] = getRequiredTexture(tmp); } else if (strcmp(function, "LOAD_NARRATIVE") == 0) { sscanf(args, "%s", tmp); buildCutsceneNarrative(tmp, text); } else if (strcmp(function, "PREPARE_SCENE") == 0) { sceneIndex = textIndex = 0; } else if (strcmp(function, "SHOW_IMAGE") == 0) { currentScene[1] = currentScene[0]; currentSceneFade[1] = 1; currentScene[0] = scene[sceneIndex++]; currentSceneFade[0] = 0; } else if (strcmp(function, "SHOW_NARRATIVE") == 0) { currentText[1] = currentText[0]; currentTextFade[1] = 1; currentText[0] = text[textIndex++]; currentTextFade[0] = 0; } else if (strcmp(function, "WAIT") == 0) { delay = atof(args); } } } } if (!skip) { audio->fadeMusic(); graphics->delay(3500, true); } graphics->clearScreen(); graphics->delay(1000, true); audio->stopMusic(); glClearColor(mission->fogColor.color[0], mission->fogColor.color[1], mission->fogColor.color[2], mission->fogColor.color[3]); } void processCutscene(List *data) { debug(("processCutscene - %d\n", data->getSize())); if (data->getSize() == 0) { return; } audio->stopMusic(); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); graphics->clearScreen(); audio->loadMusic("music/Return To Napali.s3m"); audio->playMusic(); glClearColor(mission->fogColor.color[0], mission->fogColor.color[1], mission->fogColor.color[2], mission->fogColor.color[3]); engine->resetTimeDifference(); Properties props; props.setProperty("name", "CutsceneCamera"); props.setProperty("position", "0 0 0"); camera->targetCamera = addCutsceneCamera(&props); for (Entity *ent = (Entity*)entityManager->blobList.getFirstElement() ; ent != NULL ; ent = (Entity*)ent->next) { ent->action = entityDoNothing; ent->thinkTime = 0; } for (Entity *ent = (Entity*)entityManager->enemyList.getFirstElement() ; ent != NULL ; ent = (Entity*)ent->next) { ent->action = entityDoNothing; ent->thinkTime = 0; } entityManager->bulletList.clear(); particleManager->particleList.clear(); CutsceneCommand *cc = (CutsceneCommand*)data->getFirstElement(); if (strstr(cc->command.getText(), "STATIC")) { processStaticCutscene(data); } else { processAnimatedCutscene(data); } for (Entity *ent = (Entity*)entityManager->blobList.getFirstElement() ; ent != NULL ; ent = (Entity*)ent->next) { ent->action = ent->walk; ent->thinkTime = 0; } for (Entity *ent = (Entity*)entityManager->enemyList.getFirstElement() ; ent != NULL ; ent = (Entity*)ent->next) { ent->action = ent->walk; ent->thinkTime = 0; } player->action = NULL; if (game->totalRemaining == 0) { audio->loadMusic(mission->music.getText()); } else { audio->loadMusic("music/Longstabben.xm"); audio->loadSound(SND_WEATHER, "sound/ambience/general/klaxon.ogg"); } } // ============= init statements ======================== void initCutsceneInstructions() { int i = 0; // animated cut scenes... sceneInstruction[i++].set("CAMERA_POSITION_AT", &cameraPositionAt); sceneInstruction[i++].set("CAMERA_LOOK_AT", &cameraLookAt); sceneInstruction[i++].set("CAMERA_MOVE_TO", &cameraMoveTo); sceneInstruction[i++].set("CAMERA_STOP;", &cameraStop); sceneInstruction[i++].set("ENTITY_POSITION_AT", &entityPositionAt); sceneInstruction[i++].set("ENTITY_MOVE_TO", &entityMoveTo); sceneInstruction[i++].set("ENTITY_TURN", &entityTurn); sceneInstruction[i++].set("ENTITY_LOOK_AT", &entityLookAt); sceneInstruction[i++].set("ENTITY_LOOK_RANDOM", &entityLookRandom); sceneInstruction[i++].set("ENTITY_WATCH", &entityWatch); sceneInstruction[i++].set("ENTITY_JUMP", &entityJump); sceneInstruction[i++].set("ENTITY_VANISH", &entityVanish); sceneInstruction[i++].set("ENTITY_REAPPEAR", &entityReappear); sceneInstruction[i++].set("ENTITY_STOP", &entityStop); }