Official Fulqrum Publishing forum

Official Fulqrum Publishing forum (http://forum.fulqrumpublishing.com/index.php)
-   FMB, Mission & Campaign builder Discussions (http://forum.fulqrumpublishing.com/forumdisplay.php?f=203)
-   -   Randomising missions (http://forum.fulqrumpublishing.com/showthread.php?t=28207)

salmo 12-02-2011 06:11 AM

Randomising missions
 
I'm trying the write a script to randomise mission parameters in the "MAIN" section of the mission file; time, weather, cloud height, wind etc. Here's what I have, but it does not work, can anyone help out please?

code removed by author

41Sqn_Banks 12-02-2011 07:09 AM

Hi, first of all the "GamePlay.gpLoadSectionFile(fileName);" doesn't load the mission file into the game engine. It only creates a ISectionFile object that allows to read/write a mission file with the script and do some changes (like you are already do).

To post load a mission file into the running battle you need to call "GamePlay.gpPostMissionLoad(fileName);"

However I don't think it's possible to change the MAIN parameters by a mission file that is post loaded into a running battle.

Anyhow your script should look something like this:

Code:

using System;
using System.Collections.Generic;
using maddox.game;
using maddox.game.world;
using maddox.GP;
using System.Security.Cryptography;


public class Mission : AMission
{

public override void OnBattleInit()
{
      RandomiseMission();
}

internal ISectionFile RandomiseMission()
{

    //time of day
    bool RandomiseTime = true;
    int minTime = 6;
    int maxTime = 17;
    // WeatherIndex
    bool RandomiseWeatherIndex = true;
    int minWeatherIndex = 0;
    int maxWeatherIndex = 1;
    // cloud height
    bool RandomiseClouds = true;
    int minClouds = 1000;
    int maxClouds = 2000;
    // BreezeActivity
    bool RandomiseBreezeActivity = true;
    int minBreezeActivity = 0;
    int maxBreezeActivity = 3;
    // ThermalActivity
    bool RandomiseThermalActivity = true;
    int minThermalActivity = 0;
    int maxThermalActivity = 2;

    ISectionFile f = GamePlay.gpCreateSectionFile();
    string sect;
    string key;
    string value;
    // time of day
    if (RandomiseTime == true)
    {
        sect = "MAIN";
        key = "TIME";
        value = NextInt(minTime, maxTime).ToString();
        f.add(sect, key, value);
        GamePlay.gpHUDLogCenter("Time: " +  value); // for testing
    }

    // WeatherIndex
    if (RandomiseWeatherIndex == true)
    {
        sect = "MAIN";
        key = "CloudsHeight";
        value = NextInt(minWeatherIndex, maxWeatherIndex).ToString();
        f.add(sect, key, value);
    }

    // clouds
    if (RandomiseClouds == true)
    {
        sect = "MAIN";
        key = "CloudsHeight";
        value = NextInt(minClouds, maxClouds).ToString();
        f.add(sect, key, value);
    }

    // minBreezeActivity
    if (RandomiseBreezeActivity == true)
    {
        sect = "MAIN";
        key = "BreezeActivity";
        value = NextInt(minBreezeActivity, maxBreezeActivity).ToString();
        f.add(sect, key, value);
    }

    // ThermalActivity
    if (RandomiseThermalActivity == true)
    {
        sect = "MAIN";
        key = "ThermalActivity";
        value = NextInt(minThermalActivity, maxThermalActivity).ToString();
        f.add(sect, key, value);
    }

// This post loads the section file f into the current battle.
// It's also possible to save the section file f first by f.save("filename") and then
// call GamePlay.gpPostMissionLoad("filename"); if you want to save the randomised file
// on your hard disk.
  GamePlay.gpPostMissionLoad(f);
 
  return f;

}

private static int NextInt(int min, int max)
{
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    byte[] buffer = new byte[4];

    rng.GetBytes(buffer);
    int result = BitConverter.ToInt32(buffer, 0);

    return new Random(result).Next(min, max);
}

}


salmo 12-02-2011 08:19 AM

The script compiles without errors in the FMB compiler. When I run the mission, I get 2 outcomes in about 50/50 proportion:
(1) mission load & runs OK & time/weather is random :) :) :)
(2) mission load fails & I get an "object reference not set to an instance of an object" error dialog window.

salmo 12-02-2011 08:57 AM

Here's a working (beta) of a script to randomise mission time/cloud height/weather/breeze & thermals. Feedback is very welcome.

code removed by author

Octocat 12-08-2011 10:32 AM

More concise and economical way to generate random numbers:

Code:

internal ISectionFile RandomiseMission()
{
    var random = new Random(Environment.TickCount);

    ...
    value = random.Next(minBreezeActivity, maxBreezeActivity).ToString();

    ...
    value = random.Next(minThermalActivity, maxThermalActivity).ToString();

    ...
}


salmo 12-08-2011 10:53 AM

Quote:

Originally Posted by Octocat (Post 368240)
More concise and economical way to generate random numbers:

code removed by author

It's a moot point. The random method certainly uses less resources & therefore is "more efficient", but many argue about the "randomness" of the numbers generated. The Cryptography system method uses more resources, but provides a "better" (more unpredictable) random number set. Of course, to get true randon numbers you would have to seed the generator with something truely random such as cosmic radiation or background noise.

Octocat 12-08-2011 11:24 AM

Here the main idea: do not create an object of Random anew for each use. One would be enough.

salmo 12-08-2011 01:16 PM

Quote:

Originally Posted by Octocat (Post 368255)
Here the main idea: do not create an object of Random anew for each use. One would be enough.

So are you saying that the NextInt routine would be better if the highlighted line were used?

code removed by author

Octocat 12-08-2011 01:37 PM

No, code "return Random(result).Next(min, max);" is syntactic error for C# compiler, because keyword new required.

I saying, you can remove NextInt function completely. Then create single instance of Random class in the RandomizeWeather function, and then use it as many times as you like, as in my example above.

Octocat 12-08-2011 01:51 PM

Quote:

The random method certainly uses less resources...
Random in not the method, but class.

Line below creates instance of Random class, and assigns the value to variable named random.

var random = new Random(Environment.TickCount);

Now, we can call method "Next" any number of times:

int randomNumber = random.Next(min, max);

salmo 12-08-2011 02:23 PM

Thankyou Octocat. I known how random.next works, but I chose to go with the RNGCryptoServiceProvider rather than the Random class because I feel it gives me "better" random numbers. Anyone using the script is welcome to replace the NextInt routine with the random class as suggested.

Octocat 12-08-2011 03:28 PM

My variant with RNGCryptoServiceProvider :)

- Code slightly reduced and refactored
- Added minutes randomization
- Fixed maxValue problem (random.Next always return value by 1 less then the specified maximum)

Code:

using System;
using maddox.game;
using System.Security.Cryptography;

public class Mission : AMission
{
  public override void OnBattleInit()
  {
    RandomiseMission();
  }

  internal ISectionFile RandomiseMission()
  {
    // initialize a new instance of the Random class
    var provider = new RNGCryptoServiceProvider();
    var buffer = new byte[4];
    provider.GetBytes(buffer);
    var random = new Random(BitConverter.ToInt32(buffer, 0));

    //time of day
    bool RandomiseTime = true;
    int minTime = 6;    // 6am
    int maxTime = 17;  // 5pm
    // WeatherIndex
    bool RandomiseWeatherIndex = true;
    int minWeatherIndex = 0;    // clear
    int maxWeatherIndex = 2;    // medium clouds
    // cloud height
    bool RandomiseClouds = true;
    int minClouds = 500;    // 500m
    int maxClouds = 1500;  // 1500m
    // BreezeActivity
    bool RandomiseBreezeActivity = true;
    int minBreezeActivity = 0;  // zero breeze
    int maxBreezeActivity = 10; // max breeze
    // ThermalActivity
    bool RandomiseThermalActivity = true;
    int minThermalActivity = 0;    // zero thermals
    int maxThermalActivity = 10;    // max thermals

    ISectionFile secfile = GamePlay.gpCreateSectionFile();
   
    // time of day
    if (RandomiseTime == true)
    {
      string value = (random.Next(minTime * 100, maxTime * 100) / 100f).ToString();
      secfile.add("MAIN", "TIME", value);
    }

    // WeatherIndex
    if (RandomiseWeatherIndex == true)
    {
      string value = random.Next(minWeatherIndex, maxWeatherIndex + 1).ToString();
      secfile.add("MAIN", "WeatherIndex", value);
    }

    // clouds
    if (RandomiseClouds == true)
    {
      string value = random.Next(minClouds, maxClouds + 1).ToString();
      secfile.add("MAIN", "CloudsHeight", value);
    }

    // minBreezeActivity
    if (RandomiseBreezeActivity == true)
    {
      string value = random.Next(minBreezeActivity, maxBreezeActivity + 1).ToString();
      secfile.add("MAIN", "BreezeActivity", value);
    }

    // ThermalActivity
    if (RandomiseThermalActivity == true)
    {
      string value = random.Next(minThermalActivity, maxThermalActivity + 1).ToString();
      secfile.add("MAIN", "ThermalActivity", value);
    }

    // This post loads the section file secfile into the current battle.
    // It's also possible to save the section file secfile first by secfile.save("filename") and then
    // call GamePlay.gpPostMissionLoad("filename"); if you want to save the randomised file
    // on your hard disk.
    GamePlay.gpPostMissionLoad(secfile);

    return secfile;
  }
}


