Jump to content
Sign in to follow this  
deanolium

Mod Script Findings

Recommended Posts

in my sprocket version patch%02d.pak files are automatically loaded (they're not listed in the ccsettings.xml), higher number shadows lower number - i.e. I was able to shadow files contained in the patch00.pak with a patch01.pak

Share this post


Link to post
Share on other sites

It's pretty much what I expected - fake patch mods would be possible this way. Steam users are now out of luck... I tried wildcards in the settings dir; didn't work.

---------- Post added at 21:23 ---------- Previous post was at 21:19 ----------

Try the following please:

  • name the file patch66.pak and see what happens
  • name the file patchMyMod.pak and see what happens

If we're lucky, the code that reads these files doesn't use a sequential enumeration that stops at the first nonexistent file, but some glob like patch*.pak or patch??.pak :-)

Edited by Thygrrr

Share this post


Link to post
Share on other sites

no luck, it has to be patch%02d.pak (that string can be found in the .exe, too) and the enumeration has to be continuous, i.e. patch<x+1>.pak is only loaded if patch<x>.pak is available

Edited by Master85

Share this post


Link to post
Share on other sites
no luck, it has to be patch%02d.pak (that string can be found in the .exe, too) and the enumeration has to be continuous, i.e. patch<x+1>.pak is only loaded if patch<x>.pak is available

is the % sign required?

Share this post


Link to post
Share on other sites

No, the %02d is something from a string in the carrier.exe ... meaning it takes a number, and pads it to two digits to decide which file names to look for while loading.

So the only patch files that work now are patch00.pak (included with the game), and subsequently a mod named patch01.pak ... and only if you have a patch01.pak, you can also have a patch02.pak, and so on.

Obviously, creating fake patches can screw up future updates of the game by causing updated content to be reverted to older versions at load time. As can any "plain" mods based on directories.

Share this post


Link to post
Share on other sites

Preprocessor

The scripting language has a macro preprocessor. Not sure how powerful it is.

However, one can define symbols like this (for example at the start of script.c)

#define DEVELOPER 1 (this actually enables a shitload of developer options. still trying to figure out where all the logging happens.)

However, it does seem like DEVELOPER has to be defined in every file you want it defined (I guess it's related to the interactive designer outlined below)

To be more exact, you can get away with #define DEVELOPER to turn on the developer features. The logging does indeed happen in that directory you mentioned, in the Campaigns.log.

Something else that's useful is the SLog function. This is used in the AI Advisor files (which despite the file names are for both the human player and the AI player), and is defined in uec_empireadvisor.h

By default this is set to just Print (which obviously just goes nowhere for us), but I then added the following lines to the end of that function:

stratAI = "AI: " + stratAI;

g_Game.AddMessage(stratAI, COLOR_RED, LMF_EMPIRE);

This then causes the AI log messages to output to screen. I then went further and replaced print statements with SLog to further probe what was happening.

Share this post


Link to post
Share on other sites

Database Format

The dbexport.bin is probably a Firebird database.

---------- Post added at 22:39 ---------- Previous post was at 21:10 ----------

Yes it is a firebird database, as an IFF chunk (4 char head 'DBEX' + 4 bytes Little endian int32 designating there is data that follows).

However, I am having a little trouble finding the right version of fbembed.dll they used.

firebird 2.0: kinterbasdb.OperationalError: (-820, 'isc_attach_database: \n unsupported on-disk structure for file C:\\WORKSPACE\\CARRIERTOOLS\\SRC\\DBEXPORT.BIN; found 5936.7, support 11.0')

firebird 2.1: kinterbasdb.OperationalError: (-820, 'isc_attach_database: \n unsupported on-disk structure for file C:\\WORKSPACE\\CARRIERTOOLS\\SRC\\DBEXPORT.BIN; found 5936.7, support 11.1')

firebird 2.5: kinterbasdb.OperationalError: (-820, 'isc_attach_database: \n unsupported on-disk structure for file C:\\WORKSPACE\\CARRIERTOOLS\\SRC\\DBEXPORT.BIN; found 5936.7, support 11.2')

---------- Post added at 22:41 ---------- Previous post was at 22:39 ----------

Maybe some part of the file is encrypted or corrupted.

---------- Post added at 22:48 ---------- Previous post was at 22:41 ----------

I'm trying to build a small test program in C++, because they used IBPP. However, I expect they used it in combination with fbembed.

---------- Post added at 22:58 ---------- Previous post was at 22:48 ----------

There's also a static firebird library.

Edited by Thygrrr

Share this post


Link to post
Share on other sites

I think they're using a firebird database internally which they're exporting to dbexport.bin - i.e. dbexport.bin isn't necessarily a fdb file

Edited by Master85

Share this post


Link to post
Share on other sites

Yeah that thought just hit me when I was starting to look for ways to access firebird files in an in-memory state.

It's a pure coincidence (maybe) that the first section of the dbexport.bin looks like a firebird database page.

Anyway:

Database Format

The dbexport.bin is quite likely created from a Firebird database. In itself, the format seems mysterious.

More interestingly, the game itself can be configured to use a remote DB. It's hardcoded, so you'd have to set up a server on your LAN precisely to serve 192.168.34.16:/var/firebird/CCNew.fdb (default password and username) after configuring the db entry in ccsettings.xml.

I'll stop here because this is definitely reverse engineering territory and the practical uses for us modders are very slim (I'll rather continue with my 'cleanwrite' of the script.c / ccgame.c files so I can get that minimal mod thing going).

