Jump to content

Sposób edytowania skryptu bez wyjścia z gry


Recommended Posts

Sposób edytowania skryptu bez wyjścia z gry.

Jest to małe powtórzenie jednego bardzo przydatnego, ale z jakiejś przyczyny mało znanej lekcji. Sposób w większym stopniu dla tych twórców modów, które umią grzebać w skryptach gry i mogą powiedzieć, czym różni się funkcja od metody.

Nie jest tajemnicą, że skrypty są często debugowane pod kątem błędów, algorytm jest skandalicznie prosty:
1. Piszemy skrypt.
2. Włączamy grę.
3. Łapiemy wylot.
4. Patrzymy do loga i naprawiamy skrypt.
5. Powtarzamy punkt 2 - 4, aż wyloty przestaną się pojawiać.
Ponadto skrypt jest często wywoływany nie bezpośrednio, ale poprzez jakąś akcję w grze - poprzez dialog, podnoszenie/używanie przedmiotu, itp.
Czysty masochizm. "Ale co robić?" - powiecie, - "Jeśli tak została zaprojektowana gra". Rozwiąż problem, a raczej dwa!
Pierwszym problemem są „niekończące się” wyloty.
Drugim problemem jest pewna akcja wywołania skryptu w celu sprawdzenia wydajności.

 

PIERWSZY PROBLEM:

Spoiler

Pierwszy problem można rozwiązać przy pomocy dwóch standardowych funkcji języka Lua, a dokładnie dofile i pcall.

Więcej o funkcjach:

Spoiler

dofile

resultChunk = dofile("C:\\Study\\My.script")

Otwiera określony plik i wykonuje jego zawartość jako chunk. Zwraca wynik wykonania lub zgłasza błąd.

Chunk - w tłumaczeniu z angielskiego, porcja lub blok. W Lua to porcja - dowolna sekwencja instrukcji (последовательность операторов) Lua.

Tutaj ważne jest, aby zrozumieć co to jest sekwencja instrukcji ("последовательность операторов"). function - to także porcja lub innymi słowy blok instrukcji. Takim sposobem, wykonując przy pomocy dofile jakiś skrypt, w pliku należy pisać dokładnie zawartość bloku, który trzeba wykonać.

 

pcall

pcall (funcName, arg1, ...)

Wywołuje funkcję funcName z danymi parametrami (arg1, i dalej, oddzielone przecinkami potrzebne parametry dla funkcji funcName) w tak nazywanym trybie chronionym (protected mode).
To znaczy, że przy wystąpieniu jakiegoś błędu wewnątrz funkcji funcName, nie zostanie on przekazany; natomiast, pcall przechwytuje błąd i zwraca stan. pcall zwraca najpierw stan (typ boolean), który jest równy true jeśli wywołanie zakończyło się bez błędów, i wszystkie efekty od pracy chronionej przez niego funkcji ?. W przypadku wystąpienia błędu, pcall zwraca false i wiadomość o błędzie.
Przykład:

Mamy prostą funkcję zwracającą sumę dwóch liczb:

function Addition (a, b)
      return a + b
end

