For some reason it was working... except the rain itself wasn't appearing. I could hear the rain and thunder, but the rain itself wasn't visible.
That was very peculiar.
I might include this in a project, however I wanted to report the fact it seems the rain, at least for me, isn't appearing. I didn't change anything related to the graphics so I dunno what's happening there.
Re: [Zsript] Very simple weather effects
Posted: Thu Oct 22, 2020 7:10 am
by Hidden Hands
Awesome. Thank you so much. LZDoom seems to be saving my life a lot lately.
Re: [Zsript] Very simple weather effects
Posted: Thu Oct 22, 2020 10:49 am
by Apeirogon
SanyaWaffles wrote:For some reason it was working... except the rain itself wasn't appearing. I could hear the rain and thunder, but the rain itself wasn't visible.
That was very peculiar.
I might include this in a project, however I wanted to report the fact it seems the rain, at least for me, isn't appearing. I didn't change anything related to the graphics so I dunno what's happening there.
I added CVAR to disable droplets completele (if it slowdown really low end PC/bugging peoples) and after testing forgot to turn it off. Redownload archive from Github, or open cvarinfo file and change
//base event
class weather_event : eventhandler
{
//maps weather info container
private array<parsed_weather> maps_weather;
// Checks if the new parser is already created
private bool weather_activated;
private bool world_loaded;
//read weatdef lump and construct from it actual weather definition
override void onregister()
{
if(!weather_activated)
{
weather_activated = true;
array<string> data; data.clear();
//parser
c_like_parser p = c_like_parser(new("c_like_parser") );
c_like_parser(p).read_lump("WEATHDEF", data);
weather_definition wd = weather_definition(new("weather_definition") );
weather_definition(wd).create_weather(data, maps_weather);
}
}
//entry point for initializing weather
override void worldLoaded(WorldEvent e)
{
if(!world_loaded)
{
world_loaded = true;
//check for info from WEATHER lump
for(uint i = 0; i < maps_weather.size(); i++)
{
if(maps_weather[i])
{
let weather_thing = parsed_weather(maps_weather[i]);
if(weather_thing.level_number == level.levelnum)
{
set_weather();
prepeare_droplets();
//cvar as globally accessible storage for weather type
cvar.findcvar("CWE_weather_type").setint(weather_thing.weather_type);
cvar.findcvar("CWE_intensity").setint(weather_thing.intensity);
if(weather_thing.weather_type == CWE_RAIN)
cvar.findcvar("CWE_rain_sound").setint(1);
else
cvar.findcvar("CWE_rain_sound").setint(0);
}
}
}
}
}
//store here all effects...
private array<weather_actor> precipitation;
//...and all thinkers which "spawn" them
private array<sector_cyclon> hurricanes;
//check sector ceiling texture and return true if it SKY1
static bool is_sky_sector(sector s)
{
return s.getTexture(Sector.ceiling) == TexMan.checkForTexture("F_SKY1", TexMan.Type_Any);
}
//compute bounding box of a sector (minimal described rectangular)
private vector2, vector2 get_sector_bounding_box(sector s)
{
vector2 x_axis, y_axis;
if(s)
{
double x_smaller, x_greater, y_smaller, y_greater;
x_smaller = y_smaller = double.infinity;
x_greater = y_greater = -double.infinity;
for (int i = 0; i < s.lines.size(); i++)
{
let lyn = s.lines[i];
if (lyn.v1.p.x < x_smaller ) x_smaller = lyn.v1.p.x;
if (lyn.v2.p.x < x_smaller ) x_smaller = lyn.v2.p.x;
if (lyn.v1.p.x > x_greater ) x_greater = lyn.v1.p.x;
if (lyn.v2.p.x > x_greater ) x_greater = lyn.v2.p.x;
if (lyn.v1.p.y < y_smaller ) y_smaller = lyn.v1.p.y;
if (lyn.v2.p.y < y_smaller ) y_smaller = lyn.v2.p.y;
if (lyn.v1.p.y > y_greater ) y_greater = lyn.v1.p.y;
if (lyn.v2.p.y > y_greater ) y_greater = lyn.v2.p.y;
}
x_axis = (x_smaller, x_greater);
y_axis = (y_smaller, y_greater);
return x_axis, y_axis;
}
else
object.throwabortexception("No sector in get_sector_bounding_box");
return x_axis, y_axis;
}
//should be called from world loaded
//init weather base code
private void set_weather()
{
//storage for al sky sectors
array<sector> processed_sectors; processed_sectors.clear();
//for every sectors on map...
for (int i = 0; i < level.sectors.size(); i++)
{
sector sec = level.sectors[i];
//...check is it have sky ceiling and if so store it to array
if (is_sky_sector(sec) && processed_sectors.find(sec) == processed_sectors.size() )
processed_sectors.push(sec);
}
//now if we find some sky sectors, try to group separate, but touching sectors in one group
//for (int i = 0; i < processed_sectors.size(); i++)
while(processed_sectors.size() >= 1)
{
//and push it in new array
array<sector> new_group; new_group.clear();
new_group.push(processed_sectors[0]);
//delete current sector from "main" array
processed_sectors.delete(0);
//now, time to collect neighbor sectors with sky texture
for(uint j = 0; j < new_group.size(); j++)
{
if(new_group[j] )
{
sector cur_sec = new_group[j];
//check all lines of a sector
for(uint k = 0; k < cur_sec.lines.size(); k++)
{
sector check_f;
//pick "other" sector...
if(!cur_sec.lines[k].backsector)
//one sided line, skip it
continue;
else
{
if(cur_sec.lines[k].frontsector == cur_sec)
check_f = cur_sec.lines[k].backsector;
else check_f = cur_sec.lines[k].frontsector;
}
//...check for it in array of sky sectors...
uint sec_id = processed_sectors.find(check_f);
if(sec_id != processed_sectors.size() )
{
//...and if it is there delete it from there
//and push it to new group instead
new_group.push(processed_sectors[sec_id]);
processed_sectors.delete(sec_id);
}
}
}
}
//for storing current and function returned bbox
Vector2 bbox_x, bbox_y, temp_bbox_x, temp_bbox_y;
bbox_x = bbox_y = (double.infinity, -double.infinity);
//calculate bbox here
for(uint j = 0; j < new_group.size(); j ++)
{
[temp_bbox_x, temp_bbox_y] = get_sector_bounding_box(new_group[j]);
if (temp_bbox_x.x < bbox_x.x ) bbox_x.x = temp_bbox_x.x;
if (temp_bbox_x.y > bbox_x.y ) bbox_x.y = temp_bbox_x.y;
if (temp_bbox_y.x < bbox_y.x ) bbox_y.x = temp_bbox_y.x;
if (temp_bbox_y.y > bbox_y.y ) bbox_y.y = temp_bbox_y.y;
}
//and now create new cyclon handler for a current group of sectors
sector_cyclon sc = sector_cyclon.new_cyclone(new_group, bbox_x, bbox_y, self);
hurricanes.push(sc);
new_group.clear();
}
}
//find free weather actor for thinker
weather_actor find_droplet() const
{
for(uint i = 0; i <= precipitation.size(); i++)
{
//no free actors, create new one
if(i == precipitation.size() )
{
weather_actor a = weather_actor(actor.spawn("weather_actor", pos: (double.infinity, double.infinity, double.infinity) ) );
precipitation.push(a);
return a;
}
if(precipitation[i] && weather_actor(precipitation[i]).is_on_hold() )
{
return weather_actor(precipitation[i] );
break;
}
}
//should never enter here
throwabortexception("find_droplet: outside of function body");
return null;
}
//precache some of the effects at the beginning of a map
private void prepeare_droplets()
{
for(uint i = 0; i < 100; i ++)
{
actor a = actor.spawn("weather_actor", pos:(double.infinity, double.infinity, double.infinity) );
a.setstatelabel("cooldown");
precipitation.push(weather_actor(a));
}
}
//find thinker by attached sector, if it exist
private sector_cyclon find_cyclon_by_sector(sector s)
{
if(s)
{
for(uint i = 0; i < hurricanes.size(); i++)
{
if(hurricanes[i])
{
array<sector> temp; temp.clear();
sector_cyclon(hurricanes[i]).get_sectors(temp);
for(uint j = 0; j < temp.size(); j++)
{
if(temp[j] && temp[j] == s)
{
return sector_cyclon(hurricanes[i]);
break;
}
}
}
}
}
return null;
}
//for preventing multiple thunder sounds in one tick
private int lightning_cooldown;
//delay between lighting flash and thunder sound
private int light_to_sound;
//set cooldown between two separate lightings
bool turn_on_lighting()
{
if(lightning_cooldown <= 0)
{
lightning_cooldown = random[weather] (850, 4000);
light_to_sound = random[weather] (35, 350);
return true;
}
else return false;
}
//try light lighting
private bool try_lightning()
{
if(CWE_weather_type == CWE_RAIN)
{
if(random[weather](0, 500) <= 1)
return turn_on_lighting();
}
return false;
}
private void activate_lightning_flash()
{
for(uint i = 0; i < hurricanes.size(); i++)
{
hurricanes[i].lightning_flash(self);
}
}
private void activate_thunder_sound()
{
for(uint i = 0; i < hurricanes.size(); i++)
{
hurricanes[i].thunder_sound(self, random[weather](LST_1, LST_8) );
}
}
private void lightning_handling()
{
if(CWE_weather_type == CWE_RAIN)
{
if(try_lightning() )
activate_lightning_flash();
if(lightning_cooldown > 0)
lightning_cooldown--;
if(light_to_sound > 0)
{
light_to_sound--;
if(light_to_sound <= 0 || cwe_thunder_debug)
activate_thunder_sound();
}
}
}
override void worldtick()
{
lightning_handling();
}
}
As you can see, I've added two booleans "weather_activated" and "world_loaded" to main_event.zs that make it fire off only once per map.
Maybe not elegant, but works, and I can re-enter the same map 100 times now without crashing.
Another setting/CVAR to disable Fade change would be great. I had to find it and comment it out, but again only for those who are too lazy/dont know how to find FADE change .
I've also replaced droplets with vertical lines to my liking, and now it fully satisfies my needs.
Thank you very much!
Re: [Zsript] Very simple weather effects
Posted: Sun Oct 31, 2021 4:00 am
by Apeirogon
Didnt test it in hubs.
I changed the way it works, now it should work properly.
About fade, I think it have more sense to just add new option to weather definition. Wait here til I made it.
Re: [Zsript] Very simple weather effects
Posted: Sun Oct 31, 2021 2:46 pm
by Deon
I have observed a strange behavior of the rain.
At one point of the map it's just "cut off", like it only pours in specific square.
It cuts off in a middle of a sector, and moving map around doesn't move where it's cut off. Can you give me a hint at what could be causing it?
P.S. It actually looks more like a specific rectangle doesn't get rain. It has sky texture, so I dont understand why.
Here's a demonstration:
Re: [Zsript] Very simple weather effects
Posted: Fri Nov 05, 2021 3:41 am
by Apeirogon
I know it sounds crazy, but I cant run video in Gzdoom to check whats wrong with this particular map spot. That one of the tabooed programmer powers that could easily destroy 1/3 part of a world.
So, can you post/send me in pm this map so I could run it, instead of breaking fragile balance of a universe.
did some testing but it seems that this will not work if you use wadsmooshes doom_complete as an iwad and open a level through the episode selection menu. it does however work when manually entering a map through the console.