Jump to content
Sign in to follow this  
dwringer

A method of getting weighted date/time randomization

Recommended Posts

Hello all,

I've had this script sitting around with some bugs in it for a while, so I used the A3 alpha release as an opportunity to go over it and try to bring things up to a higher standard.

This script, when called, will set the date and time to random values chosen from within the ranges provided in the mission editor.

The time of day, however, is not entirely random. I have implemented a function by rübe that calculates, for a given latitude and longitude read from the map config, the time-of-day corresponding to both sunrise and sunset (Within a reasonable margin of error). In a majority of cases when a randomly chosen time falls into what might be considered "night", the time will instead be set to either the calculated sunrise or sunset time. As a final result, there is a relatively small chance of missions occurring in the dead of night, with a higher-than-average chance of getting relatively dramatic lighting effects. This script really showcases the engine's dynamic lighting and palette effects.

Anyway, without further ado:

/* Arma 3 Random Time/Date Script
Sets a random time and date.
Globally sets isDay & isNight,
        which may or may not be useful.
        The two are not complementary.
Not tested in MP.

 Updated: 8 March 2013

 Author: dwringer

 Credit: rube, Sunrise/sunset calculator function
	<rube_fn_sun.sqf>

 Arguments: None
*/

if (!isServer) exitWith {};

private ["_y", "_m", "_d", "_h", "_n", "_mn", "_sel",
 "_ssArr", "_sr", "_ss", "_srh", "_ssh", "_ts",
 "_dl", "_nl", "_dhsr", "_dhss", "_dds"];

_y = 1982 + (floor random 69);
_m = 1 + (floor random 12);
_d = 2 + (floor random 30);
_h = floor random 24;
_n = floor random 60;
_mn = "";

isDay = false;
isNight = false;

switch (_m) do {
case 1: {
	_mn = "January";};
case 2: {
	_mn = "February";
	if ((_y % 400) == 0) then {
		_d = 1;}
	else {
		if ((_y % 100) == 0) then {
			_d = 0;}
		else {
			if ((_y % 4) == 0) then {
				_d = 1;}
			else {_d = 0;};};};			
	_d = _d + (floor random 29);};
case 3: {
	_mn = "March";};
case 4: {
	_mn = "April";
	_d = _d - (floor random 2);};
case 5: {
	_mn = "May";};
case 6: {
	_mn = "June";
	_d = _d - (floor random 2);};
case 7: {
	_mn = "July";};
case 8: {
	_mn = "August";};
case 9: {
	_mn = "September";
	_d = _d - (floor random 2);};
case 10: {
	_mn = "October";};
case 11: {
	_mn = "November";
	_d = _d - (floor random 2);};
case 12: {
	_mn = "December";};
default {};};

_ssArr = [_y, _m, _d] call compile preprocessFile "rube_fn_sun.sqf";
_sr = _ssArr select 0; // sunrise [h,m]
_ss = _ssArr select 1; // sunset [h,m]
_srh = _sr select 0;   // sunrise hour
_ssh = _ss select 0;   // sunset hour
_dl = abs(_ssh - _srh);// day length
_nl = 24 - _dl;	       // night length
_dhsr = abs(_h - _srh);// diff. in hrs. from sunrise
_dhss = abs(_h - _ssh);// diff. in hrs. from sunset
_dds = abs(_dhsr - _dhss);// difference in above two differences

if ((_h > _srh) && (_h < _ssh)) then {
if ((_dl > 5) && (_dds <= ((2.0 * _dl)/ 3.0))) then {
	isDay = true;
	// midday
}
else {};}
else {
if ((_nl > 3) && ((_h > (_ssh + 1)) || (_h < (_srh - 1)))) then {
	isNight = true;
	// Over an hour from sunrise/sunset
};};

if (isNight) then {
if ((floor random 11) > 1) then {
	isNight = false;
	_sel = floor random 2;
	_h = [_srh, _ssh] select _sel;
	_n = [_sr select 1, _ss select 1] select _sel;};};

setDate [_y, _m, _d, _h, _n];

_ts = "S";
if(isDay) then {_ts = "Midday, s";};
if(isNight) then {_ts = "Night, s";};
hint format ["%1omewhere in the Mediterranean\n%2 %3, %4", _ts, _d, _mn, _y];

publicVariable "isDay";
publicVariable "isNight";

This code is also dependent on the following, which must be saved as "rube_fn_sun.sqf":

