diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 0a5879458..5f02075ad 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -513,3 +513,21 @@ std::string EQ::constants::GetObjectTypeName(int object_type) return std::string(); } + +const std::map& EQ::constants::GetWeatherTypeMap() +{ + static const std::map weather_type_map ={ + { WeatherTypes::None, "None" }, + { WeatherTypes::Raining, "Raining" }, + { WeatherTypes::Snowing, "Snowing" } + }; +} + +std::string EQ::constants::GetWeatherTypeName(uint8 weather_type) +{ + if (EQ::ValueWithin(weather_type, WeatherTypes::None, WeatherTypes::Snowing)) { + return EQ::constants::GetWeatherTypeMap().find(weather_type)->second; + } + + return std::string(); +} \ No newline at end of file diff --git a/common/emu_constants.h b/common/emu_constants.h index d723fbed8..96806cc7d 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -312,6 +312,12 @@ namespace EQ NoDeposit }; + enum WeatherTypes : uint8 { + None, + Raining, + Snowing + }; + const char *GetStanceName(StanceType stance_type); int ConvertStanceTypeToIndex(StanceType stance_type); @@ -345,6 +351,9 @@ namespace EQ extern const std::map& GetObjectTypeMap(); std::string GetObjectTypeName(int object_type); + extern const std::map& GetWeatherTypeMap(); + std::string GetWeatherTypeName(uint8 weather_type); + const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index b49cddd13..38dd99a1d 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1750,11 +1750,14 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) outapp = new EQApplicationPacket(OP_Weather, 12); Weather_Struct *ws = (Weather_Struct *)outapp->pBuffer; ws->val1 = 0x000000FF; - if (zone->zone_weather == 1) { ws->type = 0x31; } // Rain - if (zone->zone_weather == 2) { + + if (zone->zone_weather == EQ::constants::WeatherTypes::Raining) { + ws->type = 0x31; + } else if (zone->zone_weather == EQ::constants::WeatherTypes::Snowing) { outapp->pBuffer[8] = 0x01; - ws->type = 0x02; + ws->type = EQ::constants::WeatherTypes::Snowing; } + outapp->priority = 6; QueuePacket(outapp); safe_delete(outapp); diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 0b31ca871..2e80e0c97 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3706,6 +3706,24 @@ bool Perl__hasrecipelearned(uint32 recipe_id) return quest_manager.HasRecipeLearned(recipe_id); } +bool Perl__IsRaining() +{ + if (!zone) { + return false; + } + + return zone->IsRaining(); +} + +bool Perl__IsSnowing() +{ + if (!zone) { + return false; + } + + return zone->IsSnowing(); +} + void perl_register_quest() { perl::interpreter perl(PERL_GET_THX); @@ -3752,6 +3770,8 @@ void perl_register_quest() package.add("delete_data", &Perl__delete_data); package.add("IsBeneficialSpell", &Perl__IsBeneficialSpell); package.add("IsEffectInSpell", &Perl__IsEffectInSpell); + package.add("IsRaining", &Perl__IsRaining); + package.add("IsSnowing", &Perl__IsSnowing); package.add("IsRunning", &Perl__IsRunning); package.add("LearnRecipe", &Perl__LearnRecipe); package.add("MerchantCountItem", &Perl__MerchantCountItem); diff --git a/zone/gm_commands/weather.cpp b/zone/gm_commands/weather.cpp index 2590c51e3..7c06255a7 100755 --- a/zone/gm_commands/weather.cpp +++ b/zone/gm_commands/weather.cpp @@ -2,67 +2,104 @@ void command_weather(Client *c, const Seperator *sep) { - if (!(sep->arg[1][0] == '0' || sep->arg[1][0] == '1' || sep->arg[1][0] == '2' || sep->arg[1][0] == '3')) { - c->Message(Chat::White, "Usage: #weather <0/1/2/3> - Off/Rain/Snow/Manual."); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #weather [0|1|2] - [Off|Rain|Snow]"); + c->Message(Chat::White, "Usage: #weather 3 [Type] [Intensity] - Manually set weather type and intensity"); + return; } - else if (zone->zone_weather == 0) { - if (sep->arg[1][0] == - '3') { // Put in modifications here because it had a very good chance at screwing up the client's weather system if rain was sent during snow -T7 - if (sep->arg[2][0] != 0 && sep->arg[3][0] != 0) { - c->Message(Chat::White, "Sending weather packet... TYPE=%s, INTENSITY=%s", sep->arg[2], sep->arg[3]); - zone->zone_weather = atoi(sep->arg[2]); - auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[0] = atoi(sep->arg[2]); - outapp->pBuffer[4] = atoi(sep->arg[3]); // This number changes in the packets, intensity? - entity_list.QueueClients(c, outapp); - safe_delete(outapp); + + int arguments = sep->argnum; + if (arguments == 1) { + auto new_weather = static_cast(std::stoul(sep->arg[1])); + uint8 new_intensity = 0; + std::string weather_message = "The sky clears."; + + if (new_weather == EQ::constants::WeatherTypes::Snowing) { + weather_message = "Snowflakes begin to fall from the sky."; + new_weather = EQ::constants::WeatherTypes::Snowing; + new_intensity = 0x02; + } else if (new_weather == EQ::constants::WeatherTypes::Raining) { + weather_message = "Raindrops begin to fall from the sky."; + new_weather = EQ::constants::WeatherTypes::Raining; + new_intensity = 0x01; // This is how it's done in Fear, and you can see a decent distance with it at this value + } else { + c->Message(Chat::White, "Usage: #weather [0|1|2] - [Off|Rain|Snow]"); + c->Message(Chat::White, "Usage: #weather 3 [Type] [Intensity] - Manually set weather type and intensity"); + return; + } + + zone->zone_weather = new_weather; + auto outapp = new EQApplicationPacket(OP_Weather, 8); + + if (new_weather != EQ::constants::WeatherTypes::None) { + if (new_weather == EQ::constants::WeatherTypes::Snowing) { + outapp->pBuffer[0] = EQ::constants::WeatherTypes::Snowing; } - else { - c->Message(Chat::White, "Manual Usage: #weather 3 "); + + outapp->pBuffer[4] = new_intensity; + } + + + c->Message(Chat::White, weather_message.c_str()); + entity_list.QueueClients(c, outapp); + + safe_delete(outapp); + } else if (arguments == 3) { + auto command_type = static_cast(std::stoul(sep->arg[1])); + uint8 new_weather = EQ::constants::WeatherTypes::None; + uint8 new_intensity = 0; + std::string weather_message; + + if (zone->zone_weather == EQ::constants::WeatherTypes::None) { + if (command_type > EQ::constants::WeatherTypes::Snowing) { + new_weather = static_cast(std::stoul(sep->arg[2])); + new_intensity = static_cast(std::stoul(sep->arg[3])); + + weather_message = fmt::format( + "Sending {} ({}) with an intensity of {}.", + EQ::constants::GetWeatherTypeName(new_weather), + new_weather, + new_intensity + ); + } else if (command_type == EQ::constants::WeatherTypes::Snowing) { + weather_message = "Snowflakes begin to fall from the sky."; + new_weather = EQ::constants::WeatherTypes::Snowing; + new_intensity = 0x02; + } else if (command_type == EQ::constants::WeatherTypes::Raining) { + weather_message = "Raindrops begin to fall from the sky."; + new_weather = EQ::constants::WeatherTypes::Raining; + new_intensity = 0x01; // This is how it's done in Fear, and you can see a decent distance with it at this value } - } - else if (sep->arg[1][0] == '2') { - entity_list.Message(0, 0, "Snowflakes begin to fall from the sky."); - zone->zone_weather = 2; + + zone->zone_weather = new_weather; auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[0] = 0x01; - outapp->pBuffer[4] = 0x02; // This number changes in the packets, intensity? + + if (new_weather != EQ::constants::WeatherTypes::Raining) { + outapp->pBuffer[0] = new_weather; + } + + outapp->pBuffer[4] = new_intensity; + + c->Message(Chat::White, weather_message.c_str()); entity_list.QueueClients(c, outapp); + safe_delete(outapp); - } - else if (sep->arg[1][0] == '1') { - entity_list.Message(0, 0, "Raindrops begin to fall from the sky."); - zone->zone_weather = 1; + } else { auto outapp = new EQApplicationPacket(OP_Weather, 8); - outapp->pBuffer[4] = 0x01; // This is how it's done in Fear, and you can see a decent distance with it at this value - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - } - else { - if (zone->zone_weather == 1) { // Doing this because if you have rain/snow on, you can only turn one off. - entity_list.Message(0, 0, "The sky clears as the rain ceases to fall."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else if (zone->zone_weather == 2) { - entity_list.Message(0, 0, "The sky clears as the snow stops falling."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) - outapp->pBuffer[0] = 0x01; // Snow has it's own shutoff packet - entity_list.QueueClients(c, outapp); - safe_delete(outapp); - } - else { - entity_list.Message(0, 0, "The sky clears."); - zone->zone_weather = 0; - auto outapp = new EQApplicationPacket(OP_Weather, 8); - // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) + weather_message = "The sky clears."; + + if (zone->zone_weather == EQ::constants::WeatherTypes::Snowing) { + weather_message = "The sky clears as the snow stops falling."; + outapp->pBuffer[0] = 0x01; // Snow has it's own shutoff packet + } else if (zone->zone_weather == EQ::constants::WeatherTypes::Raining) { + weather_message = "The sky clears as the rain ceases to fall."; + } + + zone->zone_weather = EQ::constants::WeatherTypes::None; + + c->Message(Chat::White, weather_message.c_str()); entity_list.QueueClients(c, outapp); + safe_delete(outapp); } } diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index dd91b183e..09a24e85e 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -1203,8 +1203,9 @@ luabind::adl::object lua_get_characters_in_instance(lua_State *L, uint16 instanc } int lua_get_zone_weather() { - if(!zone) - return 0; + if (!zone) { + return EQ::constants::WeatherTypes::None; + } return zone->zone_weather; } @@ -3427,6 +3428,22 @@ bool lua_has_recipe_learned(uint32 recipe_id) { return quest_manager.HasRecipeLearned(recipe_id); } +bool lua_is_raining() { + if (!zone) { + return false; + } + + return zone->IsRaining(); +} + +bool lua_is_snowing() { + if (!zone) { + return false; + } + + return zone->IsSnowing(); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3891,6 +3908,8 @@ luabind::scope lua_register_general() { luabind::def("get_recipe_made_count", &lua_get_recipe_made_count), luabind::def("get_recipe_name", &lua_get_recipe_name), luabind::def("has_recipe_learned", &lua_has_recipe_learned), + luabind::def("is_raining", &lua_is_raining), + luabind::def("is_snowing", &lua_is_snowing), /* Cross Zone diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index d97520fed..bbed45323 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2110,7 +2110,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Stop Rain"); #endif - zone->zone_weather = 0; + zone->zone_weather = EQ::constants::WeatherTypes::None; zone->weather_intensity = 0; zone->weatherSend(); break; diff --git a/zone/zone.cpp b/zone/zone.cpp index a861d2344..2e1feb58b 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1027,7 +1027,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) Weather_Timer = new Timer(60000); Weather_Timer->Start(); LogDebug("The next weather check for zone: [{}] will be in [{}] seconds", short_name, Weather_Timer->GetRemainingTime()/1000); - zone_weather = 0; + zone_weather = EQ::constants::WeatherTypes::None; weather_intensity = 0; blocked_spells = nullptr; zone_total_blocked_spells = 0; @@ -1705,120 +1705,117 @@ void Zone::ChangeWeather() } int chance = zone->random.Int(0, 3); - uint8 rainchance = zone->newzone_data.rain_chance[chance]; - uint8 rainduration = zone->newzone_data.rain_duration[chance]; - uint8 snowchance = zone->newzone_data.snow_chance[chance]; - uint8 snowduration = zone->newzone_data.snow_duration[chance]; - uint32 weathertimer = 0; - uint16 tmpweather = zone->random.Int(0, 100); + auto rain_chance = zone->newzone_data.rain_chance[chance]; + auto rain_duration = zone->newzone_data.rain_duration[chance]; + auto snow_chance = zone->newzone_data.snow_chance[chance]; + auto snow_duration = zone->newzone_data.snow_duration[chance]; + uint32 weather_timer = 0; + auto temporary_weather = static_cast(zone->random.Int(0, 100)); uint8 duration = 0; - uint8 tmpOldWeather = zone->zone_weather; + auto temporary_old_weather = zone->zone_weather; bool changed = false; - if(tmpOldWeather == 0) - { - if(rainchance > 0 || snowchance > 0) - { - uint8 intensity = zone->random.Int(1, 10); - if((rainchance > snowchance) || (rainchance == snowchance)) - { - //It's gunna rain! - if(rainchance >= tmpweather) - { - if(rainduration == 0) + if (temporary_old_weather == EQ::constants::WeatherTypes::None) { + if (rain_chance || snow_chance) { + auto intensity = static_cast(zone->random.Int(1, 10)); + if (rain_chance > snow_chance || rain_chance == snow_chance) { // Rain + if (rain_chance >= temporary_weather) { + if (!rain_duration) { duration = 1; - else - duration = rainduration*3; //Duration is 1 EQ hour which is 3 earth minutes. + } else { + duration = rain_duration * 3; //Duration is 1 EQ hour which is 3 earth minutes. + } - weathertimer = (duration*60)*1000; - Weather_Timer->Start(weathertimer); - zone->zone_weather = 1; + weather_timer = (duration * 60) * 1000; + Weather_Timer->Start(weather_timer); + zone->zone_weather = EQ::constants::WeatherTypes::Raining; zone->weather_intensity = intensity; changed = true; } - } - else - { - //It's gunna snow! - if(snowchance >= tmpweather) - { - if(snowduration == 0) + } else { // Snow + if (snow_chance >= temporary_weather) { + if (!snow_duration) { duration = 1; - else - duration = snowduration*3; - weathertimer = (duration*60)*1000; - Weather_Timer->Start(weathertimer); - zone->zone_weather = 2; + } else { + duration = snow_duration * 3; //Duration is 1 EQ hour which is 3 earth minutes. + } + + weather_timer = (duration * 60) * 1000; + Weather_Timer->Start(weather_timer); + zone->zone_weather = EQ::constants::WeatherTypes::Snowing; zone->weather_intensity = intensity; changed = true; } } } - } - else - { + } else { changed = true; //We've had weather, now taking a break - if(tmpOldWeather == 1) - { - if(rainduration == 0) + if (temporary_old_weather == EQ::constants::WeatherTypes::Raining) { + if (!rain_duration) { duration = 1; - else - duration = rainduration*3; //Duration is 1 EQ hour which is 3 earth minutes. + } else { + duration = rain_duration * 3; //Duration is 1 EQ hour which is 3 earth minutes. + } - weathertimer = (duration*60)*1000; - Weather_Timer->Start(weathertimer); + weather_timer = (duration * 60) * 1000; + Weather_Timer->Start(weather_timer); zone->weather_intensity = 0; - } - else if(tmpOldWeather == 2) - { - if(snowduration == 0) + } else if (temporary_old_weather == EQ::constants::WeatherTypes::Snowing) { + if (!snow_duration) { duration = 1; - else - duration = snowduration*3; //Duration is 1 EQ hour which is 3 earth minutes. + } else { + duration = snow_duration * 3; //Duration is 1 EQ hour which is 3 earth minutes. + } - weathertimer = (duration*60)*1000; - Weather_Timer->Start(weathertimer); + weather_timer = (duration * 60) * 1000; + Weather_Timer->Start(weather_timer); zone->weather_intensity = 0; } } - if(changed == false) - { - if(weathertimer == 0) - { - uint32 weatherTimerRule = RuleI(Zone, WeatherTimer); - weathertimer = weatherTimerRule*1000; - Weather_Timer->Start(weathertimer); + if (!changed) { + if (!weather_timer) { + uint32 weather_timer_rule = RuleI(Zone, WeatherTimer); + weather_timer = weather_timer_rule * 1000; + Weather_Timer->Start(weather_timer); } LogDebug("The next weather check for zone: [{}] will be in [{}] seconds", zone->GetShortName(), Weather_Timer->GetRemainingTime()/1000); - } - else - { - LogDebug("The weather for zone: [{}] has changed. Old weather was = [{}]. New weather is = [{}] The next check will be in [{}] seconds. Rain chance: [{}], Rain duration: [{}], Snow chance [{}], Snow duration: [{}]", zone->GetShortName(), tmpOldWeather, zone_weather,Weather_Timer->GetRemainingTime()/1000,rainchance,rainduration,snowchance,snowduration); + } else { + LogDebug("The weather for zone: [{}] has changed. Old weather was = [{}]. New weather is = [{}] The next check will be in [{}] seconds. Rain chance: [{}], Rain duration: [{}], Snow chance [{}], Snow duration: [{}]", zone->GetShortName(), temporary_old_weather, zone_weather,Weather_Timer->GetRemainingTime()/1000,rain_chance,rain_duration,snow_chance,snow_duration); weatherSend(); - if (zone->weather_intensity == 0) - { - zone->zone_weather = 0; + if (!zone->weather_intensity) { + zone->zone_weather = EQ::constants::WeatherTypes::None; } } } bool Zone::HasWeather() { - uint8 rain1 = zone->newzone_data.rain_chance[0]; - uint8 rain2 = zone->newzone_data.rain_chance[1]; - uint8 rain3 = zone->newzone_data.rain_chance[2]; - uint8 rain4 = zone->newzone_data.rain_chance[3]; - uint8 snow1 = zone->newzone_data.snow_chance[0]; - uint8 snow2 = zone->newzone_data.snow_chance[1]; - uint8 snow3 = zone->newzone_data.snow_chance[2]; - uint8 snow4 = zone->newzone_data.snow_chance[3]; + auto rain_chance_one = zone->newzone_data.rain_chance[0]; + auto rain_chance_two = zone->newzone_data.rain_chance[1]; + auto rain_chance_three = zone->newzone_data.rain_chance[2]; + auto rain_chance_four = zone->newzone_data.rain_chance[3]; - if(rain1 == 0 && rain2 == 0 && rain3 == 0 && rain4 == 0 && snow1 == 0 && snow2 == 0 && snow3 == 0 && snow4 == 0) + auto snow_chance_one = zone->newzone_data.snow_chance[0]; + auto snow_chance_two = zone->newzone_data.snow_chance[1]; + auto snow_chance_three = zone->newzone_data.snow_chance[2]; + auto snow_chance_four = zone->newzone_data.snow_chance[3]; + + if ( + !rain_chance_one && + !rain_chance_two && + !rain_chance_three && + !rain_chance_four && + !snow_chance_one && + !snow_chance_two && + !snow_chance_three && + !snow_chance_four + ) { return false; - else + } else { return true; + } } void Zone::StartShutdownTimer(uint32 set_time) @@ -2200,18 +2197,20 @@ bool ZoneDatabase::GetDecayTimes(npcDecayTimes_Struct *npcCorpseDecayTimes) void Zone::weatherSend(Client *client) { auto outapp = new EQApplicationPacket(OP_Weather, 8); - if (zone_weather > 0) { + if (zone_weather > EQ::constants::WeatherTypes::None) { outapp->pBuffer[0] = zone_weather - 1; } - if (zone_weather > 0) { + + if (zone_weather > EQ::constants::WeatherTypes::None) { outapp->pBuffer[4] = zone->weather_intensity; } + if (client) { client->QueuePacket(outapp); - } - else { + } else { entity_list.QueueClients(0, outapp); } + safe_delete(outapp); } diff --git a/zone/zone.h b/zone/zone.h index 566cacf76..1ceba6b37 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -249,6 +249,9 @@ public: uint32 GetCurrencyID(uint32 item_id); uint32 GetCurrencyItemID(uint32 currency_id); + inline bool IsRaining() { return zone_weather == EQ::constants::WeatherTypes::Raining; } + inline bool IsSnowing() { return zone_weather == EQ::constants::WeatherTypes::Snowing; } + std::string GetZoneDescription(); void SendReloadMessage(std::string reload_type);