Skrypty SCM i CLEO we wszystkich GTA trzeciej ery (GTA III, GTA VC, GTA SA, a także niezbyt modowalne GTA LCS i GTA VCS) opierają się wyłącznie na opcodach. Część z nich pozwala tworzyć struktury, pętle i różne sprawdzenia. Sanny Builder posiada wbudowane kilka komend, które imitują struktury języków wysokopoziomowych (dlatego też nazywane są one
konstrukcjami wysokiego poziomu, lub z angielskiego
high-level constructs), oraz przy kompilacji zostają zamienione na zwykłe opcody.
Są dwa typy tej konstrukcji. Jedna z nich,
while true powtarza akcję w nieskończoność:
Kod:
while true
wait 1000
0109: player
$PLAYER_CHAR money += 1000
end
Taka pętla po prostu dodaje graczowi 1000 dolarów co sekundę (1000 milisekund). Przy kompilacji konstrukcja jest zamieniana na:
Kod:
:LABEL
0001: wait 1000 ms
0109: player
$PLAYER_CHAR money += 1000
0002: jump @LABEL
Drugi typ tej konstrukcji to
while [warunek]. Jest ona wykonywana w czasie, gdy dany warunek zwraca wartość
true.
Kod:
while 0AB0: key_pressed 0x9
0001: wait 1000 ms
0109: player
$PLAYER_CHAR money += 1000
end
Analogicznie do pierwszej konstrukcji, ta dodaje graczowi 1000$ co sekundę, ale tylko w momencie, gdy wciska on klawisz
Tab. Jeśli gracz zwolni przycisk, gra wychodzi z pętli i odpala dalsze partie kodu. Przy kompilacji konstrukcja jest zamieniana na:
Kod:
:LABEL
0AB0: key_pressed 0x9
004D: jump_if_false
@LABEL_2
0001: wait 1000 ms
0109: player
$PLAYER_CHAR money += 1000
0002: jump @LABEL
:LABEL_2
Nie, brak
if nie jest tu błędem, gra działa bez tego normalnie.
Co jednak mamy zrobić w wypadku, gdy chcemy wyjść z któreś z tych pętli? Sanny Builder obsługuje jeszcze dwa kody -
continue oraz
break. Ten pierwszy przeskakuje na sam koniec pętli:
Kod:
while true
wait 1000
0AB0: key_pressed 0x9
else_jump continue
0109: player
$PLAYER_CHAR money += 1000
end
Kompiluje się to jako:
Kod:
:LABEL
0001: wait 1000 ms
0AB0: key_pressed 0x9
004D: jump_if_false
@LABEL_2
0109: player
$PLAYER_CHAR money += 1000
:LABEL_2
0002: jump @LABEL
break za to po prostu wyskakuje z pętli, tak samo jak
while [warunek]:
Kod:
while true
wait 1000
0AB0: key_pressed 0x9
else_jump break
0109: player
$PLAYER_CHAR money += 1000
end
-->
Kod:
:LABEL
0001: wait 1000 ms
0AB0: key_pressed 0x9
004D: jump_if_false
@LABEL_2
0109: player
$PLAYER_CHAR money += 1000
0002: jump @LABEL
:LABEL_2
Nic nie stoi na przeszkodzie, aby napisać ręczny skok do innego nagłówka w środku tej pętli (oraz każdej innej pętli wysokiego poziomu).
Ta pętla jest już bardziej zaawansowana i ma więcej typów. Prościej w tym wypadku jest objaśnić ją na przykładzie z main.scm:
Kod:
for 6@ = 0
to 2 step 1
092B: 8@ = group
$PLAYER_GROUP member
6@
01C2: remove_references_to_actor
8@
end
Kompiluje się to jako:
Kod:
0006: 6@ = 0
:LABEL
092B: 8@ = group
$PLAYER_GROUP member
6@
01C2: remove_references_to_actor
8@
000A: 6@ += 1
0019: 6@ > 2
004D: jump_if_false
@LABEL
Po kolei:
- 6@ - zmienna, która ma pełnić funkcję licznika pętli
- 0 - początkowa wartość licznika. Może to być inna zmienna.
- to - oznacza, że pętla ma się zwiększać o określona wartość. Zamiana tego na downto spowoduje odejmowanie wartości, zamiast jej dodawania
- 2 - końcowa wartość licznika. Może to być inna zmienna.
- step 1 - określenie, o ile ma się zwiększać pętla przy każdej iteracji. Domyślnie 1 - wtedy ręczne określenie tego nie jest konieczne
Ta pętla obsługuje też operacje na liczbach zmiennoprzecinkowych. Ponadto, tak samo jak w poprzedniej pętli, można używać w niej komend
continue (przeskakuje na koniec pętli, zwiększając licznik) i
break (wyskakuje z pętli):
Kod:
for 0@ = 0
to 15
0109: player
$PLAYER_CHAR money += 1000
if
010A: player
$PLAYER_CHAR money > 10000
then
break
end
end
->
Kod:
0006: 0@ = 0
:LABEL
0109: player
$PLAYER_CHAR money += 1000
00D6: if
010A: player
$PLAYER_CHAR money > 10000
004D: jump_if_false
@LABEL_2
0002: jump @LABEL_3
:LABEL_2
000A: 0@ += 1
0019: 0@ > 15
004D: jump_if_false
@LABEL
:LABEL_3
Działa ona dość podobnie do pętli
while. Tak samo jak ta pierwsza, obsługuje dwa typy. Pierwszy to
repeat...until false, który powtarza operację w nieskończoność:
Kod:
repeat
wait 1000
0109: player
$PLAYER_CHAR money += 1000
until false
Kompiluje się to tak samo jak pętla
while true.
Drugi typ to pętla
repeat...until [warunek]. Jak sama nazwa wskazuje,
powtarza ona działanie...do czasu aż [warunek zwróci Prawda].
Kod:
repeat
wait 1000
0109: player
$PLAYER_CHAR money += 1000
until 8AB0: not key_pressed 0x9
Taka pętla wykonuje działanie do czasu aż gracz zwolni przycisk Tab. Kompiluje się ona jako:
Kod:
:LABEL
0001: wait 1000 ms
0109: player
$PLAYER_CHAR money += 1000
8AB0: not key_pressed 0x9
004D: jump_if_false
@LABEL
Podsumowując - konstrukcje wysokiego poziomu to rzecz bardzo przydatna. Wymagają one przyzwyczajenia, ale dzięki nim skrypt jest dużo czytelniejszy, często też wydajniejszy, a sam mod pisze się krócej (w moim przypadku).