// Level.cpp : Level class // // 08.09.1998 / DH - First release // 16.11.1998 / DH - Vertical scrolling // 17.11.1998 / DH - Redesign // 23.11.1998 / DH - Lights redone // 30.11.1998 / DH - Weather extended // 02.12.1998 / DH - Level dynamically from memory // // Copyright (c) 1998 Daniel Hartmeier. All rights reserved. #include "Level.hpp" #include "Sound.hpp" #include "Pad.hpp" // ------------------------------------------------------------------- extern "C++" Image *imageSprites; extern "C++" Image *imageTiles; extern "C++" Sound *sound; // ------------------------------------------------------------------- inline int abs(int i) { return (i < 0 ? -i : i); } bool collision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { return (abs(x1-x2) < (w1+w2)/2) && (abs(y1-y2) < (h1+h2)/2); } // ------------------------------------------------------------------- Level::Level(Handler &handler, Hero &hero, Image *image, const unsigned char *data) : handler(handler), hero(hero), image(image), center(0), back(0), weather(0) { int width = data[0]; int height = data[1]; center = new Layer(*this, 256, width , height , data+2, data+2+256 ); back = new Layer(*this, 512, 20+(width-20)/2, 16+(height-16)/2, data+2, data+2+256+width*height); objects = data+2+256+width*height+(20+(width-20)/2)*(16+(height-16)/2); exitsprite = new ExitSprite(imageSprites); } Level::~Level() { if (weather) delete weather; delete exitsprite; for (Bullet *bullet = bullets.First(); bullet; bullet = bullets.Remove()) delete bullet; for (Enemy *enemy = enemies.First(); enemy; enemy = enemies.Remove()) delete enemy; for (Light *light = lights.First(); light; light = lights.Remove()) delete light; delete back; delete center; } void Level::reset() { hero.setLight(0); exitsprite->setLight(0); for (Bullet *bullet = bullets.First(); bullet; bullet = bullets.Remove()) delete bullet; for (Enemy *enemy = enemies.First(); enemy; enemy = enemies.Remove()) delete enemy; for (Light *light = lights.First(); light; light = lights.Remove()) delete light; back->init(); center->init(); hero.move(10, 12); loadingGun = false; // Add exit light Light *light = new Light(0, 0, 0, 0, 0, 64); lights.Append(light); exitsprite->setLight(light); // Add objects for (const unsigned char *object = objects; *object; object += 3) { int x = object[1]*16, y = object[2]*16; if (object[0] == 1) { hero.move(x+8, y+8); } else if (object[0] == 2) exitsprite->move(x, y); else if (object[0] == 3) { Enemy *enemy = new Spider(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 4) { Enemy *enemy = new Scorpion(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 5) { Enemy *enemy = new BeetleA(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 6) { Enemy *enemy = new BeetleB(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 7) { Enemy *enemy = new BeetleC(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 8) { Enemy *enemy = new Duck(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 9) { Enemy *enemy = new Bird(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 10) { Enemy *enemy = new Snail(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 11) { Enemy *enemy = new Mosquito(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 12) { Enemy *enemy = new Libelle(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 13) { Enemy *enemy = new AntA(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 14) { Enemy *enemy = new AntB(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 15) { Enemy *enemy = new Raupe(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 16) { Enemy *enemy = new Mushroom(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 17) { Enemy *enemy = new Fly(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 18) { Enemy *enemy = new Frog(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 19) { Enemy *enemy = new Crab(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } else if (object[0] == 20) { Enemy *enemy = new Dragon(this, imageSprites); enemy->move(x, y); enemies.Append(enemy); } } scroll(); return; } void Level::scroll() { // Scroll background if hero moves out int scrolledX = 0, scrolledY = 0; if (hero.x() > 200) scrolledX = moveHorz(200-hero.x()); else if (hero.x() < 120) scrolledX = moveHorz(120-hero.x()); if (hero.y() > /*160*/96) scrolledY = moveVert(/*160*/96-hero.y()); else if (hero.y() < 64) scrolledY = moveVert( 64-hero.y()); if (scrolledX || scrolledY) { hero.move(hero.x()+scrolledX, hero.y()+scrolledY); for (Enemy *enemy = enemies.First(); enemy; enemy = enemies.Next()) enemy->move(enemy->x()+scrolledX, enemy->y()+scrolledY); for (Bullet *bullet = bullets.First(); bullet; bullet = bullets.Next()) bullet->move(bullet->x()+scrolledX, bullet->y()+scrolledY); exitsprite->move(exitsprite->x()+scrolledX, exitsprite->y()+scrolledY); if (weather) weather->scroll(scrolledX, scrolledY); } if (hero.x() > 310) { hero.move(310, hero.y()); hero.setSpeedX(0); } else if (hero.x() < 10) { hero.move(10, hero.y()); hero.setSpeedX(0); } if (hero.y() > 246) { handler.heroDeath(); } else if (hero.y() < 10) { hero.move(hero.x(), 10); hero.setSpeedY(0); } return; } void Level::act(unsigned long pad) { // Hero hero.moveSpeed(); collisionTiles(hero); scroll(); if (hero.isDead()) handler.heroDeath(); else hero.act(pad); // Weather if (weather) weather->act(pad); // Bullets // S = Shoot if (pad & padS) { if (!loadingGun) { Bullet *bullet = new Bullet(&hero); bullet->move(hero.x()+hero.direction()*10, hero.y()+1); bullet->setSpeedX(hero.direction()*1800); addBullet(bullet); sound->play(SOUND_GUNSHOT); loadingGun = true; } } else loadingGun = false; // Move bullets, explode, remove Bullet *bullet = bullets.First(); while (bullet) { bullet->moveSpeed(); bullet->act(pad); if (bullet->isGone()) { delete bullet; bullet = bullets.Remove(); } else { if (!bullet->isExploding()) { int cx, cy; unsigned char attr; center->checkCollision(bullet->x(), bullet->y(), cx, cy, attr); if (attr && 1) { bullet->explode(); sound->play(SOUND_EXPLOSION); } } bullet = bullets.Next(); } } // Enemies Enemy *enemy = enemies.First(); while (enemy) { enemy->moveSpeed(); collisionTiles(*enemy); // ... for (Bullet *bullet = bullets.First(); bullet; bullet = bullets.Next()) { if (bullet->parent() != enemy) { if (!bullet->isExploding()) { if (collision(bullet->x(), bullet->y(), bullet->w(), bullet->h(), enemy->x(), enemy->y(), enemy->w(), enemy->h())) { sound->play(SOUND_EXPLOSION); bullet->explode(); enemy->hit(1024); } } } } if (enemy->isDead()) { delete enemy; enemy = enemies.Remove(); } else { enemy->act(pad); enemy = enemies.Next(); } } for (Enemy *enemy = enemies.First(); enemy; enemy = enemies.Next()) { if (collision(enemy->x(), enemy->y(), enemy->w(), enemy->h(), hero.x(), hero.y(), hero.w(), hero.h())) hero.hit(1024); } // Enemy shots for (Bullet *bullet = bullets.First(); bullet; bullet = bullets.Next()) { if (bullet->parent() != &hero) { if (collision(bullet->x(), bullet->y(), bullet->w(), bullet->h(), hero.x(), hero.y(), hero.w(), hero.h())) { sound->play(SOUND_EXPLOSION); bullet->explode(); hero.hit(1024); } } } if (collision(hero.x(), hero.y(), hero.w(), hero.h(), exitsprite->x(), exitsprite->y(), exitsprite->w(), exitsprite->h())) { hero.move(exitsprite->x(), exitsprite->y()); exitsprite->setReached(); handler.levelComplete(); } return; } void Level::collisionTiles(PersonSprite &sprite) { bool left = false, right = false, top = false, bottom = false; int sw = sprite.w()/2, sh = sprite.h()/2; int cx, cy; unsigned char attr; center->checkCollision(sprite.x()+sw, sprite.y(), cx, cy, attr); if (attr & 2) sprite.hit(1024); else if ((attr & 1) && (sprite.x() > cx-2-sw)) { right = true; sprite.move(cx-2-sw, sprite.y()); if (sprite.speedX() > 0) sprite.setSpeedX(0); } center->checkCollision(sprite.x()-sw, sprite.y(), cx, cy, attr); if (attr & 2) sprite.hit(1024); else if ((attr & 1) && (sprite.x() < cx+2+sw)) { left = true; sprite.move(cx+2+sw, sprite.y()); if (sprite.speedX() < 0) sprite.setSpeedX(0); } center->checkCollision(sprite.x(), sprite.y()-sh, cx, cy, attr); if (attr & 2) sprite.hit(1024); else if (attr & 1) { top = true; sprite.move(sprite.x(), cy+8+sh); if (sprite.speedY() < 0) sprite.setSpeedY(0); } center->checkCollision(sprite.x(), sprite.y()+sh, cx, cy, attr); if (attr & 2) sprite.hit(1024); else if (attr & 1) { bottom = true; sprite.move(sprite.x(), cy-8-sh); if (sprite.speedY() > 0) sprite.setSpeedY(0); } sprite.contact(left, right, top, bottom); return; } void Level::draw(GsOT *ot) { // Calculate light effects List effective; for (Light *light = lights.First(); light; light = lights.Next()) { int r = light->radius(), x = light->x(), y = light->y(); if ((x >= -r) && (x < 320+r) && (y >= -r) && (y < 256+r)) { light->update(); effective.Append(light); } } int baseIntensity = (weather ? weather->getBaseIntensity() : 128); lightTiles(effective, center->getTiles(), baseIntensity, false); lightTiles(effective, back->getTiles(), baseIntensity, true); lightSprite(effective, hero, baseIntensity, false); exitsprite->draw(ot); for (Bullet *bullet = bullets.First(); bullet; bullet = bullets.Next()) { lightSprite(effective, *bullet, baseIntensity, false); bullet->draw(ot); } hero.draw(ot); for (Enemy *enemy = enemies.First(); enemy; enemy = enemies.Next()) if (enemy->isVisible()) { lightSprite(effective, *enemy, baseIntensity, false); enemy->draw(ot); } center->draw(ot); if (weather) weather->draw(ot); back->draw(ot); return; } int Level::moveHorz(int x) { int bx = back->moveHorz(x); int cx = center->moveHorz(x); for (Light *light = lights.First(); light; light = lights.Next()) light->move(light->x()+(light->background() ? bx : cx), light->y()); return cx; } int Level::moveVert(int y) { int by = back->moveVert(y); int cy = center->moveVert(y); for (Light *light = lights.First(); light; light = lights.Next()) light->move(light->x(), light->y()+(light->background() ? by : cy)); return cy; } void Level::checkCollision(int x, int y, int &cx, int &cy, unsigned char &attr) const { center->checkCollision(x, y, cx, cy, attr); return; } void Level::lightSprite(List &lights, Sprite &sprite, int baseIntensity, bool background) { int r = baseIntensity, g = baseIntensity, b = baseIntensity; for (Light *light = lights.First(); light; light = lights.Next()) { int dx = abs(light->x() - sprite.x()); int dy = abs(light->y() - sprite.y()); int radius = light->radius() * light->radius(); int d = radius - (dx*dx + dy*dy); // 8192..0 gross: nahe, klein: entfernt if (d > 0) { int reduced = radius; if (background != light->background()) reduced += 3000; if (light->r() > r) r += (light->r() - r) * d / reduced; if (light->g() > g) g += (light->g() - g) * d / reduced; if (light->b() > b) b += (light->b() - b) * d / reduced; } } sprite.setRGB(r, g, b); return; } void Level::lightTiles(List &lights, Tile **tiles, int baseIntensity, bool background) { for (int r = 0; r < 17; ++r) for (int c = 0; c < 21; ++c) { Tile *tile = tiles[r*21+c]; if (tile) lightSprite(lights, *tile, baseIntensity, background); } return; } Tile *Level::createTile(unsigned char type) { Tile *tile = 0; if (type) { int col = type % 16; int row = type / 16; if ((row == 12) && (col == 13)) tile = new TorchTile(); else tile = new Tile(imageTiles, col*16, row*16); } return tile; } // ------------------------------------------------------------------- Level1::Level1(Handler &handler, Hero &hero, Image *image, const unsigned char *data) : Level(handler, hero, image, data) { weather = new SunnyWeather(); } void Level1::reset() { Level::reset(); } // ------------------------------------------------------------------- Level2::Level2(Handler &handler, Hero &hero, Image *image, const unsigned char *data) : Level(handler, hero, image, data) { weather = new RainyWeather(imageSprites, *this); } void Level2::reset() { Level::reset(); Light *light; light = new Torch(0, 0, 180, 128, 64, 70); lights.Append(light); hero.setLight(light); lights.Append(new Torch( 6*16+8, 13*16+8, 180, 128, 32, 50, true)); lights.Append(new Torch(15*16+8, 13*16+8, 180, 128, 32, 50, true)); lights.Append(new Torch(24*16+8, 13*16+8, 180, 128, 32, 50, true)); lights.Append(new Torch(33*16+8, 13*16+8, 180, 128, 32, 50, true)); lights.Append(new Torch(42*16+8, 13*16+8, 180, 128, 32, 50, true)); } // -------------------------------------------------------------------