#include "map.h" #include "landscape.h" #include "minorGems/util/SimpleVector.h" #include int seed = time( NULL ); //int seed = 10; extern int tileH; extern int tileW; char isBlockedGrid( int inGridX, int inGridY, char inDoNotRecurse ); char isBlockedGridHash( int inGridX, int inGridY, char inDoNotRecurse ); char allNeighborsBlocked( int inGridX, int inGridY ) { if( ! isBlockedGridHash( inGridX, inGridY - 1, true ) ) { // top return false; } if( ! isBlockedGridHash( inGridX + 1, inGridY, true ) ) { // right return false; } if( ! isBlockedGridHash( inGridX, inGridY + 1, true ) ) { // bottom return false; } if( ! isBlockedGridHash( inGridX - 1, inGridY, true ) ) { // left return false; } // all neighbors blocked return true; } #include "HashTable.h" HashTable blockedHashTable( 3000 ); char isBlockedGridHash( int inGridX, int inGridY, char inDoNotRecurse ) { char found; char blocked = blockedHashTable.lookup( inGridX, inGridY, &found ); if( found ) { return blocked; } // else not found // call real function to get result blocked = isBlockedGrid( inGridX, inGridY, inDoNotRecurse ); // only insert result if we called the full recursive version of // inBlockGrid. Otherwise, we aren't getting the real result back // so we shouldn't be tracking it in our table if( ! inDoNotRecurse ) { blockedHashTable.insert( inGridX, inGridY, blocked ); } return blocked; } char isBlocked( int inX, int inY ) { // reduce to grid coordinates int gridX = inX / tileW; int gridY = inY / tileH; // try hash first return isBlockedGridHash( gridX, gridY, false ); } char isBlockedGrid( int inGridX, int inGridY, char inDoNotRecurse ) { // wall along far left and top if( inGridX <=0 || inGridY <= 0 ) { return true; } // empty passage at top if( inGridY <= 1 ) { return false; } // make a grid of empty spaces from which blocks can be // removed below to make a maze if( inGridX % 2 != 0 && inGridY % 2 != 0 ) { // however, if all neighbors are blocked, this should // be blocked too if( !inDoNotRecurse ) { return allNeighborsBlocked( inGridX, inGridY ); } else { // non-recursive mode return false; } } // blocks get denser as y increases double threshold = 1 - inGridY / 20.0; double randValue = noise3d( inGridX, inGridY, seed ); char returnValue = randValue > threshold; if( returnValue ) { return true; } else { // not blocked // should be blocked if all neighbors are blocked if( !inDoNotRecurse ) { return allNeighborsBlocked( inGridX, inGridY ); } else { // non-recursive mode return false; } } } struct pairStruct { int x; int y; }; typedef struct pairStruct pair; SimpleVector openedChests; char isChest( int inX, int inY ) { // reduce to grid coordinates int gridX = inX / tileW; int gridY = inY / tileH; // chests get denser as y increases // no chests where gridY < 5 // even less dense than blocks double threshold = 1 - ( gridY - 5 ) / 200.0; // use different seed than for blocks double randValue = noise3d( 73642 * gridX, 283277 * gridY, seed * 987423 ); char returnValue = randValue > threshold; if( returnValue ) { // make sure it's not opened already for( int i=0; ix == gridX && p->y == gridY ) { return CHEST_OPEN; } } return CHEST_CLOSED; } else { return CHEST_NONE; } } unsigned char getChestCode( int inX, int inY ) { // reduce to grid coordinates int gridX = inX / tileW; int gridY = inY / tileH; // use different seed double randValue = noise3d( 37462 * gridX, 1747 * gridY, seed * 3748147 ); /* Note: Original versions of Passage (up to v3 for Mac/PC/Unix) contained a bug. This was the original code: // use it to fill first 6 binary digits return (unsigned char)( randValue * 15 ) & 0x3F; Note two things: 1. That randValue is *signed* (and can be negative), so casting it to an uchar is a weird thing to do. 2. That we're only multiplying randValue by 15, which only fills the first 4 binary digits (there used to be only 4 gems). Thus, if randValue is actually *positive* between 0 and 1, only the first 4 gems have a chance of being activated. What this code should have done was convert randValue to the range [0..1] first and then multiply it by 63 (to convert it to a 6-bit random binary string). However, this code *seems* to work, anyway, since 2's compliment was being invoked to handle casting of negative randValues, so all 6 gems were sometimes being activated. However, the 6-bit gem strings were always of the form 00XXXX or 11XXXX (where XXXX is a random 4-bit sting) due to the 2's compliment and the fact that the negative numbers being complimented were never less than -15. So, 2 of the gems were tied together in their on/off status, essentially turning 6 gems into 5. The problem appeared on platforms (like iPhone) that don't preserve 2's compliment bits when doing signed to unsigned casting. On these platforms, all negative numbers were being casted to 0 when turned to uchars. Thus, half the treasure chests, on average, had no gems at all. Of course, a proper fix, as described above, would go beyond just making it work on these platforms, but would also change the behavior of the game (those last two gems would no longer be tied together in their on-off status). After much deliberation, I came to the following solution: The following code emulates 2's compliment casting, whether the platform does it or not, making behavior on all platforms identical. (And preserving the original bug!!) */ unsigned char castedChar; if( randValue < 0 ) { castedChar = (unsigned char)( 256 + (char)( randValue * 15 ) ); } else { castedChar = (unsigned char)( randValue * 15 ); } return castedChar & 0x3F; } void getChestCenter( int inX, int inY, int *outCenterX, int *outCenterY ) { int gridX = inX / tileW; int gridY = inY / tileH; *outCenterX = gridX * tileW + tileW / 2; *outCenterY = gridY * tileH + tileH / 2; } void openChest( int inX, int inY ) { pair p; // reduce to grid coordinates p.x = inX / tileW; p.y = inY / tileH; openedChests.push_back( p ); } void resetMap() { // new seed seed = time( NULL ); //seed = 10; openedChests.deleteAll(); blockedHashTable.clear(); }