/* This file is part of Warzone 2100. Copyright (C) 1999-2004 Eidos Interactive Copyright (C) 2005-2007 Warzone Resurrection Project Warzone 2100 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. Warzone 2100 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. You should have received a copy of the GNU General Public License along with Warzone 2100; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Atmos.c - Handles atmospherics such as snow and rain */ /* Alex McLean, Pumpkin Studios, EIDOS Interactive */ /* At present, the water effects are part of the atmos system and aren't properly implemented in the software mode */ #include "lib/framework/frame.h" #include "lib/ivis_common/piedef.h" // FIXME Direct iVis implementation include! #include "lib/ivis_opengl/piematrix.h" #include "lib/ivis_common/piestate.h" #include "display3d.h" #include "display3ddef.h" #include "lib/gamelib/gtime.h" #include "miscimd.h" #include "map.h" #include "atmos.h" #include "loop.h" #include "lib/ivis_common/geo.h" #include "effects.h" #include "lighting.h" #include "bucket3d.h" #include "hci.h" // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Shift all this gubbins into a .h file if it makes it into game // ----------------------------------------------------------------------------- /* Roughly one per tile */ #define MAX_ATMOS_PARTICLES ((VISIBLE_XTILES * VISIBLE_YTILES)) #define SNOW_SPEED_DRIFT (40 - rand()%80) #define SNOW_SPEED_FALL (0-(rand()%40 + 80)) #define RAIN_SPEED_DRIFT (rand()%50) #define RAIN_SPEED_FALL (0-((rand()%300) + 700)) #define TYPE_WATER 75 #define TYPE_LAND 76 typedef enum { AP_RAIN, AP_SNOW } AP_TYPE; typedef enum { APS_ACTIVE, APS_INACTIVE, } AP_STATUS; ATPART asAtmosParts[MAX_ATMOS_PARTICLES]; static FRACT fraction; static UDWORD freeParticle; static UDWORD weather; // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- void atmosInitSystem ( void ); void atmosUpdateSystem ( void ); void atmosDrawParticles ( void ); void processParticle ( ATPART *psPart ); void atmosAddParticle ( iVector *pos, AP_TYPE type ); void renderParticle ( ATPART *psPart ); void testParticleWrap ( ATPART *psPart ); void atmosSetWeatherType ( WT_CLASS type ); WT_CLASS atmosGetWeatherType ( void ); // ----------------------------------------------------------------------------- /* Setup all the particles */ void atmosInitSystem( void ) { UDWORD i; for(i=0; i0 AND pos.z>0 AND pos.x<(SDWORD)((mapWidth-1)*TILE_UNITS) AND pos.z<(SDWORD)((mapHeight-1)*TILE_UNITS) ) { /* On grid, so which particle shall we add? */ switch(weather) { case WT_SNOWING: atmosAddParticle(&pos,AP_SNOW); break; case WT_RAINING: atmosAddParticle(&pos,AP_RAIN); break; case WT_NONE: break; default: break; } } } } } // ----------------------------------------------------------------------------- /* Moves one of the particles */ void processParticle( ATPART *psPart ) { SDWORD groundHeight; iVector 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 += (psPart->velocity.x * fraction); psPart->position.y += (psPart->velocity.y * fraction); psPart->position.z += (psPart->velocity.z * fraction); /* Wrap it around if it's gone off grid... */ testParticleWrap(psPart); /* If it's gone off the WORLD... */ if(psPart->position.x<0 OR psPart->position.z<0 OR psPart->position.x>((mapWidth-1)*TILE_UNITS) OR 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((UDWORD)MAKEINT(psPart->position.x),(UDWORD)MAKEINT(psPart->position.z)); /* Are we below ground? */ if( (MAKEINT(psPart->position.y) < groundHeight) OR (psPart->position.y<0.0f) ) { /* Kill it and return */ psPart->status = APS_INACTIVE; if(psPart->type == AP_RAIN) { x = (MAKEINT(psPart->position.x))>>TILE_SHIFT; y = (MAKEINT(psPart->position.z))>>TILE_SHIFT; psTile = mapTile(x,y); if(TERRAIN_TYPE(psTile) == TER_WATER AND TEST_TILE_VISIBLE(selectedPlayer,psTile)) { pos.x = MAKEINT(psPart->position.x); pos.z = MAKEINT(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 = MAKEFRACT(SNOW_SPEED_DRIFT); } if(rand()%30 == 1) { psPart->velocity.x = MAKEFRACT(SNOW_SPEED_DRIFT); } } } } // ----------------------------------------------------------------------------- /* Adds a particle to the system if it can */ void atmosAddParticle( iVector *pos, AP_TYPE type ) { UDWORD activeCount; UDWORD i; for(i=freeParticle,activeCount=0; (asAtmosParts[i].status==APS_ACTIVE) AND activeCount= (MAX_ATMOS_PARTICLES-1)) { /* Go back to the first one */ i = 0; } } /* Check the list isn't just full of essential effects */ if(activeCount>=MAX_ATMOS_PARTICLES-1) { /* All of the particles active!?!? */ return; } else { freeParticle = i; } /* Record it's type */ asAtmosParts[freeParticle].type = (UBYTE)type; /* Make it active */ asAtmosParts[freeParticle].status = APS_ACTIVE; /* Setup the imd */ switch(type) { case AP_SNOW: asAtmosParts[freeParticle].imd = getImdFromIndex(MI_SNOW); asAtmosParts[freeParticle].size = 80; break; case AP_RAIN: asAtmosParts[freeParticle].imd = getImdFromIndex(MI_RAIN); asAtmosParts[freeParticle].size = 50; break; default: break; } /* Setup position */ asAtmosParts[freeParticle].position.x = MAKEFRACT(pos->x); asAtmosParts[freeParticle].position.y = MAKEFRACT(pos->y); asAtmosParts[freeParticle].position.z = MAKEFRACT(pos->z); /* Setup its velocity */ if(type == AP_RAIN) { asAtmosParts[freeParticle].velocity.x = MAKEFRACT(RAIN_SPEED_DRIFT); asAtmosParts[freeParticle].velocity.y = MAKEFRACT(RAIN_SPEED_FALL); asAtmosParts[freeParticle].velocity.z = MAKEFRACT(RAIN_SPEED_DRIFT); } else { asAtmosParts[freeParticle].velocity.x = MAKEFRACT(SNOW_SPEED_DRIFT); asAtmosParts[freeParticle].velocity.y = MAKEFRACT(SNOW_SPEED_FALL); asAtmosParts[freeParticle].velocity.z = MAKEFRACT(SNOW_SPEED_DRIFT); } } // ----------------------------------------------------------------------------- void atmosDrawParticles( void ) { UDWORD i; if(weather==WT_NONE) { return; } /* Traverse the list */ for(i=0; iposition.x); y = MAKEINT(psPart->position.y); z = MAKEINT(psPart->position.z); /* Transform it */ dv.x = ((UDWORD)x - player.p.x) - terrainMidX * TILE_UNITS; dv.y = (UDWORD)y; dv.z = terrainMidY * TILE_UNITS - ((UDWORD)z - player.p.z); iV_MatrixBegin(); /* Push the indentity matrix */ iV_TRANSLATE(dv.x,dv.y,dv.z); rx = player.p.x & (TILE_UNITS-1); /* Get the x,z translation components */ rz = player.p.z & (TILE_UNITS-1); iV_TRANSLATE(rx,0,-rz); /* Translate */ /* Make it face camera */ iV_MatrixRotateY(-player.r.y); iV_MatrixRotateX(-player.r.x); /* Scale it... */ pie_MatScale(psPart->size); /* Draw it... */ centreX = ( player.p.x + ((visibleXTiles/2)<imd, 0, 0, brightness, 0, pie_NO_BILINEAR, 0); iV_MatrixEnd(); } // ----------------------------------------------------------------------------- /* Makes a particle wrap around - if it goes off the grid, then it returns on the other side - provided it's still on world... Which it should be */ void testParticleWrap( ATPART *psPart ) { /* Gone off left side */ if(psPart->position.x < player.p.x) { psPart->position.x += (visibleXTiles*TILE_UNITS); } /* Gone off right side */ else if(psPart->position.x > (player.p.x + (visibleXTiles*TILE_UNITS))) { psPart->position.x -= (visibleXTiles*TILE_UNITS); } /* Gone off top */ if(psPart->position.z < player.p.z) { psPart->position.z += (visibleYTiles*TILE_UNITS); } /* Gone off bottom */ else if(psPart->position.z > (player.p.z + (visibleYTiles*TILE_UNITS))) { psPart->position.z -= (visibleYTiles*TILE_UNITS); } } // ----------------------------------------------------------------------------- void atmosSetWeatherType(WT_CLASS type) { if(type == WT_NONE) { atmosInitSystem(); } else { weather = type; } } // ----------------------------------------------------------------------------- WT_CLASS atmosGetWeatherType( void ) { return (WT_CLASS)weather; }