/* 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 "features.h" void drawFeature(bool transparent, bool reflected) { Feature *feature; FeatureDef *featureDef; GLColor c; if (!transparent) { return; } feature = (Feature*)self; featureDef = (FeatureDef*)feature->definition; c = featureDef->color; switch (feature->featureType) { case FEATURE_TELEPORTER: c.color[3] = (!feature->active) ? 0.25 : 0.5; break; case FEATURE_EXIT: c.color[3] = (!feature->active) ? 0.1 : 0.5; break; case FEATURE_SAVE_POINT: c.color[3] = (!feature->active) ? 0.25 : self->custom; break; case FEATURE_INFORMATION: c.color[3] = 0.5; break; } if (feature->health > 0) { drawCyclinder(feature->position, featureDef->segments, featureDef->radius, featureDef->height, c); } else { drawCyclinder(feature->position, featureDef->segments, featureDef->radius + (self->health / 10), featureDef->height, c); } } void fireFeatureTrigger(char *name) { int triggerId = -1; for (Feature *feature = (Feature*)entityManager->featureList.getFirstElement() ; feature != NULL ; feature = (Feature*)feature->next) { if (feature->name == name) { if (feature->triggerId != -1) { triggerId = feature->triggerId; } feature->active = false; feature->health = 0; } } } char *extractEnemyFromList(char *enemyList) { static String enemies[25]; int i; String list; if (strcmp(enemyList, "") == 0) { printf("WARNING: extractEnemyFromList() - Enemy List is blank! Returning NME_PISTOL_BLOB!\n"); return "NME_PISTOL_BLOB"; } i = 0; list = enemyList; char *token = strtok(list.getText(), "|"); while (token != NULL) { enemies[i] = token; token = strtok(NULL, "|"); i++; } return enemies[rand() % i].getText(); } void spawnerSpawnEntity() { //debug(("spawnerSpawnEntity()\n")); Vector position; Feature *feature = (Feature*)self; feature->thinkTime = Math::rrand(feature->minSpawnTime, feature->maxSpawnTime); position = feature->position; Properties props; props.setProperty("position", position); props.setProperty("name", feature->spawnedName.getText()); if (feature->blobType == "") { if (feature->armySpawner) { // too many enemies... if (entityManager->enemyList.getSize() > 75) { return; } } debug(("spawnerSpawnEntity() - Spawning Enemy\n")); position.x += Math::rrand(-((rand() % 5) * 10), ((rand() % 5) * 10)); position.y += Math::rrand(-((rand() % 5) * 10), ((rand() % 5) * 10)); props.setProperty("classname", extractEnemyFromList(feature->enemyType.getText())); addEnemy(&props); if ((--feature->custom) == 0) { feature->health = 0; feature->thinkTime = RAND_MAX; } } else { if (feature->blobType == "BLOB_SOLDIER") { // too many blobs... don't bother... if (getBlobSoldierCount() >= 12) { return; } props.setProperty("name", randomNameGenerator->getRandomName()); } if (feature->armySpawner) { if (Math::getDistance(position, player->position) > 2500) { return; } } else { if ((--feature->custom) == 0) { feature->health = 0; feature->thinkTime = RAND_MAX; } } debug(("spawnerSpawnEntity() - Spawning Blob\n")); props.setProperty("classname", feature->blobType.getText()); addBlob(&props); } addTeleportParticles(position); if (!feature->silent) { audio->playSound(SND_TELEPORT2, CH_SPAWN, camera->getSoundDistance(self->position)); } debug(("spawnerSpawnEntity() - Done\n")); } void exitPointSearch() { BoundingBox box; if (player->health < 0) { return; } Feature *feature = (Feature*)self; box = feature->boundingBox; box.mins.x -= 6; box.mins.y -= 6; box.maxs.x += 6; box.maxs.y += 6; if (Collision::collision(player->position, player->boundingBox, self->position, box)) { if (!feature->active) { game->setGamePlayMessage(INFO_GENERAL, _("Cannot exit yet - Primary objectives not completed")); return; } Unit *teeka = findTeeka(); if (teeka != NULL) { if (Math::getDistance(teeka->position, player->position) > 100) { game->setGamePlayMessage(INFO_GENERAL, _("Cannot leave without Teeka")); return; } } game->setGamePlayMessage(INFO_GENERAL, _("Press ACTION to exit mission")); if (game->isControl(CTRL_ACTION)) { String exitToMission = mission->getNextMissionName(); debug(("Going to mission '%s'\n", exitToMission.getText())); if (exitToMission == "") { exitToMission = feature->targetMission.getText(); } if (exitToMission != "TITLE_SCREEN") { game->setMissionStatus(MISSION_COMPLETE); game->changingLevel = true; game->targetMission = exitToMission.getText(); game->resetControl(CTRL_ACTION); } else { game->setMissionStatus(MISSION_QUITTING); } } } feature->active = mission->allObjectiveComplete(); self->thinkTime = 1; } void teleporterSearch() { BoundingBox box; Vector mins, maxs; addTeleporterParticle(self->position); mins = self->position; mins.x -= 10; mins.y -= 10; maxs = self->position; maxs.x += 10; maxs.y += 10; bbManager->initCollisionCandidates(mins, maxs); Entity *ent; bool doTeleportEffect; while ((ent = bbManager->getNext()) != NULL) { doTeleportEffect = true; switch (ent->entityType) { case ET_BLOB: case ET_BIOMECH: case ET_WEAPON: case ET_ITEM: case ET_PUSHBLOCK: doTeleportEffect = true; break; case ET_BULLET: case ET_DECORATION: doTeleportEffect = false; break; default: continue; break; } if ((ent->health < 0) || (ent->flags & EF_TELEPORTING)) { continue; } if (Math::getDistance(ent->position, self->position) <= (5 + ent->boundingBox.getHorizontalSize())) { if (doTeleportEffect) { addTeleportParticles(ent->position); audio->playSound(SND_TELEPORT3, (ent == player) ? CH_PLAYER : CH_SPAWN, camera->getSoundDistance(self->position)); } if (ent->entityType == ET_BLOB) { ent->destination = self->destination; ent->action = teleportUnit; ent->velocity.set(0, 0, 0); ent->thinkTime = 100; ent->contentType = CONTENTS_SOLID; ((Unit*)ent)->liquidLevel = 0; ent->flags |= EF_TELEPORTING; } else { ent->position = self->destination; if (doTeleportEffect) { addTeleportParticles(ent->position); } } if (ent == player) { blobsRunToBob(); } } } } void savePointSearch() { BoundingBox box; if ((player->health < 0) || (!(player->flags & EF_ONGROUND))) { return; } Feature *feature = (Feature*)self; feature->thinkTime = 1; box = feature->boundingBox; box.mins.x -= 6; box.mins.y -= 6; box.maxs.x += 6; box.maxs.y += 6; if (Collision::collision(player->position, player->boundingBox, self->position, box)) { feature->custom += (0.01 * engine->getTimeDifference(TD_LOGIC)); game->canSaveGame = true; game->setGamePlayMessage(INFO_GENERAL, _("Press ACTION to Save Game")); if (game->isControl(CTRL_ACTION)) { saveGame(); } } else { feature->custom -= (0.01 * engine->getTimeDifference(TD_LOGIC)); } Math::limit(&feature->custom, 0.3, 0.8); } void disableSavePoints() { for (Feature *feature = (Feature*)entityManager->featureList.getFirstElement() ; feature != NULL ; feature = (Feature*)feature->next) { if (feature->featureType == FEATURE_SAVE_POINT) { feature->active = false; } } } void infoPointSearch() { BoundingBox box; if ((player->health < 0) || (!(player->flags & EF_ONGROUND))) { return; } Feature *feature = (Feature*)self; feature->thinkTime = 1; box = feature->boundingBox; box.mins.x -= 4; box.mins.y -= 4; box.maxs.x += 4; box.maxs.y += 4; if (Collision::collision(player->position, player->boundingBox, self->position, box)) { game->setGamePlayMessage(INFO_GENERAL, _("Press ACTION to get Information")); if (game->isControl(CTRL_ACTION)) { game->resetControl(CTRL_ACTION); feature->thinkTime = 5; showMessageDialog(feature->name.getText(), "OK"); } } } void grapplePointSearch() { BoundingBox box; if ((player->health < 0) || (!(player->flags & EF_ONGROUND)) || (player->action != NULL)) { return; } Feature *feature = (Feature*)self; box = feature->boundingBox; box.mins.x -= 8; box.mins.y -= 8; box.maxs.x += 8; box.maxs.y += 8; if (Math::getDistance(feature->position, player->position) <= 10) { int slot = game->getItemSlot(_("Grappling Hook")); if ((slot == -1) && (!game->cheatItems)) { game->setGamePlayMessage(INFO_GENERAL, _("Grappling Hooks required")); return; } game->setGamePlayMessage(INFO_GENERAL, _("Press ACTION to use Grappling Hook")); if (game->isControl(CTRL_ACTION)) { if (!game->cheatItems) { game->item[slot]->itemPower--; if (game->item[slot]->itemPower == 0) { game->removeItem(_("Grappling Hook")); } } game->resetControl(CTRL_ACTION); addGrapplingHook(player, self->destination); faceLocation(player, self->destination); String s; s.setText("str_item_%d", slot); textureManager->removeTexture(s.getText()); audio->playSound(SND_GRAPPLE_FIRE, CH_PLAYER, 128); } } } void enableFeature(char *name, bool enable) { for (Feature *feature = (Feature*)entityManager->featureList.getFirstElement() ; feature != NULL ; feature = (Feature*)feature->next) { if (feature->name == name) { feature->active = enable; } } } void addFeature(Properties *props) { debug(("addFeature()\n")); int type = entityManager->getEntityType(props); Feature *feature = entityManager->spawnFeature(type); switch (type) { case FEATURE_SPAWNER: feature->custom = props->getInt("numberToSpawn", 1); feature->action = &spawnerSpawnEntity; break; case FEATURE_TELEPORTER: feature->action = &teleporterSearch; break; case FEATURE_EXIT: feature->action = &exitPointSearch; feature->thinkTime = 1; break; case FEATURE_SAVE_POINT: feature->action = &savePointSearch; feature->custom = 0.3; break; case FEATURE_INFORMATION: feature->action = &infoPointSearch; feature->custom = 1; break; case FEATURE_GRAPPLE_POINT: feature->action = &grapplePointSearch; feature->custom = 1; break; default: printf("WARNING: addFeature() - Feature Type not recognised!\n"); break; } feature->load(props); feature->draw = drawFeature; getCluster(feature, 3); debug(("addFeature() - Done\n")); } void doFeatures() { // made true by save points game->canSaveGame = false; for (Feature *feature = (Feature*)entityManager->featureList.getFirstElement() ; feature != NULL ; feature = (Feature*)feature->next) { self = feature; if (feature->health <= 0) { feature->health -= engine->getTimeDifference(TD_LOGIC); if (feature->health <= -100) { feature = (Feature*)feature->previous; entityManager->featureList.remove(feature->next); continue; } } if ((feature->active) || (feature->featureType == FEATURE_EXIT)) { if (feature->performNextAction(engine->getTimeDifference(TD_LOGIC))) { if (feature->action != NULL) { feature->action(); } } } if (withinViewableRange(feature)) { entityManager->addEntityToDrawList(feature); } } }