Zaczynając artykuł chciałbym stwierdzić, że w silnikach nastąpiło dość duże rozróżnienie w posiadanych funkcjach.
Ja będe opierał się na tych Tfs'owskich gdyż wydaje mi się, że jest to teraz najpopoularniejszy silnik.
Podstawowymi funkcjami o które opiera się każdy skrypt są:
Actions:
Movements:Kod:function onUse(cid, item, fromPosition, itemEx, toPosition)
cid - użytkownik
item - przedmiot używany
formPosition - pozycja na której znajduje sie użytkownik przedmiotu
itemEx - przedmiot na którym został użyty przedmiot (item)
toPosition - pozycja na której znajduje się przedmiot
Wywoływana gdy używamy przedmiotu.
Deklaracja w actions.xml:
<action itemid="itemid" script="sciezka.lua" />
<action uniqueid="uniqueid" script="sciezka.lua"/>
<action actionid="actionid" script="sciezka.lua"/>
TalkactionsKod:function onAddItem(moveitem, tileitem, position):
moveitem - przedmiot na którym 'wrzucamy' przedmiot
tileitem - kratka na którą 'wrzucamy' przedmiot
position - pozycja
Wywoływana gdy 'rzucimy' jakiś przedmiot na drugim.
Szczerze się przyznam, że onAddItem raz wychodzą raz nie więc ekspertem w nich nie jestem.
Deklaracja w movements.xml:
<movevent event="AddItem" tileitem="1" itemid="itemid" script="dough.lua"/>
function onStepIn(cid, item, position, fromPosition)
cid - użytkownik
item - przedmiot
position - pozycja
formPosition - z pozycji
Wywoływana gdy po wejściu na daną pozycje.
Deklaracja w movements.xml:
<movevent event="StepIn" itemid="itemid" script="sciezka.lua""/>
<movevent event="StepIn" uniqueid="uniqueid" script="sciezka.lua"/>
<movevent event="StepIn" actionid="actionid" script="sciezka.lua"/>
function onStepOut(cid, item, position, fromPosition)
Argumenty funkcji jak wyzej.
Wywołana po zejściu z danej pozycji.
Deklaracja w movements.xml:
<movevent event="StepOut" itemid="itemid" script="sciezka.lua""/>
<movevent event="StepOut" uniqueid="uniqueid" script="sciezka.lua"/>
<movevent event="StepOut" actionid="actionid" script="sciezka.lua"/>
function onEquip(cid, item, slot)
cid - użytkownok
item - przedmiot
slot - miejsce na które wkładamy przedmiot
Wywoływana po założeniu jakiegoś Eq
Deklaracja w movements.xml:
<movevent event="Equip" itemid="itemid" slot="slot" function="sciezka.lua"/>
function onDeEquip(cid, item, slot)
Odwrotność funckji onEquip. Argumenty te same.
Deklaracja w movements.xml:
<movevent event="DeEquip" itemid="itemid" slot="slot" function="sciezka.lua""/>
Uważam, że są to informacje podstawowe. Nie chce narazie wdrażać się w tematyke Creaturescripts gdyż wydaje mi się, że i tak każdy zacznieKod:function onSay(cid, words, param)
cid - użytkownik
words - słowo kluczowe(stałe)
param - parametr(w odróżnieniu od words jest zmienne)
Funkcja wywoływana mową
Deklaracja w talkactions.xml:
<talkaction words="słowo_kluczowe" script="sciezka.lua"/>
od talkactions ,a reszta sama przyjdzie, lecz na pewno potem rozwine o Creaturescripts ten artykuł.
Tak jak powiedziałem, mój artykuł opierać się będzie o TFS, lecz nie chciałbym zupełnie wprowadzić jedynie funkcje TFS'owskie, więc wkleje
funkcje które były dostępne w TFS 0.2. <a href = "http://rafb.net/p/a6IwIa68.html">Funkcje</a>
Podstawy jakiegokolwiek działania już mamy, więc teraz możnaby przejść do samego programowania w lua.
I.Typy zmiennych
W lua zmienne nie są tak rygorystycznie potraktowane jak w np c++, gdzie każda deklaracja poprzedzona jest typem zmiennej.
Są dwa rodzaje zmiennych:
lokalne - 'local'
globalne - najczęściej zapisywane w global.lua lub w lib/constant.lua
Zmienne mogą przyjmować takie wartości jak:
boolen - wartość logiczna zwracana wartość to true/false
integer - wartość liczbowa zwraca poprostu liczbe
tablicowa - zbiór elementów, niekoniecznie jednej maści
Jak deklarujemy zmienne:
Możliwe jest również sprawdzenie typu zmiennej. W tym wypadku używamy funkcji:Kod:local logiczna = false ---boolen
local liczba = 15 -- integer
local tablica = {15, 20, 16, 50} -- tablicowa
local str = "String"
local str2 = 'String'
-- laczenie (konkatenacja) lancuchow - nie mozna uzywac +!
local osoba = "Tato"
local str3 = "Witaj" .. osoba .. "!"
-- str3 => "Witaj Tato!"
Kod:type(nazwa_zmiennej)
II.Operatory arytmetyczneKod:local n = 12
local s = 't'
local t = { 1, 2, 3 }
local x = true
doPlayerSendTextMessage( cid, MSG_INFO_DESCR,
type( n ) .. ' ' .. type( s ) .. ' ' .. type( t ) .. ' ' .. type( x ) )
-- wypisze graczowi => number string table boolean
Operatory arytmetyczne to nic prosteszego tylko znane Nam z matematyki znaki. Nie będe się tutaj rozwodził na ich temat bo znacie na pewno je doskonale
ze szkoły. Przejdź od razu do ich działania w lua.
Uważam, że to są to najważniejsze operatory arytmetyczne. Niech nie zwiedzie Was to że to matematyka itd, bo dobre ich opanowanie to klucz do dobregoKod:== Znak przyrównania
= Znak równości, nadaje wartość zmiennej
+ Znak dodawania
- Znak odejmowania
~= Znak oznaczający że coś jest różne od
> Znak większości
< Znak mniejszości
<= Znak mniejszy lub równy
>= Znak większy lub równy
programowania.
III.Instrukcje warunkowe
Instrukcje warunkowe to bardzo proste instrukcje sprawdzające najczęściej czy lewa strona spełnia nierówność z prawą stroną. To tego będą potrzebne
nam operatory arytmetyczne omówione z poprzednim rozdziale. Wygląda ona tak:
Kod:if cos operator_aryt cos2 then
--kod
elseif cos operator_aryt cos2 then
--kod
else
--kod
end
Są też zagnieżdżone instrukcje warunkowe które w lua są dość często spotykane tzn.:Kod:Czyli np.:
if 4 == 5 then
--Warunek nie spełniony przechodzimy dalej.
elseif 4 == 4 then
--Warunek spełniony, tutaj będzie pracować nasz skrypt
else
--Jeżeli oba powyższe warunki nie byłyby spełnione wykonałoby sie to
end -- Każda instrukcje kończymy end'em.
W takim wypadku else zawsze nalezy do najblizszego if'a.Kod:if 4 ~= 5 then
if 8 == 8 then
--kod
else
--kod
end
else
--kod
end
W instrukcjach warunkowych moga rowniez wystapic zmienne np.:
W tym wypadku porownujemy dwie zmienne.Do ktorych przypisane są wartości. W przypadku zmiennych logicznych sprawdzana jest zgodnośćKod:local cos = 10
local cos1 = 15
if cos == cos1 then
elseif cos > cos1 then
else
end
wartości logicznych.
IV.Troche wiecej o tablicach
[code]
local tablica = {15, 20, 16, 50}
[code]
Jest to znany Nam przyklad z poprzedniego paragrafu. Jest to tablica 4-elementowa. W lua nietypowym jest numerowanie elementów tablicy.
Przebiega ona od numeru 1, a nie jak większośći języków programowania od 0.
Chcą 'wyciągnąć' wartość 20 z naszej tablicy odnosimy sie poprostu w ten sposób
Tablice używane są najczęściej do optymalizacji kodu, w czym pomagają pętle, o których zaraz powiemy.Kod:tablica[2]
Istnieje również typ tablic w których sami możemy decydować o ich indeksowaniu.
Teraz prawdopodobnie wydaje Ci się 'Ledwo co rozumiem zwykłe tablice, a teraz te o jeszcze innych zasadach panujących'.
Spokojnie, wrócisz do tego na pewno gdy zwykłe tablice będą Ci ciążyły ;)
Nazwa tych tablic, to tablice asocjacyjne.
Zacznijmy od przykładu:
Jak widzisz w pierwszych trzech elemenetach indeksowanie jest standardowe, ale element numer 4.Kod:local tab = { 1, 2, 3, foo = 'bar' }
-- tab[1] == 1
-- tab[2] == 2
-- tab[3] == 3
-- tab.foo == 'bar'
W tym wypadku indeksem tablicy jest nie liczba [4], ale 'foo'.
To nie wszystko, bo możemy również stworzyć tablicę w tablicy:
Chcą wyciągnąć wartość 5, z tablicy o indeksie [2134], stosuje coś takiego:Kod:local tab =
[2134] = {4, 8, 3, 5}
[2135] = {4, 8, 3, 5}
[2136] = {4, 8, 3, 5]
V.PętleKod:tab[2134][4]
Pętle są to swoiste funkcje które powtarzają kod w określonej liczbie razy. Właście w pętli możemy skorzystać z dobrodziejstw niesionych przez
tablica. No to zacznijmy od kodu, to rozjaśni sytuacje.
Mamy dwa kody, pierwszy jest bez użycia pętli:
Coś dużo takich samych instrukcji wyszło, co nie?Kod:function onUse(cid, item, fromPosition, itemEx, toPosition)
if item.uid == 1234 then
doPlayerAddItem(cid, 4567, 1)
doPlayerAddItem(cid, 4568, 1)
doPlayerAddItem(cid, 4569, 1)
doPlayerAddItem(cid, 4570, 1)
doPlayerAddItem(cid, 4571, 1)
doPlayerAddItem(cid, 4572, 1)
doPlayerAddItem(cid, 4576, 1)
doPlayerAddItem(cid, 4574, 1)
doPlayerAddItem(cid, 4579, 1)
end
end
Spójrzmy teraz na kod z użyciem pętli:
Czy nie wygląda to estetyczniej i czytelniej?Kod:function onUse(cid, item, fromPosition, itemEx, toPosition)
local tablica = {4567, 4568, 4569, 4570, 4571, 4572, 4576, 4574, 4579}
if item.uid == 1234 then
for i = 1, #tablica do
doPlayerAddItem(cid, tablica[i], 1)
end
end
end
Teraz przejdźmy do samej budowy pętli:
Ja w swojej pętli użyłem znaczka '#' jest on niczym innym jak operatorem pobierającym największy element tablicy co w wypadkuKod:for (wartosc liczbowa(bardzo czesto i = 1), do jakiej wartości liczbowej i ma zapętlać) do
ciało_pętli
end
pokazanej wyżej tablicy to 9.
Ciekawie tez wyglada sama funkcja:
W tym wypadku poprostu pobiera element tablicy, ponieważ zmienna jaka jest i, przyjmuje dla kazdego zapetlenie wartosc wieksza o 1.Kod:doPlayerAddItem(cid, tablica[i], 1)
Istnieją również pętle które wspomagają użycie tablic asocjacyjnych podanych jak przykład pierwszy, wróć proszę do nich
czytając tą część poradnika o pętlach ;)
Pierwszy rodzaj pętli:
Drugi rodzaj pętli:Kod:for element in tablica do
-- w tej petli przy kazdej iteracji (obiegu) zmienna element bedzie ustawiana na nastepny element tablicy.
end
No i oczywiście przyda się przykład, który jest zaczerpnięty z silnika DeadTouch:Kod:for klucz, element in pairs( tablica ) do
-- druga wersja - w tym wypadku zmienna klucz bedzie ustawiona na klucz do wartosci, a element na wartość. Przykladowo dla tablicy:
-- tab = { 12, 23, 36, x = 'y' }
-- zmienne beda przyjmowaly nastepujace wartosci:
-- klucz = 1, element = 12
-- klucz = 2, element = 23
-- klucz = 3, element = 36
-- klucz = 'x', element = 'y'
end
VI.FunkcjeKod:local itemArr =
{
item.itemid,
item.actionid,
item.uid,
pos.x,
pos.y,
pos.z
}
local itemArrNames = { "ID", "AID", "UID", "X", "Y", "Z" }
for i, v in pairs(itemArr) do
if type(v) ~= "table" then
if v ~= 0 and v < 70000 then
msg = msg .. "[" .. itemArrNames[i] .. ": " .. v .. "]"
end
else
msg = msg .. "[" .. itemArrNames[i] .. ": " .. implode(v, "/") .. "]"
end
end
Pisząc ten artykuł, użyłem w nim pare razy funkcji, nie informując Cię Drogi Czytelniku o tym, za co serdecznie przepraszam.
Teraz poświęce ten podrozdział tylko funckjom, co myślę zrekompensuje moje wcześniejsze niedbalstwo.
Zacznijmy od prostego przykładu, funkcji:
Tak wygląda bez 'dopasowania' do danej sytuacji argumentów, a tak gdy mamy konkretny przykład:Kod:doPlayerAddItem(cid, item_id, ilosc)
Zacznijmy od tego, że funkcja to zbite w jakieś konkretne informacje, działanie który ma określony cel, czyliKod:doPlayerAddItem(cid, 4567, 1)
na przykład funkcja wyżej podana ma dać nam item.Abyśmy mogli decydować jaki to item i ile tych itemów potrzebne nam
są argumenty funkcji.
To teraz spróbujmy napisać własną funkcję:Kod:doPlayerAddItem(cid, item_id, ilosc)
Chcemy, aby ta funkcja dawała graczowi item o id = reward , o ilosc = reward_number oraz zmieniała storage value = storage_valueKod:function onDoQuest(cid, storage_value, reward, reward_number)
Cid tutaj oznacza osobę wykonująca tzn gracza.
Musimy się zastanowić, co zrobić, aby nasza funkcja była jak najbardziej uniwersalną, aby podczas używania jej w skrypcie
nie trzebaby dodawać za dużo instrukcji.
1. Na pewno musimy sprawdzić czy gracz już czasem nie zrobił danego questa
2. Musimy sprawdzić czy gracz ma tyle wolnego capa.
3. Jeżeli spełnia te warunki no to musimy mu dać item.
4. Miło też by było gdyby wiedział co dostaje, więc wyświetlamy nazwę itemu.
5. Trzeba również się zabezpieczyć przed kolejnym wykonaniem tego quest'a.
6. Jeżeli nie spełnił dwóch pierwszych warunków, trzeba go o tym poinformować.
Jeżeli już wszystko obmyśliliśmy i mamy pewność, jak nasza funkcja ma działać, to bierzemy się za jej pisanie.
W moim wypadku wygląda to tak:
Widzicie zapewne w przed ostatniej linijce 'return TRUE', to jest tzw zwracanie wartości, w tej funckji akurat zwraca 1, ponieważ(dziękuje za przypomnienie Killa) w tym wypadku w global.lua TRUE = 1, FALSE = 0.Kod:function onDoQuest(cid, storage_value, reward, reward_number)
local freeCap = getItemWeight(reward, reward_number)
local itemName = getItemNameById(reward)
if getPlayerStorageValue(cid, storage_value) < 1 then
if getPlayerFreeCap(cid) >= freeCap then
doPlayerAddItem(cid, reward, reward_number)
setPlayerStorageValue(cid, storage_value, 1)
doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE, "You have found "..reward_number..""..itemName..".")
else
doPlayerSendCancel(cid, "You don/'t have enough capacity.")
end
else
doPlayerSendCancel(cid, "It is empty.")
end
return TRUE
end
Może również zwracać:
VII.EventyKod:return 10
local zmienna = 40
return zmienna
return FALSE --return 0
local table = {}
return table
Ostatnim rozdziałem będą eventy. Są to funkcje wywołujące funkcje po określonym czasie.
Na przykład mamy funkcję zabierzGraczowiPunktyŻycia(ilość_pkt_życia).
Chcemy żeby ta funkcja zadziałała no, ale np po 10 sekundach. Nic prostszego!
Z pomocą przychodzą nam właście Eventy.
Wyglądają one tak:
Umieściłem go w zmiennej ciekawyEvent, ponieważ przyda nam się to aby można zastopować Event w razie jakiejś ewentualności.Kod:ciekawyEvent = addEvent(funkcja_uzywana, czas_w_milisekundach, argumenty_funkc_używanej)
Wyglądałoby to tak:
Wywołując nasz konkretny Event będzie to wyglądało tak:Kod:stopEvent(ciekawyEvent)
-------------------------------------------------------------------------------------------------------------------------------Kod:addEvent(zabierzGraczowiPunktyŻycia, 10 * 1000, {10})
Dziękuje bardzo Killavusowi za wzbogacenie tego artykułu paroma cennymi informacjami(pętle, zmienna string, motywacja do napisania o tablicach asocjacyjnych, jak i rozpoczęcie tego wątku.)
Artykuł pogłębiłem o pare informacji jak i dodałem dział o Funkcjach oraz o Eventach.
Jeżeli widzicie jakieś błędy lub macie jakieś uwagi proszę zgłaszać, proszę również o informacje o czym jeszcze
chcielibyście poczytać.
Pozdrawiam,
Tairens