Author Topic: Learning AI  (Read 4621 times)

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« on: January 07, 2005, 06:59:42 PM »
Hey guys (and girls)  :P

I've collected a few items that i have coded, tested & worked out about the AI routines. Some of those were obscure, but when you take a good look at it, they're not all that hard to understand;

First things first:  Building construction
I think most of you figured this out by yourselves, using this forum as a guideline, but here it is anyway:
  • create a new buildingGroup
  • put at least 1 convec and 1 SF in it using TakeUnit
  • record as many buildings as you like (i haven't found the limit yet) using RecordBuilding. The (x,y) location of any building is determined by the point where the 2 tubes that come out of it meet ("underneath" the building, where you cannot actually see it)
  • set a MAP_RECT where the vehicle(s) of your buildingGroup will moveto when they have nothing to do. It would be a good idea to have them close to your SF - but don't make the rect too small to manouver
  • Earthworkers can be added to the buildingGroup (using TakeUnit again) if you want to also construct tubes and/or walls
  • to have your earthworkers reconnect buildings that are no longer connected, due to a destroyed building somewhere in the line, record tubes THROUGH buildings; that way your earthworker will always build those missing tubes. If there's a building in that spot already, it won't cause any problems.
  • be advised that the order in wich things are being build is determined by the order you've recorded them. This also means, that if a couple of buildings gets destroyed they will be rebuild in the original order set out with RecordBuilding. The same goes for tubes/walls
  • finally, you can ad a Robo Miner to your group, and record a mine - the miner will build the mine for you
Now, some remarks about building construction:
  • A remark about the construction order: be sure to record a mine & smelter early - even if those buildings are present when the mission starts, because you've used CreateUnit to put them onto the map: ANY building in the recorded list that already exists, will be ignored (until it gets killed). Imagine: you record a VF before you record any smelters. Now if both get destroyed, your ai might use its last ore to build the VF, and have nothing left to construct the smelter kit. A simular problem exists when you've recorded buildings that require rare ore, even though you might have 50,000 common ore available, a simple guardpost recorded right after that will not be built until the ai constructs the previous building. Keep that in mind !
  • the SF will automaticly build the kits as the convec(s) need them. If one convec is moving towards its construction location, another kit will not be started. As soon as that convec starts its actual construction, only then will the SF start building the kit, and the next convec will move towards the SF (if there's still convecs in your group waiting for instructions)
  • you can speed up your ai, by putting ready-made kits in the SF, using SetFactoryCargo - careful, if used with anything else but an SF (or SpacePort), op2 will crash!
I'll put on some more next time ,, about VehReinforce  ;)


For now, happy testing!  (replies are welcome)
 
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #1 on: January 08, 2005, 06:16:30 PM »
As i've promised: Vehicle Reinforce & working with Groups !
Many people have been wondering how this actually works (including myself  :lol:) and i'm happy to inform you that i've worked out the details. More below !

First thing you need to do, after creating a new building- mining- or fightGroup, is assign units to it using TakeUnit. A buildingGroup should have a structure factory and one or more convecs, a miningGroup should have cargo trucks, and a fightGroup is of course suppose to have tanks. Nothing changes so far.  

You can now set the target count for your groups, using SetTargCount. This code will instruct the AI to keep your fightGroup at 3 RPG-lynx at all time; BUT, nothing happens until you've used the VehReinforceGroup function (this has advantages):
Code: [Select]
 FightGroup1.SetTargCount(mapLynx,mapRPG,3)
After you've constructed a vehicle factory, create a NEW buildingGroup (this is very important - i'll explain why this is later) and assign the VF to it, again using TakeUnit:
Code: [Select]
Unit VF;
TethysGame::CreateUnit(VF,mapVehicleFactory,LOCATION(40,10),0,mapNone,0);
VFgroup1=CreateBuildingGroup(Player[0]);
VFgroup1.TakeUnit(VF);
VFgroup1.RecordVehReinforceGroup(FightGroup1,0);
This code will instruct the new group (VFgroup1) to reinforce any lost units from FightGroup1. Whenever a unit is missing in the destinationgroup, the VF will create the unit, and add it automaticly to the fightGroup.
Using UnRecordVehGroup you can remove FightGroup1 from the reinforce LIST - yes LIST. Everytime you use RecordVehReinforceGroup you will add a new group to be reinforced by this VF. This is where the second param comes in: it is the construction priority.
Now using this priority might seem a bit odd at first, but you can add several fight/mining/buildingGroups to the VF reinforcelist, and subsequently set different priorities to them. For example:
Code: [Select]
VFgroup1.RecordVehReinforceGroup(buildingGrp1,100);
VFgroup1.RecordVehReinforceGroup(miningGrp1,200);
VFgroup1.RecordVehReinforceGroup(fightGroup1,4000);
This code will construct all the tanks, trucks, convecs and whatever else you've set with SetTargCount, and it will do so randomly BUT, it'll randomise with those priorities. 0 is the lowest priority, and MAX_INT would be the highest. In the example above chances are units for fightGroup1 will be produced first, then miningGrp1, and finally buildingGrp1. Units are created one at a time, and the priorities are randomised every time. It's all a matter of chance: if i create a miningGrp1 with 10 trucks, and the fightGroup1 with 10 lynx, chances are pretty good, before the 10th lynx is produced i'll have a couple of trucks ready. I hope you understand this - the order set out with the priority param, is not absolute but more like a guideline for the AI.

Now, the other side of the reinforce story is the targCounts: you can create more than 1 targCount for a group!  So, if you have a fightGroup and you want it to have 3 RPG-lynx, 6 EMP-panthers and 2 ESG-tigers, you can program like this:
Code: [Select]
FightGroup1.SetTargCount(mapLynx,mapRPG,3);
FightGroup1.SetTargCount(mapPanther,mapEMP,6);
FightGroup1.SetTargCount(mapTiger,mapESG,2);
There is no way to set priorities within a group. Group members will be created completely by random.

A final note (trust me on this one): for basic AI programming, you can suffice with adding the VF into the buildingGroup, and set the ReinforceGroup to point to itself. But when you are going to "mess around" with groups, you will get into trouble later, as vehicle counts will get lost, and the AI will not reinforce those groups correctly anymore, in which it is part itself, THEREFOR i suggest you create a new buildingGroup wich contains only the VF. This, of course, is not necessary if your VF-group does not have any TargCount set, or when 2 VF's keep each other up.


Next time:... uhm.. ?  what do you guys wanna know ? more on triggers ? Let me know!
« Last Edit: April 08, 2005, 03:42:35 PM by Eddy-B »
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #2 on: January 10, 2005, 05:56:56 PM »
Okay, since i got no requests  :P  .. working with Groups

First of all: i'm not saying this is THE way to do it, but it is MY way. if you like it, follow my lead - if you don't: submit a better way! If you don't have a better (or easier) way, don't complain...

okay; here we go:
Ever had your AI attack enemy units, but all their tanks are spreading around, not really co-ordinating their attack ? ..can't keep track of all your units ? well .. i had these probs, but i managed to solve them... somehow.

When my DLL gets loaded, i call a procedure SetupGroups from the InitProc rigth at the start. In this procedure i create all fight/mining/buildingGroups i need for the whole mission. I found this to be the safest way, since accessing a non-initialized group causes op2 to crash real bad { exception - read of address... }

So, you'd see something like this in there (actual code from a working mission):
Code: [Select]
waitGrp=CreateFightGroup(Player[1]);
waitGrp.SetRect(MAP_RECT(40,4,60,12));
waitGrp.SetLights(1);

moveGrp=CreateFightGroup(Player[1]);
moveGrp.SetRect(MAP_RECT(80,24,90,32));
moveGrp.SetLights(0);

attackGrp=CreateFightGroup(Player[1]);
attackGrp.SetRect(MAP_RECT(40,4,60,12));
attackGrp.DoAttackEnemy();
attackGrp.SetLights(1);
Now, obviously these are 3 fightGroups, but you should initialise any and all group types here. I've had it happen too many times i try to find out what a certain group is doing (specificly fightgroups), in a timetrigger, when they're not even created yet. So, this way, i won't get any of those problems anymore!

The trick about creating a "smart" AI, is to have'm create the units one-by-one at the VF (as i've demostrated in my earlier "Lesson"); then move its tanks to a safespot when your group is of sufficient size. To make it even smarter: my AI's will start creating a NEW attack group, the moment the 'old' group moves off to its safespot, in fact the VF is continuously producing units and creating new attack groups in this way.

