Author Topic: DisasterHelper class for helping create Outpost 2 scenarios  (Read 9010 times)

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
DisasterHelper class for helping create Outpost 2 scenarios
« on: November 30, 2016, 02:24:00 PM »
Update 6DEC: Added a version # to top of DisasterHelper.h. Now of version 1.0. See attached .zip file that contains .cpp/.h for newest code.

I finished a polished final draft of the DisasterHelper class. I'm happy to receive feedback on the mess, but since it is about 400 lines of code, I understand if people do not want to read through it. I'll be passing it off to Dave for use if he still wants it in his multiplayer rebalancing scenarios, so it would be easier change things now before that happens.

Due to limits on how functions are passed to Triggers, you cannot directly pass the function CreateRandomDisaster from DisasterHelper to an Outpost 2 Trigger function. However, you can simply wrap the function and then pass it without problem. See the initialization example below on what I mean.

If using this code in a single player scenario, you must ensure DisasterHelper is initialized after a game is re-loaded. This is best done by calling DisasterHelper.MapPropertiesSet(). If this is false, then you need to re-initialize it before calling. Not initializing the class before calling will not crash the game, it will just cause disasters to not be created when called for.

Debugging

In Visual Studio (or another IDE), if you leave the Solution Configuration in Debug mode instead of release mode, DisasterHelper will send warning messages via the in game chat console. In particular warnings about not initializing the class, not being able to find a LOCATION outside of a safe area, trying to create a vortex without first setting a VortexCorridor (MAP_RECT), or setting innapropriate duration/strength values are sent.

How to use it:

Below are three examples of how to integrate DisasterHelper into a scenario. They will all get disasters on the map in a reasonable manner. A lot more properties of DisasterHelper can be set if you want to massage them, such as disaster duration, disaster strength, percent chance of each disaster, etc. The default values should be noticeable without being too powerful.

Single Player (Colony Game) Concerns
If you use DisasterHelper in a colony game, you have to place your triggers in ScriptGlobal and do the rest of the requirements to ensure it is available for use between Saves/Loads of the scenario. When saving/loading the scenario, the class DisasterHelper will not retain its settings. The code below checks if DisasterHelper is not initialized every time CreateRandomDisaster is called to ensure the class data is restored after a load.

Since multiplayer scenarios cannot be saved/loaded, If you are designing a multiplayer scenario, you can disregard placing the trigger in ScriptGlobal and just initialize DisasterHelper in the function InitProc and not worry about checking if its properties are set later.

Example 1: Pairing DisasterHelper with BaseHelper in a multiplayer scenario. Safe Areas set Manually.
Code: [Select]
#include "DisasterHelper.h"

DisasterHelper disasterHelper;

//Shows setting up 4 base locations for a 4 player LoS game.
//Base starting locations are randomized.
int playerSlots[] = { 0, 1, 2, 3 };

const MAP_RECT baseSafeRects[] = {
    { 10 + X_, 10 + Y_, 30 + X_, 30 + Y_ },
    { 100 + X_, 10 + Y_, 30 + X_, 30 + Y_ },
    { 10 + X_, 100 + Y_, 30 + X_, 30 + Y_ },
    { 100 + X_, 100 + Y_, 30 + X_, 30 + Y_ },
};

Export int InitProc()
{
    disasterHelper.SetMapProperties(256, 64, false); //MapWidth, MapHeight, Does map wrap East/West
    RandomizeList(AutoSize(playerSlots));

    for (int i = 0; i < TethysGame::NoPlayers(); i++)
    {
        // Create bases loop
CreateBase(i, startLocation[playerSlots[i]]); // See BaseHelper.h for documentation on creating startLocation array.

// Loop to create safe rects
disasterHelper.AddSafeRect(baseSafeRects[playerSlots[i]]);
    }

    CreateTimeTrigger(true, false, 1500, 3500, "CreateDisaster"); //Set time in ticks (marks / 100)
}

Export void CreateDisaster()
{
    disasterHelper.CreateRandomDisaster();
}

