Advertisement
Deji

SuperVars - Save System (SuperVars demo)

Jun 26th, 2014
358
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.07 KB | None | 0 0
  1. /////////////////////////////////////
  2. // SuperVars Example Source File
  3. /////////////////////////////////////
  4. // Language:
  5. // SCR for Sanny Builder
  6. //
  7. // Description:
  8. // This script demonstrates how to create a simple save/load
  9. // system using SuperVars and CLEO 4 opcodes (IntOperations.cleo
  10. // is also required)
  11. //
  12. // Level:
  13. // Ini-termediate
  14. //
  15. // Compiled Script Checksum (CRC-32):
  16. // 0001C445
  17. //
  18.  
  19. {$CLEO}
  20. {$VERSION 3.0.1000}
  21.  
  22. SCRIPT_NAME 'savesys'
  23.  
  24. CONST
  25. SuperVar = string // SB exploit
  26. NUM_VARS = 8 // lets have 8 normal vars
  27. NUM_SAVE_VARS = 8 // 8 saveable vars should also do..
  28.  
  29. RampageData = 0@
  30. SaveVar = 31@
  31.  
  32. // For our RampageData array:
  33. Score = RampageData // zero-index must use the same var the memory is stored in
  34. NumPedsKilled = -1@
  35. NumCarsWrecked = -2@
  36. NumCopsKilled = -3@
  37. NumGangstersKilled = -4@
  38. SaveVarPointer = -5@ // we can now store the SaveVar value here for easy access via SVar
  39.  
  40. // For our SaveVar array:
  41. HighScore = SaveVar
  42. NumTimesRampaged = -1@
  43. TotalPedsKilled = -2@
  44. TotalCarsWrecked = -3@
  45. TotalCopsKilled = -4@
  46. TotalGangstersKilled = -5@
  47. TimesFailedRampage = -6@
  48. END
  49.  
  50. VAR
  51. RampageData: array 4 of SuperVar
  52. SaveVar: array 4 of SuperVar
  53. END
  54.  
  55. // Verify SuperVars installation...
  56. IF NOT IS_BIT_SET_L 38@ 9
  57. THEN
  58. PRINTSTRING "SuperVars is not installed"
  59. TERMINATE_THIS_CUSTOM_SCRIPT
  60. ELSE
  61. PRINTSTRING "SuperVars is running"
  62. END
  63.  
  64. // Activate SuperVars for this script
  65. SET_BIT_L 38@ 8
  66.  
  67. // Get the memory space for our vars
  68. RampageData = @VARIABLE_MEMORY
  69.  
  70. // We can also use allocated memory for our SaveVar
  71. 0A90: 1@ = NUM_SAVE_VARS * 4 // make sure we have enough space (4 bytes per var)
  72. IF NOT ALLOCATE_MEMORY 1@ SaveVar
  73. THEN
  74. PRINTSTRING "ALLOCATE_MEMORY_FAILED"
  75. TERMINATE_THIS_CUSTOM_SCRIPT
  76. END
  77.  
  78. // Make sure all the SaveVar data is null
  79. FOR 1@ = 0 TO NUM_SAVE_VARS
  80. 0006: SaveVar[1@] = 0
  81. END
  82.  
  83. // Store a SaveVar pointer to an index of RampageData, just in case we have one and not the other
  84. 0085: RampageData[SaveVarPointer] = SaveVar
  85.  
  86. // The "number of times visited safehouse" stat increases each time the game is saved
  87. // In other words, if it's 0, we shouldn't attempt to load, as the game must be new
  88. GET_INT_STAT 136 2@
  89. IF 2@ > 0
  90. THEN
  91. READ_MEMORY 0xBA68A7 1 0 2@ // read the selected menu slot
  92.  
  93. // We have to pass the SVar to any functions we call that require it, which makes 0@ a good
  94. // choice for having as a global SVar, if we want to const it.
  95. CALL @Load 2 RampageData 2@
  96. END
  97.  
  98. CONST
  99. // Some more vars (we obviously cant use 0@ or 31@ as they are already used)
  100. IsRampageOn = 1@
  101. RampageStartTime = 2@
  102.  
  103. TempVal1 = 3@
  104. TempVal2 = 4@
  105. TempVal3 = 5@
  106. TempVal4 = 6@
  107. TempVal5 = 7@
  108.  
  109. ShowSaveMenuStatus = 8@
  110.  
  111. RAMPAGE_TIME = 60 // number of seconds of rampage time
  112. END
  113.  
  114. // NULL vars we used above
  115. IsRampageOn = 0
  116. RampageStartTime = 0
  117.  
  118. WHILE TRUE
  119. WAIT 0
  120.  
  121. GOSUB @CheckSaveGame
  122.  
  123. IF IS_PLAYER_PLAYING $PLAYER_CHAR
  124. THEN
  125. IF IsRampageOn == FALSE
  126. THEN
  127. IF TEST_CHEAT "RAMPAGE"
  128. THEN
  129. // display high score
  130. PRINT_BIG_FORMATTED "High Score: %d" TIME 3000 STYLE 6 INPUT SaveVar[HighScore]
  131.  
  132. WAIT 3000
  133.  
  134. // lets go on a rampage!
  135. IsRampageOn = TRUE
  136. GET_GAME_TIMER RampageStartTime
  137. RESET_NUM_OF_MODELS_KILLED_BY_PLAYER $PLAYER_CHAR
  138.  
  139. // reset rampage data
  140. 0006: RampageData[Score] = 0
  141. 0006: RampageData[NumPedsKilled] = 0
  142. 0006: RampageData[NumCarsWrecked] = 0
  143. 0006: RampageData[NumCopsKilled] = 0
  144. 0006: RampageData[NumGangstersKilled] = 0
  145.  
  146. // increase our save var stat
  147. 000A: SaveVar[NumTimesRampaged] += 1
  148. END
  149. ELSE
  150. GOSUB @Rampage
  151. END
  152. ELSE
  153. // if the player isnt playing, they probably died or got busted...
  154. IF IsRampageOn == TRUE
  155. THEN
  156. 000A: SaveVar[TimesFailedRampage] += 1
  157. IsRampageOn = FALSE
  158. END
  159. END
  160. END
  161.  
  162. TERMINATE_THIS_CUSTOM_SCRIPT
  163.  
  164. :Rampage
  165. GET_GAME_TIMER TempVal1
  166. 0062: TempVal1 -= RampageStartTime // get the time since the rampage started
  167. TempVal1 /= 1000 // convert milleseconds to seconds
  168.  
  169. IF TempVal1 < RAMPAGE_TIME // the rampage started less than 60 seconds ago?
  170. THEN
  171. TempVal2 = RAMPAGE_TIME
  172. 0062: TempVal2 -= TempVal1 // so, as TempVal1 increases, TempVal2 decreases
  173.  
  174. // Print the time we have left...
  175. PRINT_BIG_FORMATTED "Rampage Time Left: %d" TIME 1 STYLE 5 INPUT TempVal2
  176.  
  177. 0006: RampageData[Score] = 0
  178. 0006: RampageData[NumPedsKilled] = 0
  179. 0006: RampageData[NumCarsWrecked] = 0
  180. 0006: RampageData[NumCopsKilled] = 0
  181. 0006: RampageData[NumGangstersKilled] = 0
  182.  
  183. TempVal3 = 0x969A50 // we can use plain ol' memory addresses as SuperVars, remember?
  184. // 0x969A50 is an address that stores numbers of each model the
  185. // player has destroyed. in each index are two 2 byte values.
  186. // the first is the number of times player 1 has destroyed a model,
  187. // the second is the number of times player 2 has destroyed the model.
  188.  
  189. FOR TempVal1 = 0 TO 1600 STEP 2 // loop through destroyed model counter range (each index refers to a model ID)
  190.  
  191. {
  192. We can quickly access indexes of an array in the game memory using the start address
  193. as a SuperVar base.
  194.  
  195. Although SuperVars cannot read/write less than 4 bytes at a time, we can still use 2 as
  196. the size specifier to get the right index, and use CLEO 4's 'BITWISE_AND', which comes
  197. with IntOperations.cleo, to read only the first 2 bytes of the data there...
  198.  
  199. Similarly, 0xFF would mask only 1 byte. And 0xFFFFFFFF would mask all 4 bytes.
  200. The bitmask is what covers which bits to read, almost like how we use IS_BIT_SET.
  201. }
  202. BITWISE_AND TempVal3(TempVal1,2s) AND 0xFFFF TempVal2
  203.  
  204. IF TempVal2 > 0
  205. THEN
  206. 0A91: TempVal4 = TempVal1 / 2 // get actual model index
  207.  
  208. // ok, the model index in TempVal1 has been destroyed by the player since 'RESET_NUM_OF_MODELS_KILLED_BY_PLAYER'...
  209. IF TempVal4 < 300 // if the model index is under 300, it's probably a ped (see peds.ide)
  210. THEN
  211. // remember the technique to get a memory array index?
  212. // 0xA9B0C8 is an array of pointers to model information structs.
  213. TempVal5 = 0xA9B0C8
  214.  
  215. // read the struct pointer and get an offset from that pointer
  216. 0A8E: TempVal5 = TempVal5(TempVal4,4s) + 0x28
  217.  
  218. // we just got an offset of CPedModelInfo* from a ModelInfo array (CModelInfo * ModelInfo[20000])...
  219.  
  220. // now store the value at that offset. we could also use READ_MEMORY, but SuperVars is quicker
  221. // however, only memory addresses which dont have virtual protection can be accessed this way
  222. 0085: TempVal5 = TempVal5(TempVal5,4s)
  223.  
  224. // what we just did was read the default ped type of the ped model (CPedModelInfo+0x28)
  225. // now we can see what kind of ped the player killed
  226. IF TempVal5 == 6
  227. THEN
  228. // killed a cop!
  229. 005A: RampageData[NumCopsKilled] += TempVal2
  230. 000A: RampageData[Score] += 5
  231. ELSE
  232. IF AND
  233. TempVal5 >= 7
  234. TempVal5 <= 16
  235. THEN
  236. // killed a gangster!
  237. 005A: RampageData[NumGangstersKilled] += TempVal2
  238. 000A: RampageData[Score] += 2
  239. ELSE
  240. // killed some other ped...
  241. 005A: RampageData[NumPedsKilled] += TempVal2
  242. 000A: RampageData[Score] += 1
  243. END
  244. END
  245. ELSE
  246. // 400-614 is the vehicle model range
  247. IF AND
  248. TempVal4 >= 400
  249. TempVal4 < 615
  250. THEN
  251. // destroyed a vehicle
  252. 005A: RampageData[NumCarsWrecked] += TempVal2
  253. 000A: RampageData[Score] += 8
  254. END
  255. END
  256. END
  257.  
  258. END
  259.  
  260. // For debugging...
  261. PRINTNL
  262. PRINTINT2 "CARS_WRECKED" RampageData[NumCarsWrecked]
  263. PRINTINT2 "GANG_KILLED" RampageData[NumGangstersKilled]
  264. PRINTINT2 "COPS_KILLED" RampageData[NumCopsKilled]
  265. PRINTINT2 "PEDS_KILLED" RampageData[NumPedsKilled]
  266. PRINTINT2 "SCORE" RampageData[Score]
  267. ELSE
  268. // 60 seconds passed, so lets tally up the totals and end the rampage
  269.  
  270. // storing it in the save var saves the data when the game is saved
  271. 005A: SaveVar[TotalPedsKilled] += RampageData[NumPedsKilled]
  272. 005A: SaveVar[TotalCopsKilled] += RampageData[NumCopsKilled]
  273. 005A: SaveVar[TotalGangstersKilled] += RampageData[NumGangstersKilled]
  274. 005A: SaveVar[TotalCarsWrecked] += RampageData[NumCarsWrecked]
  275.  
  276. // did we beat our previously saved high score?
  277. IF 001D: RampageData[Score] > SaveVar[HighScore]
  278. THEN
  279. // High score!
  280. PRINT_BIG_FORMATTED "High Score: %d" TIME 4000 STYLE 5 INPUT RampageData[Score]
  281. 0085: SaveVar[HighScore] = RampageData[Score]
  282. ELSE
  283. PRINT_BIG_FORMATTED "Score: %d" TIME 4000 STYLE 5 INPUT RampageData[Score]
  284. END
  285.  
  286. WAIT 4000
  287. PRINT_BIG_FORMATTED "Peds Killed: %d" TIME 4000 STYLE 5 INPUT RampageData[NumPedsKilled]
  288. WAIT 4000
  289. PRINT_BIG_FORMATTED "Cops Killed: %d" TIME 4000 STYLE 5 INPUT RampageData[NumCopsKilled]
  290. WAIT 4000
  291. PRINT_BIG_FORMATTED "Gangsters Killed: %d" TIME 4000 STYLE 5 INPUT RampageData[NumGangstersKilled]
  292. WAIT 4000
  293. PRINT_BIG_FORMATTED "Cars Wrecked: %d" TIME 4000 STYLE 5 INPUT RampageData[NumCarsWrecked]
  294.  
  295. IsRampageOn = FALSE
  296. END
  297. RETURN
  298.  
  299. :CheckSaveGame
  300. // Are we waiting for a save to happen?
  301. IF ShowSaveMenuStatus == 1
  302. THEN
  303. // Has the save happened?
  304. READ_MEMORY 0xBA68A6 1 0 TempVal1 // read the last menu screen ID
  305. IF OR
  306. TempVal1 == 18 // Save Complete Screen
  307. TempVal1 == 19 // Save Complete Screen 2
  308. THEN
  309. READ_MEMORY 0xBA68A7 1 0 TempVal1 // read the selected menu slot
  310.  
  311. // Now we can use the save slot picked in the menu to save our game
  312. CALL @Save 2 RampageData TempVal1
  313. END
  314.  
  315. ShowSaveMenuStatus = 0
  316. END
  317.  
  318. // 0xBA67A7 == bShowSaveScreen - part of the 'CMenuManager' class
  319. READ_MEMORY 0xBA67A7 1 0 TempVal1
  320. IF TempVal1 == 1
  321. THEN
  322. // If bShowSaveScreen is 1, we need to wait for a save to happen
  323. ShowSaveMenuStatus = 1
  324. END
  325. RETURN
  326.  
  327. // The comment trick can be used to preview function arguments.
  328. // See this topic: http://gtag.gtagaming.com/forums/index.php?showtopic=400
  329.  
  330. :Save{__(SVar,_nSlot)__}
  331. // 0@ - RampageData SuperVar
  332. // 1@ - Save Slot
  333.  
  334. // Because SaveVar is assigned to 31@, we'd have to pass a lot of vars to the function in order to get
  335. // the same use out of SaveVar. Instead, we can pass RampageData, which is already set to var 0@ and
  336. // get the copy of SaveVar we stored there...
  337. 0085: SaveVar = RampageData[SaveVarPointer]
  338.  
  339. // A SuperVar trick we could use here is assigning 2@ to @TempMemory and using GET_VAR_POINTER on 2@(2@,4s)
  340. // which is the exact equivalent to using the command below. But we dont need to do that in this case...
  341. GET_LABEL_POINTER @TempMemory 2@
  342. STRING_FORMAT 2@ "cleo\saves\supervars%d.bin" 1@ // writes the formatted string to the space at :TempMemory
  343.  
  344. // Open/create the save file for writing binary - note: the directory "cleo\saves" must already exist
  345.  
  346. IF OPEN_FILE 2@ "wb" 3@
  347. THEN
  348. // Write each SaveVar index to the save file
  349. 0A90: 4@ = 4 * NUM_SAVE_VARS
  350. WRITE_TO_FILE 3@ 4@ SaveVar[SaveVar] // WRITE_TO_FILE can continuously write from any given var
  351. CLOSE_FILE 3@
  352.  
  353. PRINTINT2 "SUPERVARS_SCRIPT_SAVED" 1@
  354. END
  355. RET 0
  356.  
  357. :Load{__(SVar,_nSlot)__}
  358. // 0@ - RampageData SuperVar
  359. // 1@ - Save Slot
  360.  
  361. // We basically follow a similar procedure as for saving here...
  362. 0085: SaveVar = RampageData[SaveVarPointer]
  363. GET_LABEL_POINTER @TempMemory STORE_TO 2@
  364. STRING_FORMAT 2@ "cleo\saves\supervars%d.bin" 1@
  365.  
  366. // Open the save file for reading binary, if it exists - if not, no data will be loaded
  367. IF OPEN_FILE 2@ "rb" 3@
  368. THEN
  369. // Make sure the size of the file is the same as the size of our var space
  370. 0A90: 4@ = 4 * NUM_SAVE_VARS
  371. GET_FILE_SIZE 3@ STORE_TO 5@
  372. IF 003B: 4@ == 5@
  373. THEN
  374. // Read each SaveVar index from the file
  375. 6@ = 0
  376. WHILE NOT IS_END_OF_FILE_REACHED 3@
  377. READ_FROM_FILE 3@ 4 STORE_TO SaveVar[6@]
  378. 6@ += 1
  379. END
  380. PRINTINT2 "SUPERVARS_SCRIPT_LOADED" 1@
  381. ELSE
  382. PRINTINT2 "SUPERVARS_SAVE_INCOMPATIBLE" 1@
  383. END
  384. CLOSE_FILE 3@
  385. END
  386. RET 0
  387.  
  388. // This label is used as variable memory. The HEX..END structure
  389. // allows raw hex data to be inserted. We're inserting a lot of 00's.
  390. // Each 00 is worth 1 byte of data. A basic variable uses 4 bytes of
  391. // data, so for every 00 00 00 00 we get 1 variable to use...
  392. //
  393. // This also means that every SVar Index starts out with the value 0
  394. //
  395. // This is 40 bytes of blank space... We dont have to use it all, but
  396. // the amount of space should be equal to or higher to the space we
  397. // need to store our vars
  398. //
  399. :VARIABLE_MEMORY
  400. HEX
  401. 00 00 00 00 // SVar Index 0
  402. 00 00 00 00 // SVar Index 1
  403. 00 00 00 00 // SVar Index 2
  404. 00 00 00 00 // SVar Index 3
  405. 00 00 00 00 // SVar Index 4
  406. 00 00 00 00 // SVar Index 5
  407. 00 00 00 00 // SVar Index 6
  408. 00 00 00 00 // SVar Index 7
  409. 00 00 00 00 // SVar Index 8
  410. 00 00 00 00 // SVar Index 9
  411. END
  412.  
  413. // This is some space where we can store anything we want.
  414. // We'll use it for storing a formatted string temporarily.
  415. // We have 128 bytes here, so we could have a 127 char string stored here...
  416. //
  417. :TempMemory
  418. HEX
  419. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  420. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  421. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  422. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  423. END
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement