Author Topic: Multiple Questions Asked  (Read 16223 times)

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« on: September 12, 2007, 08:00:42 AM »
So, I managed to get a map up and running (sorta) today. I just used the Plymouth starship map with some modifications to the map (added a few extra cliffs, placed fixed beacon sites with random bar numbers) and made a human player start.

//1. Now... I wanted a 2nd player (AI) to start (just start for now, he doesn't have to do anything) but not a single structure/unit shows up...
//2. I would like to know how I can use the _player functions, to set certain things like starting population, morale and maybe the color too.
//3. Also, I would need to know how to have cargo trucks start with metals and food in them.
//4. How to use daylight stuff?
//5. How to set lights for every unit to on?
//6. When I use the unit class to set the cargo to the trucks, only one of my trucks actually get filled. What should I do?
//7.I tried getting the AI to deploy a mine using some code I found in the AI programming,but it doesn't work. The Surveyor seems to survey the beacon, but the miner doesn't build... in fact, it just locks itself there like a ghost, units can get through it. How to fix this?
//8. I want this map to have random disasters, and I mean 100% random disasters. Random locations, random magnitude (for quakes) random intervals, etc...
But... I'm not sure what to use for randomizing values and I can't figure out how to use time triggers (any triggers for that matter) yet.
edit: Well... I changed my mind about the 100% randomness :D I've mapped out some safer areas, that'll have less destructive stuff... but no place will be 100% safe.
//9. How to get the AI trucks to mine?
//10. I attempted getting an eruption via a trigger, but I'm getting something wrong. How should I write a disaster that comes after a time trigger?
//11. Where can I find/get the code for a working eruption, using the basic stuff, and not the smartlava thing? I just want to get the map defined terrain-wise(including eruptions/blight). I will eventually use the SmartLava script to make it a bit more intricate, but for now I just want to get an eruption to go off at around mark 20, and the lava flow animation on the volcano to start at mark 10.
//12. If the AI has a structure kit pre-placed in its SF storage, will a ConVec take the placed one if it has to be build using the normal RecordBuilding, or will the SF build another kit and the ConVec use the newly built kit?
/13. Past attempts on avoiding the RecordBuilding function to help speed up the process of building the AI base has come to a stand-still, as I can't figure out a way to make it work. I've started using the IUnits header and have got the SF to produce a basic lab kit. But then's the problem... how do I get a next ConVec that has no cargo to the dock and load the SF cargo, whilst building a new kit in the meantime.
//14. How big does the reinforce group value difference hav eto be to force the VF to build something before the other things? Using the reinforceVehicleGroup to get the VF to make stuff, it won't always make both the Cargo Trucks it has to, building an Earthworker or a Repair Vehicle in the meantime...?
//15.I'm now trying to get the AI to respect certain research orders, but the way I tried it makes the game crash. Any suggestions?

16. Next real challenge will be optimizing building times for the AI. Record Building just doesn't cut it. No problems yet, but suggestions as to how I could do this are always welcome.


Warning: Content might cause head aches, severe migraines or a possible stroke to any experienced map coder!
« Last Edit: July 18, 2008, 02:43:24 PM by Hidiot »
"Nothing from nowhere, I'm no one at all"

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Multiple Questions Asked
« Reply #1 on: September 12, 2007, 12:48:24 PM »
Quote
1. Now... I wanted a 2nd player (AI) to start (just start for now, he doesn't have to do anything) but not a single structure/unit shows up...

2. I would like to know how I can use the _player functions, to set certain things like starting population, morale and maybe the color too.

3. Also, I would need to know how to have cargo trucks start with metals and food in them.

4. How to use daylight stuff?
I'll answer the questions in a slightly different order (the simple ones first):

4. This is pretty easy. Just do something like:
Code: [Select]
TethysGame::SetDaylightEverywhere(0);
TethysGame::SetDaylightMoves(1);

The first line shuts off daylight (changes it from being everywhere to just a single band). The second line allows the band to move (effectively turning on day and night mode).

2. Players can be accessed using the Player array, which is indexed from 0 to 6 starting with the first player, for example:
Code: [Select]
Player[0].SetWorkers(30); // give player 1 30 workers
Player[1].SetKids(200); // give player 2 200 kids

Morale is done slightly differently, using TethysGame:
Code: [Select]
TethysGame::ForceMoraleGood(1); // this will lock Player #2 to "Good" morale (75)

Change the 1 above to do it for a different player. Likewise, you can lock players to other levels (Rotten, Poor, Fair, and Great are the other ones).

You can unlock morale using:
Code: [Select]
TethysGame::FreeMoraleLevel(playerNum);

Note that if you do any morale locking / unlocking after the game has started (i.e. not tick 0), a "CHEATED GAME" message will appear in the upper left corner of the screen.

1. First off, in the DescBlock you need to change the player number to allow for more players:
Code: [Select]
SDescBlock DescBlock = { Colony, 2, 12, 0 }; // Important level details

Note the 2 instead of the 1 as in your code below. Change this to however many players you need (keep in mind that the game only allows 7 players, with the last player being mostly used for "gaia" / environment owned items).

As far as setting whether the AI player is Eden or Plymouth, use something like:
Code: [Select]
Player[1].GoEden(); // for eden
Player[1].GoPlymouth(); // for plymouth

You can do the same thing for Player[0] if you want to do it for the human player as well.

Furthermore, you can set the AI player to 'AI mode' if you wish:
Code: [Select]
Player[1].GoAI();
All this does is to give the player basically unlimited population (kids, workers, scientists are all set to 4096). It's sort of a cheat -- it's best to just let your AI player act as a human player and manage everything normally.

3. In your InitProc, define new Unit variables for the trucks:
Code: [Select]
Unit truck;

Then, for the CreateUnit calls for the trucks, for example to modify one of your trucks:
Code: [Select]
TethysGame::CreateUnit(truck, (map_id)1, LOCATION(125+31, 108-1), 0, (map_id)0, 4);

Simply change the 'x' to whatever you named the variable up above. This gives you a reference to the unit that you can use later to set the cargo as follows:
Code: [Select]
truck.SetTruckCargo(truckFood, 0); // to give it a cargo of food for example

Hope this answers your questions!

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #2 on: September 12, 2007, 03:20:56 PM »
On compiling it gives me the following errors:

undeclared identifiers (Truckf and Trucko)


and:

"left of '.SetTruckCargo' must have class/struck/union type"

It identifies the truckf and trucko when it generates the trucks. It only does this on the cargo setting part.


EDIT: Stupid me... I was using the unit type uncaptioned  :heh:

Now it works.


Thanks BlackBox!
« Last Edit: September 12, 2007, 03:46:57 PM by Hidiot »
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #3 on: September 12, 2007, 04:28:49 PM »
You might also want to consider using the named unit types in the map_id enum. (Check "MapIdEnum.h"). Instead of using "(map_id)1", you can use "mapCargoTruck". Same with most of your other uses of the (map_id) cast.

The only one that really requires the cast, is for the tiger speed modification in the wreckage. If you're using the default tech tree, then you can use the TechID enum found in "OP2Helper\EnumTechId.h". (You're interested in the "techTigerSpeedModification" one). If you're not using the default tech tree then you're pretty much stuck with the way you currently have it, or making your own enum.