When your group is the correct size (say 3 units), you can move them over to a new group using TakeAllUnits. For this you will need 2 groups to make it work:
Code: [Select]
moveGrp.TakeAllUnits(waitGrp);
This will automaticly move all units from waitGrp to moveGrp. If you'd use TakeUnit to move them one by one, it would work also, BUT the units would be part of both groups, so you'd have to use RemoveUnit to remove it from the waitGrp!  When using TakeAllUnits, the removal actions is included in the fuction call.

AFTER the call, moveGrp will have the 3 tanks, and waitGrp will be empty again. This by itself is a trigger for the VehReinfroce -if used correctly- to produce new units into waitGrp:
Code: [Select]
waitGrp.SetTargCount(mapLynx,mapRPG,3);
VFgroup.RecordVehReinforceGroup(waitGrp,0);

The above code could already be set up in the SetupGroups procedure (it should -coz it's group settings).
The new group of 3 (moveGrp) will be going to its Rect as coded into the moveGrp initialization. This means, you do not have to move the units by yourself with DoMove!  You might also have noticed i've set the lights of moveGrp to zero, wich means they're OFF .. this is a sneak attack.

Once the 3 units get to their destination rect, you can let them wait for some time, and then move those 3 units to the third fightGroup:
Code: [Select]
attackGrp.TakeAllUnits(moveGrp);

As you might have guessed, this'll remove all units from moveGrp, and put them into attackGrp. So far i have not yet issued 1 single command to my tanks! All i've done is move the units from one group to another. All the commands are precoded into my SetupGroups: being part of the group will automaticly issue the commands to the units. So, your 3 tanks should now be on their way (with lights on!) to the nearest enemy unit and attack it. Using DoAttackEnemy, as i have, tells them to attack any military unit. Next to tanks, this includes tokamaks, factories, GPs etc. Agridomes & residences for example are left alone.

When no more enemies can be located, the group will move to its SetRect, in this case back to our base.

---------------------------------------------------------------------------------------------------------------------------
So far our lesson for today;
next time i will explain HOW to figure out when your groups are up to strength and at the right spot.
« Last Edit: April 08, 2005, 03:43:20 PM by Eddy-B »
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #3 on: February 16, 2005, 01:26:17 PM »
Hey there !!

..after 'some' silence i decided that i'd have to add something again. Well, that and haxtor asking too many questions .. lol  :P juz kidding!

So, the song-list:  HOW the heck do i play those in-game songs ?
The songs are obviously set up using TethysGame::SetMusicPlayList
The procedure accepts 3 params: 2 indexes (or was indices?) and a list of SongIds. Now to start with the latter: the SongIds are nothing more then an array of song IDs .. simple isn't it?

I've added the following lines to my Outpost2DLL.h, but until such time that a uniform NEW version of this header becomes available - it is subject to change, so don't complain if your own source files won't compile anymore after you've downloaded any new version of the mentioned header. In other words: no guarantees and no liability crap about this posting, okay?

Here we go (songlist has been edited; this is the way you will find it in the new SDK in enums.h):
Code: [Select]
enum SongIds {
songEden11= 0x00,
songEden21,
songEden22,
songEden31,
songEden32,
songEden33,
songEP41,
songEP42,
songEP43,
songEP51,
songEP52,
songEP61,
songEP62,
songEP63,
songPlymth11,
songPlymth12,
songPlymth21,
songPlymth22,
songPlymth31,
songPlymth32,
songPlymth33,
songStatic01,
songStatic02,
songStatic03,
songStatic04,
songStatic05
};
The way you use those enums is by creating an array of type enum SongIds (as expected):
Code: [Select]
SongIds songs[]={ songPlymth11, songPlymth12, songEP51, songEP52 };
This will set up the game to play those 4 songs (Plymth11.raw; Plymth12.raw; EP51.raw and EP52.raw).
The other 2 params are important:
Param1 sets the array size: in our case it should be 4
Param2 tells op2 where to re-start playing after it's done. To explain this i've put 2 songtypes into the SongIds list. When i set param2 to 0 it'll play all 4 songs, then return to song ID#0 (=Plymth11) and start over again etc...  When i set param2 to 2 it'll first play all 4 songs, then return to song ID#2 (=EP51) and continue from there..indefinately!  This gives the player those 2 easy/soft songs, and then go on to the uptempo EP-songs, and never go back again (unless you restart the mission)

This should play the songs i've explained above:
Code: [Select]
TethysGame::SetMusicPlayList(4,2,songs);

...next time .... ?  who knows - drop me a message what you'd like to learn.
If i don't know how it works myself: i'll be honest about that, and/or i'll try to figure it out, and let you know after a while!

Entil-zha !
« Last Edit: April 08, 2005, 03:38:34 PM by Eddy-B »
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #4 on: April 08, 2005, 04:55:26 PM »
Hi again, all you (new) coders !!

Since i keep seeing this question over and over, i thought it might be a good idea to shed some light on this topic: TRIGGERS

To learn about HOW they work, i'll explain a little bit of the game-engine, so you'd understand the trigger-system.
Outpost 2 works with whats called "Ticks" and "Time";  Time is what you see in the game, when you click the communications tab. Ticks are only used internally by the game-engine. Thedre are 100 ticks in 1 time-mark. Every move or other graphic change you see on the screen is done 1 tick at a time. For example: turning a truck 180 degrees takes about a dozen ticks. The faster you set your game-speed, the faster the ticks will go by. The game-engine has to perform a LOT of things in 1 game-tick; not just anything that's been programmed in the mission DLL but also ALL graphics changes you see on the screen, including drawing up a new image if you move the view-box.  This is the reason why slower pc's won't show much change in gamespeed if you move from let's say speed 6 to speed 10. This is because at speed 6 your pc is running at its peak already, and simply cannot cope with more game-ticks in a second.

Now, the easiest trigger to create is the TimeTrigger: it triggers at a certain time.
Code: [Select]
CreateTimeTrigger(int boolEnable, int boolNoRepeat, int time, char const *triggerFunction);
I'll go through all the params:[ol type=\'I\'][li]int boolEnable: Make sure this param is 1 to enable your trigger. Setting it to 0 will disable your trigger, and it doesn't make sense to create a trigger that won't work;[/li][li]int boolNoRepeat: this one is tricky: set it to 1 to run only 1-time (=no repeat). Set it to 0 to run indefinately. More on this later;[/li][li]int time: The tick when this trigger should fire, starting from NOW. When set in InitProc, the trigger will fire when the tick is reached. If set at any other time in the game, it fires at 'time' gameticks after the moment it was created;[/li][li]char const *triggerFunction: This is the function that is called when the trigger is fired, in this case: the 'time' is reached.[/li][/ol]The items marked in blue above appear in ALL the trigger functions, and are key-params. The 'time' param only applies to a TimeTrigger.

Example:
Code: [Select]
CreateTimeTrigger(1,1,200,"Attack");
This code will create a trigger that will call the function Attack() one time only, after 200 gameticks. IMPORTANT: 200 game-ticks is only 2 timemarks. Everytime you use this, you have to multiply the time x100 !!
The function Attack should be defined like this:
Code: [Select]
SCRIPT_API void Attack()

[DoHtml]
<table><th>Trigger function
</th><th>It fires when...
</th>
<tr><td>CreateBuildingCountTrigger&nbsp;&nbsp;&nbsp;&nbsp;</td><td>a certain number of buildings is reached</td></tr>
<tr><td>CreateVehicleCountTrigger</td><td>a certain number of vehicles is reached</td></tr>
<tr><td>CreateCountTrigger</td><td>a certain number of units is reached</td></tr>
<tr><td>CreateAttackedTrigger</td><td>a unit is attacked (not functioning)</td></tr>
<tr><td>CreateDamagedTrigger</td><td>a certain part of a group is killed</td></tr>
<tr><td>CreateEscapeTrigger</td><td>a unit escapes to a given region</td></tr>
<tr><td>CreateKitTrigger</td><td>a certain kit is ready</td></tr>
<tr><td>CreateMidasTrigger</td><td>midas condition is reached</td></tr>
<tr><td>CreateOnePlayerLeftTrigger</td><td>uhm.. make a guess</td></tr>
<tr><td>CreateOperationalTrigger</td><td>a certain number of buildings is operational</td></tr>
<tr><td>CreatePointTrigger</td><td>any unit moves over a point</td></tr>
<tr><td>CreateRectTrigger</td><td>any unit moves into a region</td></tr>
<tr><td>CreateResearchTrigger</td><td>some research is completed</td></tr>
<tr><td>CreateResourceTrigger</td><td>a certain amount of resources is reached</td></tr>
<tr><td></td></tr>
<tr><td>CreateEvacTrigger</td><td>unknown/untested at the moment</td></tr>
</table>[/DoHtml]

int boolNoRepeat: Be careful using this. In a timeTrigger it creates a trigger everytime the set timeticks have passed. If i changed the boolNoRepeat from 1 to 0 in the example way above, the trigger would fire every 2 gamemarks. BUT; if i use it with other triggers, it might fire every single gametick when a condition is met! eg. if you'd use this in the OperationalTrigger, it would first fire when the building in that trigger becomes operational, and would then fire every gametick from that point on, making your DLL run the triggerFunction code 100 times per time-mark!


Good luck with those triggers. Oh, don't ask about the set trigger. I DO know what it does and how it should be used, but if you're not 100% comfortable yet with all the triggers, you certainly won't be able to use the SetTrigger...

[EDIT] CreateAttackedTrigger doesn't seem to work; using it will crash op2. Also, not a single original mission uses this trigger, so its code is either never written, or it just wasn't tested. Either way: DONT USE IT!

[EDIT2] I've been testing and debugging CreateSpecialTarget (and GetSpecialTargetData and Unit::ClearSpecialTarget), and it seems OP2 has its shortcomings. It is kinda buggy, and i won't explain it here. So please don't use it as well

Entil-Zha!
« Last Edit: May 08, 2005, 04:08:24 PM by Eddy-B »
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Punboy

  • Full Member
  • ***
  • Posts: 142
Learning AI
« Reply #5 on: May 07, 2005, 10:23:15 PM »
Ok, but how would one USE the special triggers. Like with CreateRectTigger, how does one specify a rect?

And is there any way to cause the trigger to pass data to the trigger? Similar to the way Signals/Slots work in Trolltech's Qt? I want the trigger to know what type of vehicle, etc entered the rect.

Could you maybe provide some sample code on how to use these different triggers?
Help control the human population. Have your spouse spayed or neutered.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Learning AI
« Reply #6 on: May 08, 2005, 01:32:13 PM »
Sadly what OP2 has to offer is nowhere near what Qt has. The trigger system in OP2 is probably one of the biggest areas where the API lacks. There is no information passed to any of the callback functions about what caused the trigger to fire, or even which trigger fired. If two triggers have the same callback function, you won't know which trigger caused the callback function to be called. If you need to know that, you have to figure it out yourself. And that would mean testing game variables yourself. (Checking the game time, enumerating units in a rect to see if any are there, or what type they are, etc.)

It should be quite obvious how to specify the rect for CreateRectTrigger if you just look at it's declaration in Outpost2DLL\Functions.h. If you want to know what type of vehicle entered the rect, use InRectEnumerator to check for all units in the rect. The trigger can fire to let you know something is there, but you have to find out what yourself. You can also try cheating and play around with CreateEscapeTrigger. This might allow you to do what you want to accomplish, although, I doubt the trigger was meant to be used in that way.


... and I can't think of any sample code I happen to have lying around, so you'll get none from me.  :P
To be honest, I've found the trigger system so lacking that I usually avoid using it. If there is stuff you want to know, or events you need to respond to, it just might be easier to test for them in AIProc and have all your handler code called from there. (Explicitly, by yourself.) The only thing I really rely on trigger for is victory/failure conditions. There doesn't seem to be any way around that without hacking that game, so I'm content to use them there.


I have had a few ideas for extending things and adding a new trigger system. However there currently seems to be a lack of people willing to write the library. I don't really have time for it at the moment and no one else seems to have started anything much on the idea. Plus, a new trigger system would have been a later thing added to it. The main focus initially was sending data into the game engine (unit commands, etc.) and then eventually extend the library to include getting more data back out (a new trigger system would fall into this part, but a later portion of this part).
 

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #7 on: May 08, 2005, 04:25:59 PM »
Well, now seems as good a time as any..
I've created a new addition to Outpost2 programming with op2extra.zip which extends the Unit class to include some interesting functions, that can be used for AI

Below is the original post from the Dev-team forum:
Quote
By now, you've probably all figured out i've implemented Hooman's findings -i should mention i did do a lot of work towards this, but i got Hooman to thank for helping me in the right direction- anyway: i've implemented those findings into a shared library: op2extra.zip. It includes a new Unit structure for use with the extended unit-functions, which i've dubbed IUnit (I as in "Internal", since it uses the internal OP2-unit structure, rather then the one exported). It is derived from the exported Unit class, and as so inherits ALL the Unit's members.
To add to this: it uses some of the hacks that we (Hooman and i) found to issue commandPackets right into the core of the outpost game engine. Some remarks about this are in place here:
You all know the hacks are windows-dependend, which means they could become useless, if MicroSoft decides to change the way executables are loaded. On the other hand; putting everything into a shared dll makes updating pretty easy, so IF Windows changes we can patch the dll and others wouldn't even have to recompile their projects to use the new dll version.
Not all member functions have been tested to this date. If anyone feels compelled to this: be my guest and let me know
Some of the commandPackets that i've used are meant to be used on a group of units (like Scatter), but since i've added them as a member-function to the IUnit class, they refer to only one unit. As such: the scatter command is not supported, since obviously scattering a single unit makes no sense.
I've tried to insert code that will verify certain things, so the code won't backfire on you, if used in a wrong way. Like trying to use IUnit::Research(..) on a non-lab unit. IMPORTANT: with the current version, there will not be any warning if you try to use a wrong method, it will simply return from the subroutine -doing nothing- In time, i'm going to change this to either displaying a clean error-msg or using a standard windows exception

Added IUnit methods:

CODE 
void Doze(MAP_RECT region);  // use with RoboDozer only
void Dock();       // to be used when a unit in on a docking pad
void DockGarage();     // to be used when a unit in on a docking pad of a garage
void Build(LOCATION location); // use when there's a kit inside a convec, or when this is a miner/geocon
void Produce(enum map_id itemToProduce,enum map_id weaponType);
void TransferCargo(short bay); // to be used when a unit in on a docking pad
void LoadCargo();      // to be used when a unit in on a docking pad
void UnloadCargo();     // to be used when a unit in on a docking pad
void DumpCargo();
void Research(short techID,short numScientists); // use only with labs
void TrainScientists(short numTrained);    // use only with university
void Salvage(MAP_RECT region,IUnit gorf);
void Poof();
void Repair(IUnit target);   // use with convec or repait vehicle
void Reprogram(IUnit target);  // use with spider only
void Dismantle(IUnit target);  // use with convec

command_type GetBusy();    // returns what this unit is doing (0=not busy)
void GetFactoryCargo(int bay, enum map_id &unitType, enum map_id &weaponType);
void GetCargo(map_id &cargoType,map_id &weaponType); // works for convecs
int GetTruckCargo(Truck_Cargo &cargoType);    // returns amount of cargo
int GetDamage();      // 0=no damage

bool IsInRect(MAP_RECT rect);  // returns true if the unit is in the rect



Some remarks:
There is a difference between Build and Produce: Build is to be used on a vehicle that can build something, like a convec, geocon or miner: you only supply the location where the new building has to be built. Produce can be used on buildings that produce either units or kits; eg. SF, VF, Aracnid F, Consumer F and SpacePort

TransferCargo hasn't been tested yet, so i honestly don't know if it should be used on the building or the vehicle. Either way: the unit MUST be already on the pad! Same goes for LoadCargo and UnloadCargo.

Poof() will make the unit exit the gamemap at the nearest exit point - the unit will first move to the side of the map, then "poof". Be advised, somehow, this will cause a graphics bug - the game doesnt repaint its DC after the unit poofs!

Repair/Reprogram/Dismantle all use the same internal structure - but i've included them seperately.


Then, i've added some other methods, that i've used before (succesfully i might add):
GetFactoryCargo -- does just that
GetCargo -- retrieves the cargo PLUS its weapontype (original GetCargo doesn't do this). I've included the original GetCargo as an overloaded method (C++ allows this). Depending the number of arguments IUnit will use either the new or old method.
GetTruckCargo -- use this to get the cargo (as with the old GetCargo) but it also returns the amount held in the truck!
GetDamage -- obvious: there IS a SetDamage, so why isn't there a GetDamage ?? go figure!
IsInRect -- a handy method, to find out if a unit is in your specified rect.
Have fun !
I hope all (new) coders can work with this. I'll keep new version backward-compatible, so older missions will run with a newer version of op2extra.dll
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline LupusShearhart

  • Newbie
  • *
  • Posts: 13
Learning AI
« Reply #8 on: May 09, 2005, 04:16:02 PM »
Hmm - quick question surrounding the Research method.

void Research(short techID,short numScientists);

For numScientists, is there a getMaxScientists command to see what the maximum scientists allowed for any given topic would be, then a getNumScientists to get an ammount of scientists the AI has at its disposal? And if numScientists<maxScientists, use numScientists, else use maxScientists?

To simplify the question through code, is it possible to do something similar to the following:

int maxScientists = getMaxScientists;
int nScientists = getNumScientists;
int use = 0;
if (nScientists < maxScientists)
{
  use = maxScientists;
}
else
{
  use = nScientists;
}
Research(<techidhere>, use);
« Last Edit: May 09, 2005, 04:18:26 PM by LupusShearhart »
"Wake up, Mr. Freeman. Wake up and...smell the ashes." ~The G-man in Half Life 2

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #9 on: May 09, 2005, 04:26:23 PM »
There's no function that would do that. You could check the sheets for them, coz i think it'll take a while before one of us will add that function. Since i never tested the Research function, you could try to use Player::Scientists() which returns the number of scientists a player has. OP2 might simply top it of to the max number allowed for that specific research tech.
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Learning AI
« Reply #10 on: May 09, 2005, 09:45:15 PM »
I haven't looked recently, but I did have the function which did research in my AI test to only assign up to the maximum number allowed for that tech. (I'm not sure if that's the version I released). I didn't have a function to return the number of available scientists however. So it had the problem of making all your buildings go offline once too many scientists died since it kept assigning too many of them to research.

Anyways, I'd imagine a simple memory scan could reveal the location of a variable that displays the current number of available scients. This is viewable from the morale display, so it's calculated or stored somewhere. If it's in a global variable, then the problem is easily solved. If it's calculated, well..., then I guess you'd need to do that yourself from available data. Although, all the values you should need are most likely stored in global variables (for efficiency reasons) so once you get the right addresses, the rest of this problem becomes easy.



Edit: I looked into this. The number of colonists you have (for Workers, Scientists, and Kids) all seem to be modified in a function at address 0x00471D60. It accesses variables in the internal player class. There is an array of these classes with base address 0x0056EF1C. The offset of the number of Workers, Scientists, and Kids, is respectively 0x94, 0x98, 0x9C. Shortly after this you can also find things like the number of available Scientists (0xA8), number of available/assigned workers? (0xCC), amount of power total (0xAC) and amount of power available (0xB8). The player class has a size of 3108 bytes. So you can access the data using the address: 0x56EF1C + playerNum*3108 + offsetOfData.

For playerNum = 0, the number of available scientists is at: 0x56EF1C + 0*3108 + 0xA8 = 0x56EFC4.

A really simple way to get at this value would be something like the following:
Code: [Select]
int *numAvailableScientists = (int*)0x56EFC4;

int num = *numAvailableScientsts; // Read the value and store it in "num"
*numAvailableScientists = 5000; // Give lots of available scientists =)
Note: I haven't compiled or tested the above code, just typed it right there as the sort of way you might go about doing this. I'll leave it to whoever wants to try it to tell me if it works. =)


Btw, if might shed some light on a few issues if someone takes a closer look at that function where the stuff is modified. With a bit of assembly knowledge, you can find out if having more than one nursery/university actually helps, med center effects, number of workers vs. number of scientists, birth rates, death rates, and all that good stuff. (I've looked at it a little bit, so I have a little idea about a few of these things).

 
« Last Edit: May 11, 2005, 02:32:52 AM by Hooman »

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #11 on: May 25, 2005, 04:29:03 PM »
Okay - a request: Group members - what do they do and how do i use them ?

In short: there are 3 types of groups:
  • FightGroups;
  • MiningGroups;
  • BuildingGroups.
Do NOT use ScGroup by itself, and do NOT create groups using the new keyword; use the CreateGroup functions instead!

The type of group you use depends on what you want the group to do.
  • If you want to build something (eg. buildings, tubes, walls) use the BuildingGroup type. Use this type also if you want to create new vehicles at a VF. Theoreticly you can assign ANY unit type to this group, but it's advisable to only assign units with building capabilities, such as ConVecs, Earthworkers, RoboMiners and factories;
  • Obviously, a MiningGroup is used for mining operations. You should only assign cargotrucks to this type of group;
  • FightGroups are used for grouping units for grouped actions. This does not necessarily have to be fighting actions, it can be any action.
If you do not have any special action for the group (you only need the units to be grouped to be referenced later), use a FightGroup

[dohtml]<center><hr width=80%></center>[/dohtml]
TakeUnit: adds a unit to the group. Use this to put your units into the group one by one.
TakeAllUnits: if you want to combine 2 groups, use this class member. It will move the units from one group to the other. Be careful: a unit can only belong to 1 group at any time. After using TakeAllUnits the old group is void & invalid (referencing it will cause the game to crash).
SetDeleteWhenEmpty: can be used to delete the group after all its units are removed, for example: after using TakeAllUnits, or after they all been killed. As far as i know, you cannot UNset it again. Just use this right after you've created the group, and added some units. Always remove unused groups either by setting this option, to save up resources. Outpost2 can only have 255 ScStubs assigned, which includes Triggers as well.
SetTargCount: is to be used together with RecordVehReinforceGroup; see the earlier post about groups. It can be used several times: each time you add one more targetcount to the reinforce-list. In this manner you can set up a FightGroup to be reinforced automaticly with different types of units.
ClearTargCount: removes any and all targetcounts set by SetTargCount. Since there's no way of removing a specific targetcount (in case you've added multiple, see above), the only way to remove 1 of them, is to use this class member, and adding the targetcounts again.
RemoveUnit: remove a single unit from the group. The unit will no longer execute the group actions.
GetFirstOfType: This is rather complicated. It returns the first unit it finds in the group, that meet your classifications. It returns false (0) when no such unit can be found, or when all units have been returned already.

SetRect: (included in both BuildingGroup and FightGroup) sets a Rect where your vehicles will go to, when they are not performing any actions. For a buildingGroup this is where your convecs will go to when they are waiting for the next kit to be done, for fightergroups, when there's no more enemies to attack. This Rect is ALSO included in MiningGroups, but it is set with the Setup class member, rather than Setrect. Also, for mining groups, you cannot change this rect after the group has been initialised other then initialising it again. For the other group types, you can always change the rect.

SetAttackType: was probably to be used for having your group attack a certain type of units, although this doesn't seem to work.
SetCombineFire: units will try to combine their firepower onto a target.
DoAttackEnemy: will attack ANY and ALL units set as 'enemy'. It will only target military units/buildings. This means agridomes and residences will be left alone. The group will go towards the closest enemy unit, even if they are found on the other side of the map, in the dark, with their lights off!
DoAttackUnit: sets a single target to be attacked (SetTargetUnit). The group will not seek out enemies, but will fire on enemy units when they come into range/view.
DoExitMap: will make the group drive to the side of the map, after which they will dissapear.
DoGuardGroup/DoGuardUnit: will guard the target group (SetTargetGroup/SetTargetUnit).
SetPatrolMode: uses the waypoints structure to set up a list of waypoints for a group to patrol. The group will NOT seek out enemies, but will fire if they come into range/view. Use DoPatrolOnly to initiate patrol, and ClearPatrolMode to stop the units patrolling. They will return to the SetRect region.
DoGuardRect: units will attack any enemy unit that enters one of the guarded rects -that can be added/removed with AddGuardedRect and ClearGuarderdRects. They will NOT stay inside the rects all the time. After all enemy units have either been killed or left the guarded rect(s), the group will return to the rect defined by SetRect. BE ADVICED: After using DoGuardRect you MUST supply a SetRect, not before, since that has side-efftecs.

ClearGuarderdRects: NOTE the extra r in ClearGuarderdRects, it's a typo that the outpost coders made. Unfortunately you have to include this misspelled member as shown. It will remove all guarded rects added to the group by AddGuardedRect

I hope this clears out some questions.
If some things are unclear, let me know.  I did not test ANY of the members above while writing this, all is done by memory, so i might have some mix-ups :whistle:

[EDIT] some minor changes, and took out some spelling typos :D
« Last Edit: August 19, 2005, 06:29:58 PM by Eddy-B »
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #12 on: November 04, 2005, 02:52:20 PM »
[size=8]An addition is in place, after working with groups some more recently:[/size]

Any FightGroup can have upto 8 Guardedrects. If you try to add a 9th, outpost 2 will crash. Calling ClearGuarderdRects will remove all entries, and again you can add 8 GuardedRects.
Because of the way that FightGroup works, when using FightGroup::SetRect, it internally calls AddGuardedRect to add the region to its list of guarded rects. This way the rect where the units are stationed is also guarded. At the same time, this reduces the number of guarded rects that you can add to 7.

Also, when changing the SetRect of a FightGroup { since it calls AddGuardedRect } the old rect that was assigned to the group is STILL in the guarded rects list!

So, please: when you need to change the groups location, use the following command sequence:
Code: [Select]
Group1.ClearGuarderdRects();
Group1.SetRect(NewMapRect);
If you added extra guarded rects, you have to add them again, because they are gone also now.
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Mcshay

  • Administrator
  • Sr. Member
  • *****
  • Posts: 404
Learning AI
« Reply #13 on: February 26, 2006, 06:43:42 AM »
Eddy, your op2extra.zip doesn't work.

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #14 on: February 27, 2006, 01:14:04 AM »
Looks like your the first to get it (?!) coz no one ever mentioned ANYthing about op2ext .. lol

i'll look at it this week
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz

Offline Mcshay

  • Administrator
  • Sr. Member
  • *****
  • Posts: 404
Learning AI
« Reply #15 on: February 27, 2006, 05:23:26 AM »
Hmm, I was able to get the files off the Wiki. Thanks anyway.

Offline Eddy-B

  • Hero Member
  • *****
  • Posts: 1186
    • http://www.eddy-b.com
Learning AI
« Reply #16 on: February 27, 2006, 06:01:06 AM »
I know the files are linked on the wiki, and so is a simple (but incomplete) description. Anyway: there's a new version of the dll: v1.1 and this comes with Renegades. I am planning to release this new version soon, coz i think the bugs & crashes in renegades are not caused by op2extra.dll
Rule #1:  Eddy is always right
Rule #2: If you think he's wrong, see rule #1
--------------------

Outpost : Renegades - Eddy-B.com - Electronics Pit[/siz