salmo 12-09-2011 01:12 AM

OK, I see what you've done. One declaration of var provider = new RNGCryptoServiceProvider(); Much better Thankyou.

1. I've moved the Random Class declaration outside the randomisemission routine so it can also be used in the randomisation of mission selection etc. (see below). But this construct produces an error CS1519: Invalid token '(' in class, struct, or interface member declaration on the provider.GetBytes(buffer); line. pehaps you can help?

code removed by author

2. Multiplayer implimentation

This script randomised the [MAIN] section of the mission file OK in single-player mode, but fails in multi-player(server) mode. I have in mind some pseudo code such as: Randomise [MAIN] on server battle start; save [MAIN] section file on the server side; as players connect reload the saved [MAIN] section file from server side so they get the same mission parameters in multi-player mode.

Octocat 12-09-2011 04:45 PM

Yes, because dynamic type resolution "var x = " works in functions only, and calls like "provider.GetBytes(buffer)" too.

In your case better to use constructor, to bury inside all temporary instances like "buffer" and "provider", when they perform their task. For example:

Code:

using System;
using maddox.game;
using System.Security.Cryptography;

public class Mission : AMission
{
  // allowed to change only in constructor
  private readonly Random m_random;

  // Constructor (do not call manually)
  public Mission()
  {
    // initialize a new instance of the Random class
    var provider = new RNGCryptoServiceProvider();
    var buffer = new byte[4];
    provider.GetBytes(buffer);
    m_random = new Random(BitConverter.ToInt32(buffer, 0));
  }

  public override void OnBattleInit()
  {
    RandomiseMission();
  }

  internal ISectionFile RandomiseMission()
  {
    //time of day
    bool RandomiseTime = true;
    int minTime = 6;    // 6am
    int maxTime = 17;  // 5pm
    // WeatherIndex
    bool RandomiseWeatherIndex = true;
    int minWeatherIndex = 0;    // clear
    int maxWeatherIndex = 2;    // medium clouds
    // cloud height
    bool RandomiseClouds = true;
    int minClouds = 500;    // 500m
    int maxClouds = 1500;  // 1500m
    // BreezeActivity
    bool RandomiseBreezeActivity = true;
    int minBreezeActivity = 0;  // zero breeze
    int maxBreezeActivity = 10; // max breeze
    // ThermalActivity
    bool RandomiseThermalActivity = true;
    int minThermalActivity = 0;    // zero thermals
    int maxThermalActivity = 10;    // max thermals

    ISectionFile secfile = GamePlay.gpCreateSectionFile();

    // time of day
    if (RandomiseTime == true)
    {
      string value = (m_random.Next(minTime * 100, (maxTime) * 100) / 100f).ToString();
      secfile.add("MAIN", "TIME", value);
    }

    // WeatherIndex
    if (RandomiseWeatherIndex == true)
    {
      string value = m_random.Next(minWeatherIndex, maxWeatherIndex + 1).ToString();
      secfile.add("MAIN", "WeatherIndex", value);
    }

    // clouds
    if (RandomiseClouds == true)
    {
      string value = m_random.Next(minClouds, maxClouds + 1).ToString();
      secfile.add("MAIN", "CloudsHeight", value);
    }

    // minBreezeActivity
    if (RandomiseBreezeActivity == true)
    {
      string value = m_random.Next(minBreezeActivity, maxBreezeActivity + 1).ToString();
      secfile.add("MAIN", "BreezeActivity", value);
    }

    // ThermalActivity
    if (RandomiseThermalActivity == true)
    {
      string value = m_random.Next(minThermalActivity, maxThermalActivity + 1).ToString();
      secfile.add("MAIN", "ThermalActivity", value);
    }

    // This post loads the section file secfile into the current battle.
    // It's also possible to save the section file secfile first by secfile.save("filename") and then
    // call GamePlay.gpPostMissionLoad("filename"); if you want to save the randomised file
    // on your hard disk.
    GamePlay.gpPostMissionLoad(secfile);

    return secfile;
  }
}



All times are GMT. The time now is 09:10 AM.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Copyright © 2007 Fulqrum Publishing. All rights reserved.