Example 2: Auto find Command Centers and add MAP_RECTs of a pre-determined size to DisasterHelper. Designed for a use in a multiplayer scenario.

Code: [Select]
#include "DisasterHelper.h"

DisasterHelper disasterHelper;

Export int InitProc()
{
    disasterHelper.SetMapProperties(256, 64, false); //MapWidth, MapHeight, Does map wrap East/West

    AutoSetBaseSafeRects(30, 30);

    CreateTimeTrigger(true, false, 1500, 3500, "CreateDisaster"); //Set time in ticks (marks / 100)
}

// Places a safeRectangle centered on all command centers currently in game.
void AutoSetBaseSafeRects(int safeAreaWidth, int safeAreaHeight)
{
    for (int i = 0; i < TethysGame::NoPlayers(); i++)
    {
        PlayerBuildingEnum playerBuildingEnum(i, map_id::mapCommandCenter);

        Unit unit;
while (playerBuildingEnum.GetNext(unit))
{
    LOCATION loc = unit.Location();
    MAP_RECT safeRect = MAP_RECT(loc.x - safeAreaWidth / 2, loc.y - safeAreaHeight / 2, loc.x + 30, loc.y + 30);

    disasterHelper.AddSafeRect(safeRect);
}
    }
}

Export void CreateDisaster()
{
    disasterHelper.CreateRandomDisaster();
}


Example 3: Use DisasterHelper in a colony game scenario where dealing with Saving/Loading games must be taken into account.

Code: [Select]
#include "DisasterHelper.h"

DisasterHelper disasterHelper;

Export int InitProc()
{
    scriptGlobal.DisasterTimeTrig = CreateTimeTrigger(true, false, 1500, 3500, "CreateDisaster"); //Set time in ticks (marks / 100)
}

Export void CreateDisaster()
{
    if (!disasterHelper.MapPropertiesSet())
    {
disasterHelper.SetMapProperties(256, 64, false); //MapWidth, MapHeight, Does map wrap East/West

disasterHelper.AddSafeRect(MAP_RECT(10 + X_, 10 + Y_, 30 + X_, 30 + Y_));
    }

    disasterHelper.CreateRandomDisaster();
}



The Actual Code

The header and CPP file for DisasterHelper are attached in a ZIP file. Beyond Outpost 2 they depend on vector, cmath and climits. You need a C++11 compliant compiler, but that should be pretty standard now (I think?). This code is currently contained in the repository at https://svn.outpostuniverse.org:8443/!/#outpost2/view/head/LevelsAndMods/trunk/Levels/RescueEscort. I'm using the scenario RescueEscort to test it, although this scenario is far from being completed I'm ensuring RescueEscort always compiles/runs fine when committing to it.

* Note: You may need to login with an Outpost Universe Forum Account to see the attachment.
« Last Edit: December 06, 2016, 04:52:55 PM by Vagabond »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #1 on: December 01, 2016, 07:23:52 AM »
In SetDisasterSafeZones(), why are you using a switch statement inside of a loop? Why not just put the case blocks one after each other.

I suppose the loop method only creates safe zones for players that actually exist. (Is this even necessary? Is this desirable?) If that's really what you want, you could define the safe zones in an array, and loop over the array creating the safe zones, based on the number of players.

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #2 on: December 01, 2016, 10:52:50 AM »
Personally I would do away with the switch statement entirely and use a table approach. It's cleaner and easier to maintain without mistakes:

Code: [Select]
std::map<int, MAP_RECT> LOCATIONS_TABLE;


Export int InitProc()
{
initLocationTable();

/* Set time in ticks (marks / 100) */
scriptGlobal.DisasterTimeTrig = CreateTimeTrigger(true, false, 1500, 3500, "CreateDisaster");
}


void initLocationTable()
{
LOCATIONS_TABLE[0] = MAP_RECT(10 + X_, 10 + Y_, 30 + X_, 30 + Y_);
LOCATIONS_TABLE[1] = MAP_RECT(100 + X_, 10 + Y_, 30 + X_, 30 + Y_);
LOCATIONS_TABLE[2] = MAP_RECT(10 + X_, 100 + Y_, 30 + X_, 30 + Y_);
LOCATIONS_TABLE[3] = MAP_RECT(100 + X_, 100 + Y_, 30 + X_, 30 + Y_);
}


Export void CreateDisaster()
{
if (!disasterHelper.MapPropertiesSet())
{
/* MapWidth, MapHeight, Does map wrap East/West */
disasterHelper.SetMapProperties(256, 64, false);
SetDisasterSafeZones();
}

disasterHelper.CreateRandomDisaster();
}


void setDisasterSafeZones()
{
for (int i = 0; i < TethysGame::NoPlayers(); i++)
disasterHelper.AddSafeRect(LOCATIONS_TABLE[i]);
}

Note that the code syntax highlighter breaks on C++ style comments which is why I switched to C style comments instead and also on compiler directives (such as #include)... need to see if I can fix that.

Anyway, I dunno... I find table approaches cleaner and easier to work with than large control tatements. As you can see the setDisasterSafeZones() function is suddenly a lot easier to read but more importantly, to add an additional location you can simply add a new MAP_RECT entry to the LOCATIONS_TABLE.

The rest of it looks good. Really love the DisasterHelper object -- seems to be a great addition to the library of coding tools we have available to us! Might make sense to include it as part of the SDK... but we'll see when Arklon/BlackBox decide to move the way of Python. :P
« Last Edit: December 01, 2016, 10:58:52 AM by leeor_net »

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #3 on: December 01, 2016, 12:40:28 PM »
@Hooman & Leeor,

Thanks for the suggestions!

Hooman, Yes the idea was for a multiplayer scenario you only define the safe area for where an actual player has a starting base. In retrospect, I agree the loop over the switch statement isn't an ideal way to write the code. Ultimately, it will be up to the user of the class how they want to set the safe zones. I was thinking maybe a simple way is if they enumerates over all the structures and auto-create MAP_RECTs to pass of a certain size around each command center. This way they don't have to manage moving SafeAreas separately.

I'll change the use example code to include a std::MAP or C style array that is easier to read. I'll also update the class to include a timer the user can set to make safe zones expire as suggested by Hooman the other forum post. Should just take me a day or two for this.

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #4 on: December 01, 2016, 12:54:46 PM »
Would still highly recommend using std::map vs. a c array for three reasons:

  • Less likelihood of bugs, particularly memory issues.
  • Easier to read/use.
[li]Since this is internal to the DLL and isn't being passed to the game at all, it makes sense to take advantage of well known libraries and tools.
[/li][/list]

When I think about it though a std::vector may even be a better option. Less overhead and access syntax is basically the same:

Code: [Select]
std::vector<MAP_RECT> LOCATIONS_TABLE;

/* ... snip ... */

void initLocationTable()
{
LOCATIONS_TABLE.push_back(MAP_RECT(10 + X_, 10 + Y_, 30 + X_, 30 + Y_));
LOCATIONS_TABLE.push_back(MAP_RECT(100 + X_, 10 + Y_, 30 + X_, 30 + Y_));
LOCATIONS_TABLE.push_back(MAP_RECT(10 + X_, 100 + Y_, 30 + X_, 30 + Y_));
LOCATIONS_TABLE.push_back(MAP_RECT(100 + X_, 100 + Y_, 30 + X_, 30 + Y_));
}


/* ... snip ... */


void setDisasterSafeZones()
{
for (int i = 0; i < TethysGame::NoPlayers(); i++)
disasterHelper.AddSafeRect(LOCATIONS_TABLE[i]);
}

Def kinda scratching my head over why I thought using a map was the best option considering that an int was being used as a key...

Anyway, whatever. The vector is a better option so eh.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #5 on: December 02, 2016, 01:10:23 AM »
Was scratching my head over that too. ;)

I would recommend arrays over vectors for two reasons:
  • Easier to read/use
  • Done right, the memory should be static, so the whole issue of dynamic memory management, and associated performance hits are avoided completely.

