Author Topic: Outpost 2 DLL Programming  (Read 15731 times)

Offline PlayingOutpost0-24

  • Hero Member
  • *****
  • Posts: 537
    • http://op3np.xfir.net
Outpost 2 DLL Programming
« Reply #25 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?
Great news for OP2 fans... OP3 in progress.
Official Site
Outpost 3: A New Power progress
OP3:NP Discussion

Progress in OP3:NP[/size][/font]
PLANNING[|||||||||-]
GRAPHICS [||||------]
SOUNDS [|---------]
MAP DESIGNING [|||||-----]
CODING [----------]
Going slowly... Very slow.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #26 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.
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #27 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.
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #28 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.
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #29 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.
 
« Last Edit: August 15, 2004, 05:16:07 PM by Hooman »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #30 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);
« Last Edit: August 22, 2004, 01:32:09 AM by Hooman »

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #31 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:
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #32 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:
« Last Edit: August 27, 2004, 03:06:11 PM by Hooman »

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Outpost 2 DLL Programming
« Reply #33 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.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #34 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.
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #35 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.
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #36 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.
 

Offline PlayingOutpost0-24

  • Hero Member
  • *****
  • Posts: 537
    • http://op3np.xfir.net
Outpost 2 DLL Programming
« Reply #37 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?)
« Last Edit: September 27, 2004, 12:27:35 PM by PlayingOutpost0-24 »
Great news for OP2 fans... OP3 in progress.
Official Site
Outpost 3: A New Power progress
OP3:NP Discussion

Progress in OP3:NP[/size][/font]
PLANNING[|||||||||-]
GRAPHICS [||||------]
SOUNDS [|---------]
MAP DESIGNING [|||||-----]
CODING [----------]
Going slowly... Very slow.

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Outpost 2 DLL Programming
« Reply #38 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.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #39 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);

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Outpost 2 DLL Programming
« Reply #40 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)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #41 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.
« Last Edit: September 28, 2004, 09:01:18 PM by Hooman »

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Outpost 2 DLL Programming
« Reply #42 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".)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #43 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"


 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #44 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.
 

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #45 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.  :)
 
« Last Edit: October 05, 2004, 02:02:12 AM by Hooman »

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Outpost 2 DLL Programming
« Reply #46 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)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #47 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.
« Last Edit: December 09, 2004, 06:52:45 PM by Hooman »

Offline BlackBox

  • Administrator
  • Hero Member
  • *****
  • Posts: 3093
Outpost 2 DLL Programming
« Reply #48 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.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Outpost 2 DLL Programming
« Reply #49 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.