GTAGames.nl

Een echte missie scripten (GTA San Andreas)

Van WikiGTA - De Nederlandse Grand Theft Auto Walkthrough!
Ga naar: navigatie, zoeken

Hoofdpagina > Modding > Modding Tutorial List > Een echte missie scripten


Inleiding

In deze nogal lange tutorial gaan we uitleggen hoe je een echte missie maakt, zoals Rockstar ze ook bouwt. Dus niet als een simpele thread, zoals in de voorgaande tutorials.

Om te beginnen zullen we de structuur en de bijzonderheden op een rijtje zetten, daarna zullen we templates geven voor de verschillende delen, en deze uitleggen, en tenslotte zullen we een missie laten zien die op basis van deze templates zijn gemaakt.

Structuur

Missies zoals ze door rockstar zijn gemaakt, en zoals we ze nu ook gaan maken, wijken qua gedrag af van normale threads die je normaal gebruikt. Ten eerste wordt de code voor een missie pas vanaf de schijf in het geheugen geladen, wanneer de missie gestart wordt. Dit heeft tot gevolg dat er slechts 1 missie tegelijk actief kan zijn. Een ander heel groot verschil is de manier hoe de code zich gedraagt waneer de speler BUSTED of WASTED is. In een normale thread moet je hier steeds op checken (is_payer_defined), maar tijdens een missie hoeft dat niet. Als de speler tijdens een missie WASTED of BUSTED wordt, voert de SCM-engine, namelijk op dat moment automatisch een "return"-opcode uit. Je hoeft dus tijdens de missie zelf niet steeds te controleren of de speler nog leeft, maar je moet wel het standaard framework van een Rockstar missie gebruiken, om die automatische "return"-opcode op te vangen. Dit framework zal verderop uitgelegd wordt bij de missie-template.

Om een missie te maken heb je twee delen nodig:

  • De missie zelf
  • Een zogenaamde "sniffer"-thread, die de startvoorwaarden van een missie checked en controleert of de speler de start van een missie triggert ( door b.v. op een bepaalde plek in een marker te lopen).

Voor beide delen zullen we een template geven, die je voor je eigen project moet invullen en evt. uitbreiden.

Sniffer-thread template

De sniffer-thread wordt gestart, zodra de missies gedaan kunnen worden. Dit kan direct vanaf de start van het spel zijn, of op het moment dat je een andere missie gehaalt hebt. Deze template bevat de code voor een sniffer-thread voor twee missies, maar dit kun je vrij eenvoudig uitbreiden tot veel meer missies die na elkaar gedaan kunnen worden.

 // $TUT_MISSIONS_PASSED wordt vooraf op 0 gezet.
 // bij het "PASS"-en van een missie , moet je $TUT_MISSIONS_PASSED ophogen.
 :TUT_MISSIONS_SNIFFER
 thread "TUT_SNIFF"
 $MARKER_SWEET_HOUSE = Marker.CreateIconAndSphere(42, <<xcoord>>, <<ycoord>>, <<zcoord>>)
 repeat
   wait $DEFAULT_WAIT_TIME 
   if and
     player.Defined($PLAYER_CHAR)
     $ONMISSION == 0 
   then
     if and
       00FF:   actor $PLAYER_ACTOR sphere 0 in_sphere <<xcoord>>, <<ycoord>>, <<zcoord>> radius 1.0 1.0 2.0 on_foot 
       Player.Controllable($PLAYER_CHAR)
       $TUT_MISSIONS_PASSED == 0
     then
       $ONMISSION = 1
       00BA: show_text_styled GXT 'INTRO_1' time 1000 style 2  
      start_mission 1  
     end
     
     if and
       00FF:   actor $PLAYER_ACTOR sphere 0 in_sphere <<xcoord>>, <<ycoord>>, <<zcoord>> radius 1.0 1.0 2.0 on_foot 
       Player.Controllable($PLAYER_CHAR)
       $TUT_MISSIONS_PASSED == 1
     then
       $ONMISSION = 1
       00BA: show_text_styled GXT 'INTRO_2' time 1000 style 2  
      start_mission 2  
     end
   end 
 until $TUT_MISSIONS_PASSED == 2
 marker.Disable($MARKER_SWEET_HOUSE)
 end_thread 
 

Globaal gesproken, zet deze thread een marker op de goede plek waar de missions gestart kunnen worden en wacht dan in een lus tot de speler in de marker loopt, of dat beide missies gepassed zijn. Zolang $TUT_MISSIONS_PASSED op 0 staat, is er nog geen van de twee tutorial missies gepassed, en zal de eerste missie gestart worden, als de speler in de marker loopt (de eerste tak van de lus). Nadat de speler de eerste missie gehaald heeft, (waarbij de $TUT_MISSIONS_PASSED is opgehoogd naar 1), zal de snifferthread opnieuw wachten tot de speler in de marker loopt (de tweede tak van de lus). Na het succesvol afronden van de tweede missie, zal de $TUT_MISSIONS_PASSED op 2 staan, waarna de lus wordt verlaten, de marker wordt opgeruimd, en de thread wordt beëindigd.

Mission template

 //-------------Mission 1---------------
 // Mission wrapper
 :TUT_MISSION_1
 thread 'TUT 1' 
 gosub @TUT_MISSION_1_MAIN 
 if wasted_or_busted 
 then 
   gosub @TUT_MISSION_1_FAIL 
 end
 gosub @TUT_MISSION_1_CLEANUP 
 end_thread 
 
 //-------------------------------------
 :TUT_MISSION_1_MAIN
 
 
 //-------------------------------------
 :TUT_MISSION_1_PASSED
 Player.Money($PLAYER_CHAR) += 10000
 01E3: show_text_1number_styled GXT 'M_PASS' number 10000 time 5000 style 1 // MISSION PASSED!~n~~w~$~1~
 // This subroutine is executed when the mission is passed. 
 // Give the rewards, and make new missions available if needed.
 
 return
 
 //-------------------------------------
 :TUT_MISSION_1_FAIL
 00BA: show_text_styled GXT 'M_FAIL' time 5000 style 1  // ~r~MISSION FAILED!
 // This subroutine is executed when the mission fails. 
 return 
 
 //-------------------------------------
 :TUT_MISSION_1_CLEANUP
 $ONMISSION = 0 
 // This subroutine is always executed at the end of the mission, regardless the outcome.
 // This is the place to unload models etc. 
 mission_cleanup 
 return 
   

Het eerste stukje, van deze code is de missie-wrapper, die de verschillende delen van de missie aanroept met behulp van "gosub", om zo de automatische "return" bij het WASTED of BUSTED van de speler ook opvangt. Als eerste roept hij het hoofdgedeelte van de missie aan. Als die terug komt, kunnen er twee dingen zijn gebeurt:

  • Een automatische "return, omdat de speler WASTED of BUSTED is. In dat geval wordt de mission failed nog aangeroepen. en daarna de "cleanup" routine, die alles opruimt.
  • De missie is PASSED of FAILED om een andere reden. In dat geval in de mission_passed of mission_failed routine al aangeroepen, en hoeft alleen de "cleanup" nog gedaan te worden.

In het complete voorbeeld hieronder zul je zien, hoe je vanuit het hoofddeel, de mission_passed en mission_failed routines aanroept bij bepaalde gebeurtenissen.

Voorbeeld missie

Op basis van bovenstaande templates hebben we hieronder een simpele missie gemaakt. Naast de templates uit deze tutorials, worden ook verschillende dingen uit voorgaande tutorials gebruikt.

De bedoeling van deze missie, die je kunt starten, voor het huis van Sweet, is om een andere actor dood te schieten. Er staan echter twee actors, een mannetje en een vrouwtje. Het is de bedoeling om het vrouwtjes dood te schieten, dan is de missie passed. Als je echter het mannetje doodschiet, dan is de missie failed. Als de speler WASTED of BUSTED is, voor dat het vrouwtje is doodgeschoten, is de missie ook failed.

 DEFINE MISSIONS 2
 DEFINE MISSION 0 AT @INITIAL
 DEFINE MISSION 1 AT @TUT_MISSION_1
 DEFINE EXTERNAL_SCRIPTS 0 // Use -1 in order not to compile AAA script
 //DEFINE SCRIPT {NAME}  AT {LABEL} @
 DEFINE UNKNOWN_EMPTY_SEGMENT 0
 DEFINE UNKNOWN_THREADS_MEMORY 0
 
 {$VERSION 3.0.0000}
 var
  $PLAYER_CHAR: Player
 end // var 
 03A4: name_thread 'MAIN' 
 01F0: set_max_wanted_level_to 6 
 0111: toggle_wasted_busted_check 0 
 00C0: set_current_time_hours_to 8 minutes_to 0 
 04E4: unknown_refresh_game_renderer_at 2488.56 -1666.84 
 03CB: set_rendering_origin_at 2488.56 -1666.84 13.38 
 0053: $PLAYER_CHAR = create_player #NULL at 2488.56 -1666.84 13.38 
 01F5: $PLAYER_ACTOR = create_player_actor $PLAYER_CHAR 
 07AF: $PLAYER_GROUP = player $PLAYER_CHAR group 
 0373: set_camera_directly_behind_player 
 01B6: set_weather 0 
 0001: wait 0 ms 
 087B: set_player $PLAYER_CHAR clothes_texture "PLAYER_FACE" model "HEAD" body_part 1 
 087B: set_player $PLAYER_CHAR clothes_texture "JEANSDENIM" model "JEANS" body_part 2 
 087B: set_player $PLAYER_CHAR clothes_texture "SNEAKERBINCBLK" model "SNEAKER" body_part 3 
 087B: set_player $PLAYER_CHAR clothes_texture "VEST" model "VEST" body_part 0 
 070D: rebuild_player $PLAYER_CHAR 
 01B4: toggle_player $PLAYER_CHAR can_move 1 
 016A: fade 1 time 0 
 04BB: select_interior 0 
 0629: change_integer_stat 181 to 4 
 016C: restart_if_wasted_at 2027.77 -1420.52 15.99 angle 137.0 town_number 0 
 016D: restart_if_busted_at 1550.68 -1675.49 14.51 angle 90.0 town_number 0 
 0180: set_on_mission_flag_to $ONMISSION // Note: your missions have to use the variable defined here 
 0004: $DEFAULT_WAIT_TIME = 250
 03E6: remove_text_box 
 0417: start_mission 0  // Initial 
 wait 0
 // put your create_thread commands here
 00D7: create_thread @TUT_MISSIONS_SNIFFER 
 
 :MAIN_LOOP
 0001: wait $DEFAULT_WAIT_TIME ms
 0002: jump @MAIN_LOOP 
   
 
 
 // put your mods (threads) here
 :TUT_MISSIONS_SNIFFER
 thread "TUT_SNIFF"
 $MARKER_SWEET_HOUSE = Marker.CreateIconAndSphere(42, $X_SWEET_HOUSE, $Y_SWEET_HOUSE, $Z_SWEET_HOUSE)
 repeat
   wait $DEFAULT_WAIT_TIME 
   if and
     player.Defined($PLAYER_CHAR)
     $ONMISSION == 0 
   then
     if and
       00FF:   actor $PLAYER_ACTOR sphere 0 in_sphere $X_SWEET_HOUSE $Y_SWEET_HOUSE $Z_SWEET_HOUSE radius 1.0 1.0 2.0 on_foot 
       Player.Controllable($PLAYER_CHAR)
       $TUT_MISSIONS_PASSED == 0
     then
       $ONMISSION = 1
       00BA: show_text_styled GXT 'INTRO_1' time 1000 style 2  // Big Smoke
      start_mission 1  
     end
     
 
   end 
 until $TUT_MISSIONS_PASSED == 1
 marker.Disable($MARKER_SWEET_HOUSE)
 end_thread 
 
 //-------------Mission 0---------------
 :INITIAL
 $TUT_MISSIONS_PASSED = 0
 $ONMISSION = 0
 
 $X_SWEET_HOUSE = 2515.07 
 $Y_SWEET_HOUSE = -1673.98 
 $Z_SWEET_HOUSE = 12.71 
 0629: change_integer_stat 225 to 999 
 
 end_thread
 
 //-------------Mission 1---------------
 // Mission wrapper
 :TUT_MISSION_1
 thread 'TUT 1' 
   gosub @TUT_MISSION_1_MAIN 
   if wasted_or_busted 
   then 
     gosub @TUT_MISSION_1_FAIL 
   end  
   gosub @TUT_MISSION_1_CLEANUP 
 end_thread 
 
 //-------------------------------------
 :TUT_MISSION_1_MAIN
 //Load models
 model.Load(#m4)
 model.Load(#BFYST)
 model.Load(#BMYST)
 model.Load(#TAMPA)
 
 038B: load_requested_models 
 
 :TUT_MISSION_1_LOAD
 wait 0 ms 
 if and 
   model.Available(#m4)
   model.Available(#BMYST)  
   model.Available(#BFYST)
   model.Available(#TAMPA)
 004D: jump_if_false @TUT_MISSION_1_LOAD 
 
 01B2: give_actor $PLAYER_ACTOR weapon 31 ammo 10000 // Load the weapon model before using this
 01B9: set_actor $PLAYER_ACTOR armed_weapon_to 31
 
 0674: set_car_model #TAMPA numberplate "PATRICK_" 
 $car_num = car.Create(#TAMPA,2488.56, -1666.84, 13.38)
 
 
 $victim = Actor.Create(CIVFEMALE, #BFYST, 2488.56, -1656.84, 13.38)
 $innocent = Actor.Create(CIVMALE, #BMYST, 2490.56, -1656.84, 13.38)
 0187: $victim_marker = create_marker_above_actor $victim   
 
 repeat
   wait 0 ms
   if actor.Dead($innocent)
   then
     jump @TUT_MISSION_1_FAIL
   end
 until actor.Dead($victim)
 
 :TUT_MISSION_1_PASSED
 0394: play_music 1
 $TUT_MISSIONS_PASSED += 1
 Player.Money($PLAYER_CHAR) += 10000
 01E3: show_text_1number_styled GXT 'M_PASS' number 10000 time 5000 style 1 // MISSION PASSED!~n~~w~$~1~
 return
 
 //-------------------------------------
 :TUT_MISSION_1_FAIL
 00BA: show_text_styled GXT 'M_FAIL' time 5000 style 1  // ~r~MISSION FAILED!
 // Hier kun je alles kwijt wat moet gebeuren als de missie gefaald is. Bv. bepaalde dingen weer terugdraaien
 0555: remove_weapon 31 from_actor $PLAYER_ACTOR 
 return 
 
 //-------------------------------------
 :TUT_MISSION_1_CLEANUP
 $ONMISSION = 0 
 // Hier komt alle code om  dingen op te ruimen die voor de missie zijn geladen
 // En om speciale settings voor deze missie terug te zetten naar normaal 
 Marker.Disable($victim_marker)
 010D: set_player $PLAYER_CHAR wanted_level_to 0 
 model.Destroy(#BFYST)
 model.Destroy(#BMYST)
 model.Destroy(#M4)
 mission_cleanup 
 return 
 
 
 
 //-------------External script 0---------------
 // put your external scripts here
 

Het eerste deel van de code, is een standaard stuk dat iedereen wel zal kennen van de stripped SCM file. Het enige dat daaraan is toegevoegd, is het starten van de snifferthread:

 00D7: create_thread @TUT_MISSIONS_SNIFFER

En ten slotte laten we de main-thread rondjes draaien in :MAIN_LOOP

Daarna volgt de sniffer-thread. Gebaseerd op de template, maar beperkt tot maar 1 missie.

Je ziet dat we twee missies hebben gedefineerd, de eerste (Missie 0) gebruiken we net als rockstar voor het initialiseren van allerlei dingen. Hier zou je ook car_generators, en b.v. wapen pickups kunnen plaatsen. Wij zetten hier de $TUT_MISSIONS_PASSED op 0, en plaatsen de locatie van de mission trigger in 3 variabelen, zodat we ze makkelijk kunnen gebruiken op verschillende plekken.

De tweede missie (Missie1) is volgens de template opgezet. In het main deel worden eerst de modellen geladen, actors gespawned, wapens toegekend en een auto gespawned, zoals we in voorgaande tutorials al gezien hebben. Daarna wacht de code in een lus, totdat de $VICTIM wordt doodgeschoten, in dat geval loopt de code door in de mission_passed routine en is de missie dus afgelopen. Tijdens de lus wordt ook gechecked of het onschuldige mannetje nog leeft. Is dat niet meer het geval, dan springen we naar de mission_failed routine, en is de missie ook afgelopen.

In de mission passed routine, zetten we een boodschap op het scherm, geven we de speler het beloning-bedrag en hogen de $TUT_MISSIONS_PASSED op.

In de mission failed routine zetten we de boodschap op het scherm, en pakken de speler het wapen af dat hij gekregen heeft.

In de cleanup routine, ruimen we alle gebruikte modellen op en zetten het wanted-level terug op 0.