Jump to content
Sign in to follow this  
Thygrrr

[TOOL] CarrierTools (modding tool inside)

Recommended Posts

No, I won't redistribute the unpacked data files from the game, as that would violate the Carrier Command EULA (and the TOS of bitbucket, too!).

The data00.cc, data01.cc and data02.cc files also contain about 3.5 GB of stuff - basically the entire game. Some files are a few hundred bytes long, some are dozens or hundreds of megabytes. Mods tend to change a few files here and there, but for example the ui, worlds (islands), sounds and scripts are spread out over all three data files.

You'll simply have to unpack your locally installed data files and then you can mod the game to your heart's content.

Your mod that you share with others should then only contain the things you changed.

I even created convenient batch files so all you need to do is drag your data02.cc onto extract.bat and copy the folders you want to change into your Carrier Command install directory.

I'll probably create EXE files from the python scripts after some time. Problem there is: It means more maintenance work for me - an evening spent setting up all the DLLs and maybe an installer, and including a point of failure and having to respond to user error reports for yet another file; and subsequently only very little gain for the modders (modding is so much work, installing python is la mere 1/10000th of the complete time you'll need to invest). Also, the tools change so quickly at the moment (multiple changes every evening), taking an hour to make a release every night isn't worth it at the time.

Conclusion: Modding for Carrier Command is in its infancy, it's for tinkerers who like to take things apart by hand to find out how stuff works under the hood. Bohemia Interactive themselves announced they will release some modding tools in a little over a month to the general public; until then they will probably change a lot in the game's code and structure itself so mods don't have to be hacks that only us freaks can install and create anymore.

Edited by Thygrrr

Share this post


Link to post
Share on other sites
Guest

I am back from my short holiday so I have updated both the news and the mirror on Carriercommandaholic.com.

You can find our mirror here: CarrierTools (I added a date as version so its clear this is the latest version).

Share this post


Link to post
Share on other sites

The only "clear" way for the version is the changeset-id that's at the end of the filename when you get it from https://bitbucket.org/thygrrr/carriertools/get/tip.zip

Unfortunately, it's not ascending; however, the advantage is that it uniquely identifies every version. You could also use changeset numbers if you clone the repository (but these are bound to repositories - if you commit local changes, your changeset numbers would be duplicated starting when the repo was cloned; the changeset-ids on the other hand are never duplicated).

Long story short - you are free to mirror the files of course. I recommend you follow the repository on bitbucket so you see every little update. :-)

Eventually I will add tags (i.e. actual version numbers) and probably make some exe files with py2exe.

But CarrierTools is all version 0.0.0.0 at the moment. :-)

Edited by Thygrrr

Share this post


Link to post
Share on other sites

Modding API Framework Added

I am nearly done stripping down the game. I decided not to split up some specific files from the stock code, but instead included them. I have pushed the code to the bitbucket repository.

guts - The Guts of Carrier Command, extracted and refactored

These are the guts of the game. This is not a "mod" in the traditional sense. It's rather an API. It's also a work in progress.

In fact, this mod crashes when you try to start a new game in it because the empty CGame class that is instantiated doesn't spawn any islands or players. Basically the entire game was stripped down and refactored so it provides all the original native entry points, but with all other functionality removed. The design goal here was to allow for custom campaigns and game types, ideally coexisting (so you can extend your and other people's work more easily).

This API hopefully makes is possible to build a mod from the ground up. It also (in theory) makes it possible to write a "stock mod" that represents the original game with a cleaner architecture.

There may also be some script-side entrypoints that are called by native code that I missed. It's almost impossible to sniff these out at design time, they will pop up at runtime soon enough.

NOTE: You will see the game still does a lot of things despite being so stripped down. This is all part of the native game magic. The stock cutscenes are incompatible - they use the g_Game singleton, which is superseded in guts by the proper GetGame() entrypoint. They will still "work" syntactically (their scripts are interpreted line by line), but they will cause log spam and may malfunction semantically.

Therefore, a fixed main_menu.cut file is provided. I decided to not strip it down, but instead left it so it gives you the comforting experience of the original game's main menu scene.