Dram mentioned that there will be a modding tool to edit this, so nothing for us to do here.

Edited by Thygrrr

Share this post


Link to post
Share on other sites

Probably most of the game's "balancing" properties. Like Dram said, they will likely provide a tool to edit this (or give us a Dbexport tool).

Share this post


Link to post
Share on other sites
Yeah that thought just hit me when I was starting to look for ways to access firebird files in an in-memory state.

It's a pure coincidence (maybe) that the first section of the dbexport.bin looks like a firebird database page.

Anyway:

Database Format

The dbexport.bin is quite likely created from a Firebird database. In itself, the format seems mysterious.

More interestingly, the game itself can be configured to use a remote DB. It's hardcoded, so you'd have to set up a server on your LAN precisely to serve 192.168.34.16:/var/firebird/CCNew.fdb (default password and username) after configuring the db entry in ccsettings.xml.

Yes, the database was used in-company to allow us to change settings on the server, and thus automatically see the effects in game. It is now, however, obsolete. We are removing the database altogether, and instead moving to xml files, which will be easily adjustable by anyone for mods.

Is dbexport all the weapon stuff?

Yes, weapon settings, vehicle settings, the strategy island list, and many more things.

Share this post


Link to post
Share on other sites

Forward Declarations / "Header" files

While some of the .h files seem to contain forward declarations, they don't really seem to work like headers in the traditional sense. First I thought it was dirty code (it still kind of is), but now I actually think the Carrier Command scripting language is really just a "include everything once to make it run" deal, and forward declarations are treated without the strictness that C would impose on them.

The .c / .h dichotomy is kind of misleading (not that the developers paid any heed to that). Considering using .inc instead in my refactoring efforts. Because it seems impossible to have the engine compile other .c files than the ones it's hardwired to compile as entry points. :)

Anyone have an idea how I can get it to compile a new .c file?

Share this post


Link to post
Share on other sites

Totally not. :-) Why should we ask random strangers to help us out on something the devs will solve for us soon enough, and better?

---------- Post added at 23:47 ---------- Previous post was at 22:58 ----------

RTTI

You can make your type casts safer by querying the RTTI where appropriate to ensure an object is of a certain type:

if ( IsInherited( pTarget,Type("Carrier")) )
	{
		Carrier carrier = pTarget;
		//... do stuff

---------- Post added at 23:50 ---------- Previous post was at 23:47 ----------

Torpedo Fake Damage

Well, the damage rules for Torpedos are funny:

/! When torpedo hits target
void OnTorpedoHitTarget( Projectile pTorpedo, InteractiveEntity pTarget )
{
	if ( pTarget == NULL )
		return;

	if ( IsInherited( pTarget,Type("Carrier")) )
	{
		Carrier carrier = pTarget;

		InteractiveRepairZone rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_MANTALIFTS );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_WALRUSLEFT );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_WALRUSRIGHT );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_CRUISEMISSILE );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_CONTROLTOWER );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_DEFENSE );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_OFFENSE );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_SUPERSTRUCTURE );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_ENGINES );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);

		rz =  carrier.GetLocalRepairZone( ECARRIER_ZONE_REPAIRSYSTEM );
		if ( rz.GetHealth() > 0.7 )
			rz.SetHealth( 0.5, true);
		else
			rz.SetHealth( 0, true);
	}
}

Share this post


Link to post
Share on other sites

True, the tools look pretty good, many hours of tinkering, those intelligent torpedos (lol) know the targets health and do a precise amount of damage to different systems, wow

Edited by ToxLaximus

Share this post


Link to post
Share on other sites

Torpedo Fake Damage

Well, the damage rules for Torpedos are funny:

Am I reading that correct? If the helth is less than .7 then set it to .5, but if the helth is less than .5 than heal it to .5?????

Share this post


Link to post
Share on other sites
Am I reading that correct? If the helth is less than .7 then set it to .5, but if the helth is less than .5 than heal it to .5?????

It more looks like: If health (of each section) is >70% then set the health of that bit to 0%. If instead the health of that bit is <=70% then set health to 0% (ie, destroy it). Which basically means two hits will kill the carrier. Massively overpowered if I'm reading the code right and just plain weird anyway.

