Jump to content
Sign in to follow this  
pumpkin

Scripting language for Take On Mars

Recommended Posts

As I come from Operation Flashpoint/Arma series, I was surprise not to find a scripting language in Take On Mars (as SQS/SQF), even with very few commands. a (small) scripting language could eventualy relieve to the missing of functionnalities and/or permits to easily insert random in user's missions. Did you plane to add a scripting language (I saw a sub-forum on Steam "Scripting & Configs") ? Or may I suggest you to add one ?

Share this post


Link to post
Share on other sites

Hi, actually the scripting language in Take On Mars is extremely powerful. It is very low-level though, which can scare off potential modders, but it is very similar to C++, and actually the entire game is written in script. The scripts are packed in .pak files but can be unpacked. We'll hopefully release some videos showing how to unpack and mod the files soon.

The reason there are no SQF or SQS files is that it supports any text format. If you have a look at the example addon we released on steam workshop, you can unpack it straight from the main menu, and then you can have a look at its contents in:

C:\Program Files (x86)\Steam\userdata\YOUR STEAM ID\244030\local\addonpacks\ADDON NAME\

Here's an example of a controller script for the basic probe:

/****************************************************************************
*	PROBE CONTROLLER
***************************************************************************/
const float PRB_LANDSPEED = 64; // 2m/s
const float PRB_LANDSPEED_SPACE = 32; // 1m/s
const float PRB_HEIGHTTHRUST = 3200; // 100m
const float PRB_HEIGHTTHRUST_SPACE = 4800; // 150m
const float PRB_HEIGHTLAND = 16; // 0.5m
const float PRB_ANGPUSHMAX = 10;
const float PRB_ANGPUSHMULT = 0.5;
const float PRB_ANGPUSHLATSPEEDMAX = 800; // 2m/s


float sinInc;
int probeBody;
int part_thrusters;


void Controller_Init(Vehicle_Controller controller)
{
Vehicle_Handler vehicle = controller.ownerVehicle;

vehicle.EmergencyMode = false;

probeBody = -1;
part_thrusters = -1;
sinInc = 0;
for (int i = 0; i < vehicle.storage.parts_num; i++)
{
	if (vehicle.storage.parts_types[i] == "ProbeBody")
		probeBody = i;
	if (vehicle.storage.parts_types[i] == "Antenna" && vehicle.storage.parts_subtypes[i] == "ProbeDish")
		part_thrusters = i;
}
}