Please note: This is a very early WIP. It doesn't have a mod dispatch yet. It's not even nicely documented. (mod dispatch will work by instantiating a custom CGame child class for each mod - still working out a nice way to do this at runtime). When the mod loader API is done, I will provide a minimal mod that tries to spawn an island and a carrier and nothing more, just to ensure I didn't break anything (I have yet to see the game side again - currently, my guts / minimal mod / carrier command total conversion are not playable).

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

It's a little late now, I'll clean up the guts code tomorrow. Maybe I can figure out a nice mod dispatch technique soon (i.e. the API loading at least one generic mod at runtime). Game Modes etc. can later be dispatched by the mods themselves if desired.

Share this post


Link to post
Share on other sites

For the minimal mod, you may want to do 2 islands to test to see if the islands will link up or not.

Share this post


Link to post
Share on other sites

Not sure.

It just crossed my mind that the most productive mod (i.e. to find out if the API is solid) would be a stock mod, that means making the vanilla, unmodded game work with the guts API. It probably won't be too hard (I know pretty much exactly the parts I had to rewrite while gutting the game, so I can put in the appropriate recoded functionality on the stock side - but I will need to cut some corners because otherwise I'd have to redistribute a significant portion of the original game code, and I wouldn't want to do that).

That means the stock mod will still inherently not be very extensible. So what. :)

Share this post


Link to post
Share on other sites
Is there a way to distribute mods as a package?

Yes, but it's a little tricky to install them - and it works differently for steam and non-steam versions. I am still working on improving this process.

(steam) 1. Put all your mod components into a directory named like you want your mod to be named, and then pack that directory using pac1.py or by dragging that directory onto the package.bat that I conveniently include with the CarrierTools. Put the resulting .pak into your Carrier Command directory.

(steam) 2. Open the ccsettings.xml that belongs to your player's profile - this is NOT the one in the Carrier Command directory, it's the one where your save games are.

Add the line <path directory="mymodname.pak" /> to the top of the filesystems list, replacing mymodname with the name of your mod, obviously.

(sprocket/retail) Name your mod patch01.pak (or maybe later) and place that in the Carrier Command Directory. Alternative: on version 1.2, unpack patch00.pak and put the mod into it as well (overwriting the originals), then re-pack.

As for the Sprocket version: Isn't 1. necessary? Because if I start reading at the blue part ("Name your mod patch01.pak") then I'm thinking "Huh? Which mod?"

Share this post


Link to post
Share on other sites

Guys, I want to pack a directory structure. But what folder level will I package?

If I have a changed file, e.g. ".\scripts\ai\whateverfile.h" - will I drag the scripts-folder onto the package.bat? Or will I take the ai-Folder?

TIA

Share this post


Link to post
Share on other sites

No you must put your scripts and other modded directories into another directory (e.g. mymodfolder) and package THAT.

Share this post


Link to post
Share on other sites

Thygrrr, so this is alright? (myscriptname PLUS complete unpacked folder structure)

mymodfolder\scripts\ai\whateverfile.h

I'm asking because I want to have that Walrus AI change and it is not easily recognizable if the mod works...

Share this post


Link to post
Share on other sites

Yeah. You should put in some debug test output to see if it really works (it's also not trivial to include your mod, even if it's packaged right)

Share this post


Link to post
Share on other sites

Thygrrr, I did make a small mod that improves the walrus a little, I wondered if you could take it further.

I'm more a video maker than coder, but if you want to test anything I can do comparison vids like the walrus AI one.

The 2 h4m remarks are the changes, changed a 0.9 to 0 and a -0.9 to 0 from a suggestion from Tontow to make the walrus steer more efficiently, most of what's below I don't understand much, but maybe the walrus can be fixed from here?

