#ifdef PRECOMPILEDHEADERS #include "TileEngine All.h" #else #include "sgp.h" #include "environment.h" #include "renderworld.h" #include "Sound Control.h" #include "overhead.h" #include "Game Clock.h" #include "Ambient Control.h" #include "Game Events.h" #include "message.h" #include "opplist.h" #include "Random.h" #include "Points.h" // added by Flugente #include "Campaign.h" // added by Flugente #include "Isometric Utils.h" // added by Flugente #include "worldman.h" // added by Flugente #include "Dialogue Control.h" // added by Flugente #endif #include "Text.h" #include "connect.h" extern SECTOR_EXT_DATA SectorExternalData[256][4]; //effects whether or not time of day effects the lighting. Underground //maps have an ambient light level that is saved in the map, and doesn't change. BOOLEAN gfBasement = FALSE; BOOLEAN gfCaves = FALSE; #define ENV_TOD_FLAGS_DAY 0x00000001 #define ENV_TOD_FLAGS_DAWN 0x00000002 #define ENV_TOD_FLAGS_DUSK 0x00000004 #define ENV_TOD_FLAGS_NIGHT 0x00000008 /* #define DAWNLIGHT_START ( 5 * 60 ) #define DAWN_START ( 6 * 60 ) #define DAY_START ( 8 * 60 ) #define TWILLIGHT_START ( 19 * 60 ) #define DUSK_START ( 20 * 60 ) #define NIGHT_START ( 22 * 60 ) */ #define DAWN_START ( 6 * 60 + 47 ) //6:47AM #define DAY_START ( 7 * 60 + 5 ) //7:05AM #define DUSK_START ( 20 * 60 + 57 ) //8:57PM #define NIGHT_START ( 21 * 60 + 15 ) //9:15PM #define DAWN_TO_DAY (DAY_START-DAWN_START) #define DAY_TO_DUSK (DUSK_START-DAY_START) #define DUSK_TO_NIGHT (NIGHT_START-DUSK_START) #define NIGHT_TO_DAWN (24*60-NIGHT_START+DAWN_START) //UINT32 guiEnvWeather = 0; // Flugente: this was used as a flagmask, but will now be used as an enum - we can't have several types of weather at the same time anyway UINT32 guiRainLoop = NO_SAMPLE; // frame cues for lightning UINT8 ubLightningTable[3][10][2]= { { {0, 15}, {1, 0}, {2, 0}, {3, 6}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0} }, { {0, 15}, {1, 0}, {2, 0}, {3, 6}, {4, 0}, {5, 15}, {6, 0}, {7, 6}, {8, 0}, {9, 0} }, { {0, 15}, {1, 0}, {2, 15}, {3, 0}, {4, 0}, {5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0} } }; // CJC: I don't think these are used anywhere! UINT8 guiTODFlags[ENV_NUM_TIMES] = { ENV_TOD_FLAGS_NIGHT, // 00 ENV_TOD_FLAGS_NIGHT, // 01 ENV_TOD_FLAGS_NIGHT, // 02 ENV_TOD_FLAGS_NIGHT, // 03 ENV_TOD_FLAGS_NIGHT, // 04 ENV_TOD_FLAGS_DAWN, // 05 ENV_TOD_FLAGS_DAWN, // 06 ENV_TOD_FLAGS_DAWN, // 07 ENV_TOD_FLAGS_DAY, // 08 ENV_TOD_FLAGS_DAY, // 09 ENV_TOD_FLAGS_DAY, // 10 ENV_TOD_FLAGS_DAY, // 11 ENV_TOD_FLAGS_DAY, // 12 ENV_TOD_FLAGS_DAY, // 13 ENV_TOD_FLAGS_DAY, // 14 ENV_TOD_FLAGS_DAY, // 15 ENV_TOD_FLAGS_DAY, // 16 ENV_TOD_FLAGS_DAY, // 17 ENV_TOD_FLAGS_DAY, // 18 ENV_TOD_FLAGS_DUSK, // 19 ENV_TOD_FLAGS_DUSK, // 20 ENV_TOD_FLAGS_DUSK, // 21 ENV_TOD_FLAGS_NIGHT, // 22 ENV_TOD_FLAGS_NIGHT}; // 23 typedef enum { COOL, WARM, HOT } Temperatures; typedef enum { TEMPERATURE_DESERT_COOL, TEMPERATURE_DESERT_WARM, TEMPERATURE_DESERT_HOT, TEMPERATURE_GLOBAL_COOL, TEMPERATURE_GLOBAL_WARM, TEMPERATURE_GLOBAL_HOT, } TemperatureEvents; #define DESERT_WARM_START ( 8 * 60 ) #define DESERT_HOT_START ( 9 * 60 ) #define DESERT_HOT_END (17 * 60 ) #define DESERT_WARM_END (19 * 60 ) #define GLOBAL_WARM_START ( 9 * 60 ) #define GLOBAL_HOT_START (12 * 60 ) #define GLOBAL_HOT_END (14 * 60 ) #define GLOBAL_WARM_END (17 * 60 ) #define HOT_DAY_LIGHTLEVEL 2 BOOLEAN fTimeOfDayControls=TRUE; UINT32 guiEnvTime=0; UINT32 guiEnvDay=0; UINT8 gubEnvLightValue = 0; BOOLEAN gfDoLighting = FALSE; UINT8 gubDesertTemperature = 0; UINT8 gubGlobalTemperature = 0; // local prototypes void EnvDoLightning(void); // polled by the game to handle time/atmosphere changes from gamescreen void EnvironmentController( BOOLEAN fCheckForLights ) { UINT32 uiOldWorldHour; UINT8 ubLightAdjustFromWeather = 0; // do none of this stuff in the basement or caves if( gfBasement || gfCaves ) { if (gGameSettings.fOptions[ TOPTION_RAIN_SOUND ] == TRUE) { if ( guiRainLoop != NO_SAMPLE ) { SoundStop( guiRainLoop ); guiRainLoop = NO_SAMPLE; } } return; } if(fTimeOfDayControls ) { uiOldWorldHour = GetWorldHour(); // If hour is different if ( uiOldWorldHour != guiEnvTime ) { // Hour change.... guiEnvTime=uiOldWorldHour; } //ExecuteStrategicEventsUntilTimeStamp( (UINT16)GetWorldTotalMin( ) ); // Polled weather stuff... // ONly do indooors if( !gfBasement && !gfCaves ) { if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_RAIN || SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_THUNDERSHOWERS || SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_SANDSTORM ) { if (gGameSettings.fOptions[ TOPTION_RAIN_SOUND ] == TRUE) { if ( guiRainLoop == NO_SAMPLE ) { if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_THUNDERSHOWERS ) { guiRainLoop = PlayJA2Ambient( RAIN_1, 140, 0 ); } else if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_SANDSTORM ) { guiRainLoop = PlayJA2Ambient( WIND, 105, 0 ); } else { guiRainLoop = PlayJA2Ambient( RAIN_1, 70, 0 ); } } } // Do lightning if we want... if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_THUNDERSHOWERS ) { EnvDoLightning( ); } } else { if (gGameSettings.fOptions[ TOPTION_RAIN_SOUND ] == TRUE) { if ( guiRainLoop != NO_SAMPLE ) { SoundStop( guiRainLoop ); guiRainLoop = NO_SAMPLE; } } } } if ( gfDoLighting && fCheckForLights ) { // Adjust light level based on weather... ubLightAdjustFromWeather = GetTimeOfDayAmbientLightLevel(); // ONly do indooors if( !gfBasement && !gfCaves ) { // Rain storms.... // Thunder showers.. make darker if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_THUNDERSHOWERS ) { ubLightAdjustFromWeather = (UINT8)(__min( gubEnvLightValue+2, NORMAL_LIGHTLEVEL_NIGHT )); } else if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_RAIN ) { ubLightAdjustFromWeather = (UINT8)(__min( gubEnvLightValue+1, NORMAL_LIGHTLEVEL_NIGHT )); } else if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_SANDSTORM ) { ubLightAdjustFromWeather = (UINT8)(__min( gubEnvLightValue + 5, NORMAL_LIGHTLEVEL_NIGHT )); } else if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_SNOW ) { ubLightAdjustFromWeather = (UINT8)(__min( gubEnvLightValue + 1, NORMAL_LIGHTLEVEL_NIGHT )); } } LightSetBaseLevel( ubLightAdjustFromWeather ); //Update Merc Lights since the above function modifies it. HandlePlayerTogglingLightEffects( FALSE ); // Make teams look for all // AllTeamsLookForAll( FALSE ); // Set global light value SetRenderFlags(RENDER_FLAG_FULL); gfDoLighting = FALSE; } } } void BuildDayLightLevels() { UINT32 uiLoop, uiHour; /* // Dawn; light 12 AddEveryDayStrategicEvent( EVENT_CHANGELIGHTVAL, DAWNLIGHT_START, NORMAL_LIGHTLEVEL_NIGHT - 1 ); // loop from light 12 down to light 4 for (uiLoop = 1; uiLoop < 8; uiLoop++) { AddEveryDayStrategicEvent( EVENT_CHANGELIGHTVAL, DAWN_START + 15 * uiLoop, NORMAL_LIGHTLEVEL_NIGHT - 1 - uiLoop ); } */ //Transition from night to day for( uiLoop = 0; uiLoop < 9; ++uiLoop ) { AddEveryDayStrategicEvent( EVENT_CHANGELIGHTVAL, DAWN_START + 2 * uiLoop, NORMAL_LIGHTLEVEL_NIGHT - 1 - uiLoop ); } // Add events for hot times AddEveryDayStrategicEvent( EVENT_TEMPERATURE_UPDATE, DESERT_WARM_START, TEMPERATURE_DESERT_WARM ); AddEveryDayStrategicEvent( EVENT_TEMPERATURE_UPDATE, DESERT_HOT_START, TEMPERATURE_DESERT_HOT ); AddEveryDayStrategicEvent( EVENT_TEMPERATURE_UPDATE, DESERT_HOT_END, TEMPERATURE_DESERT_WARM ); AddEveryDayStrategicEvent( EVENT_TEMPERATURE_UPDATE, DESERT_WARM_END, TEMPERATURE_DESERT_COOL ); AddEveryDayStrategicEvent( EVENT_TEMPERATURE_UPDATE, GLOBAL_WARM_START, TEMPERATURE_GLOBAL_WARM ); AddEveryDayStrategicEvent( EVENT_TEMPERATURE_UPDATE, GLOBAL_HOT_START, TEMPERATURE_GLOBAL_HOT ); AddEveryDayStrategicEvent( EVENT_TEMPERATURE_UPDATE, GLOBAL_HOT_END, TEMPERATURE_GLOBAL_WARM ); AddEveryDayStrategicEvent( EVENT_TEMPERATURE_UPDATE, GLOBAL_WARM_END, TEMPERATURE_GLOBAL_COOL ); /* // Twilight; light 5 AddEveryDayStrategicEvent( EVENT_CHANGELIGHTVAL, TWILLIGHT_START, NORMAL_LIGHTLEVEL_DAY + 1 ); // Dusk; loop from light 5 up to 12 for (uiLoop = 1; uiLoop < 8; uiLoop++) { AddEveryDayStrategicEvent( EVENT_CHANGELIGHTVAL, DUSK_START + 15 * uiLoop, NORMAL_LIGHTLEVEL_DAY + 1 + uiLoop ); } */ //Transition from day to night for( uiLoop = 0; uiLoop < 9; ++uiLoop ) { AddEveryDayStrategicEvent( EVENT_CHANGELIGHTVAL, DUSK_START + 2 * uiLoop, NORMAL_LIGHTLEVEL_DAY + 1 + uiLoop ); } //Set up the scheduling for turning lights on and off based on the various types. uiHour = NIGHT_TIME_LIGHT_START_HOUR == 24 ? 0 : NIGHT_TIME_LIGHT_START_HOUR; AddEveryDayStrategicEvent( EVENT_TURN_ON_NIGHT_LIGHTS, uiHour * 60, 0 ); uiHour = NIGHT_TIME_LIGHT_END_HOUR == 24 ? 0 : NIGHT_TIME_LIGHT_END_HOUR; AddEveryDayStrategicEvent( EVENT_TURN_OFF_NIGHT_LIGHTS, uiHour * 60, 0 ); uiHour = PRIME_TIME_LIGHT_START_HOUR == 24 ? 0 : PRIME_TIME_LIGHT_START_HOUR; AddEveryDayStrategicEvent( EVENT_TURN_ON_PRIME_LIGHTS, uiHour * 60, 0 ); uiHour = PRIME_TIME_LIGHT_END_HOUR == 24 ? 0 : PRIME_TIME_LIGHT_END_HOUR; AddEveryDayStrategicEvent( EVENT_TURN_OFF_PRIME_LIGHTS, uiHour * 60, 0 ); } void BuildDayAmbientSounds( ) { // Add events! for ( INT32 cnt = 0; cnt < gsNumAmbData; ++cnt ) { switch( gAmbData[ cnt ].ubTimeCatagory ) { case AMB_TOD_DAWN: AddSameDayRangedStrategicEvent( EVENT_AMBIENT, DAWN_START, DAWN_TO_DAY, cnt ); break; case AMB_TOD_DAY: AddSameDayRangedStrategicEvent( EVENT_AMBIENT, DAY_START, DAY_TO_DUSK, cnt ); break; case AMB_TOD_DUSK: AddSameDayRangedStrategicEvent( EVENT_AMBIENT, DUSK_START, DUSK_TO_NIGHT, cnt ); break; case AMB_TOD_NIGHT: AddSameDayRangedStrategicEvent( EVENT_AMBIENT, NIGHT_START, NIGHT_TO_DAWN, cnt ); break; } } //guiRainLoop = NO_SAMPLE; //rain if (gGameSettings.fOptions[ TOPTION_RAIN_SOUND ] == TRUE) { if ( guiRainLoop != NO_SAMPLE ) { SoundStop( guiRainLoop ); guiRainLoop = NO_SAMPLE; } } //end rain } // Flugente: weather can now be localized, meaning that different sectors can have different types of weather at the same time extern SECTOR_EXT_DATA SectorExternalData[256][4]; // we use this structure to set up weather. For a given weather type, we put the chance of the weather happening in here, and later note whether a sector has been checked, and if weather does occur struct WeatherHelperStruct { WeatherHelperStruct( ) : weatherhappens( FALSE ), handled( FALSE ), chance( 0 ) {} BOOLEAN weatherhappens; BOOLEAN handled; UINT8 chance; }; void DetermineLocalizedWeatherGivenHelperArray( WeatherHelperStruct helperstruct[256] ) { UINT32 totalweatherpoints = 0; for ( UINT8 sector = 0; sector < 255; ++sector ) { totalweatherpoints += helperstruct[sector].chance; } if ( totalweatherpoints ) { INT32 chosenweatherpoint = Random( totalweatherpoints ); UINT8 chosensector = 0; for ( UINT8 sector = 0; sector < 255; ++sector ) { chosenweatherpoint -= helperstruct[sector].chance; if ( chosenweatherpoint < 0 ) { chosensector = sector; break; } } // we now have a starting sector. We now loop over all sectors several times. If a sector is adjacent to a sector with new weather, determine whether the weather will also happen there helperstruct[chosensector].weatherhappens = TRUE; helperstruct[chosensector].handled = TRUE; BOOLEAN weatherchangehappened = TRUE; while ( weatherchangehappened ) { weatherchangehappened = FALSE; for ( UINT8 sector = 0; sector < 255; ++sector ) { if ( !helperstruct[sector].handled ) { // if it can't rain in a sector anyway, screw this if ( !helperstruct[sector].chance ) { helperstruct[sector].handled = TRUE; continue; } INT8 x = SECTORX( sector ); INT8 y = SECTORY( sector ); // check adjacent sectors; INT16 adjacentsectors[4] = {-1, -1, -1, -1}; if ( x > 0 ) adjacentsectors[0] = SECTOR( x - 1, y ); if ( x < 15 ) adjacentsectors[1] = SECTOR( x + 1, y ); if ( y > 0 ) adjacentsectors[2] = SECTOR( x, y - 1 ); if ( y < 15 ) adjacentsectors[3] = SECTOR( x, y + 1 ); for ( int i = 0; i < 4; ++i ) { if ( adjacentsectors[i] > 0 ) { UINT8 adjacentsector = (UINT8)(adjacentsectors[i]); // if weather happens in the adjacent sector, determine whether it also happens here if ( helperstruct[adjacentsector].handled && helperstruct[adjacentsector].weatherhappens ) { helperstruct[sector].weatherhappens = Chance( helperstruct[sector].chance ); helperstruct[sector].handled = TRUE; // if weather spreads, we will continue with this function if ( helperstruct[sector].weatherhappens ) { weatherchangehappened = TRUE; continue; } } } } } } } // loop over all sectors again. If a sector doesn't have the new weather, but most or all adjacent sectors do, consider adding it here too for ( UINT8 sector = 0; sector < 255; ++sector ) { if ( !helperstruct[sector].weatherhappens && helperstruct[sector].chance ) { INT8 x = SECTORX( sector ); INT8 y = SECTORY( sector ); // check adjacent sectors; INT16 adjacentsectors[4] = {-1, -1, -1, -1}; if ( x > 0 ) adjacentsectors[0] = SECTOR( x - 1, y ); if ( x < 15 ) adjacentsectors[1] = SECTOR( x + 1, y ); if ( y > 0 ) adjacentsectors[2] = SECTOR( x, y - 1 ); if ( y < 15 ) adjacentsectors[3] = SECTOR( x, y + 1 ); UINT8 adjacentweathercount = 0; for ( int i = 0; i < 4; ++i ) { if ( adjacentsectors[i] > 0 ) { UINT8 adjacentsector = (UINT8)(adjacentsectors[i]); if ( helperstruct[adjacentsector].weatherhappens ) { ++adjacentweathercount; } } } // no weather 'holes' if ( adjacentweathercount >= 4 ) { helperstruct[sector].weatherhappens = TRUE; } // if weather is on 3 sides, at least consider awarding it else if ( adjacentweathercount == 3 && Chance( 66 ) ) { helperstruct[sector].weatherhappens = TRUE; } } } } } void ForecastDayEvents( ) { UINT32 uiOldDay; UINT32 uiStartTime, uiEndTime; // Get current day and see if different if ( ( uiOldDay = GetWorldDay() ) != guiEnvDay ) { // It's a new day, forecast weather guiEnvDay = uiOldDay; // Set light level changes //BuildDayLightLevels(); // Build ambient sound queues BuildDayAmbientSounds( ); // ATE: Don't forecast if start of game... if ( guiEnvDay > 1 ) { //rain if ( gGameExternalOptions.gfAllowRain ) { for ( int i = 0; i < gGameExternalOptions.gusWeatherPerDayRain; ++i ) { if ( Chance( gGameExternalOptions.gusRainChancePerDay ) ) { uiStartTime = GetWorldTotalMin( ) + (UINT32)(Random( 1440 - 1 - gGameExternalOptions.gusRainMaxLength )); UINT32 length = gGameExternalOptions.gusRainMinLength + Random( max(1, gGameExternalOptions.gusRainMaxLength - gGameExternalOptions.gusRainMinLength ) ); uiEndTime = uiStartTime + length; // We determine the 'start point' of the weather via xml chances. Weather can then spread to adjacent sectors according to chances WeatherHelperStruct helperstruct[256]; for ( UINT8 sector = 0; sector < 255; ++sector ) helperstruct[sector].chance = SectorExternalData[sector][0].rainchance; DetermineLocalizedWeatherGivenHelperArray( helperstruct ); // now that we know in which sectors the new weather will happen, set up timed events BOOLEAN dothunderstorm = (gGameExternalOptions.gfAllowLightning && Chance( 50 )); for ( UINT8 sector = 0; sector < 255; ++sector ) { if ( helperstruct[sector].weatherhappens ) { AddStrategicEvent( dothunderstorm ? EVENT_WEATHER_THUNDERSHOWERS : EVENT_WEATHER_SHOWERS, uiStartTime, sector ); AddStrategicEvent( EVENT_WEATHER_NORMAL, uiEndTime, sector ); } } } } } // sandstorm if ( gGameExternalOptions.gfAllowSandStorms ) { for ( int i = 0; i < gGameExternalOptions.gusWeatherPerDaySandstorm; ++i ) { if ( Chance( gGameExternalOptions.gusSandStormsChancePerDay ) ) { uiStartTime = GetWorldTotalMin( ) + (UINT32)(Random( 1440 - 1 - gGameExternalOptions.gusSandStormsMaxLength )); UINT32 length = gGameExternalOptions.gusSandStormsMinLength + Random( max( 1, gGameExternalOptions.gusSandStormsMaxLength - gGameExternalOptions.gusSandStormsMinLength ) ); uiEndTime = uiStartTime + length; // We determine the 'start point' of the weather via xml chances. Weather can then spread to adjacent sectors according to chances WeatherHelperStruct helperstruct[256]; for ( UINT8 sector = 0; sector < 255; ++sector ) helperstruct[sector].chance = SectorExternalData[sector][0].sandstormchance; DetermineLocalizedWeatherGivenHelperArray( helperstruct ); for ( UINT8 sector = 0; sector < 255; ++sector ) { if ( helperstruct[sector].weatherhappens ) { AddStrategicEvent( EVENT_WEATHER_SANDSTORM, uiStartTime, sector ); AddStrategicEvent( EVENT_WEATHER_NORMAL, uiEndTime, sector ); } } } } } // snow if ( gGameExternalOptions.gfAllowSnow && Chance( gGameExternalOptions.gusSnowChancePerDay ) ) { for ( int i = 0; i < gGameExternalOptions.gusWeatherPerDaySnow; ++i ) { if ( Chance( gGameExternalOptions.gusSnowChancePerDay ) ) { uiStartTime = GetWorldTotalMin( ) + (UINT32)(Random( 1440 - 1 - gGameExternalOptions.gusSnowMaxLength )); UINT32 length = gGameExternalOptions.gusSnowMinLength + Random( max( 1, gGameExternalOptions.gusSnowMaxLength - gGameExternalOptions.gusSnowMinLength ) ); uiEndTime = uiStartTime + length; // We determine the 'start point' of the weather via xml chances. Weather can then spread to adjacent sectors according to chances WeatherHelperStruct helperstruct[256]; for ( UINT8 sector = 0; sector < 255; ++sector ) helperstruct[sector].chance = SectorExternalData[sector][0].snowchance; DetermineLocalizedWeatherGivenHelperArray( helperstruct ); for ( UINT8 sector = 0; sector < 255; ++sector ) { if ( helperstruct[sector].weatherhappens ) { AddStrategicEvent( EVENT_WEATHER_SNOW, uiStartTime, sector ); AddStrategicEvent( EVENT_WEATHER_NORMAL, uiEndTime, sector ); } } } } } } } } void EnvEnableTOD(void) { fTimeOfDayControls=TRUE; } void EnvDisableTOD(void) { fTimeOfDayControls=FALSE; } //rain UINT32 guiMinLightningInterval = 5; UINT32 guiMaxLightningInterval = 15; UINT32 guiMinDLInterval = 1; UINT32 guiMaxDLInterval = 5; UINT32 guiProlongLightningIfSeenSomeone = 5; UINT32 guiChanceToDoLightningBetweenTurns = 20; // 60 = 1 second #define MIN_LIGHTNING_INTERVAL ( 60 * guiMinLightningInterval ) #define MAX_LIGHTNING_INTERVAL ( 60 * guiMaxLightningInterval ) #define MAX_DELAYED_SOUNDS 10 #define NO_DL_SOUND 0xFFFFFFFF // 1000 = 1 second #define MIN_DL_INTERVAL ( 1000 * guiMinDLInterval ) #define MAX_DL_INTERVAL ( 1000 * guiMaxDLInterval ) #define EXTRA_ADD_VIS_DIST_IF_SEEN_SOMEONE ( 1000 * guiProlongLightningIfSeenSomeone ) #define CHANCE_TO_DO_LIGHTNING_BETWEEN_TURNS guiChanceToDoLightningBetweenTurns BOOLEAN gfLightningInProgress = FALSE; BOOLEAN gfHaveSeenSomeone = FALSE; UINT8 ubRealAmbientLightLevel = 0; BOOLEAN gfTurnBasedDoLightning = FALSE; BOOLEAN gfTurnBasedLightningEnd = FALSE; BOOLEAN gfWasTurnBasedWhenLightningBegin = FALSE; UINT8 gubLastTeamLightning; #define IS_TURNBASED ( gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT ) void BeginTeamTurn( UINT8 ubTeam ); void EnvDoLightning(void) { static UINT32 uiCount=10, uiIndex=0, uiStrike=0, uiFrameNext=0; static UINT8 ubLevel=0, ubLastLevel=0; static UINT32 uiLastUpdate = 0xFFFFFFFF; static UINT32 uiTurnOffExtraVisDist = 0xFFFFFFFF; static UINT32 pDelayedSounds[ MAX_DELAYED_SOUNDS ]; UINT32 uiDSIndex; if( GetJA2Clock() < uiLastUpdate ) { uiLastUpdate = 0; memset( pDelayedSounds, NO_DL_SOUND, sizeof( UINT32 ) * MAX_DELAYED_SOUNDS ); } if( GetJA2Clock() < uiLastUpdate + 1000 / 60 ) return; else uiLastUpdate = GetJA2Clock(); if( GetJA2Clock() > uiTurnOffExtraVisDist ) { AllTeamsLookForAll( FALSE ); uiTurnOffExtraVisDist = 0xFFFFFFFF; if( gfTurnBasedLightningEnd ) { BeginTeamTurn( gubLastTeamLightning ); gfTurnBasedLightningEnd = FALSE; } } for ( uiDSIndex = 0; uiDSIndex < MAX_DELAYED_SOUNDS; ++uiDSIndex ) { if( GetJA2Clock() > pDelayedSounds[ uiDSIndex ] ) { PlayJA2Ambient(LIGHTNING_1+Random(2), HIGHVOLUME, 1); pDelayedSounds[ uiDSIndex ] = NO_DL_SOUND; } } if ( gfPauseDueToPlayerGamePause ) { return; } if( IS_TURNBASED && !gfLightningInProgress ) { if( !gfTurnBasedDoLightning ) { return; } else { gfTurnBasedDoLightning = FALSE; uiFrameNext = 1; uiCount = 0; gfTurnBasedLightningEnd = TRUE; } } ++uiCount; if(uiCount >= (uiFrameNext+10)) { if( gfHaveSeenSomeone && gfWasTurnBasedWhenLightningBegin ) uiTurnOffExtraVisDist = GetJA2Clock() + EXTRA_ADD_VIS_DIST_IF_SEEN_SOMEONE; else uiTurnOffExtraVisDist = GetJA2Clock(); gfLightningInProgress = FALSE; gfHaveSeenSomeone = FALSE; uiCount=0; uiIndex=0; ubLevel=0; ubLastLevel=0; uiStrike=Random(3); uiFrameNext=MIN_LIGHTNING_INTERVAL+Random(MAX_LIGHTNING_INTERVAL - MIN_LIGHTNING_INTERVAL); } else if(uiCount >= uiFrameNext) { if(uiCount == uiFrameNext) { //EnvStopCrickets(); // PlayJA2Ambient(LIGHTNING_1+Random(2), HIGHVOLUME, 1); for( uiDSIndex = 0; uiDSIndex < MAX_DELAYED_SOUNDS; ++uiDSIndex ) if( pDelayedSounds[ uiDSIndex ] == NO_DL_SOUND ) { pDelayedSounds[ uiDSIndex ] = GetJA2Clock() + MIN_DL_INTERVAL+Random(MAX_DL_INTERVAL - MIN_DL_INTERVAL); break; } ubRealAmbientLightLevel = ubAmbientLightLevel; gfWasTurnBasedWhenLightningBegin = IS_TURNBASED; gfLightningInProgress = TRUE; AllTeamsLookForAll( FALSE ); } while(uiCount > ((UINT32)ubLightningTable[uiStrike][uiIndex][0] + uiFrameNext)) ++uiIndex; ubLastLevel=ubLevel; ubLevel=min( ubRealAmbientLightLevel - 1, ubLightningTable[uiStrike][uiIndex][1] ); /* // ATE: Don't modify if scrolling! if ( gfScrollPending || gfScrollInertia ) { } else*/ { if(ubLastLevel!=ubLevel) { if(ubLevel > ubLastLevel) { LightAddBaseLevel(0, (UINT8)(ubLevel-ubLastLevel)); if(ubLevel > 0) RenderSetShadows(TRUE); } else { // LightSubtractBaseLevel(0, (UINT8)(ubLastLevel-ubLevel)); LightSetBaseLevel( ubRealAmbientLightLevel - ubLevel ); if(ubLevel > 0) RenderSetShadows(TRUE); } SetRenderFlags(RENDER_FLAG_FULL); } } } } BOOLEAN LightningEndOfTurn( UINT8 ubTeam ) { if ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather != WEATHER_FORECAST_THUNDERSHOWERS ) return TRUE; if ( Random(100) >= CHANCE_TO_DO_LIGHTNING_BETWEEN_TURNS ) return TRUE; if( !gfTurnBasedLightningEnd ) { gfTurnBasedDoLightning = TRUE; gubLastTeamLightning = ubTeam; EnvDoLightning(); return FALSE; } return TRUE; } UINT8 GetTimeOfDayAmbientLightLevel() { if (!is_networked) { if ( SectorTemperature( GetWorldMinutesInDay(), gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == HOT ) { return( HOT_DAY_LIGHTLEVEL ); } else { return( gubEnvLightValue ); } } else { return ( gubEnvLightValue ); } } void ChangeWeather( UINT8 aSector, UINT8 aType ) { if ( aType >= WEATHER_FORECAST_MAX ) { ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Error in ChangeWeather(): Invalid weather" ); return; } if ( SectorInfo[aSector].usWeather != aType ) { if ( SECTOR( gWorldSectorX, gWorldSectorY ) == aSector && !gbWorldSectorZ ) { if ( !gfBasement && !gfCaves ) gfDoLighting = TRUE; CHAR16 pStrSectorName[128]; GetSectorIDString( SECTORX( aSector ), SECTORY( aSector ), 0, pStrSectorName, TRUE ); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: Changed weather from %s to %s", pStrSectorName, szWeatherTypeText[SectorInfo[aSector].usWeather], szWeatherTypeText[aType] ); } // Flugente: additional dialogue AdditionalTacticalCharacterDialogue_AllInSector( SECTORX( aSector ), SECTORY( aSector ), 0, NO_PROFILE, ADE_WEATHERCHANGE, aType ); // First turn off whatever weather it is, then turn on the requested weather SectorInfo[aSector].usWeather = aType; } } UINT8 GetWeatherInCurrentSector() { // no weather underground! if ( gbWorldSectorZ ) return WEATHER_FORECAST_NORMAL; return SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather; } //end rain /* void EnvDoLightning(void) { static UINT32 uiCount=0, uiIndex=0, uiStrike=0, uiFrameNext=1000; static UINT8 ubLevel=0, ubLastLevel=0; if ( gfPauseDueToPlayerGamePause ) { return; } uiCount++; if(uiCount >= (uiFrameNext+10)) { uiCount=0; uiIndex=0; ubLevel=0; ubLastLevel=0; uiStrike=Random(3); uiFrameNext=1000+Random(1000); } else if(uiCount >= uiFrameNext) { if(uiCount == uiFrameNext) { //EnvStopCrickets(); PlayJA2Ambient(LIGHTNING_1+Random(2), HIGHVOLUME, 1); } while(uiCount > ((UINT32)ubLightningTable[uiStrike][uiIndex][0] + uiFrameNext)) uiIndex++; ubLastLevel=ubLevel; ubLevel=ubLightningTable[uiStrike][uiIndex][1]; // ATE: Don't modify if scrolling! if ( gfScrollPending || gfScrollInertia ) { } else { if(ubLastLevel!=ubLevel) { if(ubLevel > ubLastLevel) { LightAddBaseLevel(0, (UINT8)(ubLevel-ubLastLevel)); if(ubLevel > 0) RenderSetShadows(TRUE); } else { LightSubtractBaseLevel(0, (UINT8)(ubLastLevel-ubLevel)); if(ubLevel > 0) RenderSetShadows(TRUE); } SetRenderFlags(RENDER_FLAG_FULL); } } } } UINT8 GetTimeOfDayAmbientLightLevel() { if ( SectorTemperature( GetWorldMinutesInDay(), gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == HOT ) { return( HOT_DAY_LIGHTLEVEL ); } else { return( gubEnvLightValue ); } } */ void TurnOnNightLights() { for ( INT32 i = 0; i < MAX_LIGHT_SPRITES; ++i ) { if( LightSprites[ i ].uiFlags & LIGHT_SPR_ACTIVE && LightSprites[ i ].uiFlags & LIGHT_NIGHTTIME && !(LightSprites[ i ].uiFlags & (LIGHT_SPR_ON|MERC_LIGHT) ) ) { LightSpritePower( i, TRUE ); } } } void TurnOffNightLights() { for ( INT32 i = 0; i < MAX_LIGHT_SPRITES; ++i ) { if( LightSprites[ i ].uiFlags & LIGHT_SPR_ACTIVE && LightSprites[ i ].uiFlags & LIGHT_NIGHTTIME && LightSprites[ i ].uiFlags & LIGHT_SPR_ON && !(LightSprites[ i ].uiFlags & MERC_LIGHT) ) { LightSpritePower( i, FALSE ); } } } void TurnOnPrimeLights() { for ( INT32 i = 0; i < MAX_LIGHT_SPRITES; ++i ) { if( LightSprites[ i ].uiFlags & LIGHT_SPR_ACTIVE && LightSprites[ i ].uiFlags & LIGHT_PRIMETIME && !(LightSprites[ i ].uiFlags & (LIGHT_SPR_ON|MERC_LIGHT) ) ) { LightSpritePower( i, TRUE ); } } } void TurnOffPrimeLights() { for ( INT32 i = 0; i < MAX_LIGHT_SPRITES; ++i ) { if( LightSprites[ i ].uiFlags & LIGHT_SPR_ACTIVE && LightSprites[ i ].uiFlags & LIGHT_PRIMETIME && LightSprites[ i ].uiFlags & LIGHT_SPR_ON && !(LightSprites[ i ].uiFlags & MERC_LIGHT) ) { LightSpritePower( i, FALSE ); } } } void UpdateTemperature( UINT8 ubTemperatureCode ) { switch( ubTemperatureCode ) { case TEMPERATURE_DESERT_COOL: gubDesertTemperature = 0; break; case TEMPERATURE_DESERT_WARM: gubDesertTemperature = 1; break; case TEMPERATURE_DESERT_HOT: gubDesertTemperature = 2; break; case TEMPERATURE_GLOBAL_COOL: gubGlobalTemperature = 0; break; case TEMPERATURE_GLOBAL_WARM: gubGlobalTemperature = 1; break; case TEMPERATURE_GLOBAL_HOT: gubGlobalTemperature = 2; break; } gfDoLighting = TRUE; } INT8 SectorTemperature( UINT32 uiTime, INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ) { // SANDRO - updated this code if (bSectorZ > 0) { // cool underground return( 0 ); } else if ( IsSectorDesert( sSectorX, sSectorY ) && !(SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_RAIN || SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather == WEATHER_FORECAST_THUNDERSHOWERS) ) // is desert { return( gubDesertTemperature ); } else { return( gubGlobalTemperature ); } } // Flugente: fun with snakes void AddSnakeAmim( INT32 sGridno, UINT8 usDirection ) { if ( !TileIsOutOfBounds( sGridno ) ) { ANITILE_PARAMS AniParams; memset( &AniParams, 0, sizeof(ANITILE_PARAMS) ); AniParams.sGridNo = sGridno; AniParams.ubLevelID = ANI_OBJECT_LEVEL; AniParams.sDelay = 100; AniParams.sStartFrame = 0; AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_USE_DIRECTION_FOR_START_FRAME;//| ANITILE_LOOPING; AniParams.sX = CenterX( sGridno ); AniParams.sY = CenterY( sGridno ); AniParams.sZ = 0; strcpy( AniParams.zCachedFile, "TILECACHE\\WATERSNAKE_MOVE.sti" ); AniParams.uiUserData3 = gOneCCDirection[usDirection]; CreateAnimationTile( &AniParams ); } } // Flugente: the environment can interact with soldiers void HandleEnvironmentHazard( ) { UINT8 sector = (UINT8)SECTOR( gWorldSectorX, gWorldSectorY ); UINT8 ubTraverseType = SectorInfo[sector].ubTraversability[THROUGH_STRATEGIC_MOVE]; if ( gGameExternalOptions.gfAllowSnakes && !gbWorldSectorZ && Chance( SectorExternalData[sector][0].snakechance ) ) { // the number of snake attacks is randomized int actionstodo = 1 + Random( SectorExternalData[sector][0].numsnakes ); BOOLEAN soundplayed = FALSE; SOLDIERTYPE *pSoldier = NULL; UINT32 uiCnt = 0; for ( uiCnt = 0, pSoldier = MercPtrs[uiCnt]; uiCnt < TOTAL_SOLDIERS; ++uiCnt, ++pSoldier ) { if ( pSoldier->bActive && !pSoldier->bSectorZ && pSoldier->sSectorX == gWorldSectorX && pSoldier->sSectorY == gWorldSectorY && pSoldier->stats.bLife > 0 ) { if ( TERRAIN_IS_WATER( pSoldier->bOverTerrainType ) && pSoldier->pathing.bLevel <= 0 ) { // there is a chance to be attacked, it is increased if we have open wounds UINT16 chancetobeattacked = 20 + pSoldier->bBleeding + 20 * TERRAIN_IS_DEEP_WATER( pSoldier->bOverTerrainType ); if ( Chance( chancetobeattacked ) ) { // there is a chance we defeat the snake and do not take damage, but get a bit of exp if ( Chance( pSoldier->GetWaterSnakeDefenseChance( ) ) ) { StatChange( pSoldier, EXPERAMT, 10, FROM_SUCCESS ); ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szSnakeText[0], pSoldier->GetName( ) ); } // otherwise, the snake damages us else { INT16 damage = 10 + Random( 11 ); pSoldier->EVENT_SoldierGotHit(47, damage, damage * 100, pSoldier->ubDirection, 0, NOBODY, FIRE_WEAPON_NO_SPECIAL, AIM_SHOT_LEGS, 0, pSoldier->sGridNo); // if this is a swamp, handle possible extra infection if ( ubTraverseType == SWAMP || ubTraverseType == SWAMP_ROAD ) { HandlePossibleInfection( pSoldier, NULL, INFECTION_TYPE_SWAMP ); } ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szSnakeText[1], pSoldier->GetName( ) ); } // in any case, play animation and lose APs DeductPoints( pSoldier, 20, 200, DISABLED_INTERRUPT ); AddSnakeAmim( pSoldier->sGridNo, Random( NUM_WORLD_DIRECTIONS ) ); if ( !soundplayed ) { PlayJA2SampleFromFile( Chance( 50 ) ? "Sounds\\WATERSNAKE_ATTACK_01.WAV" : "Sounds\\WATERSNAKE_ATTACK_02.WAV", RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) ); soundplayed = TRUE; } --actionstodo; } if ( actionstodo <= 0 ) break; } } } // if there are actions left, just have the snakes travel ominously in random locations int cnt = 0; while ( actionstodo > 0 && cnt < 1000 ) { INT32 sGridNo = RandomGridNo( ); if ( TERRAIN_IS_WATER( GetTerrainType( sGridNo ) ) ) { AddSnakeAmim( sGridNo, Random( NUM_WORLD_DIRECTIONS ) ); --actionstodo; } ++cnt; } if ( !actionstodo && !soundplayed ) { PlayJA2SampleFromFile( Chance( 50 ) ? "Sounds\\WATERSNAKE_MOVE_01.WAV" : "Sounds\\WATERSNAKE_MOVE_02.WAV", RATE_11025, LOWVOLUME, 1, MIDDLEPAN ); } } }