Share this post


Link to post
Share on other sites

Wait, ya, I didn't see the else statment

So it reads, if the current hp is more than .7 , then set it to .5 . But if hp is less than .7 , then set hp to 0 (destroy it).

And I agree that is overpowered. It puts the damage on a very odd curve. IE: It will eather do 50% damage or 70% damage. Maby it is done this way to account for armor?

But right off the bat I see a huge bug, what if the current hp is exactly .7?

Note: I have not unpacked and looked at any of the code directly yet. I'm waiting for the SDK befor I do.

Share this post


Link to post
Share on other sites

Hi..

I really do love this game, it is very fun to play, and would hate if it would go to waste so I ended up here and found the fantastic Carrier Tool!

One problem is the whimpy Carrier AI that run pasts you and doing nothing in most of the times.

Since I'm an old programmer and had some time over I scanned through the AI-code and I found and fixed one AI-problem

in uec_militaryadvisor.h in the class MilitaryAdvisor, function: AnalyzeNeeds

I found this code:

if ( AvoidEnemyCarrier )

WriteNeed( Logger, mem, this, GOTO_ISLAND, enCurr.GetArea(), 0, 9 );

else

{

if ( myCurr == enCurr && d < 320.0 ) // 1000m

WriteNeed( Logger, mem, this, ASSAULT_CARRIER, enemy.GetID(), 0, 9 );

else

WriteNeed( Logger, mem, this, GOTO_ISLAND, enCurr.GetArea(), 0, 9 );

}

and replaced it with:

if ( AvoidEnemyCarrier )

WriteNeed( Logger, mem, this, GOTO_ISLAND, enCurr.GetArea(), 0, 9 );

else

{

if ( d < 640.0 ) // 2000m ...

{

WriteNeed( Logger, mem, this, ASSAULT_CARRIER, enemy.GetID(), 0, 9 );

}

else

{

WriteNeed( Logger, mem, this, GOTO_ISLAND, enCurr.GetArea(), 0, 9 );

}

}

If you get too close to him he might get very personal and come after you with walruses and mantas! The Carrier -battles will be awesome! But he will still run if he is scared of you. :)

Happy Hunting

/J.

---------- Post added at 19:48 ---------- Previous post was at 19:39 ----------

Omg.. sorry for the previoius post.. The code looked terrible.. I try again with code-tags

Replace:

if ( AvoidEnemyCarrier )
WriteNeed( Logger, mem, this, GOTO_ISLAND, enCurr.GetArea(), 0, 9 );
else
{

if ( myCurr == enCurr && d < 320.0 ) // 1000m
{
WriteNeed( Logger, mem, this, ASSAULT_CARRIER, enemy.GetID(), 0, 9 );
else
WriteNeed( Logger, mem, this, GOTO_ISLAND, enCurr.GetArea(), 0, 9 );
}

With:

if ( AvoidEnemyCarrier )
WriteNeed( Logger, mem, this, GOTO_ISLAND, enCurr.GetArea(), 0, 9 );
else				
{
if ( d < 640.0 ) // 2000m
{
	WriteNeed( Logger, mem, this, ASSAULT_CARRIER, enemy.GetID(), 0, 9 );
}

else
{
	WriteNeed( Logger, mem, this, GOTO_ISLAND, enCurr.GetArea(), 0, 9 );
}
}

Share this post


Link to post
Share on other sites

I've moved from trying to refactor the game to thoroughly gutting it. The scripts are full of circular dependencies and some very disadvantageous design "patterns".

I should have a "minimal mod" ready this weekend or so, "minimal" meaning you and your (probably empty) carrier at a neutral island. I will have refactored every core component (CGame, CCCampaign, CCPlayer, CCMPmode, CCTelecontrol, IslandModel, _entity and its descendants, etc.) to be cleanly object oriented, and there will be no singletons (except for what proto native GetGame() returns from the engine, and this is only to be used in the entry points for the engine).

I will then re-add functionality from the classic campaign parts of the scripts (keeping it clean and separate from the gaea mission stuff, which, under this mod, will be deactivated!).

The "gutted" mod will probably be part of CarrierTools. It should help people write clean mods instead of hacks.

I will also write a mod installer that works with profiles, making it possible that mods peacefully coexist without blocking each other, by using specifically configured subfolders (on steam, it won't work with the current sprocket/retail versions I'm afraid). This installer will also help us developers to try ot other peoples' mods without damaging or interfering with their own development versions.

---------- Post added at 10:27 ---------- Previous post was at 10:05 ----------

I can't guarantee my mod will work though. Lots of stuff is woven into the engine (seemingly for oftentimes dubious reasons). :)

Edited by Thygrrr

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×