Quantcast

Jump to content

» «
Photo

[Intro] How To Fix Missions with Variables Corrupted by Bad Cleo Scripts

2 replies to this topic
OrionSR
  • OrionSR

    Chain Game Development Team

  • Feroci
  • Joined: 23 May 2007
  • None
  • Helpfulness Award [GTA & Modding]

#1

Posted 11 September 2017 - 09:57 PM Edited by OrionSR, 11 September 2017 - 10:08 PM.

Introduction: (San Andreas PC specific, but the same general principles apply to GTA3 era games)
 
This question (paraphrased) came up in PM and I wanted to move the discussion to the open forums for others to use as reference. This is intended as an introductions to saved data and game scripts, and I won't answer the current question right away.
 
This topic will cover the basics of getting started with decompiling the main script file (main.scm) using Sanny Builder 3 and figuring out what the mess of bizarre codes might mean.
 
SASE 1.00 will be used to view and modify save information. This isn't exactly a perfect editing tool, but it's a powerful save viewer and much easier to use than a hex editor or learning to write custom cleo scripts.
 
I'll fill in the blanks later if necessary but I'll trust that readers won't have too much trouble searching for official links to Sanny or finding specific files in their data folders.
 
Decompiling with Sanny
 
If you are working with a decompiled main.scm in Sanny you run the risk of accidentally hitting Compile and Copy or otherwise overwriting your original main.scm, so make a backup, and zip up your backup for safe keeping. script.img is paired with main.scm, so keep them together in the same archives. Then make a another copy of your backup folder in your user files and decompile those.
 
Rename your decompiled main scripts to keep track of their purpose and decompile settings. unmoddified.GTASA.v1.default.main.scm, for whatever you get out of the box.
 
Decompiled scripts will look much different depending on the settings. It won't make much sense if my examples look different than yours so I'll cover some specific settings. However, I've made many adjustments to Sanny over the years and sometimes lose track of why I set what. Hopefully I haven't mess things up too much.
 
