Example #1
0
Level::FileFormat Level::get_file_format(const Filename& filename)
{
    if (! ::exists(filename.get_rootful().c_str())) return FORMAT_NOTHING;
    std::ifstream file(filename.get_rootful().c_str(), std::ios::binary);
    // the length check before the read() was necessary for me on Linux
    // to get the Debugger past this, it got stuck on read() when nothing
    // was wrong.
    file.seekg (0, std::ios::end);
    if (file.tellg() < 8) {
        file.close();
        return FORMAT_NOTHING;
    }
    file.seekg(0, std::ios::beg);
    unsigned char buf[8];
    file.read((char*) &buf, 8);
    file.close();

    // A binary file has two-byte numbers in big endian
    // for rate, lixes, required, seconds at the beginning.
    // Neither should be > 0x00FF. If all of them are,
    // this is an ASCII file which shouldn't have '\0' chars.
    if (buf[0] == '\0' || buf[2] == '\0' || buf[4] == '\0' || buf[6] == '\0')
        return FORMAT_BINARY;

    // This isn't a binary file. Is it a Lemmini file?
    // Lemmini files start with "# LVL".
    else if (buf[0] == '#' && buf[1] == ' ' && buf[2] == 'L' && buf[3] == 'V')
        return FORMAT_LEMMINI;

    else return FORMAT_LIX;
}
Example #2
0
void Level::load_from_file(const Filename& filename)
{
    clear();
    status = GOOD;

    level_filename = filename.get_rootless();

    FileFormat fmt = get_file_format(filename);
    if (fmt == FORMAT_BINARY) {
        // load an original .LVL file from L1/ONML/...
        load_from_binary(filename);
    }
    else if (fmt == FORMAT_LEMMINI) {
        // load an .INI file from Lemmini
        load_from_lemmini(filename);
    }
    else {
        // load the regular Lix format
        std::vector <IO::Line> lines;
        if (IO::fill_vector_from_file(lines, filename.get_rootful())) {
            load_from_vector(lines);
        }
        else status = BAD_FILE_NOT_FOUND;
    }
    load_finalize();
}
Example #3
0
void LevelMetaData::read_metadata_binary(const Filename& fn)
{
    std::ifstream file(fn.get_rootful().c_str(), std::ios::binary);

    // see level_bi.cpp for documentation of the L1 format
    file.seekg(0x2);
    initial  = read_two_bytes_levelbi(file);
    required = read_two_bytes_levelbi(file);
    name_english = read_level_name_bytes(file);
    file.close();
}
Example #4
0
bool dir_exists(const Filename& fn)
{
    std::string dir = fn.get_rootful();
    if (dir.size() > 0 && dir[dir.size() - 1] == '/')
        dir.erase(--dir.end());
    // Allegro's file_exits sucks, you have to check twice like the following
    // if you want to find all directories, no matter whether FA_ARCH or not
    const bool a = file_exists(dir.c_str(), FA_ALL, 0);
    const bool b = file_exists(dir.c_str(), FA_ALL & ~ FA_DIREC, 0);
    return a && ! b;
}
Example #5
0
void LevelMetaData::read_metadata_lix(const Filename& fn)
{
    std::vector <IO::Line> lines;
    if (!IO::fill_vector_from_file(lines, fn.get_rootful())) {
        return;
    }
    // File exists
    for (IO::LineIt i = lines.begin(); i != lines.end(); ++i) {
        if (i->text1 == gloB->level_built)        built        = i->text2;
        if (i->text1 == gloB->level_name_german)  name_german  = i->text2;
        if (i->text1 == gloB->level_name_english) name_english = i->text2;
        if (i->text1 == gloB->level_initial)      initial      = i->nr1;
        if (i->text1 == gloB->level_required)     required     = i->nr1;
    }
}
Example #6
0
// Dateisuchfunktionen in verschiedenen Variationen
void find_files(
    const Filename& fn_where,
    const std::string& what,
    DoStr              dostr,
    void*              from
) {
    std::string where = fn_where.get_rootful();
    al_ffblk info;
    std::string search_for = where + what;
    if (al_findfirst(search_for.c_str(), &info,
     FA_RDONLY | FA_HIDDEN | FA_LABEL | FA_ARCH) == 0) {
        do {
            // Gefundene Datei zum Vektor hinzuf�gen
            Filename fn_result(where + info.name);
            dostr(fn_result, from);
        } while (al_findnext(&info) == 0);
        al_findclose(&info);
    }
}
Example #7
0
void LevelMetaData::read_metadata_lemmini(const Filename& fn)
{
    std::ifstream file(fn.get_rootful().c_str());
    if (! file.good()) return;

    // File exists
    std::string s;
    while (file >> s) {
        if (s == "name") {
            file >> s; // parse the "=";
            s.clear();
            char c;
            while (file.get(c)) {
                if (c == ' ' && s.empty()); // discard spaces before name
                else if (c == '\n' || c == '\r') break; // done reading name
                else s += c;
            }
            name_english = s;
        }
        else if (s == "numLemmings") {
Example #8
0
void find_dirs(const Filename& fn_where, DoStr dostr, void* from)
{
    al_ffblk info;
    std::string where = fn_where.get_rootful();
    if (where[where.size()-1] != '/') where += '/';
    if (where[where.size()-1] != '*') where += '*';
    if (al_findfirst(where.c_str(), &info,
     FA_RDONLY | FA_HIDDEN | FA_LABEL | FA_DIREC | FA_ARCH) == 0) {
        do {
            // Gefundenes Verzeichnis hinzuf�gen
            if ((info.attrib & FA_DIREC) == FA_DIREC && info.name[0] != '.') {
                std::string s = where;
                s.resize(s.size() -1 ); // * von der Maske abschnibbeln
                s += info.name;
                s += '/';
                Filename fn_result(s);
                dostr(fn_result, from);
            }
        } while (al_findnext(&info) == 0);
        al_findclose(&info);
    }
}
Example #9
0
void find_tree
(const Filename& fn_where, const std::string& what, DoStr dostr, void* from)
{
    std::string where = fn_where.get_rootful();
    // Nach Verzeichnissen suchen
    al_ffblk info;
    if (where[where.size()-1] != '/') where += '/';
    std::string search_for = where + '*';
    if (al_findfirst(search_for.c_str(), &info,
     FA_RDONLY | FA_HIDDEN | FA_LABEL | FA_DIREC | FA_ARCH) == 0) {
        do {
            // Dies nur f�r jedes Verzeichnis au�er . und .. ausf�hren:
            // Neue Suche mit gleichem Vektor im gefundenen Verzeichnis
            if ((info.attrib & FA_DIREC) == FA_DIREC && info.name[0] != '.') {
                Filename fn_recurs(where + info.name + '/');
                find_tree(fn_recurs, what, dostr, from);
            }
        } while (al_findnext(&info) == 0);
        al_findclose(&info);
    }
    // Nach Dateien suchen, die dem Suchkriterium entsprechen
    Filename fn_where_with_slash(where);
    find_files(fn_where_with_slash, what, dostr, from);
}
Example #10
0
File: cutbit.cpp Project: Lucki/Lix
Cutbit::Cutbit(const Filename& filename, const bool cut)
:
    bitmap  (0),
    xl      (0),
    yl      (0),
    x_frames(1),
    y_frames(1)
{
    // Angegebene Datei als Bild laden.
    // Wenn dies kein Bild ist, Fehlermeldung schreiben und abbrechen.
    bitmap = load_bitmap(filename.get_rootful().c_str(), 0);
    if (!bitmap) {
        std::string str = Language::log_bitmap_bad;
        str += " ";
        str += filename.get_rootless();
        Log::log(Log::ERROR, str);
        return;
    }
    if (cut) cut_bitmap();
    else {
        xl = bitmap->w;
        yl = bitmap->h;
    }
}
Example #11
0
void Replay::load_from_file(const Filename& fn)
{
    clear();

    level_filename   = fn; // Standardwert: Annahme, Level in Replaydatei
    unsigned long vm = 0;  // version_min erst spaeter setzen wegen add()

    std::vector <IO::Line> lines;
    if (!IO::fill_vector_from_file(lines, fn.get_rootful())) {
        file_not_found = true;
        holds_level    = false;
        return;
    }

    for (IO::LineIt i = lines.begin(); i != lines.end(); ++i) switch(i->type) {
    case '$':
        if      (i->text1 == gloB->replay_built_required) built_required = i->text2;
        else if (i->text1 == gloB->replay_permu         ) permu          = Permu   (i->text2);
        else if (i->text1 == gloB->replay_level_filename) {
            // We changed the names of the directories on 2012-04-12. Probably
            // a year from this time on, there shouldn't be any important
            // replays with the old path anymore. Then, remove this erase()
            // to finally allow a directory (levels-dir)/levels/ in theory.
            std::string filestr = i->text2;
            if (filestr.substr(0, 7) == "levels/") filestr.erase(0, 7);
            level_filename = Filename(
                gloB->dir_levels.get_dir_rootless() + filestr);
        }
        break;

    case '#':
        if      (i->text1 == gloB->replay_version_min   ) vm = i->nr1;
        break;

    case '+':
        if (i->text1 == gloB->replay_player
         || i->text1 == gloB->replay_friend) {
            add_player(i->nr1, LixEn::string_to_style(i->text2), i->text3);
            if (i->text1 == gloB->replay_player) player_local = i->nr1;
        }
        break;

    case '!': {
        Data d;
        d.update = i->nr1; // d.player ist zwar ein char, aber wir lesen ja
        d.player = i->nr2; // nicht aktiv longs ein, sondern weisen nur zu.
        d.what   = i->nr3;
        if      (i->text1 == gloB->replay_spawnint     ) d.action = SPAWNINT;
        else if (i->text1 == gloB->replay_skill        ) d.action = SKILL;
        else if (i->text1 == gloB->replay_assign       ) d.action = ASSIGN;
        else if (i->text1 == gloB->replay_assign_legacy) d.action = ASSIGN;
        else if (i->text1 == gloB->replay_aim          ) d.action = AIM;
        else if (i->text1 == gloB->replay_nuke         ) d.action = NUKE;
        add(d);
        break; }

    default:
        break;
    }

    // Variablen nach dem Laden zuweisen, damit add() nichts kaputtmacht
    version_min = vm;

    // check whether the pointed-to level exists, otherwise use itself
    // as a fallback level
    Level pointedto(level_filename);
    if (pointedto.get_status() == Level::BAD_FILE_NOT_FOUND
     || pointedto.get_status() == Level::BAD_EMPTY) {
        level_filename = fn;
    }

    // load the replay file itself as a level, to see whether there's a level
    // in the file itself. This is important e.g. for the extract button.
    Level itself(fn);
    if (itself.get_status() == Level::BAD_FILE_NOT_FOUND
     || itself.get_status() == Level::BAD_EMPTY) {
        holds_level = false;
    }
    else {
        holds_level = true;
        if (level_filename == fn) {
            built_required = Level::get_built(level_filename);
        }
    }

}
Example #12
0
void Replay::save_to_file(const Filename& s, const Level* const lev)
{
    bool save_level_into_file = holds_level
                             || level_filename == gloB->empty_filename
                             || lev != 0;

    // We currently override the above check, and will always save a level
    // into the replay, thus have the replay never point back into the level
    // tree.
    save_level_into_file = true;

    std::ofstream file(s.get_rootful().c_str());

    // Also override NOT saving the filename. Always save the filename right
    // now, and use the level in the replay as a fallback if there is nothing
    // at the pointed-to level position.
    if (true || !save_level_into_file) {
        built_required = Level::get_built(level_filename);
        // Write the path to the level, but omit the leading (dir-levels)/
        // This if is just against a crash in networking games.
        if (level_filename.get_rootless().size()
         >  gloB->dir_levels.get_dir_rootless().size())
        {
             file << IO::LineDollar(gloB->replay_level_filename,
                               level_filename.get_rootless().substr(
                               gloB->dir_levels.get_dir_rootless().size()))
             << IO::LineDollar(gloB->replay_built_required, built_required);
        }
    }
    file << IO::LineHash(gloB->replay_version_min, version_min)
         << std::endl;

    // Alle Spieler notieren.
    for (std::set <Player> ::const_iterator itr = players.begin();
     itr != players.end(); ++itr)
     file << IO::LinePlus(itr->number == player_local
             ? gloB->replay_player : gloB->replay_friend,
             itr->number, LixEn::style_to_string(itr->style), itr->name);

    if (players.size() > 1) {
        std::ostringstream pstr;
        pstr << permu;
        file << IO::LineDollar(gloB->replay_permu, pstr.str()) << std::endl;
    }

    file << std::endl;

    // Die einzelnen Aktionen schreiben
    for (It itr = data.begin(); itr != data.end(); ++itr) {
        file << IO::LineBang(itr->update, itr->player,
           itr->action == Replay::SPAWNINT ? gloB->replay_spawnint
         : itr->action == Replay::SKILL    ? gloB->replay_skill
         : itr->action == Replay::ASSIGN   ? gloB->replay_assign
         : itr->action == Replay::AIM      ? gloB->replay_aim
         : itr->action == Replay::NUKE     ? gloB->replay_nuke
                                           : Language::cancel,
         itr->what);
    }

    if (save_level_into_file) {
        file << std::endl;
        file << (lev ? *lev : Level(level_filename));
    }

    file.close();
}
Example #13
0
void Level::save_to_file(const Filename& filename) const
{
    std::ofstream file(filename.get_rootful().c_str());
    file << *this;
    file.close();
}
Example #14
0
void Level::load_from_binary(const Filename& filename)
{
    std::ifstream file(filename.get_rootful().c_str(), std::ios::binary);
    if (!file.good()) {
        status = BAD_FILE_NOT_FOUND;
        return;
    }

    // ==GLOBALS===============================================================

    // BYTES 0x0000 to 0x0001
    // Release Rate	: 0x0000 is slowest, 0x00FA is fastest
    // 0x00FA ist 250 im Dezimalsystem. 99 ist die hoechste Rate. If the value
    // is abstruse, it'll be corrected to > 1 and < some upper bound
    // later in load_from().
    spawnint_slow = 4 + Help::even(99 - read_two_bytes_levelbi(file)) / 2;

    // BYTES 0x0002 to 0x0003
    // Num of lemmings	: maximum 0x0072.  0x0010 would be 16 lemmings.
    initial = read_two_bytes_levelbi(file);

    // BYTES 0x0004 to 0x0005
    // Num to rescue	: should be less than or equal to number of lemmings
    required = read_two_bytes_levelbi(file);

    // BYTES 0x0006 to 0x0007
    // Time Limit	: max 0x00FF, 0x0001 to 0x0009 works best
    seconds = read_two_bytes_levelbi(file) * 60;

    // BYTES 0x0008 to 0x0017 (2 bytes each, only lower byte is used)
    // Num of skills	: max 0x00FA.  order is Climber, Floater, Bomber,
    // 		  Blocker,Builder, Basher, Miner, Digger
    for (int i = 0; i < 8; ++i) {
        LixEn::Ac ac = LixEn::NOTHING;
        switch (i) {
            case 0: ac = LixEn::CLIMBER;  break; // 0x0008 und 09
            case 1: ac = LixEn::FLOATER;  break; // 0x000A ...
            case 2: ac = LixEn::EXPLODER; break; // 0x000C
            case 3: ac = LixEn::BLOCKER;  break; // 0x000E
            case 4: ac = LixEn::BUILDER;  break; // 0x0010
            case 5: ac = LixEn::BASHER;   break; // 0x0012
            case 6: ac = LixEn::MINER;    break; // 0x0014 ...
            case 7: ac = LixEn::DIGGER;   break; // 0x0016 und 17
        }
        skills[ac] = read_two_bytes_use_only_second_byte_levelbi(file);
        // skill slots with 0 skills will be culled in the finalize function

        legacy_ac_vec.push_back(ac);
    }

    // ORIGHACK: If a two-player level is loaded, make the given calculation
    // for the time, as the result (5 -> 2 minutes) is a nice overtime for
    // a multiplayer game. The overtime starts counting after the first player
    // is out of lixes, but has some saved.
    // Also: Use knockback exploders instead of L1-style exploders.
    if (filename.get_rootful().find("network/") != std::string::npos) {
        if (skills.find(LixEn::EXPLODER) != skills.end()) {
            skills      [LixEn::EXPLODER2] = skills[LixEn::EXPLODER];
            skills.erase(LixEn::EXPLODER);
        }
        seconds = (seconds / 2) - 30;
        if (seconds <= 0) seconds = 15;
    }

    // BYTES 0x0018 to 0x0019
    // Start screen xpos : 0x0000 to 0x04F0.  is rounded to nearest multiple
    // 		    of 8.
    // We will multiply everything by 2 later, as L++ uses double the resol.
    start_x  = read_two_bytes_levelbi(file);
    size_x   = 1600;
    size_y   = 160;

    // BYTES 0x001A to 0x001B
    // Normal Graphic Set: 0x0000 is dirt, 0x0001 is fire, 0x0002 is squasher,
    // 		    0x0003 is pillar,0x0004 is crystal, 0x0005 is brick,
    // 		    0x0006 is rock, 0x0007 is snow and 0x0008 is bubble.
    // Bei mir zusaetzlich: 0x0009 is holiday
    int graphics_set = read_two_bytes_use_only_second_byte_levelbi(file);

    // ORIGHACK: Bei Levels aus den Verzeichnissen zu ONML oder Holiday
    // entsprechend um 5 erhoehen bzw. auf 9 setzen.
    const std::string& filestr = filename.get_rootful();
    if (filestr.find("ONML/")         != std::string::npos
     || filestr.find("onml/")         != std::string::npos
     || filestr.find("ore Lemmings/") != std::string::npos) graphics_set += 5;
    if (filestr.find("oliday")        != std::string::npos) graphics_set =  9;

    // BYTES 0x001C to 0x001D
    // Extended Graphic Set: corresponds to VGASPEC?.DAT
    int spec_graphics = read_two_bytes_use_only_second_byte_levelbi(file);
    if (spec_graphics != 0) {
        Pos ter;
        ter.x = 304;
        ter.y = 0;
        ter.ob = ObjLib::get_orig_vgaspec(spec_graphics);
        if (ter.ob) pos[Object::TERRAIN].push_back(ter);
        else status = BAD_IMAGE;
    }

    //  BYTES 0x001E to 0x001F
    // [04:14:50] <Mindless> "BYTES 0x001E to 0x001F" in level_bi.cpp
    // [04:15:37] <Mindless> that's 0x0000 in normal levels, 0xffff in
    // "Inroducing SuperLemming!" in which the gameplay is double speed
    // No support for this in L++ (yet).
    read_two_bytes_levelbi(file); // wegwerfen

    // ==OBJECTS===============================================================
    // BYTES 0x0020 to 0x011F (8 byte blocks)
    // each 8 byte block starting at byte 0x0020 represents an interactive
    // object. there can be a maximum of 32 objects.  write 0x00 to fill bytes
    // up to 0x0120 if there are less than 32 objects.
    for (int loop = 0; loop < 32; ++loop) {

        bool add = false; // Design der Funktion hier: vorzeitiges continue
        Pos  spe;         // fuehrte zu: zu wenige Werte werden ausgelesen
        // x pos  : min 0xFFF8, max 0x0638.  0xFFF8 = -24, 0x0001 = -16,
        //  0x0008= -8, 0x0010 = 0, 0x0018 = 8, ... , 0x0638 = 1576
        //  note: should be multiples of 8
        spe.x = read_two_bytes_levelbi(file);
        if (spe.x != 0) add = true; // 0x0001 ist -16; also 0x0000 wohl nix
        if (spe.x == 1) spe.x -= 1;
        if (spe.x >= 0xFFF8) spe.x -= 0x10000;
        spe.x -= 0x10;

        // y pos  : min 0xFFD7, max 0x009F.  0xFFD7 = -41, 0xFFF8 = -8,
        //  0xFFFF = -1, 0x0000 = 0, ... , 0x009F = 159.
        // 	note: can be any value in the specified range
        spe.y = read_two_bytes_levelbi(file);
        if (spe.y >= 0xFFD7) spe.y -= 0x10000;

        // L1 has a bug where special objects must be at a certain multiple
        // in x-direction, I think it's 8. I think there is no such condition
        // in y-direction. So, let's disable this: the following line.
        // spe.y -= spe.y % 4;

        // obj id : min 0x0000, max 0x000F.  the object id is different in each
        // 	graphics set, however 0x0000 is always an exit and 0x0001 is
        //  always a start. see appendix a for full object listings
        int spe_id = read_two_bytes_levelbi(file);

        // modifier : first byte can be 80 (do not overwrite existing terrain)
        // or 40 (must have terrain underneath to be visible). 00 specifies
        // always draw full graphic. second byte can be 8F (display graphic
        // upside-down) or 0F (display graphic normally)
        // Wird von L++ nicht unterstuetzt
        read_two_bytes_levelbi(file); // wegwerfen

        if (add) {
            spe.ob = ObjLib::get_orig_special(graphics_set, spe_id);
            if (spe.ob) {
                // Do not load/show the waving green and blue flags.
                // L++ features a better way of showing the goal ownership.
                if (spe.ob->type != Object::DECO || spe.ob->subtype != 1)
                 pos[spe.ob->type].push_back(spe);
            }
            else status = BAD_IMAGE;
        }
    }

    // ==TERRAIN===============================================================
    // BYTES 0x0120 to 0x075F (4 byte blocks)

    // each 4 byte block starting at byte 0x0120 represents a terrain object.
    // there can be a maximum of 400 terrain objects. Write 0xFF fill the bytes
    // up to byte 0x0760 if need be.
    for (int loop = 0; loop < 400; ++loop) {
        Pos  ter;
        bool add = true;

        // x ter : min 0x0000, max 0x063F.  0x0000 = -16, 0x0008 = -8,
        //  0x0010 = 0, 0x063f = 1583.
        // note: the xter also contains modifiers.  the first nibble can be...
        ter.x = read_two_bytes_levelbi(file);
        int first_nibble = ter.x / 0x1000;
        ter.x %= 0x1000; // Erstes Nibble entfernen
        ter.x -= 16;
        if (ter.x > 0x700) add = false; //ter.x -= 0x1000; // Sir Edmund, ...

        // y ter : 9-bit value. min 0xEF0, max 0x518.  0xEF0 = -38,
        //  0xEF8 = -37, 0x020 = 0, 0x028 = 1, 0x030 = 2, 0x038 = 3, ... ,
        //  0x518 = 159
        //  note: the yter value bleeds into the next value since it is 9bits.
        // terrain id: min 0x00, max 0x3F.
        // not all graphic sets have all 64 graphics.

        ter.y = read_two_bytes_levelbi(file);
        int terrain_id = ter.y % 0x0080; // Terrain-ID sind 7 Bits davon
        //if (ter.y >= 0xEF00) ter.y -= 0x10000;
        if (ter.y >= 0xE000) ter.y -= 0x10080;
        ter.y -= 0x0200;
        ter.y /= 0x0080; // Hintere 7 Bits abschnibbeln

        if (terrain_id == 0x7F) add = false;

        // Write 0xFF fill the bytes up to byte 0x0760 if need be.
        // Terrain-ID hat 7 Bits mit Maximum 0x3F, also ist 0x7F der Wert,
        // den die Terrain-ID annimmt, wenn das Objekt nicht existiert.
        if (add) {
            ter.ob = ObjLib::get_orig_terrain(graphics_set, terrain_id);
            // note: the xter also contains modifiers.  the first nibble can be
            // 	8 (don't overwrite existing terr., 4 (display upside-down), or
            // 	2 (remove terrain instead add). you can add them together.
            // 	0 indicates normal.
            // 	eg: 0xC011 means draw at xter=1, do not overwirte, upside-down.
            ter.noow = first_nibble & 8;
            ter.mirr = first_nibble & 4;
            ter.dark = first_nibble & 2;
            if (ter.ob) pos[Object::TERRAIN].push_back(ter);
            else status = BAD_IMAGE;
        }
    }

    // ==STEEL AREAS===========================================================
    // BYTES 0x0760 to 0x07DF (4 byte blocks)
    //
    // x pos : 9-bit value.  min 0x000, max 0xC78.  0x000 = -16, 0x008 = -12,
    // 	0x010 = -8, 0x018 = -4, ... , 0xC78 = 1580.
    // 	note: each hex value represents 4 pixels.  since it is 9 bit value it
    // 	      bleeds into the next attribute.
    //
    // y pos : min 0x00, max 0x27. 0x00 = 0, 0x01 = 4, 0x02 = 8, ... ,
    //        0x27 = 156
    // 	note: each hex value represents 4 pixels
    //
    // area : min 0x00, max 0xFF.  the first nibble is the x-size, from 0 - F.
    //        each value represents 4 pixels. the second nibble is the y-size.
    //        0x00 = (4,4), 0x11 = (8,8), 0x7F = (32,64), 0x23 = (12,16)
    //
    // eg: 00 9F 52 00 = put steel at (-12,124) width = 24, height = 12
    //
    // each 4 byte block starting at byte 0x0760 represents a steel area which
    // the lemmings cannot bash through. The first three bytes are given above,
    // and the last byte is always 00.. what a waste of space considering how
    // compact they made the first 3 bytes!  write 0x00 to fill each byte up to
    // 0x07E0 if need be.

    // for (int loop = 0x0760; loop < 0x07E0; ++loop) read_one_byte(file);

    name_english = read_level_name_bytes(file);

    file.close();

    // Finalisieren: Level auf tatsaechliche Groesse bringen, denn L++ hat
    // alles doppelt so gross wie normal
    size_x  *= 2;
    size_y  *= 2;
    start_x *= 2;
    for (int type = Object::TERRAIN; type != Object::MAX; ++type)
     for (std::list <Pos> ::iterator
     itr = pos[type].begin(); itr != pos[type].end(); ++itr) {
        itr->x *= 2;
        itr->y *= 2;
    }

    load_finalize_binary_or_lemmini(filename);
}
Example #15
0
void Level::load_finalize_binary_or_lemmini(const Filename& filename)
{
    built = Date("0");
    start_manual = true;

    // Ueble Machenschaften, die den Level gar nicht so sehr wie das Original
    // darstellen, sondern dafuer viel schoener! Links und rechts den Level
    // abschneiden, wenn der Platz nicht gebraucht wird.
    int min_x = size_x;
    int max_x = 0;
    // Minimum und Maximum finden
    for (int type = Object::TERRAIN; type != Object::MAX; ++type)
     for (std::list <Pos> ::iterator
     itr = pos[type].begin(); itr != pos[type].end(); ++itr) {
        if (itr->dark) continue;
        const int ix  = itr->x + itr->ob->selbox_x;
        const int ix2 = itr->x + itr->ob->selbox_x + itr->ob->selbox_xl;
        if (ix  < min_x) min_x = ix;
        if (ix2 > max_x) max_x = ix2;
    }
    if (min_x < 0)      min_x = 0;
    if (max_x > size_x) max_x = size_x;

    // Nun alles entsprechend verschieben
    size_x  = max_x - min_x;
    start_x -= min_x;
    for (int type = Object::TERRAIN; type != Object::MAX; ++type)
     for (std::list <Pos> ::iterator
     itr = pos[type].begin(); itr != pos[type].end(); ++itr) {
        itr->x -= min_x;
    }

    const std::string& filestr = filename.get_rootful();

    // ORIGHACK: In multiplayer levels, the hatch direction should point
    // towards the center because torus_x can't be set.
    if (filestr.find("network/") != std::string::npos
     && pos[Object::HATCH].size() > 1) {
        for (std::list <Pos> ::iterator hatch = pos[Object::HATCH].begin();
         hatch != pos[Object::HATCH].end(); ++hatch)
         if (hatch->x + hatch->ob->get_trigger_x() >= size_x / 2)
         hatch->rot = 1;
    }
    // ORIGHACK: In 2-player levels, the goal order should be set in such
    // a way that the distance for both players to get to their goal is
    // the same. This is only done for 1. Lemmings, as in 2. ONML, the
    // goal order is already as intended.
    if (filestr.find("network/2player/1. Lemmings") != std::string::npos
     && pos[Object::HATCH].size() == 2 && pos[Object::GOAL].size() == 2) {
        const Pos& h1 = *  pos[Object::HATCH].begin();
        const Pos& h2 = *++pos[Object::HATCH].begin();
        const Pos& g1 = *  pos[Object::GOAL ].begin();
        const Pos& g2 = *++pos[Object::GOAL ].begin();
        double dist_cur  = Help::hypot(h1.x, h1.y, g1.x, g1.y)
                         + Help::hypot(h2.x, h2.y, g2.x, g2.y);
        double dist_swap = Help::hypot(h1.x, h1.y, g2.x, g2.y)
                         + Help::hypot(h2.x, h2.y, g1.x, g1.y);
        if (dist_swap > dist_cur) pos[Object::GOAL].reverse();
    }
    // ORIGHACK: In 2-player levels, if there's one hatch only, player 0 goes
    // to the right and player 1 to the left. Thus, put goal 0 to the right.
    if ((filestr.find("network/2player/1. Lemmings") != std::string::npos
      || filestr.find("network/2player/2. ONML")     != std::string::npos)
     && pos[Object::HATCH].size() == 1 && pos[Object::GOAL].size() == 2) {
        const Pos& g1 = *  pos[Object::GOAL ].begin();
        const Pos& g2 = *++pos[Object::GOAL ].begin();
        if (g2.x > g1.x)   pos[Object::GOAL].reverse();
    }

}
Example #16
0
void Level::load_from_lemmini(const Filename& filename)
{
    typedef std::map <std::string, LemminiLine> Lines;
    Lines lines;

    std::ifstream file(filename.get_rootful().c_str());
    if (! file.good()) {
        status = BAD_FILE_NOT_FOUND;
        return;
    }

    // safely parse into lines, see other/file/io.cpp for comments.
    // ignore lines starting with #.
    std::string s;
    char        c;
    while (file.get(c)) {
        if (c != '\n' && c != '\r') s += c;
        else if (! s.empty()) {
            if (s[0] != '#') {
                LemminiLine ll(s);
                lines[ll.name] = ll;
            }
            s.clear();
        }
    }
    // No newline at end of file shall not matter
    if (! s.empty() && s[0] != '#') {
        LemminiLine ll(s);
        lines[ll.name] = ll;
    }

    file.close();

    // parse the contents of the LemminiLines

    name_english = lines["name"].str;
    size_x       = 2 * 1600;
    size_y       = 2 * 160;
    start_x      = lines["xPos"].nr;

    seconds       = std::max(lines["timeLimit"].nr * 60,
                             lines["timeLimitSeconds"].nr);
    initial       = lines["numLemmings"].nr;
    required      = lines["numToRescue"].nr;
    spawnint_slow = 4 + Help::even(99 - lines["releaseRate"].nr) / 2;
    spawnint_fast = 4;

    skills[LixEn::CLIMBER]  = lines["numClimbers"].nr;
    skills[LixEn::FLOATER]  = lines["numFloaters"].nr;
    skills[LixEn::EXPLODER] = lines["numBombers"].nr;
    skills[LixEn::BLOCKER]  = lines["numBlockers"].nr;
    skills[LixEn::BUILDER]  = lines["numBuilders"].nr;
    skills[LixEn::BASHER]   = lines["numBashers"].nr;
    skills[LixEn::MINER]    = lines["numMiners"].nr;
    skills[LixEn::DIGGER]   = lines["numDiggers"].nr;
    // skill slots with 0 skills will be culled in the finalize function

    legacy_ac_vec.push_back(LixEn::CLIMBER);
    legacy_ac_vec.push_back(LixEn::FLOATER);
    legacy_ac_vec.push_back(LixEn::EXPLODER);
    legacy_ac_vec.push_back(LixEn::BLOCKER);
    legacy_ac_vec.push_back(LixEn::BUILDER);
    legacy_ac_vec.push_back(LixEn::BASHER);
    legacy_ac_vec.push_back(LixEn::MINER);
    legacy_ac_vec.push_back(LixEn::DIGGER);

    int graphics_set = 0;
    std::string sty = lines["style"].str;
    Help::string_to_nice_case(sty);
    if      (sty == "Dirt")    graphics_set = 0;
    else if (sty == "Fire")    graphics_set = 1;
    else if (sty == "Marble")  graphics_set = 2;
    else if (sty == "Pillar")  graphics_set = 3;
    else if (sty == "Crystal") graphics_set = 4;
    else if (sty == "Brick")   graphics_set = 5;
    else if (sty == "Rock")    graphics_set = 6;
    else if (sty == "Snow")    graphics_set = 7;
    else if (sty == "Bubble")  graphics_set = 8;
    else if (sty == "Special") graphics_set = 9;

    // Parse the terrain

    int piece_nr = 0;
    while (true) {
        std::ostringstream piece_str;
        piece_str << "terrain_" << piece_nr;
        Lines::const_iterator itr = lines.find(piece_str.str());
        if (itr == lines.end()) break;
        else {
            Pos ter;
            ter.ob   = ObjLib::get_orig_terrain(graphics_set, itr->second.nr);
            ter.x    = itr->second.x;
            ter.y    = itr->second.y;
            ter.noow = itr->second.flags & 8;
            ter.mirr = itr->second.flags & 4;
            ter.dark = itr->second.flags & 2;
            if (ter.ob) pos[Object::TERRAIN].push_back(ter);
            else status = BAD_IMAGE;
        }
        ++piece_nr;
    }

    // parse the special objects

    piece_nr = 0;
    while (true) {
        std::ostringstream piece_str;
        piece_str << "object_" << piece_nr;
        Lines::const_iterator itr = lines.find(piece_str.str());
        if (itr == lines.end()) break;
        else {
            Pos ter;
            ter.ob = ObjLib::get_orig_special(graphics_set, itr->second.nr);
            ter.x  = itr->second.x;
            ter.y  = itr->second.y;
            if (ter.ob) {
                // Do not load/show the waving green and blue flags.
                if (ter.ob->type != Object::DECO || ter.ob->subtype != 1)
                    pos[ter.ob->type].push_back(ter);
            }
            else status = BAD_IMAGE;
        }
        ++piece_nr;
    }

    load_finalize_binary_or_lemmini(filename);
}