I would also do away with the initialization function, and instead initialize the variable where it is declared with an initializer list. This applies to both arrays and vectors, though you need a fairly up to date compiler to do this with vectors. This should be do-able outside of a function, so this will work for both local and global variables.
Code: [Select]
const int a[] = {1, 2, 3}; // const optional, but very appropriate if the array won't be modified

I haven't compiled this, but here's my rough shot at what the code would look like:
Code: [Select]
const MAP_RECT safeList[] = {
  { 10 + X_, 10 + Y_, 30 + X_, 30 + Y_ },
  { 100 + X_, 10 + Y_, 30 + X_, 30 + Y_ },
  { 10 + X_, 100 + Y_, 30 + X_, 30 + Y_ },
  { 100 + X_, 100 + Y_, 30 + X_, 30 + Y_ },
};

void setDisasterSafeZones()
{
  for (int i = 0; i < TethysGame::NoPlayers(); i++)
    disasterHelper.AddSafeRect(safeList[i]);
}

Also, if you are randomizing base locations, which I assume you are, the associated safe locations will need to be randomized as well in the same order. Rather than randomize the base data directly, you can randomize an array (let's say playerSlot) containing integers 0..NumStartingLocations, and use that as an index into both the baseData array, and the safeList array.
Code: [Select]
int playersSlot[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12 };  // Lots of base locations =)

Export int InitProc() {
  RandomizeList(AutoSize(playerSlot));
  // ...
  // Create bases loop
    CreateBase(i, startLocation[playerSlot[i]]);
  // ...
  // Loop to create safe rects
    disasterHelper.AddSafeRect(safeList[playerSlot[i]]);

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #6 on: December 02, 2016, 11:08:52 AM »
I find standard C arrays to be harder to read than their STL counterparts. The memory/performance hits mentioned are negligible at worst -- these are small arrays that aren't being resized dynamically in memory frequently. They're intiailized once at the beginning of the program and the accesses are pretty quick. Sure, maybe not as fast as with a C array but again, negligible and pretty much imperceptible.

I suppose it's ultimately another stylistic choice but I find working with raw C style arrays to be archaic and unnecessary except in performance critical applications and even then only in rare cases. The added overhead for well tested and consistent syntax, in my humble opionion, is well worth it. Plus, as you saw in my first mistake, implementation can be changed later with almost no need to change the way the array is accessed. Effectively it makes internal implementation irrelevant.

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #7 on: December 02, 2016, 04:31:28 PM »
I would recommend arrays over vectors for two reasons:
  • Easier to read/use
  • Done right, the memory should be static, so the whole issue of dynamic memory management, and associated performance hits are avoided completely.
C++11 adds std::array. It's mostly just a wrapper around a C-style array to add iterator accessing, so you can use range-based for statements for instance.

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #8 on: December 03, 2016, 01:42:09 AM »
Okay,

Thank you for everyones' replies here and in the other Disaster thread. I think in order to stay consistent with how BaseBuilder is working, I'll use standard c arrays for the example code. Of course whoever uses the class can initialize it using std::array or std::vector if they want instead.

I added a constant for the default time in marks. I understand that ticks are used in a lot of the program. TethysGame disaster function durations are set in marks, so using marks as a default unit in DisasterHelper seemed natural. It is also natural for me when I want to think about durations of disasters to think in the broad strokes of marks instead of micro stroke of ticks. But the conversion is easy, so probably not worth dwelling on one way or another.

Code: [Select]
static const int TimerNeverExpires = INT_MAX;
static const int TimerDefaultValue = 250; //In Marks

I'd like to stick with CamelCase for the static variables. I think Leeor would prefer to see CAMEL_CASE. My disagreement is probably my C# background coming through. I did a search for static const variables in the SDK and didn't really find any. If there is a strong preference towards ONLY_CAPITAL, then I will switch to it so as to keep coding style similar across the SDK.

I modified the code in the first post to include the new Timer content. I also re-wrote the example code. There are now 3 use examples.
  • Pairing the Disaster Helper with BaseHelper in a multiplayer type scenario. Safe Areas are manually set.
  • Using a function to auto find Command Centers and adding MAP_RECTs of a pre-determined size to DisasterHelper. Sort of a hands off approach.
  • Using DisasterHelper in a colony game scenario where dealing with Saving/Loading games must be taken into account.

If there are more suggestions, then please send them along. I think it is getting time to get the code pushed into a scenario that gets finished and see how it goes. I'm kind of lamenting not being familiar with BaseBuilder now since I want to see how DisasterHelper fits in with it. I'm working on a single player unit only mission that can sort of test it. The last one standing scenarios that Dave is working on would probably be better though.

Offline Arklon

  • Administrator
  • Hero Member
  • *****
  • Posts: 1269
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #9 on: December 03, 2016, 01:51:04 AM »
I'd like to stick with CamelCase for the static variables. I think Leeor would prefer to see CAMEL_CASE. My disagreement is probably my C# background coming through. I did a search for static const variables in the SDK and didn't really find any. If there is a strong preference towards ONLY_CAPITAL, then I will switch to it so as to keep coding style similar across the SDK.
The case scheme I see most often is this:
UPPER_SNAKE_CASE for macros
lowerCamelCase for variables
UpperCamelCase for functions

Notable exceptions are the STL uses lower_snake_case for everything, and Microsoft uses FULLUPPERCASE for typedefs, but I don't often see those used like that elsewhere.

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #10 on: December 03, 2016, 09:01:22 AM »
... snip ...

I think Leeor would prefer to see CAMEL_CASE.

... snip ...

I would, but that's just me and my own personal preference. Ultimately it's a stylistic choice and its your helper class, you get to make the choices, not me. I'm not one of those style zealots so you're not going to get any complaints from me. :)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #11 on: December 05, 2016, 03:48:19 AM »
Ahh yes, CARPAL_TUNNEL_SYNDROME_INDUCING_CASE_WHICH_MAKES_ME_WANT_TO_SAW_MY_WRISTS_OFF.

No, I've never been diagnosed with Carpal tunnel syndrome. Yes, typing lots of constants like that hurts my wrists. By the end of the day I'm often left feeling sore and not so dexterous with my fingers. Please, choose any case but that one.


I use lowerCamelCase for variable names. I use UpperCamelCase for type names. That includes classes, structs, unions, enums. The Outpost 2 SDK uses UpperCamelCase for functions, as does a lot of the Windows API. At times I've seen lowerCamelCase used for functions. It doesn't really matter, as long as you're consistent. As most things in Outpost 2 and the Windows API use UpperCamelCase, I would recommend that convention for functions. I've also seen a Microsoft document that recommends UpperCamelCase for enum values. Constants are similar to enum value, being a read-only value, so I suggest also making them UpperCamelCase.


Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #12 on: December 05, 2016, 09:13:20 PM »
Just had a thought on increasing convenience. Instead of having user's write their own code to add safe rects:
Code: [Select]
void setDisasterSafeZones()
{
  for (int i = 0; i < TethysGame::NoPlayers(); i++)
    disasterHelper.AddSafeRect(safeList[i]);
}

You could provide a method on the disaster helper class to add an array of safe rects.
Code: [Select]
disasterHelper.AddSafeRects(TethysGame::NoPlayers(), safeList);

It's probably easier for the user to define an array of safe zones, so just let them set that directly, rather than force them to write a loop. Note that I named the function AddSafeRects above. An alternative might be SetSafeRects if you want to clear the list and then add (or just store a pointer to the passed list to avoid copying, though that's less encapsulated and would need to be documented).

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #13 on: December 06, 2016, 09:50:14 AM »
Ahh yes, CARPAL_TUNNEL_SYNDROME_INDUCING_CASE_WHICH_MAKES_ME_WANT_TO_SAW_MY_WRISTS_OFF.