/*
  Author:
   rübe

  Description:
   calculates sunrise/sunset on a given date, 
   based on the algorithm from:

    Almanac for Computers, 1990
    published by Nautical Almanac Office
    United States Naval Observatory
    Washington, DC 20392

    (see williams.best.vwh.net/sunrise_sunset_algorithm.htm)

   Latitude and longitude are read from the world config.
   Local time is approximated by: floor (_longitude / 15)

  Parameter(s):
   _this: date (array [year, month, day, ...] or as returned by `date`)

  Returns:
   [sunrise, sunset] (array) 

   ... where `sunrise` and `sunset` are 
   arrays [hour (integer), minute (integer)]
*/

private ["_year", "_month", "_day", "_zenith", "_latitude", "_longitude"];

_year = _this select 0;
_month = _this select 1;
_day = _this select 2;

/*
  zenith:
  - offical = 90 degrees
  - civil = 96 degrees
  - nautical = 102 degrees
  - astronomical = 108 degrees
*/
_zenith = 90; 

_latitude = getNumber(configFile >> "CfgWorlds" >> worldName >> "latitude") * -1;
_longitude = getNumber(configFile >> "CfgWorlds" >> worldName >> "longitude");



/*
  CALCULATION
*/

private ["_n1", "_n2", "_n3", "_n", "_lngHour", "_times"];

// day of the year
_n1 = floor (275 * _month / 9);
_n2 = floor ((_month + 9) / 12);
_n3 = 1 + floor ((_year - (4 * (floor (_year / 4))) + 2) / 3);
_n = _n1 - (_n2 * _n3) + _day - 30;

// convert longitude to hour value and calculate an approximate time
_lngHour = _longitude / 15;


_times = [];

{
  private [
     "_t", "_m", "_l", "_ra", "_lQuadrant", "_raQuadrant", 
     "_sinDec", "_cosDec", "_cosH", "_h", "_ut", "_local", "_localH"
  ];

  if (_x) then
  {
     _t = (_n + ((6 - _lngHour) / 24)); // rising time
  } else
  {
     _t = (_n + ((18 - _lngHour) / 24)); // setting time
  };

  // sun's mean anomaly
  _m = (0.9856 * _t) - 3.289;

  // sun's true longitude
  _l = _m + (1.916 * (sin _m)) + (0.020 * (sin (2 * _m))) + 282.634;

  while {(_l < 0)} do { _l = _l + 360; };
  _l = _l % 360;

  // sun's right ascension
  _ra = atan (0.91764 * (tan _l));

  while {(_ra < 0)} do { _ra = _ra + 360; };
  _ra = _ra % 360;

  // right ascension value needs to be in the same quadrant as L
  _lQuadrant = (floor (_l / 90)) * 90;
  _raQuadrant = (floor (_ra / 90)) * 90;
  _ra = _ra + (_lQuadrant - _raQuadrant);

  // right ascension value needs to be converted into hours
  _ra = _ra / 15;

  // sun's declination
  _sinDec = 0.39782 * (sin _l);
  _cosDec = cos (asin _sinDec);

  // sun's local hour angle
  _cosH = ((cos _zenith) - (_sinDec * (sin _latitude))) / (_cosDec * (cos _latitude));

  /*
  if (_cosH > 1) then
  {
     // the sun never rises on this location (on the specified date)
  };

  if (_cosH < -1) then
  {
     // the sun never sets on this location (on the specified date)
  };
  */

  // finish calculating H and convert into hours
  if (_x) then
  {
     _h = 360 - (acos _cosH); // rising time
  } else
  {
     _h = acos _cosH; // setting time
  };

  _h = _h / 15;

  // local mean time of rising/setting
  _t = _h + _ra - (0.06571 * _t) - 6.622;

  // adjust back to UTC
  _ut = _t - _lngHour;

  while {(_ut < 0)} do { _ut = _ut + 24; };
  _ut = _ut % 24;

  // plus ~local time
  _local = _ut + (floor (_longitude / 15));

  _localH = floor _local;

  // scalar to hours and minutes
  _times set [
     (count _times), 
     [
        _localH,
        (floor ((_local - _localH) * 60))
     ]
  ];

} forEach [
  true, // rising time
  false // setting time
];


// return
_times

Please let me know if anyone finds any issues with the above. There are probably glaring flaws to which I remain oblivious, but I imagine everyone could benefit if they were fixed ;)

EDIT:

Instructions: Put the script into an sqf file, such as "randomTime.sqf", and then

A) create another file called "init.sqf" containing the line: execVM "randomTime.sqf";

or B) using a trigger from the mission editor, in the "On Act: " field put something like: _rt = execVM "randomTime.sqf";

Hope that clears anything up :)

Edited by dwringer

Share this post


Link to post
Share on other sites

Hey no issue with this works great! but is there any way of setting this up so its 1hour day and 1 hour night? If so please help! :) thanks!

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×