Jeśli wywołamy funkcję z parametrem b różnym od liczby (typ number😞

sum = Addition(13, true)

to wystąpi błąd o następującej treści:

attempt to perform arithmetic on local 'b' (a boolean value)

Jeśli użyjemy funkcji pcall, to efekt będzie zupełnie inny:

status, result = pcall(Addition, 13, true)

Funkcja pcall przyjmuje jako pierwszy argument nazwę funkcji, a jako kolejne argumenty - argumenty wywołanej funkcji.
Zwraca dwie wartości. Pierwszą - efekt wykonania, stan określający czy wystąpił błąd (false - jeśli się pojawił, true - jeśli nie).
Drugą - efekt wykonania funkcji, jeśli błąd nie wystąpił lub wiadomość o błędzie, jeśli wewnątrz funkcji pojawił się błąd.
W przykładzie ze zmienną status będzie wartość false, a w zmiennej result - linijka "attempt to perform arithmetic on local 'b' (a boolean value)".
Przy przekazaniu prawidłowych parametrów, które nie wywołają błędu, w zmiennej status będzie wartość true, a w result - suma dwóch liczb, przekazanych jako te same parametry.

W obydwu przypadkach błędu wykonania, a co za tym idzie nie będzie wylotu w kontekście gry!

Łącząc te dwie funkcje można uzyskać niezwykle użyteczną konstrukcję, która pozwoli na wykrywanie błędów bez doprowadzania do wylotów gry oraz łatwe wyświetlanie opisu błędu bezpośrednio w logu.
Aby to zrobić, stwórzmy dla siebie funkcję, w której pcall wywoła funkcję dofile, która z kolei wykona plik z potrzebnym nam skryptem. Ta funkcja będzie wyglądać następująco:

function ProtectedFunction()
      local status, result = pcall(dofile, [[..\gamedata\scripts\__test.script]])
      get_console():execute(status and "Successful!" or string.gsub(result, " ", "_"))
end

Wszystko jest bardzo proste. Wywołujemy pcall, jako parametr przekazujemy funkcję dofile, a jej argumentem jest ścieżka do naszego pliku __test.script, który najpierw musi zostać utworzony w folderze scripts.

Zostało ustalone, że w SoC, Lua traktuje bin jako katalog główny, dlatego zapisujemy ścieżkę względem niej. W CS i CoP to folder główny zainstalowanej gry, dlatego ścieżka dla tych gier będzie taka: "[[gamedata\scripts\__test.script]]"

Druga linia drukuje komunikat „Successful!” na konsoli, jeśli skrypt w pliku _test.script został wykonany bez błędów lub komunikat o błędzie w czasie wykonywania, jeśli wystąpił błąd. Komunikat jest wyświetlany z podkreśleniem zamiast spacji. Jest to niezbędna miara dla metody execute.

Nie ma sensu wyprowadzać wyniku do konsoli, ponieważ sama gra pokaże nam wynik.
Teraz, jeśli wywołasz funkcję ProtectedFunction, zostanie wykonany kod zapisany w pliku __test.script. Jednocześnie sam kod w tym pliku można zmienić bez wychodzenia z gry, po prostu go minimalizując.

Dlatego po otrzymaniu błędu w wywoływanym pliku w konsoli pojawi się komunikat z opisem tego błędu. Dalej: zminimalizuj grę; popraw błąd; przywracacie zminimalizowaną grę; uruchamiacie ponownie wywołanie funkcji ProtectedFunction; patrzycie na wyniki poprawionego skryptu.

 

Warto zwrócić uwagę na fakt, że blok instrukcji musi znajdować się w pliku __test.script!

Np.:

Spoiler

Zawartość pliku może być taka:

function MyFunction ()
      db.actor.health = 1
end

Podczas wywoływania ProtectedFunction nie wystąpi żaden błąd, ponieważ go tam nie ma, ale nic nie zostanie dodane do zdrowia aktora, tak jak byśmy tego chcieli. Powodem jest to, że ten kod opisuje tylko funkcję, ale nie ma wywołania tej funkcji w pliku. Dlatego funkcję tę należy wywołać w tym samym miejscu, w pliku:

function MyFunction ()
      db.actor.health = 1
end
MyFunction()

Ostatnia linia to wywołanie funkcji. Wywołanie funkcji jest w pewnym sensie instrukcją. Z tą zawartością pliku funkcja ProtectedFunction zostanie wykonana bez błędów i zobaczysz wynik wykonania, w postaci uzupełnienia zdrowia GG do maksymalnej wartości.

W trakcie gry, nie wychodząc z niej, można edytować plik __test.script, np. zmieniając właściwość health na power, dzięki czemu kolejne wywołanie chronionej funkcji uzupełni nie tyle zdrowie, co wytrzymałość.

Ważne jest, aby zrozumieć, że pcall przechwytuje tylko błędy wywoływanej przez siebie funkcji. W naszym przypadku dofile, który z kolei wywołuje nasz plik __test.script. Dlatego jeśli w tym pliku znajduje się wywołanie funkcji z innego pliku i już w nim występuje błąd (w innym pliku), to gra zawiesi się z logiem błędów innego pliku. To samo dotyczy działania funkcji silnika; jeśli podasz niepoprawny argument, niepoprawnie wywołasz jakąś funkcję wyeksportowaną do skryptów z silnika, gra się zawiesi.
W większości przypadków błędy wykonania skryptu są dozwolone, więc taki projekt pozwala uniknąć dużej liczby restartów gry, co pozwola zaoszczędzić zarówno czas, jak i nerwy.

 

DRUGI PROBLEM:

Spoiler

Jeśli chodzi o drugi problem, najskuteczniejszym rozwiązaniem byłoby po prostu naciśnięcie klawisza na klawiaturze, tzn. własny skrót klawiszowy.
Jako najlepszą skryptową metodę skrótów klawiszowych warto użyć metody podanej tutaj: link do artykułu, szczególnie ciekawe jest akcja po naciśnięciu (pierwszy sposób)

Działanie skrótów klawiszowych:

Spoiler

Kluczową częścią w tym sposobie jest polecenie konsoli bind_cosole. Polecenie pozwala przypisać wykonanie innego polecenia konsoli do dowolnego klawisza na klawiaturze. Jego składnia wygląda następująco:
bind_cosole <nazwa polecenia konsoli> <wartość dla polecenia> <kod klucza>

Np.:

bind_cosole save 1 knumpad1

Tak więc po naciśnięciu jedynki na klawiaturze numerycznej w konsoli wywołane zostanie polecenie "save 1”, co spowoduje zapisanie gry pod nazwą "1”.

Istnieją polecenia konsoli, które przechowują przekazaną wartość, na przykład "mm_net_player_name”. Polecenie zmienia/zwraca nazwę gracza w trybie multiplayer, więc nie jest używane w trybie dla jednego gracza i dlatego autor go wybrał.

W skryptach możliwe jest uzyskanie wartości poleceń konsoli za pomocą metod obiektu konsoli CConsole; w grze taki obiekt można uzyskać za pomocą funkcji globalnej przestrzeni nazw get_console.

Taki kod:

local mmPlayrName = get_console():get_string("mm_net_player_name")

Zapisuje nazwę gracza w grze wieloosobowej do zmiennej.

Taki tandem z komendy bind_cosole i metody get_string, pozwala przy naciśnięciu klawisza, ustawić dla komendy "mm_net_player_name" wartość nazwy funkcji i we właściwym czasie pobrać nazwę tej funkcji żeby ją wywołać.
To jest cała zasada klawiszy skrótu tego sposobu.
Początkowo przeglądamy plik w poszukiwaniu funkcji o nazwie zgodnej z wyliczeniem nazw klasy DIK_keys, zapisanych tylko małymi literami. Przypisujemy komendę konsoli "mm_net_player_name <nazwa funkcji>" do klawisza odpowiadającego tej nazwie. Tym samym po naciśnięciu takiego klawisza do tego parametru zostanie przypisana nazwa funkcji. Wartość tego polecenia przechwytujemy metodą get_string - jeżeli jest to nazwa funkcji znajdującej się w bieżącym pliku, to ją wywołamy i od razu przypiszemy komendzie wartość domyślną, aby funkcja nie była wywoływana w przyszłości. Po ponownym naciśnięciu klawisza, komendzie ponownie zostanie przypisana wartość z nazwą funkcji, która zostanie przechwycona przy następnej aktualizacji. I tak w kółko.

Przed rozpoczęciem gry musisz zdecydować, którego z nieużywanych klawiszy użyjesz jako "klawisza skrótu”.

Listę klawiszy w Stalkerze, których można do tego użyć, można znaleźć tutaj: DIK_keys. Jest to wyliczenie stałych klasy DIK_keys, których nazwy są używane jako nazwy funkcji dla klawiszy skrótu.

Wybieramy klawisz Scroll Lock, aby jakoś potwierdzić jego obecność na klawiaturze. Kodem tego klawisza jest dik_scroll, czyli dokładnie to, co powinna wywołać funkcja, którą wybraliśmy.

Nazwy funkcji, które będą bezpośrednio wywoływać twoje skróty klawiszowe, muszą być pisane małymi literami.

Teraz wszystko, co pozostaje do zrobienia, to napisać funkcję z nazwą dik_scroll, w pliku zawierającym funkcję update skrótów klawiszowych. Będzie wyglądać to tak:

function dik_scroll()
      local status, result = pcall(dofile, [[..\gamedata\scripts\__test.script]])
      get_console():execute(status and "Successful!" or string.gsub(result, " ", "_"))
end

Jak widać, w ciele funkcji została napisana zawartość "chronionej funkcji" i nie została wywołana osobno - po co niepotrzebnie tworzyć kod jeszcze raz?

 

Teraz, wchodząc do gry i wczytując zapis (lub rozpoczynając ją od nowa), możesz być ciekawy pracy. Naciśnij Scroll Lock (lub inny wybrany klawisz) i zobacz zawartość uruchomionego pliku __test.script. Jeśli w pliku nie było błędów, w konsoli zobaczysz napis „Succesful!” lub komunikat o błędzie. Spróbuj bez wychodzenia z gry, zminimalizować ją za pomocą Alt + Tab, zmienić zawartość pliku, a następnie ponownie naciśnąć klawisz skrótu w przywróconej zminimalizowanej grze - jak sam widzisz, wynik będzie oparty na zmienionych danych.

GOTOWA MINI-GAMEDATA:

Wszystko, co musisz zrobić, to umieścić dwa skrypty w folderze scripts i dodać w aktualizacji/update aktora następującą linijkę:

bind_keys.update()

Wejdź do gry i naciśnij na klawisz Scroll Lock.

 

 

SYNTAX CHECKER OD GUN12:

 

ŹRÓDŁO:

https://ap-pro.ru/forums/topic/1285-sposob-redaktirovaniya-skripta-bez-vyhoda-iz-igry/

  • Positive 1
Link to comment
Share on other sites

  • Wojownik changed the title to Sposób edytowania skryptu bez wyjścia z gry

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.

Comunity