SPOTTER::~SPOTTER() { for (int i = 0; i < numWatchedTiles; i++) { const TILEPOS pos = watchedTiles[i]; MAPTILE *psTile = mapTile(pos.x, pos.y); uint8_t *visionType = (pos.type == 0) ? psTile->watchers : psTile->sensors; ASSERT(visionType[player] > 0, "Not watching watched tile (%d, %d)", (int)pos.x, (int)pos.y); visionType[player]--; updateTileVis(psTile); } free(watchedTiles); }
static void calcTileIllum(UDWORD tileX, UDWORD tileY) { /* The number or normals that we got is in numNormals*/ Vector3f finalVector = {0.0f, 0.0f, 0.0f}; unsigned int i, val; int dotProduct; unsigned int numNormals = 0; // How many normals have we got? Vector3f normals[8]; // Maximum 8 possible normals /* Quadrants look like:- * * 0 * 1 * * **********V********** * * 3 * 2 * * */ /* Do quadrant 0 - tile that's above and left*/ normalsOnTile(tileX-1, tileY-1, 0, &numNormals, normals); /* Do quadrant 1 - tile that's above and right*/ normalsOnTile(tileX, tileY-1, 1, &numNormals, normals); /* Do quadrant 2 - tile that's down and right*/ normalsOnTile(tileX, tileY, 2, &numNormals, normals); /* Do quadrant 3 - tile that's down and left*/ normalsOnTile(tileX-1, tileY, 3, &numNormals, normals); for(i = 0; i < numNormals; i++) { finalVector = Vector3f_Add(finalVector, normals[i]); } dotProduct = Vector3f_ScalarP(Vector3f_Normalise(finalVector), theSun); val = abs(dotProduct) / 16; if (val == 0) val = 1; if (val > 254) val = 254; mapTile(tileX, tileY)->illumination = val; }
// ------------------------------------------------------------------------------------ void preProcessVisibility( void ) { UDWORD i,j; MAPTILE *psTile; STRUCTURE *psStruct; //FEATURE *psFeature; for(i=0; i<mapWidth;i++) { for(j=0; j<mapHeight; j++) { psTile = mapTile(i,j); if(TEST_TILE_VISIBLE(selectedPlayer,psTile)) { psTile->bMaxed = TRUE; psTile->level = psTile->illumination; //can't have this cos when load up a save game where a structure has been built by the //enemy in an area that has been seen before it flags the structure as visible! /*if(TILE_HAS_STRUCTURE(psTile)) { psStruct = getTileStructure(i,j); if (psStruct) { psStruct->visible[selectedPlayer] = UBYTE_MAX; } else { ASSERT((FALSE, "preProcessVisibility: should be a structure at %d, %d", i, j)); } }*/ /* if(TILE_HAS_FEATURE(psTile)) { psFeature = getTileFeature(i,j); psFeature->visible[selectedPlayer] = UBYTE_MAX; } */ } else { psTile->level = UBYTE_MAX; psTile->bMaxed = FALSE; } } } }
/** Draw the map tiles on the radar. */ static void DrawRadarTiles(void) { SDWORD x, y; for (x = scrollMinX; x < scrollMaxX; x++) { for (y = scrollMinY; y < scrollMaxY; y++) { MAPTILE *psTile = mapTile(x, y); size_t pos = radarTexWidth * (y - scrollMinY) + (x - scrollMinX); ASSERT(pos * sizeof(*radarBuffer) < radarBufferSize, "Buffer overrun"); radarBuffer[pos] = appliedRadarColour(radarDrawMode, psTile).rgba; } } }
/* Lowers a tile by a #defined height */ void lowerTile(int tile3dX, int tile3dY) { int i, j; if (tile3dX < 0 || tile3dX > mapWidth - 1 || tile3dY < 0 || tile3dY > mapHeight - 1) { return; } for (i = tile3dX; i <= MIN(mapWidth - 1, tile3dX + brushSize); i++) { for (j = tile3dY; j <= MIN(mapHeight - 1, tile3dY + brushSize); j++) { adjustTileHeight(mapTile(i, j), TILE_LOWER); markTileDirty(i, j); } } }
// ------------------------------------------------------------------------------------ void preProcessVisibility( void ) { UDWORD i,j; MAPTILE *psTile; for(i=0; i<mapWidth;i++) { for(j=0; j<mapHeight; j++) { psTile = mapTile(i,j); psTile->level = 0; if (!bRevealActive || TEST_TILE_VISIBLE(selectedPlayer, psTile)) { psTile->level = psTile->illumination; } } } }
//By passing in params - it means that if the scroll limits are changed mid-mission //we can re-do over the area that hasn't been seen void initLighting(UDWORD x1, UDWORD y1, UDWORD x2, UDWORD y2) { UDWORD i, j; // quick check not trying to go off the map - don't need to check for < 0 since UWORD's!! if (x1 > mapWidth || x2 > mapWidth || y1 > mapHeight || y2 > mapHeight) { ASSERT( false, "initLighting: coords off edge of map" ); return; } for (i = x1; i < x2; i++) { for(j = y1; j < y2; j++) { MAPTILE *psTile = mapTile(i, j); // always make the edge tiles dark if (i==0 || j==0 || i >= mapWidth-1 || j >= mapHeight-1) { psTile->illumination = 16; // give water tiles at edge of map a border if (terrainType(psTile) == TER_WATER) { psTile->texture = 0; } } else { calcTileIllum(i,j); } // Basically darkens down the tiles that are outside the scroll // limits - thereby emphasising the cannot-go-there-ness of them if ((SDWORD)i < scrollMinX + 4 || (SDWORD)i > scrollMaxX - 4 || (SDWORD)j < scrollMinY + 4 || (SDWORD)j > scrollMaxY - 4) { psTile->illumination/=3; } } } }
/** Draw the map tiles on the radar. */ static void DrawRadarTiles() { SDWORD x, y; for (x = scrollMinX; x < scrollMaxX; x++) { for (y = scrollMinY; y < scrollMaxY; y++) { MAPTILE *psTile = mapTile(x, y); size_t pos = radarTexWidth * (y - scrollMinY) + (x - scrollMinX); ASSERT(pos * sizeof(*radarBuffer) < radarBufferSize, "Buffer overrun"); if (y == scrollMinY || x == scrollMinX || y == scrollMaxY - 1 || x == scrollMaxX - 1) { radarBuffer[pos] = WZCOL_BLACK.rgba; continue; } radarBuffer[pos] = appliedRadarColour(radarDrawMode, psTile).rgba; } } }
// ------------------------------------------------------------------------------------ void processAVTile(UDWORD x, UDWORD y ) { FRACT time; MAPTILE *psTile; UDWORD newLevel; psTile = mapTile(x,y); if(psTile->level == UBYTE_MAX OR psTile->bMaxed) { return; } time = (MAKEFRACT(frameTime)/GAME_TICKS_PER_SEC); newLevel = MAKEINT(psTile->level + (time*FADE_IN_TIME)); if(newLevel >= psTile->illumination) { psTile->level = psTile->illumination; psTile->bMaxed = TRUE; } else { psTile->level =(UBYTE)newLevel; } }
void calcDroidIllumination(DROID *psDroid) { UDWORD lightVal; // sum of light vals UDWORD presVal; UDWORD tileX,tileY; UDWORD retVal; float adjust; /* See if the droid's at the edge of the map */ tileX = psDroid->pos.x/TILE_UNITS; tileY = psDroid->pos.y/TILE_UNITS; /* Are we at the edge */ if(tileX<=1 || tileX>=mapWidth-2 || tileY<=1 || tileY>=mapHeight-2) { lightVal = mapTile(tileX,tileY)->illumination; lightVal += MIN_DROID_LIGHT_LEVEL; } else { lightVal = mapTile(tileX,tileY)->illumination + // mapTile(tileX-1,tileY)->illumination + // * mapTile(tileX,tileY-1)->illumination + // *** pattern mapTile(tileX+1,tileY)->illumination + // * mapTile(tileX+1,tileY+1)->illumination; // lightVal/=5; lightVal += MIN_DROID_LIGHT_LEVEL; } /* Saturation */ if(lightVal>255) lightVal = 255; presVal = psDroid->illumination; adjust = (float)lightVal - (float)presVal; adjust *= graphicsTimeAdjustedIncrement(DROID_SEEK_LIGHT_SPEED); retVal = presVal + adjust; if(retVal > 255) retVal = 255; psDroid->illumination = (UBYTE)retVal; }
/* Remove a Feature and free it's memory */ bool destroyFeature(FEATURE *psDel) { UDWORD widthScatter,breadthScatter,heightScatter, i; EFFECT_TYPE explosionSize; Vector3i pos; UDWORD width,breadth; UDWORD mapX,mapY; ASSERT_OR_RETURN(false, psDel != NULL, "Invalid feature pointer"); /* Only add if visible and damageable*/ if(psDel->visible[selectedPlayer] && psDel->psStats->damageable) { /* Set off a destruction effect */ /* First Explosions */ widthScatter = TILE_UNITS/2; breadthScatter = TILE_UNITS/2; heightScatter = TILE_UNITS/4; //set which explosion to use based on size of feature if (psDel->psStats->baseWidth < 2 && psDel->psStats->baseBreadth < 2) { explosionSize = EXPLOSION_TYPE_SMALL; } else if (psDel->psStats->baseWidth < 3 && psDel->psStats->baseBreadth < 3) { explosionSize = EXPLOSION_TYPE_MEDIUM; } else { explosionSize = EXPLOSION_TYPE_LARGE; } for(i=0; i<4; i++) { pos.x = psDel->pos.x + widthScatter - rand()%(2*widthScatter); pos.z = psDel->pos.y + breadthScatter - rand()%(2*breadthScatter); pos.y = psDel->pos.z + 32 + rand()%heightScatter; addEffect(&pos,EFFECT_EXPLOSION,explosionSize,false,NULL,0); } if(psDel->psStats->subType == FEAT_SKYSCRAPER) { pos.x = psDel->pos.x; pos.z = psDel->pos.y; pos.y = psDel->pos.z; addEffect(&pos,EFFECT_DESTRUCTION,DESTRUCTION_TYPE_SKYSCRAPER,true,psDel->sDisplay.imd,0); initPerimeterSmoke(psDel->sDisplay.imd, pos); shakeStart(); } /* Then a sequence of effects */ pos.x = psDel->pos.x; pos.z = psDel->pos.y; pos.y = map_Height(pos.x,pos.z); addEffect(&pos,EFFECT_DESTRUCTION,DESTRUCTION_TYPE_FEATURE,false,NULL,0); //play sound // ffs gj if(psDel->psStats->subType == FEAT_SKYSCRAPER) { audio_PlayStaticTrack( psDel->pos.x, psDel->pos.y, ID_SOUND_BUILDING_FALL ); } else { audio_PlayStaticTrack( psDel->pos.x, psDel->pos.y, ID_SOUND_EXPLOSION ); } } if (psDel->psStats->subType == FEAT_SKYSCRAPER) { // ----- Flip all the tiles under the skyscraper to a rubble tile // smoke effect should disguise this happening mapX = map_coord(psDel->pos.x) - psDel->psStats->baseWidth/2; mapY = map_coord(psDel->pos.y) - psDel->psStats->baseBreadth/2; for (width = 0; width < psDel->psStats->baseWidth; width++) { for (breadth = 0; breadth < psDel->psStats->baseBreadth; breadth++) { MAPTILE *psTile = mapTile(mapX+width,mapY+breadth); // stops water texture chnaging for underwateer festures if (terrainType(psTile) != TER_WATER) { if (terrainType(psTile) != TER_CLIFFFACE) { /* Clear feature bits */ psTile->texture = TileNumber_texture(psTile->texture) | RUBBLE_TILE; } else { /* This remains a blocking tile */ psTile->psObject = NULL; psTile->texture = TileNumber_texture(psTile->texture) | BLOCKING_RUBBLE_TILE; } } } } } removeFeature(psDel); return true; }
// free up a feature with no visual effects bool removeFeature(FEATURE *psDel) { int mapX, mapY, width, breadth, player; MESSAGE *psMessage; Vector3i pos; ASSERT_OR_RETURN(false, psDel != NULL, "Invalid feature pointer"); ASSERT_OR_RETURN(false, !psDel->died, "Feature already dead"); if(bMultiMessages && !ingame.localJoiningInProgress) { SendDestroyFeature(psDel); // inform other players of destruction return true; // Wait for our message before really destroying the feature. } //remove from the map data mapX = map_coord(psDel->pos.x) - psDel->psStats->baseWidth/2; mapY = map_coord(psDel->pos.y) - psDel->psStats->baseBreadth/2; for (width = 0; width < psDel->psStats->baseWidth; width++) { for (breadth = 0; breadth < psDel->psStats->baseBreadth; breadth++) { if (tileOnMap(mapX + width, mapY + breadth)) { MAPTILE *psTile = mapTile(mapX + width, mapY + breadth); if (psTile->psObject == (BASE_OBJECT *)psDel) { psTile->psObject = NULL; auxClearBlocking(mapX + width, mapY + breadth, FEATURE_BLOCKED | AIR_BLOCKED); } } } } if(psDel->psStats->subType == FEAT_GEN_ARTE) { pos.x = psDel->pos.x; pos.z = psDel->pos.y; pos.y = map_Height(pos.x,pos.z); addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_DISCOVERY,false,NULL,0); scoreUpdateVar(WD_ARTEFACTS_FOUND); intRefreshScreen(); } if (psDel->psStats->subType == FEAT_GEN_ARTE || psDel->psStats->subType == FEAT_OIL_RESOURCE) { for (player = 0; player < MAX_PLAYERS; player++) { psMessage = findMessage((MSG_VIEWDATA *)psDel, MSG_PROXIMITY, player); while (psMessage) { removeMessage(psMessage, player); psMessage = findMessage((MSG_VIEWDATA *)psDel, MSG_PROXIMITY, player); } } } killFeature(psDel); return true; }
/* Create a feature on the map */ FEATURE * buildFeature(FEATURE_STATS *psStats, UDWORD x, UDWORD y,BOOL FromSave) { UDWORD mapX, mapY; UDWORD width,breadth, foundationMin,foundationMax, height; UDWORD startX,startY,max,min; SDWORD i; UBYTE vis; //try and create the Feature FEATURE* psFeature = createFeature(); if (psFeature == NULL) { debug(LOG_WARNING, "Feature couldn't be built."); return NULL; } psFeature->psStats = psStats; // features are not in the cluster system // this will cause an assert when they still end up there psFeature->cluster = ~0; //add the feature to the list - this enables it to be drawn whilst being built addFeature(psFeature); // get the terrain average height startX = map_coord(x); startY = map_coord(y); foundationMin = TILE_MAX_HEIGHT; foundationMax = TILE_MIN_HEIGHT; for (breadth = 0; breadth < psStats->baseBreadth; breadth++) { for (width = 0; width < psStats->baseWidth; width++) { getTileMaxMin(startX + width, startY + breadth, &max, &min); if (foundationMin > min) { foundationMin = min; } if (foundationMax < max) { foundationMax = max; } } } //return the average of max/min height height = (foundationMin + foundationMax) / 2; // snap the coords to a tile if (!FromSave) { x = (x & ~TILE_MASK) + psStats->baseWidth %2 * TILE_UNITS/2; y = (y & ~TILE_MASK) + psStats->baseBreadth%2 * TILE_UNITS/2; } else { if ((x & TILE_MASK) != psStats->baseWidth %2 * TILE_UNITS/2 || (y & TILE_MASK) != psStats->baseBreadth%2 * TILE_UNITS/2) { debug(LOG_WARNING, "Feature not aligned. position (%d,%d), size (%d,%d)", x, y, psStats->baseWidth, psStats->baseBreadth); } } psFeature->pos.x = x; psFeature->pos.y = y; /* Dump down the building wrecks at random angles - still looks shit though */ if(psStats->subType == FEAT_BUILD_WRECK || psStats->subType == FEAT_TREE) { psFeature->rot.direction = gameRand(DEG_360); } else { psFeature->rot.direction = 0; } psFeature->selected = false; psFeature->body = psStats->body; psFeature->player = MAX_PLAYERS+1; //set the player out of range to avoid targeting confusions objSensorCache((BASE_OBJECT *)psFeature, NULL); objEcmCache((BASE_OBJECT *)psFeature, NULL); psFeature->bTargetted = false; psFeature->timeLastHit = 0; psFeature->lastHitWeapon = WSC_NUM_WEAPON_SUBCLASSES; // no such weapon // it has never been drawn psFeature->sDisplay.frameNumber = 0; if(getRevealStatus()) { vis = 0; } else { if(psStats->visibleAtStart) { vis = UBYTE_MAX; } else { vis = 0; } } // note that the advanced armour system current unused for features for (i = 0; i < NUM_HIT_SIDES; i++) { int j; for (j = 0; j < WC_NUM_WEAPON_CLASSES; j++) { psFeature->armour[i][j] = psFeature->psStats->armourValue; } } memset(psFeature->seenThisTick, 0, sizeof(psFeature->seenThisTick)); memset(psFeature->visible, 0, sizeof(psFeature->visible)); //load into the map data mapX = map_coord(x) - psStats->baseWidth/2; mapY = map_coord(y) - psStats->baseBreadth/2; // set up the imd for the feature if(psFeature->psStats->subType==FEAT_BUILD_WRECK) { psFeature->sDisplay.imd = getRandomWreckageImd(); } else { psFeature->sDisplay.imd = psStats->psImd; } ASSERT_OR_RETURN(NULL, psFeature->sDisplay.imd, "No IMD for feature"); // make sure we have an imd. for (width = 0; width < psStats->baseWidth; width++) { for (breadth = 0; breadth < psStats->baseBreadth; breadth++) { MAPTILE *psTile = mapTile(mapX + width, mapY + breadth); //check not outside of map - for load save game ASSERT_OR_RETURN(NULL, mapX + width < mapWidth, "x coord bigger than map width - %s, id = %d", getName(psFeature->psStats->pName), psFeature->id); ASSERT_OR_RETURN(NULL, mapY + breadth < mapHeight, "y coord bigger than map height - %s, id = %d", getName(psFeature->psStats->pName), psFeature->id); if (width != psStats->baseWidth && breadth != psStats->baseBreadth) { if (TileHasFeature(psTile)) { FEATURE *psBlock = (FEATURE *)psTile->psObject; debug(LOG_ERROR, "%s(%d) already placed at (%d+%d, %d+%d) when trying to place %s(%d) at (%d+%d, %d+%d) - removing it", getName(psBlock->psStats->pName), psBlock->id, map_coord(psBlock->pos.x), psBlock->psStats->baseWidth, map_coord(psBlock->pos.y), psBlock->psStats->baseBreadth, getName(psFeature->psStats->pName), psFeature->id, mapX, psStats->baseWidth, mapY, psStats->baseBreadth); removeFeature(psBlock); } psTile->psObject = (BASE_OBJECT*)psFeature; // if it's a tall feature then flag it in the map. if (psFeature->sDisplay.imd->max.y > TALLOBJECT_YMAX) { auxSetBlocking(mapX + width, mapY + breadth, AIR_BLOCKED); } if (psStats->subType != FEAT_GEN_ARTE && psStats->subType != FEAT_OIL_DRUM && psStats->subType != FEAT_BUILD_WRECK) { auxSetBlocking(mapX + width, mapY + breadth, FEATURE_BLOCKED); } } if( (!psStats->tileDraw) && (FromSave == false) ) { psTile->height = height; } } } psFeature->pos.z = map_TileHeight(mapX,mapY);//jps 18july97 // // set up the imd for the feature // if(psFeature->psStats->subType==FEAT_BUILD_WRECK) // { // psFeature->sDisplay.imd = wreckageImds[rand()%MAX_WRECKAGE]; // } // else // { // psFeature->sDisplay.imd = psStats->psImd; // } return psFeature; }
/** Draw the droids and structure positions on the radar. */ static void DrawRadarObjects() { UBYTE clan; PIELIGHT playerCol; PIELIGHT flashCol; int x, y; /* Show droids on map - go through all players */ for (clan = 0; clan < MAX_PLAYERS; clan++) { DROID *psDroid; //see if have to draw enemy/ally color if (bEnemyAllyRadarColor) { if (clan == selectedPlayer) { playerCol = colRadarMe; } else { playerCol = (aiCheckAlliances(selectedPlayer, clan) ? colRadarAlly : colRadarEnemy); } } else { //original 8-color mode STATIC_ASSERT(MAX_PLAYERS <= ARRAY_SIZE(clanColours)); playerCol = clanColours[getPlayerColour(clan)]; } STATIC_ASSERT(MAX_PLAYERS <= ARRAY_SIZE(flashColours)); flashCol = flashColours[getPlayerColour(clan)]; /* Go through all droids */ for (psDroid = apsDroidLists[clan]; psDroid != NULL; psDroid = psDroid->psNext) { if (psDroid->pos.x < world_coord(scrollMinX) || psDroid->pos.y < world_coord(scrollMinY) || psDroid->pos.x >= world_coord(scrollMaxX) || psDroid->pos.y >= world_coord(scrollMaxY)) { continue; } if (psDroid->visible[selectedPlayer] || (bMultiPlayer && alliancesSharedVision(game.alliance) && aiCheckAlliances(selectedPlayer, psDroid->player))) { int x = psDroid->pos.x / TILE_UNITS; int y = psDroid->pos.y / TILE_UNITS; size_t pos = (x - scrollMinX) + (y - scrollMinY) * radarTexWidth; ASSERT(pos * sizeof(*radarBuffer) < radarBufferSize, "Buffer overrun"); if (clan == selectedPlayer && gameTime - psDroid->timeLastHit < HIT_NOTIFICATION) { radarBuffer[pos] = flashCol.rgba; } else { radarBuffer[pos] = playerCol.rgba; } } } } /* Do the same for structures */ for (x = scrollMinX; x < scrollMaxX; x++) { for (y = scrollMinY; y < scrollMaxY; y++) { MAPTILE *psTile = mapTile(x, y); STRUCTURE *psStruct; size_t pos = (x - scrollMinX) + (y - scrollMinY) * radarTexWidth; ASSERT(pos * sizeof(*radarBuffer) < radarBufferSize, "Buffer overrun"); if (!TileHasStructure(psTile)) { continue; } psStruct = (STRUCTURE *)psTile->psObject; clan = psStruct->player; //see if have to draw enemy/ally color if (bEnemyAllyRadarColor) { if (clan == selectedPlayer) { playerCol = colRadarMe; } else { playerCol = (aiCheckAlliances(selectedPlayer, clan) ? colRadarAlly : colRadarEnemy); } } else { //original 8-color mode playerCol = clanColours[getPlayerColour(clan)]; } flashCol = flashColours[getPlayerColour(clan)]; if (psStruct->visible[selectedPlayer] || (bMultiPlayer && alliancesSharedVision(game.alliance) && aiCheckAlliances(selectedPlayer, psStruct->player))) { if (clan == selectedPlayer && gameTime - psStruct->timeLastHit < HIT_NOTIFICATION) { radarBuffer[pos] = flashCol.rgba; } else { radarBuffer[pos] = playerCol.rgba; } } } } }
// //////////////////////////////////////////////////////////////////////////// BOOL recvBuildFinished(NETQUEUE queue) { uint32_t structId; STRUCTURE *psStruct; Position pos; uint32_t type,typeindex; uint8_t player; NETbeginDecode(queue, GAME_BUILDFINISHED); NETuint32_t(&structId); // get the struct id. NETuint32_t(&type); // Kind of building. NETPosition(&pos); // pos NETuint8_t(&player); NETend(); ASSERT( player < MAX_PLAYERS, "invalid player %u", player); psStruct = IdToStruct(structId,ANYPLAYER); if (psStruct) { // make it complete. psStruct->currentBuildPts = psStruct->pStructureType->buildPoints+1; if (psStruct->status != SS_BUILT) { debug(LOG_SYNC, "Synch error, structure %u was not complete, and should have been.", structId); psStruct->status = SS_BUILT; buildingComplete(psStruct); } debug(LOG_SYNC, "Created normal building %u for player %u", psStruct->id, player); return true; } // The building wasn't started, so we'll have to just plonk it down in the map. // Find the structures stats for (typeindex=0; // Find structure target (typeindex<numStructureStats ) && (asStructureStats[typeindex].ref != type); typeindex++); // Check for similar buildings, to avoid overlaps if (TileHasStructure(mapTile(map_coord(pos.x), map_coord(pos.y)))) { // Get the current structure psStruct = getTileStructure(map_coord(pos.x), map_coord(pos.y)); if (asStructureStats[typeindex].type == psStruct->pStructureType->type) { // Correct type, correct location, just rename the id's to sync it.. (urgh) psStruct->id = structId; psStruct->status = SS_BUILT; buildingComplete(psStruct); debug(LOG_SYNC, "Created modified building %u for player %u", psStruct->id, player); #if defined (DEBUG) NETlogEntry("structure id modified", SYNC_FLAG, player); #endif return true; } } // Build the structure psStruct = buildStructure(&(asStructureStats[typeindex]), pos.x, pos.y, player, true); if (psStruct) { psStruct->id = structId; psStruct->status = SS_BUILT; buildingComplete(psStruct); debug(LOG_SYNC, "Huge synch error, forced to create building %u for player %u", psStruct->id, player); #if defined (DEBUG) NETlogEntry("had to plonk down a building", SYNC_FLAG, player); #endif } else { debug(LOG_SYNC, "Gigantic synch error, unable to create building for player %u", player); NETlogEntry("had to plonk down a building, BUT FAILED!", SYNC_FLAG, player); } return false; }
/** * Set the decals for a sector. This takes care of both the geometry and the texture part. */ static void setSectorDecals(int x, int y, DecalVertex *decaldata, int *decalSize) { Vector3i pos; Vector2f uv[2][2], center; int a,b; int i,j; for (i = x*sectorSize; i < x*sectorSize+sectorSize; i++) { for (j = y*sectorSize; j < y*sectorSize+sectorSize; j++) { if (i < 0 || j < 0 || i >= mapWidth || j >= mapHeight) { continue; } if (TILE_HAS_DECAL(mapTile(i, j))) { getTileTexCoords(*uv, mapTile(i,j)->texture); averageUV(¢er, *uv); getGridPos(&pos, i, j, true, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = center.x; decaldata[*decalSize].v = center.y; (*decalSize)++; a = 0; b = 1; getGridPos(&pos, i+a, j+b, false, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = uv[a][b].x; decaldata[*decalSize].v = uv[a][b].y; (*decalSize)++; a = 0; b = 0; getGridPos(&pos, i+a, j+b, false, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = uv[a][b].x; decaldata[*decalSize].v = uv[a][b].y; (*decalSize)++; getGridPos(&pos, i, j, true, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = center.x; decaldata[*decalSize].v = center.y; (*decalSize)++; a = 1; b = 1; getGridPos(&pos, i+a, j+b, false, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = uv[a][b].x; decaldata[*decalSize].v = uv[a][b].y; (*decalSize)++; a = 0; b = 1; getGridPos(&pos, i+a, j+b, false, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = uv[a][b].x; decaldata[*decalSize].v = uv[a][b].y; (*decalSize)++; getGridPos(&pos, i, j, true, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = center.x; decaldata[*decalSize].v = center.y; (*decalSize)++; a = 1; b = 0; getGridPos(&pos, i+a, j+b, false, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = uv[a][b].x; decaldata[*decalSize].v = uv[a][b].y; (*decalSize)++; a = 1; b = 1; getGridPos(&pos, i+a, j+b, false, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = uv[a][b].x; decaldata[*decalSize].v = uv[a][b].y; (*decalSize)++; getGridPos(&pos, i, j, true, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = center.x; decaldata[*decalSize].v = center.y; (*decalSize)++; a = 0; b = 0; getGridPos(&pos, i+a, j+b, false, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = uv[a][b].x; decaldata[*decalSize].v = uv[a][b].y; (*decalSize)++; a = 1; b = 0; getGridPos(&pos, i+a, j+b, false, false); decaldata[*decalSize].x = pos.x; decaldata[*decalSize].y = pos.y; decaldata[*decalSize].z = pos.z; decaldata[*decalSize].u = uv[a][b].x; decaldata[*decalSize].v = uv[a][b].y; (*decalSize)++; } } } }
/// Get the colour of the terrain tile at the specified position PIELIGHT getTileColour(int x, int y) { return mapTile(x, y)->colour; }
int main(int argc, char **argv) { char *filename, *p_filename; char *base, tmpFile[PATH_MAX]; GAMEMAP *map; MAPTILE *psTile; if (argc != 2) { printf("Usage: %s <map>\n", argv[0]); return -1; } physfs_init(argv[0]); filename = physfs_addmappath(argv[1]); p_filename = strrchr(filename, '/'); if (p_filename) { p_filename++; base = strdup(p_filename); } else { base = strdup(filename); } map = mapLoad(filename); free(filename); if (!map) { return EXIT_FAILURE; } const PreviewColors* tileColors = NULL; switch (map->tileset) { case TILESET_ARIZONA: tileColors = &pArizonaColors; break; case TILESET_URBAN: tileColors = &pUrbanColors; break; case TILESET_ROCKIES: tileColors = &pRockiesColors; break; default: fprintf(stderr, "Unknown tileset: %d\n", (int)map->tileset); mapFree(map); physfs_shutdown(); return EXIT_FAILURE; } const int mapWidth = (int) map->width; const int mapHeight = (int) map->height; int col; // RGB888 pixels uint8_t *pixels = (uint8_t*) malloc(sizeof(uint8_t) * mapWidth * mapHeight * 3); for (int y = 0; y < mapHeight; y++) { for (int x = 0; x < mapWidth; x++) { // We're placing the origin at the top for aesthetic reasons psTile = mapTile(map, x, mapHeight-1-y); col = psTile->height / 2; // 2 = ELEVATION_SCALE uint8_t * const p = &pixels[(y * map->width + x) * 3]; switch(terrainType(psTile)) { case TER_CLIFFFACE: p[0] = tileColors->cliffLow.x + (tileColors->cliffHigh.x - tileColors->cliffLow.x) * col / 256; p[1] = tileColors->cliffLow.y + (tileColors->cliffHigh.y - tileColors->cliffLow.y) * col / 256; p[2] = tileColors->cliffLow.z + (tileColors->cliffHigh.z - tileColors->cliffLow.z) * col / 256; break; case TER_WATER: p[0] = tileColors->water.x; p[1] = tileColors->water.y; p[2] = tileColors->water.z; break; case TER_ROAD: p[0] = tileColors->roadLow.x + (tileColors->roadHigh.x - tileColors->roadLow.x) * col / 256; p[1] = tileColors->roadLow.y + (tileColors->roadHigh.y - tileColors->roadLow.y) * col / 256; p[2] = tileColors->roadLow.z + (tileColors->roadHigh.z - tileColors->roadLow.z) * col / 256; break; default: p[0] = tileColors->groundLow.x + (tileColors->groundHigh.x - tileColors->groundLow.x) * col / 256; p[1] = tileColors->groundLow.y + (tileColors->groundHigh.y - tileColors->groundLow.y) * col / 256; p[2] = tileColors->groundLow.z + (tileColors->groundHigh.z - tileColors->groundLow.z) * col / 256; break; } } } paintStructureData(pixels, map); strcpy(tmpFile, base); strcat(tmpFile, ".png"); savePng(tmpFile, pixels, mapWidth, mapHeight); free(pixels); mapFree(map); physfs_shutdown(); return 0; }
// set the gateway flag on a tile static void gwSetGatewayFlag(SDWORD x, SDWORD y) { mapTile((UDWORD)x,(UDWORD)y)->tileInfoBits |= BITS_GATEWAY; }
/* The terrain revealing ray callback */ static void doWaveTerrain(const BASE_OBJECT *psObj, TILEPOS *recordTilePos, int *lastRecordTilePos) { const int sx = psObj->pos.x; const int sy = psObj->pos.y; const int sz = psObj->pos.z + MAX(MIN_VIS_HEIGHT, psObj->sDisplay.imd->max.y); const unsigned radius = objSensorRange(psObj); const int rayPlayer = psObj->player; size_t i; size_t size; const WavecastTile *tiles = getWavecastTable(radius, &size); int tileHeight, perspectiveHeight, perspectiveHeightLeeway; #define MAX_WAVECAST_LIST_SIZE 1360 // Trivial upper bound to what a fully upgraded WSS can use (its number of angles). Should probably be some factor times the maximum possible radius. Is probably a lot more than needed. Tested to need at least 180. int heights[2][MAX_WAVECAST_LIST_SIZE]; int angles[2][MAX_WAVECAST_LIST_SIZE + 1]; int readListSize = 0, readListPos = 0, writeListPos = 0; // readListSize, readListPos dummy initialisations. int readList = 0; // Reading from this list, writing to the other. Could also initialise to rand()%2. int lastHeight = 0; // lastHeight dummy initialisation. int lastAngle = 0x7FFFFFFF; // Start with full vision of all angles. (If someday wanting to make droids that can only look in one direction, change here, after getting the original angle values saved in the wavecast table.) heights[!readList][writeListPos] = -0x7FFFFFFF-1; // Smallest integer. angles[!readList][writeListPos] = 0; // Smallest angle. ++writeListPos; for (i = 0; i < size; ++i) { const int mapX = map_coord(sx) + tiles[i].dx; const int mapY = map_coord(sy) + tiles[i].dy; MAPTILE *psTile; bool seen = false; if (mapX < 0 || mapX >= mapWidth || mapY < 0 || mapY >= mapHeight) { continue; } psTile = mapTile(mapX, mapY); tileHeight = psTile->height; perspectiveHeight = (tileHeight - sz) * tiles[i].invRadius; perspectiveHeightLeeway = (tileHeight - sz + MIN_VIS_HEIGHT) * tiles[i].invRadius; if (tiles[i].angBegin < lastAngle) { // Gone around the circle. (Or just started scan.) angles[!readList][writeListPos] = lastAngle; // Flip the lists. readList = !readList; readListPos = 0; readListSize = writeListPos; writeListPos = 0; lastHeight = 1; // Impossible value since tiles[i].invRadius > 1 for all i, so triggers writing first entry in list. } lastAngle = tiles[i].angEnd; while (angles[readList][readListPos + 1] <= tiles[i].angBegin && readListPos < readListSize) { ++readListPos; // Skip, not relevant. } while (angles[readList][readListPos] < tiles[i].angEnd && readListPos < readListSize) { int oldHeight = heights[readList][readListPos]; int newHeight = MAX(oldHeight, perspectiveHeight); seen = seen || perspectiveHeightLeeway >= oldHeight; // consider point slightly above ground in case there is something on the tile if (newHeight != lastHeight) { heights[!readList][writeListPos] = newHeight; angles[!readList][writeListPos] = MAX(angles[readList][readListPos], tiles[i].angBegin); lastHeight = newHeight; ++writeListPos; ASSERT_OR_RETURN( , writeListPos <= MAX_WAVECAST_LIST_SIZE, "Visibility too complicated! Need to increase MAX_WAVECAST_LIST_SIZE."); } ++readListPos; } --readListPos; if (seen) { // Can see this tile. psTile->tileExploredBits |= alliancebits[rayPlayer]; // Share exploration with allies too visMarkTile(psObj, mapX, mapY, psTile, recordTilePos, lastRecordTilePos); // Mark this tile as seen by our sensor } }
// clear the gateway flag on a tile static void gwClearGatewayFlag(SDWORD x, SDWORD y) { mapTile((UDWORD)x,(UDWORD)y)->tileInfoBits &= ~BITS_GATEWAY; }
int main(int argc, char **argv) { char *filename, *p_filename; char *base, tmpFile[PATH_MAX]; GAMEMAP *map; if (argc != 2) { printf("Usage: %s <map>\n", argv[0]); return -1; } physfs_init(argv[0]); filename = physfs_addmappath(argv[1]); p_filename = strrchr(filename, '/'); if (p_filename) { p_filename++; base = strdup(p_filename); } else { base = strdup(filename); } if (!PHYSFS_exists(base)) { PHYSFS_mkdir(base); } map = mapLoad(filename); free(filename); if (!map) { return EXIT_FAILURE; } uint x, y; uint8_t *pixels = (uint8_t *)malloc(map->width * map->height); for (x = 0; x < map->width; x++) { for (y = 0; y < map->height; y++) { MAPTILE *psTile = mapTile(map, x, y); int pixpos = y * map->width + x; pixels[pixpos++] = psTile->height; } } strcpy(tmpFile, base); strcat(tmpFile, "/height.png"); savePngI8(tmpFile, pixels, map->width, map->height); free(pixels); mapFree(map); physfs_shutdown(); return 0; }
static void updateLightMap() { for (int j = 0; j < mapHeight; ++j) { for (int i = 0; i < mapWidth; ++i) { MAPTILE *psTile = mapTile(i, j); PIELIGHT colour = psTile->colour; if (psTile->tileInfoBits & BITS_GATEWAY && showGateways) { colour.byte.g = 255; } if (psTile->tileInfoBits & BITS_MARKED) { int m = getModularScaledGraphicsTime(2048, 255); colour.byte.r = MAX(m, 255 - m); } lightmapPixmap[(i + j * lightmapWidth) * 3 + 0] = colour.byte.r; lightmapPixmap[(i + j * lightmapWidth) * 3 + 1] = colour.byte.g; lightmapPixmap[(i + j * lightmapWidth) * 3 + 2] = colour.byte.b; if (!pie_GetFogStatus()) { // fade to black at the edges of the visible terrain area const float playerX = map_coordf(player.p.x); const float playerY = map_coordf(player.p.z); const float distA = i - (playerX - visibleTiles.x / 2); const float distB = (playerX + visibleTiles.x / 2) - i; const float distC = j - (playerY - visibleTiles.y / 2); const float distD = (playerY + visibleTiles.y / 2) - j; float darken, distToEdge; // calculate the distance to the closest edge of the visible map // determine the smallest distance distToEdge = distA; if (distB < distToEdge) { distToEdge = distB; } if (distC < distToEdge) { distToEdge = distC; } if (distD < distToEdge) { distToEdge = distD; } darken = (distToEdge) / 2.0f; if (darken <= 0) { lightmapPixmap[(i + j * lightmapWidth) * 3 + 0] = 0; lightmapPixmap[(i + j * lightmapWidth) * 3 + 1] = 0; lightmapPixmap[(i + j * lightmapWidth) * 3 + 2] = 0; } else if (darken < 1) { lightmapPixmap[(i + j * lightmapWidth) * 3 + 0] *= darken; lightmapPixmap[(i + j * lightmapWidth) * 3 + 1] *= darken; lightmapPixmap[(i + j * lightmapWidth) * 3 + 2] *= darken; } } } } }
/*looks around the given droid to see if there is any building wreckage to clear*/ FEATURE * checkForWreckage(DROID *psDroid) { FEATURE *psFeature; UDWORD startX, startY, incX, incY; SDWORD x=0, y=0; startX = map_coord(psDroid->pos.x); startY = map_coord(psDroid->pos.y); //look around the droid - max 2 tiles distance for (incX = 1, incY = 1; incX < WRECK_SEARCH; incX++, incY++) { /* across the top */ y = startY - incY; for(x = startX - incX; x < (SDWORD)(startX + incX); x++) { if(TileHasFeature(mapTile(x,y))) { psFeature = getTileFeature(x, y); if(psFeature && psFeature->psStats->subType == FEAT_BUILD_WRECK) { return psFeature; } } } /* the right */ x = startX + incX; for(y = startY - incY; y < (SDWORD)(startY + incY); y++) { if(TileHasFeature(mapTile(x,y))) { psFeature = getTileFeature(x, y); if(psFeature && psFeature->psStats->subType == FEAT_BUILD_WRECK) { return psFeature; } } } /* across the bottom*/ y = startY + incY; for(x = startX + incX; x > (SDWORD)(startX - incX); x--) { if(TileHasFeature(mapTile(x,y))) { psFeature = getTileFeature(x, y); if(psFeature && psFeature->psStats->subType == FEAT_BUILD_WRECK) { return psFeature; } } } /* the left */ x = startX - incX; for(y = startY + incY; y > (SDWORD)(startY - incY); y--) { if(TileHasFeature(mapTile(x,y))) { psFeature = getTileFeature(x, y); if(psFeature && psFeature->psStats->subType == FEAT_BUILD_WRECK) { return psFeature; } } } } return NULL; }
// not a direct script function but a helper for scrSkDefenseLocation and scrSkDefenseLocationB static bool defenseLocation(bool variantB) { SDWORD *pX, *pY, statIndex, statIndex2; UDWORD x, y, gX, gY, dist, player, nearestSoFar, count; GATEWAY *psGate, *psChosenGate; DROID *psDroid; UDWORD x1, x2, x3, x4, y1, y2, y3, y4; bool noWater; UDWORD minCount; UDWORD offset; if (!stackPopParams(6, VAL_REF | VAL_INT, &pX, VAL_REF | VAL_INT, &pY, ST_STRUCTURESTAT, &statIndex, ST_STRUCTURESTAT, &statIndex2, ST_DROID, &psDroid, VAL_INT, &player)) { debug(LOG_ERROR, "defenseLocation: failed to pop"); return false; } if (player >= MAX_PLAYERS) { ASSERT(false, "defenseLocation:player number is too high"); return false; } ASSERT_OR_RETURN(false, statIndex < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex, numStructureStats); ASSERT_OR_RETURN(false, statIndex2 < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex2, numStructureStats); STRUCTURE_STATS *psWStats = (asStructureStats + statIndex2); // check for wacky coords. if (*pX < 0 || *pX > world_coord(mapWidth) || *pY < 0 || *pY > world_coord(mapHeight) ) { goto failed; } x = map_coord(*pX); // change to tile coords. y = map_coord(*pY); // go down the gateways, find the nearest gateway with >1 empty tiles nearestSoFar = UDWORD_MAX; psChosenGate = NULL; for (psGate = gwGetGateways(); psGate; psGate = psGate->psNext) { if (auxTile(psGate->x1, psGate->y1, player) & AUXBITS_THREAT) { continue; // enemy can shoot there, not safe to build } count = 0; noWater = true; // does it have >1 tile unoccupied. if (psGate->x1 == psGate->x2) { // vert //skip gates that are too short if (variantB && (psGate->y2 - psGate->y1) <= 2) { continue; } gX = psGate->x1; for (gY = psGate->y1; gY <= psGate->y2; gY++) { if (! TileIsOccupied(mapTile(gX, gY))) { count++; } if (terrainType(mapTile(gX, gY)) == TER_WATER) { noWater = false; } } } else { // horiz //skip gates that are too short if (variantB && (psGate->x2 - psGate->x1) <= 2) { continue; } gY = psGate->y1; for (gX = psGate->x1; gX <= psGate->x2; gX++) { if (! TileIsOccupied(mapTile(gX, gY))) { count++; } if (terrainType(mapTile(gX, gY)) == TER_WATER) { noWater = false; } } } if (variantB) { minCount = 2; } else { minCount = 1; } if (count > minCount && noWater) //<NEW> min 2 tiles { // ok it's free. Is it the nearest one yet? /* Get gateway midpoint */ gX = (psGate->x1 + psGate->x2) / 2; gY = (psGate->y1 + psGate->y2) / 2; /* Estimate the distance to it */ dist = iHypot(x - gX, y - gY); /* Is it best we've found? */ if (dist < nearestSoFar && dist < 30) { /* Yes, then keep a record of it */ nearestSoFar = dist; psChosenGate = psGate; } } } if (!psChosenGate) // we have a gateway. { goto failed; } // find an unnocupied tile on that gateway. if (psChosenGate->x1 == psChosenGate->x2) { // vert gX = psChosenGate->x1; for (gY = psChosenGate->y1; gY <= psChosenGate->y2; gY++) { if (! TileIsOccupied(mapTile(gX, gY))) { y = gY; x = gX; break; } } } else { // horiz gY = psChosenGate->y1; for (gX = psChosenGate->x1; gX <= psChosenGate->x2; gX++) { if (! TileIsOccupied(mapTile(gX, gY))) { y = gY; x = gX; break; } } } // back to world coords and store result. *pX = world_coord(x) + (TILE_UNITS / 2); // return centre of tile. *pY = world_coord(y) + (TILE_UNITS / 2); scrFunctionResult.v.bval = true; if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // success { return false; } // order the droid to build two walls, one either side of the gateway. // or one in the case of a 2 size gateway. //find center of the gateway x = (psChosenGate->x1 + psChosenGate->x2) / 2; y = (psChosenGate->y1 + psChosenGate->y2) / 2; //find start pos of the gateway x1 = world_coord(psChosenGate->x1) + (TILE_UNITS / 2); y1 = world_coord(psChosenGate->y1) + (TILE_UNITS / 2); if (variantB) { offset = 2; } else { offset = 1; } if (psChosenGate->x1 == psChosenGate->x2) //vert { x2 = x1; //vert: end x pos of the first section = start x pos y2 = world_coord(y - 1) + TILE_UNITS / 2; //start y loc of the first sec x3 = x1; y3 = world_coord(y + offset) + TILE_UNITS / 2; } else //hor { x2 = world_coord(x - 1) + TILE_UNITS / 2; y2 = y1; x3 = world_coord(x + offset) + TILE_UNITS / 2; y3 = y1; } //end coords of the second section x4 = world_coord(psChosenGate->x2) + TILE_UNITS / 2; y4 = world_coord(psChosenGate->y2) + TILE_UNITS / 2; // first section. if (x1 == x2 && y1 == y2) //first sec is 1 tile only: ((2 tile gate) or (3 tile gate and first sec)) { orderDroidStatsLocDir(psDroid, DORDER_BUILD, psWStats, x1, y1, 0, ModeQueue); } else { orderDroidStatsTwoLocDir(psDroid, DORDER_LINEBUILD, psWStats, x1, y1, x2, y2, 0, ModeQueue); } // second section if (x3 == x4 && y3 == y4) { orderDroidStatsLocDirAdd(psDroid, DORDER_BUILD, psWStats, x3, y3, 0); } else { orderDroidStatsTwoLocDirAdd(psDroid, DORDER_LINEBUILD, psWStats, x3, y3, x4, y4, 0); } return true; failed: scrFunctionResult.v.bval = false; if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // failed! { return false; } return true; }
/* Create a feature on the map */ FEATURE * buildFeature(FEATURE_STATS *psStats, UDWORD x, UDWORD y,bool FromSave) { UDWORD mapX, mapY; UDWORD width,breadth, foundationMin,foundationMax, height; UDWORD startX,startY,max,min; //try and create the Feature FEATURE *psFeature = new FEATURE(generateSynchronisedObjectId(), psStats); if (psFeature == NULL) { debug(LOG_WARNING, "Feature couldn't be built."); return NULL; } // features are not in the cluster system // this will cause an assert when they still end up there psFeature->cluster = ~0; //add the feature to the list - this enables it to be drawn whilst being built addFeature(psFeature); // get the terrain average height startX = map_coord(x); startY = map_coord(y); foundationMin = TILE_MAX_HEIGHT; foundationMax = TILE_MIN_HEIGHT; for (breadth = 0; breadth < psStats->baseBreadth; breadth++) { for (width = 0; width < psStats->baseWidth; width++) { getTileMaxMin(startX + width, startY + breadth, &max, &min); if (foundationMin > min) { foundationMin = min; } if (foundationMax < max) { foundationMax = max; } } } //return the average of max/min height height = (foundationMin + foundationMax) / 2; // snap the coords to a tile if (!FromSave) { x = (x & ~TILE_MASK) + psStats->baseWidth %2 * TILE_UNITS/2; y = (y & ~TILE_MASK) + psStats->baseBreadth%2 * TILE_UNITS/2; } else { if ((x & TILE_MASK) != psStats->baseWidth %2 * TILE_UNITS/2 || (y & TILE_MASK) != psStats->baseBreadth%2 * TILE_UNITS/2) { debug(LOG_WARNING, "Feature not aligned. position (%d,%d), size (%d,%d)", x, y, psStats->baseWidth, psStats->baseBreadth); } } psFeature->pos.x = x; psFeature->pos.y = y; if (psStats->subType == FEAT_TREE) { psFeature->rot.direction = gameRand(DEG_360); } else { psFeature->rot.direction = 0; } psFeature->body = psStats->body; psFeature->inFire = false; objSensorCache((BASE_OBJECT *)psFeature, NULL); objEcmCache((BASE_OBJECT *)psFeature, NULL); // it has never been drawn psFeature->sDisplay.frameNumber = 0; for (int j = 0; j < WC_NUM_WEAPON_CLASSES; j++) { psFeature->armour[j] = psFeature->psStats->armourValue; } memset(psFeature->seenThisTick, 0, sizeof(psFeature->seenThisTick)); memset(psFeature->visible, 0, sizeof(psFeature->visible)); //load into the map data mapX = map_coord(x) - psStats->baseWidth/2; mapY = map_coord(y) - psStats->baseBreadth/2; // set up the imd for the feature psFeature->sDisplay.imd = psStats->psImd; ASSERT_OR_RETURN(NULL, psFeature->sDisplay.imd, "No IMD for feature"); // make sure we have an imd. for (width = 0; width < psStats->baseWidth; width++) { for (breadth = 0; breadth < psStats->baseBreadth; breadth++) { MAPTILE *psTile = mapTile(mapX + width, mapY + breadth); //check not outside of map - for load save game ASSERT_OR_RETURN(NULL, mapX + width < mapWidth, "x coord bigger than map width - %s, id = %d", getName(psFeature->psStats->pName), psFeature->id); ASSERT_OR_RETURN(NULL, mapY + breadth < mapHeight, "y coord bigger than map height - %s, id = %d", getName(psFeature->psStats->pName), psFeature->id); if (width != psStats->baseWidth && breadth != psStats->baseBreadth) { if (TileHasFeature(psTile)) { FEATURE *psBlock = (FEATURE *)psTile->psObject; debug(LOG_ERROR, "%s(%d) already placed at (%d+%d, %d+%d) when trying to place %s(%d) at (%d+%d, %d+%d) - removing it", getName(psBlock->psStats->pName), psBlock->id, map_coord(psBlock->pos.x), psBlock->psStats->baseWidth, map_coord(psBlock->pos.y), psBlock->psStats->baseBreadth, getName(psFeature->psStats->pName), psFeature->id, mapX, psStats->baseWidth, mapY, psStats->baseBreadth); removeFeature(psBlock); } psTile->psObject = (BASE_OBJECT*)psFeature; // if it's a tall feature then flag it in the map. if (psFeature->sDisplay.imd->max.y > TALLOBJECT_YMAX) { auxSetBlocking(mapX + width, mapY + breadth, AIR_BLOCKED); } if (psStats->subType != FEAT_GEN_ARTE && psStats->subType != FEAT_OIL_DRUM) { auxSetBlocking(mapX + width, mapY + breadth, FEATURE_BLOCKED); } } if( (!psStats->tileDraw) && (FromSave == false) ) { psTile->height = height; } } } psFeature->pos.z = map_TileHeight(mapX,mapY);//jps 18july97 return psFeature; }
/// Set the colour of the tile at the specified position void setTileColour(int x, int y, PIELIGHT colour) { MAPTILE *psTile = mapTile(x, y); psTile->colour = colour; }
/* Create a feature on the map */ FEATURE * buildFeature(FEATURE_STATS *psStats, UDWORD x, UDWORD y,bool FromSave) { //try and create the Feature FEATURE *psFeature = new FEATURE(generateSynchronisedObjectId(), psStats); if (psFeature == NULL) { debug(LOG_WARNING, "Feature couldn't be built."); return NULL; } // features are not in the cluster system // this will cause an assert when they still end up there psFeature->cluster = ~0; //add the feature to the list - this enables it to be drawn whilst being built addFeature(psFeature); // snap the coords to a tile if (!FromSave) { x = (x & ~TILE_MASK) + psStats->baseWidth %2 * TILE_UNITS/2; y = (y & ~TILE_MASK) + psStats->baseBreadth%2 * TILE_UNITS/2; } else { if ((x & TILE_MASK) != psStats->baseWidth %2 * TILE_UNITS/2 || (y & TILE_MASK) != psStats->baseBreadth%2 * TILE_UNITS/2) { debug(LOG_WARNING, "Feature not aligned. position (%d,%d), size (%d,%d)", x, y, psStats->baseWidth, psStats->baseBreadth); } } psFeature->pos.x = x; psFeature->pos.y = y; StructureBounds b = getStructureBounds(psFeature); // get the terrain average height int foundationMin = INT32_MAX; int foundationMax = INT32_MIN; for (int breadth = 0; breadth <= b.size.y; ++breadth) { for (int width = 0; width <= b.size.x; ++width) { int h = map_TileHeight(b.map.x + width, b.map.y + breadth); foundationMin = std::min(foundationMin, h); foundationMax = std::max(foundationMax, h); } } //return the average of max/min height int height = (foundationMin + foundationMax) / 2; if (psStats->subType == FEAT_TREE) { psFeature->rot.direction = gameRand(DEG_360); } else { psFeature->rot.direction = 0; } psFeature->body = psStats->body; psFeature->periodicalDamageStart = 0; psFeature->periodicalDamage = 0; // it has never been drawn psFeature->sDisplay.frameNumber = 0; memset(psFeature->seenThisTick, 0, sizeof(psFeature->seenThisTick)); memset(psFeature->visible, 0, sizeof(psFeature->visible)); // set up the imd for the feature psFeature->sDisplay.imd = psStats->psImd; ASSERT_OR_RETURN(NULL, psFeature->sDisplay.imd, "No IMD for feature"); // make sure we have an imd. for (int breadth = 0; breadth < b.size.y; ++breadth) { for (int width = 0; width < b.size.x; ++width) { MAPTILE *psTile = mapTile(b.map.x + width, b.map.y + breadth); //check not outside of map - for load save game ASSERT_OR_RETURN(NULL, b.map.x + width < mapWidth, "x coord bigger than map width - %s, id = %d", getName(psFeature->psStats), psFeature->id); ASSERT_OR_RETURN(NULL, b.map.y + breadth < mapHeight, "y coord bigger than map height - %s, id = %d", getName(psFeature->psStats), psFeature->id); if (width != psStats->baseWidth && breadth != psStats->baseBreadth) { if (TileHasFeature(psTile)) { FEATURE *psBlock = (FEATURE *)psTile->psObject; debug(LOG_ERROR, "%s(%d) already placed at (%d+%d, %d+%d) when trying to place %s(%d) at (%d+%d, %d+%d) - removing it", getName(psBlock->psStats), psBlock->id, map_coord(psBlock->pos.x), psBlock->psStats->baseWidth, map_coord(psBlock->pos.y), psBlock->psStats->baseBreadth, getName(psFeature->psStats), psFeature->id, b.map.x, b.size.x, b.map.y, b.size.y); removeFeature(psBlock); } psTile->psObject = (BASE_OBJECT*)psFeature; // if it's a tall feature then flag it in the map. if (psFeature->sDisplay.imd->max.y > TALLOBJECT_YMAX) { auxSetBlocking(b.map.x + width, b.map.y + breadth, AIR_BLOCKED); } if (psStats->subType != FEAT_GEN_ARTE && psStats->subType != FEAT_OIL_DRUM) { auxSetBlocking(b.map.x + width, b.map.y + breadth, FEATURE_BLOCKED); } } if( (!psStats->tileDraw) && (FromSave == false) ) { psTile->height = height; } } } psFeature->pos.z = map_TileHeight(b.map.x, b.map.y);//jps 18july97 return psFeature; }
/** * Check what the videocard + drivers support and divide the loaded map into sectors that can be drawn. * It also determines the lightmap size. */ bool initTerrain(void) { int i, j, x, y, a, b, absX, absY; PIELIGHT colour[2][2], centerColour; int layer = 0; RenderVertex *geometry; RenderVertex *water; DecalVertex *decaldata; int geometrySize, geometryIndexSize; int waterSize, waterIndexSize; int textureSize, textureIndexSize; GLuint *geometryIndex; GLuint *waterIndex; GLuint *textureIndex; PIELIGHT *texture; int decalSize; int maxSectorSizeIndices, maxSectorSizeVertices; bool decreasedSize = false; // call VBO support hack before using it screen_EnableVBO(); // this information is useful to prevent crashes with buggy opengl implementations glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &GLmaxElementsVertices); glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &GLmaxElementsIndices); // testing for crappy cards debug(LOG_TERRAIN, "GL_MAX_ELEMENTS_VERTICES: %i", (int)GLmaxElementsVertices); debug(LOG_TERRAIN, "GL_MAX_ELEMENTS_INDICES: %i", (int)GLmaxElementsIndices); // now we know these values, determine the maximum sector size achievable maxSectorSizeVertices = iSqrt(GLmaxElementsVertices/2)-1; maxSectorSizeIndices = iSqrt(GLmaxElementsIndices/12); debug(LOG_TERRAIN, "preferred sector size: %i", sectorSize); debug(LOG_TERRAIN, "maximum sector size due to vertices: %i", maxSectorSizeVertices); debug(LOG_TERRAIN, "maximum sector size due to indices: %i", maxSectorSizeIndices); if (sectorSize > maxSectorSizeVertices) { sectorSize = maxSectorSizeVertices; decreasedSize = true; } if (sectorSize > maxSectorSizeIndices) { sectorSize = maxSectorSizeIndices; decreasedSize = true; } if (decreasedSize) { if (sectorSize < 1) { debug(LOG_WARNING, "GL_MAX_ELEMENTS_VERTICES: %i", (int)GLmaxElementsVertices); debug(LOG_WARNING, "GL_MAX_ELEMENTS_INDICES: %i", (int)GLmaxElementsIndices); debug(LOG_WARNING, "maximum sector size due to vertices: %i", maxSectorSizeVertices); debug(LOG_WARNING, "maximum sector size due to indices: %i", maxSectorSizeIndices); debug(LOG_ERROR, "Your graphics card and/or drivers do not seem to support glDrawRangeElements, needed for the terrain renderer."); debug(LOG_ERROR, "- Do other 3D games work?"); debug(LOG_ERROR, "- Did you install the latest drivers correctly?"); debug(LOG_ERROR, "- Do you have a 3D window manager (Aero/Compiz) running?"); return false; } debug(LOG_WARNING, "decreasing sector size to %i to fit graphics card constraints", sectorSize); } // +4 = +1 for iHypot rounding, +1 for sector size rounding, +2 for edge of visibility terrainDistance = iHypot(visibleTiles.x/2, visibleTiles.y/2)+4+sectorSize/2; debug(LOG_TERRAIN, "visible tiles x:%i y: %i", visibleTiles.x, visibleTiles.y); debug(LOG_TERRAIN, "terrain view distance: %i", terrainDistance); ///////////////////// // Create the sectors xSectors = (mapWidth +sectorSize-1)/sectorSize; ySectors = (mapHeight+sectorSize-1)/sectorSize; sectors = (Sector *)malloc(sizeof(Sector)*xSectors*ySectors); //////////////////// // fill the geometry part of the sectors geometry = (RenderVertex *)malloc(sizeof(RenderVertex)*xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2); geometryIndex = (GLuint *)malloc(sizeof(GLuint)*xSectors*ySectors*sectorSize*sectorSize*12); geometrySize = 0; geometryIndexSize = 0; water = (RenderVertex *)malloc(sizeof(RenderVertex)*xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2); waterIndex = (GLuint *)malloc(sizeof(GLuint)*xSectors*ySectors*sectorSize*sectorSize*12); waterSize = 0; waterIndexSize = 0; for (x = 0; x < xSectors; x++) { for (y = 0; y < ySectors; y++) { sectors[x*ySectors + y].dirty = false; sectors[x*ySectors + y].geometryOffset = geometrySize; sectors[x*ySectors + y].geometrySize = 0; sectors[x*ySectors + y].waterOffset = waterSize; sectors[x*ySectors + y].waterSize = 0; setSectorGeometry(x, y, geometry, water, &geometrySize, &waterSize); sectors[x*ySectors + y].geometrySize = geometrySize - sectors[x*ySectors + y].geometryOffset; sectors[x*ySectors + y].waterSize = waterSize - sectors[x*ySectors + y].waterOffset; // and do the index buffers sectors[x*ySectors + y].geometryIndexOffset = geometryIndexSize; sectors[x*ySectors + y].geometryIndexSize = 0; sectors[x*ySectors + y].waterIndexOffset = waterIndexSize; sectors[x*ySectors + y].waterIndexSize = 0; for (i = 0; i < sectorSize; i++) { for (j = 0; j < sectorSize; j++) { if (x*sectorSize+i >= mapWidth || y*sectorSize+j >= mapHeight) { continue; // off map, so skip } /* One tile is composed of 4 triangles, * we need _2_ vertices per tile (1) * e.g. center and bottom left * the other 3 vertices are from the adjacent tiles * on their top and right. * (1) The top row and right column of tiles need 4 vertices per tile * because they do not have adjacent tiles on their top and right, * that is why we add _1_ row and _1_ column to provide the geometry * for these tiles. * This is the source of the '*2' and '+1' in the index math below. */ #define q(i,j,center) ((x*ySectors+y)*(sectorSize+1)*(sectorSize+1)*2 + ((i)*(sectorSize+1)+(j))*2+(center)) // First triangle geometryIndex[geometryIndexSize+0] = q(i ,j ,1); // Center vertex geometryIndex[geometryIndexSize+1] = q(i ,j ,0); // Bottom left geometryIndex[geometryIndexSize+2] = q(i+1,j ,0); // Bottom right // Second triangle geometryIndex[geometryIndexSize+3] = q(i ,j ,1); // Center vertex geometryIndex[geometryIndexSize+4] = q(i ,j+1,0); // Top left geometryIndex[geometryIndexSize+5] = q(i ,j ,0); // Bottom left // Third triangle geometryIndex[geometryIndexSize+6] = q(i ,j ,1); // Center vertex geometryIndex[geometryIndexSize+7] = q(i+1,j+1,0); // Top right geometryIndex[geometryIndexSize+8] = q(i ,j+1,0); // Top left // Fourth triangle geometryIndex[geometryIndexSize+9] = q(i ,j ,1); // Center vertex geometryIndex[geometryIndexSize+10] = q(i+1,j ,0); // Bottom right geometryIndex[geometryIndexSize+11] = q(i+1,j+1,0); // Top right geometryIndexSize += 12; if (isWater(i+x*sectorSize,j+y*sectorSize)) { waterIndex[waterIndexSize+0] = q(i ,j ,1); waterIndex[waterIndexSize+1] = q(i ,j ,0); waterIndex[waterIndexSize+2] = q(i+1,j ,0); waterIndex[waterIndexSize+3] = q(i ,j ,1); waterIndex[waterIndexSize+4] = q(i ,j+1,0); waterIndex[waterIndexSize+5] = q(i ,j ,0); waterIndex[waterIndexSize+6] = q(i ,j ,1); waterIndex[waterIndexSize+7] = q(i+1,j+1,0); waterIndex[waterIndexSize+8] = q(i ,j+1,0); waterIndex[waterIndexSize+9] = q(i ,j ,1); waterIndex[waterIndexSize+10] = q(i+1,j ,0); waterIndex[waterIndexSize+11] = q(i+1,j+1,0); waterIndexSize += 12; } } } sectors[x*ySectors + y].geometryIndexSize = geometryIndexSize - sectors[x*ySectors + y].geometryIndexOffset; sectors[x*ySectors + y].waterIndexSize = waterIndexSize - sectors[x*ySectors + y].waterIndexOffset; } } glGenBuffers(1, &geometryVBO); glError(); glBindBuffer(GL_ARRAY_BUFFER, geometryVBO); glError(); glBufferData(GL_ARRAY_BUFFER, sizeof(RenderVertex)*geometrySize, geometry, GL_DYNAMIC_DRAW); glError(); free(geometry); glGenBuffers(1, &geometryIndexVBO); glError(); glBindBuffer(GL_ARRAY_BUFFER, geometryIndexVBO); glError(); glBufferData(GL_ARRAY_BUFFER, sizeof(GLuint)*geometryIndexSize, geometryIndex, GL_STATIC_DRAW); glError(); free(geometryIndex); glGenBuffers(1, &waterVBO); glError(); glBindBuffer(GL_ARRAY_BUFFER, waterVBO); glError(); glBufferData(GL_ARRAY_BUFFER, sizeof(RenderVertex)*waterSize, water, GL_DYNAMIC_DRAW); glError(); free(water); glGenBuffers(1, &waterIndexVBO); glError(); glBindBuffer(GL_ARRAY_BUFFER, waterIndexVBO); glError(); glBufferData(GL_ARRAY_BUFFER, sizeof(GLuint)*waterIndexSize, waterIndex, GL_STATIC_DRAW); glError(); free(waterIndex); glBindBuffer(GL_ARRAY_BUFFER, 0); //////////////////// // fill the texture part of the sectors texture = (PIELIGHT *)malloc(sizeof(PIELIGHT)*xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*numGroundTypes); textureIndex = (GLuint *)malloc(sizeof(GLuint)*xSectors*ySectors*sectorSize*sectorSize*12*numGroundTypes); textureSize = 0; textureIndexSize = 0; for (layer = 0; layer < numGroundTypes; layer++) { for (x = 0; x < xSectors; x++) { for (y = 0; y < ySectors; y++) { if (layer == 0) { sectors[x*ySectors + y].textureOffset = (int *)malloc(sizeof(int)*numGroundTypes); sectors[x*ySectors + y].textureSize = (int *)malloc(sizeof(int)*numGroundTypes); sectors[x*ySectors + y].textureIndexOffset = (int *)malloc(sizeof(int)*numGroundTypes); sectors[x*ySectors + y].textureIndexSize = (int *)malloc(sizeof(int)*numGroundTypes); } sectors[x*ySectors + y].textureOffset[layer] = textureSize; sectors[x*ySectors + y].textureSize[layer] = 0; sectors[x*ySectors + y].textureIndexOffset[layer] = textureIndexSize; sectors[x*ySectors + y].textureIndexSize[layer] = 0; //debug(LOG_WARNING, "offset when filling %i: %i", layer, xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*layer); for (i = 0; i < sectorSize+1; i++) { for (j = 0; j < sectorSize+1; j++) { bool draw = false; bool off_map; // set transparency for (a=0;a<2;a++) { for(b=0;b<2;b++) { absX = x*sectorSize+i+a; absY = y*sectorSize+j+b; colour[a][b].rgba = 0x00FFFFFF; // transparent // extend the terrain type for the bottom and left edges of the map off_map = false; if (absX == mapWidth) { off_map = true; absX--; } if (absY == mapHeight) { off_map = true; absY--; } if (absX < 0 || absY < 0 || absX >= mapWidth || absY >= mapHeight) { // not on the map, so don't draw continue; } if (mapTile(absX,absY)->ground == layer) { colour[a][b].rgba = 0xFFFFFFFF; if (!off_map) { // if this point lies on the edge is may not force this tile to be drawn // otherwise this will give a bright line when fog is enabled draw = true; } } } } texture[xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*layer+((x*ySectors+y)*(sectorSize+1)*(sectorSize+1)*2 + (i*(sectorSize+1)+j)*2)].rgba = colour[0][0].rgba; averageColour(¢erColour, colour[0][0], colour[0][1], colour[1][0], colour[1][1]); texture[xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*layer+((x*ySectors+y)*(sectorSize+1)*(sectorSize+1)*2 + (i*(sectorSize+1)+j)*2+1)].rgba = centerColour.rgba; textureSize += 2; if ((draw) && i < sectorSize && j < sectorSize) { textureIndex[textureIndexSize+0] = q(i ,j ,1); textureIndex[textureIndexSize+1] = q(i ,j ,0); textureIndex[textureIndexSize+2] = q(i+1,j ,0); textureIndex[textureIndexSize+3] = q(i ,j ,1); textureIndex[textureIndexSize+4] = q(i ,j+1,0); textureIndex[textureIndexSize+5] = q(i ,j ,0); textureIndex[textureIndexSize+6] = q(i ,j ,1); textureIndex[textureIndexSize+7] = q(i+1,j+1,0); textureIndex[textureIndexSize+8] = q(i ,j+1,0); textureIndex[textureIndexSize+9] = q(i ,j ,1); textureIndex[textureIndexSize+10] = q(i+1,j ,0); textureIndex[textureIndexSize+11] = q(i+1,j+1,0); textureIndexSize += 12; } } } sectors[x*ySectors + y].textureSize[layer] = textureSize - sectors[x*ySectors + y].textureOffset[layer]; sectors[x*ySectors + y].textureIndexSize[layer] = textureIndexSize - sectors[x*ySectors + y].textureIndexOffset[layer]; } } } glGenBuffers(1, &textureVBO); glError(); glBindBuffer(GL_ARRAY_BUFFER, textureVBO); glError(); glBufferData(GL_ARRAY_BUFFER, sizeof(PIELIGHT)*xSectors*ySectors*(sectorSize+1)*(sectorSize+1)*2*numGroundTypes, texture, GL_STATIC_DRAW); glError(); free(texture); glGenBuffers(1, &textureIndexVBO); glError(); glBindBuffer(GL_ARRAY_BUFFER, textureIndexVBO); glError(); glBufferData(GL_ARRAY_BUFFER, sizeof(GLuint)*textureIndexSize, textureIndex, GL_STATIC_DRAW); glError(); free(textureIndex); glBindBuffer(GL_ARRAY_BUFFER, 0); // and finally the decals decaldata = (DecalVertex *)malloc(sizeof(DecalVertex)*mapWidth*mapHeight*12); decalSize = 0; for (x = 0; x < xSectors; x++) { for (y = 0; y < ySectors; y++) { sectors[x*ySectors + y].decalOffset = decalSize; sectors[x*ySectors + y].decalSize = 0; setSectorDecals(x, y, decaldata, &decalSize); sectors[x*ySectors + y].decalSize = decalSize - sectors[x*ySectors + y].decalOffset; } } debug(LOG_TERRAIN, "%i decals found", decalSize/12); glGenBuffers(1, &decalVBO); glError(); glBindBuffer(GL_ARRAY_BUFFER, decalVBO); glError(); glBufferData(GL_ARRAY_BUFFER, sizeof(DecalVertex)*decalSize, decaldata, GL_STATIC_DRAW); glError(); free(decaldata); glBindBuffer(GL_ARRAY_BUFFER, 0); lightmap_tex_num = 0; lightmapLastUpdate = 0; lightmapWidth = 1; lightmapHeight = 1; // determine the smallest power-of-two size we can use for the lightmap while (mapWidth > (lightmapWidth <<= 1)) {} while (mapHeight > (lightmapHeight <<= 1)) {} debug(LOG_TERRAIN, "the size of the map is %ix%i", mapWidth, mapHeight); debug(LOG_TERRAIN, "lightmap texture size is %ix%i", lightmapWidth, lightmapHeight); // Prepare the lightmap pixmap and texture lightmapPixmap = (GLubyte *)calloc(lightmapWidth * lightmapHeight, 3 * sizeof(GLubyte)); if (lightmapPixmap == NULL) { debug(LOG_FATAL, "Out of memory!"); abort(); return false; } glGenTextures(1, &lightmap_tex_num); glBindTexture(GL_TEXTURE_2D, lightmap_tex_num); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, lightmapWidth, lightmapHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, lightmapPixmap); terrainInitalised = true; glBindBuffer(GL_ARRAY_BUFFER, 0); // HACK Must unbind GL_ARRAY_BUFFER (in this function, at least), otherwise text rendering may mysteriously crash. return true; }
/* Moves one of the particles */ static void processParticle(ATPART *psPart) { SDWORD groundHeight; Vector3i pos; UDWORD x, y; MAPTILE *psTile; /* Only move if the game isn't paused */ if (!gamePaused()) { /* Move the particle - frame rate controlled */ psPart->position.x += graphicsTimeAdjustedIncrement(psPart->velocity.x); psPart->position.y += graphicsTimeAdjustedIncrement(psPart->velocity.y); psPart->position.z += graphicsTimeAdjustedIncrement(psPart->velocity.z); /* Wrap it around if it's gone off grid... */ testParticleWrap(psPart); /* If it's gone off the WORLD... */ if (psPart->position.x < 0 || psPart->position.z < 0 || psPart->position.x > ((mapWidth - 1)*TILE_UNITS) || psPart->position.z > ((mapHeight - 1)*TILE_UNITS)) { /* The kill it */ psPart->status = APS_INACTIVE; return; } /* What height is the ground under it? Only do if low enough...*/ if (psPart->position.y < 255 * ELEVATION_SCALE) { /* Get ground height */ groundHeight = map_Height(psPart->position.x, psPart->position.z); /* Are we below ground? */ if ((int)psPart->position.y < groundHeight || psPart->position.y < 0.f) { /* Kill it and return */ psPart->status = APS_INACTIVE; if (psPart->type == AP_RAIN) { x = map_coord(psPart->position.x); y = map_coord(psPart->position.z); psTile = mapTile(x, y); if (terrainType(psTile) == TER_WATER && TEST_TILE_VISIBLE(selectedPlayer, psTile)) { pos.x = psPart->position.x; pos.z = psPart->position.z; pos.y = groundHeight; effectSetSize(60); addEffect(&pos, EFFECT_EXPLOSION, EXPLOSION_TYPE_SPECIFIED, true, getImdFromIndex(MI_SPLASH), 0); } } return; } } if (psPart->type == AP_SNOW) { if (rand() % 30 == 1) { psPart->velocity.z = (float)SNOW_SPEED_DRIFT; } if (rand() % 30 == 1) { psPart->velocity.x = (float)SNOW_SPEED_DRIFT; } } } }