FAForever Forums
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Login

    About weather: generating clouds

    Scheduled Pinned Locked Moved Tutorials
    16 Posts 11 Posters 2.2k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • JipJ
      Jip
      last edited by Jip

      Requisites

      Map editing tools, such as:

      • Ozonex FaF Editor (https://github.com/ozonexo3/FAForeverMapEditor/releases)
        or
      • GPG Editor (https://wiki.faforever.com/index.php?title=Map_Editing_Tools)

      At the moment of writing the Ozonex editor doesn't support weather markers. You'll need the GPG editor to get this all to work.

      Weather

      Weather generators are rare, if not at all, used by maps in the vault. A lot of the original maps were supposed to use them but the code to generate the weather is all commented out. In turn, maps generally have no weather at all.

      weather-stratus.png
      Weather on the map Rainmakers Survival

      Generating weather is fairly easy but you need to know the exact steps. The GPG editor allows the creation of the proper markers. Creating and customizing the weather involves three steps: placing the proper markers in the GPG editor, editing the script file of the map and then editing those markers inside the save file through a text editor.

      GPG editor

      Open up your map and open up the marker tools (F6). From there on navigate completely to the right and at the end you'll find the markers:

      • Weather Definition
      • Weather Generator

      Place one of both of them in the center of the map. Then save the map and close the GPG editor - this prevents you from accidentally overriding the next step. You can have multiple generators, but for the sake of the article, I'll assume that you only have one.

      You can view and edit the properties of a marker inside the editor. Select a marker and extent the 'Marker Editor' window vertically. This will reveal all the properties of, for example, a Weather Definition marker.

      The weather can not be rendered and inspected inside the editor. You'll need to open up the map in Forged Alliance in order to inspect it. Constantly copying your map from the editor folder to the game folder is a bit of a pickle - therefore we use a text editor to edit the properties. This allows you to iterate more quickly when you restart your map to inspect the results.

      Script file

      Open up the _script.lua file from your map in your favorite text editor. A typical script file from a non-adaptive map looks like this:

      local ScenarioUtils = import('/lua/sim/ScenarioUtilities.lua')
      local ScenarioFramework = import('/lua/ScenarioFramework.lua')
      
      function OnPopulate()
          ScenarioUtils.InitializeArmies()
      
          -- optional, only if applicable
          ScenarioFramework.SetPlayableArea('AREA_1' , false)
      end
      
      function OnStart(scenario)
      end
      

      We want to add in the weather thread so that clouds are slowly generated over time. Change it to:

      local ScenarioUtils = import('/lua/sim/ScenarioUtilities.lua')
      local ScenarioFramework = import('/lua/ScenarioFramework.lua')
      
      -- for weather generation
      local Weather = import('/lua/weather.lua')
      
      function OnPopulate()
          ScenarioUtils.InitializeArmies()
      
          -- optional, only if applicable
          ScenarioFramework.SetPlayableArea('AREA_1' , false)
      end
      
      function OnStart(scenario)
          -- for weather generation
          Weather.CreateWeather()
      end
      

      With the current weather definition and generator clouds should already be generating. Start the game, disable fog of war and make sure that it is. Then it is time to start tweaking!

      If you have no vision over a weather generator marker then you will not see the clouds that are being generated.

      Save file

      Open up the _save.lua file from your map in your favorite text editor. Any editor will suffice, as long as it has a search function.

      Weather definition

      Search the file (CTRL + F) for 'Weather Definition'. You'll find in the markers chain a marker that looks like the following:

      ['Weather Definition 00'] = {
          ['WeatherType03Chance'] = FLOAT( 0.300000 ),
          ['color'] = STRING( 'FF808000' ),
          ['WeatherType01'] = STRING( 'SnowClouds' ),
          ['WeatherType02'] = STRING( 'WhiteThickClouds' ),
          ['WeatherType04'] = STRING( 'None' ),
          ['WeatherDriftDirection'] = VECTOR3( 1, 0, 0 ),
          ['WeatherType01Chance'] = FLOAT( 0.300000 ),
          ['WeatherType03'] = STRING( 'WhitePatchyClouds' ),
          ['WeatherType04Chance'] = FLOAT( 0.100000 ),
          ['MapStyle'] = STRING( 'Tundra' ),
          ['WeatherType02Chance'] = FLOAT( 0.300000 ),
          ['hint'] = BOOLEAN( true ),
          ['type'] = STRING( 'Weather Definition' ),
          ['prop'] = STRING( '/env/common/props/markers/M_Defensive_prop.bp' ),
          ['orientation'] = VECTOR3( 0, -0, 0 ),
          ['position'] = VECTOR3( 522.5, 68.8418, 541.5 ),
      },
      

      These values are scrambled - they can appear in any order. The important bits are:

      • WeatherType01 / 02 / 03 / 04: The type of weather that will be generated by all weather generators.
      • WeatherChance01 / 02 / 03 / 04: The chance that the given type will be generated.
      • MapStyle: Determines in what table the weather types will be searched for, such as 'Desert' or 'Evergreen'.
      • Weather Drift Direction: This value is not referenced in the code and is therefore not used. The direction that the clouds slowly move towards. You typically want this to match the direction of your water. With the standard camera the positive x-axis points to the right of a map, the positive y-axis points to the sky and the positive z-axis points the bottom of a map.

      All values are case sensitive. As an example, it is 'Dsert' and not 'desert'.

      Values that are not used by the code but by the editor:

      • prop, orientation, color, hint

      Each theme has its own list of weather that can share names but look different. Therefore choosing the right map style is important. We can find all the available weather as comments in one of the lua files of the base game:

      Map Style Types:
      	Desert
      	Evergreen
      	Geothermal
      	Lava
      	RedRock
      	Tropical
      	Tundra
      	
      Style Weather Types: 
      	Desert
      		LightStratus -
      	Evergreen
      		CumulusClouds -
      		StormClouds -
      		RainClouds - WARNING, only use these a ForceType on a weather generator, max 2 per map
      	Geothermal
      	Lava
      	RedRock
      		LightStratus -
      	Tropical
      		LightStratus -
      	Tundra
      		WhitePatchyClouds - 
      		SnowClouds - WARNING, only use these a ForceType on a weather generator, max 2 per map
      	
      	All Styles:
      		Notes: ( Cirrus style cloud emitters, should be used on a ForceType Weather Generator, placed )
      				( in the center of a map. Take note that the these are sized specific for map scale    )
      		CirrusSparse256 - 
      		CirrusMedium256 - 
      		CirrusHeavy256 - 
      		CirrusSparse512 -
      		CirrusMedium512 - 
      		CirrusHeavy512 - 
      		CirrusSparse1024 - 
      		CirrusMedium1024 - 
      		CirrusHeavy1024 - 
      		CirrusSparse4096 - 
      		CirrusMedium4096 - 
      		CirrusHeavy4096 - 
      

      This defines all the weather that is available. The RainClouds and SnowClouds generate rain and snow respectively - these should be used sparsely. An unlisted entry is 'None', which indicates that the no weather may be generated at all.

      Weather generator

      Search the file (CTRL + F) for 'Weather Generator'. You'll find in the markers chain a marker that looks like the following:

      ['Weather Generator 00'] = {
          ['cloudCountRange'] = FLOAT( 0.000000 ),
          ['color'] = STRING( 'FF808000' ),
          ['ForceType'] = STRING( 'None' ),
          ['cloudHeightRange'] = FLOAT( 15.000000 ),
          ['cloudSpread'] = FLOAT( 150.000000 ),
          ['spawnChance'] = FLOAT( 1.000000 ),
          ['cloudEmitterScaleRange'] = FLOAT( 0.000000 ),
          ['cloudEmitterScale'] = FLOAT( 1.000000 ),
          ['cloudCount'] = FLOAT( 10.000000 ),
          ['cloudHeight'] = FLOAT( 180.000000 ),
          ['hint'] = BOOLEAN( true ),
          ['type'] = STRING( 'Weather Generator' ),
          ['prop'] = STRING( '/env/common/props/markers/M_Defensive_prop.bp' ),
          ['orientation'] = VECTOR3( 0, -0, 0 ),
          ['position'] = VECTOR3( 518.5, 68.6699, 542.5 ),
      },
      

      These values are scrambled - they can appear in any order. The important bits are:

      • ForceType: Forces the weather on this generator to the given type. It can only be forced into emitters that are part of the map style chosen in the weather definition marker. Is 'None' by default: this allows the weather definition marker to determine its type. As an example, given that the MapStyle is Evergreen we can set it to CumulusClouds, StormClouds or RainClouds.
      • cloudSpread: How spread out the clouds are.
      • spawnChance: Determines whether or not it can have phases where there are no clouds generated at all.
      • cloudEmitterScale: The absolute scale of all clouds.
      • cloudEmitterScaleRange: A range value that randomizes the absolute scale.
      • cloudCount: The absolute number of clouds available at once.
      • cloudCountRange: A range value that randomizes the absolute number.
      • cloudHeight: The absolute height at which the clouds can spawn, starting from the position of the marker.
      • cloudHeightRange: A range value that randomizes the absolute height.

      As an example on the range variables: if the absolute value is 90 and the range is 15 then the final value is randomly chosen from the range [75,105].

      All values are case sensitive. As an example, it is 'None' and not 'none'.

      Values that are not used by the code but by the editor:

      • prop, orientation, color, hint

      Under the hood

      It is always good to dive into the code and discover how values are used in computations. This will bring in more perspective as to what is possible and leaves out speculation on what you feel it is doing.

      Let's start off with reading out the markers - turning the concrete data into a more abstract form that allows us to construct the weather.

      function GetWeatherMarkerData(MapScale)
        local markers = ScenarioUtils.GetMarkers()
        local WeatherDefinition = {}
        local ClusterDataList = {}
        local defaultcloudclusterSpread = math.floor(((MapScale[1] + MapScale[2]) * 0.5) * 0.15)
      
        // Make a list of all the markers in the scenario that are of the markerType
        if markers then
          for k, v in markers do
            // Read in weather cluster positions and data
            if v.type == 'Weather Generator' then
              table.insert( ClusterDataList, { 
                clusterSpread = v.cloudSpread or defaultcloudclusterSpread, 
                cloudCount = v.cloudCount or 10, 
                cloudCountRange = v.cloudCountRange or 0,
                cloudHeight = v.cloudHeight or 180,
                cloudHeightRange = v.cloudHeightRange or 10,
                position = v.position,
                emitterScale = v.cloudEmitterScale or 1,
                emitterScaleRange = v.cloudEmitterScaleRange or 0,
                forceType = v.ForceType or "None",
                spawnChance = v.spawnChance or 1,
              } )
            // Read in weather definition
                elseif v.type == 'Weather Definition' then
            if table.getn( WeatherDefinition ) > 0 then
              LOG('WARNING: Weather, multiple weather definitions found. Last read Weather definition will override any previous ones.')
            end					                
            WeatherDefinition = {
              MapStyle = v.MapStyle or "None",
              WeatherTypes = {
                {
                  Type = v.WeatherType01 or "None",
                  Chance = v.WeatherType01Chance or 0.25,
                },
                {
                  Type = v.WeatherType02 or "None",
                  Chance = v.WeatherType02Chance or 0.25,
                },
                {
                  Type = v.WeatherType03 or "None",
                  Chance = v.WeatherType03Chance or 0.25,
                },
                {
                  Type = v.WeatherType04 or "None",
                  Chance = v.WeatherType04Chance or 0.25,
                },															
              },
              Direction = v.WeatherDriftDirection or {0,0,0},
            }
                end
            end
        end
        return WeatherDefinition,ClusterDataList
      end
      

      This shows us what data is relevant and transformed into another format. We can also see that there are a lot of sane defaults provided. And last but certainly not least: we can see what the new names for the data is that is used throughout the code.

      function CreateWeatherThread()
        local MapScale = ScenarioInfo.size // x,z map scaling
        local WeatherDefinition, ClusterData = GetWeatherMarkerData(MapScale)
        local MapStyle = WeatherDefinition.MapStyle
        local WeatherEffectsType = GetRandomWeatherEffectType( WeatherDefinition )
        //WeatherEffectsType = 'StormClouds'
        if WeatherEffectsType == 'None' then
          return
        end	
        
        local numClusters = table.getn( ClusterData )
        if not WeatherDefinition.WeatherTypes and numClusters then
          LOG(' WARNING: Weather, no [Weather Definition] marker placed, with [Weather Generator] markers placed in map, aborting weather generation')
          return 
        end
          
        // If we have any clusters, then generate cluster list
        if numClusters != 0 then
          local notfoundMapStyle = true
          for k, v in MapStyleList do
            if MapStyle == v then
              SpawnWeatherAtClusterList( ClusterData, MapStyle, WeatherEffectsType )
              notfoundMapStyle = false
            end
          end
          
          if notfoundMapStyle and (MapStyle != 'None') then
            LOG(' WARNING: Weather Map style [' .. MapStyle .. '] not defined. Define this as one of the Map Style Definitions. ' .. repr(MapStyleList))
          end
        end
      end
      

      For each cluster, which is a generator, the data that has been transformed before is being used to call the function to start generating clouds on top of those generators.

      The cloud types are directly determined from the given map style that is defined in the weather definition marker - only the cloud types form that style are available because that is where the code is searching for to find a matching type.

      function SpawnWeatherAtClusterList( ClusterData, MapStyle, EffectType )
        local numClusters = table.getn( ClusterData )
        local WeatherEffects = MapWeatherList[MapStyle][EffectType]
        
        // Exit out early, if for some reason, we have no effects defined for this
        if (WeatherEffects == nil) or (WeatherEffects != nil and (table.getn(WeatherEffects) == 0)) then
          return	
        end
        
        // Parse through cluster position and datal
        for i = 1, numClusters do
          // Determine whether current cluster should spawn or not
          if ClusterData[i].spawnChance < 1 then
            local pick
            if util.GetRandomFloat( 0, 1 ) > ClusterData[i].spawnChance then
              LOG( 'Cluster ' .. i .. ' No clouds generated ' )
              continue
            end
          end
        
          local clusterSpreadHalfSize = ClusterData[i].clusterSpread * 0.5
          local numCloudsPerCluster = nil
          if ClusterData[i].cloudCountRange != 0 then
            numCloudsPerCluster = util.GetRandomInt(ClusterData[i].cloudCount - ClusterData[i].cloudCountRange / 2,ClusterData[i].cloudCount + ClusterData[i].cloudCountRange / 2)
          else
            numCloudsPerCluster = ClusterData[i].cloudCount
          end
          local clusterEffectMaxScale = ClusterData[i].emitterScale + ClusterData[i].emitterScaleRange
          local clusterEffectMinScale = ClusterData[i].emitterScale - ClusterData[i].emitterScaleRange
          
          // Calculate weather cluster entity positional range
          local LeftX = ClusterData[i].position[1] - clusterSpreadHalfSize
          local TopZ = ClusterData[i].position[3] - clusterSpreadHalfSize
          local RightX = ClusterData[i].position[1] + clusterSpreadHalfSize
          local BottomZ = ClusterData[i].position[3] + clusterSpreadHalfSize		
          
          // Get base height and height range
          local BaseHeight = ClusterData[i].position[2] + ClusterData[i].cloudHeight
          local HeightOffset = ClusterData[i].cloudHeightRange	
          
          // Choose weather cluster effects
          local clusterWeatherEffects = WeatherEffects
          local numEffects = table.getn(WeatherEffects) 
          if ClusterData[i].forceType != "None" then
            clusterWeatherEffects = MapWeatherList[MapStyle][ClusterData[i].forceType] 
            LOG( 'Force Effect Type: ', ClusterData[i].forceType )			
            numEffects = table.getn(clusterWeatherEffects) 
          end
          
          // Generate Clouds for our cluster
          for j = 0, numCloudsPerCluster do
            local cloud = Entity()
            local x = util.GetRandomInt( LeftX, RightX )
            local y = BaseHeight + util.GetRandomInt(-HeightOffset,HeightOffset)
            local z = util.GetRandomInt( TopZ, BottomZ )
            Warp( cloud, Vector(x,y,z) )	
            
            local EmitterGroupSeed = util.GetRandomInt(1,numEffects)
            local numEmitters = table.getn(clusterWeatherEffects[EmitterGroupSeed])
            local effects = clusterWeatherEffects[EmitterGroupSeed]
            
            for k, v in clusterWeatherEffects[EmitterGroupSeed] do
              CreateEmitterAtBone(cloud,-2,-1,v):ScaleEmitter(util.GetRandomFloat( clusterEffectMaxScale, clusterEffectMinScale ))					
            end
          end
        end
      end
      

      This is where the generators are turned into emitters. All the data is transformed (again) into the proper format that dictates how large the emitter can be, how much it should spawn and at what height it should spawn.

      The type of a specific generator can be overridden when you force its type - this is determined when the type of emitter is chosen. Even for the forced type it only searches through the weather available in the selected map style for the forced type.

      The emitters are attached to a (dummy) entity. If a player has no vision over this entity then the attached emitters do not spawn.

      Frequently asked Questions (FAQ)

      I don't see any clouds

      Make sure that:

      • Your script file has been updated properly. You can check the logs (F9) - if there is a warning then you did something wrong. If all is good then you should see something similar somewhere in your log to:
      INFO: Weather Definition {
      INFO:   Direction={ 15, -30, 0, type="VECTOR3" },
      INFO:   MapStyle="Tundra",
      INFO:   WeatherTypes={
      INFO:     { Chance=0.30000001192093, Type="WhitePatchyClouds" },
      INFO:     { Chance=0.30000001192093, Type="WhitePatchyClouds" },
      INFO:     { Chance=0.30000001192093, Type="None" },
      INFO:     { Chance=0.10000000149012, Type="None" }
      INFO:   }
      INFO: }
      INFO: Weather Effect Type: \000WhitePatchyClouds
      
      • You need to have at least one generate that has its 'ForceType' value to 'None'. Make sure that the word starts with a capital letter.
      • You need vision over the weather generator marker in order for it to generate. You can test this best by having no fog of war on your map.

      About you

      If you have interesting sources, approaches, opinions or ideas that aren't listed yet but may be valuable to the article: feel free to leave a message down below or contact me on Discord. The idea is to create a bunch of resources to share our knowledge surrounding various fields of development in Supreme Commander.

      If you've used this resource for one of your maps feel free to make a post below: I'd love to know about it!

      A work of art is never finished, merely abandoned

      1 Reply Last reply Reply Quote 8
      • DDDXD
        DDDX
        last edited by

        This is cool. How resource - intensive is this feature? let's assume a default/usual cloud situation.
        As SupCom gets sluggish in prolonged gameplay with tons of units, how smart is it to use this, how much will it impact SIM speed?

        1 Reply Last reply Reply Quote 0
        • ThomasHiattT
          ThomasHiatt
          last edited by

          Clouds are annoying and 10x as annoying when you are watching a replay at +10.

          1 Reply Last reply Reply Quote 3
          • QuietJoyQ
            QuietJoy
            last edited by

            Hmm, I wouldn't want clouds ever. Nothing that obscures my view of the map would be a good thing?

            1 Reply Last reply Reply Quote 1
            • JipJ
              Jip
              last edited by

              If it is subtle then it is fine in my opinion.

              A work of art is never finished, merely abandoned

              1 Reply Last reply Reply Quote 1
              • KaletheQuickK
                KaletheQuick
                last edited by

                It's an artistic tool that could be used poorly or well. And it could be used to make other effects. Idk, placing a green or black one under water to give it a subtly different texture. Having it in some mountains meant to be impassable anyways?

                You must deceive the enemy, sometimes your allies, but you must always deceive yourself!

                1 Reply Last reply Reply Quote 0
                • ZeldafanboyZ
                  Zeldafanboy
                  last edited by

                  Weather is cool and not much of an issue as you can see the icons through the cloud cover anyways. What are some good maps with nice looking weather?

                  put the xbox units in the game pls u_u

                  1 Reply Last reply Reply Quote 0
                  • FtXCommandoF
                    FtXCommando
                    last edited by

                    Clouds can definitely be an issue, funeral plains has clouds which can be annoying.

                    1 Reply Last reply Reply Quote 0
                    • arma473A
                      arma473
                      last edited by

                      A lot of people turn off "screen shake" even though it adds to immersion. I don't think people want clouds interfering with their ability to see things. Is there a way to toggle clouds on/off? And if not, can that be added?

                      1 Reply Last reply Reply Quote 0
                      • DDDXD
                        DDDX
                        last edited by

                        also, I assume they are static, unable to move? Or can they?

                        1 Reply Last reply Reply Quote 0
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • JipJ Jip referenced this topic on
                        • SaverS
                          Saver
                          last edited by

                          Hi everyone, I had seen the weather markers in FAF Map Editor. Do they work? Or has anyone tested this? So for my part I'm a fan if the clouds are placed where the main thing is not affected.

                          7dd56981-8adc-4cab-892a-371f66d1ead9-image.png
                          69f9bfd9-5647-4151-83e5-8b817338b194-image.png
                          fbe4d65c-897c-4d93-be42-39d74d970a69-image.png

                          auch mal fünf gerade sein lassen

                          IndexLibrorumI 1 Reply Last reply Reply Quote 0
                          • MadMaxM
                            MadMax
                            last edited by

                            yes they work

                            Vault Admin / Creative Team / Map Guru

                            1 Reply Last reply Reply Quote 0
                            • SaverS
                              Saver
                              last edited by

                              thx

                              auch mal fünf gerade sein lassen

                              1 Reply Last reply Reply Quote 0
                              • IndexLibrorumI
                                IndexLibrorum Global Moderator @Saver
                                last edited by

                                @saver I've recently used clouds in Project Tabula. Placed them so that they don't interfere with gameplay, though: only when you zoom all the way out do you have some light clouds, and there are some clouds below the plateau.

                                "Design is an iterative process. The required number of iterations is one more than the number you have currently done. This is true at any point in time."

                                See all my projects:

                                1 Reply Last reply Reply Quote 1
                                • JipJ
                                  Jip
                                  last edited by

                                  And clouds do not show in the editor 🙂

                                  A work of art is never finished, merely abandoned

                                  1 Reply Last reply Reply Quote 1
                                  • SaverS
                                    Saver
                                    last edited by

                                    Thank you for the answers 🙂

                                    auch mal fünf gerade sein lassen

                                    1 Reply Last reply Reply Quote 0
                                    • First post
                                      Last post