void Controller_OnSim(Vehicle_Controller controller)
{
Vehicle_Handler vehicle = controller.ownerVehicle;

bool inSpace = false;
if (vehicle.storage.owner && vehicle.storage.owner.IsSpaceLocation())
	inSpace = true;

int i2 = 0;
bool b1 = false;
bool b2 = false;
float f2 = 0;
vector v2 = ZeroVec;
vector mat1[4];
Vehicle_FX fxEnt = NULL;

if (vehicle.parts_ents[vehicle.storage.CurrentPriPart] && vehicle.storage.parts_types[vehicle.storage.CurrentPriPart] == "ProbeBody")
	b1 = true;
if (vehicle.parts_ents[vehicle.storage.CurrentSecPart] && vehicle.storage.parts_types[vehicle.storage.CurrentSecPart] == "ProbeBody")
	b2 = true;

/*float inputV = 0;
float inputH = 0;
float inputT = 0;
if (b1 && b2) {
	inputV = vehicle.input_arc_pri0;
	inputH = vehicle.input_arc_pri1;
	inputT = vehicle.input_arc_pri2;
}*/

// Increment vehicle landing time and manual landing time
if (!vehicle.storage.SystemsActivated && vehicle.storage.LandingActivated) {
	vehicle.storage.landing_totaltime += phys_FixedTick;
}

float curHeight = 0;
float curLatVel = 0;
vector curVelocity = ZeroVec;
vector curLatVelocity = ZeroVec;
sinInc += phys_FixedTick * frand(0.9, 1.1) * 360;

Vehicle_Part partBody = vehicle.parts_ents[probeBody];
if (partBody) {
	GetMatrix4(partBody, mat1);
	v2 = mat1[3];
	curVelocity = GetVelocity(partBody);
	curLatVelocity = curVelocity;
	curLatVelocity[2] = 0;
	curLatVel = VectorLength(curLatVelocity);
	curHeight = vehicle.GetSolidGroundDist(NULL, mat1[2] * -1);

	/*if (VectorLength(curVelocity) <= PRB_LANDINGVEL && manCtrlTime <= 0 && curHeight < PRB_HEIGHTLAND) {
		if (lndNoHUDSwitchTime >= PRB_NOHUDSWITCHTIME) {
			lndNoHUDSwitchTime = 0;
			partBody.GenVec2[0] = 1;
			lndCountUp += phys_FixedTick;
			if (lndCountUp > PRB_LANDINGTIME || manCtrlTime <= 0) {
				lndCountUp = 0;
				if (!vehicle.storage.SystemsActivated) {
					vehicle.storage.LandingZone = ZeroVec;
					vehicle.storage.SystemsActivated = true;
					vehicle.storage.AddLogEntry("#tkom_log_landed");
					if (g_Campaign)
						g_Campaign.VehicleLanded();
				}
			}
		}
	}*/
}

if (part_thrusters == -1)
	return;

Vehicle_Part thrusterPart = vehicle.parts_ents[part_thrusters];
// Check if part is connected to the lander
if (partBody && thrusterPart && vehicle.CheckPartConnect(part_thrusters, probeBody) && vehicle.storage.LandingActivated) {
	if (partBody.GenVec2[0] == 0) {
		float linDamp = AirFriction_Global * 1.8;
		clamp linDamp<0, 1>;
		dBodySetDamping(thrusterPart, linDamp, AirFriction_Global);
	}

	fxEnt = NULL;
	if (thrusterPart.FXIndex != -1)
		fxEnt = Vehicle_FXList[thrusterPart.FXIndex];


	float hgtThrust = PRB_HEIGHTTHRUST;
	float lndSpeed = PRB_LANDSPEED;
	if (inSpace) {
		hgtThrust = PRB_HEIGHTTHRUST_SPACE;
		lndSpeed = PRB_LANDSPEED_SPACE;
	}

	if (partBody.GenVec2[0] < 2) { // Not Landed yet
		float thrustLevel = 0;

		if (partBody.GenVec2[0] == 0 && curHeight < hgtThrust)
			partBody.GenVec2[0] = 1;
		if (partBody.GenVec2[0] == 1 && curHeight < PRB_HEIGHTLAND)
			partBody.GenVec2[0] = 2;

		// Work out thrust etc
		if (partBody.GenVec2[0] == 1 && curVelocity[2] < -lndSpeed)
			thrustLevel = 1;

		// End landing sequence if in space and travelling slowly enough
		if (inSpace && curVelocity[2] >= -lndSpeed)
			partBody.GenVec2[0] = 2;

		/**	For vehicle log:
			GenVec3[0] - if 1, logged a minor malfunction
			GenVec3[0] - if 2, logged a major malfunction
		**/
		// Now work out failure
		float malfVal = thrusterPart.GenVec3[0];
		thrusterPart.GenVec3[1] = thrusterPart.GenVec3[1] + phys_FixedTick;
		if (thrusterPart.GenVec3[1] >= 0.5 && vehicle.storage.parts_damage[part_thrusters] > 0.25) { // Only check once per 1/2 second and only if damaged
			thrusterPart.GenVec3[1] = 0;
			if (thrustLevel > 0) { // Only check if the thruster intends to thrust
				float rndChc = frand(0, 1);
				// If not jammed, % chance of jamming relative to the damage
				if (malfVal < 1 && rndChc <= vehicle.storage.parts_damage[part_thrusters] - 0.25 / 0.75)
					malfVal = 1;
				else {
					// If jammed, % chance of jamming permanently relative to the damage, and only above 0.6 damage
					if (malfVal == 1 && vehicle.storage.parts_damage[part_thrusters] > 0.6 && rndChc <= vehicle.storage.parts_damage[part_thrusters] - 0.6 / 0.4)
						malfVal = 2;
					// If jammed, % chance of unjamming relative to the damage
					if (malfVal == 1 && rndChc >= vehicle.storage.parts_damage[part_thrusters] - 0.25 / 0.75)
						malfVal = 0;
				}
				if (malfVal == 0 && thrusterPart.GenVec3[0] > 0)
					thrusterPart.GenVec3[0] = 0;
				if (malfVal == 1 && thrusterPart.GenVec3[0] == 0) {
					thrusterPart.GenVec3[0] = 1;
					vehicle.storage.AddLogEntry("#tkom_log_malfmin " + vehicle.storage.parts_title[part_thrusters] + " " + itoa(vehicle.PartSubTypeGetNum(part_thrusters, vehicle.storage.parts_subtypes[part_thrusters])));
				}
				if (malfVal == 2 && thrusterPart.GenVec3[0] != 2) {
					thrusterPart.GenVec3[0] = 2;
					vehicle.storage.AddLogEntry("#tkom_log_malfmaj " + vehicle.storage.parts_title[part_thrusters] + " " + itoa(vehicle.PartSubTypeGetNum(part_thrusters, vehicle.storage.parts_subtypes[part_thrusters])));
				}
				thrusterPart.GenVec3[0] = malfVal;
			}
		}
		if (malfVal > 0) {
			vehicle.EmergencyMode = true; // Thusters stuck
			thrusterPart.DeletePTC();
			thrusterPart.StopSnd(PART_SOUNDS_EXTCHNL1);
			thrusterPart.StopSnd(PART_SOUNDS_EXTCHNL2);
		} else {
			if (thrustLevel > 0) {
				GetMatrix4(thrusterPart, mat1);

				// Give thrust impulse
				/*v2 = VectorMatrixMultiply3(mat1, "0 0 1") * thrusterPart.GenVec[0] * phys_FixedTick * thrusterPart.GenVec2[0];
				dBodyApplyForce(thrusterPart, v2);*/
				v2 = GetVelocity(thrusterPart);
				float velScale = 1 - fsum(phys_FixedTick * 4);
				SetVelocity(thrusterPart, v2 * velScale);

				// If tilted in wrong direction, try to level out
				/*vector angs = GetAngles(thrusterPart);
				vector torque = ZeroVec;
				torque[0] = fsum(cos(angs[0] * DEG2RAD) * -angs[2]) + fsum(sin(angs[0] * DEG2RAD) * angs[1]) * PRB_ANGPUSHMULT;
				torque[1] = fsum(sin(angs[0] * DEG2RAD) * -angs[2]) + fsum(cos(angs[0] * DEG2RAD) * -angs[1]) * PRB_ANGPUSHMULT;
				for (int a = 0; a < 3; a++) {
					if (torque[a] < -PRB_ANGPUSHMAX) torque[a] = -PRB_ANGPUSHMAX;
					if (torque[a] > PRB_ANGPUSHMAX) torque[a] = PRB_ANGPUSHMAX;
				}
				dBodyApplyTorque(thrusterPart, torque);*/
				if (!inSpace)
					dBodyApplyTorque(thrusterPart, Vector(sin(sinInc * DEG2RAD) * 3 * thrusterPart.GenVec2[0], cos(sinInc * DEG2RAD) * 4 * thrusterPart.GenVec2[0], sin(sinInc * DEG2RAD) * thrusterPart.GenVec2[0]));

				if (fxEnt && fxEnt.FX_Ptc_Thruster != "")
					thrusterPart.PlayPTC(fxEnt.FX_Ptc_Thruster);
				thrusterPart.GenVec2[0] = phys_FixedTick * thrustLevel * 4 + thrusterPart.GenVec2[0];
				if (thrusterPart.GenVec2[0] > thrustLevel)
					thrusterPart.GenVec2[0] = thrustLevel;
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0] * 0.5 + 0.5, EP_VELOCITY);
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0], EP_SIZE);
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0], EP_BIRTH_RATE);
			} else {
				thrusterPart.GenVec2[0] = -phys_FixedTick * 0.75 + thrusterPart.GenVec2[0];
				if (thrusterPart.GenVec2[0] < 0)
					thrusterPart.GenVec2[0] = 0;
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0] * 0.5 + 0.5, EP_VELOCITY);
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0], EP_SIZE);
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0], EP_BIRTH_RATE);
			}
		}
	} else {
		if (thrusterPart.ptc_emit) {
			thrusterPart.GenVec2[0] = -phys_FixedTick * 1.5 + thrusterPart.GenVec2[0];
			if (thrusterPart.GenVec2[0] < 0 && GetParticleCount(thrusterPart.ptc_emit) == 0) {
				thrusterPart.GenVec2[0] = 0;
				thrusterPart.DeletePTC();
			} else {
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0] * 0.5 + 0.5, EP_VELOCITY);
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0], EP_SIZE);
				thrusterPart.LerpPTC(thrusterPart.GenVec2[0], EP_BIRTH_RATE);
			}
		}
	}
	if (thrusterPart.GenVec2[0] != 0) {
		float sndVar = sin(part_thrusters) * 0.2 + 1;
		float sndFreq = 1.2;
		if (vehicle.storage.parts_subtypes[part_thrusters] == "T_Attitude")
			sndFreq = 1.6;
		if (fxEnt && fxEnt.FX_Snd_Thruster != "")
			thrusterPart.PlaySnd(PART_SOUNDS_EXTCHNL1, fxEnt.FX_Snd_Thruster, SFX_3D, fxEnt.FX_Vol_Thruster * fabs(thrusterPart.GenVec2[0]), 0.2 * fabs(thrusterPart.GenVec2[0]) + sndFreq * sndVar);
		if (fxEnt && fxEnt.FX_Snd_Thruster_Far != "")
			thrusterPart.PlaySnd(PART_SOUNDS_EXTCHNL2, fxEnt.FX_Snd_Thruster_Far, SFX_3D, fxEnt.FX_Vol_Thruster_Far * fabs(thrusterPart.GenVec2[0]), 0.2 * fabs(thrusterPart.GenVec2[0]) + sndFreq * sndVar);
	} else {
		thrusterPart.StopSnd(PART_SOUNDS_EXTCHNL1);
		thrusterPart.StopSnd(PART_SOUNDS_EXTCHNL2);
	}
} else {
	// Delete particle effects if thruster is detached
	if (thrusterPart) {
		thrusterPart.DeletePTC();
		thrusterPart.StopSnd(PART_SOUNDS_EXTCHNL1);
		thrusterPart.StopSnd(PART_SOUNDS_EXTCHNL2);
	}
}
}
/****************************************************************************
***************************************************************************/