No, I've never been diagnosed with Carpal tunnel syndrome. Yes, typing lots of constants like that hurts my wrists. By the end of the day I'm often left feeling sore and not so dexterous with my fingers. Please, choose any case but that one.

Auto complete? Makes this a moot point. :P

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #14 on: December 06, 2016, 12:22:44 PM »
Hooman,

Thanks for the suggestion. How about the code below? It will let you add SafeRects individually, as c style arrays, as std::arrays, and as std::vectors.

I think in this case it makes since to leave the safeRects strongly encapsulated and not use pointers. The user shouldn't need to pass more than a dozen MAP_RECTs when intializing the scenario, and this is usually only going to happen once. I like allowing the user to add MAP_RECTs with multiple calls instead of resetting them every time the function is called. Although, since the user will often only set the SafeRects by calling the function one time, I can see why more of a set approach would fit nicely.

I just noticed I made a typo and made the class level object safeRects read as SafeRects, so I'll fix that as well and make it safeRects.

Arklon & Hooman,

Thanks for the insights on the different case schemes. What you two suggested is probably where I'll try to stay.

Code: [Select]
void ClearSafeRects()
{
safeRects.clear();
}

//Add a MAP_RECT where no earthquakes or large meteors will be created.
//This is designed to protect starting locations from powerful disasters.
void AddSafeRect(const MAP_RECT& safeRect)
{
safeRects.push_back(safeRect);
}

//Add a c style array of MAP_RECTs where no earthquakes or large meteors will be created.
//This is designed to protect starting locations from powerful disasters.
void AddSafeRects(int arraySize, MAP_RECT safeRects[])
{
for (int i = 0; i < arraySize; i++)
{
this->safeRects.push_back(safeRects[i]);
}
}

template<typename MAP_RECT, size_t N>
void AddSafeRects(const std::array<MAP_RECT, N>& safeRects)
{
this->safeRects.insert(this->safeRects.end(), safeRects.begin(), safeRects.end());
}

void AddSafeRects(const std::vector<MAP_RECT>& safeRects)
{
this->safeRects.insert(this->safeRects.end(), safeRects.begin(), safeRects.end());
}

* Edit: fixed an error I found in the template version of the function.
« Last Edit: December 06, 2016, 04:24:17 PM by Vagabond »

Offline dave_erald

  • Sr. Member
  • ****
  • Posts: 262
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #15 on: December 06, 2016, 03:29:00 PM »
So how do we go about adding this code into a .cpp (or what bits need to) and test to see that disasters are staying out of the SafeRects?
-David R.V.

-GMT400 fan
-OPU Influencer

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #16 on: December 06, 2016, 04:22:22 PM »
Dave,

I went ahead and placed the .h and .cpp file that compose DisasterHelper in a zip file and attached them to the first post in this topic. If I have to update in the future, I will change out the attached zip file and note the changes.

Take the 2 code files in the zip and place them into your scenario's project in Visual Studio. Then add #include<DisasterHelper.h> to the top of your scenario file that is calling DisasterHelper.h. At that point you should be able to follow one of the use cases in the first post to integrate DisasterHelper into the scenario. If it isn't clear for whatever reason, let me know and I'll explain more thoroughly or try to jump onto chat.

I went ahead and added a version line to the top of DisasterHelper.h (currently version 1.0). Hopefully this will help keep things in order when I need to update the code for a bug, new feature, etc. I also spent more time debugging DisasterHelper. I fixed a couple of errors that I introduced when refactoring and a couple I didn't test enough to catch originally. I think she should be pretty much bug free at this point, but since I'm making this statement I'm sure something will turn up wrong. All changes made it into version 1.0.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: DisasterHelper class for helping create Outpost 2 scenarios
« Reply #17 on: December 07, 2016, 12:56:22 AM »
Also thought I'd add a note about the AutoSize macro from OP2Helper.

You can add as many safe rects as players using this code:
Code: [Select]
disasterHelper.AddSafeRects(TethysGame::NoPlayers(), safeList);

You can add all safe rects using this code:
Code: [Select]
disasterHelper.AddSafeRects(AutoSize(safeList));

AutoSize macro expands to a calculated array size (works for static arrays), followed by an array pointer. There was a reason I put the arguments in that order. ;)