Debug Options - gear icon on right of menu bar

  • ON = CONSTANT_INDEXES (don't know why)
  • all others off

Edit Mode - game icon in lower right corners

  • use GTA SA mode - this is the format everyone has been using for many years
  • GTA SA SCR - original Rockstar command names and format (not always compatible)
  • SA Mobile - mobile versions have different commands and variable names

Tools > Options (F10)

  • General
    • off = Always overwrite output file
    • on = Show progress
    • on = Show report
    • on = Show warning
    • off = Manual IMG opening
    • on = Quick game loading
       
    • on = Conditions check
    • on = Ranges check
    • on = Write opcodes (this will force the display of all opcodes)
    • on = Insert original mission names
    • off = Add extra info to SCM
  • Formats
    • use Thread+Local Offset (most common setting)
    • off = hex offsets
    • on = Custom label names (MS_Riot_Missions)
    • on = Custom variable names ($Riot_Total_Passed_Missions)
    • off = Custom array names
    • Case conversion: As is

The specific issue is that the Riot mission won't start even though all the mission requirements have been unlocked. First I want to show a few examples from the RIOT sniffer thread. This is the thread that is supposed to loop in memory and start the RIOT1 mission when CJ is inside the marker. But before I show the full snippet I want to show a few examples of how the code vary depending on the setting.
 
Decompile main.scm using the settings above, then make the changes below:

:MS_Riot_Missions
03A4: name_thread 'RIOT' 

:RIOT_11
0001: wait $DEFAULT_WAIT_TIME ms 
00D6: if 
0038:   $Riot_Total_Passed_Missions == 5 
004D: jump_if_false @RIOT_36 
004E: end_thread 

Write Opcodes OFF (close to default, I think)

:MS_Riot_Missions
thread 'RIOT' 

:RIOT_11
wait $DEFAULT_WAIT_TIME 
if 
  $Riot_Total_Passed_Missions == 5 
jf @RIOT_36 
end_thread 

Hex Offsets, Custom label names OFF, UPPER case conversion (upper might be default)

:RIOT
thread 'RIOT' 

:RIOT_B
wait $DEFAULT_WAIT_TIME 
if 
  $RIOT_TOTAL_PASSED_MISSIONS == 5 
jf @RIOT_24 
end_thread 

Thread+Global Offset, Custom variable names OFF, lower case conversion

:riot_12257
thread 'RIOT' 

:riot_12262
wait $14 
if 
  $629 == 5 
jf @riot_1227B 
end_thread 

Reset to the suggested settings at the top. 
Change to the GTA SA SCR edit mode.

:MS_Riot_Missions
03A4: script_name 'RIOT' 

:RIOT_11
0001: wait $DEFAULT_WAIT_TIME 
00D6: if 
0038:   $Riot_Total_Passed_Missions == 5 
004D: goto_if_false @RIOT_36 
004E: terminate_this_script 

Up until we started SCR mode, Sanny would work with any of the format setting. Sanny can figure out which opcodes belong where during compile. An import difference in SCR mode, besides it not being setup to find vehicles.ide, is that SCR uses a handle first format which makes these codes incompatible. The text changes don't matter, but the change in the sequence of the variables ($7997) makes a huge difference.

00A5: $7997 = create_car #CAMPER at 1242.9 -805.4 82.9     // GTA SA
00A5: create_car 483 at 1242.9 -805.4 82.9 store_to $7997  // GTA SA SCR
  • Seemann and HalfOfAKebab like this

OrionSR
  • OrionSR

    Chain Game Development Team

  • Feroci
  • Joined: 23 May 2007
  • None
  • Helpfulness Award [GTA & Modding]

#2

Posted 11 September 2017 - 10:10 PM Edited by OrionSR, 11 September 2017 - 11:04 PM.

Back to the main question, why doesn't the riot mission start? If you know the answer please keep it to yourself for a while.

 

This this the start of the RIOT sniffer thread, and the part that launches the Riot mission. Similar segments follow for the rest of the missions in this final strand. The last line is where the mission is started.

:MS_Riot_Missions
03A4: name_thread 'RIOT' 

:RIOT_11
0001: wait $DEFAULT_WAIT_TIME ms 
00D6: if 
0038:   $Riot_Total_Passed_Missions == 5 
004D: jump_if_false @RIOT_36 
004E: end_thread 

:RIOT_36
00D6: if 
0256:   player $PLAYER_CHAR defined 
004D: jump_if_false @RIOT_673 
00D6: if 
0038:   $ONMISSION == 0 
004D: jump_if_false @RIOT_673 
00D6: if 
00FF:   actor $PLAYER_ACTOR sphere 0 in_sphere $X_Madd_Dogg_Crib $Y_Madd_Dogg_Crib $Z_Madd_Dogg_Crib radius 1.2 1.2 2.0 on_foot 
004D: jump_if_false @RIOT_180 
00D6: if 
03EE:   player $PLAYER_CHAR controllable 
004D: jump_if_false @RIOT_180 
00D6: if 
0038:   $Riot_Total_Passed_Missions == 0 
004D: jump_if_false @RIOT_180 
0004: $ONMISSION = 1 
00BA: show_text_styled GXT 'RIOT_1' time 1000 style 2  // Riot
0050: gosub @sub_CJ_goto_Madd_Doggs_Crib 
0417: start_mission 108  // Riot

But how did I find this segment? Near the beginning of script is a definition list for mission start address labels. A search for "riot" found this.

DEFINE MISSION 108 AT @RIOT1           // Riot
DEFINE MISSION 109 AT @RIOT2           // Los Desperados
DEFINE MISSION 110 AT @FINALEA         // End Of The Line (1)
DEFINE MISSION 111 AT @FINALEB         // End Of The Line (2)
DEFINE MISSION 112 AT @FINALEC         // End Of The Line (3)

A search for "mission 108" brought me to the end of the segment above.

 

To clarify, RIOT is the sniffer thread that controls RIOT1 - Riot, RIOT2 - Los Desperados, and what could be referred to as Riot 3, 4 and 5, the End of the Line missions. When all 5 are completed ($Riot_Total_Passed_Missions == 5) the RIOT thread should end. 

 

So what's gone wrong in this save

 

Generally, problems with corruption to global variables is caused by cleo scripts that use custom global variables. These undeclared variables usually get assigned to the first unnamed variable available. Sometimes this doesn't matter, other times it can break missions. But all of the global variables in this segment are named. I've seen problems with bad cleo scripts converted to Android stomping on the default wait time, but PC is probably immune from issues.

 

I looked up the values of the globals anyway. Nothing is wrong. If you check the script that you decompiled without the custom variable names you can get the global numbers for the global names. $409 for $ONMISSION, for example. The Script tab of SASE shows the Global Variable numbers in the left panel. The interfaces allows you to set the number format to Hex, Integer, or Float (numbers with decimal values) since each global may hold a different type of data - switch as needed.

 

Again, what's gone wrong? SASE's center Script panel has a list of running threads. And RIOT isn't on it. A better question then, is why isn't the riot thread running when all requirements have been met. What is supposed launch the riot thread?

 

Thread are launched differently than missions. And this thread is a bit different because it has a custom label name, :MS_Riot_Missions. The colon indicates it's a label to be used by a jump @ address. So search for something that is using a launch code with a @MS_Riot_Missions label address.

 

Is This It? - hench the clarification of Sanny settings

//-------------Mission 108---------------
// Originally: Riot

:RIOT1
03A4: name_thread 'RIOT1' 
0050: gosub @RIOT1_47 
00D6: if 
0112:   wasted_or_busted // mission only 
004D: jump_if_false @RIOT1_38 
0050: gosub @RIOT1_2979 

:RIOT1_38
0050: gosub @RIOT1_3082 
004E: end_thread 

Nope, that's the actual mission, and has labels that start with RIOT1. It comes later in the chain. Something should start RIOT; RIOT should start RIOT1, but what is the something not launching RIOT?

  • Seemann likes this

OrionSR
  • OrionSR

    Chain Game Development Team

  • Feroci
  • Joined: 23 May 2007
  • None
  • Helpfulness Award [GTA & Modding]

#3

Posted 11 September 2017 - 11:42 PM Edited by OrionSR, 12 September 2017 - 12:00 AM.

Coding Language Clues

 

Learning a new language can be confusing. It's perfectly normal for people to be overwhelmed with all the new information. It will start to make sense with practice. Here are a few clues about the formats and protocols.

// A comment line. These are usually comments but may also be disabled codes in a working script.
/* Commented segment */ common C style format
{
  Sanny will also take comments between brackets
}

$ONMISSION, $5653 - variable preceded by a $ indicate a global variable either by name or by number.

Global variables are saved and accessible by any thread or mission. This is how the game remembers stuff associated with the scripts.

 

@RIOT1_222 - the @ symbol in the front indicates a :label address.

[email protected] - a @ symbol at the end is a local variable.

 

Local variables are limited to 32 in threads,1022? in missions, and can be remembered by running threads, but locals in missions are usually lost when the mission ends. 

 

:StartMyLoop - Labels start with a colon and can be called by @StartMyLoop style commands. With global offsets, the numbers describe the exact byte location within the script. The labels don't actually exist in the script. Sanny just makes those up when it finds an address. That address won't be the same if you make a change and recompile. Sanny doesn't care. It'll just put in the new address as it compiles. Labels can be anything, Sanny just organizes them based on the rules set.

0001: wait $DEFAULT_WAIT_TIME ms
wait [email protected] // first set [email protected] = 250
0001: pickyournose 250 times
0001: 250 

If there is no opcode Sanny can figure it out for common commands. And if you use an opcode the text doesn't matter as long as you don't chose a keyword. You can change it to suit your needs or omit it entirely.

00D6: if 
0038:   $Riot_Total_Passed_Missions == 5 
004D: jump_if_false @RIOT_36 
004E: end_thread 

Conditional commands are indented two spaces by convention. These are the types of checks that can be made for IF commands. In this sequence the game will check IF all of the riot strand missions are complete ( $Riot_Total_Passed_Missions == 5). If this statement if true because the value is exactly (==) 5 then it will end the thread. Otherwise, (jump_if_false) it will continue on with whatever exists at label #RIOT_36.

 

== is used as a conditional check. = is used to make an assignment.

0004: $ONMISSION = 1 

Integers are indicated as numbers without a decimal. Floating point values are indicated with a decimal point and trailing 0 if necessary. Each type of value has it's own type of operator command. [email protected] = 1 and [email protected] = 1.0 will need different commands to work properly, Sanny can usually figure it out. += Add, -= subtract. But the primitive language doesn't inherently do things like [email protected] = 1 + 4. It needs to be broken down to [email protected] = 1 and then [email protected] += 4. Something like 1.0 + 4 gets even weirder.

 

It takes a bit of time to get familiar with the odd rules of a basic script language. That's why I like to start with the primitive opcode format. 

 

#KEYCARD - # indicates a constant. These can be defined in SCM scripts but Sanny can determine the ID numbers associated with many pickups, vehicles and other items, which makes the script easier to read and write. Sanny knows the values associated with these keywords and will apply them at compile time. Numbers can be hard coded, or use constant or variables.

  • Seemann likes this




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users