Dteyn_ Posted January 7, 2022 Share Posted January 7, 2022 I've put together my first CLEO script over the last few days, and while the code is functioning well, I feel like it could be optimized. I'm also new to CLEO scripting and may have missed some obvious things, and wanted to make sure my script won't break anything. For some background, during a GTA:VC playthrough I found about 85 packages through natural means, then found another 14 with the help of an online guide. Despite checking the list twice, I somehow could not locate the last package. This was so frustrating, and I'm sure some of you can relate to this experience. Of course, I realize there exist scripts to spawn 100 packages and can easily collect those to get to 100 packages collected, but I am stubborn and wanted to find that last package that I missed in my playthrough. So I set about to create a CLEO script to help me with that. Before starting, I envisioned a script that would highlight packages on the map that I had missed. But I quickly discovered the game doesn't keep track of exactly which packages have been picked up, only how many out of the total # of packages defined. So I had to settle on another approach. After finding all the hidden package coords in main.scm, the first version of my script simply displayed the coords on the map in groups of 10, but after playtesting I felt it would work better as a mission where it moves from one package to the next. This allows for a methodical search and also to display some text for each package about where it can be located and how to access it (ie: if helicopter is required, etc). The script I ended up creating is a Hidden Package mission that guides the player to all hidden packages on the map from #1 - #100 in sequence. Checkpoints are created at each coord, along with a description on screen of where to find each package. Once the player reaches the checkpoint, the next package is automatically highlighted. The point of this script is to help a player locate packages they have missed, as in my case. After much code cobbling it is working to my satisfaction, however as it's my first CLEO script, I'm sure it could be optimized. Right now the list of packages is hard-coded, but if possible it would probably be better to have a file with a list of co-ordinates, and have some re-usable code that can cycle through that list. One thing I'd like to add is a way to stop the mission once it's started. Currently there is a way to skip packages, but no way to stop the mission without cycling through all the packages. I tried adding a nested if..then with another is_key_pressed opcode to exit the loop if an exit key is pressed, but can't seem to get the second key detection working. I can either get "skip package" or "end mission" functionality working, but not both at the same time. I'll admit my coding skills are a bit rusty so I may have overlooked something obvious. I also added a warning to not save the game while this script is active. I realize the blip created could be permanent if game is saved while it's active, so I added a warning when the mission is started to that effect. Here is the code, I've truncated it down to 5 packages for brevity. The source with 100 packages is about 54kb or so. Spoiler // Hidden Package Mission VC v0.5 // Mission to track down packages in order from #1 to #100 // Game: GTA Vice City // Author: Dteyn // Date: 2021-01-06 {$CLEO .cs} {$USE ini} 0000: 0AF0: $BlipColor = get_int_from_ini_file "cleo\PackageMissionVC.ini" section "config" key "BlipColor" // $BlipColor = Color of blip on map 0AF0: $StartKey = get_int_from_ini_file "cleo\PackageMissionVC.ini" section "config" key "StartKey" // $StartKey = key for starting the mission 0AF0: $SkipKey = get_int_from_ini_file "cleo\PackageMissionVC.ini" section "config" key "SkipKey" // $SkipKey = key for skipping a package :pkgbegin while true wait 0 ms if 0AB0: key_pressed $StartKey // Press to begin mission then jump @pkg1 end end :pkg1 03E5: print_help 'PKGSTRT' // Hidden Package Mission started. DO NOT SAVE while on mission! 00BC: print_now 'PKG1' 10000 ms 1 // Hidden Package #1 - Ocean Beach - On a wooden structure in the water. Access by boat or helicopter. 0189: add_blip_for_contact_point [email protected] -368.4 -1733.2 11.6 0165: change_blip_colour [email protected] color_to $BlipColor :pkg1wait if or 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 -368.4 -1733.2 radius 4.0 4.0 0AB0: key_pressed $SkipKey // Press to skip package then 0164: remove_blip [email protected] jump @pkg2 end wait 0 jump @pkg1wait :pkg2 wait 200 00BC: print_now 'PKG2' 10000 ms 1 // Hidden Package #2 - Ocean Beach - On a rocky outcrop. Access by boat or helicopter. 0189: add_blip_for_contact_point [email protected] -213.01 -1647.11 13.1 0165: change_blip_colour [email protected] color_to $BlipColor :pkg2wait if or 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 -213.01 -1647.11 radius 4.0 4.0 0AB0: key_pressed $SkipKey // Press to skip package then 0164: remove_blip [email protected] jump @pkg3 end wait 0 jump @pkg2wait :pkg3 wait 200 00BC: print_now 'PKG3' 10000 ms 1 // Hidden Package #3 - Ocean Beach - At the south steps of Lance Vance's house. 0189: add_blip_for_contact_point [email protected] = -104.3 -1600.3 10.4 0165: change_blip_colour [email protected] color_to $BlipColor :pkg3wait if or 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 -104.3 -1600.3 radius 4.0 4.0 0AB0: key_pressed $SkipKey // Press to skip package then 0164: remove_blip [email protected] jump @pkg4 end wait 0 jump @pkg3wait :pkg4 wait 200 00BC: print_now 'PKG4' 10000 ms 1 // Hidden Package #4 - Ocean Beach - On the steps of the lighthouse. 0189: add_blip_for_contact_point [email protected] = 479.6 -1718.5 15.6 0165: change_blip_colour [email protected] color_to $BlipColor :pkg4wait if or 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 479.6 -1718.5 radius 4.0 4.0 0AB0: key_pressed $SkipKey // Press to skip package then 0164: remove_blip [email protected] jump @pkg5 end wait 0 jump @pkg4wait :pkg5 wait 200 00BC: print_now 'PKG5' 10000 ms 1 // Hidden Package #5 - Ocean Beach - NW corner of underground car park near Ocean Bay Marina. 0189: add_blip_for_contact_point [email protected] = -172.4 -1341.3 3.9 0165: change_blip_colour [email protected] color_to $BlipColor :pkg5wait if or 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 -172.4 -1341.3 radius 4.0 4.0 0AB0: key_pressed $SkipKey // Press to skip package then 0164: remove_blip [email protected] jump @pkg6 end wait 0 jump @pkg5wait :pkgdone wait 200 03E5: print_help 'PKGEND' 10000 ms 1 // Hidden package mission ended. jump @pkgbegin // Wait for mission to be started again PackageMissionVC.ini contents: [config] StartKey=50 SkipKey=51 BlipColor=4 PackageMissionVC.fxt contents: PKGSTRT Hidden Package Mission started. DO NOT SAVE while on mission! PKGEND Hidden Package Mission ended. PKG1 Hidden Package #1 - Ocean Beach - On a wooden structure in the water. Access by boat or helicopter. PKG2 Hidden Package #2 - Ocean Beach - On a rocky outcrop in the water. Access by boat or helicopter. PKG3 Hidden Package #3 - Ocean Beach - At the south (rear) steps of Lance Vance's house. PKG4 Hidden Package #4 - Ocean Beach - On the steps of the lighthouse at the SE corner of the beach. PKG5 Hidden Package #5 - Ocean Beach - NW corner of underground car park near Ocean Bay Marina. Link to comment Share on other sites More sharing options...
OrionSR Posted January 7, 2022 Share Posted January 7, 2022 6 hours ago, Dteyn_ said: wanted to make sure my script won't break anything Using custom global variable names in a cleo script will corrupt something, maybe it won't matter, but it could break the save, and would be hard to tell the difference without play testing everything. However, if you decompile your compiled script without using Sanny's special SCM Info the raw script should show you which global variable numbers your script highjacked. Then you could check how those variables are used in the main.scm script. Figure that all global variables are owned my main.scm. If you are writing a cleo script then you can only use global variables in context with main.scm. Normally, you can safely use the $Player_Char and $Player_Actor handles as expect, and change the $OnMission flag on and off as required. If you are messing with anything else then you'd better know what you are doing or something will break. Setting $OnMission = 1 will prevent missions from starting, and save pickups are disabled until it's turned off. You can fix your global variable issue by changing the global variables to local variables. Search and replace can do this quickly. Local variables can't be named like global variable, but the can be assigned to constants. So instead of, for example, replacing $BlipColor with [email protected], replace it with BlipColor, and assign the constants by defining them in your header - in this case, just before 0000: should do nicely. const StartKey = [email protected] SkipKey = [email protected] BlipColor = [email protected] end Try adding your exit check after the skip check in it's own if..then..end construct - don't nest it. As far as reorganizing goes... Don't fix what's not broken. Once you sort out a few bugs this script should be a very good guide in the context of starting a new game and collecting all the packages. Consider applying what you've learned to a more optimized script that can; search game memory and identify the next currently available package, even on custom mains with modified package locations attach the blip directly to the pickup so if the player saves the blip using a mod the blip will still get removed when the package is collected The game memory part probably sounds intimidating, but if you are comfortable with algebra and aren't afraid of hexadecimal it isn't that hard to code. I'm not familiar with the details for VC; I usually mod SA. Shouldn't be hard to look up though. Dteyn_ 1 Link to comment Share on other sites More sharing options...
Dteyn_ Posted January 8, 2022 Author Share Posted January 8, 2022 Thank you so much for your reply, this is exactly what I was hoping for. 13 hours ago, OrionSR said: Using custom global variable names in a cleo script will corrupt something, maybe it won't matter, but it could break the save, and would be hard to tell the difference without play testing everything. However, if you decompile your compiled script without using Sanny's special SCM Info the raw script should show you which global variable numbers your script highjacked. This is great to know, as a newbie I wasn't aware that using global variables is a no-no with cleo scripts. I checked by decompiling and my script had hijacked $6, $7, and $8 which appear to be part of the stadium mission scripting in main.scm. Glad to avoid this issue, I will take your suggestion of using constants and local variables instead. It's also good to know about $OnMission, I'll definitely use that as well. I'll also try your suggestion of creating a separate if..then..end construct for the "end mission" key. My algebra skills haven't been put to the test in years, but I am comfortable with hexadecimal. I like the idea of making a more advanced script in the event of custom mains, that would be a neat ability as well. May consider pursuing this for additional challenge once I've completed v1.0. Thanks again for your help and suggestions! I will be sure to reply back if I have more questions and will also post a link to the completed script in this thread once it's published. OrionSR 1 Link to comment Share on other sites More sharing options...
Dteyn_ Posted January 23, 2022 Author Share Posted January 23, 2022 I've completed most of the code changes I had planned for v1.0 and it's nearly ready to publish. I have thoroughly playtested this, but there may still be things I have missed. I'm hoping that I haven't done anything that will break a save game. I did read the CLEO scripting tutorial by ZAZ which was helpful in understanding a few things. I have implemented many changes since my last post: using only local variables instead of global variables using $OnMission and also checking to ensure it's not set before starting my script using opcode 048C to detect whether a package has been picked up and skip automatically can now stop guide at any point using ToggleKey as defined in .ini file cleaned up formatting and commented a lot of code I'm currently quite happy with how everything is working and am hoping to release v1.0 soon, provided there aren't any glaring game breaking bugs left that I have missed. Below is the current source with packages 6-100 removed for brevity. Spoiler // Hidden Package Guide VC v0.9 // Mission to track down packages in order from #1 to #100 // Game: GTA Vice City // Author: Dteyn // Date: 2021-01-22 {$CLEO .cs} {$USE ini} const BlipColor = [email protected] ToggleKey = [email protected] SkipKey = [email protected] PkgCheck = [email protected] BlipScale = 2.0 WaitPkgPU = 500 PkgMsgDelay = 10000 end 0000: nop 03A4: name_thread 'HPACK' // Name thread // Get settings from ini file 0AF0: BlipColor = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "BlipColor" // BlipColor = Color of blip on map 0AF0: ToggleKey = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "ToggleKey" // ToggleKey = key for toggling the mission on or off 0AF0: SkipKey = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "SkipKey" // SkipKey = key for skipping a package 0AF0: PkgCheck = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "PkgCheck" // PkgCheck = whether to check if packages picked up :pkgbegin while true wait 0 ms if 0AB0: key_pressed ToggleKey // Activate when ToggleKey is pressed then if $OnMission == 0 // OnMission check then $OnMission = 1 // Set OnMission flag 03E5: print_help 'PKGSTRT' // Hidden Package Guide started. jump @pkg1pre end end end // ============================== PACKAGE #1 ============================== :pkg1pre if PkgCheck == true // If PkgCheck is enabled, perform a check then jump @pkg1ck else jump @pkg1 end :pkg1ck if 048C: is_any_pickup_at_coords -368.4 -1733.2 11.6 // Check coords to see if package is present then jump @pkg1 // If so, display location else 00BC: print_now 'PKG1P' WaitPkgPU ms 1 // If not, print 'Hidden Package already picked up' wait WaitPkgPU jump @pkg2pre // and go to next package end :pkg1 00BC: print_now 'PKG1' PkgMsgDelay ms 1 // Display priority text with package location details 018A: add_blip_for_coord [email protected] -368.4 -1733.2 11.6 // Add blip 0165: change_blip_colour [email protected] color_to BlipColor // Change blip color 03BC: add_shere [email protected] -368.4 -1733.2 11.6 scale BlipScale // Add sphere wait 500 // Wait 500 ms to prevent toggle end mission keypress from activating :pkg1wait if 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 -368.4 -1733.2 radius BlipScale BlipScale // When near package then 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg2pre // And move to next package end if 0AB0: key_pressed SkipKey // If SkipKey is pressed then 03E5: print_help 'PKGSKIP' // Hidden package skipped 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg2pre // And move to next package end if 0AB0: key_pressed ToggleKey // End mission if ToggleKey is pressed then jump @pkgdone end wait 0 // Loop jump @pkg1wait // ============================== PACKAGE #2 ============================== :pkg2pre wait 200 if PkgCheck == true // If PkgCheck is enabled, perform a check then jump @pkg2ck else jump @pkg2 end :pkg2ck if 048C: is_any_pickup_at_coords -213.0 -1647.1 13.1 // Check coords to see if package is present then jump @pkg2 // If so, display location else 00BC: print_now 'PKG2P' WaitPkgPU ms 1 // If not, print 'Hidden Package already picked up' wait WaitPkgPU jump @pkg3pre // and go to next package end :pkg2 00BC: print_now 'PKG2' PkgMsgDelay ms 1 // Display priority text with package location details 018A: add_blip_for_coord [email protected] -213.0 -1647.1 13.1 // Add blip 0165: change_blip_color [email protected] color_to BlipColor // Change blip color 03BC: add_shere [email protected] -213.0 -1647.1 13.1 scale BlipScale // Add sphere :pkg2wait if 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 -213.0 -1647.1 radius BlipScale BlipScale // When near package then 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg3pre // And move to next package end if 0AB0: key_pressed SkipKey // If SkipKey is pressed then 03E5: print_help 'PKGSKIP' // Hidden package skipped 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg3pre // And move to next package end if 0AB0: key_pressed ToggleKey // End mission if ToggleKey is pressed then jump @pkgdone end wait 0 jump @pkg2wait // Loop // ============================== PACKAGE #3 ============================== :pkg3pre wait 200 if PkgCheck == true // If PkgCheck is enabled, perform a check then jump @pkg3ck else jump @pkg3 end :pkg3ck if 048C: is_any_pickup_at_coords -104.3 -1600.3 10.4 // Check coords to see if package is present then jump @pkg3 // If so, display location else 00BC: print_now 'PKG3P' WaitPkgPU ms 1 // If not, print 'Hidden Package already picked up' wait WaitPkgPU jump @pkg4ck // and go to next package end :pkg3 00BC: print_now 'PKG3' PkgMsgDelay ms 1 // Display priority text with package location details 018A: add_blip_for_coord [email protected] -104.3 -1600.3 10.4 // Add blip 0165: change_blip_color [email protected] color_to BlipColor // Change blip color 03BC: add_shere [email protected] -104.3 -1600.3 10.4 scale BlipScale // Add sphere :pkg3wait if 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 -104.3 -1600.3 radius BlipScale BlipScale // When near package then 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg4pre // And move to next package end if 0AB0: key_pressed SkipKey // If SkipKey is pressed then 03E5: print_help 'PKGSKIP' // Hidden package skipped 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg4pre // And move to next package end if 0AB0: key_pressed ToggleKey // End mission if ToggleKey is pressed then jump @pkgdone end wait 0 jump @pkg3wait // Loop // ============================== PACKAGE #4 ============================== :pkg4pre wait 200 if PkgCheck == true // If PkgCheck is enabled, perform a check then jump @pkg4ck else jump @pkg4 end :pkg4ck if 048C: is_any_pickup_at_coords 479.6 -1718.5 15.6 // Check coords to see if package is present then jump @pkg4 // If so, display location else 00BC: print_now 'PKG4P' WaitPkgPU ms 1 // If not, print 'Hidden Package already picked up' wait WaitPkgPU jump @pkg5pre // and go to next package end :pkg4 00BC: print_now 'PKG4' PkgMsgDelay ms 1 // Display priority text with package location details 018A: add_blip_for_coord [email protected] 479.6 -1718.5 15.6 // Add blip 0165: change_blip_color [email protected] color_to BlipColor // Change blip color 03BC: add_shere [email protected] 479.6 -1718.5 15.6 scale BlipScale // Add sphere :pkg4wait if 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 479.6 -1718.5 radius BlipScale BlipScale // When near package then 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg5pre // And move to next package end if 0AB0: key_pressed SkipKey // If SkipKey is pressed then 03E5: print_help 'PKGSKIP' // Hidden package skipped 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg5pre // And move to next package end if 0AB0: key_pressed ToggleKey // End mission if ToggleKey is pressed then jump @pkgdone end wait 0 jump @pkg4wait // Loop // ============================== PACKAGE #5 ============================== :pkg5pre wait 200 if PkgCheck == true // If PkgCheck is enabled, perform a check then jump @pkg5ck else jump @pkg5 end :pkg5ck if 048C: is_any_pickup_at_coords -172.4 -1341.3 3.9 // Check coords to see if package is present then jump @pkg5 // If so, display location else 00BC: print_now 'PKG5P' WaitPkgPU ms 1 // If not, print 'Hidden Package already picked up' wait WaitPkgPU jump @pkg6pre // and go to next package end :pkg5 00BC: print_now 'PKG5' PkgMsgDelay ms 1 // Display priority text with package location details 018A: add_blip_for_coord [email protected] -172.4 -1341.3 3.9 // Add blip 0165: change_blip_color [email protected] color_to BlipColor // Change blip color 03BC: add_shere [email protected] -172.4 -1341.3 3.9 scale BlipScale // Add sphere :pkg5wait if 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 -172.4 -1341.3 radius BlipScale BlipScale // When near package then 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg6pre // And move to next package end if 0AB0: key_pressed SkipKey // If SkipKey is pressed then 03E5: print_help 'PKGSKIP' // Hidden package skipped 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere jump @pkg6pre // And move to next package end if 0AB0: key_pressed ToggleKey // End mission if ToggleKey is pressed then jump @pkgdone end wait 0 jump @pkg5wait // Loop :pkg6pre // snipped 6-100 :pkgdone 0164: remove_blip [email protected] // Remove blip 03BD: remove_sphere [email protected] // Remove sphere 00BE: clear_prints // Clear any on-screen text $OnMission = 0 // Clear OnMission flag wait 200 03E5: print_help 'PKGEND' // Hidden Package Guide ended. jump @pkgbegin // Wait for mission to be started again Here is the new .fxt file: PKGSTRT Hidden Package Guide started. PKGEND Hidden Package Guide stopped. PKGSKIP Hidden Package skipped. PKG1 Hidden Package #1 - Ocean Beach - On a wooden structure in the water. Access by boat or helicopter. PKG2 Hidden Package #2 - Ocean Beach - On a rocky outcrop in the water. Access by boat or helicopter. PKG3 Hidden Package #3 - Ocean Beach - At the south (rear) steps of Lance Vance's house. PKG4 Hidden Package #4 - Ocean Beach - On the steps of the lighthouse at the SE corner of the beach. PKG5 Hidden Package #5 - Ocean Beach - NW corner of underground car park near Ocean Bay Marina. PKG1P Hidden Package #1 already picked up. PKG2P Hidden Package #2 already picked up. PKG3P Hidden Package #3 already picked up. PKG4P Hidden package #4 already picked up. PKG5P Hidden package #5 already picked up. And the latest PackageGuideVC.ini file: // Config file for Hidden Package Guide // NOTE: To redefine keys, use decimal keycode values from this link: // https://www.indigorose.com/webhelp/ams/Program_Reference/Misc/Virtual_Key_Codes.htm // ToggleKey = key for toggling the mission on/off // SkipKey = key for skipping a package // PkgCheck = whether to check for packages being picked up. set to 0 to guide to all locations // BlipColor = set the color of the radar blips, values 0-6 as below // 0 = red // 1 = green // 2 = blue // 3 = white // 4 = yellow // 5 = purple // 6 = cyan [config] ToggleKey=50 SkipKey=51 BlipColor=4 PkgCheck=1 Link to comment Share on other sites More sharing options...
OrionSR Posted January 23, 2022 Share Posted January 23, 2022 2 hours ago, Dteyn_ said: I'm currently quite happy with how everything is working and am hoping to release v1.0 soon, provided there aren't any glaring game breaking bugs left that I have missed. This version looks a lot better. Nice work. I don't see any game breaking issues, but I do have a few comments, and ideas you can use in future projects. I'll work though a few suggestions as I look for more serious issues. 0000: nop 03A4: name_thread 'HPACK' // Name thread The hard rule is that cleo scripts can't jump to a 0 offset - you can't start the script with a label. The general rule is to start with a NOP in a cleo script that will execute and terminate, and name the thread if it'll continue to run in a loop. Naming the thead/script allows the script to be terminated by other scripts, or the script offset to be identified by cleo opcodes, using the name as a handle. const [email protected] = hBlip // BlipHandle [email protected] = hSphere // SphereHandle end I really like how you used constants in this script to make it easy to read. Many of your comments are hardly needed. But you may as well go all in and define them all. What happens if your script can't find it's INI file? This is an issue in my current project as well; I'm not sure what happens. Either way, I have been considering a strategy to create a default INI if none is found. Not sure what it'll look like just yet. 03E5: print_help 'PKGSTRT' // Hidden Package Guide started. Be aware that Cleo offers opcodes to add "dynamic" gxt strings, and to print "formatted" custom text, as an alternative to using FXT files. However, considering the length and number of strings required for a hidden package guide, offloading the strings to an FXT file will shorten the total size of the script considerably. I wouldn't suggest any changes to the current strategy. 048C: is_any_pickup_at_coords -368.4 -1733.2 11.6 // Check coords to see if package is present Awesome find and use of this opcode. Have you considered using it in place of: 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 479.6 -1718.5 radius BlipScale BlipScale // When near package As I mentioned before, I would not recommend making large scale changes to a working script. But this script does present an excellent example for suggestions on how to optimize future projects. gosub..return is a powerful tool for organizing scripts. When gosub @label is called, execution will jump to a subroutine at that label, and when a Return is encountered execution resumes at the command follow the gosub. An easy example for you script would look something like this: const [email protected] = X_Coord [email protected] = Y_Coord [email protected] = Z_Coord [email protected] = PKGHINT [email protected] = PKGPICD end {...} :pkg1pre gosub @Package_Data_1 {...} :Package_Data_1 //048C: is_any_pickup_at_coords -368.4 -1733.2 11.6 // Check coords to see if package is present X_Coord = -368.4 Y_Coord = -1733.2 Z_Coord = 11.6 PKGHINT = 'PKG1' PKGPICD = 'PKG1P' return :Package_Data_2 {...} Then many codes would be more generic, easier to edit. 048C: is_any_pickup_at_coords X_Coord Y_Coord Z_Coord 00BC: print_now PKGPICD WaitPkgPU ms 1 // If not, print 'Hidden Package already picked up' 00BC: print_now PKGHINT PkgMsgDelay ms 1 // Display priority text with package location details 018A: add_blip_for_coord hBlip X_Coord Y_Coord Z_Coord 0165: change_blip_colour hBlip color_to BlipColor 03BC: add_shere hSphere X_Coord Y_Coord Z_Coord scale BlipScale 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 X_Coord Y_Coord radius BlipScale BlipScale From there... There would definitely be some problems with the logic flow of the script, particularly the exit strategy, but you would be really close changing your Package 1 segment into a generic :Do_Package_Stuff subroutine, and dumping segments for 2 through 100. The main body of the script would look something like: gosub @Package_Data_1 gosub @Do_Package_Stuff gosub @Package_Data_2 gosub @Do_Package_Stuff gosub @Package_Data_3 gosub @Do_Package_Stuff Hopefully these examples illustrate how subroutines can help optimize a script so it's easier to write and edit. But for this script, you have finished writing and editing, so the changes are not necessary. Link to comment Share on other sites More sharing options...
Dteyn_ Posted January 24, 2022 Author Share Posted January 24, 2022 Thanks for the kind words, and for the informative reply. It's hugely appreciated. 12 hours ago, OrionSR said: The hard rule is that cleo scripts can't jump to a 0 offset - you can't start the script with a label. The general rule is to start with a NOP in a cleo script that will execute and terminate, and name the thread if it'll continue to run in a loop. Naming the thead/script allows the script to be terminated by other scripts, or the script offset to be identified by cleo opcodes, using the name as a handle. My main purpose for this was that I want to leave the thread running in case the user wants to restart the guide to check for any packages they may have skipped over. At the end I just have it jump back to the while..end loop and wait for another keypress to restart the guide. If I read your comment correctly, it sounds like this is functioning as intended. I took your advice and went all-in on the constants for the other local variables. It sure does make it lot easier to read. I also like that using the constants allows for easier tweaking of the hardcoded values such as the BlipScale or Wait delays, it's nice to have those all in one spot for quick editing. 12 hours ago, OrionSR said: 048C: is_any_pickup_at_coords -368.4 -1733.2 11.6 // Check coords to see if package is present Awesome find and use of this opcode. Have you considered using it in place of: 00E3: locate_player_any_means_2d $PLAYER_CHAR 0 479.6 -1718.5 radius BlipScale BlipScale // When near package I was super excited when I stumbled across opcode 048C, it happened to catch my eye as I was scrolling through the Sanny Builder library and am sure glad I spotted it. Prior to using this opcode, the script would direct the player to each location to check if the package had been picked up which was very time-consuming. With this opcode it's greatly simplified the process, it just scans and tells you which packages still need picking up instead of blindly guiding the player to all locations. The 00E3 opcode was originally chosen so that the next package could be triggered with a helicopter flyover, as before I found 048C that was my primary way of going around and checking each package location. Using 00E3 to locate the player by 2d made it so I could just fly over any checkpoints to trigger the next one. During playtesting though I realized 00E3 wasn't ideal for some packages where the player is able to walk above/below the package while attempting to retrieve it. For those packages, I changed to opcode 00F5 locate_player_by_any_means_3d so that the trigger sphere must be walked into, to prevent accidental early triggering by walking above or below. With all that said, your idea of re-using 048C as the check seems like the best option, as opposed to using the locate_player opcodes. I hadn't considered this until you mentioned it, if I do any more re-writes on this script I'll be sure to keep that in mind. 12 hours ago, OrionSR said: this script does present an excellent example for suggestions on how to optimize future projects I really appreciate your suggestions on how to further optimize the code. The gosub...return construct is something that definitely intrigues me, I would have loved to implement it that way as it would have greatly simplified the editing of the script and made for a cleaner approach. I will keep this in mind for future projects, the example framework you've provided is extremely helpful in this regard. Thank you for that. With a bit more playtesting I should be ready to publish this. I will post a link here once it's been uploaded. Thanks once again for your help, this is a great community and I'm glad I came here to have this discussion. Link to comment Share on other sites More sharing options...
Dteyn_ Posted January 24, 2022 Author Share Posted January 24, 2022 13 hours ago, OrionSR said: What happens if your script can't find it's INI file? Also - good point about this. To solve this I have implemented a check through a setting in the INI file itself (INISettings=1). If the INI file is present, the setting will be read and the value will be true, and the INI settings are then read in and used. If the INI file is not present, that setting doesn't simply get read in and the script knows to use some default failsafe values instead of reading the INI file. // Check if INI file is present 0AF0: INISettings = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "INISettings" // INISettings = set to 1 to use settings from INI if INISettings == true then // Get settings from ini file 0AF0: BlipColor = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "BlipColor" // BlipColor = Color of blip on map 0AF0: ToggleKey = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "ToggleKey" // ToggleKey = key for toggling the mission on or off 0AF0: SkipKey = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "SkipKey" // SkipKey = key for skipping a package 0AF0: PkgCheck = get_int_from_ini_file "cleo\PackageGuideVC.ini" section "config" key "PkgCheck" // PkgCheck = whether to check if packages picked up else // Use default values below if INI is not present, or if INISettings is set to 0 BlipColor = 5 // BlipColor = purple by default ToggleKey = 50 // ToggleKey = '2' by default SkipKey = 51 // SkipKey = '3' by default PkgCheck = 1 // Enable PkgCheck by default to skip packages already picked up end I have tested this and it seems to be working as intended. OrionSR 1 Link to comment Share on other sites More sharing options...
OrionSR Posted January 24, 2022 Share Posted January 24, 2022 To clarify, including the NOP isn't necessary. The thread name serves the same purpose. Personally, I usually start with wait 3000. It helps isolate crashes caused by the script from other issues. 31 minutes ago, Dteyn_ said: With all that said, your idea of re-using 048C as the check seems like the best option BTW, this isn't always clarified in the documentation but, if it works out better for the logic you require, you can change any conditional command to a not statement by changing the 0 at the start of the opcode to 8. A not should be added to the statement for clarification. 848C: not is_any_pickup_at_coords -368.4 -1733.2 11.6 // Check coords to see if package is not present 48 minutes ago, Dteyn_ said: The gosub...return construct is something that definitely intrigues me, I would have loved to implement it that way as it would have greatly simplified the editing of the script and made for a cleaner approach. Yeah, but... Would you have appreciated the value of subroutines if you hadn't worked through a long version of the script? Figure that any time you find yourself coding pretty much the same thing over and over then there is probably a better way. And that strategy could still be applied to the main-body and Package_Data subroutines I suggested. Those sections repeat mostly the same codes; isn't there a better way? The answer is, Yes, but... Yes, all of the x,y,z coords and gxt keys could be loaded into arrays. A for..end loop can be used to cycle the index of the arrays. But... The problem with this strategy is that it would require 700 variables to hold all the data. Without access to large blocks of free global variables, or custom missions, there simply aren't enough variables to go around. So in this example, the repeated codes are simply a way to get around the limit of having only 16 local variables available in VC scripts. I like your solution to the missing INI issue. I may borrow that strategy if I can't figure out how to create a new default INI. I've only recently started messing with cleo's file commands; your examples made INIs look so easy I decided to give it a try. Creating a new INI with cleo is mostly a chance to try out some more new commands. Again, nice work on the script. I'm a little disappointed that I don't have a way to try it out. Dteyn_ 1 Link to comment Share on other sites More sharing options...