// Description: // Block Model // // Copyright (C) 2003 Frank Becker // // 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 // #include #include #include #include #include #include #include #include #include #include "BlockModel.hpp" //TODO: clean up please #include using namespace std; /* Terminology used: Element/Point: The smallest piece. Block: Made up of a number of elements. Plane: A Z-slice of elements Shaft: Like an elevator shaft; the playing area. Difficulty: level 1: 2.0 sec, element count < (width*height*5*1) level 2: 1.9 sec, element count < (width*height*5*2) ... Scoring: For each locked element: _score += 5+ (_level*5); For cleared plane(s): _score += (subscore * ((_level+planeCount)/5)); TODO: Bonus blocks: - point multiplier - eraser erases blocks - filler fills planes - crater block explodes and removes elements */ BlockModel::BlockModel( int w, int h, int d, int level, R250 &r): _width(w), _height(h), _depth(d), _level(level), _r250(r), _numBlocks(0), _orientation(0,0,1), _offset(0,0,d-1), _freefall(false), _score(0), _elementCount(0) { LOG_INFO << "New BlockModel:" << " w=" << _width << " h=" << _height << " d=" << _depth << "\n"; _planes = new unsigned char*[_depth]; _planesLockCount = new unsigned char[_depth]; memset( _planesLockCount, 0, _depth); _planesmem = new unsigned char[ _depth*_width*_height]; memset( _planesmem, 0, _depth*_width*_height); for( int i=0; i<_depth; i++) { _planes[ i] = &(_planesmem[i*_width*_height]); } _elementListNorm = new ElementList; _elementList = new ElementList; _tmpElementList = new ElementList; _lockedElementList = new ElementList; updateDropDelay(); } bool BlockModel::init( void) { if( !loadBlocks()) { return false; } addBlock(); verifyAndAdjust(); updateNextDrop( _dropDelay); return true; } BlockModel::~BlockModel() { delete [] _planes; delete [] _planesLockCount; delete [] _planesmem; ElementList::iterator i; ElementList::iterator j; ElementList::iterator k; for( i=_elementList->begin(), j=_elementListNorm->begin(), k=_tmpElementList->begin(); i!=_elementList->end(); i++, j++, k++) { delete (*i); delete (*j); delete (*k); } _elementList->clear(); _elementListNorm->clear(); _tmpElementList->clear(); delete _elementList; delete _elementListNorm; delete _tmpElementList; for( i=_lockedElementList->begin(); i!=_lockedElementList->end(); i++) { delete (*i); } _lockedElementList->clear(); delete _lockedElementList; for( unsigned int c=0; c<_numBlocks; c++) { ElementList *el = _blockList[c]; for( i=el->begin(); i!=el->end(); i++) { delete (*i); } delete el; } } void BlockModel::addBlock( void) { if( !_numBlocks) return; ElementList::iterator i; ElementList::iterator j; ElementList::iterator k; for( i=_elementList->begin(), j=_elementListNorm->begin(), k=_tmpElementList->begin(); i!=_elementList->end(); i++, j++, k++) { delete (*i); delete (*j); delete (*k); } _elementListNorm->clear(); _elementList->clear(); _tmpElementList->clear(); int nextBlock = _r250.random()%_numBlocks; ElementList *el = _blockList[nextBlock]; for( i=el->begin(); i!=el->end(); i++) { Point3Di *p = *i; _elementListNorm->insert( _elementListNorm->begin(), new Point3Di(p->x,p->y,p->z)); _elementList->insert( _elementList->begin(), new Point3Di(p->x,p->y,p->z)); _tmpElementList->insert( _tmpElementList->begin(), new Point3Di(p->x,p->y,p->z)); } _offset = Point3Di(0,0,_depth-1); _orientation = Point3Di(0,0,1); } bool BlockModel::loadBlocks( void) { string filename = "system/blocks.txt"; if( ! ResourceManagerS::instance()->selectResource( string(filename))) { LOG_ERROR << "Unable to open: [" << filename << "]" << endl; return false; } ziStream &infile = ResourceManagerS::instance()->getInputStream(); LOG_INFO << "Loading block info from [" << filename << "]" << endl; string line; int linecount = 0; while( !getline( infile, line).eof()) { linecount++; // LOG_INFO << "[" << line << "]\n"; if( line[0] == '#') continue; Tokenizer t( line); string token = t.next(); if( token == "Elements") { int numElems = atoi( t.next().c_str()); if( !readBlock( infile, numElems, linecount)) { LOG_ERROR << "Error reading blocks. Line: " << linecount << "\n"; return false; } } } return true; } bool BlockModel::readBlock( ziStream &infile, int numElems, int &linecount) { ElementList *el = new ElementList; _blockList[_numBlocks++] = el; string line; for( int i=0; iinsert( el->begin(), np); // LOG_INFO << "[" << p.x << "," << p.y << "," << p.z << "]" << endl; } return true; } void BlockModel::updateDropDelay( void) { _dropDelay = 2.1f - (float)_level*0.1; if( _dropDelay < 0.1f) _dropDelay = 0.1f; } void BlockModel::moveBlock( Direction dir) { switch( dir) { case eLeft: _offset.x--; break; case eRight: _offset.x++; break; case eUp: _offset.y++; break; case eDown: _offset.y--; break; case eOut: _offset.z++; break; case eIn: _freefall = true; break; default: break; } if( !verifyAndAdjust()) { //failed verify, undo move switch( dir) { case eLeft: _offset.x++; break; case eRight: _offset.x--; break; case eUp: _offset.y--; break; case eDown: _offset.y++; break; case eOut: case eIn: default: break; } } } void BlockModel::rotateBlock( const Point3Di &r1, const Point3Di &r2, const Point3Di &r3, const Point3Di &axis) { ElementList::iterator i; ElementList::iterator j; for( i=_elementList->begin(), j=_tmpElementList->begin(); i!=_elementList->end(); i++, j++) { Point3Di p1 = **i; Point3Di *p2 = *j; p2->x = p1.x*r1.x + p1.y*r1.y + p1.z*r1.z; p2->y = p1.x*r2.x + p1.y*r2.y + p1.z*r2.z; p2->z = p1.x*r3.x + p1.y*r3.y + p1.z*r3.z; } ElementList *tmp = _elementList; _elementList = _tmpElementList; if( verifyAndAdjust()) { //accept new element list and complete ptr swap _tmpElementList = tmp; Quaternion q; q.set( 90.0f, Point3D(axis.x,axis.y,axis.z)); _view->notifyNewRotation( q); } else { //reject and restore original ptr _elementList = tmp; } } bool BlockModel::verifyAndAdjust( void) { int minx = 0; int maxx = 0; int miny = 0; int maxy = 0; ElementList::iterator i; for( i=_elementList->begin(); i!=_elementList->end(); i++) { Point3Di *p = *i; Point3Di a = *p + _offset; if( -a.x > minx) minx = -a.x; if( a.x >= (_width+maxx)) maxx = (a.x-_width+1); if( -a.y > miny) miny = -a.y; if( a.y >= (_height+maxy)) maxy = (a.y-_height+1); if( a.z < 0) return false; } if( (minx+miny+maxx+maxy) > 0) { _offset.x += minx; _offset.x -= maxx; _offset.y += miny; _offset.y -= maxy; } for( i=_elementList->begin(); i!=_elementList->end(); i++) { Point3Di *p = *i; Point3Di a = *p + _offset; if( elementLocked( a)) return false; } return true; } bool BlockModel::canDrop( void) { ElementList::iterator i; for( i=_elementList->begin(); i!=_elementList->end(); i++) { Point3Di *p = *i; Point3Di a = *p + _offset; a.z--; if( a.z < 0) return false; if( elementLocked(a)) return false; } return true; } bool BlockModel::update( void) { if( _freefall) { if( canDrop()) { updateNextDrop( 0.5f); _offset.z--; } else { _freefall = false; } } else { float now = Timer::getTime(); if( now >= _nextDrop) { if( canDrop()) { _offset.z--; } else { //lock block ElementList::iterator i; for( i=_elementList->begin(); i!=_elementList->end(); i++) { Point3Di *p = *i; Point3Di a = *p + _offset; if( !lockElement( a)) { //failed to lock block, game over! return false; } _elementCount++; if( _elementCount > (_width*_height*5*_level)) { _level++; updateDropDelay(); LOG_INFO << "New level is " << _level << "\n"; } _score += 5+ (_level*5); } AudioS::instance()->playSample( "sounds/katoung.wav"); checkPlanes(); addBlock(); _view->notifyNewBlock(); if( ! verifyAndAdjust()) { LOG_INFO << "Can't fit new block!\n"; return false; } } updateNextDrop( _dropDelay); } } return true; } void BlockModel::checkPlanes(void) { int planeElems = _width*_height; unsigned int subscore = planeElems*_level; int planeCount = 0; for( int d=0; d<_depth; d++) { if( _planesLockCount[d] < planeElems) continue; if( _planesLockCount[d] == 0) break; planeCount++; subscore += subscore; // collapse unsigned char *tmpplane = _planes[d]; for( int i=d; i<(_depth-1); i++) { _planesLockCount[i] = _planesLockCount[i+1]; _planes[i] = _planes[i+1]; } _planesLockCount[_depth-1] = 0; _planes[_depth-1] = tmpplane; memset( tmpplane, 0, planeElems); ElementList::iterator i; for( i=_lockedElementList->begin(); i!=_lockedElementList->end(); ) { Point3Di *e = *i; if( e->z == d ) { i = _lockedElementList->erase(i); delete e; } else if( e->z > d) { e->z--; i++; } else { i++; } } //we moved everything down, so do this depth again d--; } switch( planeCount) { case 0: break; case 1: AudioS::instance()->playSample( "sounds/drop1.wav"); break; case 2: AudioS::instance()->playSample( "sounds/xdoubleplay.wav"); break; case 3: AudioS::instance()->playSample( "sounds/xtripleplay.wav"); break; default: AudioS::instance()->playSample( "sounds/xmonsterplay.wav"); break; } _score += (subscore * planeCount); } bool BlockModel::lockElement( Point3Di &a) { if( a.z >= _depth) { LOG_ERROR << "Out of bounds!\n"; return false; } _planes[ a.z][ a.y*_width + a.x]++; unsigned char val = _planes[ a.z][ a.y*_width + a.x]; if( val > 1) { LOG_ERROR << "Bad locked element! (" << a.x << "," << a.y << "," << a.z << " = " << (int)val << ")\n"; return false; } _planesLockCount[ a.z]++; _lockedElementList->insert( _lockedElementList->begin(), new Point3Di(a.x,a.y,a.z)); return true; } bool BlockModel::elementLocked( Point3Di &a) { //elements outside the shaft are never locked if( a.z >= _depth) return false; unsigned char val = _planes[ a.z][ a.y*_width + a.x]; return (val > 0); } void BlockModel::updateNextDrop( float delta) { _nextDrop = Timer::getTime() + delta; //50000.0f; //delta; //FIXME: }