Content: Slate Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate Marble
Background: Slate Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate Marble
Pattern: Blank Waves Notes Sharp Wood Rockface Leather Honey Vertical Triangles
Welcome to Xbox Chaos: Modding Evolved

Register now to gain access to all of our features. Once registered and logged in, you will be able to contribute to this site by submitting your own content or replying to existing content. You'll be able to customize your profile, receive reputation points as a reward for submitting content, while also communicating with other members via your own private inbox, plus much more! This message will be removed once you have signed in.

Akarias

.MAP
Script Creation & Editing - A terrible guide please ignore.

10 posts in this topic

Shits out of date and some parts are flat out wrong, you have been warned.

Quote

 

Please refer to: https://gist.github.com/Akarias/1155c1b651f8db4e58957071223895d6

 

Most of this is outdated

 

Okay we will start but discussing the layout of a basic script and some of the quirks with assembly when it comes to previewing scripts.

All of our work is done under SCNR in the SCRIPTS, SCRIPTS EXPRESSIONS blocks. If we need to preview all of our scripts, just click on the "Scripts" tab in assembly and it will do its best to show you a semi-accurate representation of what is actually going on with them.

EG:

cwnM7IT.png Tsavo Highway, gutted of its scripts and replaced with my own.

Here we can see a that there is a script that runs on startup and does nothing else, then we also have a command script. Command scripts are scripts that are ran on AI squads and allow you to do things such as giving orders to AI and other effects like controlling the speed of a vehicle being piloted by an AI. We will cover commands scripts in greater detail later, for now we will focus on general scripts.

Open up that has the player_count script, it is in most maps but not all. I'd recommend a multiplayer map as they have far less script data and are easier to navigate. We are going to investigate this script and work out what it is actually made of in terms of script expressions. Something to also note here is that the assembly script decompiler is a bit quirky and that was you read in the decompiler is a lot of the time not the full story. A perfect example is the phantom_show_1 script in the example above, there is actually a begin expression there that is not show. This can be confusing when trying to find where you are in the expressions list as things do not match up visually. Overtime to you begin to remember OPCODES based on their numbers and tell when assembly is having a bit of a moment. 

Hold up cowboy time for a boring bit.

Lets talk about these "expressions" I keep rambling on about. Expressions are the raw data that is read by the games scripting engine, its the reason why we cant just go and type nice big scripts and hit save/compile like we can in Halo CE. In HEK (Halo Editing Kit, for Halo CE) we can use sapien to compile our text based script into lovely expressions that are then stored in the .map file when compiled. The only current way of making our scripts is to manually create these expressions from scratch or by altering existing ones. 

Lets take a look at the player_count script and see what expressions it uses:

7LUHawh.png 6r8PCSB.png <-- as per the decompiler

First we find our desired script using the drop down box, then we follow its Root Expression Index. The "REI" is where the script begins, this first expression does a few things. It states what type of script it actually is, such as a startup script that does exactly what you think it does, it runs once when the map is start or a new round is started. It also states the return value of the script, see "Value Type" to find out. So in theory if we ran this script from within another script it would return the number of players within the game as a number, specifically a short. We could use this for say, making sure we spawn at least one vehicle per player for a race map or adjust the amount of AI that spawn. You get the idea.

You can ignore salts for the most part, they just add more work for us if I'm honest but can also be useful for double checking you are in the right place. Its up to you if you want to use them.

So we have found the start of the script, now here is where things can be bit confusing. Expression type does what it says on the tin, there are different types of expressions. Most of the time you will be only using two of them, they are:

  • Expression Type 8 "GROUP" - States anything inside of it as a group, so a open parentheses and close parentheses. Value type will be the return type of that group, set as VOID if nothing is returned.
  • Expression Type 9 "FUNCTION" - The meat and potatoes, this will actually do stuff! Uses OPCODE to set the function, I have yet to find a situation where values are used for functions at all. (Correct if wrong)

It's important to understand these greatly, so please don't rush through all of this massive post then complain about stuff not working. Let's talk about Opcodes, it's short for operation code and is what we use to define a function when expression type 9 is used. You can find every single Opcode for the scripting engine in your H3_Scripting.xml. An important note is that opcodes are in decimal format inside assembly, you will need to convert them back to hex and vice-versa when looking through your opcodes xml file.

Back to inspecting our script. Now something that will stand out is that the NEI for our very first expression is set to 65535? (FF) This is the first part where people stumble, when we use a expression type of 8 the NEI is actually the next line in the script, and instead the expression data is used to define the next expression on our line. Value 00 (LSB) is actually used to define the next expression on the line but ONLY when the value type is 8. So to find our next expression we follow Value 00 (LSB) to find the next expression on our line. If the script engine lands at 65535 it will run any expressions on that line as defined by Value 00 (LSB) and then it will end. The script is finished.