walrusvehfight.h

	vector GetMovementTarget()
{
	Object enemy = GetTarget();

	if( Parent == NULL || enemy == NULL )
	{
		ret = ZeroVec;
		return ret;
	}

	bool los = Parent.IsTargetInLOS( enemy );
	vector myPos = Parent.GetPosition();
	vector tgPos = enemy.GetPosition();
	vector myOri = Parent.GetOrientation();
	vector toTrg = tgPos - myPos;
	vector toCurr = ret - myPos;
	float dist = VectorNormalize( toTrg );
	float distToTrg = VectorLength( toCurr );

	if( los )
	{
		LastKnownEnemyPos = tgPos;
		DropTarget = NULL;
	}
	else
	{
		// solve drop target logic
		if( DropTarget )
		{
			WriteToUnitWidget( Parent, "Seeking Droptarget", MSG_MESSAGE );
			return LastKnownEnemyPos;
		}
	}

	float time0 = GetWorldTime();
	float time = time0 - TimeStamp;

	float ori = toTrg * myOri;

	UpdateDistances();
	UpdateBehaviorVars();

	// update weapons
	if( WeaponSystem )
	{
		if( WeaponSystem.PrimaryWeapon && los && WeaponSystem.PrimaryWeapon.HasLoS( enemy ) )
		{
			WeaponSystem.AutoFire();
		}
		else
		{
			WeaponSystem.Reset();
		}
	}

	// check max distance & LoS
	if( dist > MAX_DISTANCE )
	{
		WriteToUnitWidget( Parent, "Tg far", MSG_MESSAGE );
		return tgPos;
	}

	// check cover
	if( ParentSolver.MyCover )
	{
		// validate cover
		if( !ProvidesCover( tgPos ) )
		{
			RemoveCover();

			// left cover, set stamp
			CoverActStamp = time0;
		}
	}
	if( !ParentSolver.MyCover )
	{
		float dt = time0 - CoverSerchStamp;
		float dt2 = OutOfCoverTime * 2.0; // HACK to avoid heavy conditioning
		if( CoverActStamp > 0 ) // if we are out of cover
		{
			dt2 = time0 - CoverActStamp;
		}
		if( dt > COVER_SEARCH_FREQ && dt2 > OutOfCoverTime )
		{
			float r = frand( 0, 1.0 );
			CoverSerchStamp = time0;
			if( r < ToCoverProb )
			{
				FindCover(myPos, CoverSearchRadius, enemy, NULL);
			}
		}
	}
	if( ParentSolver.MyCover )
	{
		// got cover, go there
		if( CoverActStamp < 0 )
		{
			CoverPos = GetRealCover( tgPos );
			float coverD = VectorLength( CoverPos - myPos );
			if( coverD < COVER_DIST )
			{
				// I am in cover, set my stamp to arrival time
				CoverActStamp = time0;
				return ZeroVec; // stop in cover
			}
			else
			{
				// proceed into cover
				WriteToUnitWidget( Parent, "Into cover", MSG_MESSAGE );
				return CoverPos;
			}
		}
		else
		{
			// already in cover, abort?
			dt = time0 - CoverActStamp;
			if( dt > InCoverTime )
			{
				// leave cover
				CoverActStamp = time0;
				RemoveCover();
				// proceed to automated motion below
			}
			else
			{
				WriteToUnitWidget( Parent, "In cover", MSG_MESSAGE );
				return ZeroVec; // stay in cover
			}
		}
	}

	if( !los )
	{
		WriteToUnitWidget( Parent, "NO Los", MSG_MESSAGE );
		return tgPos;
	}

	if( ori < -0.8 )
	{
		WriteToUnitWidget( Parent, "On my back", MSG_MESSAGE );

		return tgPos;
	}
	else
	{
		float range = OPTIMAL_DISTANCE - MIN_DISTANCE;
		range *= 0.3;
		float goodDist = MIN_DISTANCE + range;

		// select an action to perform
		vector wantedDir;
		vector wantedOri = ZeroVec;
		if( time > TimeFreq || CurrAction < 0 )
		{
			CurrAction = rand( 0, 3 );
			TimeStamp = time0;
			TimeFreq = TIME_FREQ_BASE + frand( -TIME_FREQ_RAND, TIME_FREQ_RAND );
			if( dist > goodDist ) Distancing = false;
			else Distancing = true;
		}

		if( CurrAction == 2 ) // go to/from target
		{
			if( !Distancing ) // approach
			{
				WriteToUnitWidget( Parent, "To target", MSG_MESSAGE );
				wantedDir = toTrg;
			}
			else // away
			{
				WriteToUnitWidget( Parent, "From target", MSG_MESSAGE );
				wantedDir = -toTrg;
				wantedOri = toTrg;
			}
		}
		else
		{
			vector v1 = RotateVectorXY( toTrg, M_PI * 0.5 );
			vector v2 = -v1;
			float d1 = myOri * v1;
			float d2 = myOri * v2;

			if( CurrAction == 1 ) // forward
			{
				WriteToUnitWidget( Parent, "Forward", MSG_MESSAGE );
				if( d1 > d2 ) wantedDir = v1;
				else wantedDir = v2;
			}
			else // 0 - backward
			{
				WriteToUnitWidget( Parent, "Backward", MSG_MESSAGE );
				if( d1 > d2 )
				{
					wantedDir = v2;
					wantedOri = v1;
				}
				else
				{
					wantedDir = v1;
					wantedOri = v2;
				}
			}
		}

		float dot = myOri * wantedDir;
		if( wantedOri == ZeroVec ) // going forward
		{
			//H4m
			if( dot > 0 )
			{
				// good enough, keep going straight
				ret = myOri * PATH_DIST + myPos;
			}
			else
			{
				// alter
				ret = wantedDir * PATH_DIST + myPos;
			}
		}
		else // going backward
		{
			//H4m
			if( dot < 0 )
			{
				// good enough, keep going back
				Parent.SetMotionOrientation( myOri );
				ret = -myOri * PATH_DIST + myPos;
			}
			else
			{
				// alter
				Parent.SetMotionOrientation( wantedOri );
				ret = wantedDir * PATH_DIST + myPos;
			}
		}
	}

	if( ret != ZeroVec )
	{
		if( !CanPath( Parent, ret ) ) ret = ZeroVec;
	}
	return ret;
}

