Outpost Universe Forums

Projects & Development => Outpost 2 Programming & Development => Topic started by: gpgarrettboast on April 09, 2004, 01:28:20 AM

Title: Outpost 2 DLL Programming
Post by: gpgarrettboast on April 09, 2004, 01:28:20 AM
In this forum, you can post code that may help one another figure out the DLLs, weather its the SDK or hacking.

But to keep this neat, pleast use the CODE tags like this:

Code: [Select]
TethysGame::SetVortex(24,35,26,37,3,5);

^ (Not real Code... well, kinda. I didn't have access to any. lol)

EDIT:
Oops, I accidently clicked edit. lol
Title: Outpost 2 DLL Programming
Post by: Hooman on April 09, 2004, 01:39:00 AM
Basic initialization

Code: [Select]
SCRIPT_API int InitProc()
{
_Player *Player, *Computer;

Player = &::Player[0];
Computer = &::Player[1];

Player->GoEden();
Player->SetColorNumber(0);

Player->SetOre(5000);
Player->SetFoodStored(3000);
Player->SetWorkers(28);
Player->SetScientists(13);
Player->SetKids(15);
Player->CenterViewOn(60, 210);

Computer->GoPlymouth();
Computer->SetColorNumber(1);

Computer->SetOre(5000);
Computer->SetFoodStored(3000);
Computer->SetWorkers(28);
Computer->SetScientists(13);
Computer->SetKids(15);

TethysGame::ForceMoraleGood(-1);
TethysGame::SetDaylightMoves(-1);
TethysGame::SetDaylightEverywhere(0);
TethysGame::SetCheatFastUnits(0);
TethysGame::SetCheatFastProduction(0);
TethysGame::SetCheatUnlimitedResources(0);
TethysGame::SetCheatProduceAll(0);

Edit: Obsolete casting hack removed. SetDaylightMoves misunderstanding corrected (the parameter is a player number, not a boolean value, -1 means everyone). Also, there are better ways to do this now.
Title: Outpost 2 DLL Programming
Post by: gpgarrettboast on April 09, 2004, 10:20:56 PM
Here is the arguments for some commands

Code: [Select]
TethysGame::SetMeteor(Xpos,YPos,size)

Only one so far. I forgot the others, but I'll post them later.
Title: Outpost 2 DLL Programming
Post by: Arklon on April 09, 2004, 11:11:20 PM
Code: [Select]
TethysGame::SetInitialLight(value);

It was somewhere among those lines. It goes in InitProc().
Title: Outpost 2 DLL Programming
Post by: Hooman on April 10, 2004, 12:35:03 AM
Woohoo. Thanks. I was looking for a function that would do that. Slight correction though.
Code: [Select]
	GameMap::SetInitialLightLevel(-96);
The parameter controls how far along the band of day and night is. Note that:
Code: [Select]
TethysGame::SetDaylightEverywhere(0);
must be used for the above to have any effect. I found the value of -96 would put the entire map (at least the one I was working on) into total darkness everywhere.  :whistle:  
Title: Outpost 2 DLL Programming
Post by: Hooman on April 11, 2004, 02:53:13 PM
If that 0 referes to SetInitialLightLevel, then the effect of the value seems to depend on the map size. For the maps I tried, 0 put the band of daylight along the left edge of the map. On a smaller map, -96 put the band of daylight along the right edge while a value of -32 put that map into complete darkness. Hmm, I'd forgotten you'd already told me about his one.

As for the array, I've figured that for some time but I'm still unable to get the compiler to handle it properly. I've tried every which way I can think of but I either get a comile error, a link error, or it just doesn't resolve that import properly and crashes.

Oh, and the following creates a mining beacon. Some of the values were taken out of a DLL. I've noticed most positioning requires adding 31 to x and subtracting 1 from y to get the actual map coordinates. This seems to apply to the Disaster functions as well.
Code: [Select]
	TethysGame::CreateBeacon((map_id)0x51, xpos+31, ypos-1, 0, ?yield?, 0xFFFFFFFF);

 
Title: Outpost 2 DLL Programming
Post by: Cynex on April 12, 2004, 07:03:10 PM
Well, I've done lots of redifinitions in the current project.

Even if there is passed a TethysGame-var you can't determinate it, because it consists only of functions and therefore has no data stored at the referred address (which makes every offset possible).

For example you can define:
Code: [Select]
__declspec(dllexport) _cdecl int InitProc(TethysGame Game)
{
Game.CreateBeacon(map_Mining_Beacon, 0x41, 0x19, 0x0, 0x0, -1);
};
it'll work.

Furthermore it seems that classes actually consist of more than just functions.

Have seen the Player-Array?
It adds 0x0C to the original address, so one might assume that an index is 0x0C bytes.

just define three integers somewhere in the class _Player, preferred at the beginning:
Code: [Select]
	int unknown1, unknown2, unknown3;

This makes Player[1] work as (Player + 0x0C).
My current Player-declaration is:
Code: [Select]
_Player OP2 Player[6];

Also the LOCATION-struct seems to hold X and Y values:
Code: [Select]
	int x, y;
Put this at the beginning of the LOCATION-struct and it should work.

Furthermore the Unit-class contains the Unit-ID as I actually see it:
Code: [Select]
	int ID;
(Copy to the beginning of Unit-class)

This should make many function-calls possible.


If you disassemble one of the original dll's you might notice that there aren't any calls to Constructor-Procedures of the classes.
A Constructor takes initialising-values and stores theme somehow in the structure when defining a variable of a certain class or structure.

For the LOCATION-struct you will just add:
Code: [Select]
_inline LOCATION::LOCATION(int map_x, int map_y)
{
x = map_x;
y = map_y;
};
_inline LOCATION::LOCATION(void) { };

This eliminates unneeded extern calls (although it works with them).

For the Unit-class just add:
Code: [Select]
_inline Unit::Unit(void)
{
ID = 0;
};
(It is always set to 0 because there is no Unit yet.)

I use this function to test stuff:
Code: [Select]
__declspec(dllexport) _cdecl int InitProc(void)
{
Player[plr_1].GoEden();
Player[plr_1].SetColorNumber(0x00);
Player[plr_2].GoPlymouth();
Player[plr_2].SetColorNumber(0x01);

Unit RetUnit;
TethysGame::CreateUnit(RetUnit, map_ConVec, LOCATION(0x41, 0x19), plr_1, map_Default, 0);
RetUnit.SetCargo(map_Command_Center, map_Default);
//* RetUnit.DoBuild(RetUnit.GetCargo(), LOCATION(0x40, 0x20));
RetUnit.DoBuild(map_Command_Center, LOCATION(0x40, 0x20));
RetUnit.DoSetLights(1);

TethysGame::CreateUnit(RetUnit, map_Cargo_Truck, LOCATION(0x41, 0x18), plr_1, map_Default, 0);
RetUnit.SetTruckCargo(trck_Rare_Metal, 0);
RetUnit.SetDamage(30);
RetUnit.DoTransfer(plr_2);
// RetUnit.DoDeath();
// RetUnit.DoSelfDestruct();

Unit CargoTruck1 = RetUnit;

TethysGame::CreateUnit(RetUnit, map_Lynx, LOCATION(0x41, 0x1A), plr_1, map_RPG, 0);
RetUnit.SetWeapon(map_Microwave);
RetUnit.DoAttack(CargoTruck1);
// RetUnit.DoMove(LOCATION(0x20, 0x20));

Player[plr_1].CenterViewOn(0x41, 0x19);
Player[plr_1].SetFoodStored(3000);
Player[plr_1].SetKids(20);
Player[plr_1].SetWorkers(20);
Player[plr_1].SetScientists(15);

int RetTile = GameMap::GetTile(LOCATION(0x51, 0x13));
GameMap::InitialSetTile(LOCATION(0x41, 0x16), RetTile);
GameMap::SetTile(LOCATION(0x42, 0x17), RetTile);
GameMap::SetInitialLightLevel(0x40);

LOCATION TestLoc(0x10, 0x10);
TestLoc.Add(LOCATION(0x31, 0x01));
TethysGame::CreateUnit(RetUnit, map_Command_Center, TestLoc, plr_2, map_Default, 0);


TethysGame::ForceMoraleGreat(-1);
// TethysGame::ForceMoraleGood(-1);
// TethysGame::ForceMoraleOK(-1);
// TethysGame::ForceMoralePoor(-1);
// TethysGame::ForceMoraleRotten(-1);
TethysGame::SetDaylightMoves(1);
TethysGame::SetDaylightEverywhere(0);
TethysGame::SetCheatFastUnits(0);
TethysGame::SetCheatFastProduction(0);
TethysGame::SetCheatUnlimitedResources(0);
TethysGame::SetCheatProduceAll(0);

Player[plr_1].SetTechLevel(9);

TethysGame::CreateBeacon(map_Mining_Beacon, 0x41, 0x1B, beacon_rare, yield_3, -1);
TethysGame::CreateUnit(RetUnit, map_Mine, LOCATION(0x41, 0x1B), plr_1, map_Default, 0);

TethysGame::CreateWallOrTube(0x41, 0x1D, 0, map_Microbe_Wall);

return 1; // return 1 if OK; 0 on failure
};
(The commented functions are commented because it would make no sense to call them at these points - although they cause no error.)

I have problems calling GetCargo() and any other functions returning a value.
The exported functionname contains 'QBE' in outpost2.exe whereas C++ assumes 'QAE'.
Does anyone know how to declare it right?
I actaully use:
Code: [Select]
	map_id OP2 GetCargo(void);

Here a few tips how C++ exported symbols are decorated:
'AA' means that the specific argument is passed with an address-operator '&'
'PA' means the opposite: a pointer is passed '*'.
You can eliminate these decorations by deleting any '*' or '&' from the specific parameter.

There are many more - I don't wanna describe all (I don't even know all).


Appendix:

To make the above functions work you need some enum's.
I've declared the following:
Code: [Select]
enum map_id {
map_Default = 0,
map_Cargo_Truck,
map_ConVec,
map_Spider,
map_Scorpion,
map_Lynx,
map_Panther,
map_Tiger,
map_Robo_Surveyor,
map_Robo_Miner,
map_GeoCon,
map_Scout,
map_Robo_Dozer,
map_Evacuation_Transport,
map_Repair_Vehicle,
map_Earthworker,
map_Tube = 0x11,
map_Wall,
map_Lava_Wall,
map_Microbe_Wall,
map_Mine = 0x15,
map_Guard_Post = 0x17,
map_Light_Tower,
map_Common_Storage,
map_Rare_Storage,
map_Forum,
map_Command_Center,
map_MHD_Generator,
map_Residence,
map_Robot_Command,
map_Trade_Center,
map_Basic_Lab,
map_Medical_Center,
map_Nursury,
map_Solar_Power_Array,
map_Recreation_Facility,
map_University,
map_Agridome,
map_DIRT,
map_Garage,
map_Magma_Well,
map_Meteor_Defense,
map_Geothermal_Plant,
map_Arachnid_Factory,
map_Consumer_Factory,
map_Structure_Factory,
map_Vehicle_Factory,
map_Standard_Lab,
map_Advanced_Lab,
map_Observatory,
map_Reinforced_Residence,
map_Advanced_Residence,
map_Common_Ore_Smelter,
map_Spaceport,
map_Rare_Ore_Smelter,
map_GORF,
map_Tokamak,
map_Acid_Cloud = 0x3B,
map_EMP,
map_Laser,
map_Microwave,
map_Rail_Gun,
map_RPG,
map_Starflare,
map_Supernova,
map_ESG = 0x46,
map_Stickyfoam,
map_Thors_Hammer,
map_Mining_Beacon = 0x51
};
And:
Code: [Select]
enum Truck_Cargo {
trck_Empty = 0x00,
trck_Food,
trck_Common_Ore,
trck_Rare_Ore,
trck_Common_Metal,
trck_Rare_Metal,
trck_Common_Rubble = 0x06,
trck_Rare_Rubble,
trck_Spaceport = 0x08,
trck_Garbage = 0x09,
trck_Unit = 0x03F8
};
Also defined lots of constants:
Code: [Select]
// PlayerID's
#define plr_1 0
#define plr_2 1
#define plr_3 2
#define plr_4 3
#define plr_5 4
#define plr_6 5

// Mining beacon's
#define beacon_common 0
#define beacon_rare 1

// Yield Type's
#define yield_3 0
#define yield_2 1
#define yield_1 2

I've also given the parameters more meaningful names, but they're too much to post them all.
Here only the most important:
Code: [Select]
// TethysGame:
CreateBeacon(map_id BeaconType, int X, int Y, int Type, int Yield, int l3);
CreateUnit(Unit &ReturnUnit, map_id UnitType, LOCATION, int PlayerNum, map_id Weapon, int l9);
CreateWallOrTube(int X, int Y, int m5, map_id Type);

// GameMap:
CenterViewOn(int X, int Y);

// Unit:
SetCargo(map_id Cargo, map_id Default);
SetFactoryCargo(int Bay, map_id UnitType, map_id Default);
SetTruckCargo(Truck_Cargo Cargo, int Attrib);

That's all for now.
 
Title: Outpost 2 DLL Programming
Post by: Arklon on April 17, 2004, 09:09:46 PM
My unit value chart (http://luigi.wootest.net/misc/unitvaluechart.txt) might help...

Also, you should figure out the code for making wreakage.
Title: Outpost 2 DLL Programming
Post by: Hooman on April 18, 2004, 02:22:14 AM
DescBlock appears to be longer than 64 bits.

I'm currently using this:
#define COLONY 0xFFFFFFFF
#define TUTORIAL 0xFFFFFFFD

Code: [Select]
struct SDescBlock{
int type;
int unknown1;
int unknown2;
int unknown3;
};

SCRIPT_API SDescBlock DescBlock = { COLONY, 2, 0x0C, 0 };

If you don't properly initialize:
unknown1: You never have any available people even though you may have lots of people. Also, when you click on units, there controls appear but you really can't control them.
unknown2: You can't build anything at the structure factory
unknown3: You lose most of your menu controls

Also, I didn't notice any built in functions to lay down a line of tube or wall so I wrote this one:

Code: [Select]
// Create a line of wall or tube 
// Draw along the horizontal first:
//  If coordinates represent a bent wall/tube then draw
//  horizontal between x1 and x2 (along y1) and then
//  vertical between y1 and y2 (along x2)
void CreateTubeOrWallLine(int x1, int y1, int x2, int y2, map_id type)
{
int i;
int vert;
int horz;

// Adjust coordinates for odities of the game
x1 += 31; x2 += 31;
y1 -= 1; y2 -=1;

// Determine which edges to draw along
vert = x2;
horz = y1;

// Make sure (x1 < x2) and (y1 < y2)
if (x1 > x2){ x1 ^= x2; x2 ^= x1; x1 ^= x2; }
if (y1 > y2){ y1 ^= y2; y2 ^= y1; y1 ^= y2; }

// Create horizontal section
for (i = x1; i <= x2; i++)
  TethysGame::CreateWallOrTube(i, horz, 0, type);
// Create vertical section
for (i = y1; i <= y2; i++)
  TethysGame::CreateWallOrTube(vert, i, 0, type);
}

Enjoy.

Also, I found that by going through the steps to build the import library myself, I managed to solve a lot of other problems. For instance, linking CreateUnit and being able to properly use Player as an array. Hmm, could have been a compiler/linker version difference I guess. Repeated from op2hacker's post, the steps can be found here: http://support.microsoft.com/default.aspx?...&NoWebContent=1 (http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/Q131/3/13.asp&NoWebContent=1)

Arklon, thank you so much for that chart. Anyone have one for the techs? And Cynex, wow! Thanks.

Edit: Hey Cynex, this seems to be what you are looking for. The B in "?Workers@_Player@@QBEHXZ" seems to mean const. That is, the functions should be declared as:
   
Code: [Select]
int Workers() const;
Title: Outpost 2 DLL Programming
Post by: Arklon on April 18, 2004, 09:19:40 AM
I believe techs are identified through ID's (consists of several numbers). The only way to get these ID's is from sheets.vol.

I got the tech ID's from the demo. With any luck, they didn't changed the order of these ID's. However, the names are a bit different.

Code: [Select]
;  02101 Agridome
;  02103 Command Center
;  02104 Structure Factory
;  02106 Basic Laboratory
;  02107 Light Tower
;  02108 Common Ore Mine
;  02109 Residence
;  02110 Common Ore Smelter
;  02111 Common Metals Storage Tanks
;  02112 Tokamak
;  02114 Tube
;  02115 Trade Center
;  03101 Standard Lab
;  03301 DIRT
;  03302 GORF
;  03303 Medical Center
;  03304 Nursery
;  03305 University
;  03501 Robot Command Center
;  03502 Vehicle Factory
;  03601 Scout
;  03602 Laser Turret
;  03603 Microwave Turret
;  03604 Cargo Truck
;  03605 Construction Vehicle
;  03606 Earthworks Constructor
;  03607 Evacuation Transport
;  03608 Robo-Dozer
;  03609 Robo-Miner
;  03610 Robo-Surveyor
;  03801 Guard Post
;  03851 Light Combat Chassis
;  05101 Consumer Goods Factory
;  05102 Garage
;  05104 Advanced Lab
;  05105 Recreation Facility
;  05106 Walls, Concrete
;  05107 ASE Converter (MHD Generator, I believe)
;  05108 Forum
;  05110 Rare Ore Mine
;  05115 Geothermal Constructor
;  05116 Factory, Arachnid and Spider
;  05201 Medium Combat Chassis
;  05202 StickyFoam Turret
;  05301 Earthquake Prediction
;  05302 Electrical Storm Prediction
;  05303 Vortex Prediction
;  05304 Volcanic Eruption Prediction
;  05401 Repair Vehicle
;  05501 Rare Ore Smelter
;  05502 Rare Metals Storage Tanks
;  05503 SULV
;  05504 EDWARD Satellite
;  05506 Rail Gun Turret
;  05508 EMP Turret
;  05602 Cannon Turret (RPG, I believe)
;  07102 Starflare Turret
;  07103 ESG Turret
;  07104 Scorpion
;  08051 Observatory
;  08102 Lava Wall
;  08103 Magma Well
;  08104 Advanced Residence
;  08105 Reinforced Residence
;  08106 Meteor Defense
;  08201 Heavy Combat Chassis
;  08203 Supernova Turret
;  08317 Volcanic Eruption Prediction
;  08401 Microbe Wall
;  08503 Corrosive Acid Turret (Acid Cloud)
;  08601 Orbital Space Station (Skydock)
;  08801 Starship Superstructure I (?)
;  08901 Starship Superstructure II (?)
;  10101 RLV
;  10102 Thor's Hammer Turret
;  10202 Ship's Computer (Command Module)
;  10203 Ship's Propulsion System (I think Ion Drive or Fusion Drive)
;  10204 Solar Power Satellite & Array
;  10205 Starship Hull and Shielding (Maybe habitat ring?)
;  10206 Starship Long-Range Sensors (Sensor package?)
;  10207 Starship Superstructure III (?)
;  10208 Suspended animation (Stasis Systems)
;  10209 Starship Short-Range Sensors (Sensor package?)
;  10401 Ship's Power Distribution System (Ion Drive or Fusion Drive)
;  12201 EMP Missiles
Title: Outpost 2 DLL Programming
Post by: Hooman on April 18, 2004, 01:39:33 PM
Cool, Thanks. They seem to work. I tried a few of them. The following lets you build rail guns right at the start of the game:
Code: [Select]
	Player[0].MarkResearchComplete(3801);
Player[0].MarkResearchComplete(5506);

I've got a few triggers going.

Code: [Select]
	Trigger trig;
trig = CreateTimeTrigger(1, 1, 101, "NoResponseToTrigger");
trig = CreateTimeTrigger(1, 1, 301, "NoResponseToTrigger");
trig = CreateTimeTrigger(1, 1, 501, "NoResponseToTrigger");
trig = CreateTimeTrigger(1, 1, 701, "NoResponseToTrigger");
trig = CreateTimeTrigger(1, 1, 901, "NoResponseToTrigger");
//trig = CreateEvacTrigger(1, 1, 1, "NoResponseToTrigger");
CreateVictoryCondition(1, 1, trig, "Text that appears in objective list");
//CreateFailureCondition(1, 1, trig, "Not sure what this is for");
The third parameter is the time at which the trigger fires relative to the current time (when the trigger is setup). Each time mark is 100 units. So 100 is still time mark 0 but 101 is the start of time mark 1 which lasts until 200. The Victory and Failure conditions work.

I tried putting the follow in NoResponseToTrigger (which I guess is a response  :lol: ):
   
Code: [Select]
char buffer[64];
scr_snprintf(buffer, 64, "Opponents Workers: %d", Player[1].Workers());
TethysGame::AddMessage(-1,-1,buffer,0,0);
The output appears in your list of messages. Interestingly enough, the computer always has 4096 workers no matter what you try to set it to. If you you Player.GoHuman() then the number of workers is set properly but as soon as you use Player.GoAI() it sets it to 4096 and can't be changed from that. The -1, -1 in AddMessage causes the message to not be associated with a particular area of the map (No "JumpTo" option for this message). I haven't been able to move to focus away from the top left corner of the map so it doesn't seem like these are x and y coordinates. The 64 in scr_snprintf I believe is the length of your buffer. It doesn't seem to print beyond that many characters into the buffer. It works pretty much exactly like sprintf/(fprintf) in C.
Title: Outpost 2 DLL Programming
Post by: Cynex on April 18, 2004, 01:46:47 PM
Thanks @Hooman, however it doesn't solve the problem.

Here are the errors it gives me:
Code: [Select]
// My old call:
enum map_id __thiscall Unit::GetCargo(void)" (__imp_?GetCargo@Unit@@QAE?AW4map_id@@XZ)

// The const-version:
enum map_id const __thiscall Unit::GetCargo(void)" (__imp_?GetCargo@Unit@@QAE?BW4map_id@@XZ)
So you was right - it changed an 'A' into a 'B', but the wrong.

The Name as it should be:
Code: [Select]
?GetCargo@Unit@@QBE?AW4map_id@@XZ

I can work around this problem by editing the obj-file in a hexeditor manually, but this isn't the method anybody prefers.

Also thanks for the CreateWallOrTubeLine()-function (I've renamed it this way to not mix up with the names).
It helps a lot, however I don't see why to adjust the coordinates (+31/-1). The whole game works with them and in my opinion it actually confuses more than it helps.


To come to few new things:

Note: I ever use 'tutorial.map' for testing.

Here is how I currently think the DescBlock works:

Code: [Select]
struct SDescBlock
{
int ID, unknown2, unknown3, unknown4;
char *MAPNAME, *TECHTREE, *LEVELDESC;
int unknown8;
};

SCRIPT_API SDescBlock DescBlock = {-3, 2, 12, 0, MapName, TechtreeName, LevelDesc, 0};

I only know few IDs:
-3 = Tutorial
-1 = Colony-game or demo
All values above 0 for campaign-missions.
More values below 0 seem to be used for various kinds of multiplayer-missions.

The 4th value seems to be used in non-colony missions in which you only control vehicles and therefore most controls are disabled by setting this value true.


Also I've just discovered how CreateUnitBlock() and some Triggers work:

CreateUnitBlock()

The call is quite simple:
Code: [Select]
// TethysGame:
static int __fastcall CreateUnitBlock(_Player &Player, char const *BlockName, int m2);

Use this for testing-purposes:
Code: [Select]
	TethysGame::CreateUnitBlock(Player[plr_1], "Units\0", 0);
Where "Units" is the name of an exported Record.

The Record will be defined like follows:
Code: [Select]
SCRIPT_API UnitBlock Units(UnitRec);
('Units' is the above referred name, 'UnitRec' is a Unit-Record taken for initialisation)

I've created the following to test most values: (Also useful to test triggers)
Code: [Select]
UnitRecord UnitRec[] = {
{map_Vehicle_Factory, 0x30, 0x10, 0, 0, map_Default, 0x10, 0, 0},
{map_Structure_Factory, 0x30, 0x14, 0, 0, map_Default, 0x10, 0, 0},
{map_Command_Center, 0x30, 0x17, 0, 0, map_Default, 0x10, 0, 0},
{map_Common_Ore_Smelter,0x30, 0x1A, 0, 0, map_Default, 0x10, 0, 0},
{map_Rare_Ore_Smelter, 0x30, 0x1E, 0, 0, map_Default, 0x10, 0, 0},
{map_Tokamak,   0x23, 0x03, 0, 0, map_Default, 0x10, 0, 0},
{map_ConVec,   0x32, 0x14, 0, 0, map_Default, 0x10, 0, 0},
{map_Cargo_Truck,  0x34, 0x10, 0, 0, map_Default, 0x10, trck_Rare_Metal, 1000},
{map_Tiger,    0x34, 0x17, 0, 0, map_Thors_Hammer, 0x10, 0, 0},
{map_Default,   0,  0,  0, 0, map_Default, 0,  0, 0}
};

Needs:
Code: [Select]
struct UnitRecord {
map_id Type;
int X, Y;
int unknown1, unknown2;
map_id Weapon;
int unknown3;
short Cargo;
short Attrib;
};
This is all to make CreateUnitBlock() work.
Note: The declaration only works when including the CRT (which should be by default).
(I've excluded CRT to make the dll smaller, but then discovered it doesn't work.)


Triggers

Just start with some enums:
Code: [Select]
enum compare_mode {
cmp_Default = 0,
cmp_Equal = 0,
cmp_Lower_Equal,
cmp_Greater_Equal,
cmp_Lower = 3,
cmp_Greater = 4
};
enum trig_res {
res_common_ore = 0x01,
res_rare_ore
};
Declarations:
Code: [Select]
// Global Functions
Trigger OP2 __fastcall CreateTimeTrigger(int Enable, int BoolNoRepeat, int TMin, int TMax, char const *TriggerFunc);
Trigger OP2 __fastcall CreateTimeTrigger(int Enable, int BoolNoRepeat, int Time100, char const *TriggerFunc);
Trigger OP2 __fastcall CreateVehicleCountTrigger(int Enable, int BoolNoRepeat, int PlayerNum, int RefCount, compare_mode, char const *TriggerFunc);
Trigger OP2 __fastcall CreateBuildingCountTrigger(int Enable, int BoolNoRepeat, int PlayerNum, int RefCount, compare_mode, char const *TriggerFunc);
Trigger OP2 __fastcall CreateResourceTrigger(int Enable, int BoolNoRepeat, trig_res, int RefAmount, int PlayerNum, compare_mode, char const *TriggerFunc);
Trigger OP2 __fastcall CreateVictoryCondition(int Enable, int dd6, Trigger &, char const *ConditionText);
And a new player-constant as I guess it:
Code: [Select]
	// PlayerID's
#define plr_all -1

Add this to InitProc: (best at the end)
Code: [Select]
	Trigger TTrig = CreateTimeTrigger(0, 1, 1, "NoResponseToTrigger\0");
Trigger RetTrig;
// RetTrig = CreateVictoryCondition(0, 1, TTrig, "Test-Condition 1.");

// TTrig = CreateTimeTrigger(1, 1, 200, "TestTrigger\0");
TTrig = CreateTimeTrigger(1, 1, 100, 1000, "TestTrigger\0");
// TTrig.Enable();

TTrig = CreateVehicleCountTrigger(1, 0, plr_1, 0x4, cmp_Lower_Equal, "TestTrigger2\0");

TTrig = CreateResourceTrigger(1, 1, res_rare_ore, 500, plr_all, cmp_Greater, "TestTrigger2\0");

// TTrig = CreateTimeTrigger(1, 1, 100, "NoResponseToTrigger\0");
RetTrig = CreateVictoryCondition(1, 1, TTrig, "Test-Condition 3.");

TTrig = CreateBuildingCountTrigger(1, 1, plr_1, 0x7, cmp_Greater, "TestTrigger2");
And the following exported functions:
Code: [Select]
SCRIPT_API void TestTrigger(void)
{
Unit RetUnit;
TethysGame::CreateUnit(RetUnit, map_Tiger, LOCATION(0x21, 0x01), plr_1, map_RPG, 0);
};
SCRIPT_API void TestTrigger2(void)
{
Unit RetUnit;
TethysGame::CreateUnit(RetUnit, map_Tiger, LOCATION(0x27, 0x07), plr_1, map_Acid_Cloud, 0);
};
I think the functions are self-explaining since i give the examples.

A note concerning the CreateTimeTrigger()-procedure:
if you pass 5 parameters, the game will take the third and the forth, and randomly select a time between these marks to execute the trigger-function.

Note2: You may just discover that a time of 100 means one mark in the game.

Triggers as well as VictoryConditions have an Enabled-Switch.
Only with this switch activated (usually first parameter) they'll have effect.


The current .def-file I use:
Code: [Select]
LIBRARY t15
EXPORTS
AIProc               @1
DescBlock            @2
GetSaveRegions       @3
InitProc             @4
LevelDesc            @5
MapName              @6
NoResponseToTrigger  @7
StatusProc           @8
TechtreeName         @9
TestTrigger          @10
TestTrigger2         @11
(You only need to list exported functions here (not variables) )
 
Title: Outpost 2 DLL Programming
Post by: Hooman on April 18, 2004, 01:58:41 PM
You need to declare this:

Code: [Select]
	map_id GetCargo() const;

The const at the end means that the functions does not modify any local variables and hence can be called when your instance of the class is a constant. Putting the const at the beginning makes the return value a constsant. I just tried it and it compiled fine.

 
Title: Outpost 2 DLL Programming
Post by: Hooman on April 19, 2004, 12:54:53 AM
Update to the map_id enum. I've added Magma Vents and Fumaroles.

Code: [Select]
	map_Mining_Beacon = 0x51,
map_Magma_Vent,
map_Fumarole

Here is some code to place such things

Code: [Select]
	// Create common ore beacons
TethysGame::CreateBeacon(map_Mining_Beacon, x-28, y-21, beacon_common, yield_3, 0);
TethysGame::CreateBeacon(map_Mining_Beacon, x-25, y+7, beacon_common, yield_3, 0);
// Create rare ore beacons
TethysGame::CreateBeacon(map_Mining_Beacon, x+17, y+31, beacon_rare, yield_3, 0);

// Create magma wells
TethysGame::CreateBeacon(map_Magma_Vent, x-25, y+40, -1, -1, -1);

// Create fumaroles
TethysGame::CreateBeacon(map_Fumarole, x+1, y-24, -1, -1, -1);
TethysGame::CreateBeacon(map_Fumarole, x-5, y+38, -1, -1, -1);

Oh, and a really minor point, but I think LevelDesc and TechtreeName in the SDescBlock are backwards.

I tried using the PlayerVehicleEnum class. Couldn't think of much to do with it but to turn on the headlights of all the vehicles for a given player. It seemed kinda convenient at the time.

Code: [Select]
// Note: CAN NOT use playerNum = -1 for all players
void SetAllVehicleLightsForPlayer(int playerNum, int bOn)
{
PlayerVehicleEnum unitEnum(playerNum); // Enumerate vehicles for given player
Unit curUnit;

// Enumerate through all vehicles of this player
while (unitEnum.GetNext(curUnit))
{
  curUnit.DoSetLights(bOn); // Turn on the headlights
}
}

The updated class is

Code: [Select]
class OP2 PlayerVehicleEnum {
public:
// GetNext: If there are units left to enumerate, copies current
//   unit into curUnit and returns 1, otherwise it returns 0
int GetNext(Unit &curUnit);
PlayerVehicleEnum(int playerNum);
};

It looks like PlayerBuildingEnum and PlayerUnitEnum work the same way. Note: "Unit" encompases both Vehicles and Buildings.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on April 19, 2004, 08:41:19 PM
Well, I've got a bit of a clone going for the Eden Colony Starship level. It stills needs Failure Conditions but here are the Victory Conditions

Code: [Select]
	// Create victory conditions for - Colony, Starship
trig = CreateCountTrigger(1, 1, -1, map_Evacuation_Module, (map_id)-1, 1, cmp_Greater_Equal, "NoResponseToTrigger");
CreateVictoryCondition(1, 1, trig, "Evacuate 200 colonists to spacecraft");
trig = CreateCountTrigger(1, 1, -1, map_Food_Cargo, (map_id)-1, 1, cmp_Greater_Equal, "NoResponseToTrigger");
CreateVictoryCondition(1, 1, trig, "Evacuate 10000 units of food to spacecraft");
trig = CreateCountTrigger(1, 1, -1, map_Common_Metals_Cargo, (map_id)-1, 1, cmp_Greater_Equal, "NoResponseToTrigger");
CreateVictoryCondition(1, 1, trig, "Evacuate 10000 units of Commom Metals to spacecraft");
trig = CreateCountTrigger(1, 1, -1, map_Rare_Metals_Cargo, (map_id)-1, 1, cmp_Greater_Equal, "NoResponseToTrigger");
CreateVictoryCondition(1, 1, trig, "Evacuate 10000 units of Rare Metals to spacecraft");

The parameters match exactly what I found in the regular DLL. I added a few more things to the map_id enum.

Code: [Select]
	map_Disasterous_Building_Explosion = 0x55,
map_Catastrophic_Building_Explosion,
map_Athiest_Building_Explosion,

map_EDWARD_Satellite = 0x58,
map_Solar_Satellite,
map_Ion_Drive_Module,
map_Fusion_Drive_Module,
map_Command_Module,
map_Fueling_Systems,
map_Habitat_Ring,
map_Sensor_Package,
map_Skydock,
map_Stasis_Systems,
map_Orbital_Package,
map_Pheonix_Module,

map_Rare_Metals_Cargo,
map_Common_Metals_Cargo,
map_Food_Cargo,
map_Evacuation_Module,
map_Children_Module,

map_SULV,
map_RLV,
map_EMP_Missile,

map_Impulse_Items = 0x6C,
map_Wares,
map_Luxury_Wares,

map_Inter_Colony_Shuttle = 0x6F,
map_Spider_3_Pack,
map_Scorpion_3_Pack,

map_Pretty_Art = 0x72

I got the names by seeing what I could put into the bay of a starport. It took me a while to realize I'd already seen all these in Arklon's unit value chart. Well, consider them double checked.  :blush:

Here is the code I used for the Startport and the items in the bays
Code: [Select]
TethysGame::CreateUnit(retUnit, map_Spaceport, LOCATION(x-8, y-1), playerNum, map_Default, 0);
retUnit.SetFactoryCargo(-1, map_RLV, map_Default); // Note: Any bay number will place this on the launch pad
retUnit.SetFactoryCargo(0, map_Common_Metals_Cargo, map_Default);
retUnit.SetFactoryCargo(1, map_Rare_Metals_Cargo, map_Default);
retUnit.SetFactoryCargo(2, map_Food_Cargo, map_Default);
retUnit.SetFactoryCargo(3, map_Evacuation_Module, map_Default);
retUnit.SetFactoryCargo(4, map_EDWARD_Satellite, map_Default);
retUnit.SetFactoryCargo(5, map_Solar_Satellite, map_Default);

Oh, and I haven't had to use the CRT yet. I had it complain a little while ago about wWinMain@16 but I disabled Exception handling and it went away. I did this in Project->Settings->C/C++->C++ Language.

Hmm. For some reason all my calls to create natural disasters aren't doing anything right now. I know they used to work for me.
Title: Outpost 2 DLL Programming
Post by: gpgarrettboast on April 20, 2004, 07:56:03 AM
I can't seem to find out how to set the disasters to what mark I want them to occur at.  Does anyone know how to do that? All of the disasters I set occur at mark 10...  Thanks.
Title: Outpost 2 DLL Programming
Post by: BlackBox on April 20, 2004, 02:27:09 PM
You have to create a time trigger, and put the disaster set function in the trigger callback.
Title: Outpost 2 DLL Programming
Post by: Hooman on April 20, 2004, 03:58:32 PM
I've only gotten a Vortex to appear right away.

Code: [Select]
	//TethysGame::SetTornado(loc.x, loc.y, duration, 1, 1, 1);

I think the reason why is that most disasters give you a warning about when they are going to happen (provided you did the research). Thus, there is a delay of about 10 time marks before the disaster actually appears. If you change the last few parameters in the above code, then there is a delay as well. I think those parameters may have been added so they could create that Vortex right away in one of the demo games that appears if you do nothing at the main menu. Most of the disasters don't have that many parameters. Lightning storms can probably be used the same way.

Oh, and I had to add a few unknowns to the PlayerBuildingEnum class to get it to work right. I've currently got this:

Code: [Select]
class OP2 PlayerBuildingEnum {
public:
int GetNext(Unit &curUnit);
PlayerBuildingEnum(int playerNum, map_id buildingType);

int unknown1, unknown2;
};

I haven't tested it all that much but I'm pretty sure that what all the parameters mean.

I suppose I should also mention that we should figure out how much data each of the classes hold so that when we create them, they don't corrupt the stack. Even if they don't write to certain locations in their constructor, they may try to write elsewhere in the function calls and I think that's why I had some funny stuff happen with PlayerBuildingEnum.

Well, here's an example of how to use the above. I put this into a trigger that fires about every half a time mark.  :whistle:

Code: [Select]
	Unit curUnit;
LOCATION loc(0, 0);

// Find a player building
if (enumBuilding.GetNext(curUnit))
{
  loc = curUnit.Location();// Get the coordinates of the building
  TethysGame::SetMeteor(loc.x, loc.y, 500); // Send a big rock at it ;>
}
else
{
enumBuilding = PlayerBuildingEnum(0, map_Default);
}

Note that the class was declared globally in this example.

Code: [Select]
PlayerBuildingEnum enumBuilding(0, map_Default); // All buildings
or
Code: [Select]
PlayerBuildingEnum enumBuilding(0, map_Command_Center); // Just the command centers

 
Title: Outpost 2 DLL Programming
Post by: Cynex on April 20, 2004, 05:24:59 PM
I exclude the CRT by enabling 'ignore all default libraries' in the Linker-setup.
This causes problems when initialising global variables with a constructor (which has to be called when loading.)
For example 'UnitBlock' doesn't load.

There are maybe possibilities to work around it.


Currently I try to understand groups:

They only seem to be available for KI-Players.
Units within a group can do certain tasks automatically, like searching for enemy-units and destroying them ;)

The most general type may be 'ScGroup':

Code: [Select]
class ScGroup {
public:
int PlayerKI;
void AddUnits(UnitBlock &AddUnits);
int GetFirstOfType(Unit &ReturnUnit, UnitClassifactions);
int GetFirstOfType(Unit &ReturnUnit, map_id Type, map_id Weapon);
void RemoveUnit(Unit);
ScGroup(void);
void SetLights(int BoolOn);
void TakeUnit(Unit);
int TotalUnitCount(void);
int UnitCount(UnitClassifactions);
~ScGroup(void);
};
(Note: I've only included the functions I understood so far, the others didn't change)

The following enum isn't yet completed, but hope it'll help:
Code: [Select]
enum UnitClassifactions {
cls_Attack = 0,
cls_ESG,
cls_EMP,
cls_Stickyfoam,
cls_Convecs = 5,
cls_Cargo_Trucks = 7,
cls_Earthworkers = 8,
cls_Robo_Dozers = 9,
cls_test = 4 // No special need
};
Explaination of cls_Attack:
All vehicles or even buildings which have one of the following weapons:
Microwave
Laser
Rail Gun
Starflare
Supernova
RPG
Acid Cloud
Thor's Hammer
Energy Cannon (Scorpions)

The rest should be clear.

Use this for testing:
Code: [Select]
	TethysGame::CreateUnit(RetUnit, map_Tiger, LOCATION(0x41, 0x1B), plr_2, map_EMP, 0);
// TethysGame::CreateUnit(RetUnit, map_Scout, LOCATION(0x41, 0x1B), plr_2, map_Default, 0);
ScGroup UBGroup;
UBGroup.PlayerKI = 0; // obviously must be PlayerNum -1 of all included units
// UBGroup.AddUnits(Units);
UBGroup.TakeUnit(RetUnit); // Adds Unit to the group
UBGroup.SetLights(1);
UBGroup.GetFirstOfType(RetUnit, map_Tiger, map_EMP);
//UBGroup.RemoveUnit(RetUnit); // Removes Unit from group but not from game
//RetUnit.DoSelfDestruct();

char buffer[64];
scr_snprintf(buffer, 64, "Unit Count: %d", UBGroup.UnitCount(cls_Defense));
TethysGame::AddMessage(-1, -1, buffer, 0, 0);

Other Groups may work similar and they even have a Create-function which sets up the group for a specified Player:
Code: [Select]
BuildingGroup OP2 __fastcall CreateBuildingGroup(_Player);
FightGroup OP2 __fastcall CreateFightGroup(_Player);
MiningGroup OP2 __fastcall CreateMiningGroup(_Player);
Either they've made the Create-function for ScGroup inline or there is no one needed (setting PlayerKI suffices to make it work).


Oh - just noticed that there's a weapon I forgot:
Code: [Select]
	map_Energy_Cannon, // (Scorpion)
Add this directly behind map_Thors_Hammer.
 
Title: Outpost 2 DLL Programming
Post by: Cynex on April 21, 2004, 04:19:49 PM
The following is a complete list of the research-topics available through 'multitek.txt'.

Note:
In this tech there are different topics for Eden and Plymouth in some cases.
For all missions (edentek.txt or ply_tek.txt) only use Topics marked with Eden in this list, when they are available for Plymouth too.

For example in multiplayer the following topics are available:
05051 = Robot-Assist Mechanic (Eden)
05052 = Robot-Assist Mechanic (Plymouth)
In both Eden and Plymouth missions using edentek or ply_tek you'll only use 05051.
However in multiplayer games you have to distinguish between Eden and Ply.


===========================================================================
02099 = Spider
02101 = Agridome
02103 = Command Center
02104 = Structure Factory
02107 = Light Tower
02108 = Common Ore Mine
02109 = Residence
02110 = Common Ore Smelter
02111 = Common Metals Storage Tanks
02112 = Tokamak
02114 = Tube
02115 = Trade Center
03101 = Standard Lab
03201 = Seismology
03202 = Vulcanology
03301 = Emergency Response Systems
03302 = Metals Reclamation
03303 = Health Maintenance
03304 = Offspring Enhancement
03305 = Research Training Programs
03306 = Leisure Studies
03401 = Cybernetic Teleoperation
03402 = High-Temperature Superconductivity
03403 = Hydroponic Growing Media
03405 = Metallogeny
03406 = Environmental Psychology
03407 = Large-Scale Optical Resonators
03408 = Focused Microwave Projection
03501 = 03501 Robot Command Center
03502 = 03502 Vehicle Factory
03601 = 03601 Scout
03602 = Laser Turret
03603 = Microwave Turret
03604 = Cargo Truck
03605 = Construction Vehicle
03606 = Earthworks Constructor
03608 = Robo-Dozer
03609 = Robo-Miner
03610 = Robo-Surveyor
03801 = Guard Post
03851 = Mobile Weapons Platform
03901 = Advanced Vehicle Power Plant
05051 = Robot-Assist Mechanic (Eden)
05052 = Robot-Assist Mechanic (Plymouth)
05101 = Consumerism
05102 = Garage
05104 = Lab, Advanced
05106 = Walls, Concrete
05107 = Magnetohydrodynamics
05108 = Public Performance
05110 = Rare Ore Processing
05111 = Independent Turret Power Systems
05115 = Geothermal Power
05116 = Legged Robots
05201 = Advanced Combat Chassis
05202 = Dissipating Adhesives
05302 = Meteorology
05303 = Severe Atmospheric Disturbances
05305 = DIRT Procedural Review
05306 = Recycler Postprocessing
05307 = Automated Diagnostic Examinations
05309 = Hypnopaedia (Eden)
05310 = Hypnopaedia (Plymouth)
05317 = Reinforced Vehicle Construction
05318 = Robotic Image Processing
05401 = Repair vehicle
05405 = Space Program
05408 = Forum Reconfiguration
05501 = Smelter, Rare Ore construction
05502 = Storage Tanks, Rare Metals
05503 = SULV
05504 = EDWARD satellite
05506 = Directional Magnetic Fields
05508 = Electromagnetic Pulsing
05599 = Rocket Propulsion
05601 = Heat Dissipation Systems (Plymouth)
05602 = Rocket Launcher
05701 = Lava Defenses
07102 = Explosive Charges
07103 = Multiple Mine Projectile System
07104 = Arachnid Weaponry
07201 = Rare Ore Extraction
07202 = Hot-Cracking Column Efficiency
07203 = Smelter Postprocessing
07206 = Scout-class Drive Train Refit
07211 = Extended-Range Projectile Launcher (Eden)
07212 = Extended-Range Projectile Launcher (Plymouth)
07213 = Advanced Robotic Manipulator Arm (Eden)
07214 = Advanced Robotic Manipulator Arm (Plymouth)
07403 = Increased Capacitance Circuitry
07405 = Scorpion Power Systems
08049 = Meteor-Watch Observatory
08051 = Observatory
08103 = Magma Refining
08104 = Expanded Housing
08105 = Disaster-Resistant Housing
08106 = High-Energy Ray-Composite Projector
08201 = Dual-Turret Weapons Systems
08203 = High-Powered Explosives
08301 = Efficiency Engineering (Eden)
08302 = Efficiency Engineering (Plymouth)
08304 = Heat Mining
08306 = Enhanced Defensive Fortifications
08307 = Multitainment Console Upgrade
08309 = Reinforced Panther Construction
08316 = Meteor Detection
08317 = Meteor detection
08319 = Spider Maintenance Software Revision
08320 = Reduced Foam Evaporation
08321 = Arachnid Durability
08503 = Acid Weaponry
08601 = Skydock
08801 = Ion Drive Module
08901 = Fusion Drive Module
08951 = Fueling Systems
10101 = Improved Launch Vehicle
10102 = Artificial Lightning
10202 = Command Module
10204 = Solar Power
10205 = Habitat Ring
10206 = Sensor Package
10208 = Stasis Systems
10209 = Orbital Package
10301 = Magma Purity Control
10303 = Advanced Armoring Systems
10305 = Grenade Loading Mechanism (Eden)
10306 = Grenade Loading Mechanism (Plymouth)
10309 = Precision Trajectory Projection Software
10401 = Phoenix Module
11999 = Tiger Speed Modification
12101 = Heat Dissipation Systems (Eden)
12201 = Rocket Atmospheric Re-entry System
12599 = Evacuation Module

// Unavailable Technologies
02106 = Basic Laboratory
03607 = Evacuation Transport
08401 = Microbe Wall
12499 = Children's Module
===========================================================================

This is how to use the IDs:

Code: [Select]
class _Player {
public:
int OP2 canDoResearch(int decResearchID);
int OP2 HasTechnology(int decResearchID) const;
void OP2 MarkResearchComplete(int decResearchID);
void OP2 SetTechLevel(int TechLevel);
};

Note considering techlevels:
Although I didn't test it, it seems that the first two decimal digits of each research topic are the techlevels.
Therefore if you call
Code: [Select]
	Player[plr_1].SetTechLevel(9);
all Technologies with 09XXX might be available.
Title: Outpost 2 DLL Programming
Post by: Cynex on April 24, 2004, 02:19:27 PM
canDoResearch(int decResearchID)
Returns true if player has done all researches to do the specified research.

HasTechnology(int decResearchID) const
Returns whether the player has completed the specified research yet.

MarkResearchComplete(int decResearchID)
Marks a research as done.

SetTechLevel(int TechLevel)
Sets a whole couple of researches done.
 
Title: Outpost 2 DLL Programming
Post by: Cynex on April 24, 2004, 03:26:38 PM
Quote
There's two "Heat Dissipation Systems" research things on your list. You didn't mark them as Eden or Plymouth...
Oh - I must have overlooked that.

Seems Plymouth can do it far earlier than Eden:

05601 = Heat Dissipation Systems (Plymouth)
12101 = Heat Dissipation Systems (Eden)

I'll edit that.

edit - note:
In Plymouth-missions use 05601, in Eden and tutorials 12101.
Title: Outpost 2 DLL Programming
Post by: Hooman on June 25, 2004, 06:25:01 PM
Here is how to start a virus:

Code: [Select]
	GameMap::SetVirusUL(LOCATION(locX,locY), 1);
TethysGame::SetMicrobeSpreadSpeed(0xa0);

Interesting note: When I did this in a colony game, it only seemed to spread on bulldozed terrain.

Well, I guess it could make for an interesting multiplayer game. See who's blight walls fail first.  :lol: You could kill each other by attacking their walls or bulldozing their base.  
Title: Outpost 2 DLL Programming
Post by: Kramy on June 29, 2004, 06:00:57 PM
Here's an update to Hooman's template, which he said I should post here. :) First are mission-type defines, then an updated starting resources function for different difficulties, then I changed Hooman's create-tube function so that if you HAVE to make tube vertical(or horizontal) it's easier.(just add "true" as a last parameter and it's the same as before)

/ ---------------------------------------------- //

// Dll Names (begin with)
// a = "autodemo"(op2 idle)
// c = ColonyMission
// mu = Multiplayer-Landrush
// mf = Multiplayer-Spacerace
// mr = Multiplayer-ResourceRace
// mm = Multiplayer-Midas
// ml = Multiplayer-LastOne

// missionType defines (Grabbed with hex editor from OP2's Dlls)
#define COLONY 0xFFFFFFFF
#define TUTORIAL 0xFFFFFFFD
#define MULTI_LR 0xFFFFFFFC
#define MULTI_SR 0xFFFFFFFB
#define MULTI_RR 0xFFFFFFFA
#define MULTI_MI 0xFFFFFFF9
#define MULTI_LO 0xFFFFFFF8

// ---------------------------------------------- //

void InitializePlayerResources(_Player &player)
{
// Easy
if(player.Difficulty() == 0)
{
player.SetOre(6000);
player.SetFoodStored(3000);
player.SetWorkers(30);
player.SetScientists(16);
player.SetKids(14);
}
// Medium
else if(player.Difficulty() == 1)
{
player.SetOre(5000);
player.SetFoodStored(2000);
player.SetWorkers(24);
player.SetScientists(14);
player.SetKids(22);
}
// Hard
else if(player.Difficulty() == 2)
{
player.SetOre(4000);
player.SetFoodStored(1000);
player.SetWorkers(22);
player.SetScientists(12);
player.SetKids(16);
}
}

// ---------------------------------------------- //

void CreateTubeOrWallLine(int x1, int y1, int x2, int y2, map_id type, bool vhpriority);

// Create a line of wall or tube
// Draws between the given coordinates
// If vhpriority is true, it draws horizontal first, then vertical
// else it draws vertical first, then horizontal
void CreateTubeOrWallLine(int x1, int y1, int x2, int y2, map_id type, bool vhpriority)
{

int i;
int vert;
int horz;

// Determine which edges to draw along
if(vhpriority) // Horizontal First
  {
  vert = x2;
  horz = y1;
  }
else // Vertical First
  {
  vert = x1;
  horz = y2;
  }

// Make sure (x1 < x2) and (y1 < y2)
if (x1 > x2){ x1 ^= x2; x2 ^= x1; x1 ^= x2; }
if (y1 > y2){ y1 ^= y2; y2 ^= y1; y1 ^= y2; }

// Create horizontal section
for (i = x1; i <= x2; i++)
  TethysGame::CreateWallOrTube(i, horz, 0, type);
// Create vertical section
for (i = y1; i <= y2; i++)
  TethysGame::CreateWallOrTube(vert, i, 0, type);
}

// ---------------------------------------------- //

Also, I just found out that Outpost2 only reads the first 6 letters of a dll file's name, so if you have dll's named say..."cKramy001.dll" and "cKramy002.dll" it won't distinguish between them at all. :heh: This brings up the possibility of running out of dll names for multiplayer, since we're restricted to very few letter-number combinations for multi... <_<

This was actually also causing a duplicate map problem in the maplist, where one of the options caused op2 to crash.
Title: Outpost 2 DLL Programming
Post by: PlayingOutpost0-24 on June 30, 2004, 04:19:15 AM
The ID list:
Strange... Spider is Tech Level 2 but you can only manifacture it on Tech Level 5... :P

Kramy: forgot this:
E - Eden Mission
P - Plymouth Mission
T - Tutorial

Of course, running out will be a time, the first two will be the same, but there is still room for (26 letters + 10 numbers) on the fourth, so it is 36*36*36*36=1,679,616 combinations overall for multiplayer. For single player it is 36*36*36*36*36=60,466,176... So there nothing to be afraid of. Yet. (Of course, subtract those that already are...)

EDIT: there is more, if we count that the file can be shorter than 6 letters...
EDIT2: Forgot that some of the symbols like "_" can be used...
Title: Outpost 2 DLL Programming
Post by: PlayingOutpost0-24 on July 01, 2004, 02:32:10 PM
It looks like the DLLs disasters repeat themselves... I had five meteors comint to exactly to the same place at different times... well 400-450 marks away from each other... is this repeating or it is prorammed to do this?
Title: Outpost 2 DLL Programming
Post by: Hooman on July 01, 2004, 02:45:17 PM
If you are using triggers to create the meteor, then the trigger may be called repeatedly. If you set the second parameter to a nonzero value in the Create(X)Trigger function then it will not be called repeatedly.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on August 02, 2004, 01:56:28 AM
Well, I'm not sure how helpful this stuff is but hopefully things will start moving a little on here.

Code: [Select]
	TethysGame::AddMessage(1712, 6544, "Hello There!", 0, 0);

I figured the first 2 parameters were x and y coordinates but I only just realised they are scaled by 32 (and offset by 16). Duh! The above numbers center the screen on (22, 205) when you use the goto option for the message. Basically, 22 on the display gets mapped to 22+31=53 for the internal game x coordinate, which is scaled by 32 to get 1696, then add 16 to get 1712. Likewise, 205 on the display gets mapped to 204 for the internal y coordinate, which is scaled by 32 to get 6528, then add 16 to get 6544.

I checked the above with a disaster centered at the same coordinates and the goto centers on it exactly, right down to the pixel. As a side note, the internal representation of the coordinates of your units matches the above. So if you want to move stuff around and the usual blocked coordinates don't work, try these kind.

I also found out how OP2 represents the direction your units are facing. It seems they use integer values from 0 to 255 to represent the angle. Here are a few values and the direction they represent. I believe when trucks are turning they will have values inbetween these ones, and that it depends on the turn rate of the vehicle, but I wasn't able to test it (lack of timing resolution).

Direction
0  Facing right
32 Facing diagonally down and right
64 Facing down
96 Facing diagonally down and left
128 Facing left
160 Facing diagonally up and left
192 Facing up
224 Facing diagonally up and right

I found this in memory right after the x and y coordinates of a vehicle. The fields appear to be 4 bytes each (but I didn't test and only the lower byte seemed to be used for the direction).

Trucks seem to have a cargo identifier that takes 4 bits, the upper part of the long(short?) represent the amount in the truck. So 1000 units of Common Ore would appear in memory as 16004, and 1000 units of food would appear as 16001.

As far as I can tell from the indexing used, Player records occupy 3108 bytes and units records occupy 120 bytes.

Here are some of the fields for the player records and apparent base offsets (in memory). I haven't really tested these. All fields appear to be 4 bytes unless noted.

56EF1C      Class pointer (GetRLVCount, GoAI, GoHuman, AllyWith)
56EF24      Number of Solar Satellites (upper nibble of LSB)
56EF28      Difficulty
56EF2C      Food Stored
56EF3C      Common Ore
56EF40      Rare Ore
56EF44      Is Human
56EF48      Is Eden
56EF4C      Color Number
56EF50      Morale Level
56EF68      Food Supply
56EFB0      Workers
56EFB4      Scientists
56EFB8      Kids
56F01C      Has active Command Center (number of them?)

Here are some miscellaneous values I came accross while looking these up.
56EB28 Your player number (This is checked when centering view for a player, probably so the game won't try to center view on someone else's base like in multiplayer).
5758B4 Object address used to center the view.
5632C0 Object used to mark research as complete or setting a tech level. It would appear to store the info for all the players.

The player structures don't seem to move around in memory but the unit structures do. The base address for a table of unit structures can probably be found at address 54F848. Here are a few fields I've seen for the unit structures and relative byte offsets.

0 - x coordinate
4 - y coordinate
8 - direction
12 - unknown
16 - cargo (see above)
20 - unknown
24 - vehicle number (so for Cargo Truck #1-02, this represents the 02)
28 - unknown
32 - unknown
36 - unknown
40 - animation sequence (This value cycles through some sequence while loading/unloading at a dock)
44 - unknown
48 - Bit 0 represents the state of the headlights. Rest are unknown.

Also note, the exported classes don't hold this data. They seem to be wrapper classes for internal classes that do hold this data. The exported _Player class uses 12 bytes (3 longs) where the first 4 bytes are the player number, and the next two seem to have something to do with _Player.resetChecks. (It sets the other 2 values to -1)

Also, I've noticed that the game seems to initialize the exported Player array with 7 elements, not 6. Check address 00477E40 if you don't believe me. It initializes 7 values (0-6) that are spaced 12 bytes apart and the base address is the exported Player array.

Now, I've come to many of these conclusions by reading code, not by experimenting so it's possible that I could have misread something. Other's I've come to by experimenting but not reading any code so it could be wrong or incomplete. Feel free to check and verify any this.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on August 13, 2004, 09:45:03 PM
Hmm, I think I've stumbled into something kinda important.

I was checking how much stack space routines in DLLs allocated for local variables and trying to figure out which locations correspond to which variables. I was mainly interested in determining the size of a few classes that OP2 exports. Well, I got a little confused trying to figure out which variable a certain location corresponds to since it was being used by more than one class. Then it struck me, one class was derived from the other.

ScGroup seems to be a parent class to the other group classes.

Code: [Select]
class OP2 MiningGroup : public ScGroup {

I got some of the computer's Cargo Trucks to move and actually collect some ore. I don't quite have all the details worked out yet though. I changed something and now the trucks start spinning in circles and moving back and forth, kinda like some strange dance.  :lol: It quite funny to watch. I'm tempted to post the DLL as is.

Well, I guess I should figure out how to make it work right now.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on August 14, 2004, 05:08:22 PM
Ok, I've got some working code and I have yet to see it do strange things so I'm posting it as is.

Code: [Select]
void OreRoute(int player)
{
Unit u1, u2, u3, u4, u5;
MAP_RECT rectMineGrp(LOCATION(60, 203), LOCATION(68, 210));

TethysGame::CreateBeacon(map_Mining_Beacon, 62, 215, beacon_common, yield_3, 1 /*variant*/);

TethysGame::CreateUnit(u1, map_Command_Center, LOCATION(62, 202), player, map_Default, 0);
TethysGame::CreateUnit(u2, map_Mine, LOCATION(62, 215), player, map_Default, 0);
TethysGame::CreateUnit(u3, map_Common_Ore_Smelter, LOCATION(62, 205), player, map_Default, 0);

MiningGroup miningGrp = CreateMiningGroup(Player[player]);
miningGrp.Setup(u2, u3, rectMineGrp);

TethysGame::CreateUnit(u4, map_Cargo_Truck, LOCATION(62,210), player, map_Default, 0);
TethysGame::CreateUnit(u5, map_Cargo_Truck, LOCATION(62,210), player, map_Default, 0);

miningGrp.TakeUnit(u4);
miningGrp.TakeUnit(u5);
}

Basically, it creates a beacon, mine, smelter and 2 cargo trucks and starts them harvesting ore. It actually works for human controlled players too, not just AI players.  (thumbsup) It could make for an interesting multiplayer setup for those that want to get straight to killing each other.  :lol:

The MAP_RECT seems to be set to an area around the smelter in the original DLLs. If you put in strange coordinates, then your trucks start getting confused and moving about in strange ways. This mostly occurs if your ore storage is full. In that case, the trucks don't return to the smelter dock and wait, they start moving about like they can't decide to go to the smelter or the mine. This eventually deteriorates and they start wandering all over the map. That doesn't seem to be an issue with the current code.

There is still a slight problem however. If you run this code for a human player, they lose control of the ore trucks. You can still click on them but they won't respond to your commands. I'm going to look into this, I think it can be fixed as my code doesn't compile to quite the same thing as the original DLLs. I'm thinking it's inheritance related.

I also noticed a section in CES1.dll that I assume is called by DllMain to initialize global variables. (It is called before InitProc when loading, and a corresponding section is called when unloading the DLL). It's basically a sequence of constructor calls and a sequence of destructor calls. (Found at address 11001010, with the destructor calls in the routine right after that). Anyways, the important point is that all the variable addresses are 4 bytes apart (mostly). Which should mean, that each class being initialized uses 4 bytes of memory for internal variables. (It looks like a few other global variables are declared throughout that don't need constructors to be called). Anyways, I figure the following classes all take 4 bytes: Trigger, BuildingGroup, FightGroup, MiningGroup. I also suspect that these 4 bytes are declared in a parent class such as ScStub or ScGroup.

Speaking of inheritance, I'm pretty sure that BuildingGroup, FightGroup, and MiningGroup all inherit from ScGroup. I'm also pretty sure that Trigger inherits from ScStub. The original DLLs frequently have calls to what I assume is the parent right after creating a child, and the "this" pointer points to the same area of memory. I suspect their may be a few more relations but I'd like to check them further before posting.

Also, I've noticed some strange things about my inheritance theory. Right after the call to create many "derived" classes, it seems a call to the "parent's" destructor is made. It might just be an error undecorating the name so I'm going to check into it and make sure it really is a destructor being called.


Edit: I'm quite sure ScGroup is a parent class to MiningGroup, FightGroup, and BuildingGroup. I've checked the constructors and destructors of each exported from Outpost2.exe and they all make calls to the constructor and destructor of ScGroup. In the case of the destructors, its a simple JMP to that of ScGroup. The constructors are almost as simple (but have to return "this" which needs to be saved on the stack during the nested constructor calls). I've also considered containment of ScGroup in the other classes but it seems unlikely the compiler would have produced such clean code, so I'm pretty sure of inheritance. By similar reasoning, I've also found that ScGroup, Trigger, and Pinwheel also inherit from ScStub.

Oh, and my previous tests had the desctructor for MiningGroup called, where the original DLLs had the destructor for ScGroup called. It seems they were just inlined to get rid of the JMP. To do the same, just add {} in the definition of the destructor so the compiler knows how to inline it. (It's still not quite the same though).
Code: [Select]
	~MiningGroup() {};

Also, I'd like to make clear that ScStub holds 4 bytes of data so anything that inherits from it also gets those same 4 bytes of data. Thus, the size of the above classes which I've listed as 4 bytes should not have variables of their own declared.

Oh, and I'm not sure if anyone else has posted or noticed this but the game makes reference to a "DescBlockEx" somewhere in the code. I assume it's for a more advanced DescBlock. Has anyone looked into this? It's near the code which loads the DescBlock and makes a call to the same location for retreiving the DescBlock export.

Edit #2:
I don't know why I didn't notice this before, but the original DLLs were creating new objects, copying them to global variables, and then deleting the temporary object. I think I probably didn't notice because much of the code has been inlined and it's very easy to lost track of what you've already looked at. Anyways, I've added the copy operator to a few classes. Just stick each one into it's corresponding class.

Code: [Select]
	class ScStub & ScStub::operator=(class ScStub const &);
class Trigger & operator=(class Trigger const &);
class Pinwheel & operator=(class Pinwheel const &);
class ScGroup & operator=(class ScGroup const &);
class BuildingGroup & operator=(class BuildingGroup const &);
class FightGroup & operator=(class FightGroup const &);
class MiningGroup & operator=(class MiningGroup const &);

That should account for the remaining differences between the original DLL code and what I've been writing. I'm still a little unsure about what values to give to MAP_RECT though. The ones in my sample were based on values I found while tracing through an original DLL but they don't quite produce the results I'm used to when you can't hold any more ore. (But it's at least pretty close). I'm thinking the defined rectangle should be made a little smaller.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on August 22, 2004, 01:27:48 AM
Ok, good news for people looking to add some AI in future levels. I've run a few successful experiments with getting the computer to build/rebuild a base. It is also possible to get your own vehicles to automatically build your own base. I might add that this makes testing a little nicer since you can see what's going on.

BuildingGroup
----------------
- The building group must contain the ConVecs used to build the buildings. They can be added using BuildingGroup.TakeUnit(unit) or one of the other methods to add units to a group.
- A Structure Factory can also be added to a building group if you want/need the kits to be built first. This can also be added using TakeUnit. If the ConVecs added to the group do not have the kits carried as cargo then this is the way it needs to be done.
- Construction of one building will not start until the construction of the last building has already started. So if you have two ConVecs already loaded with structure kits, one will move to where the building needs to be built and start building. Once it has started building, the other ConVec will then move to its build site and start building. The ConVecs will not move to the build site until the previous building has started construction. If the structure factory needs to build a kit, it will not start building the kit until the building before that kit has started construction.
- When a ConVec has nothing to do, (sometimes the initial startup for a few seconds, when waiting for a structure kit to finish building, or the list of buildings is complete) it will move to a predefined rectangle and wait. This rectangle is set using BuildingGroup.SetRect(MAP_RECT);
- The MAP_RECT set above should probably be set out front of the structure factory. It's very important to set this or the default is (0,0,0,0) so vehicles will move to the top left of the map otherwise. The rectangle should be at least 2x2 or the vehicles will move about continuously and never come to a full stop.
- To specify the list of buildings to be built, and their location, use BuildingGroup.RecordBuilding(LOCATION, buildingType, weaponType /*for turrets*/);
- The order you specify the buildings with RecordBuilding matters. They will be built in the order they are added to the group.
- If a building is dismantled/destroyed, it will automatically be rebuilt, in the order they were originally listed.
- If you use BuildingGroup.SetDeleteWhenEmpty(1); then the ConVecs will self destruct after they are done building (eventually). Watch out as this will damage nearby buildings.
- This WILL build buildings even if you have NOT done the research yet! Although, for a vehicle factory, you won't be able to build anything at it until you do the research.

Finally, here is come sample code:

Code: [Select]
void BaseLayout(int player)
{
Unit u1, u2, u3, u4, u5, u6;

TethysGame::CreateUnit(u1, map_Command_Center, LOCATION(62, 205), player, map_Default, 0);
TethysGame::CreateUnit(u2, map_Structure_Factory, LOCATION(58, 205), player, map_Default, 0);
TethysGame::CreateUnit(u3, map_Common_Ore_Smelter, LOCATION(53, 205), player, map_Default, 0);
TethysGame::CreateUnit(u4, map_Tokamak, LOCATION(60, 197), player, map_Default, 0);
TethysGame::CreateUnit(u5, map_ConVec, LOCATION(60, 207), player, map_Agridome, 0);
TethysGame::CreateUnit(u6, map_ConVec, LOCATION(60, 207), player, map_Standard_Lab, 0);

BuildingGroup &bldGrp = CreateBuildingGroup(Player[player]);
bldGrp.TakeUnit(u2);
bldGrp.TakeUnit(u5);
bldGrp.TakeUnit(u6);
bldGrp.RecordBuilding(LOCATION(62, 202), map_Agridome, map_Default);
bldGrp.RecordBuilding(LOCATION(62, 208), map_Standard_Lab, map_Default);
bldGrp.RecordBuilding(LOCATION(48, 205), map_Vehicle_Factory, map_Default);
bldGrp.SetRect(MAP_RECT(58, 207, 59, 208));

bldGrp.SetDeleteWhenEmpty(1);
}

Next up, FightGroup and Pinwheel. After that we can probably do most things the computer is able to do in the regular DLLs. If someone wants to spend enough time with this, we may be able to make a comp stomp mission soon.


GetSaveRegions
-------------------
While chatting on IRC, Hacker asked if I knew what this did so we both looked into it. I think we probably know what it does now. Here's the conclusion I've come to.

When your DLL is loaded, Outpost2.exe calls GetSaveRegions (before InitProc) and passes it what appears to be a pointer to a struct that describes a buffer. The struct appears to have two fields, a pointer to the start of a buffer and the length of the buffer. I haven't yet got code to work on this but I suspect it should look something like this:
Code: [Select]
struct BufferDesc
#pragma pack(1)
struct BufferDesc
{
void *bufferStart;
int length;
};
#pragma pack()
SCRIPT_API void GetSaveRegions(struct BufferDesc *buffDesc)
{
buffDesc->bufferStart = &DLLGlobalVars;
buffDesc->length = sizeof(DLLGlobalVars);
}

The #pragma might not be necessary but I've had the C++ compiler insert extra space for alignment into structures before and really mess things up. Note that the above code doesn't quite work. For some reason, when it returns it seems to pop 4 bytes off the stack thus throwing away the return address and trying to return to the buffDesc in the code section. Needless to say, this isn't good. It might be the calling convection, I'll look into it tomorrow.

Also, note that you only get one buffer and it's length is fixed. So all your data must be stored contiguously in memory. Although the C++ compiler tends to keep variables in memory in the order in which they are declared, this is not part of the C++ specification for it's probably best to store the variables in a struct. Also, this is only really useful for variables that are global to the DLL (which is exactly what is needed).

Oh, and when tested on a regular DLL, the save game files do contain the length of the buffer followed by the buffer contents.


Mines.txt
----------
I've posted this else where but I thought I'd keep this info in the programming thread.

YIELD: LOW (1 bar), MED (2 bar), HIGH (3 bar) refers to the "bar" of the mine
VARIANT: Each type of mine has 3 variants which have characteristic Initial, Peak and Min (long run) yields. They also specify how fast the yield moves between these values.
INITIAL_YIELD_%, PEAK_YIELD_%, and MIN_YIELD_% specify the percentage of ore (out of 1000) that each cargo truck carries for the mine's initial, peak, and min yields. The actual yield varies linearly between these values with each load
PEAK_TRUCK, and MIN_TRUCK specify how fast the yield of the mine changes when moving to the corresponding yield. It specifies the number of truck loads to move from one to the next. So for:
Quote
High   1   50   14   60   50   30
we have a 3 bar mine, this is the first variant of it, the initial ore yield is 500, the yield increases linearly over a period of 14 truck loads from 500 to a peak of 600, and then it decreases linearly over a period of 50 truck loads from 600 down to 300 at which it remains stable.

The last parameter in the function CreateBeacon specifies the variant. Note that the mines.txt files specifies the variants as 1,2,3 but the function uses values 0, 1, 2. (If a greater value is passes to the function, it seems to use it modulo 3).
Code: [Select]
	static int __fastcall CreateBeacon(map_id beaconType, int x, int y, int type, int yield, int variant);
Title: Outpost 2 DLL Programming
Post by: Hooman on August 24, 2004, 01:25:21 AM
GetSaveRegions
-------------------
I finally seem to have GetSaveRegions working without crashing. (I should probably test things a little more though).

First, I defined the following struct:
Code: [Select]
struct BufferDesc
{
void *bufferStart;
int length;
};
Then I changed the declaration of GetSaveRegions to:
Code: [Select]
SCRIPT_API void __cdecl GetSaveRegions(struct BufferDesc &bufDesc);
Note the "__cdecl". For this function, the caller (Outpost2.exe) needs to clean up the stack. Here is a sample implmentation:
Code: [Select]
// Used for storing DLL specific data to a saved game file
void __cdecl GetSaveRegions(struct BufferDesc &bufDesc)
{
bufDesc.bufferStart = &enumBuilding;
bufDesc.length = sizeof(enumBuilding);
}
In this example, I only had one variable to save. Otherwise, the variables should probably be collected into a struct.


Exported class sizes
-----------------------
I finally went through all the exported classes and found out how much memory they all take. The compiler of course needs to know this when you are creating an instance of a class so it knows how much memory to allocate for the storage of its variables. When not accounting for this, memory tends to get corrupted and you get erratic behavior, particularily with stuff that used to work fine before you created a class with the wrong size. (I know this from experience all too well). I have to admit though, I haven't tested all this stuff so some of it could be wrong. (I'm pretty sure these are all at least a minimum bound on the size).

_Player, 12 bytes (3 DWORDs)
Unit, 4 bytes (1 DWORD)
UnitBlock, 136 bytes (34 DWORDs)
PlayerVehicleEnum, 4 bytes (1 DWORD)
PlayerBuildingEnum, 8 bytes (2 DWORDs)
PlayerUnitEnum, 8 bytes (2 DWORDs)
GroupEnumerator, 4 bytes (1 DWORD)
ClosestEnumerator, 52 bytes (13 DWORDs)
InRangeEnumerator, 52 bytes (13 DWORDs)
InRectEnumerator, 52 bytes (13 DWORDs)
LocationEnumerator, 52 bytes (13 DWORDs)
ScStub, 4 bytes (13 DWORDs)
GameMap, ?1 byte?
TethysGame, ?1 byte?

Note that GameMap and TethysGame have all static members so the idea of instantiating one is kinda silly and pointless. The assignment operator defined for them still seems to copy 1 byte though. Most of the sizes I got by looking at the assignment operator defined for these classes and seeing how much data was copied. I double checked most of it with the constructors as well. Basically, I checked to see how far off of the "this" pointer was data accessed. In general, I didn't check every method so it's possible that a variable is used that doesn't get copied during assignment or initialized in the constructor (but unlikely). I've also omitted all classes derived from ScStub as they have no new variables declared and so they are all 4 bytes. This should now account for the size of all classes. As for what the variables represent, much of that is still unknown but also largely unimportant. There are still a few structs whose size and fields need to be determined though. With any luck, I'm hoping a new DLL template will be posted soon. I guess it depends on how soon I get my updates to Hacker.



Some misc info here. When OP2 is loading a DLL, it creates a table with the following values (I might be missing some at the beginning or end; & denotes address of):
&InitProc
&AIProc
&StatusProc
BufferDesc.startBuffer
BufferDesc.length

These values are stored in the table during a series of calls to GetProcAddress. When it gets to GetSaveRegions, it doesn't save the address but instead, if the export is found, it calls it (with the address of the place in the table for the buffer info) so that the buffer values can be stored in the table. If GetSaveRegions was not found, it stores 0 for startBuffer. It seems GetSaveRegions is not a required export and I've gotten a level to work fine without it.

Other minor changes, I'm too tired to write about so I'll just stuff them into the updated header file.  :yawn:
 
Title: Outpost 2 DLL Programming
Post by: Hooman on August 26, 2004, 01:46:54 AM
Well, in possible preparation for actually making decent new levels, I thought I'd look a little more into Victory and Failure conditions. First, for a typical Last One Standing type of multiplayer mission, the following code creates the necessary victory conditions (and apparently failure conditions as well):

Code: [Select]
// For use in multiplayer. Note: Computer controlled opponents do not count towards this.
// You win when there is only one human opponent left or all surviving human players are allied.
// This also creates corresponding failure conditions
void CreateLastOneStandingVictoryCondition()
{
Trigger trig;

trig = CreateOnePlayerLeftTrigger(1, 1, "NoResponseToTrigger");
CreateVictoryCondition(1, 1, trig, "Eliminate your opponents.");
}

As the note says, this is for multiplayer. It's completely useless for single player. The computer controlled oppenents do not count as live opponents, so you win as soon as the level start in single player (unless you set a computer controlled opponent as human using _Player.GoHuman() ).

For single player, this trigger setup might be more useful for a failure condition.
Code: [Select]
// Player fails if the number of active Command Centers (for player 0) becomes equal to 0.
void CreateNoCommandCenterFailureCondition()
{
Trigger trig;

trig = CreateOperationalTrigger(1, 1, 0, map_Command_Center, 0, cmp_Equal, "NoResponseToTrigger");
CreateFailureCondition(1, 1, trig, "");
}

The CreateOperationTrigger creates a trigger that fires whenever the number of active buildings of a certain type equals (or other kind of compare) a certain number. Here's the new definition:
Code: [Select]
OP2 class Trigger __fastcall CreateOperationalTrigger(int boolEnable, int boolNoRepeat, int playerNum, enum map_id buildingType, int refValue, enum compare_mode compareType, char const *triggerFunction);



As an interesting note, all trigger creation functions seem to follow a certain format. Even when they don't need or even use certain parameters. As far as I can tell, the first two parameters are always boolean values to enable the trigger and to set it so it only fires once (int boolEnable, int boolNoRepeat). The last parameter is always a text string that usually specifies a DLL callback function where you can have your own code execute. The exceptions seem to be for the CreateVictoryCondition, and CreateFailureCondition functions. For CreateVictoryCondition, the text string appears as your mission objectives (you can have multiple objectives by calling CreateVictoryCondition multiple times), and for CreateFailureCondition the text string seems to do nothing and the regular DLLs pass it a null string. It was also interesting to see that CreateFailureCondition completely ignores it's second parameter (int boolNoRepeat, if we can really call it that). I put a hardware breakpoint on the memory location for that parameter to see if it is ever accessed, and it never was. Comparing it to CreateVictoryCondition, what is normally read as that parameter was just set to 0.


When writing the callback functions for trigger, it's important that you declare them as 'extern "C"'. I found that not doing this can really muck up the order/position of your exports, which generally causes the wrong exports to be found by OP2, OP2 not finding your exports, or just plain crashing. Usually OP2 doesn't recognize your DLL or it just crashes. Also, if you make sure to declare all exported trigger functions with 'extern "C"', you don't have to worry about specifying export ordinals in the .def file.
Code: [Select]
extern "C" void MyTriggerFunction()
{
    // Do stuff in here
}



I don't really have any other DLL coding info today as I was busy with other things. One of which was preparing an updated template project. Hacker should be looking it over and adding his magic to it.  B) It would be nice to see other people posting about their experiments on here once that gets out.



Also, I spent some time looking at OP2's network code to see why people can't host behind a NAT enabled router. It seems that the server code in OP2 packages the IP address of the server into a packet and sends it to the clients, which then begin using this address to communicate with the server. There's no trouble getting into the pregame setup since this address packaging doesn't happen until the host clicks "start". Then of course, people get the infamous "Replicating Players list failed" error message.

Anyways, I found a temporary way around this. I basically patched the address in the packet to my external address and everything all worked fine. I tested it at home with two computers on either side of a router and then again on the Internet. It's kindof a cheap hack since I hardcoded my external IP address into the exe (actually I was running it under a debugger and had only patched the memory, which I forgot to save  <_< ). In short, I modified one line and added two extra lines, which I'll describe below.

At address 00496DC0, there is a procedure where the packet with the server's (local LAN) IP address gets sent to the clients. The IP address is at offset 0x16 from the start of the packet data. As a temporary fix, I changed the call to winsock.sendto at address 00496E85 from:
Code: [Select]
00496E85     E8 C89F0200    CALL <JMP.&WSOCK32.#20>
to
Code: [Select]
00496E85     E8 367D0300    CALL Outpost2.004CEBC0
Then, near the end of the exe where it's all 0s at address 004CEBC0 I inserted the following two instructions:
Code: [Select]
004CEBC0     C747 16 44924F64    MOV DWORD PTR DS:[EDI+16],<your external IP address>
004CEBC7    ^EB F7               JMP SHORT Outpost2.004CEBC0; Jump to winsock function


I'll have to speak with Hacker about a more permanent solution. Heck, maybe this will even help with op2sock. The sad part is, the client is already able to communicate with the server using the correct IP address before this. It would be nice if there was an easy way to just copy over the already used good IP into this memory location.


Ahh, and of course while I had hijacked the router to test all this, someone naturally tried to access the internet. As you can expect, this led them to chasing non blinking LAN activity lights, pondering why cables were unplugged, feeble attempts to restore internet activity by plugging the cable modem into a switch, etc. Until they suddenly come to the realization "... wait a minute, where the H*## is the router?!"  :whistle:  :lol:
Title: Outpost 2 DLL Programming
Post by: BlackBox on August 27, 2004, 01:18:21 PM
BTW.. the last parameter on CreateUnit controls the rotation of a vehicle.

Code: [Select]
static int __fastcall CreateUnit(class Unit &returnedUnit, enum map_id unitType, struct LOCATION location, int playerNum, enum map_id weaponType, int rotation);

the rotation amount appears to change it in 45's, going counterclockwise like so:
0 = face east
1 = face NE
2 = face north

and so on...

Here are two defines to use to make X,Y coords much easier to look at at code:
Code: [Select]
#define X(n) n+31
#define Y(n) n-1

// example
LOCATION loc(X(3),Y(3)); // would equal 3,3 in game reality

Also.. the location that is expected by CreateUnit isn't a definite point on a structure (it doesnt refer to the top corner of the border, or the topleftmost physical part of it) It appears that the game estimates the center point of the structure and sets the center point of the unit to those coordinates.

The fault in this, however, is there isnt an absolute center point on some structures (eg the Command Center. its a 3x2 structure not counting border.) This screwed me up for quite awhile. (and made for some nasty looking bases, with buildings overlapping each other -- it may be interesting, but bad if the building contains docks, as you can't access or use them anymore.))
I think we'll have to just have a hard list for all the structures of their center points so we know how to place the structure properly (or make a bunch of defines to automate structure placement)
Also. structures will overlap other structures and impassable terrain (this is not the case for vehicles, which are placed in the closest location possible)

Another game oddity:

Creating a OnePlayerLeft trigger tied to a victory condition in multiplayer doesn't work well for me. It is fired immediately and says that I have won the game right as the game starts. Perhaps someone has any idea on that (I did place all the needed stuff to survive in the mission)

Some notes on the DLL functions:
AIProc() is a procedure that is called every game tick (which is 1/10th of a time mark). It might be useful for controlling AI (like the name suggests), but I've used it to make structures invincible (except by supernova attack) and to give people unlimited ore.

Code: [Select]
void ResetPlayerResources(_Player &player)
{
// reset player resources
player.SetWorkers(20);
player.SetOre(10000);
player.SetRareOre(5000);
}
void AIProc()
{
// reset everything every tick
int i = 0;
// reset everyone's resources
for (i=0; i<TethysGame::NoPlayers()-1; i++) {
  ResetPlayerResources(Player[i]);
}
// restore all the buildings to 0 damage
for (i=0; i<TethysGame::NoPlayers()-1; i++) {
  playerCC[i].SetDamage(0);
  playerVF[i].SetDamage(0);
  playerCS[i].SetDamage(0);
  playerAF[i].SetDamage(0);
  playerRS[i].SetDamage(0);
  playerSP[i].SetDamage(0);
}
}

(Note that those playerXX[] arrays are arrays of Units -- they are the structures for all the players)

I'm now currently working on the wreckage functions and trying to figure out how to use CreateWreck and PlaceMarker.
Title: Outpost 2 DLL Programming
Post by: Hooman on September 04, 2004, 12:31:42 AM
Hmm, I thought this might be of interest. I did a diff on various versions of the exe to see what's changed over the versions. I was mainly interested in the unofficial changes but I took a look at the official updates too. The first one updates Outpost2.exe but the second and third updates don't. In particular, the following functions were cut out of Outpost2.exe by the first update.

TethysGame.SetCheatFastUnits
TethysGame.SetCheatFastProduction
TethysGame.SetCheatUnlimitedResources
TethysGame.SetCheatProduceAll

When I first saw they were cut out I thought it was from an unofficial update to discourage cheating but I guess not. Anyways, I thought I'd mention it since I was copying the format of the original DLLs for colony games which all call these functions with a value of 0 (which I assumed was false and disabled the cheat). Anyways, if they're in anything I've posted or released, be advised that they're completely pointless.

Oh, and I was a bit curious about why they were deleted so I took a look at them. I guess if someone decided to get smart, they might have called it thinking they could cheat, but I'm pretty sure it would desync a multiplayer game if it was only called in one client. Heck, even if every client called it, it looks like it might still desync a multiplayer game since it chagnes the normal flow of execution in the game engine. At any rate, it was probably just used for testing, which it certainly would have been useful for.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on September 21, 2004, 01:28:27 AM
Hmm, I took a look at the unit creation code to see how it places buildings. It's cased so that buildings are created seperately and it does do some coordinate adjustments. I'm not exactly sure how they're supposed to work but it does seem to adjust the coordinates based on the building size. I'll have to see what's at the memory locations being accessed to be sure though. (It kinda looks like it adjusts the buildings that are an odd tile size. But then that doesn't seem quite right.)

I also came accross where it convers the last parameter (direction) to the internal direction format. It scales it by 32 (shifts left by 5) to the internal format I described in an earlier post. (Doh! That's why my earlier attempts failed so miserably at figuring out what it did, I was using values to large.) Well, thank you Hacker for that one. It was killing me.

Anyways, since the direction of rotation didn't match my previous post, I looked into it and I'm getting rotation by 45 degrees in a clockwise direction. So I've been getting:
0 = right
1 = down, right
2 = down
...

I've taken a look at CreateWreck and I'm still a little baffled by it's behavior (and analysing the assembly for it hasn't helped too much so far). But here's what I've got for it.
Code: [Select]
	static int __fastcall CreateWreck(int x, int y, enum map_id, int boolNoIdea);	// **Figure out last param**
The strange thing is, I get different stuff depending on where the wreck is created. If the y coordinate was less than 9, I got rare rubble when the Cargo Truck picked it up. Anything wreckage placed below that appeared properly as wreckage. I had no trouble create multiple wrecks as long as they were below that y coordinate. Also, changing the map_id param didn't seem to affect it in any noticable way. The last parameter controls some kind of boolean switch (learned that from analysing the assembly code) but I'm not yet sure what the two cases of the IF are. The parameter is only used once, when it's branching.

Also, remember that you have to have a scout nearby where the wreckage is placed for the flag to appear. Without the scout, you can't tell if the wreckage has been placed.

I also had some fun with EMP missiles. Here's how to launch them   :D
Code: [Select]
	static void __fastcall SetEMPMissile(int launchX, int launchY, int, int destX, int destY); // Set third param to 0
You don't need to launch from a Spaceport. They can launch from anywhere on the map (but it looks awkward if it's not from a Spaceport).

Oh, and I've been using the more intuitive name
Code: [Select]
	map_None = 0,
in the map_id enum rather than the previous map_Default. Not really important but if we're gonna change little things like that, it's probably better to do it now before people start writing lots of code.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on September 23, 2004, 11:52:53 PM
Ok, I think I understand a bit more about how CreateWreck works. It suddenly occured to me that the enum map_id parameter represents the tech number of the technology you get from returning the wreck.

Code: [Select]
	static int __fastcall CreateWreck(int x, int y, enum map_id techNum, int boolNoIdea);

Hence for the tiger speed updgrade, the tech num is 11999 (0x2EDF) which matches the value in the original DLLs. I still don't know what the last parameter does but I'm pretty sure it's some kind of switch.

Anyways, you can create multiple wreck on the same map. So you could potentially put wreck in everyones base and allow everyone to get fast tigers. The tigers move slightly faster than panthers but still slower than lynxes. The effect takes place once the wreckage is unloaded into a spaceport.

It might also be worth while to see if other techs could be obtained from wreckage. My initial attempts failed. It just crashed the game when I tried to unload the wreckage. It might only work for that one tech, or perhaps it depends on the level of the tech. (Tiger speed upgrade is the only 11XXX tech I know of.)


Good news for DLL programming. I've successfully tested a from-scratch multiplayer DLL with working victory and failure conditions. The map seems to be completely playable. I'd like to make a few quick fixes before releasing it though. I plan to randomize the starting locations, allow the day and night setting to be adjustable, and possibly add support for disasters (but probably not likely before releasing it).

While doing this, I've found that vehicles for certain players show up as white on the minimap no matter what their color is and buildings don't show up at all. This seems to be a DescBlock problem. By changing the second value to the number of players, it all works fine. The following is for a 4 player map.

Code: [Select]
SCRIPT_API SDescBlock DescBlock = { MULTI_LastOneStanding, 4 /*number of players*/, 0x0C, 0, MapName, LevelDesc, TechtreeName, 0};

Here is the corresponding update to the structure definition.

Code: [Select]
// Structure for important data exports needed for OP2 to recognize the level
struct SDescBlock{
int missionType; // For campaign games, this is the level number (positive values)
int numPlayers;  // For multiplayer, this is the number of players
int unknown2;
int unknown3;
char *MapName;
char *LevelDescription;
char *TechtreeName;
int unknown4;
};

I've also listed a number of mission types and the DLL name prefixes. I'm not sure when I added it to the Template.h so here it is just to make sure everyone has it.

Code: [Select]
// missionType defines (and prefix needed on DLL name)
#define COLONY     -1 //0xFFFFFFFF // c
#define AUTODEMO    -2 //0xFFFFFFFE // a
#define TUTORIAL    -3 //0xFFFFFFFD // t
#define MULTI_LandRush   -4 //0xFFFFFFFC // mu(x)
#define MULTI_SpaceRace   -5 //0xFFFFFFFB // mf(x)
#define MULTI_ResourceRace  -6 //0xFFFFFFFA // mr(x)
#define MULTI_Midas    -7 //0xFFFFFFF9 // mm(x)
#define MULTI_LastOneStanding -8 //0xFFFFFFF8 // ml(x)

I verified all of the above values since I got some funny results using info from a previous post. (I may have just typed it in wrong though.) I've also switched to using negative values instead of the 8 digit hex number. I figured that's the way the original programmers probably defined it.


Does anyone know if the .map files could potentially have extra data stored at the end which could be accessed by the DLL? I was thinking we might be able to store stuff like Mine, Magma Vent, Fumarole, or Unit data into the map file and have it processed by the DLL. It might make it easier to add support for these things into a map editor.
 
Title: Outpost 2 DLL Programming
Post by: PlayingOutpost0-24 on September 27, 2004, 12:25:19 PM
Hooman, first of all, look in Multitek.txt
I only found the Tiger Speed Modification that is wreckage-based.

Can someone help me fix these errors i get when compiling C++ DLL?
F:\Outpost 2\OP2Script\OP2Script\Colony\CENSB1.cpp(25) : error C2065: 'map_Default' : undeclared identifier
F:\Outpost 2\OP2Script\OP2Script\Colony\CENSB1.cpp(34) : error C2664: 'DoTransfer' : cannot convert parameter 1 from 'class _Player *' to 'int'
        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
F:\Outpost 2\OP2Script\OP2Script\Colony\CENSB1.cpp(101) : error C2447: missing function header (old-style formal list?)
Title: Outpost 2 DLL Programming
Post by: BlackBox on September 27, 2004, 02:47:00 PM
Quote
Does anyone know if the .map files could potentially have extra data stored at the end which could be accessed by the DLL? I was thinking we might be able to store stuff like Mine, Magma Vent, Fumarole, or Unit data into the map file and have it processed by the DLL. It might make it easier to add support for these things into a map editor.
Yea, you can add data to the end of the map file without suffering any ill consequences. I've done it before (and old versions of my map editor did it, too. there was a bug that has now been fixed that added about 20K of redundant data every time you saved... so some maps were 5mb in size, because of this crap on the end)

PlayingOp2: it's easier to diagnose errors if I have a copy of the .cpp file that had the errors.

However:
F:\Outpost 2\OP2Script\OP2Script\Colony\CENSB1.cpp(25) : error C2065: 'map_Default' : undeclared identifier

change map_Default to map_None and you're all set.

F:\Outpost 2\OP2Script\OP2Script\Colony\CENSB1.cpp(34) : error C2664: 'DoTransfer' : cannot convert parameter 1 from 'class _Player *' to 'int'

It's expecting a reference to the player... not the numeric value... so if you wanted to transfer to player 1 you'd use
DoTransfer(Players[0], . . .

F:\Outpost 2\OP2Script\OP2Script\Colony\CENSB1.cpp(101) : error C2447: missing function header (old-style formal list?)

Simple C error. you forgot to prototype the function.
Title: Outpost 2 DLL Programming
Post by: Hooman on September 28, 2004, 01:57:40 PM
That should work if you've declared Player and Computer as follows:

Code: [Select]
_Player *Player;
_Player *Computer;

In which case you could have to access class members as follows:

Code: [Select]
Player->SetOre(5000);

Or you could declare something like this:
Code: [Select]
_Player &Player = Player[0];
_Player &Computer = Player[1];

and then just access the members with the usual ".":

Code: [Select]
Player.SetOre(5000);
Title: Outpost 2 DLL Programming
Post by: BlackBox on September 28, 2004, 02:58:13 PM
Correction: would it not be
Code: [Select]
_Player &Player = Players[0];
_Player &Computer = Players[1];

(Note the s in the array name)
Title: Outpost 2 DLL Programming
Post by: Hooman on September 28, 2004, 08:57:06 PM
I don't do that anymore. I did once try something like this:

Code: [Select]
_Player &plyr = Player[0];
_Player &comp = Player[1];

plyr.SetOre(5000);
comp.SetOre(5000);

I don't bother doing things that way anymore though. I just use Player[0], or Player[1], or just Player[playerNum] where ever I need it.

And it's
Code: [Select]
OP2 class _Player Player[7];
without an "s" at the end.


Ok, looks like it's time to post some new info.

First up, random numbers. I used the built in random number generation to randomize the starting locations. (Actually you would have to use the built in function or each client would randomize differently).
Code: [Select]
	static int __fastcall GetRand(int range);  // Returns a number from 0 to (range-1)
static void __fastcall SetSeed(unsigned int randNumSeed); // Set random number seed

I imagine the SetSeed function won't be of much use. A seed is set when the level starts up and all clients are syncronized to it. This ensures that random numbers on all the clients are the same (without having to send the numbers across the network). Being able to set the seed certainly has benefits, just none that I can think of for DLL programming.

Anyways, here's come code to randomize a list of numbers (randomly reorder the list).
Code: [Select]
// Note: Uses TethysGame::GetRand();
void RandomizeList(int numItems, int list[])
{
int i;
int next;
int temp;

// Randomly reorder the list (backwards)
for (i = numItems-1; i; i--)
{
  // Determine which item to place next in list
  next = TethysGame::GetRand(i+1);
  // Swap the 'next' item into place
  temp = list[i];
  list[i] = list[next];
  list[next] = temp;
}
}
Note that I may have changed the above code since I last tested it, so don't be too surprised if it has dumb mistakes in it. Also, I'm not using this code, but I am using similar code to randomize starting locations.

Code: [Select]
	static void __fastcall AddMessage(int x, int y, char *message, int recipientPlayerNum, int soundToPlay);
static void __fastcall AddMessage(class Unit, char *message, int recipientPlayerNum, int soundToPlay);
I finally figured out what all the parameters mean. For the second form of AddMessage, the coordinates of the unit are read and the rest is the same as the first form of AddMessage. The x and y coordinates are pixel coordinates (tile coordinates * 32). If recipientPlayerNum is -1, the message is sent to all players, otherwise only to the player whose number is specified (0 based). The soundToPlay determines which sound effect or voice to play when the message is displayed (beeps, bloops, and "Power level optimal" sort of thing).

I figured out some of the oddity associated with CreateWreck.
Code: [Select]
	static int __fastcall CreateWreck(int x, int y, enum map_id techID, int boolAllPlayers); // Note: techID must be >= 8000 but < (8000+4096)
(Btw, I'm not too sure what the last parameter means yet, but it definately controls whether or not something is processed for all players or not).
It seems 8000 is added to the stored value in memory, so only techs with an ID above 8000 can be given from wreck. Also, there are only 12 bits available so there is a max techID of 8000+4095.

The reason for this, is that Cargo Trucks store the cargo type in the same WORD (16 bits) as the cargo attribute. The lower 4 bits are for the cargo type (commore ore, rare ore, common metal, rare metal, wreckage, etc.) and the upper 12 bits for the attribute, such as how much ore, or what techID to get from the wreckage. The game stores the value so that 8000+attribute gives techID. You just specify the techID to create wreck, and the game converts to it's internal format.

As an example:
Code: [Select]
	TethysGame::CreateWreck(95, 63, (map_id)8201, 0); // Tech #11999 - tiger speed upgrade
This was used to create wreckage that gave the technology to build Tigers (techID 8201) and placed the wreckage in the middle (approximate) of a 128x128 map.

Note: You would still need at least Cybernetic Teleoperation, Mobile Weapons Platform, and a weapon tech to make use of it (possibly others, maybe less).

I've also found that certain functions take a player number which I had previously thought were boolean true/flase values.
Code: [Select]
	static void __fastcall ForceMoraleGood(int playerNum);
static void __fastcall ForceMoraleGreat(int playerNum);
static void __fastcall ForceMoraleOK(int playerNum);
static void __fastcall ForceMoralePoor(int playerNum);
static void __fastcall ForceMoraleRotten(int playerNum);
static void __fastcall FreeMoraleLevel(int playerNum);
During a round of testing, I was surprised to find that morale was only steady on one computer when using:
Code: [Select]
FreeMoraleLevel(1);
Anyways, after a few minutes it dawned on me that the parameter is a player number of who it affects (0 based). You can use -1 for everyone.



That seems to be all the DLL programming info I can think of. Now here's a bit of hacking info (still somewhat incomplete). The following stuff corresponds to values and data structures I've found in memory.

54FC5C      Base address of map tile data

Map Tile Format (32 bits)
-------------------------
Bit Offset    length    BitMask    Description
----------    ------    -------    -----------
0x0       5    0x0000001F    Cell type (GetCellType)
0x5       11    0x0000FFE0    Tile (GetTile)
0x10       11    0x07FF0000    unitID of unit on this tile
0x1B       1    0x08000000    
0x1C       1    0x10000000    Lava possible (SetLavaPossible)
0x1D       1    0x20000000    
0x1E       1    0x40000000    (SetVirusUL)
0x1F       1    0x80000000    

I'm tempted to post some info on the internal format of player and unit data as well as some global variables but I figure I should fill in more of the details first. Btw, I was wondering how close the the internal format is to the .map file format. Any info on it? I'm guess the unitID probably isn't part of the file format.
Title: Outpost 2 DLL Programming
Post by: BlackBox on September 29, 2004, 03:18:08 PM
Yes, you need to add the op2.lib to your project.

You need to turn off default libraries and set the entry point to DllMain.

You need to compile in release mode.

(it worked for you when you did this, this is a note to all who do this)

Regarding the map format:

The only information in the map files are the dimensions of the map, and the tile id (within the tile id it specifies the image well index, cell type (if that refers to how fast units can cross it), and the index of the image within the image well.))
The tile set information is stored after the tile data at the end (search for a 20+ byte block of nulls and/or the string "TILE SET".)
Title: Outpost 2 DLL Programming
Post by: Hooman on October 02, 2004, 09:27:23 PM
Here's a table of sounds for anyone that needs it. These are for use with AddMessage or AddGameSound. I'm not sure if index 0 really works. I seem to get just a beep with AddMessage. The rest seem to work right though.

A table containing pointers to sound file filenames can be found at address 004E0170. There are 252 entries in the table.

Index    Pointer    Filename    Speech
-----    -------    --------    ------
0    004E015C     "bld_fal3.wav"
1    004E014C     "bld_fal1.wav"
2    004E013C     "bld_fal2.wav"
3    004E012C     "bld_make.wav"
4    004E0120     "ag_sel.wav"
5    004E0110     "tokamsel.wav"
6    004E0100     "pantheon.wav"
7    004E00F4     "commsel.wav"
8    004E00E8     "dirt.wav"
9    004E00D8     "fac_andr.wav"
10    004E00C8     "fac_cons.wav"
11    004E00BC     "fac_sel.wav"
12    004E00BC     "fac_sel.wav"
13    004E00B0     "gar_sel.wav"
14    004E00A4     "geo_sel.wav"
15    004E0098     "gorf.wav"
16    004E0088     "guardsel.wav"
17    004E007C     "lab_3.wav"
18    004E0070     "lab_1.wav"
19    004E0064     "lab_2.wav"
20    004E0058     "light.wav"
21    004E004C     "mag_sel.wav"
22    004E0040     "medisel.wav"
23    004E00E8     "dirt.wav"
24    004E0034     "mine_1.wav"
25    004E0028     "mine_2.wav"
26    004E001C     "nur_sel.wav"
27    004E000C     "observat.wav"
28    004E0000     "rec_fac.wav"
29    004DFFF0     "res1_sel.wav"
30    004DFFE0     "res2_sel.wav"
31    004DFFD0     "res3_sel.wav"
32    004DFFC4     "robosel.wav"
33    004DFFB4     "smelter1.wav"
34    004DFFB4     "smelter1.wav"
35    004E0110     "tokamsel.wav"
36    004DFFA4     "spaceprt.wav"
37    004DFF94     "storage1.wav"
38    004DFF84     "storage2.wav"
39    004E0110     "tokamsel.wav"
40    004E0100     "pantheon.wav"
41    004DFF78     "uni_sel.wav"
42    004DFF6C     "consvhc.wav"
43    004DFF5C     "dozr_sel.wav"
44    004DFF4C     "evac_trn.wav"
45    004DFF3C     "earthwrk.wav"
46    004DFF30     "robot4.wav"
47    004DFF24     "spidsel.wav"
48    004DFF14     "infantry.wav"
49    004DFF08     "repvhc.wav"
50    004DFEFC     "robmine.wav"
51    004DFEF0     "robsurv.wav"
52    004DFEE4     "scout.wav"
53    004DFED8     "combhev.wav"
54    004DFECC     "comblit.wav"
55    004DFEC0     "combmed.wav"
56    004DFEB0     "truk_sel.wav"
57    004DFEA4     "bld_exp.wav"
58    004DFE94     "uni_grad.wav"
59    004DFE88     "tokaexp.wav"
60    004DFE7C     "geyser.wav"
61    004DFE70     "gar_doc.wav"
62    004DFE60     "doc_dor1.wav"
63    004DFE50     "doc_dor2.wav"
64    004DFE40     "doc_grab.wav"
65    004DFE34     "sulv.wav"
66    004DFE28     "robdep.wav"
67    004DFE1C     "reprep.wav"
68    004DFE10     "consrep.wav"
69    004DFE04     "dump.wav"
70    004DFDF8     "micwarn.wav"
71    004DFDEC     "micfire.wav"
72    004DFDE0     "bigexp1.wav"
73    004DFDD4     "bigexp2.wav"
74    004DFDC8     "bigexp3.wav"
75    004DFDBC     "medexp1.wav"
76    004DFDB0     "medexp2.wav"
77    004DFDA4     "smalxp1.wav"
78    004DFD98     "smalxp2.wav"
79    004DFD8C     "smalxp3.wav"
80    004DFD80     "quake.wav"
81    004DFD70     "tornado2.wav"
82    004DFD64     "thunder.wav"
83    004DFD58     "microbe.wav"
84    004DFD4C     "fuse.wav"
85    004DFD40     "beep2.wav"
86    004DFD40     "beep2.wav"
87    004DFD34     "beep5.wav"
88    004DFD28     "bld_not.wav"
89    004DFD1C     "beep6.wav"
90    004DFD10     "beep9.wav"
91    004DFD04     "beep8.wav"
92    004DFD34     "beep5.wav"
93    004DFCF4     "message2.wav"
94    004DFCE4     "savnt212.wav"    "Structure completed"
95    004DFCD8     "savant2.wav"    "Research completed"
96    004DFCCC     "savant3.wav"    "Our colony is under attack"
97    004DFCC0     "savant4.wav"    "We are under attack"
98    004DFCB4     "savant6.wav"    "Vehicle ready"
99    004DFCA8     "savant7.wav"    "Consumer items completed"
100    004DFC9C     "savant8.wav"    "Structure kit manufactured"
101    004DFC90     "savant9.wav"    "Spider ready"
102    004DFC80     "savant10.wav"    "Scorpion ready"
103    004DFC70     "savant11.wav"    "Structure destroyed"
104    004DFC60     "savnt206.wav"    "Vehicle destroyed"
105    004DFC50     "savant13.wav"    "Database reconstructed"
106    004DFC40     "savant14.wav"    "Enemy unit sighted"
107    004DFC30     "savant15.wav"    "Vehicle repaired"
108    004DFC20     "savnt204.wav"    "Structure repaired"
109    004DFC10     "savant17.wav"    "Warning, missile launch detected"
110    004DFC00     "savant18.wav"    "Warning, incomming missile"
111    004DFBF0     "savant19.wav"    "Morale is excellent"
112    004DFBE0     "savant20.wav"    "Morale is good"
113    004DFBD0     "savant21.wav"    "Morale is fair"
114    004DFBC0     "savant22.wav"    "Morale is poor"
115    004DFBB0     "savant23.wav"    "Morale is terrible"
116    004DFBA0     "savant24.wav"    "Food production in surplus"
117    004DFB90     "savant25.wav"    "Caution, food storage diminishing"
118    004DFB80     "savant26.wav"    "Warning, people are starving"
119    004DFB70     "savant27.wav"    "Power levels optimal"
120    004DFB60     "savant28.wav"    "Warning, power levels marginal"
121    004DFB50     "savant29.wav"    "Warning, power shortage"
122    004DFB40     "savant30.wav"    "New mission objective"
123    004DFB30     "savant32.wav"    "Edward satellite deployed"
124    004DFB20     "savant33.wav"    "Solar power satellite deployed"
125    004DFB10     "savant46.wav"    "Rocket launched"
126    004DFB00     "savant47.wav"    "RLV launched"
127    004DFAF0     "savant48.wav"    "RLV ready"
128    004DFAE0     "savant49.wav"    "Rocket ready"
129    004DFAD0     "savnt221.wav"    "Common metals storage needed"
130    004DFAC0     "savant51.wav"    "Rare metal storage needed"
131    004DFAB0     "savant53.wav"    "New worker ready"
132    004DFAA0     "savant58.wav"    "Our people are dying"
133    004DFA90     "savnt202.wav"    "Structure disabled"
134    004DFA80     "savnt227.wav"    (Beep) "You have failed, our colony is doomed"
135    004DFA70     "savant65.wav"    (Beep) "You have done well, our colony is surviving"
136    004DFA60     "savant66.wav"    "Caution, volcanoe watch initiated"
137    004DFA50     "savant67.wav"    "Alert, volcanic eruption imminent"
138    004DFA40     "savant68.wav"    "Warning, volcanic eruption detected"
139    004DFA30     "savant69.wav"    "Caution, vortex watch initiated"
140    004DFA20     "savant70.wav"    "Alert, vortex imminent"
141    004DFA10     "savant71.wav"    "Warning, vortex detected"
142    004DFA00     "savant72.wav"    "Caution, electrical storm watch initiated"
143    004DF9F0     "savant73.wav"    "Alert, electrical storm imminent"
144    004DF9E0     "savant74.wav"    "Warning, electrical storm detected"
145    004DF9D0     "savant75.wav"    "Caution, meteor watch initiated"
146    004DF9C0     "savant76.wav"    "Alert, meteor impact imminent"
147    004DF9B0     "savant77.wav"    "Warning, meteor approaching"
148    004DF9A0     "savant78.wav"    "Caution, seismic watch initiated"
149    004DF990     "savant79.wav"    "Alert, seismic event imminent"
150    004DF980     "savant80.wav"    "Warning, seismic event detected"
151    004DF970     "savant93.wav"    "Wreckage located"
152    004DF960     "savnt278.wav"    "The blight is approching"
153    004DF950     "savnt207.wav"    "Damage warning"
154    004DF940     "savant96.wav"    "Mining location surveyed"
155    004DF930     "savant97.wav"    "Cannot deploy"
156    004DF920     "savant98.wav"    "Command control initiated"
157    004DF910     "savant99.wav"    "Savant series computer activated, Outpost2 demo initiated, please choose an option"
158    004DF900     "savnt100.wav"    "Can not comply"
159    004DF8F0     "savnt101.wav"    "Commander, a volcanic eruption is imminent, evacuate the colony and rendevous at the mining beacon north of our location"
160    004DF8E0     "savnt201.wav"    "Data transmitted"
161    004DF8D0     "savnt203.wav"    "Structures disabled"
162    004DF8C0     "savnt205.wav"    "Reinforcements ready"
163    004DF8B0     "savnt208.wav"    "Wall completed"
164    004DF8A0     "savnt209.wav"    "Tube completed"
165    004DF890     "savnt210.wav"    "Bulldozing completed"
166    004DF880     "savnt211.wav"    "Vehicle infected"
167    004DF870     "savnt213.wav"    "Structure lost"
168    004DF860     "savnt214.wav"    "Vehicle lost"
169    004DF850     "savnt215.wav"    "Vehicle captured"
170    004DF840     "savnt216.wav"    "Repair stopped"
171    004DF830     "savnt218.wav"    "Outpost2 demo mode initiated"
172    004DF820     "savnt219.wav"    "Please stand by"
173    004DF810     "savnt220.wav"    "Command control terminated"
174    004DF800     "savnt223.wav"    "Starship module manufactured"
175    004DF7F0     "savnt224.wav"    "Satellite manufactured"
176    004DF7E0     "savnt225.wav"    "Wreckage loaded"
177    004DF7D0     "savnt226.wav"    "Food stores are plentiful"
178    004DF7C0     "savnt228.wav"    "Alliance formed with player1"
179    004DF7B0     "savnt229.wav"    "Alliance formed with player2"
180    004DF7A0     "savnt230.wav"    "Alliance formed with player3"
181    004DF790     "savnt231.wav"    "Alliance formed with player4"
182    004DF780     "savnt232.wav"    "Alliance formed with player5"
183    004DF770     "savnt233.wav"    "Alliance formed with player6"
184    004DF760     "savnt234.wav"    "Alliance with player1 broken"
185    004DF750     "savnt235.wav"    "Alliance with player2 broken"
186    004DF740     "savnt236.wav"    "Alliance with player3 broken"
187    004DF730     "savnt237.wav"    "Alliance with player4 broken"
188    004DF720     "savnt238.wav"    "Alliance with player5 broken"
189    004DF710     "savnt239.wav"    "Alliance with player6 broken"
190    004DF700     "savnt240.wav"    "Transfer to player1 complete"
191    004DF6F0     "savnt241.wav"    "Transfer to player2 complete"
192    004DF6E0     "savnt242.wav"    "Transfer to player3 complete"
193    004DF6D0     "savnt243.wav"    "Transfer to player4 complete"
194    004DF6C0     "savnt244.wav"    "Transfer to player5 complete"
195    004DF6B0     "savnt245.wav"    "Transfer to player6 complete"
196    004DF6A0     "savnt246.wav"    "Transfer received from player1"
197    004DF690     "savnt247.wav"    "Transfer received from player2"
198    004DF680     "savnt248.wav"    "Transfer received from player3"
199    004DF670     "savnt249.wav"    "Transfer received from player4"
200    004DF660     "savnt250.wav"    "Transfer received from player5"
201    004DF650     "savnt251.wav"    "Transfer received from player6"
202    004DF640     "savnt252.wav"    "Player1 has allied with you"
203    004DF630     "savnt253.wav"    "Player2 has allied with you"
204    004DF620     "savnt254.wav"    "Player3 has allied with you"
205    004DF610     "savnt255.wav"    "Player4 has allied with you"
206    004DF600     "savnt256.wav"    "Player5 has allied with you"
207    004DF5F0     "savnt257.wav"    "Player6 has allied with you"
208    004DF5E0     "savnt258.wav"    "Player1 has broken the alliance"
209    004DF5D0     "savnt259.wav"    "Player2 has broken the alliance"
210    004DF5C0     "savnt260.wav"    "Player3 has broken the alliance"
211    004DF5B0     "savnt261.wav"    "Player4 has broken the alliance"
212    004DF5A0     "savnt262.wav"    "Player5 has broken the alliance"
213    004DF590     "savnt263.wav"    "Player6 has broken the alliance"
214    004DF580     "savnt264.wav"    "Ion drive module deployed"
215    004DF570     "savnt265.wav"    "Fusion drive module deployed"
216    004DF560     "savnt266.wav"    "Command module deployed"
217    004DF550     "savnt267.wav"    "Fueling systems deployed"
218    004DF540     "savnt268.wav"    "Habitat ring deployed"
219    004DF530     "savnt269.wav"    "Sensor package deployed"
220    004DF520     "savnt270.wav"    "Skydock operational"
221    004DF510     "savnt271.wav"    "Statis systems deployed"
222    004DF500     "savnt272.wav"    "Orbital package deployed"
223    004DF4F0     "savnt273.wav"    "Pheonix module deployed"
224    004DF4E0     "savnt274.wav"    "Rare metals storage module deployed"
225    004DF4D0     "savnt275.wav"    "Common metals storage module deployed"
226    004DF4C0     "savnt276.wav"    "Food storage module deployed"
227    004DF4B0     "savnt277.wav"    "Colonist evacuation module deployed"
228    004DF4A0     "mis_fire.wav"
229    004DF494     "acid2.wav"
230    004DF4A0     "mis_fire.wav"
231    004E00A4     "geo_sel.wav"
232    004DF488     "laser.wav"
233    004DFDBC     "medexp1.wav"
234    004DF478     "microwve.wav"
235    004DFDBC     "medexp1.wav"
236    004DF46C     "railfir.wav"
237    004DF460     "railtar.wav"
238    004DFDE0     "bigexp1.wav"
239    004DFDD4     "bigexp2.wav"
240    004DF4A0     "mis_fire.wav"
241    004DF454     "spam1.wav"
242    004DF4A0     "mis_fire.wav"
243    004DF448     "foam.wav"
244    004DF43C     "thor.wav"
245    004DFDBC     "medexp1.wav"
246    004DF4A0     "mis_fire.wav"
247    004DFDB0     "medexp2.wav"
248    004DF488     "laser.wav"
249    004DFDA4     "smalxp1.wav"
250    004DF430     "gun1.wav"
251    004DFDBC     "medexp1.wav"


 
Title: Outpost 2 DLL Programming
Post by: Hooman on October 03, 2004, 04:41:28 PM
Quote
all i can say is that someone needs to make a GUI based program for all of theless code saavy ppl and also for the ppl who don't have the time on their hands to do it this way (such as me)
Keep in mind that a graphical editor is a major time commitment for the coders, who also don't have a lot of time on their hands.

Anyways, I hope that in the future something can be done in this regards. I have a few ideas about creating a DLL that's generic enough to be used for multiple levels and will read extra data tagged onto the end of the .map files. But then I'm not responsible for any of the editors and I have no idea how hard it would be to add that kind of support to an editor, or how well the file format suits it. Anyways, I think Hacker has plans for this so maybe it'll get added to the next version of the map editor if we are lucky.

Anyways, does anyone have any requests (preferably simple memory hacking type ones) for extra "non standard" functionality for DLL programming. I've noticed a lack of something along the lines of Player.GetColor(), which could be useful for team games where selecting your color would place you at the correct starting location/team location.

Code update:
Code: [Select]
OP2 class Trigger __fastcall CreateResearchTrigger(int boolEnable, int boolNoRepeat, int techID, int playerNum, char const *triggerFunction);
Again, you can use -1 for all players.

PlayingOutpost, send me your DLL sometime and I'll take a look at it, see if I can get it working.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on October 05, 2004, 01:01:51 AM
I've gotten tired of looking back at multitek.txt to find techIDs so I made an enum for them. I've placed the following in OP2Helper.h (It's not exported from the exe so I didn't want to put it in Outpost2DLL.h)

Code: [Select]
// Research techIDs
enum TechID
{
// Group I (No predecessors)
CyberneticTeleoperation    = 3401,
EmergencyResponseSystems   = 3301,
EnvironmentalPsychology    = 3406,
FocusedMicrowaveProjection   = 3408,
HealthMaintenance     = 3303,
HighTemperatureSuperconductivity = 3402,
HydroponicGrowingMedia    = 3403,
LargeScaleOpticalResonators   = 3407,
Metallogeny       = 3405,
MetalsReclamation     = 3302,
OffspringEnhancement    = 3304,
ResearchTrainingPrograms   = 3305,
Seismology       = 3201,
// Group II (All predecessors in Group I)
AdvancedVehiclePowerPlant   = 3901,
AutomatedDiagnosticExaminations  = 5307,
Consumerism       = 5101,
DIRTProceduralReview    = 5305,
DisasterResistantHousing   = 8105,
ExpandedHousing      = 8104,
HeatDissipationSystemsPlymouth  = 5601,
HypnopaediaEden      = 5309,
HypnopaediaPlymouth     = 5310,
LeisureStudies      = 3306,
MobileWeaponsPlatform    = 3851,
PublicPerformance     = 5108,
RareOreProcessing     = 5110,
RecyclerPostprocessing    = 5306,
RobotAssistMechanicEden    = 5051,
RobotAssistMechanicPlymouth   = 5052,
RoboticImageProcessing    = 5318,
Vulcanology       = 3202,
// Group III (All predecessors in Group II or earlier)
AdvancedCombatChassis    = 5201,
AdvancedRoboticManipulatorArmEden = 7213,
AdvancedRoboticManipulatorArmPlymouth = 7214,
DissipatingAdhesives    = 5202,
EnhancedDefensizeFortifications  = 8306,
ExplosiveCharges     = 7102,
ForumReconfiguration    = 5408,
GeothermalPower      = 5115,
HotCrackingColumnEfficiency   = 7202,
IndependentTurretPowerSystems  = 5111,
LavaDefenses      = 5701,
Magnetohydrodynamics    = 5107,
Meteorology       = 5302,
MultitainmentConsoleUpgrade   = 8307,
RareOreExtraction     = 7201,
ReinforcedVehicleConstruction  = 5317,
ScoutClassDriveTrainRefit   = 7206,
SmelterPostprocessing    = 7203,
// Group IV (All predecessors in Group III or earlier)
AcidWeaponry      = 8503,
AdvancedArmoringSystems    = 10303,
ArachnidWeaponry     = 7104,
ExtendedRangeProjectileLauncherPlymouth = 7212,
HighPoweredExplosives    = 8203,
IncreasedCapacitanceCircuitry  = 7403,
MagmaPurityControl     = 10301,
SpaceProgram      = 5405,
SpiderMaintenanceSoftwareRevision = 8319,
// Group V (All predecessors in Group IV or earlier)
DualTurretWeaponsSystems   = 8201,
DirectionalMagneticFields   = 5506,
ElectromagneticPulsing    = 5508,
HeatMining       = 8304,
LeggedRobots      = 5116,
MagmaRefining      = 8103,
MultipleMineProjectileSystem  = 7103,
ReducedFoamEvaporation    = 8320,
ReinforcedPantherConstruction  = 8309,
RocketPropulsion     = 5599,
SevereAtmosphericDisturbances  = 5303,
// Group VI (All predecessors in Group V or earlier)
ArachnidDurability     = 8321,
ArtificialLightning     = 10102,
EfficiencyEngineeringEden   = 8301,
EfficiencyEngineeringPlymouth  = 8302,
ExtendedRangeProjectileLauncherEden = 7211,
GrenadeLoadingMechanismPlymouth  = 10306,
ImprovedLaunchVehicle    = 10101,
MeteorWatchObservatory    = 8049,
PrecisionTrajectoryProjectionSoftware = 10309,
RocketAtmosphericReEntrySystem  = 12201,
ScorpionPowerSystems    = 7405,
Skydock        = 8601,
SolarPower       = 10204,
StasisSystems      = 10208,
// Group VII (All predecessors in Group VI or earlier)
HabitatRing       = 10205,
GrenadeLoadingMechanismEden   = 10305,
HeatDissipationSystemsEden   = 12101,
HighEnergyRayCompositeProjector  = 8106,
IonDriveModule      = 8801,
CommandModule      = 10202,
MeteorDetection      = 8316,
OrbitalPackage      = 10209,
SensorPackage      = 10206,
// Group VIII (All predecessors in Group VII or earlier)
FuelingSystems      = 8951,
FusionDriveModule     = 8901,
// Group IX (All predecessors in Group VIII or earlier)
PhoenixModule      = 10401,
// Special Technologies
TigerSpeedModification    = 11999,
// Free Technologies
Spider        = 2099,
Agridome       = 2101,
CommandCenter      = 2103,
StructureFactory     = 2104,
LightTower       = 2107,
CommonOreMine      = 2108,
Residence       = 2109,
CommonOreSmelter     = 2110,
CommonMetalsStorageTanks   = 2111,
Tokamak        = 2112,
Tube        = 2114,
TradeCenter       = 2115,
StandardLab       = 3101,
ConcreteWalls      = 5106,
AdvancedLab       = 5104,
RobotCommandCenter     = 3501,
VehicleFactory      = 3502,
Scout        = 3601,
LaserTurret       = 3602,
MicrowaveTurret      = 3603,
CargoTruck       = 3604,
ConstructionVehicle     = 3605,
EarthworksConstructor    = 3606,
RoboDozer       = 3608,
RoboMiner       = 3609,
RoboSurveyor      = 3610,
GuardPost       = 3801,
Garage        = 5102,
Repairvehicle      = 5401,
RareOreConstructionSmelter    = 5501,
RareMetalsStorageTanks    = 5502,
RocketLauncher      = 5602,
Observatory       = 8051,
SULV        = 5503,
EDWARDSatellite      = 5504,
MeteorDetectionConstruction   = 8317,
EvacuationModule     = 12599,
// Unavailable Technologies
BasicLaratory      = 2106,
EvacuationTransport     = 3607,
MicrobeWall       = 8401,
ChildrensModule      = 12499
};

The names and techID are taken from multitek.txt. Let me know if there are any problems, typos, ommisions, etc. (mostly a copy and paste job so there shouldn't be any). Let me know about any name collisions. I may need to place it in a namespace. Especially if we rename things in the map_id enum.

Speaking of the map_id enum, should I rename things to make it easier to work with? (For one, the "_"s really slow down my typing) Eg. instead of first line below, perhaps one of the following:

map_Command_Center,
mapCommandCenter,
CommandCenter,
commandCenter,

I would normally use the last for a variable name, but there aren't variables so I'm thinking of perhaps the CommandCenter one. Or should I just leave them as is? Also, should we place enums in name spaces to avoid conflicts? It would remove the reason for adding a "map" prefix to each constant.



Oh, and I'm having some initial success with come AI related things. I've gotten the computer to research a tech at a lab (the standard DLLs just "give" the computer technology at regular intervals with Time Triggers). Anyways, this seems kinda cool since disabling/destroying the computer's lab would actually set back their research.

I've also had a Robo Surveyor go survey a beacon and a Miner to move on top. No luck yet getting the mine to deploy. Seems like it wanted to but something went wrong. I know I've done this sort of thing before. I've also gotten ore trucks to hardvest before and ConVecs to build a base, so it looks like this could be a good start.

One minor detail, some of the code I'm using is a little, umm how shall we say..., nonstandard? I've kinda been bypassing the interface functions for the DLL and calling things directly from the exe. Not exactly a coding practice thats smiled upon but Sierra isn't about to release an updated exe, so it'll work. ;)


Edit: It appears Unit.DoBuild doesn't work for deploying RoboMiners. (It does use the right command though). Anyways, it seems that's either functionality that was left out by the OP2 programmers or a bug. Do you think they'll still accept bug reports?  :lol: Anyways, you can still use the BuildingGroup class to place mines I believe. I can't seem to find any standard DLL that uses DoBuild. Ah well, I'll probably just write my own function to handle it like I've been doing for other things. Hmm, the beauty of DLLs is showing through. If it doesn't exist, write it yourself.  :)
 
Title: Outpost 2 DLL Programming
Post by: BlackBox on October 05, 2004, 03:08:34 PM
Hooman: we could define compatibility stuff so you can go on using the old names... for example:

(somewhere in the template header, after the OP2 header is included:)
Code: [Select]
// for compatibility with the old SDK
#define map_Default map_None

BTW Hooman... if you want to export new crap from the OP2 EXE for stuff like this, go for it! (Just make sure to tell me so it gets into the update :-P)
Title: Outpost 2 DLL Programming
Post by: Hooman on October 08, 2004, 06:36:32 PM
(deleted)

Edit2: Oops, I forgot I commented out a bunch of code when trying to find out what was wrong. It still doesn't work. It doesn't seem to like your plyr.xxx and comp.xxx calls. Looking into it now.

Edit3: Ok, for some reason the compiler is linking with the exported variable wrong. It's giving the wrong address for where the Player[7] objects are when you use _Player &plyr = Player[0];. You can get around this by just doing things the way we've switched to doing them, using Player[0].GoEden(); directly rather than plyr.GoEden();. Basically, remove the declarations and assignments to all _Player variables you define and use Player[0] everywhere you've used plyr, and use Player[1] everywhere you've used comp.

I'm not really sure why it doesn't work the other way. I know I've gotten it to work the other way before. But then, that's kinda an old way so I'd suggest you don't bother with it.
Title: Outpost 2 DLL Programming
Post by: BlackBox on October 09, 2004, 01:26:50 PM
Btw... if you still WANT to use the comp, plyr stuff, this is a much more streamlined way: (Hooman, I'm surprised you didn't think about this one)

#define plyr Player[0]
#define comp Player[1]

etc...

then you can just go comp.GoEden();
the compiler will convert it to Player[1].GoEden() when it preprocesses the file.
Title: Outpost 2 DLL Programming
Post by: Hooman on October 09, 2004, 05:07:45 PM
Heh, yeah. I didn't think of it because I generally disapprove of having to rely on a preprocessor.  B) That's one aspect of C++ I've never really liked. But yeah, it's simple and it works.

Just be careful about how you use the preprocessor. I've seen some example of macros written with it that have some surprising side effects. I guess I'll illustrate a simple example:

Code: [Select]
#define double(i) i+i

double(i++); // Increments the value of "i" twice

The only reason I ever really use the preprocessor is for multiple redefinition error protection (which I don't think should be necessary) and the occasion #define (although I prefer using const, just doesn't always compile as effeciently, depending on the compiler and the optimization settings).

Anyways, in short, I view the preprocessor as evil.  :lol:



Btw, Hacker, I had a strange though about the possibility of adding new unit types to OP2. It wouldn't be easy or even likely we'd ever get it done, but we could implement a unit class inside the DLL that's of the same format used internally and then place our unit into the internal array. Granted there are a lot of possible problems and it's probably a little more involved than that, but we could certainly mod a unit without too much trouble. The interesting part is that the unit functions are generally virtual, so we would be free to implement our own functions as long as they took the same parameters and return value. Unfortunatly I don't know what (almost) any of the virtual functions do, and there seems to be a lot of them. Anyways, to mod a unit, we just implement the new function and replace the corresponding address in the vtbl for that unit.

 
Title: Outpost 2 DLL Programming
Post by: Hooman on October 10, 2004, 10:49:12 PM
Well I looked into Unit.DoSimpleCommand today. It's pretty useless. Here's an updated definition all the same. It basically creates a CommandPacket and issues it to the game engine but unfortunatly, the format it creates is only compatible with about 2 or 3 commands. The first 2 are Stop, and SelfDestruct, which already have corresponding exported functions: Unit.DoStop(), and Unit.DoSelfDestruct(). The third possibility is Scatter, but DoSimpleCommand only specifies one unit, so a one unit "scatter" isn't exactly going to get you much. Plus, the function was declared as protected (which affects name decoration, so you can't really change that) so you kinda have to go out of your way to call it.

Code: [Select]
	void DoSimpleCommand(int commandType);

Say, if anyone has a way to get the AI to build an army, I'd like to know about it. I know of one hack that should work but I'm wondering about other ways to go about doing it. I'd like to get the computer to continuously build units, preferable without having to keep ordering it to do so. Maybe one of the group functions?
Title: Outpost 2 DLL Programming
Post by: Hooman on October 11, 2004, 12:05:38 AM
That could make for an intersting level. Having units pump out of a garage nonstop.

Anyways, I've looked into Garages a bit and here's what I've got. In the Unit class:
Code: [Select]
	void PutInGarage(int bay, int tileX, int tileY);
You call this on the unit you want to place in the garage (not on the garage you want a unit placed in). The coordinates are the location of the garage (wherever it was built). The bay is of course which slot you want the unit placed in (0-5). The unit must be on the pad of the garage for this to work.

Note: the location of the garage pad is (x-1, y) if you placed the garage at (x,y).

Code: [Select]
	TethysGame::CreateUnit(unit3, map_Garage, LOCATION(46, 10), 0, map_None, 0);
TethysGame::CreateUnit(unit3, map_Cargo_Truck, LOCATION(45, 10), 0, map_None, 0);
unit3.PutInGarage(0, 46, 10);
Title: Outpost 2 DLL Programming
Post by: PlayingOutpost0-24 on October 11, 2004, 02:35:10 AM
is there a way to add a repeat-type command?
maybe every 10 marks it creates 3 lynxes (random wpn) or later panthers and tigers and then every 120 marks it attacks. it continously buids Residences, Agridomes and Med. Centers, DIRTs, expands to beacons... maybe Res., Agri, Med 60 marks + build time, DIRT 90 + build and beacons like... 400 marks.
Is that enough?


Volcanic eruption at 800, but AI has warning systems, so at Mark 700 it moves out with 15 convecs.
At mark 789, a huge meteor hits one of its smelters.
At mark 180 it gets Panther
At mark 320 it gets Tiger
At mark 290 it gets Thor/Nova
At mark 15 it gets Lynx

- Laser already researched
- Adv. lab instantly.
- Fastened research :D




BTW, u now really should compile my dll and c what's w/ it :D


U want more?
Title: Outpost 2 DLL Programming
Post by: BlackBox on October 11, 2004, 06:41:56 PM
That would be easy to do, just use a time trigger.

CreateTimeTrigger(1, 1, mark*100, "CreateWeapon");

Add CreateWeapon to the .def. then define it as:
void CreateWeapon() {
 TethysGame::CreateUnit( blah blah blah, don't want to list the parameters);

// you could use GetRand to randomize the weapons

}

etc...
Title: Outpost 2 DLL Programming
Post by: Hooman on October 19, 2004, 09:44:17 PM
PlayingOutpost: I did compile your DLL. It looks largely fine, but the way the Player[0] and Player[1] was specified seemed to be causing problems. If you just used Player[0] and Player[1] instead of the way you were doing it, it should work fine. Other than that I'm not sure how to help you.


I looked into the positioning of units and structures with TethysGame::CreateUnit. It seems the structures are centered on the coordinates specified. So a Command Center is 3x2 (just the building size, not the size of the bulldozed area) so it is offset by (-1, -1) to find the upper left corner of the building. Note: You must round down when dividing by 2. (It actually shifts right by 1, hence the rounding down). So a call such as:
Code: [Select]
	TethysGame::CreateUnit(unit2, map_Command_Center, LOCATION(46,10), 0, map_None, 0);
Will Create a Command Center centered at (46, 10), so the upper left corner of the Command Center is at (45, 9). Adjusting for the internal coordinates of the game, this would appear to the player as (14, 10) for the upper left corner of the CC.

Also, the internal coordinates of the units are all stored as pixel values and are centered in the tile: (tileX*32+16, tileY*32+16) where each tile is 32x32 pixels. For buildings, the offset of 16 is conditional on the size of the building in that direction. If the size is odd, it gets the 16 pixel offset otherwise it doesn't. (You can think of vehicles as a special case of 1x1 units.) Note: for buildings, the stored value is the center of the building, not the adjusted coordinates for the upper left corner. They might have done this as an easy way to make it when attacking buildings, the weapons hit in the center rather than the upper left corner of the building. Not sure why they'd do this though.

Anyways, so if you were reflecting a base about a certain origin, all the buildings of odd size (in the direction of the flip) will appear an equal distance from the center of the flip. Buildings of even size will be offset by a tile and won't appear symmetric.
 
Title: Outpost 2 DLL Programming
Post by: Hooman on October 24, 2004, 05:06:34 AM
Well I've found that TethysGame::AddMessage can only accomodate up to 60 characters. Any more and it'll be truncated. This includes the time stamp the game adds to all messages. (So really you can't use more than 57 characters for the first 10 time marks, 56 for the next 90 time marks, 55 for the next 900 time marks....)



AI ramblings...:

I've continued work on an AI project. I've found out how to enumerate all the beacons on the map. This should allow selection of a beacon to survey and deploy a RoboMiner at. As for which beacon to select, that's another matter. I'm gonna need a few easily programmed heuristics to get this all working nicely. If anyone has any really good ideas out there, I'd love to hear them. Preferably ones that I can program easily.

I'm thinking I should look for the closest mine to the smelter, survey it and deploy a miner at it. If the smelter doesn't exist, then the closest beacon to the miner. I'm thinking I should also add some code to wait on deploying the RoboMiner if more than one beacon is "close" to the smelter until they are all surveyed (unless one is 3 bars, in which case deploy right away). Still no idea on how I'll be defining closeness, or how highly to weight it.

Now, the above certainly isn't perfect but it should largely work. Problems occur with natural obstacles. The closest mine might not be easiest to get to due to ridges. It could have a really long route as compared to a farther mine with a direct route. It might not even be possible to get to it. Rather than distance, I'd prefer to use a route length, but I currently have no real way of calculating that. Besides, even if I found a way of calculating a route distance, it might not match the internal algorithm Outpost2 uses to find routes, and so it wouldn't be accurate since the trucks could take a widely different route. Granted, it should be fairly close in value, but then again, having an active RobotCommandCenter can make a fairly big impact on transit times. (Which reminds me, it's not even the route distance, but the time it takes, which takes terrain type into consideration).

Well, suffice it say, taking distance into account properly isn't really feasible. At least certainly not right now. So good ideas on faking it would be appreciated. Or at least feedback on the simple idea I have.

Another problem is determining where to place new structures. I guess the easy way is to randomly pick a spot adjacent to another building, where there is room. I'd prefer something a little smarter than this though. I don't want buildings placed in the way of ore mines. Also, like like buildings placed in areas depending on the amount of protection they need. For instance, I'd rather have an advanced lab at the back of my base rather than in the front. And a vehicle factory should probably be a little closer to where the units are needed, but not overly exposed. Placing smelters shouldn't be too much of a problem. Just put them close to mines.

Placing buildings in strategic locations isn't so straightforward. What are these locations? How would you find "the back" of your base? Certainly when you play, you'd take natural obstacles into consideration like ridges, and where the breaks are, but it's not so easy to program the computer to find these. It seems like the only real way to defend a base is to scatter units around it. Then you might wonder how hard it'll be to deploy buildings if units are scattered everywhere in the way.

Research doesn't seem to be too much of a problem. I've got the computer to research it's way through a tech tree. A simple minded implementation was to just hardcode a list of research for the computer to work it's way through. There are still a few issues with the list but they'll get worked out soon. (Researches advanced lab stuff at a standard lab, should be easy to check for, and it doesn't really take prerequisites into account, but that is also easy to check for) To my credit, I at least fixed it so it wouldn't assign more scientists than you are allowed to do that research.  :D The more troubling problem is selecting a lab to research at. If there is more than one standard lab, which one will research be done at? So far, I'm just taking the first one found when enumerated, but it might be better to select a lab based on how well defended it is. But then, I'm not sure how to determine "how well defended" it is. I guess I can stick with the assumption that there won't be more than 1 lab, and thus there is no real problem. Anyways, I'd also like to upgrade the research topic selection a little. Maybe make it based on what it needs, or what it's opponents are doing to it. But then I have no clear idea on how to do this sort of objective research. Interestingly enough, I have way of determining pretty much everything about a tech, just no idea how to analyse it. This could also include analysing future changes to the tech files. (I know enough of the internal game structures to determine everything that is read in from the sheets files and even a few more precalculated values).

Resource balancing. When should workers be trained into scientists, and how many. How should workers and scientists be allocated among the buildings. How much ore should go towards base building, resource expansion, military expenditures. I guess some priorties are a little more obvious. Like power is needed before buildings that consume more than you have. But then you might plan on having inactive buildings, so do you take those into account when determining when to build power plants? How long should you put off building agridomes to solve food shortages if you have other pressing needs. Generally you start a level with a shortage but never build agridomes right away. How many smelters should be built and when? Vehicle factories? Extra Command Centers? Special cases like nurseries and universities also have to be considered.

I'm guessing there should be certain priorities to the buildings. Like Always needing a Command Center active. If the computer is being attacked or losing a battle, it might need it's Vehicle Factories all productive. It seems there should be some kind of emergency mode where all resources get put towards defense before any extra is allocated elsewhere. Extra money might be kept in reserve however since you can run out pretty fast. Certain buildings, mainly morale buildings like residences (and maybe agridomes) could have their workers shifted to factories temporarily. Possibly other buildings like nurseries and universities too if there is enough need for defensive production. In normal mode, morale buildings will be important to keep your population up so workers should be assigned to things like residences. Workers must be kept busy so certain buildings may be kept active that aren't strictly needed. But then excess workers might be trained into scientists, or perhaps kept in reserve for buildings that are needed in the near future. Some clear idea as to how to shift these workers around is needed.

Also, for land rush games, where should the base be built? Seems like you should find a decent beacon first. But then you can't wait too long or you starve to death or just get sacked easily.


Anyways, there are my thoughts on what an AI might (should?) consider. I guess I'll have to stick with simplifying assumptions for the meantime. If anyone has any good ideas on how to make a good AI, I'd love to hear them. What it should consider, what simplifying assumptions seem justified, how an algortihm might work, or what problems such a solution might have. Even just what should be focused on first. Right now I think getting an initial resource base going is important, and research is mostly going. Next, probably determining which structure kits to build.
 
Title: Outpost 2 DLL Programming
Post by: Eddy-B on November 18, 2004, 06:15:42 PM
Hi guys..
i might be new to this forum, but i've read some of it (not all  <_< ).. It seems a lot has been solved/found already. just want to add my little bit, since i could not find a descent explanation about the DescBlock variable.

It should be defined like this:
Code: [Select]
struct SDescBlock{
  int missionType;
  int numPlayers;
  int techLevel;    // Tech-level you start with (in combination with the SetTechLevel function
  bool unitMission;    // false= base mission; true= unit mission (no base commands available)
  char *MapName;
  char *LevelDescription;
  char *TechtreeName;
  int unknown4;
};

It may be someone figured it out already, but i didn't see it yet on this forum...
Title: Outpost 2 DLL Programming
Post by: Eddy-B on November 20, 2004, 04:37:32 PM
Quote
Or even how many fields should be in this structure?
This is definately THE number of arguments, although the last "unknown" argument seems to be always zero, and has no effect when changed...
it might just be a filler to get to an 8-Byte offset, who knows. For now, just leave it in as "unknown" and don't worry about it too much i guess.

Code: [Select]
struct SDescBlock{
int missionType;     // For campaign games, this is the level number (positive values)
int numPlayers;      // For multiplayer, this is the number of players. For singleplayer, set to 2 when using computer opponent !
int techLevel;         // Tech level for available research
bool unitMission;       // true=unitmission; false=basemission
char *MapName;
char *LevelDescription;
char *TechtreeName;
int unknown;
};
[/font]
The whole DescBlock structure is now 32 bytes long.
Title: Outpost 2 DLL Programming
Post by: Eddy-B on November 21, 2004, 06:45:30 AM
Okay Hooman, how about this:

I'm trying to set up an AI, but i didn't get far...
I'm trying to figure out how to have it create structure kits at its newly build factory. I've seen you use CreateBuildingGroup, but this works only if you have the units' addresses. Now how do i get the address of a newly constructed structure.

Code: [Select]
Unit convec1,convec2;
int x=60,y=10;

EXPORT void CreateTrucks()
{
   TethysGame::CreateUnit(convec1,map_ConVec,LOCATION(46,0),0,map_Command_Center,1);
   convec1.DoBuild(map_Command_Center,LOCATION(x,y));
   TethysGame::CreateUnit(convec2,map_ConVec,LOCATION(48,0),0,map_Structure_Factory,1);
   convec2.DoBuild(map_Structure_Factory,LOCATION(x+5,y+1));

   CreateOperationalTrigger(1,1,1,map_Structure_Factory,1,cmp_Equal,"Base1");
}

EXPORT void Base1()
{
   BuildingGroup &bldGrp1 = CreateBuildingGroup(Player[0]);
   bldGrp1.TakeUnit(convec1);
   bldGrp1.TakeUnit(convec2);
   bldGrp1.RecordBuilding(LOCATION(x+1,y+3),map_Agridome,map_None);
}
I've checked my trigger - it works fine, but the "AI" simply won't do any construction. My guess is i need to add the structure factory to the BuildingList, but how ??

Any help ?



PS: you need to define this to be able to export any functions:
Code: [Select]
#define EXPORT extern "C" __declspec(dllexport)
Title: Outpost 2 DLL Programming
Post by: BlackBox on November 21, 2004, 10:58:04 AM
I think you can call .DoBuild() of the structure factory unit class to make it produce a structure kit.
You can use an enumerator to get the structure factory Unit object.

but anyway, to add the factory to the building group, get it's Unit object and use .TakeUnit() to add it. (Structures are considered to be Units)

FYI: You don't need the #define EXPORT stuff to export a function.
Just remove all the qualifiers from the function prototype and add the function name to the .def file (which should be part of the project).

That's how every other function is exported, (look at the .def to see)
Title: Outpost 2 DLL Programming
Post by: Eddy-B on November 21, 2004, 11:08:34 AM
Quote
Just remove all the qualifiers from the function prototype and add the function name to the .def file (which should be part of the project).
Well, i find it easier this way: i've been programming for 19 years now, and i'm the kind of guy that can be compared mostly to what nature doez: resist changes { you know, the things Newton and Einstein propagate }
In other words: when using this EXPORT structure, you don't need to have an extra .def file in your project. In fact i compiled my dll's withoutANY def file. My philosophy: one less file to worry about !

As for the other part: i did look into the EnumerateBuildings function, but couldn't quite figure it out. Tried looking at the 2 "unknown" members. First one seems to be some pointer, while the 2nd looks more like an int or char kind of variable.
Unknown1 does NOT point to a Unit class, since casting to a Unit and accessing a member causes Outpost to crash !

Hey, help me out here !  I might be 'hardened' programmer, but i'm not an experienced hacker nor do i know a lot about Outpost2 programming (YET!) I do find it fascinating though that Sierra online used DLLs instead of just .map files like most gaming engines do. I have build mappers for the X-Wing and Tie-Fighter games with ease... but this ..  <_<

BTW; The source i've submitted before (see below) sets up 2 ConVecs with the 2 key structures you need to start a game, and having the player (or computer) start really "From scratch"

Thanx for your help, and i hope I can help you guys further also !

Quote
Code: [Select]
Unit convec1,convec2;
int x=60,y=10;

EXPORT void CreateTrucks()
{
  TethysGame::CreateUnit(convec1,map_ConVec,LOCATION(46,0),0,map_Command_Center,1);
  convec1.DoBuild(map_Command_Center,LOCATION(x,y));
  TethysGame::CreateUnit(convec2,map_ConVec,LOCATION(48,0),0,map_Structure_Factory,1);
  convec2.DoBuild(map_Structure_Factory,LOCATION(x+5,y+1));

  CreateOperationalTrigger(1,1,1,map_Structure_Factory,1,cmp_Equal,"Base1");
}

EXPORT void Base1()
{
  BuildingGroup &bldGrp1 = CreateBuildingGroup(Player[0]);
  bldGrp1.TakeUnit(convec1);
  bldGrp1.TakeUnit(convec2);
  bldGrp1.RecordBuilding(LOCATION(x+1,y+3),map_Agridome,map_None);
}
Title: Outpost 2 DLL Programming
Post by: Hooman on November 21, 2004, 12:48:23 PM
Ok, this seems like an easy problem to fix. Move the Building group to InitProc. The Operation Triggers will only fire once the building is built and operational. If the ConVecs never build the buildings, it'll never fire. Besides, all the original DLLs use InitProc to setup stuff like that. It's starting to feel like an establised standard  :P . If you really want to add stuff to the group as it's created, I'd suggest making the group a global variable and then add units or recorded buildings to the group in you triggers/other functions. If you want to add a unit to the group as it's created, take a look at the enumerator classes. I've used that in a project in AIProc to add units to groups after they are created. Keep in mind that it'll enumerate all units of that type (or all units) and it won't return any useful results until the unit is created. AIProc is called every game cycle (or every 4 games cycles) so you can put any continuous checks you need in there. (Probably better ways to do most of that though).

And yeah, I've noticed that about the .def files too. I've stopped using it for new functions but I've been to lazy to remove it for functions already using it. I stay away from the #define though. I've never really liked #define.  (thumbsdown) Heh, you should see some of the bugs they can cause.

Quote
Unknown1 does NOT point to a Unit class, since casting to a Unit and accessing a member causes Outpost to crash !
Yes, most definately. These aren't really unknown anymore. Just that the values aren't really useful and I'd rather people not try and use them. (And I'm too lazy to update the header file  :P ) Besides, what have you been told about accessing class member veriables!  :lol:

The problem with the above is that OP2 doesn't use any of the exported classes internally. They are just wrappers for the internal classes. What you're seeing I believe IS a pointer to a Unit class, just not the exported one. If for some reason people really need access to that stuff, I'll release a library but I'm still a little hesitant to post much info on it.

To use the enumerators, construct a new one everytime to want to enumerate.
Code: [Select]
PlayerBuildingEnum enm(playerNum, buildingType);
Where playerNum is from 0-6 and buildingType is one of the map_id enum constants in Outpost2DLL.h.
Then you need a Unit variable to hold the returned unit from the enumeration.
Code: [Select]
Unit unit;
enm.GetNext(unit);
If the return value of GetNext is non-zero, then a unit was returned and copied into "unit". (Actually, all the Unit class is, is a wrapper for the internal class and all it really contains is the unitID, which is just the index of the inernal unit class in an array. So don't worry about it copying large objects, it's only 4 bytes).
If you only want one unit, you can do that (but be sure to actually check the return value is non-zero before trying to use the unit) or, if you need all units of that type, throw the GetNext into a loop.
Code: [Select]
While(GetNext(unit))
{
// whatever
}


Edit: Come join us on IRC in channel #Outpost2.tutor. That's were all the programming stuff is talked about. (They get tired of us flooding the gaming channel with technical info).  :lol:  
Title: Outpost 2 DLL Programming
Post by: Eddy-B on November 21, 2004, 12:59:19 PM
Got it to work, but i overlooked one slight thing. Anyway, here's how it works:
{ appearently, while YOU were typing YOUR reply..  :P }

Quote
Code: [Select]
Unit convec1,convec2;
int x=60,y=10;

EXPORT void CreateTrucks()
{
  TethysGame::CreateUnit(convec1,map_ConVec,LOCATION(46,0),0,map_Command_Center,1);
  convec1.DoBuild(map_Command_Center,LOCATION(x,y));
  TethysGame::CreateUnit(convec2,map_ConVec,LOCATION(48,0),0,map_Structure_Factory,1);
  convec2.DoBuild(map_Structure_Factory,LOCATION(x+5,y+1));

  CreateOperationalTrigger(1,1,0,map_Structure_Factory,2,cmp_Equal,"Base1");
}

EXPORT void Base1()
{
  PlayerBuildingEnum bldgs=PlayerBuildingEnum(0,map_Structure_Factory);
  bldgs.GetNext(unit);

  BuildingGroup bldGrp1 = CreateBuildingGroup(Player[0]);
  bldGrp1.TakeUnit(unit);  // the structure factory
  bldGrp1.TakeUnit(convec2);
  bldGrp1.RecordBuilding(LOCATION(x+1,y+5),map_Agridome,map_None);
}

Anywayz; what i forgot { and figured out, when i gave the 2 trucks 'human' control } is that when the mission starts, and all you have is the 2 ConVecs with the kits inside, and NO ore storage facilities, your ore immediatly turns to zero, making it impossible to build anything more than just that what you have kits for.
So in order to have someone "start from scratch" you'll need at least:
Title: Outpost 2 DLL Programming
Post by: Hooman on November 21, 2004, 01:04:35 PM
Ahh yes. I see now that I misread your code. I guess what threw me off was the EXPORT for the CreateTrucks function. I'm kinda wondering why it's exported now? I assume you already know this, but just in case, only Trigger callbacks and the required functions already given in the template file need to be exported.

Heh, last time I had a building group and no money, I heard a lot of that sound that plays when your order is cancelled due to lack of ore.

 
Title: Outpost 2 DLL Programming
Post by: Eddy-B on November 21, 2004, 01:30:00 PM
Quote
AIProc is called every game cycle (or every 4 games cycles) so you can put any continuous checks you need in there. (Probably better ways to do most of that though).
Thats how i did it at first, before reading about the triggers. It's just that the whole thing is missing one important trigger: BuildingDone or something likewise. But anyway: the BuildingGroup makes it a lot easier to make an AI.
Q: Is there a simular procedure for building vehicles at the factory ? if not: is there a trigger when a vehicle is build ??
Title: Outpost 2 DLL Programming
Post by: BlackBox on November 21, 2004, 02:07:57 PM
to build stuff at a vehicle factory, you can use the .DoBuild() member, again.

However if you need to create it with a cargo / weapon it has to be done through a BuildingGroup (don't remember now, how to do that)

As for a trigger when a vehicle is built, you could do this:

Trigger &trig = CreateCountTrigger(1, 1, playerID, unitType, cargoType, 1, cmp_Greater_Equal, "VehicleCreated");

that would fire if 1 or more of the unitType units existed with cargo cargoType. If you wanted to know if it was created you'd have to figure out how many units there were beforehand (probably need an enumerator), then compare the count to numUnits + 1 to see if it exists yet.

Or you could enumerate thru them in AIProc() and check for changes in the values...
Title: Outpost 2 DLL Programming
Post by: Hooman on November 21, 2004, 05:08:41 PM
For combat vehicles, there is some group commands you can use that Haxtor found. Check the post http://forum.outpostuniverse.net/index.php?showtopic=116...indpost&p=26169 (http://forum.outpostuniverse.net/index.php?showtopic=1162&view=findpost&p=26169) for details. It uses UnitBlocks and BuildingGroups to do it.

There is a way to specifically order a vehicle factory to build a unit using DoDevelop(), but not combat units. You have to use the above mentioned method for that. The problem with combat units is the weapon isn't set properly, so the game crashes once the unit is built. Also, I believe DoBuild() only works for deploying strucutres and mines (but is kinda sick with mines). I don't believe it'll work for building stuff at a factory. I could be wrong though.
 
Title: Outpost 2 DLL Programming
Post by: Eddy-B on November 26, 2004, 01:40:12 PM
Hey guys, another thing you can ADD to the Outpost2DLL.h headerfile:
Code: [Select]
enum trig_res {
   res_food  = 0,
   res_common_ore,
   res_rare_ore,
   res_kids,
   res_workers,
   res_scientists,
   res_colonists
};
The res_common_ore and res_rare_ore lines are there already, so just overwrite them with the above (copy&paste).

Always happy to help out !

Cheerz  :D
Eddy-B



{edit} PS: res_colonists is kids+workers+scientists
Title: Outpost 2 DLL Programming
Post by: Eddy-B on November 26, 2004, 07:38:18 PM
Well,, several hours later, and i found some more AI entries into the great mysteries of outpost2.exe:
Update your outpost2DLL.h to include this part:
Code: [Select]
struct OP2 PatrolRoute {
   int unknown1;
   LOCATION *waypoints;
   int unknown[10];
};
Look for the definition of PatrolRoute, and copy&paste the middle 3 lines of the above code in between the braces as shown.
Now, the way you use this is as follows:
Code: [Select]
void SetPatrol()
{
   LOCATION scoutRoute[]=
   {
      LOCATION(120, 14),
      LOCATION( 80, 14),
      LOCATION( 96, 24),
      LOCATION( -1, -1)
   };
   PatrolRoute route1;
   route1.waypoints=scoutRoute;

   patrolGrp=CreateFightGroup(Player[1]);
   patrolGrp.SetPatrolMode(route1);
   patrolGrp.TakeUnit(scout1);     // assuming scout1 was already created
}
The scoutRoute array contains the waypoints my scout will follow. You can set upto 8 waypoints, after wich the scout will continue from its first waypoint again. If you want less than 8 waypoints, mark the end of the list with a LOCATION(-1,-1)

It's as simple as that !

[size=8](don't i get credits for this or what ARE those credits for ???)[/size]
Title: Outpost 2 DLL Programming
Post by: Hooman on December 09, 2004, 07:23:14 PM

Ok, I just did some thread pruning. I know some people have wanted this thread to be a little better organised and easier to read so I've deleted most of the posts that don't add now useful technical content. There are still a few that contain obsolete technical content that will probably be either edited or deleted at a later point. (I hope I haven't interrupted the flow of thought too much by doing all this.)

I'd like to thanks everyone who contributed. Some very good questions were raised. Unfortuneately I didn't deem many good questions to be good techincal content so your posts have been deleted.  ;)

Anyways, I thought I'd list/thank a few people who have contributed but who's posts (some/most/all of them) have been deleted.
Arklon, Cynex, Ezekel, Hacker, Haxtor, Kramy, Leviathan, PlayingOutpost, Saibot.

Keep it coming people (, I can just keep pruning!  :lol: )

 
Title: Outpost 2 DLL Programming
Post by: Hooman on January 05, 2005, 12:37:43 AM
Figured I'd post a few updates before people started thinking this thread was dead.

Code: [Select]
	static void __fastcall SetMusicPlayList(int numSongs, int repeatStartIndex, enum SongIds *songIdList);

The songIdList is an array of SongIds. These are the songs that OP2 will play in the background. Playing begins at the first song (index 0) and when the lists is finished, playing continues by looping back to the song at index repeatStartIndex. (Thanks Eddy, seems like that repeat idea was right).

And here is a filled in SongIds enum and associated filenames (sorta).
Code: [Select]
enum SongIds {
songEden11 = 0,
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
};



Another few sound/messaging functions:
Code: [Select]
	// Note: Recorded voice messages can be played by specifying the right soundID
static void __fastcall AddGameSound(int soundID, int recipientPlayerNum);
static void __fastcall AddMapSound(int soundID, struct LOCATION location);
static void __fastcall AddMessage(int pixelX, int pixelY, char *message, int recipientPlayerNum, int soundID);
static void __fastcall AddMessage(class Unit, char *message, int recipientPlayerNum, int soundID);

AddGameSound just play a sound which the given player hears. You can use -1 to make it heard by all players.

AddMapSound is used to place a directional sound like fumaroles. When their current view is over that section of the map, they will hear this sound continuously. (Kinda annoying actually  <_< ) If they are close to that section of the map then the sound will play quietly and you won't hear it at all if you're far enough away.

AddMessage places a message in the communcations pane. Use coordinates (-1, -1) if you want to disable the spacebar jumpto feature for this message. Otherwise, enter the *pixel* coordinates of where on the map this message will take the view on a jumpto. Note that you must multiply the tile coordinates by 32 to obtain the pixel coordinates. (The usualy tile offsets apply). Using a soundID of 0 will play the default beep you normally hear when messages are added to the communications pane. The "class Unit" version of this function works the same as the other form except the location for the jumpto is taken to be the coordinates of the unit. (The view is centered on where that unit was at the time the message was added for the jumpto).


Btw, anyone have a better name than "recipientPlayerNum"? Personally I can't stand that variable name and changing it won't affect anything, not even existing code. It'd be nice to change it to something a little more intuitive sounding before another release of the header file though.


In another note, I'm getting pretty sick of the "_"s in enum names and I've heard a few people prefer them not being there so in the next update I'd like to have them removed. As for people who already have code with the underscores, I think we should leave in some compatibility option so your code should still compile. Maybe phase them out slowly (but completely!   :angry: ). Ok, yeah, so I'm a little biased against the underscores.
Title: Outpost 2 DLL Programming
Post by: BlackBox on January 05, 2005, 03:13:48 PM
I agree.. I suggest we use the identifiers that the sheets.vol and the rest of the game use. (but probably lower case of course. Also, the current names are full of spelling mistakes)

Yes, some sort of compatibility option. Maybe a #define in the header file to allow the old names?

eg.
Code: [Select]
#define USE_OLDNAMES
#include "../Outpost2DLL.h"
Title: Outpost 2 DLL Programming
Post by: Eddy-B on January 05, 2005, 06:35:59 PM
better yet: why not keep both names ?
i know it's a lot of typing, but at least it's backward-compatible!

#define map_None  mapNone
#define map_Cargo_Truck mapCargoTruck

etc..
Title: Outpost 2 DLL Programming
Post by: Hooman on January 06, 2005, 07:12:59 PM
Lol, guys there is a really simple way

copy and paste, and then delete all the underscores. Also, maybe throw a #define around one set so they can be removed to test if your code compiles under the new names.

enum map_id
{
mapNone = 0,
.
.
.

#ifdef OLD_NAMES // Meh, why not use an underscore in this name ;)
map_None = 0,
.
.
.
#endif
}


And spelling mistakes? I've only really noticed one that Eddy pointed out to me.
 
Title: Outpost 2 DLL Programming
Post by: BlackBox on January 07, 2005, 02:36:08 PM
Hmm. I see these:
   map_Nursury,            // 23
   map_Athiest_Building_Explosion,         // 57
   map_Pheonix_Module,         // 63
There's probably more.

But anyway, we should rename them to something simple. (So it's not a guessing game trying to remember the EXACT name of the constant. Like, map_Robot_Command or map_Robot_Command_Center ? (And it's ridiculously long to type)
It should also be immediately apparent what things are. (What is map_Energy_Cannon? I'd THINK it's the scorpion laser, but I'm not certain)

We also need to fill in all the parameters with information that makes the usefulness of the function more apparent.
Title: Outpost 2 DLL Programming
Post by: PlayingOutpost0-24 on January 07, 2005, 04:52:06 PM
actually, Energy Cannon HAS TO BE the scorpion weapon, and if its not maybe its a hidden Ion Cannon easter egg.
Title: Outpost 2 DLL Programming
Post by: BlackBox on January 11, 2005, 02:53:57 PM
How about, we put different names into a different namespace. (Very simple then, you could do this)

In header:
Code: [Select]
namespace newNames {
enum map_id {
// blah
}
}

In your source, to use the new names just do something like
Code: [Select]
using namespace newNames;

or you could go the long way by doing something like
Code: [Select]
... newNames::LYNX ...
Title: Outpost 2 DLL Programming
Post by: HaXtOr on January 18, 2005, 05:40:13 PM
I figured something nifty out for all of you trying to enumerate a rare ore mine. If you used the map_Mine 0x15 then you will never get a rare ore mine only common mines.
the folowing can be added to the outpost2 headder file but is not the "official name"... yet
map_Mine_Rare = 0x16,
This should only be used for enumerateing rare mines, but I have not tryed useing this value for createing new mines.

I personaly think that map_mine 0x15 sould be named map_Mine_Common
and that the new value i found should be map_Mine_rare
 
Title: Outpost 2 DLL Programming
Post by: Eddy-B on January 18, 2005, 05:45:53 PM
Old news..
Below is the complete list of all map_ids:
Code: [Select]
	map_None = 0,
map_Cargo_Truck,    // 01
map_ConVec,        // 02
map_Spider,        // 03
map_Scorpion,       // 04
map_Lynx,        // 05
map_Panther,       // 06
map_Tiger,        // 07
map_Robo_Surveyor,   // 08
map_Robo_Miner,    // 09
map_GeoCon,        // 0A
map_Scout,      // 0B
map_Robo_Dozer,    // 0C
map_Evacuation_Transport, // 0D
map_Repair_Vehicle,   // 0E
map_Earthworker,    // 0F
map_SmallCapacityAirTransport, // Crashes game when it moves (looks like a scout)

map_Tube = 0x11,
map_Wall,        // 12
map_Lava_Wall,       // 13
map_Microbe_Wall,      // 14
map_Mine,      // 15
map_Rare_Ore_Mine,   // 16
map_Guard_Post,    // 17
map_Light_Tower,      // 18
map_Common_Storage,   // 19
map_Rare_Storage,      // 1A
map_Forum,      // 1B
map_Command_Center,   // 1C
map_MHD_Generator,   // 1D
map_Residence,       // 1E
map_Robot_Command,   // 1F
map_Trade_Center,      // 20
map_Basic_Lab,     // 21
map_Medical_Center,   // 22
map_Nursery,     // 23
map_Solar_Power_Array,  // 24
map_Recreation_Facility, // 25
map_University,    // 26
map_Agridome,      // 27
map_DIRT,        // 28
map_Garage,        // 29
map_Magma_Well,    // 2A
map_Meteor_Defense,   // 2B
map_Geothermal_Plant,  // 2C
map_Arachnid_Factory,  // 2D
map_Consumer_Factory,  // 2E
map_Structure_Factory,  // 2F
map_Vehicle_Factory,     // 30
map_Standard_Lab,    // 31
map_Advanced_Lab,      // 32
map_Observatory,    // 33
map_Reinforced_Residence, // 34
map_Advanced_Residence,  // 35
map_Common_Ore_Smelter,  // 36
map_Spaceport,       // 37
map_Rare_Ore_Smelter,  // 38
map_GORF,        // 39
map_Tokamak,       // 3A

map_Acid_Cloud = 0x3B,  // 3B
map_EMP,       // 3C
map_Laser,        // 3D
map_Microwave,     // 3E
map_Rail_Gun,       // 3F
map_RPG,       // 40
map_Starflare,     // 41
map_Supernova,     // 42
map_Starflare2,    // 43  increased power
map_Supernova2,    // 44  increased power
map_Normal_Explosion = 0x45
map_ESG = 0x46,
map_Stickyfoam,    // 47
map_Thors_Hammer,    // 48
map_Energy_Cannon,   // 49

map_EMP_Missile_Damage = 0x4A
   map_Electrical_Storm = 0x4C
   map_Tornado = 0x4D
   map_EarthQuake = 0x4E
   map_Lava_Eruption = 0x4F
   map_Meteor = 0x50

map_Mining_Beacon = 0x51,
map_Magma_Vent,    // 52
map_Fumarole,     // 53
   map_Discovered,    // 54

map_Disasterous_Building_Explosion = 0x55,
map_Catastrophic_Building_Explosion, // 56
map_Athiest_Building_Explosion,   // 57

map_EDWARD_Satellite = 0x58, // Lynx (in Cargo Truck)
map_Solar_Satellite,    // Wreckage (in Cargo Truck)
map_Ion_Drive_Module,   // Gene Bank 5 (in Cargo Truck)
map_Fusion_Drive_Module, // 5B
map_Command_Module,   // 5C
map_Fueling_Systems,     // 5D
map_Habitat_Ring,      // 5E
map_Sensor_Package,   // 5F
map_Skydock,       // 60
map_Stasis_Systems,   // 61
map_Orbital_Package,     // 62
map_Pheonix_Module,   // 63

map_Rare_Metals_Cargo,  // 64
map_Common_Metals_Cargo, // 65
map_Food_Cargo,    // 66
map_Evacuation_Module,  // 67
map_Children_Module,     // 68

map_SULV,        // 69
map_RLV,       // 6A
map_EMP_Missile,      // 6B

map_Impulse_Items = 0x6C,
map_Wares,        // 6D
map_Luxury_Wares,      // 6E

map_Inter_Colony_Shuttle = 0x6F,
map_Spider_3_Pack,   // 70
map_Scorpion_3_Pack,     // 71

map_Pretty_Art = 0x72,

map_Any = -1

[EDIT]Pay special attention to the map_Any : it is NOT to be used in any definitions, but it should be used in things like enumerators or triggers. For example a countTrigger to count ALL cargo trucks (regardless of cargo).
Title: Outpost 2 DLL Programming
Post by: BlackBox on January 18, 2005, 06:06:58 PM
We have that already.
Code: [Select]
#define plr_all -1

Thanks again (This was going to end up in the new SDK header anyway).
Title: Outpost 2 DLL Programming
Post by: Eddy-B on January 19, 2005, 10:59:22 AM
So that's what plr_all is for huh ?  :P
...

plr_all is for sending messages to all players.
map_Any (as i defined it), is ti be used where the function requires a map_id.
I know both represent the same value (-1) but for me; it's much clearer when using the map_Any   :-)