The only time this is not correct is at the very start of a script, if you are using a begin you will need to set the second expression to function type 9 and then use NEI to point to the next type 8 expression. Confusing but you will see what I mean.

Time for a double whammy, when you are looking a bungie made script in a campaign map the extra data at the bottom that you see is not strictly true at first glance. This is due to the way assembly reads this data as told to by our scnr.xml plugin. You have to right click view value as on Value 00 (LSB) in order to find the correct index to go to. If all else fails generally speaking a expression type of 8 will have is accompanying expression type of 9 right after it in chronological order. By the way as far as I can tell the extra 00 00 at the end of the expression 0x18 length is completely unused, it is normally set as invisible but I'd recommend editing your plugin so you can see it. Helps for getting offsets when copying scripts from one map to another. (Ill leave a link to my plugin at the bottom of this post. LAZY BOIS)

Going back to op codes, when an expression has a type of 8 it doesn't necessarily need to have a opcode of any kind, but for the sake of making your expressions easier to read please make them the same as your follow up expression type 9 function opcodes as bungie's compiler seems to have done this (makes working out their scripts sooo much easier). So the above expression has an opcode of 0. Thats the opcode for "begin" so we can assume by default that the type 9 for the next expression has the same op code and thus this has gotta be the start of the script.(Again, useful for trawling through expressions and finding the start.) Please refer to a Halo CE or Halo 2 scripting guide for what this actually does, and yes its not required but just do it. 

Next is the "String Address", this value represents a decimal offset that the engine will look into the string table and in most cases is not needed. Even for functions that have quotes in them such as #cs_go_to "path/p0" 1# they don't actually rely on the string to work, instead they use the extra data from values 03 to 00. (In the case of cs_go_to it uses value 02 as the point set index and 00 as the point index.) I could change the string to anything and it would still do the same thing. The only time they really matter is on some specific functions such as #print "My string"#, the function will actually use the data from the string table in this case and ignore any extra values. This is on a function by function basis.

So having strings is nice and all but does not really matter for most things you are doing, unless of course they are required. Don't waste time entering strings for every single freekin' expression, that's how people go insane. :v

Okay with this all in mind, lets go back to looking at our player_count script and I'll show you the layout of the expressions in a more visual format:

Quote

avI9RF0.jpg

Fantastic, you can now navigate expressions according to the script itself and hopefully will be able to see any "invisible" expressions that assembly does not show.

 

Let's have a go at writing our first expressions, the classic low-gravity script.

  

Quote

Our target is to have the following in our maps scripts.

qPpDkLV.png This will half the gravity on the entire map.

Open up a empty map, navigate to scnr and goto the scripts area. We will use shrine.map in this example.

On shrine.map an existing script is used to setup the auto-turrets (Guardians). Make sure to use empty expressions or you will have a crash!

Go down to scripts and expand the block by 1 by hitting the + sign at the right side:

V8R7Pf8.png

Give it a name, set its script type to start up. This means the script will be ran every time the map is loaded, or a new round is started in MP. Return type as void, we are not returning anything. 

Set its REI to something that is not being used, I will use 50. Hit Save.

b0dMVpr.png

Now if you were to open shrine.hsc from the scripts area, assembly would crash as the REI has no valid expression. Let's start by creating our begin group & begin function.

Begin uses opcode 0.

Set the opcode to 0, value type to void, expression type 8 for our group, string address to 0. 0 out all of the invalid data from value 03 down to line number (or the invisible, if enabled).

Set line number to something ever increasing, assembly will rely on it for displaying the script. it will still work without it just assembly wont be able to display it. 

Set Value 00 to our next expression, ill be using 51. Hit save, change indexes to another script then back so the values all update. Correct if anything unexpected also changed. 

vWdr5Pb.png

Next we create our begin function on index 51.

lnGSlAk.png

Just to make sure everything is working correctly so far, set the NEI for our begin function to 65535. Open shrine.hsc and it should show up.

kCTb8th.png  <--- Whats going on?

Our script is working just fine, we know its there just assembly is not displaying it. This is due to incorrect line numbers, set them to 1 2 3 etc. They dont need to be correct, just to exist in our expressions and assembly should have no issues. After checking, set it back to our next expression.

