/* * weathermanager.cpp * * Copyright (C) 2002 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include "weathermanager.h" #include "psserver.h" #include "cachemanager.h" #include "globals.h" #include "netmanager.h" #include "client.h" #include "gem.h" #include "util/eventmanager.h" //#define WEATHER_DEBUG WeatherManager::WeatherManager() { current_daynight = 0; randomgen = psserver->rng; } WeatherManager::~WeatherManager() { } void WeatherManager::Initialize() { // Start rain/snow for all sectors csHash::GlobalIterator iter = CacheManager::GetSingleton().GetSectorIterator(); while (iter.HasNext() ) { psSectorInfo *si = iter.Next(); StartWeather(si); } // Start the time of day clock QueueNextEvent(GAME_HOUR,psWeatherMessage::DAYNIGHT,12,0,0,"",NULL); } void WeatherManager::StartWeather(psSectorInfo *si) { // Is rain/snow enabled for this sector if (si->rain_enabled) { // Queue event to start rain/snow QueueNextEvent(si->GetRandomRainGap(), psWeatherMessage::RAIN, si->GetRandomRainDrops(), 0, // Duration is calculated when stop event is created 0, // Fade is calculated when sending weather event si->name, si); } } void WeatherManager::UpdateClient(uint32_t cnum) { psWeatherMessage::NetWeatherInfo info; // Update client with weather for all sectors csHash::GlobalIterator iter = CacheManager::GetSingleton().GetSectorIterator(); while (iter.HasNext() ) { psSectorInfo *sector = iter.Next(); info.has_downfall = false; info.has_lightning = false; info.has_fog = false; info.sector = sector->name; if(sector->is_raining) { info.has_downfall = true; info.downfall_is_snow = false; info.downfall_drops = sector->current_rain_drops; info.downfall_fade = 0; // Don't fade in } else if (sector->is_snowing ) //If it is snowing { info.has_downfall = true; info.downfall_is_snow = true; info.downfall_drops = sector->current_rain_drops; info.downfall_fade = 0; // Don't fade in } if( sector->fog_density > 0 || sector->current_rain_fog_density > 0 ) { info.has_fog = true; info.fog_density = MAX( sector->fog_density, sector->current_rain_fog_density ); info.fog_fade = 0; // Don't fade in info.r = sector->r;info.g = sector->g;info.b = sector->b; } if (info.has_downfall || info.has_fog) { psWeatherMessage weather(cnum,info); weather.SendMessage(); } } // Update time psWeatherMessage time(cnum,current_daynight); time.SendMessage(); // Send #ifdef WEATHER_DEBUG printf("Sending weather and time updates to client %u\n",cnum); #endif } void WeatherManager::QueueNextEvent(int delayticks, int eventtype, int eventvalue, int duration, int fade, const char *sector, psSectorInfo *si, uint clientnum, int r,int g,int b) { psWeatherGameEvent *event; // There are no notify for 7 pars so we create a string first. csString note; note.Format("QueueNextEvent Delay: %d Type: %d Value: %d " "Duration: %d Fade: %d Sector: %s ...", delayticks,eventtype,eventvalue,duration, fade,sector); Notify1(LOG_WEATHER,note.GetDataSafe()); event = new psWeatherGameEvent(this, delayticks, eventtype, eventvalue, duration, fade, sector, si, clientnum, r,g,b); events.Push(event); psserver->GetEventManager()->Push(event); } void WeatherManager::SendClientCurrentTime(int cnum) { psWeatherMessage time(cnum,current_daynight); time.SendMessage(); } void WeatherManager::HandleWeatherEvent(psWeatherGameEvent *event) { events.Delete(event); // Delete this from our "db" // See if we want to ignore this event for(size_t i = 0; i < ignored.Length();i++) { if( event == ignored[i] ) { ignored.DeleteIndex(i); return; } } switch (event->type) { case psWeatherMessage::SNOW: case psWeatherMessage::RAIN: { event->si->current_rain_drops = event->value; Notify3(LOG_WEATHER,"New rain in sector '%s': %d",event->si->name.GetData(),event->value); psWeatherMessage::NetWeatherInfo info; info.has_downfall = true; info.downfall_is_snow = (event->type == psWeatherMessage::SNOW); info.has_fog = true; info.has_lightning = false; info.downfall_drops = event->value; // We set the fog depending from the drops! // Any rain will override the current fog settings if (info.downfall_drops < 2001) { event->si->current_rain_fog_density = 0; //Too few drops for having any fog! } else if (info.downfall_drops > 1000 && info.downfall_drops < 8000) { // Calculate fog to be linear in range 0 to 200 event->si->current_rain_fog_density = (int)(200.0f*(info.downfall_drops-1000.0f)/8000.0f); } else { event->si->current_rain_fog_density = 200; } info.fog_density = MAX( event->si->fog_density, event->si->current_rain_fog_density ); info.r = info.g = info.b = 255; info.sector = event->sector; if (event->fade) { info.downfall_fade = event->fade; info.fog_fade = event->fade; } else { if (event->value) { info.downfall_fade = event->si->GetRandomRainFadeIn(); info.fog_fade = info.downfall_fade; } else { info.downfall_fade = event->si->GetRandomRainFadeOut(); info.fog_fade = info.downfall_fade; } } Notify4(LOG_WEATHER,"Drops: %d Density: %d Sector: %s\n", info.downfall_drops,info.fog_density,info.sector.GetDataSafe()); psWeatherMessage rain(0,info); if (rain.valid) psserver->GetEventManager()->Broadcast(rain.msg,NetBase::BC_EVERYONE); else { Bug1("Could not create valid psWeatherMessage (rain) for broadcast.\n"); } // Make sure we don't have any other events in this sector that will disturb // Simple case is when event types are equal. In addition we have to test // for the mutal exclusive case where we are changing from snow to rain or // rain to snow. for(size_t i = 0; i < events.Length();i++) { psWeatherGameEvent* evt = events[i]; if(evt->sector == event->sector && (evt->type == event->type || ((evt->type==psWeatherMessage::RAIN|| evt->type==psWeatherMessage::SNOW)&& (event->type==psWeatherMessage::RAIN|| event->type==psWeatherMessage::SNOW)))) { ignored.Push(evt); // Ignore when the eventmanager handles the event events.DeleteIndex(i); i--; Notify4(LOG_WEATHER,"Removed disturbing event for sector '%s' (%d,%d)", evt->sector.GetData(),evt->value,evt->duration); } } if (event->value) // if this has drops, then queue event to turn off rain { if(event->type != psWeatherMessage::SNOW) event->si->is_raining = true; else event->si->is_snowing = true; if (event->si->lightning_min_gap && event->value > 2000 && event->si->is_raining && event->si->rain_enabled) { // Queue lightning during rain storm here first QueueNextEvent(event->si->GetRandomLightningGap(), psWeatherMessage::LIGHTNING, 0, 0, 0, event->si->name, event->si, event->clientnum); } // Queue event to stop rain/snow int duration; if (event->duration) { duration = event->duration; } else { duration = event->si->GetRandomRainDuration(); } QueueNextEvent(duration, event->type, 0, 0, event->fade, event->si->name, event->si); } // if this is event to turn off rain/rain else { if(event->type== psWeatherMessage::SNOW) event->si->is_snowing = false; else event->si->is_raining = false; // Queue event to turn on again later if enabled StartWeather(event->si); } break; } case psWeatherMessage::FOG: { // Update sector weather info event->si->fog_density = event->value; event->si->r = event->cr; event->si->g = event->cg; event->si->b = event->cb; // Update the clients psWeatherMessage::NetWeatherInfo info; info.has_downfall = false; info.downfall_is_snow = false; info.has_fog = true; info.has_lightning = false; info.sector = event->si->name; info.fog_density = MAX( event->si->fog_density, event->si->current_rain_fog_density ); info.fog_fade = event->fade; info.r = event->cr; info.g = event->cg; info.b = event->cb; info.downfall_drops = 0; info.downfall_fade = 0; psWeatherMessage fog(0,info); if (fog.valid) psserver->GetEventManager()->Broadcast(fog.msg,NetBase::BC_EVERYONE); else { Bug1("Could not create valid psWeatherMessage (fog) for broadcast.\n"); } break; } case psWeatherMessage::LIGHTNING: { if (event->si->is_raining) { Notify2(LOG_WEATHER,"Lightning in sector '%s'",event->sector.GetData()); psWeatherMessage::NetWeatherInfo info; info.has_downfall = false; info.has_fog = false; info.has_lightning = true; info.sector = event->sector; info.fog_fade = info.downfall_fade = 0; info.r = info.g = info.b = 0; info.downfall_drops = info.fog_density = 0; psWeatherMessage lightning(0,info, event->clientnum); if (lightning.valid) { psserver->GetEventManager()->Broadcast(lightning.msg); } else { Bug1("Could not create valid psWeatherMessage (lightning) for broadcast.\n"); } if (event->si->rain_enabled && event->si->lightning_max_gap != 0) { QueueNextEvent(event->si->GetRandomLightningGap(), psWeatherMessage::LIGHTNING, 0, 0, 0, event->si->name, event->si, event->clientnum); } } break; } case psWeatherMessage::DAYNIGHT: { psWeatherMessage time(0,event->value); if (time.valid) { psserver->GetEventManager()->Broadcast(time.msg); } else { Bug1("Could not create valid psWeatherMessage (daynight) for broadcast.\n"); } current_daynight = event->value; QueueNextEvent(GAME_HOUR, psWeatherMessage::DAYNIGHT, ++(event->value) % 24, // hourly cycle 0, 0, NULL, NULL); break; } default: { break; } } } /*---------------------------------------------------------------------*/ psWeatherGameEvent::psWeatherGameEvent(WeatherManager *mgr, int delayticks, int eventtype, int eventvalue, int length, int fadevalue, const char *where, psSectorInfo *sinfo, uint client, int r,int g,int b) : psGameEvent(0,delayticks,"psWeatherGameEvent") { weathermanager = mgr; type = eventtype; value = eventvalue; sector = where; si = sinfo; duration = length; fade = fadevalue; clientnum = client; cr = r; cg = g; cb = b; } void psWeatherGameEvent::Trigger() { weathermanager->HandleWeatherEvent(this); } const char *psWeatherGameEvent::GetType() { switch (type) { case psWeatherMessage::SNOW: return "Snow"; case psWeatherMessage::LIGHTNING: return "Lightning"; case psWeatherMessage::FOG: return "Fog"; case psWeatherMessage::RAIN: return "Rain"; case psWeatherMessage::DAYNIGHT: return "Day/Night"; default: return "Unknown"; } }