Share this post


Link to post
Share on other sites

Hey code explorers, didn't find something yet. Sometimes mantas get stuck at hills. Send them from a to b with a high hill in between, and they will slowly "climb up". Extremenly braindead as well as Walrusses. Please as a next project, maybe we can make the Manta AI better. Not sure if it will be possible.

Share this post


Link to post
Share on other sites

They need 10x more horsepower, then they just fly over the hills :p

Share this post


Link to post
Share on other sites

When I try to use the bat file get:

'python' is not recognized as an internal or external command,

operable program or batch file.

Press any key to continue . . .

Share this post


Link to post
Share on other sites
Hey code explorers, didn't find something yet. Sometimes mantas get stuck at hills. Send them from a to b with a high hill in between, and they will slowly "climb up". Extremenly braindead as well as Walrusses. Please as a next project, maybe we can make the Manta AI better. Not sure if it will be possible.

Just did this with the grip mod. See walrus AI mod for details.

When I try to use the bat file get:

It's not finding python unless you set it up to find it, this is what I did.

Install Python 2.7

extract tools to a folder

Edit the 2 batch files so that ..\ is at the front of each python command like so

"..\python "%~dp0\pac1.py" extract %1"

copy the mod folder to inside the python folder..

C:\python27\cctools (or whatever)

then drag and drop files that needed processing onto the batch files.

Share this post


Link to post
Share on other sites

[Fix dumb Manta AI making Mantas get stuck on hills instead of flying higher]

Just did this with the grip mod. See walrus AI mod for details.

To be honest, I do not find any information there that you have touched Manta AI.

Share this post


Link to post
Share on other sites
[Fix dumb Manta AI making Mantas get stuck on hills instead of flying higher]

To be honest, I do not find any information there that you have touched Manta AI.

Doh my bad, I misread walruses (I've got walruses on the brain lately)

Manta's, not sure yet. Will look into it.

Share this post


Link to post
Share on other sites

Odd, It wont work when I drag the .cc file from another folder, but when I put the .cc file in the python27 folder, it works...

Share this post


Link to post
Share on other sites

Do the tools enable us to change the colour and width of the laser weapons?

Share this post


Link to post
Share on other sites

I stripped the GUTS-API stub from the repo, it was leading me nowhere. Development on CarrierTools will be dormant until a proper patch is out.

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  

×