Share this post


Link to post
Share on other sites
Hi, actually the scripting language in Take On Mars is extremely powerful. It is very low-level though, which can scare off potential modders, but it is very similar to C++, and actually the entire game is written in script.
Happy to know a scripting language exists, even(mostly!) if "very low-level". The greatest difficulty will be to find the syntax of the language; did you plane to create any documentation or a wiki ?
The scripts are packed in .pak files but can be unpacked. We'll hopefully release some videos showing how to unpack and mod the files soon.The reason there are no SQF or SQS files is that it supports any text format. If you have a look at the example addon we released on steam workshop, you can unpack it straight from the main menu, and then you can have a look at its contents in:

C:\Program Files (x86)\Steam\userdata\YOUR STEAM ID\244030\local\addonpacks\ADDON NAME\

Got it : Addon > pack and unpack (without videos), and test it, and read some ".h" files. During the test, I'm afraid to have discovered a little bug (see feedback tracker ID 0000376: Tech-Tree blocked when unpack addon). I have a suggestion too about... well, just look at the summary : ID 0000377: presence of addon's items in tech-tree.

Share this post


Link to post
Share on other sites

I have been playing Arma 3 for a while now so of course I would ask this, will we be able to do the same things with ToM as we do with Arma 3 like mods, addon, textures, etc..