I'm sort of wondering if you're using some really old template code because of that actually. Or if you just happened to know enough C/C++ to do that to make the compiler stop complaining after getting an error about it.
 

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Multiple Questions Asked
« Reply #4 on: September 12, 2007, 11:17:45 PM »
Quote
You might also want to consider using the named unit types in the map_id enum. (Check "MapIdEnum.h"). Instead of using "(map_id)1", you can use "mapCargoTruck". Same with most of your other uses of the (map_id) cast.

The only one that really requires the cast, is for the tiger speed modification in the wreckage. If you're using the default tech tree, then you can use the TechID enum found in "OP2Helper\EnumTechId.h". (You're interested in the "techTigerSpeedModification" one). If you're not using the default tech tree then you're pretty much stuck with the way you currently have it, or making your own enum.


I'm sort of wondering if you're using some really old template code because of that actually. Or if you just happened to know enough C/C++ to do that to make the compiler stop complaining after getting an error about it.
It looks like code that was generated by the mapper -- the mapper doesn't have all the names for each of the enum values written it (it just outputs numbers). IIRC the cast was needed for certain things (for example specifying cargo in a cargo truck, you have to use values from the Truck_Cargo enum). I believe in some cases warnings would appear (by default, some MS compiler option I'm sure) as well if you used integer literals instead of the enum symbols which is why the casts are used.

The code generator is incredibly stupid as well. (Basically, it parses a file containing text blocks for each piece of code that it might want to generate, and performs simple text substutition on some piece of data in the input). It doesn't actually parse the code into any type of structure (and really there isn't a huge need to, considering that it doesn't need a lot of complexity).

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #5 on: September 14, 2007, 06:37:13 AM »
Uh... I did use the mapper to set units/buildings/beacons...

I have added more questions in the first post and updated the code with some more modifications I've made.
I have marked answered questions with the "//" thing.
That's pretty much what I'm going to do until I'm finished...


Maybe the answers provided here will help other beginners?
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #6 on: September 14, 2007, 06:30:49 PM »
Code: [Select]
5. How to set lights for every unit to on?

6. When I use the unit class to set the cargo to the trucks, only one of my trucks actually get filled. What should I do?

7.I tried getting the AI to deploy a mine using some code I found in the AI programming,but it doesn't work. The Surveyor seems to survey the beacon, but the miner doesn't build... in fact, it just locks itself there like a ghost, units can get through it. How to fix this?

There is code to turn on the lights for every unit posted somewhere. I think it was in an earlier version of the SDK. I guess it should be added back in. Anyways, it's typically done in one of two ways. For both ways, you call <Unit>.DoSetLights(bool bOn). You can do it after you create each unit (which you are kind of doing), or you can use one of the Enum classes to loop over all the units, and call DoSetLights. With this later method, you won't need to make any change when adding or removing units, although it also offers less control over which units have their lights turned on or off.

Here's what's in your post:
Code: [Select]
TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(126+31, 107-1), 0, mapCommandCenter, 4);
 TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(126+31, 105-1), 0, (mapStructureFactory), 4);
 TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(125+31, 106-1), 0, (mapTokamak), 4);
 TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(127+31, 106-1), 0, (mapTokamak), 4);
 TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(124+31, 105-1), 0, (mapCommonOreSmelter), 4);
 TethysGame::CreateUnit(ConVec, (mapConVec), LOCATION(124+31, 107-1), 0, (mapAgridome), 4);
 TethysGame::CreateUnit(x, (mapEarthworker), LOCATION(126+31, 103-1), 0, (map_id)0, 4);
 TethysGame::CreateUnit(x, (mapRoboDozer), LOCATION(125+31, 104-1), 0, (map_id)0, 4);
 TethysGame::CreateUnit(RM, (mapRoboMiner), LOCATION(124+31, 103-1), 0, (map_id)0, 4);
 TethysGame::CreateUnit(RS, (mapRoboSurveyor), LOCATION(123+31, 104-1), 0, (map_id)0, 4);
 TethysGame::CreateUnit(Trucko, (mapCargoTruck), LOCATION(125+31, 108-1), 0, (map_id)0, 4);
 TethysGame::CreateUnit(Truckm, (mapCargoTruck), LOCATION(123+31, 108-1), 0, (map_id)0, 4);
 TethysGame::CreateUnit(Truckf, (mapCargoTruck), LOCATION(123+31, 106-1), 0, (map_id)0, 4);
...
       ConVec.DoSetLights(1); Truckf.DoSetLights(1); Trucko.DoSetLights(1); Truckm.DoSetLights(1); RS.DoSetLights(1); RM.DoSetLights(1);

The problem with the above, is the ConVec unit reference gets overwritten each time you use it. If you look at the first 5 units created, they all store their reference into the ConVec variable, overwriting any previous value. What you can do is put the DoSetLights calls inbetween the CreateUnit calls, so you set the lights for that unit before you overwrite the reference to it.

[Side note: the Unit class you use in your DLL isn't the same as what's used inside the game engine. The exported class for use in DLLs contains only one 4 byte int, which is just the index of the internal unit it refers to. Overwriting a variable of this type or destroying one has no affect on the actual unit in the game. The internal unit class has room for 120 bytes to store all the unit info, such as it's current position, it's hitpoints, cargo/weapon, current animation, etc.]

The other method [I'm not testing this code, or even compiling it to check for syntax errors] can set the lights of all units, and is basically done like this:
Code: [Select]
Unit unit;
PlayerVehicleEnum vehicleEnum(0);  // Player 0
while(vehicleEnum.GetNext(unit) != 0)
    unit.DoSetLights(true);

You can also throw that into a larger loop that will set the lights for all players. That chunk of code only does it for all units of a given player.


As for setting the cargo, it looks like you're doing it correctly here:
Code: [Select]
 TethysGame::CreateUnit(Trucko, (mapCargoTruck), LOCATION(125+31, 108-1), 0, (map_id)0, 4);
 TethysGame::CreateUnit(Truckm, (mapCargoTruck), LOCATION(123+31, 108-1), 0, (map_id)0, 4);
 TethysGame::CreateUnit(Truckf, (mapCargoTruck), LOCATION(123+31, 106-1), 0, (map_id)0, 4);
       Truckf.SetTruckCargo(truckFood, 1000);
       Trucko.SetTruckCargo(truckCommonOre, 1000);
       Truckm.SetTruckCargo(truckCommonMetal, 1000);
Each created unit is stored into a new unit variable, so you're not overwriting the reference before using it to set the cargo. You could also have interleaved the calls to CreateUnit and SetTruckCargo, which would only require one variable. (But most people find that looks a bit messy, and is harder to find stuff and edit).

Is this the way you had it before when you had problems? Or are you still having problems with this one?


I'm not too sure about the mine building. There is a difference between how a ConVec and a RoboMiner build though. If you mix up the commands, than the unit might go to the wrong place, and not build properly. Remember that a robo miner builds right over the mine, but a ConVec needs to position itself below right of the structure. I remember some oddities where you can get a robo miner to build a mine if you played around with the coordinates to match the location a ConVec needs to be, but this is kind of a hack and is a bit ugly. I think that was using DoDevelop to build a mine, using ConVec coordinates. It looks like you may be using ConVec coordinates with the building group way though. Plus I notice you have a commented out DoDevelop. Maybe try adjusting the coordinates back on top of the mine.

If that fails, I know of another method, but I don't feel it's very appropriate. I think getting the BuildingGroup to work for you is probably the best option.
« Last Edit: September 14, 2007, 06:38:11 PM by Hooman »

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #7 on: September 15, 2007, 07:37:53 AM »
Ok... the light now work.


I tried putting common metals in both Cargo Trucks and it didn't work back then, so I just filled one with ore to get over the problem. But I think I could fill both trucks with metals now with your help.


The DoDevelop doesn't work either. The miner just goes there and does nothing... and it goes directly over the beacon.  I'm going to try moving its location a bit and see what happons....
EDIT: I put the Robo-Miner on the neighboring lower-right tile and it worked this time.
After I finish fixing certain things I did with the tech tree I'll get back to working o nthe map on getting the AI do do stuff. Next thing on the list will be getting 2 trucks to haul ore from the mine to the smelter and get a ConVec to fix those Tokamaks from time to time.


Also, updated the first post with latest code state and question(s).

2nd EDIT: Found the library with the triggers and I think I can use them... I quite understand what they need to work. NOw I need to find out what I should use for randomizing values (I have some ideas, but I want to be sure).
« Last Edit: September 15, 2007, 08:25:17 AM by Hidiot »
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #8 on: September 15, 2007, 02:47:13 PM »
Make sure you use the built in randomization. Just make calls to TethysGame::GetRand(int range). If you try to use some other random number generator in a multiplayer game, the games will desync. It's just good habit for single player, and there's really no reason to use something else anyways.


You can use a MiningGroup to get the ore hauling going. There's some important stuff on that that's already posted somewhere. Basically make sure to set their rect properly, so they go back to the smelter and wait when they're full and you can't store any more ore.
 

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #9 on: September 15, 2007, 03:03:37 PM »
Yeah,uh... how do I set that damn rect? I can't seem to make it work :|
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #10 on: September 16, 2007, 02:38:54 AM »
I believe it's top left inclusive, and bottom right exclusive. So a rect of (0, 0, 0, 0), is an empty rect, and (0, 0, 1, 1) has exactly one tile in it. Put that rect around the dock of your smelter, and make it a little bigger. If you put it elsewhere, then your trucks will go to some weird location to wait when the smelter is full. If you make the rect too small, then the cargo trucks will fight each other to get into the rect, and keep pushing each other out. It's kind of annoying when they don't settle down, or head off to some obscure location.

So yeah, basically find where the dock of the smelter is, and set the rect to start a little bit left and above it, and end a little right and below it, and make sure it's big enough to hold a few tucks around the pad, that aren't on it.
 

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #11 on: September 16, 2007, 04:27:57 AM »
Ah, so the rect determines where the trucks wait... I see... I'll try again and see if I can make it work now...


Also, if the questions I ask here about how to get the AI to do stuff should be moved to the AI Coding forum, I will make a different topic there to continue my... annoying questions.

edit: Hmmm... I'm going something wrong, cause the game crashes because of what I did with the mining. Updated code...
« Last Edit: September 16, 2007, 05:27:30 AM by Hidiot »
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #12 on: September 16, 2007, 01:46:58 PM »
Get rid of your for loop. The while loop is what you need. What you have is a doubly nested loop, and it might be adding the trucks twice. (Or just doing nothing the second time). It's basically running the whole while loop twice (once for j =0, and once for j=1).

If that doesn't work, maybe change the order of the function calls to setup the group. It might matter if you set the rect before or after you add the units to the group. Maybe not though.


Edit: Moved thread to AI Coding forum.
 
« Last Edit: September 16, 2007, 01:49:20 PM by Hooman »

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #13 on: September 17, 2007, 08:37:37 AM »
Updated initial code and questions.

Changing the place where the RECT goes, doesn't work. The game stil lcrshes before it starts.

I really need an example of a working trigger, cause I'm not sure what I'm using is actually working...

And,uh... I think it's better to split this topic... I'll be having AI questions, that's for sure, but I'm still not done with the coding part, mainly disasters.
« Last Edit: September 17, 2007, 08:43:26 AM by Hidiot »
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #14 on: September 17, 2007, 06:04:03 PM »
Ok, I found a slight error in my SDK header file. In case anyone else has run into this, the correct definition of (the last overloaded versions of) MineGroup.Setup is:
Code: [Select]
	void Setup(class Unit mine, class Unit smelter, struct MAP_RECT &smelterArea);

The mine and smelter parameters were reversed. I noticed that when I tried to setup a mining group and the empty cargo truck headed straight to my smelter and just sat there.

Some code from a working example is:
Code: [Select]
	Unit commandCenter;
TethysGame::CreateUnit(commandCenter, mapCommandCenter, LOCATION(40, 7), 0, mapNone, 0);
Player[0].CenterViewOn(45, 10);
Player[0].SetFoodStored(10000);
Player[0].SetScientists(10);
Player[0].SetOre(9000);

Unit mine, smelter, cargoTruck1, cargoTruck2;
TethysGame::CreateBeacon(mapMiningBeacon, 50, 10, 0, 0, -1); // [(50, 10), common, 3 bar, random variant]
TethysGame::CreateUnit(mine, mapCommonOreMine, LOCATION(50, 10), 0, mapNone, 0);
TethysGame::CreateUnit(smelter, mapCommonOreSmelter, LOCATION(40, 10), 0, mapNone, 0);
TethysGame::CreateUnit(cargoTruck1, mapCargoTruck, LOCATION(47, 10), 0, mapNone, 0);
TethysGame::CreateUnit(cargoTruck2, mapCargoTruck, LOCATION(44, 11), 0, mapNone, 0);

MiningGroup &mineGroup = CreateMiningGroup(Player[0]);
//mineGroup.Setup(mine, smelter, MAP_RECT(40, 9, 42, 12));
mineGroup.Setup(mine, smelter, MAP_RECT(40, 10, 43, 13));
mineGroup.TakeUnit(cargoTruck1);
mineGroup.TakeUnit(cargoTruck2);

Just dump that in InitProc to watch that in action. I set the initial ore to 9000, so it'll fill up after the two trucks unload, and then on the second trip they'll stop and wait by the smelter with their loads.

Note that on my first attempt, I choose a bad rect, and so I had one cargo truck circling one side of the smelter. If you make the rect too big, they might stop nowhere particularly close to your smelter.





Volcanos
----------

I just took a look at CES1.dll to see how they did it, and tried something similar.

Place something like this in InitProc
Code: [Select]
	// Make the vent appear in 1 mark (1 mark = 100 ticks)
Trigger &timeTrigger = CreateTimeTrigger(true, true, 100, "VolcanoVent");

Then add something like these two functions to your source file:
Code: [Select]
SCRIPT_API void VolcanoVent()
{
// Make the vent appear (just changes map tiles)
// (Appearance shortly before eruption is detectable)
GameMap::SetTile(LOCATION(165+31, 154-1), 1140);
GameMap::SetTile(LOCATION(165+31, 155-1), 1150);

// Start the eruption on it's way in 1 mark
Trigger &timeTrigger = CreateTimeTrigger(true, true, 100, "Eruption");
}

SCRIPT_API void Eruption()
{
// Mark where the lava can flow
// (do this anytime before the eruption, including from InitProc)
GameMap::SetLavaPossible(LOCATION(165+31, 156-1), true);
GameMap::SetLavaPossible(LOCATION(163+31, 157-1), true);
GameMap::SetLavaPossible(LOCATION(164+31, 157-1), true);
GameMap::SetLavaPossible(LOCATION(165+31, 157-1), true);
GameMap::SetLavaPossible(LOCATION(166+31, 157-1), true);

// Make the vent appear (just changes map tiles)
// (Appearance when eruption is detectable)
GameMap::SetTile(LOCATION(165+31, 154-1), 1147);
GameMap::SetTile(LOCATION(165+31, 155-1), 1158);

// Start the lava flowing  (... in about 10 marks)
// At this point, the earliest disaster warning will sound
TethysGame::SetEruption(165+31, 156-1, 150);
}

In CES, the vent first appears (bubbling flow) at mark 50 (first timer set to 5000), it stays that way for 15 marks (second timer set to 1500), then at mark 65 it switches to the solid flow and sets the eruption. If you had early disaster warning, you'd hear about the volcano at this point. Then after 10 marks (standard time between when disasters are created, and when they first appear, giving time for the player to hear the first and second disaster warnings) the eruption finally happens. There is no way to change that 10 mark delay with the exported functions (but it can be done with some memory editing).

In the example, I wait 1 mark before displaying the lava vent, then another 1 mark before changing the graphic to a solid flow and setting the eruption. After another 10 marks (at mark 12 now) the eruption finally happens.
 

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #15 on: September 18, 2007, 03:53:43 AM »
updated code.

The mine I use here is deployed by a miner, so that code won't work.. .and the LOCATION one didn't work... it just crashes the game if I leave the mining code in...
edit: Tried it with a time trigger, and it will still just crash the game.

Is there no code to get 2 trucks to mine a deployed mine? Must the mine start deployed? If so, how do you get the AI to make working Rare ore mines? :|



I made the volcano erupt and I've set a few tiles for it to move on, but it just won't move... not even on its 2 adjunct tiles specified individually.
« Last Edit: September 18, 2007, 04:14:35 AM by Hidiot »
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #16 on: September 18, 2007, 11:20:16 PM »
It can take a while for the lava to expand. Lava and blight expansion is a little weird. Basically when you set the spread spead, it determines how many tiles per expansion cycle are checked for possible expansion. The number you specify as a parameter gets scaled based on the map size, so it should end up giving the same rate for different sized maps. The tiles that are checked are basically random ones all over the map (they duplicated the random number generator code exactly for much of it). It checks the specified number of tiles, generating the (x,y) coordinates randomly, and sees if it's next to other blight or lava and is a possible point of expansion. That's partly why it grows in weird trails and is sort of vine/finger like sometimes, and not just solid growth outwards in a circle. Plus the random number generator is strangely deterministic and predicatable, to a point of actually being physically noticable. Although it is still possible that it'll be a while before a tile adjacent to your starting position is checked for expansion, so it can take a while to start spreading.

The point of eruption should be marked as a place where lava is possible. If you don't mark it, then lava can't be placed when the eruption happens, and so there will also be no lava to spread to adjacent tiles. The animation of lava comming out of the volcano is not set to lava possible. If you did that, then the animation would get overwritten with the ugly lava tiles, and look really weird.


Btw, the game DLLs tend to use special code and structs for marking lava possible. If you want to do it the same way, or just an example, here's how they do it. It seems to store the info sort of in scanlines, where you'd have a y coordinate, and then two x coordinates for the start and end of the line along that y. It loops between the starting x value and the ending x value, and marks all those tiles with lava possible. It also loops over all these structs, doing that for each of the scanlines, and eventually it has an entire region marked that can be filled with lava. Well, almost, except the game seems to use vertical strips if I remember right instead of horizontal strips, so maybe scanline isn't a good word to use, but it's the same idea. Note that the game just stores the lava possible as a bit in the map data. (The map data has 32 bits per tile. It uses 5 bits per cell type, 11 bits per tile index, 11 bits per unit index of a unit occupying that tile, and various bit flags, in no particular order, for tiles occupied by a building or are otherwise unbuildable, lava possible, actual lava presence, and blight presence). In other words you just need an easy way to set the lava possible bit for the tiles you want, and there is no real reason to do it like it's currently being done. You could even do this in a map editor (that supports it), since the in memory format of the map data is the same as the disk format.

The code would look something like this:
Code: [Select]
struct LavaScanline
{
  int y;
  int x1;
  int x2;
};

void SetLavaPath(int numStructs, struct lavaScanline)
{
  for (i = 0; i < numStructs; i++)
    for (j = lavaScanline.x1, j <= lavaScanline.x2; j++)
      GameMap::SetLavaPossible(j, lavaScanline.y, true);
}



// Defining data (might want to check if this is even valid C++ code)
LavaScanline lavaScanline[] = {
  { 40, 50, 55 },
  { 41, 49, 56 },
  { 42, 47, 56 }.
  { 43, 46, 58 },
  // ...
};

// Maybe use an expression like this to calculate the size of the array,
// and possibly a macro to output both the array size and pointer,
// much like what is used in OP2Helper for the base data
int numLines = size(lavaScanline) / sizeof(lavaScanline[0]);  // sizeOfArray/sizeOfElement
// Calling the function
SetLavaPath(numLines, lavaScanline);


Edit: Oh, and don't forget that I only posted tile indexes for eruptions in one direction. There are other tiles you can use too. Like eruptions on the left, middle, and right side of a volcano.


I'm pretty sure there is a way to do the mining right, like you wanted, but I'll probably have to wait till the weekend to find the time to look into it. Make sure to bump this thread around that time.
« Last Edit: September 18, 2007, 11:22:45 PM by Hooman »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #17 on: September 23, 2007, 01:50:05 AM »
Ok, I got some working example mine code based off of CES1.dll. What they did is setup the mining using a repeated time trigger. The time trigger would fire every 97 ticks, and would check for the presence of the right kind of smelter. Once the smelter existed, they setup the mining group and destroyed the time trigger (or reused it to trigger a different even, thus essentially destroying it for it's previous purpose).

I don't think the whole trigger setup is actually necessary, but I figure I'll post code that does it that way. Mostly because I was following how CES1.dll did it, and when I got it working the code was in that form.

Also, the current form of the code uses global objects in the DLL, which will need to be saved correctly if the user Loads/Saves the level. If you didn't setup the SaveRegions properly while using global variables, they wouldn't have their values restored when you loaded the game, and who knows what would happen then. Things might just stop working, or the game might crash. At any rate, I get to demonstrate an example of that too.

One fine point to note about variables in C++ is that the language doesn't specify the order of variables in memory in relation to the order they are declared. They usually get stuck in memory in the same order they are declared, but this is not a requirement, and there are some research projects that attempt to reorder the variables in memory for various purposes. Sometimes it's to do with speed and cache locality, and sometimes it's for security reasons. There is one way to force the ordering however. You just put the variables into a struct. That way you know exactly where all the variables go, and can easily get the size of all of them. This is important for SaveRegions since we just get to save one contiguous chunk of memory, and we'll want exactly the variables we specify to be in that chunk.


The code:
Code: [Select]
// Global Script variable layout
struct ScriptGlobal
{
BuildingGroup buildGroup;
Trigger mineGroup1TimeTrigger;
Trigger mineGroup2TimeTrigger;
Unit supernovaLynx;
};

// Global Script variables
ScriptGlobal scriptGlobal;


int InitProc()
{
// Bulldoze smelter area for faster testing
int i, j;
for (j = 4; j < 14; j++)
{
  for (i = 41; i < 50; i++)
  {
   GameMap::SetCellType(LOCATION(i, j), 21);
   GameMap::SetTile(LOCATION(i, j), 1670);
  }
}

// Set initial colony status
TethysGame::ForceMoraleGreat(-1);
Player[0].MarkResearchComplete(techEfficiencyEngineeringEden);
Player[0].MarkResearchComplete(techCyberneticTeleoperation);
Player[0].GoAI();
Player[0].GoEden();
Player[0].CenterViewOn(45, 10);
Player[0].SetFoodStored(10000);
Player[0].SetWorkers(10);
Player[0].SetScientists(10);
Player[0].SetOre(10000);

// Create an initial base to support the testing
Unit commandCenter, structureFactory, vehicleFactory, tokamak, commonStorage;
TethysGame::CreateUnit(commandCenter, mapCommandCenter, LOCATION(40, 7), 0, mapNone, 0);
TethysGame::CreateUnit(structureFactory, mapStructureFactory, LOCATION(36, 7), 0, mapNone, 0);
TethysGame::CreateUnit(vehicleFactory, mapVehicleFactory, LOCATION(36, 11), 0, mapNone, 0);
TethysGame::CreateUnit(tokamak, mapTokamak, LOCATION(36, 2), 0, mapNone, 0);
TethysGame::CreateUnit(commonStorage, mapCommonStorage, LOCATION(39, 10), 0, mapNone, 0);
TethysGame::CreateUnit(commonStorage, mapCommonStorage, LOCATION(41, 10), 0, mapNone, 0);

TethysGame::CreateUnit(scriptGlobal.supernovaLynx, mapLynx, LOCATION(45, 4), 0, mapSupernova, 0);

// Create a mining beacon
TethysGame::CreateBeacon(mapMiningBeacon, 50, 10, 0, 0, -1); // [(50, 10), common, 3 bar, random variant]

// Create a building group
BuildingGroup &buildGroup = CreateBuildingGroup(Player[0]);
buildGroup.SetRect(MAP_RECT(33, 5, 39, 10));
buildGroup.TakeUnit(structureFactory);
buildGroup.TakeUnit(vehicleFactory);
buildGroup.RecordBuilding(LOCATION(45, 10), mapCommonOreSmelter, mapNone);
buildGroup.SetTargCount(mapConVec, mapNone, 1);
buildGroup.RecordBuilding(LOCATION(50, 10), mapCommonOreMine, mapNone);
buildGroup.RecordBuilding(LOCATION(45, 6), mapCommonOreSmelter, mapNone);
buildGroup.RecordVehReinforceGroup(buildGroup, 1000);

// Save building group reference in global
scriptGlobal.buildGroup = buildGroup;

// Check periodically for the smelters being built
scriptGlobal.mineGroup1TimeTrigger = CreateTimeTrigger(true, false, 97, "SetupMining1");
scriptGlobal.mineGroup2TimeTrigger = CreateTimeTrigger(true, false, 97, "SetupMining2");

return 1; // return 1 if OK; 0 on failure
}

void AIProc()
{
if ((TethysGame::Tick() > 7500) && (scriptGlobal.supernovaLynx.IsLive()))
{
  scriptGlobal.supernovaLynx.DoSelfDestruct();
}
}

void __cdecl GetSaveRegions(struct BufferDesc &bufDesc)
{
bufDesc.bufferStart = &scriptGlobal;
bufDesc.length = sizeof(scriptGlobal);
}

int StatusProc()
{
return 0;
}

SCRIPT_API void NoResponseToTrigger()
{
}


SCRIPT_API void SetupMining1()
{
Unit smelter;
PlayerBuildingEnum buildingEnum(0, mapCommonOreSmelter);
LOCATION smelterLoc(45, 10);

// Check for a smelter at the desired location
for (;;)
{
  if (buildingEnum.GetNext(smelter) == 0) return;
  LOCATION &temp = smelter.Location();
  if ((temp.x == smelterLoc.x) && (temp.y == smelterLoc.y)) break;
}

// Setup a mining group, which will be later reinforced
MiningGroup &mineGroup1 = CreateMiningGroup(Player[0]);
mineGroup1.Setup(LOCATION(49, 10), LOCATION(46, 11), mapCommonOreMine, mapCommonOreSmelter, MAP_RECT(45, 10, 49, 14));
mineGroup1.SetTargCount(mapCargoTruck, mapNone, 2);

// Make the building group reinforce the mining groups
scriptGlobal.buildGroup.RecordVehReinforceGroup(mineGroup1, 100);

// Done with the trigger, make sure we don't reenter
scriptGlobal.mineGroup1TimeTrigger.Destroy();
}

SCRIPT_API void SetupMining2()
{
Unit smelter;
PlayerBuildingEnum buildingEnum(0, mapCommonOreSmelter);
LOCATION smelterLoc(45, 6);

// Check for a smelter at the desired location
for (;;)
{
  if (buildingEnum.GetNext(smelter) == 0) return;
  LOCATION &temp = smelter.Location();
  if ((temp.x == smelterLoc.x) && (temp.y == smelterLoc.y)) break;
}

// Setup a second mining group, which will be later reinforced
MiningGroup &mineGroup2 = CreateMiningGroup(Player[0]);
mineGroup2.Setup(LOCATION(49, 10), LOCATION(46, 7), mapCommonOreMine, mapCommonOreSmelter, MAP_RECT(45, 7, 49, 11));
mineGroup2.SetTargCount(mapCargoTruck, mapNone, 2);

// Make the building group reinforce the mining groups
scriptGlobal.buildGroup.RecordVehReinforceGroup(mineGroup2, 100);

// Done with the trigger, make sure we don't reenter
scriptGlobal.mineGroup2TimeTrigger.Destroy();
}

Description:
Player 0 is set to GoAI, so you can't issue any commands to the units. You start with a base consisting of a CC, SF, VF, Tok, and two Common Stoage. I also added a nova lynx that is set to self destruct at Mark 75 (see AIProc). I added a variable to the globals section to keep the reference around for the self destruct command.

I added a BuildingGroup to setup the following. It builds the kits for two Common Ore Smelters, it builts a Robo Miner, it deploys the Robo Miner, and it builds a Convec. The Convec will pick up the kits from the SF and deploy the two Common Ore Smelters.

The two time triggers setup two MiningGroups, one for each smelter, and they share a common mine. The time trigger checks periodically for the smelter to see when it's there (initialled placed), and once it detects this, it sets up the mining groups, sets a target count of 2 cargo trucks for each group, and sets the BuildingGroup to reinforce the MiningGroups, so those cargo trucks will be built as they are needed at the VF.


The second smelter should finish building at about mark 70, and everything will be harvesting. Then at mark 75, the nova lynx explodes. This should take out the top smelter, and possibly a cargo truck or two. When this happens, the SF should start to build a new Common Ore Smelter kit, and the Convec should move to the pad to await pickup, and the VF should start to replace any lost Cargo Trucks. Once the kits is ready, the Convec will deploy it, and when it's finished building the cargo trucks will resume harvesting. This all happens automatically with the groups. Basically you just set them up and forget about them. As long as the building group is capable of replacing lost buildings and groups, it will. And as long as you have a mine and smelter operational at the correct location, mining will continue.


There is one important point about the mining Setup call. The locations you specify MUST be the EXACT coordinates of the DOCKs. These will not usually be the same as the coordinates used to build them. You must pay attention to where the docks are, or the trucks won't head to their destination. The internal code for the Groups actually does an exact compare on the coordinates it's given in Setup, and the dock location (using the internal function GetDockLocation) to see if they are an exact match.


I suspect, but have not yet tested, code that calls Setup using the overloaded location form will work even if you call setup well before the building is built. It seems like it should work that way, since when the smelter is destroyed and rebuilt, mining will continue with the new smelter. If this is the case then you can really get away without using the time triggers and without using the global variables. You just have to make sure the coordinates are the exact coordinates of the dock for each building.
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #18 on: September 23, 2007, 06:27:15 PM »
Ok, I tested out my idea, and it works just fine if you stick everything in InitProc. It works exactly the same in all cases (mostly). The only difference is the cargo trucks get built a little earlier, since the build group is aware it needs to build them earlier. I believe it selects a random order to build the vehicles in. This means the cargo trucks can be built before the convec or robo miner, which is maybe a little undesirable. That might slow down the overall buildup if it doesn't built what it needs by the time it needs it. I'm pretty sure you can get the old behavior by just putting the SetTargCount or RecordVehReinforceGroup calls into a time trigger, or delaying them by some other means. The Setup function seems quite happy to be run at any time.


This does sort of make setting up multiple bases and expansion somewhat easier. Just need to time when the initial units needed to start the building process are added to the groups.


For the new test, I modified the end of InitProc to be the following:
Code: [Select]
	// Create a building group
BuildingGroup &buildGroup = CreateBuildingGroup(Player[0]);
buildGroup.SetRect(MAP_RECT(33, 5, 39, 10));
buildGroup.TakeUnit(structureFactory);
buildGroup.TakeUnit(vehicleFactory);
buildGroup.RecordBuilding(LOCATION(45, 10), mapCommonOreSmelter, mapNone);
buildGroup.SetTargCount(mapConVec, mapNone, 1);
buildGroup.RecordBuilding(LOCATION(50, 10), mapCommonOreMine, mapNone);
buildGroup.RecordBuilding(LOCATION(45, 6), mapCommonOreSmelter, mapNone);
buildGroup.RecordVehReinforceGroup(buildGroup, 1000);

// Setup a mining group, which will be later reinforced
MiningGroup &mineGroup1 = CreateMiningGroup(Player[0]);
mineGroup1.Setup(LOCATION(49, 10), LOCATION(46, 11), mapCommonOreMine, mapCommonOreSmelter, MAP_RECT(45, 10, 49, 14));
mineGroup1.SetTargCount(mapCargoTruck, mapNone, 2);
// Make the building group reinforce the mining groups
buildGroup.RecordVehReinforceGroup(mineGroup1, 100);

// Setup a second mining group, which will be later reinforced
MiningGroup &mineGroup2 = CreateMiningGroup(Player[0]);
mineGroup2.Setup(LOCATION(49, 10), LOCATION(46, 7), mapCommonOreMine, mapCommonOreSmelter, MAP_RECT(45, 7, 49, 11));
mineGroup2.SetTargCount(mapCargoTruck, mapNone, 2);
// Make the building group reinforce the mining groups
buildGroup.RecordVehReinforceGroup(mineGroup2, 100);

return 1; // return 1 if OK; 0 on failure

I also deleted the two trigger callback functions, and gutting a few members from the ScriptGlobal struct, since I wasn't using them anymore. I only left the nova lynx entry, since I still wanted to test what happens when the smelter is destroyed.
 

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #19 on: September 24, 2007, 01:37:27 AM »
I'll have to get to integrating these things in my map when I get the time :| I was rather sick this weekend and couldn't do very much...

EDIT: I finally got what I was doing wrong. The Setup on the mining group caused the game to crash, cause I didn't specify what buildings are at the locations I fed it.

I've found and am looking into McShay's demo for a working AI and am progressing slowly now. Got the code a tad tidier, setup the groups and preparing to test if I can make the AI build a new structure.

More work to be done... when I get the time :|


Almost forgot... updated code and a new question... more questions that is...


next EDIT: More updating on the code. I go the AI to expand a bit, so I can finally make it build a main base. Will be looking into making it build a 2nd base tomorrow. I've copied something that I've put in the AIProc from Mcshay's working AI example that I will try to edit and adapt tomorrow to my map.

Until I got the AI base planned, it will look and be built very unrealistically.  Once I get it looking and working like a base, I will ponder more on the time certain building should be built.
« Last Edit: September 29, 2007, 11:23:27 PM by Hidiot »
"Nothing from nowhere, I'm no one at all"

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #20 on: October 14, 2007, 09:02:40 AM »
Ok, so it's been a while now, progress has been and will be slow. I will update the code later, cause I'm out of time right now.

More questions added in the hope I won't have to try my luck at coding what I need.
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #21 on: October 14, 2007, 11:25:01 AM »
Quote
12. If the AI has a structure kit pre-placed in its SF storage, will a ConVec take the placed one if it has to be build using the normal RecordBuilding, or will the SF build another kit and the ConVec use the newly built kit?

No. It will just build a new one to place. Not too sure why, but it only uses stuff that it produces itself.


Quote
13. Past attempts on avoiding the RecordBuilding function to help speed up the process of building the AI base has come to a stand-still, as I can't figure out a way to make it work. I've started using the IUnits header and have got the SF to produce a basic lab kit. But then's the problem... how do I get a next ConVec that has no cargo to the dock and load the SF cargo, whilst building a new kit in the meantime.

Yes.... That is quite a problem. It's easy to gloss over it with high level details, but when it comes to implementing something like that, it's usually somewhat more diffifcult in practice. There are a lot of details that you'd have to deal with. I'd say just figure out how to use RecordBuilding, since that's only one line, and doing it another way could easily take a few hundred to get it working the way you want it to. Granted, it could be a little more streamlined than the BuildingGroup works. I've noticed it won't start building the next structure kit until the current kit is placed. But still, I have no problem relying on this one considering the alternative.


Quote
14. How big does the reinforce group value difference hav eto be to force the VF to build something before the other things? Using the reinforceVehicleGroup to get the VF to make stuff, it won't always make both the Cargo Trucks it has to, building an Earthworker or a Repair Vehicle in the meantime...?

I don't think you can force that for certain. It would probably just change the probabilities of what will be built next. What you can do is delay setting the target counts for the vehicles that you don't want until later. Just setup a time trigger, and after enough time has passed to build the important stuff first, add the target counts for the other vehicles.

You can do something similar with RecordBuilding if you want the base to be built in steps. CES does that. The initial base it built up a little further at successive time intervals. After each of those marks, the SF, convecs, and earthworkers start on the next expansion part.
 

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #22 on: October 14, 2007, 01:48:38 PM »
ok, so about 13... I'm willing to give it a try on the complicated part. If I can get an example running, then maybe it will be easier in the future.
"Nothing from nowhere, I'm no one at all"

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Multiple Questions Asked
« Reply #23 on: October 15, 2007, 02:39:04 AM »
OK, but you're on your own! :P

Honestly, I don't think the built in method is that bad. Besides, you don't really want something that's too perfect usually, just because the player can't play that perfectly. It slows down the computer a bit and makes it a bit more fair. Mind you, a practised player could probably build a little faster provided they didn't have too many other thigns to worry about.


I know how to issue the orders to the units. I also know how to check most properties of units. You can also use enums to find the units. However, if you're constantly scanning for units, and testing for all the properties you want, it's gonna be kind of slow. I think what you'd really need is a good way to maintain an active list of units, and keep track of what they're doing. I don't currently know of a very good way of doing this. I've considered this problem to some extent though. What might be a partial solution, is to use the ScStub system, possibly creating your own objects compatible with the internal ones. They seem to have callbacks for certain events which you could then hook into. The main one I was interested in was the event that's raised when a unit is produced at the vehicle factory. This seems to be used to add units to the appropriate group (MiningGroup, BuildingGroup, etc.) when they are built. I've never actually written any code to play with this idea however. Plus, there is the other side of things, and keeping track of units that get destroyed. I don't currently know of a good way of doing that. I would hope there is something better than scan the list periodically for ones that died, because they could just get replaced by a new unit if the timing is just right. I guess the groups need some way of keeping track of this. I suppose I could look into it sometime.
« Last Edit: October 15, 2007, 02:40:04 AM by Hooman »

Offline Hidiot

  • Hero Member
  • *****
  • Posts: 1018
Multiple Questions Asked
« Reply #24 on: October 16, 2007, 10:25:32 PM »
Finally updated the script... worked on it, doing stuff and trying new things...

But I now run into a major problem. and that's question 15.

Tried removing the Player[1].GoAI, but that didn't help.


Also, I'm not sure I fully got what you said... And I doubt I can make it work on my own :/ I just want something that works faster than the RecordBuilding and that allows me to replace buildings when needed (for example, replace residences or the basic lab with med centers).
"Nothing from nowhere, I'm no one at all"