Yesno I will won't. :P
It comes in two parts.
// Replace the BOOL APIENTRY Dllmain section with this:
// -----------------------------------------------------------------------
// Blight Helper Code
// -----------------------------------------------------------------------
unsigned char *setTileLoc = (unsigned char *)0x00476D32;
unsigned char oldSetTile[] =
{
0x25, 0xE0, 0xFF, 0x00, 0x00, // and eax, 0FFE0h
0xC1, 0xE8, 0x05 // shr eax, 5
};
unsigned char newSetTile[] =
{
0x90, 0x90, 0x90, 0x90, 0x90, // nop * 5
0x90, 0x90, 0x90 // nop * 3
};
struct mapCell
{
int cellType:5;
int tileIndex:11;
int unitID:11;
int lava:1;
int lavaPossible:1;
int expand:1;
int microbe:1;
int wallBuilding:1;
};
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
DWORD oldAttr;
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
VirtualProtect(setTileLoc, sizeof(newSetTile), PAGE_EXECUTE_READWRITE, &oldAttr);
memcpy(setTileLoc, newSetTile, sizeof(newSetTile));
}
else if (fdwReason == DLL_PROCESS_DETACH)
{
memcpy(setTileLoc, oldSetTile, sizeof(newSetTile));
}
return TRUE;
}
Now, you have two choices for killing infected buildings. You can either destroy them as they become infected, or gradually damage infected buildings and destroy them when their HP drops below zero.
//Kill structures as they become infected
for ( int i = 0; i < TethysGame::NoPlayers(); i++ )
{
Unit curUnit;
PlayerBuildingEnum unitEnum(i, mapNone);
while (unitEnum.GetNext(curUnit) )
{
mapCell cell;
int tile = GameMap::GetTile(curUnit.Location() );
memcpy(&cell, &tile, 4);
if (cell.expand || cell.microbe)
{
curUnit.DoDeath();
}
}
}
OR
// Gradually damage infected buildings over time and then kill them
// NOTE: Requires IUnit add-on
void AIProc()
{
for (int i = 0; i < TethysGame::NoPlayers(); i++)
{
//Blighted Building Kill Code
IUnit curUnit;
PlayerBuildingEnum unitEnum(i, mapNone);
while (unitEnum.GetNext(curUnit))
{
mapCell cell;
int tile = GameMap::GetTile(curUnit.Location());
memcpy(&cell, &tile, 4);
if (cell.expand || cell.microbe) {
curUnit.SetDamage(curUnit.GetDamage()+7+TethysGame::GetRand(5));
if (curUnit.GetDamage() >= GetHitPoints(curUnit))
curUnit.DoDeath();
}
}
}
}
You can learn more about IUnit here (http://forum.outpostuniverse.net/index.php?showtopic=2011).
Hope that helps! May I ask what you want it for, though?
Strange. That is exacly what i did. I added op2extra.lib correcly and wrote
#include "..\Outpost2DLL\Outpost2DLL.h"
#include "..\OP2Helper\OP2Helper.h"
#include "..\IUnit\IUnit.h"
#include "..\IUnit\commands.h"
But Visual C++ can't find "GetHitPoints"
Flashy, I'll see if I can setup the SDK & MSVC again, and I'll try to recreate your steps
[size=8]Edit: For me, it breaks as early as mapCell. Is mapCell defined in IUnit?[/size]
[size=8]1>------ Build started: Project: OP2Script, Configuration: Release MinSize Win32 ------
1>Compiling...
1>LevelMain.cpp
1>.\LevelMain.cpp(131) : error C2065: 'mapCell' : undeclared identifier
1>.\LevelMain.cpp(131) : error C2146: syntax error : missing ';' before identifier 'cell'
1>.\LevelMain.cpp(131) : error C2065: 'cell' : undeclared identifier
1>.\LevelMain.cpp(133) : error C2065: 'cell' : undeclared identifier
1>.\LevelMain.cpp(133) : error C3861: 'memcpy': identifier not found
1>.\LevelMain.cpp(134) : error C2065: 'cell' : undeclared identifier
1>.\LevelMain.cpp(134) : error C2228: left of '.expand' must have class/struct/union
1> type is ''unknown-type''
1>.\LevelMain.cpp(134) : error C2065: 'cell' : undeclared identifier
1>.\LevelMain.cpp(134) : error C2228: left of '.microbe' must have class/struct/union
1> type is ''unknown-type''
1>.\LevelMain.cpp(136) : error C3861: 'GetHitPoints': identifier not found
1>Build log was saved at "file://c:\Program Files\Outpost2\SDK\Levels\Flashy\ReleaseMinSize-IntermediateFiles\BuildLog.htm"
1>OP2Script - 10 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========[/size]
[size=8]Edit 2: Wait, what?! That's part of Sirbomber's snippet! wtf
Edit 3: Adding that mapCell struct to LevelMain.cpp leaves me with this:[/size]
[size=8]1>------ Build started: Project: OP2Script, Configuration: Release MinSize Win32 ------
1>Compiling...
1>LevelMain.cpp
1>.\LevelMain.cpp(144) : error C3861: 'memcpy': identifier not found
1>.\LevelMain.cpp(147) : error C3861: 'GetHitPoints': identifier not found
1>Build log was saved at "file://c:\Program Files\Outpost2\SDK\Levels\Flashy\ReleaseMinSize-IntermediateFiles\BuildLog.htm"
1>OP2Script - 2 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
[/size]
[size=8]
Edit 4: Got it to stop complaining. Now waiting for the blight to spread[/size]
Edit 5: Okay, got it working now.
I had to put this in LevelMain.ccp:
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <IUnit/IUnit.h> // The specific loading function for the SVN's SDK. Extracted op2extra.zip into /API/IUnit, and my project is in /Levels/Flashy
// Also, my Linker > Input > Additional Dependencies now reads: odbc32.lib odbccp32.lib ../../API/IUnit/op2extra.lib
struct mapCell
{
int cellType:5;
int tileIndex:11;
int unitID:11;
int lava:1;
int lavaPossible:1;
int expand:1;
int microbe:1;
int wallBuilding:1;
};
int GetHitPoints(Unit &unit)
{
int unitObj = *((int*)0x54F848) + (120*unit.unitID);
int hp = 0;
__asm
{
mov ecx, unitObj
mov eax, [ecx]
call [eax]
add eax, 8
mov eax, [eax]
mov hp, eax
}
return hp;
}
(be sure to read the IUnit related comments at the top!)
And DLLMain.ccp:
unsigned char *setTileLoc = (unsigned char *)0x00476D32;
unsigned char oldSetTile[] =
{
0x25, 0xE0, 0xFF, 0x00, 0x00, // and eax, 0FFE0h
0xC1, 0xE8, 0x05 // shr eax, 5
};
unsigned char newSetTile[] =
{
0x90, 0x90, 0x90, 0x90, 0x90, // nop * 5
0x90, 0x90, 0x90 // nop * 3
};
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
DWORD oldAttr;
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
// Modify the SetTile code for our purposes
VirtualProtect(setTileLoc, sizeof(newSetTile), PAGE_EXECUTE_READWRITE, &oldAttr);
memcpy(setTileLoc, newSetTile, sizeof(newSetTile));
}
else if (fdwReason == DLL_PROCESS_DETACH)
{
// Put the old SetTile code back
memcpy(setTileLoc, oldSetTile, sizeof(newSetTile));
}
return TRUE;
}
Are you using the SVN's updated SDK, Flashy?
Forgot something. Stick this with the rest of the "Blight Helper Code":
int GetHitPoints(Unit &unit);
And stick this below AIProc somewhere:
int GetHitPoints(Unit &unit)
{
int unitObj = *((int*)0x54F848) + (120*unit.unitID);
int hp = 0;
__asm
{
mov ecx, unitObj
mov eax, [ecx]
call [eax]
add eax, 8
mov eax, [eax]
mov hp, eax
}
return hp;
}
LevelMain...? Uhhh... Are we using the same templates here...?
OP2 Mission Skeleton Template (Most comments removed)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Outpost2DLL.h>
#include <OP2Helper.h>
char MapName[] = "on6_01.map";
char LevelDesc[] = "6 Player, LastOne, '<map name>' map";
char TechtreeName[] = "MULTITEK.TXT";
SDescBlock DescBlock = { MultiLastOneStanding, 6, 12, 0 };
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
}
return TRUE;
}
int InitProc()
{
// **TODO**: Add your own code here.
return 1; // return 1 if OK; 0 on failure
}
void AIProc()
{
}
void __cdecl GetSaveRegions(struct BufferDesc &bufDesc)
{
bufDesc.bufferStart = 0; // Pointer to a buffer that needs to be saved
bufDesc.length = 0; // sizeof(buffer)
}
int StatusProc()
{
return 0; // must return 0
}
SCRIPT_API void NoResponseToTrigger()
{
}