wj1SaDK.png physics_set_gravity uses opcode 150 and requires a real. (As per our H3_Scripting.xml

Now you don't have to create a group if the script is just going to have one function but it's best practice to do so, create a new group with return type of void. NEI set to 65535, there are no more new lines in this script. Set V00 to where our physics_set_gravity will be.

LNeMp6n.png Our group, follow it up with our function.

HUoJeFE.png Our function, phys_set_gravity requires a real. So lets make our next expression a real.

ZAcbNKp.png Set your opcode to real, change value type to real, expression type to 9. Set NEI to 65535, this is the last expression and finally set our value for the real to 0.5.

VvtuPse.png

And we are done, go check your script and test it!

5JD6Mam.png

 

Good stuff, you can now write a basic script. All you really need to do now is read some example scripts or the single player scripts for examples on how to write certain values and expressions.

Below will be some examples of logic and detecting player input.  

Quote

Work in progress

-Sorry for not having any of this written in months, I've been busy with life etc. 24th July 18.

 

 

 

Edited by Akarias
Forgot this existed.
blackdimund, Rob and ImDatNigga like this

Share this post


Link to post
Share on other sites

Reasons.

 

Some info is this is flat out wrong and over complicates things. Nuking into a quote with fair warning, forgot this post even existed.

Edited by Akarias

Share this post


Link to post
Share on other sites
20 hours ago, Akarias said:

Reasons.

 

Some info is this is flat out wrong and over complicates things. Nuking into a quote with fair warning, forgot this post even existed.

Gotcha. I know it's a tall order and you don't have to do it or anything, but I'd kinda like to see an updated tutorial on this subject. I'm trying to learn how to script in Halo: Reach, but I don't really know what I'm doing lol. I'm editing the Firefight maps for Halo Reach: Evolved, and one of the things I'd like to do is have Marine allies spawn on each map that respawn after each round is completed. 

Share this post


Link to post
Share on other sites

Take a look at the "survival_mode*" opcodes, there are tons of functions for dealing with firefight. 

Quote

    Line 4281:         <function opcode="0x47E" name="survival_mode_respawn_dead_players" returnType="void" />
    Line 4282:         <function opcode="0x47F" name="survival_mode_lives_get" returnType="long" argc="1">
    Line 4285:         <function opcode="0x480" name="survival_mode_lives_set" returnType="void" argc="2">
    Line 4291:         <function opcode="0x483" name="survival_mode_set_get" returnType="short" />
    Line 4292:         <function opcode="0x484" name="survival_mode_round_get" returnType="short" />
    Line 4293:         <function opcode="0x485" name="survival_mode_rounds_per_set_get?" returnType="short" />
    Line 4294:         <function opcode="0x486" name="survival_mode_waves_per_round_get?" returnType="short" />
    Line 4295:         <function opcode="0x487" name="survival_mode_wave_get" returnType="short" />
    Line 4296:         <function opcode="0x488" name="survival_mode_set_multiplier_get" returnType="real" />
    Line 4297:         <function opcode="0x489" name="survival_mode_set_multiplier_set" returnType="void" argc="1">
    Line 4300:         <function opcode="0x48A" name="survival_mode_round_multiplier_get?" returnType="real" />
    Line 4301:         <function opcode="0x48B" name="survival_mode_round_multiplier_set?" returnType="void" argc="1">
    Line 4304:         <function opcode="0x48C" name="survival_mode_get_wave_squad" returnType="long" />
    Line 4305:         <function opcode="0x48D" name="survival_mode_current_wave_is_initial" returnType="boolean" />
    Line 4306:         <function opcode="0x48E" name="survival_mode_current_wave_is_boss" returnType="boolean" />
    Line 4307:         <function opcode="0x48F" name="survival_mode_is_?" returnType="boolean" />
    Line 4308:         <function opcode="0x490" name="survival_mode_is_?" returnType="boolean" />
    Line 4309:         <function opcode="0x491" name="survival_mode_begin_new_set" returnType="void" />
    Line 4310:         <function opcode="0x492" name="survival_mode_begin_new_wave" returnType="void" />
    Line 4311:         <function opcode="0x493" name="survival_mode_end_set" returnType="void" />
    Line 4312:         <function opcode="0x494" name="survival_mode_end_wave" returnType="void" />
    Line 4313:         <function opcode="0x495" name="survival_mode_award_hero_medal" returnType="void" />
    Line 4314:         <function opcode="0x496" name="survival_mode_event_new?" returnType="void" argc="1">
    Line 4326:         <function opcode="0x49A" name="survival_mode_get_time_limit" returnType="long" />
    Line 4327:         <function opcode="0x49B" name="survival_mode_get_set_count" returnType="long" />
    Line 4328:         <function opcode="0x49C" name="survival_mode_get_round_count" returnType="long" />
    Line 4329:         <function opcode="0x49D" name="survival_mode_get_wave_count" returnType="long" />
    Line 4330:         <function opcode="0x49E" name="survival_mode_get_shared_team_life_count" returnType="long" />
    Line 4331:         <function opcode="0x49F" name="survival_mode_get_elite_life_count" returnType="long" />
    Line 4332:         <function opcode="0x4A0" name="survival_mode_max_lives" returnType="long" />
    Line 4333:         <function opcode="0x4A1" name="survival_mode_generator_count" returnType="long" />
    Line 4334:         <function opcode="0x4A2" name="survival_mode_bonus_lives_elite_death" returnType="long" />
    Line 4335:         <function opcode="0x4A3" name="survival_mode_scenario_extras_enable" returnType="boolean" />
    Line 4336:         <function opcode="0x4A4" name="survival_mode_weapon_drops_enable" returnType="boolean" />
    Line 4337:         <function opcode="0x4A5" name="survival_mode_ammo_crates_enable" returnType="boolean" />
    Line 4338:         <function opcode="0x4A6" name="survival_mode_generator_defend_all" returnType="boolean" />
    Line 4339:         <function opcode="0x4A7" name="survival_mode_generator_random_spawn" returnType="boolean" />
    Line 4340:         <function opcode="0x4A8" name="survival_mode_current_wave_uses_dropship" returnType="boolean" />
    Line 4341:         <function opcode="0x4A9" name="survival_mode_get_current_wave_time_limit" returnType="short" />
    Line 4345:         <function opcode="0x4AB" name="survival_mode_team_respawns_on_wave" returnType="boolean" argc="1">
    Line 4348:         <function opcode="0x4AC" name="survival_mode_sudden_death" returnType="void" argc="1">
    Line 4351:         <function opcode="0x4AD" name="survival_increment_human_score" returnType="void" argc="1">
    Line 4354:         <function opcode="0x4AE" name="survival_increment_elite_score" returnType="void" argc="1">
    Line 4357:         <function opcode="0x4AF" name="survival_mode_set_spartan_license_plate" returnType="void" argc="5">
    Line 4364:         <function opcode="0x4B0" name="survival_mode_set_elite_license_plate" returnType="void" argc="5">
    Line 4371:         <function opcode="0x4B1" name="survival_mode_team_respawns_on_wave_mode_player_count_by_team" returnType="long" argc="1">
    Line 4374:         <function opcode="0x4B2" name="survival_mode_players_by_team" returnType="object_list" argc="1">
    Line 6276:         <function opcode="0x6FE" name="ai_survival_cleanup" returnType="void" argc="3">

 

You could either make a script that checks the round for an increment and renews any previously dead squads or you could add some extra lines into the existing round scripts. 

https://docs.google.com/document/d/1pTGzMOSNCZcf2OiQauBFok1Gx-1El1UKJZx0FzXkyN8/ Reach Specific documentation iirc. Old but none the less useful.

 

Edited by Akarias

Share this post


Link to post
Share on other sites
4 hours ago, Akarias said:

Take a look at the "survival_mode*" opcodes, there are tons of functions for dealing with firefight. 

 

You could either make a script that checks the round for an increment and renews any previously dead squads or you could add some extra lines into the existing round scripts. 

https://docs.google.com/document/d/1pTGzMOSNCZcf2OiQauBFok1Gx-1El1UKJZx0FzXkyN8/ Reach Specific documentation iirc. Old but none the less useful.

 

Wow, that's my old word doc. My main intention was to document as much of Reach's script format as possible, so that a script compiler could be created in the future. I believe I never ended up releasing it. It's nice to see, that it has been shared around and is of use to some of you guys. Most of the information should still be fairly relevant nowadays.

 

Akarias likes this

Share this post


Link to post
Share on other sites
13 minutes ago, SnipeStyle said:

Wow, that's my old word doc. My main intention was to document as much of Reach's script format as possible, so that a script compiler could be created in the future. I believe I never ended up releasing it. It's nice to see, that it has been shared around and is of use to some of you guys. Most of the information should still be fairly relevant nowadays.

 

Bloody hell the devil still exists.

Your research with AMD and co was of serious help an august or two ago where I spent two weeks smashing my face into expressions and finally worked everything out.

Im pushing some changes to my assembly fork with some extra bits in it. Ive tried to write a compiler a few times but Ive never wrote a proper lexer before.

Not long after doing that I found this: https://web.archive.org/web/20180911200814/http://z9.invisionfree.com/Warriors_of_Auspex/ar/t1365.htm

I was enraged.

I spent some time with Vadam on discord, he can script now. ;)

Edited by Akarias
The Vengeful 'Vadam likes this

Share this post


Link to post
Share on other sites

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