Jump to content

OpenJK wiki ICARUS-Scripting potential corrections (or misunderstandings on my part!)


SimonP

Recommended Posts

Hi,

I've been reading about ICARUS scripting on the wiki, I realise I could probably "correct" these things that I think are mistakes myself (as it's a wiki), but thought I should actually confirm I'd not misunderstood what I'm reading. Perhaps this would be better as a GitHub Issue, dunno.

FWIW it is wonderful to have such nice documentation, so my thanks to the authors and my comments here are simply to make it better/clarify if I've misunderstood 🙂

 

Anyway, onwards:

The Wiki page in question is here: https://github.com/JACoders/OpenJK/wiki/ICARUS-Scripting

The things I've spotted are:

 

In this section (https://github.com/JACoders/OpenJK/wiki/ICARUS-Scripting#brush-manipulation-commands) it says "This code will put the affected entity on a constant motion between (0,0,0) and (0,0,100), each move taking 2 seconds, with 3 second stops.", however it looks like it actually has 5s (5000ms stops). The same is true for the example code above and below the quoted text.

 

In this section (https://github.com/JACoders/OpenJK/wiki/ICARUS-Scripting#set-command), the Tasks in the two examples are declared inside the Loop( -1 ) loops, which it's stated earlier in the document (https://github.com/JACoders/OpenJK/wiki/ICARUS-Scripting#tasks) is not what one should do, as you only need to declare a task once. I also think these examples are probably non-functional as there's no dowait() to actually run the tasks - i.e. tasks should be declared outside the Loop(){ } and have the dowait() commands within.

 

Link to comment
2 hours ago, SimonP said:

In this section (https://github.com/JACoders/OpenJK/wiki/ICARUS-Scripting#brush-manipulation-commands) it says "This code will put the affected entity on a constant motion between (0,0,0) and (0,0,100), each move taking 2 seconds, with 3 second stops.", however it looks like it actually has 5s (5000ms stops). The same is true for the example code above and below the quoted text.

All commands are send off immediately one after the other - wait commands make the script wait.

So if you instruct a brush to move for 2 seconds, and have a 5 sec wait command after it, the brush starts moving as soon as the wait starts counting. So the wait arrives at 2s when the brush reaches final position, leaving you off with 3 more seconds of wait till the next brush movement is send off with another wait.

Try to figure out what actually is executed at which point and scope. For example if you define tasks in an affect scope that include waits, the scope in the affect block will regard them, but any command executed after the affect block on the same level of hierarchy will just immediately fire off, as soon as the affect block before has been sent off as instructions. If you need for the script to wait, you'll either have to carefully time everything using wait commands in the correct scopes, or you use if blocks and parms / variables to check for completion or check out the signal / waitsignal mechanic.

On a sidenote, if you want to move entities in a linear fashion with ICARUS, instead of smoothed out / accellerated / decellerated - you have to give them a key/value combination on the enitiy in Radiant/BSP:


linear
1

This is not sth you can set with ICARUS, it has to be compiled into the map / changed via entity modding / patching.

 

To understand when wait does actually wait:

//(BHVD)

affect ( "some_mover_entity", /*@AFFECT_TYPE*/ FLUSH )
{

	task ( "DEFAULT" )
	{
		move ( < 0.000 0.000 128.000 >, < 0.000 0.000 45.000 >, 4000.000 );
	}

	dowait ( "DEFAULT" );
	sound ( /*@CHANNELS*/ CHAN_AUTO, "sound/movers/sec_panel_pass.wav" );
}

The sound will play as soon as the mover starts moving - the task does not include a wait command and will report as completed to the "dowait" as soon as the move command is send off and the brush starts moving.

If you wanted the task to take as long as the brush moves, you must include a wait inside the task:

 

//(BHVD)
rem ( "comment" );

affect ( "some_mover_entity", /*@AFFECT_TYPE*/ FLUSH )
{

	task ( "DEFAULT" )
	{
		move ( < 0.000 0.000 128.000 >, < 0.000 0.000 45.000 >, 4000.000 );
		wait ( 4000.000 );
	}

	dowait ( "DEFAULT" );
	sound ( /*@CHANNELS*/ CHAN_AUTO, "sound/movers/sec_panel_pass.wav" );
}

 

On the other example - the one about tasks and loops - this really looks like a mistake to me. I think what they intended to convey was to define a task that uses parms exclusively outside the loop once, and call it inside the loop as dowait or do instruction.

Using parms / variables can help you write better universal scripts that can be reused on many entites throughout a level. But for basic stuff it's not really relevant too much.

What are you cooking up though? 🙂

Link to comment
2 hours ago, mjt said:

All commands are send off immediately one after the other - wait commands make the script wait.

So if you instruct a brush to move for 2 seconds, and have a 5 sec wait command after it, the brush starts moving as soon as the wait starts counting. So the wait arrives at 2s when the brush reaches final position, leaving you off with 3 more seconds of wait till the next brush movement is send off with another wait.

Thanks for the clarification, that makes sense now, I wrongly assumed the commands were blocking.

 

2 hours ago, mjt said:

On the other example - the one about tasks and loops - this really looks like a mistake to me. I think what they intended to convey was to define a task that uses parms exclusively outside the loop once, and call it inside the loop as dowait or do instruction.

Using parms / variables can help you write better universal scripts that can be reused on many entites throughout a level. But for basic stuff it's not really relevant too much.


What are you cooking up though? 🙂

Yes, I understood the reasoning behind the use of the entity param variables to make generic scripts - nice to see such depth of explanation in the docs 🙂

Re what I'm doing - pondering procedural level generation (with a Star Wars theme of course). I was actually looking to see whether there were e.g. Python hooks into GtkRadiant (as I thought that generating archetypal "buildings" might be a good first goal) but then got sidetracked reading the scripting docs, which is fine, I need to understand how scripting refers to the objects/entities even if I expect to initially just copy the logic from existing scripts in the game levels/SDK.

Link to comment

Remember the random mission generator from SoF2? You'll find parts of that still in the code in JKA I think, but it's not in a usable state. For all I know neither GTK Radiant nor NetRadiant Custom provide any phython hooks. Your best bet for that would probably be blender, combined with q3map2, the io_map_export plugin - that way you can do level geometry instancing from assets, and export it all to brushwork for collisions and use .fbx as a modelformat for your actual drawsurfaces - could set up things in python or geometry nodes to procedurally assemble the assets to a level then.

I'm looking to do the same for a remake of a KotoR level right now.

 

I'll leave you a few links wrt level generation / modification that I frequently use for this:

https://github.com/c-d-a/io_export_qmap / https://github.com/andyp123/blender_vhacd / https://github.com/kmammou/v-hacd

https://github.com/SomaZ/Blender_BSP_Importer/tree/FAKK_EF2_Support (Has a more up to date WIP build floating around Discord)

https://garux.github.io/NRC/ / https://github.com/Garux/netradiant-custom/releases

36 minutes ago, SimonP said:

I need to understand how scripting refers to the objects/entities even if I expect to initially just copy the logic from existing scripts in the game levels/SDK.

Entities like trigger_* target_scriptrunner, NPCs, NPC_Spawner / NPC_Vehicle and all the func_entities (bare func_group), potentially the normal point based entities like target_ as well, can have a spawnscript / usescript setup on the entity in Radiant - this script runs on spawn/use on the entitiy itself (exception being target_scriptrunner which can run it on the entity that triggered it)

From that scope you have access to the parm1 to parm16 fields of the entity and can control it with all the ICARUS commands supported by this entity. Some commands however will not execute without an affect block directing them. In that case you should check out here: https://jediacademy.fandom.com/wiki/Icarus_Scripting:_basics#script_targetname

You can use this script_targetname / targetname stuff to affect other entities and run scripts on them from another script. I am not sure if defined variables in scripts are inherited down anything that is started from its scope or if it's accessible from two different running scripts. Something to test eventually.

SimonP likes this
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...