/* levels.cpp - levels and level construction functinos Copyright (C) 2006 Mark boyd 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 fun to play, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "arena.h" #include "levels.h" #include "movers.h" #include "enemies.h" #include "etc.h" void make_bobbler_swarm(arena &the_arena, float x, float y, int n, float radius) { for(int i=0; iteleport_centre(x + radius*randn(), y + radius*randn()); the_arena.add_mover(b); } } //Construct level from an array of strings //(which might one day be loaded from a file) template void construct_level(arena &the_arena, T strings, int height) { int width=std::string(strings[0]).size(); the_arena.set_size(width, height); for(int x=0; x': the_arena.set(x,y,A_BUTTON_L); break; case 'v': the_arena.set(x,y,A_BUTTON_U); break; case '<': the_arena.set(x,y,A_BUTTON_R); break; case '^': the_arena.set(x,y,A_BUTTON_D); break; //non-tiles.. //turrets (4 varieties) case 'U': case 'D': case 'L': case 'R': { turret *t=new turret(strings[y][x]=='U'?turret::UP: strings[y][x]=='D'?turret::DOWN: strings[y][x]=='L'?turret::LEFT: turret::RIGHT); t->teleport(TILE_SIZE*x, TILE_SIZE*y); the_arena.add_mover(t); } break; case 'S': { //ship start position the_arena.set_player_spawn(x,y); } break; } } #include "debug.h" #include bool read_word(std::string &line, const std::string &word) { int i=0; while(i>number; //Remove the word we just read line=std::string(line, j, std::string::npos); return true; } void kill_leading_whitespace(std::string &str) { int i=0; for(;i=0; --i) if(!(' '==str[i])) break; str = std::string(str, 0, i+1); } //todo: don't use DBG_WHINE level_loader::level_loader(const std::string &filename) { m_input.open(filename.c_str(), std::ios::in); if (!m_input) DBG_WHINE("Unable to load file: "+filename); //Briefly scan for L:START and L:END; reject entire file if they don't match bool in_level=false; int line_number=0; std::string line; try { while(m_input) { getline(m_input, line); line_number++; if (read_word(line, "L") && read_word(line, ":")) { if (read_word(line, "START")) { if (in_level==false) in_level=true; else DBG_WHINE("L:START inside level!"); } if (read_word(line, "END")) { if (in_level==true) in_level=false; else DBG_WHINE("L:END outside level!"); } } } if (in_level==true) DBG_WHINE("Final level never ends!"); m_input.clear(); // Forget eof "error" m_input.seekg(0); if (!m_input) DBG_WHINE("Error after reading file!"); } catch(const whinage &w) { DBG_WHINE("Error in line "+int2str(line_number)+" of file "+filename); } } //This is glue and is unpleasant. Level data is read from the file and stuffed into temporary variables: //strings, enemy_orders, connect_orders, etc. //Only if everything checks out and is valid do we touch the arena struct bobbler_swarm {float x; float y; int num; float radius;}; struct miner_order {float x; float y;}; struct graldor_order {float x; float y;}; struct connect_order {int xc; int yc; int xe; int ye;}; struct kick_order {int x;int y;}; struct invader_order{float x; float y; float xmin; float xmax;}; struct ufo_order{float x; float y; float xmin; float xmax;}; bool level_loader::next_level(arena &the_arena, std::string &desc) { std::string description; std::deque strings; std::string line; //order as in request std::deque bobbler_swarms; std::deque miner_orders; std::deque graldor_orders; std::deque invader_orders; std::deque ufo_orders; std::deque connect_orders; std::deque kick_orders; //Find the start of the level do getline(m_input, line); while(m_input && !(read_word(line, "L") && read_word(line, ":") && read_word(line, "START"))); do { getline(m_input, line); //D - description if (read_word(line, "D") && read_word(line, ":")) { description+=line; description+='\n'; } //A if (read_word(line, "A") && read_word(line, ":")) { kill_leading_whitespace(line); kill_trailing_whitespace(line); if (strings.size()>0 && strings[0].size() != line.size()) { DBG_WHINE("mismatched arena line lengths!\n"+line); } strings.push_back(line); } //E if (read_word(line, "E") && read_word(line, ":")) { if (read_word(line, "BOBBLERS")) { float x,y,num,radius; if (read_number(line,x) && read_number(line,y) && read_number(line,num) && read_number(line,radius)) { bobbler_swarm bs={x*TILE_SIZE,y*TILE_SIZE,int(num),radius}; bobbler_swarms.push_back(bs); } else DBG_WHINE("Invalid BOBBLERS entry"); } else if (read_word(line, "MINER")) { float x,y; if (read_number(line,x) && read_number(line,y)) { miner_order mo={x*TILE_SIZE,y*TILE_SIZE}; miner_orders.push_back(mo); } else DBG_WHINE("Invalid MINER entry"); } else if (read_word(line, "GRALDOR")) { float x,y; if (read_number(line,x) && read_number(line,y)) { graldor_order go={x*TILE_SIZE,y*TILE_SIZE}; graldor_orders.push_back(go); } else DBG_WHINE("Invalid GRALDOR entry"); } else if (read_word(line, "INVADER")) { float x,y,xmin,xmax; if (read_number(line,x) && read_number(line,y) && read_number(line,xmin) && read_number(line,xmax)) { invader_order io={x*TILE_SIZE,y*TILE_SIZE,xmin*TILE_SIZE,xmax*TILE_SIZE}; invader_orders.push_back(io); } else DBG_WHINE("Invalid INVADER entry"); } else if (read_word(line, "UFO")) { float x,y,xmin,xmax; if (read_number(line,x) && read_number(line,y) && read_number(line,xmin) && read_number(line,xmax)) { ufo_order uo={x*TILE_SIZE,y*TILE_SIZE,xmin*TILE_SIZE,xmax*TILE_SIZE}; ufo_orders.push_back(uo); } else DBG_WHINE("Invalid UFO entry"); } } //C if (read_word(line, "C") && read_word(line, ":")) { if (read_word(line, "CONNECT")) { float xc,yc,xe,ye; if (read_number(line,xc) && read_number(line,yc) && read_number(line,xe) && read_number(line,ye)) { connect_order co={int(xc),int(yc),int(xe),int(ye)}; connect_orders.push_back(co); } else DBG_WHINE("Invalid CONNECT entry"); } else if (read_word(line, "KICK")) { float x,y; if (read_number(line,x) && read_number(line,y)) { kick_order ko={int(x),int(y)}; kick_orders.push_back(ko); } else DBG_WHINE("Invalid KICK entry"); } } //L if (read_word(line, "L") && read_word(line, ":")) { if (!read_word(line, "END")) { //"END" is the only legal possibility, so... DBG_WHINE("Unexpected L: code"); } else { //Todo: Check that we have everything we need //Todo: check validity construct_level(the_arena, strings, strings.size()); for(int i=0; iteleport_centre(miner_orders[i].x, miner_orders[i].y); the_arena.add_mover(m); } for(int i=0; iteleport_centre(graldor_orders[i].x, graldor_orders[i].y); the_arena.add_mover(g); } for(int i=0; iteleport_centre(io.x, io.y); the_arena.add_mover(in); } for(int i=0; iteleport_centre(uo.x, uo.y); the_arena.add_mover(u); } for (int i=0; i