Share this post


Link to post
Share on other sites

yes, u can mod tkom etc, u even have workshop, although tkom uses different engine so expect thing to not be excatly the same

Share this post


Link to post
Share on other sites

Hi Hugo, as FeltHat mentioned, TKOM is very moddable, in fact, the entire game is written in script, and may thus be edited in its entirety. This includes the human controller, spawning of vehicles, creating/dissecting packets, etc. Take On Mars also comes with its own editing suite called Workbench, which allows the creation of terrain, particle effects, materials, and so on.

Share this post


Link to post
Share on other sites

That's too bad, all these years of scripting for Arma 2 and I think I've finally mastered that language, now I gotta learn another. It looks more like C#/Java, which is much better than C++. At least we'll finally have classes and not have to 'select' things out of arrays. I just hope we get some good documentation.

Share this post


Link to post
Share on other sites

I play TKOM in single and manned missions are really busy. I would be great if the player could print rover parts and build together to a non-science, but working rover and then program it: eg a rover check the marsdust container in the transformer machine and if it's empty, the rover can take it and bring to the dustmaker and fill it.

Just a suggestion to the future.

And yes, my idea was inspirated by Colobot. ;)

Share this post


Link to post
Share on other sites

Hello,

 

After spending a day in game I noticed the gameplay could be improved by adding user space scripts for managing rovers navigation, etc if that would become as optional part of gameplay. There are basic commands already so why not to expand on this? :)

So the long hours spent for just driving rover or similar thing could be exchanged with something else in background while that small rover moves 2km+ between two exploration sites using gamer made script in rovers 'console'. If anyone is familar with Ceebot(Colobot) old game - http://www.ceebot.com/ceebot/index-e.php it had that functionality and was really fun to play with.

 

Cheers!

Share this post


Link to post
Share on other sites

So, not to sound rude, but has anyone found the name of this scripting language?

It looks like C++/CLI. It is basically a CLI (the .NET bytecode) version of C++. It is pretty nice, though it is not _standard_ C++ (and I believe it lacks a lot of features from C++/11 and C++/14 [and nothing from C++/17]).

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  

×