open.mp API design
Uvod
Kao prvo, veoma važna klarifikacija - postojeće skripte će i dalje raditi kao što jesu. Naporno smo radili na kompatibilnosti unatrag i imamo to na umu pri svakoj odluci. Postoje mnoga poboljšanja koja bismo željeli napraviti koja jednostavno ne možemo iz ovog razloga, kao i drugi kod koji je uvelike komplikovan ovim zahtjevom kompatibilnosti.
Ipak, postoje poboljšanja koja se mogu napraviti svuda. Pogledajmo neke primjere nedosljednosti u SA:MP skriptiranju:
Tagovi
Menu:CreateMenu
- tag.DB:db_open
- tag.CreateVehicle
- bez taga.CreateActor
- bez taga.
#define SELECT_OBJECT_GLOBAL_OBJECT 1
#define SELECT_OBJECT_PLAYER_OBJECT 2
forward OnPlayerSelectObject(playerid, type, objectid, modelid, Float:fX, Float:fY, Float:fZ);
type
is untagged, as are ALL SA:MP defined constants; unlike pawn default constants:
native File:fopen(const name[], filemode:mode = io_readwrite);
Imenovanje
SetVehiclePos
- "Vehicle" (vozilo) u sredini imena funkcije.TextDrawTextSize
- "TextDraw" na početku.db_open
- "db" (databaza) na početku, malim slovima.fread
- "file" (datoteka) na početku, i skraćeno (izfile
uf
).asin
- A SA:MP added function without camel/pascal case.
Konzistencije:
GetVehicleZAngle
- "Z-Angle" (Z-Ugao)GetVehicleRotationQuat
- "Rotation" (rotacija)SetPlayerFacingAngle
- "Facing Angle" (gledani ugao)SetObjectRot
- "Rot" (rotacija, ali skraćeno - izRotation
uRot
)
I uprkos svemu tome, većina biblioteka je sada prihvatila konvenciju imenovanja Module_Method
.
Konstantne vrijednosti
65535
Vrijednost koja predstavlja nevažeće ID-ove igrača, aktora, TextDraw-ova i još nekoliko stvari. Također može biti i za vozila, ali 0
je također (može biti) ID za nevažeće vozilo ponakada returnovana kao povratna vrijednost u funkcijama.
0
Vrijednost za nevažeće ili nepostojeće datoteke, ponekada i za vozila (isto kao i 65535
). Također i vrijednost za neke nedostajuće stvari kao što su akciona stanja i oružje.
255
Vrijednost za nevažeće timove i menije.
-1
Vrijednosti za nevažeće teritorije i stanja oružja.
Uz to, mnoge biblioteke koriste 0x80000000
i -1
za nevažeća stanja jer je mnogo manje vjerovatno da će na kraju biti važeći ID. 65535 je prilično velik broj, ali veliki server lako može imati više objekata od toga.
Funkcije "po igraču"
CreateObject
iCreatePlayerObject
Ima verziju posebnu za igrača i globalnu verziju.
SetPlayerMapIcon
Nema globalnu verziju.
SetGravity
Nema verziju "po igraču", uprkos tome što je to možda jedna od najtraženijih funkcija po igraču, a dodana je skoro i trenutno od strane YSF-a i drugih sličnih plugina.
CreateVehicle
Nema verziju "po igraču", uprkos tome što je isto tako još jedna od više traženih funkcija. Također, nije dodana od strane ni jednog (javnog / dostupnog) plugina, čak ni od strane streamera.
SendClientMessage
iSendClientMessageToAll
Ima globalne verzije i verzije po igraču, ali verzija po igraču je zadana za razliku od većine drugih funkcija.
GangZoneShowForPlayer
iGangZoneShowForAll
Meniji, teritoriji i TextDrawovi jedine su zadane SA:MP funkcije u kojima možete točno odrediti koji igrači ih mogu vidjeti. Svi ostali su ili svi ili samo jedna osoba. Naravno, biblioteke i dodaci su se od tada značajno proširili na ovaj model i većina njih sada omogućava vrlo finu kontrolu nad tim koji podskupovi igrača (grupe) mogu koristiti bilo koji entitet (ili komponentu).
IDevi
CreateObject
iCreatePlayerObject
Skup ID-ova za ove funkcije se dijeli. Ako globalni objekat ima ID 4
, nijedan objekt igrača nikada ne može imati ID 4
, ali više igrača može imati različite objekte sa ID-om 5
.
Create3DTextLabel
iCreatePlayer3DTextLabel
Skup ID-ova je podijeljen - prvi 1024
ID-ovi su globalni, drugi 1024
su po igraču. Svaki igrač može imati do 2048
3D tekstova, ali samo 1024
svake vrste uprkos činjenici da nema razlike na strani klijenta.
SetPlayerMapIcon
ID-ovi su ručno specificirani, do ograničenja od 32. Neko vrijeme ovo ograničenje nije provjeravano na strani klijenta, što je dovelo do potencijalnog ACE eksploatacije.
ShowPlayerDialog
ID-ovi su ručno specificirani, bez ograničenja. ID-ovi su također potpuno besmisleni jer igrač može vidjeti samo jedan dijalog odjednom.
SetTimer
ID-ovi se omotavaju, bez provjere da postojeći tajmer ima isti ID. Morali biste pokrenuti više od 4.000.000.000 tajmera u nekom trenutku da biste naišli na ovaj problem, ali može se dogoditi - čak i ne moraju svi ostati uključeni.
I naravno, neki ljudi se oslanjaju na to da se ID-ovi dodjeljuju u vrlo specifičnim redosljedima, a onda se pitaju zašto im se cijeli način prekida kada, na primjer, dodaju ili uklone jedno vozilo.
Kompatibilnost
Dakle, opet, moramo jasno staviti do znanja da će sav postojeći "SA:MP kod" raditi kao i prije. Šta to tačno znači? Bilo koji kod koji je:
- Napisan u Pawn jeziku,
- Koristi originalni SA:MP programski interfejs (bez dodataka i plugina),
- Je (re)kompajlovan sa našim inkludovima (bibliotekama),
- Već koristi brzi kompajler (poznat kao Pawn Kompajler zajednice)
... će raditi.
Kako god:
Ako koristite dodatak za pisanje na jeziku koji nije Pawn, taj plugin (dodatak) će vjerovatno morati prvo biti prenjet. Dakle, vaš kod neće raditi odmah.
Ako koristite druge dodatke kao što su streamer, YSF, audio, itd; možda već rade, možda će im trebati prijenos ili mogu biti potpuno suvišni jer je njihova funkcionalnost integrirana u osnovni server. Dakle, vaš kod može raditi.
Ako imate samo .AMX vašeg načina rada, a ne originalni izvor, zašto? U svakom slučaju, dok postoje sve SA:MP funkcije, neke su preuređene ili zamijenjene kodom ili makroima i MORATE ponovo kompajlirati. Dakle, ako ne možete, vaš kod uopće neće raditi.
Kodiranje i gradnja
Primjer
Pogledajmo na ovaj jednostavni kod sa kojim ćemo započeti:
#include <a_samp>
main() {}
public OnGameModeInit()
{
SetGameModeText("Primjer");
AddPlayerClass(0, 0.0, 0.0, 4.0, 0.0, 0, 0, 0, 0, 0, 0);
return 1;
}
public OnPlayerSpawn(playerid)
{
SetPlayerCheckpoint(playerid, 20.0, 20.0, 4.0, 2.0);
return 1;
}
public OnPlayerEnterCheckpoint(playerid)
{
SendClientMessage(playerid, 0xFF0000AA, "Pobijedili ste!");
return 1;
}
Spawnujete se u igru. Idete do markera (checkpointa). Pobjeđujete - prilično jednostavno.
Konverzija
Da biste ovo isto uradili u open.mp-u, samo moramo promjeniti a_samp.inc
i dodati jednu definiciju ispred.
Dakle - ovo:
#include <a_samp>
main() {}
Postaje ovo:
#define OPENMP_COMPAT
#include <openmp\openmp>
main() {}
Prvi error koji možete dobiti je:
open.mp scripts require the community compiler from: git.io/pawn-compiler
(Prijevod: open.mp skripte trebaju kompajler zajednice koji možete preuzeti sa: git.io/pawn-compiler
)
Ako dobijete ovo, idite na https://git.io/pawn-compiler i preuzmite verziju kompajlera 3.10.10 ili noviju. Za izdanje bismo željeli alat ekvivalentan pawno sa ovim kompajlerom već integriranim, ali to još nismo učinili. SNAŽNO predlažem da prvo pokušate kompajlirati svoj način rada s ovim kompajlerom jer je ponovo omogućio upozorenja o konstantnoj ispravnosti tako da ćete vjerovatno imati gomilu novih upozorenja odmah (ovo NIJE problem s kompajlerom, ovo su problemi u vašem kodu koji su oduvek postojali, ali su prethodno ignorisani). Također ćete vjerovatno htjeti zamijeniti svoje uključene sa ovim:
https://github.com/pawn-lang/pawn-stdlib
https://github.com/pawn-lang/samp-stdlib
To je dobra stvar za učiniti čak i ako ne koristite open.mp, jer oni popravljaju mnoštvo problema sa oznakama i constom u originalnim uključenjima.
Upozorenja
Ako ne dobijete nikakva upozorenja koristeći novi kompajler s novom verzijom a_samp, sada ćete vidjeti mnoštvo novih upozorenja duž linija:
warning 234: function is deprecated (symbol "AddPlayerClass") Use "Class_Add" instead.
(Znači: upozorenje 234: funkcija je zastarjela (simbol "AddPlayerClass") Umjesto toga koristite "Class_Add".
)
Imate tri opcije za učiniti - ali, sve će raditi:
Ignorišite upozorenja: Vaš kod će i dalje savršeno raditi.
Prigušte upozoranja: Izmjenite već pomenutu definiciju
OPENMP_COMPAT
uOPENMP_QUIET
:
#define OPENMP_QUIET
#include <openmp\openmp>
main() {}
- Jednostavno popravite upozorenja: Neke funkcije su promijenile imena radi međusobnog usklađivanja; neke funkcije su promijenile parametre jer su se stare razvile i nisu u potpunosti otkrile mogućnosti open.mp. Još ne postoji jednostavan način za konvertovanje svake funkcije, ali možete ostaviti upozorenja uključena dok polako konvertujete kod - stare funkcije će nastaviti da rade savršeno dobro.
Postoje tri faze konverzije:
#define OPENMP_QUIET
Korištenje ove definicije omogućava vašem načinu kompajliranja bez novih upozorenja od zastarjelih funkcija. Ali ne biste se trebali držati ove definicije, a implicitne konverzije će raditi samo za kod zalagaonice. Možete konvertovati kod u ovom režimu, jer će novi API takođe raditi, ali ne možete koristiti kompajler da vidite gdje ostaju problemi.
#define OPENMP_COMPAT
Ovo je druga faza. Jednom kada poželite da počnete da koristite prednosti svih poboljšanih karakteristika open.mp-a kao što su fine kontrole entiteta po igraču i uklonjena ograničenja, morate da počnete da koristite nove verzije funkcija. Nove funkcije su uvijek dostupne, ali možda ne znate svugdje gdje je potrebna konverzija. Ovo će dati upozorenja za stare funkcije, ali one će i dalje raditi, omogućavajući vam da pretvarate dijelove vašeg načina rada jedan po jedan.
Bez ikakvih definicija
Onda kada mislite da ste završili za konverzijom vašega koda, slobodno možete ukloniti definicije.
#include <openmp\openmp>
main() {}
Sada ćete dobijati errore (greške) umjesto warninga (upozorenja) za svaki stari kod koji se i dalje koristi.
Novi API
Dakle, sada smo vidjeli koji su problemi bili sa starim API-jem i kako pronaći gdje trebate primijeniti novi API, zapravo bismo trebali pogledati navedeni novi API u smislu prethodno identificiranih problema:
Tagovi (tipovi podataka)
Funkcije sada koriste oznake skoro svuda. Pokušali smo pronaći balans između previše upozorenja o oznakama i premalo korisnih informacija, ali upozorenja su tu s razlogom i mogu vam pomoći da pronađete probleme koje ste možda propustili. Na primjer, prosljeđivanje vozila kao parametra funkciji objekta ili davanje nekome oružja koje ne postoji:
// Nema upozorenja od strane kompajlera, uprkos tome da je kod potpuno pogrešan!
new object = CreateObject(various, parameters, here);
PutPlayerInVehicle(playerid, object);
// Isti slučaj ovjde - oružje ID 20 ne postoji, ali je 20 validan broj tako da nema upozorenja.
GivePlayerWeapon(playerid, 20, 200);
Bilo bi mnogo bolje kada bi svi dijelovi očigledno pogrešnog koda mogli dati upozorenja. Ovo je moć jezika koji je siguran za tipove, i iako pawn nije u potpunosti siguran za tipove, možemo se približiti oznakama. Ti primjeri postaju:
// warning 213: tag mismatch: expected tag "Vehicle", but found "Object" (Znači: očekivan tag "Vozilo", ali je iskorišten "Objekat")
new Object:object = Object@Create(various, parameters, here);
Player_PutInVehicle(playerid, object);
// warning 213: tag mismatch: expected tag "WeaponType", but found but found none ("_") (Znači: očekivan tag "Oružje", ali ni jedan nije iskorišten: "_")
GivePlayerWeapon(playerid, 20, 200);
// This doesn't give a warning:
GivePlayerWeapon(playerid, WEAPON_COLT45, 200); // Koristit ćemo konstantu "WEAPON_COLT45"
Imenovanje
Većina novih funkcija koristi preciziranje šeme imenovanja koju su već usvojile mnoge biblioteke i dodaci - Module_VerbNoun
. Neki ne, kada su jednokratni koji se ne uklapaju ni u jedan veći modul, ali uglavnom možete pogoditi naziv funkcije koja vam je potrebna. Nema više razmišljanja da li je to bilo "Rot" ili "Rotation" za ovu vrstu elementa, nema skraćenica osim ako je naziv funkcije inače predugačak (32+ znaka, kompajler ih neće prihvatiti). Želite promijeniti model objekta? Object_SetModel
. Želite pokazati tekst igraču? Text_Show
.
Postoji ograničen skup glagola - Get
, Set
, Create
, Destroy
, Add
, Remove
, Show
, Hide
, Run
, Move
, Stop
i Count
. Može se dodati još i možda se pojaviti u posebnim situacijama, ali općenito ako jedno od ovih odgovara, vjerovatno je to ono. Daleko najčešći su Get
i Set
, koji za razliku od SA:MP-a sada uvijek dolaze u paru - ako možete podesiti bilo koji parametar, možete ga dobiti i kasnije. Oni su ujedno i glagoli koji najčešće dolaze s imenicom - potrebno je navesti šta ćete dobiti ili postaviti - Health
, Position
, Model
, Width
, itd.
Nekoliko primjera:
native bool:Menu_SetDisabled(Menu:menuid, bool:disabled);
native bool:Menu_GetDisabled(Menu:menuid);
native bool:Text_SetAlignment(Text:text, alignment);
native Text_GetAlignment(Text:text);
native bool:Object_Move(Object:objectid, Float:posX, Float:posY, Float:posZ, Float:speed, Float:rotX = FLOAT_NAN, Float:rotY = FLOAT_NAN, Float:rotZ = FLOAT_NAN);
native DBResult_CountRows(DBResult:dbresult);
native Player_Spawn(Player:playerid);
Imajte na umu da se nazivi modula i oznaka uvijek podudaraju - Vehicle
, DB
, Player
itd. Postoje razlozi osim dosljednosti za ovo - daje sučelje više nalik na OO i lakše ga je zapamtiti. Međutim, možda ste primijetili u ranijem primjeru funkciju Object@Create
, a ne Object_Create
. Razlog tome je ono što se nalazi u prvom parametru.
U svih sedam primjera neposredno iznad prvog parametra nalazi se entitet (objekat, vozilo, igrač, itd.) kojim se operiše. Želite da dobijete poziciju određenog entiteta. Želite premjestiti određeni entitet. Želite dobiti preostalo vrijeme određenog entiteta. Ovo se opet preslikava na API sličan OO - Player_Spawn(playerid)
se može smatrati player.Spawn()
. PAWN to ne može učiniti, ali to ne znači da drugi jezici koji se priključe na ovaj API ne mogu. U C++ terminima - _
je .
ili ->
i uvijek treba važeći ID dat kao prvi parametar. Međutim, funkcija Dialog_IsValid(Dialog:id)
po definiciji možda nema važeći ID kao prvi parametar (ili koja je poenta toga*?), i Icon_Create(image, Float:x, Float:y, Float:z)
uopće ne uzima ličnu kartu. Oni tako postaju @
umjesto - ::
u C++ sintaksi. Možda uopće ne uzimaju ID i apsolutno im nije potreban važeći (@Destroy
također spada u ovu grupu funkcija, jer je uništavanje entiteta operacija koja je logički van entiteta, a ne operacija na entitetu ).
Konstantne vrijednosti
Za početak, umjesto #define
za sve koristimo const
i enum
koliko god je to moguće, osim gdje očekujemo da će stvari biti poništene (MAX_PLAYERS
):
// Prije definisanje konstante prije includovanja nije radilo, sada će raditi:
#define MAX_PLAYERS 100
#include <openmp\openmp>
// Re-definisanje će isto raditi:
#undef MAX_PLAYERS
#define MAX_PLAYERS 200
Upotreba enuma znači da konstante imaju oznake za provjeru vremena kompajliranja:
enum ObjectMaterialTextAlignment
{
MATERIAL_TEXT_ALIGN_LEFT,
MATERIAL_TEXT_ALIGN_CENTRE,
MATERIAL_TEXT_ALIGN_RIGHT,
};
Object_SetMaterialText
će moći prihvatiti samo jednu od te tri vrijednosti - ni jednu drugu.
Šta je sa nevažećim ID-ovima? I njih smo učinili dosljednim. Svi entiteti sada koriste istu nevažeću vrijednost, daleko izvan raspona mogućih važećih vrijednosti - open.mp je uklonio skoro sva ograničenja, tako da nevažeći vrijednost poput 65536
jednostavno neće raditi. Koja je ova nova nevažeća vrijednost? Nismo odlučili... Postoje dva glavna kandidata i sa za i protiv, a odluka nije tako laka kao što se na prvi pogled čini. Ali, srećom, to ne čini veliku razliku u unutrašnjem radu jer se možemo prebaciti gotovo trenutno.
Ova dva izbora su objašnjena u nastavku i bili bismo zahvalni na povratnim informacijama o ovome ako je moguće:
0
Korištenje 0
kao nevažeće vrijednosti ima nekoliko prednosti:
to nije nevažeći indeks, tako da kada se vrati i nije ispravno provjeren vaš kod se neće srušiti. Možda neće raditi savršeno, ali će barem nešto nastaviti.
To je vrlo lako provjeriti, a namjera postaje očigledna sa njim:
new Object:object = Object@Create(various, parameters, here);
if (object)
{
Object_SetMaterialText(object, "Hello");
}
else
{
printf("Couldn't create the object.");
}
- Novodeklarirana varijabla je po defaultu nevažeća vrijednost:
new Dialog:d;
Jedna od najčešćih grešaka koje ljudi dobijaju je njihov kod koji radi samo za igrača 0, jer su zaboravili da incijalizuju varijablu. Ako nema igrača/objekta/vozila (0), kod se neće primjenjivati ni na koga - bolje je da nijedan igrač nije promoviran u administratora nego pogrešan igrač.
-1
Korištenje -1
kao nevažeće vrijednosti ima nekoliko prednosti:
To JE nevažeći indeks. To što niste jedan je navedeno kao prednost za
0
jer će vaš kod često nastaviti raditi umjesto da se ruši, ali s crashdetect to može biti dobra stvar - postoji greška u vašem kodu i rušenje će vam reći gdje je točno, ponekad na tačnu liniju izvornog koda. Što je bolje, tiho nastaviti ili završiti glasno?Ljudi su navikli na
0
kao važeću vrijednost većinu vremena. Programeri počinju da broje na0
, tako da bi trebalo da bude validno, a nešto izvan područja pozitivnih celih brojeva bi trebalo da bude nevažeće.U matematici bez predznaka to je najveći mogući cijeli broj -
0xFFFFFFFF
,4294967295
. To znači da je interno ograničeno ograničenje za bilo koji tip entiteta najviše što može biti -4,294,967,295
stavki prije nego što ponestane ID-ova (i memorije).
Funkcije "po igraču"
Ukratko, ovih više nema. Svaka funkcija ForPlayer
, ForAll
, CreatePlayerX
itd. je zamijenjena jednom jednostavnom funkcijom - X_Display
(gdje je X
bilo koji entitet) i X_Has
za provjeru:
Object_Display(objectid, playerid, true); // Prikazati.
Object_Display(objectid, playerid, false); // Sakriti.
Text_Display(textid, true); // Prikazati je svima.
if (Zone_Has(zoneid, playerid))
{
// Igrac ima dozvolu vidjeti teritoriju.
}
YSI je koristio X_SetPlayer
, ali pokazivanje stvari igračima je najosnovnija stvar, tako da zaslužuje svoj glagol. Neke biblioteke koriste X_Show
i X_Hide
, ali to su dvije funkcije koje samo dovode do viška koda kada se provjerava šta treba učiniti:
if (var) Checkpoint_Show(cpid, playerid);
else Checkpoint_Hide(cpid, playerid);
Ovo je puno jednostavnije:
Checkpoint_Display(cpid, playerid, var);
Imajte na umu da samo pozivanje X_Display
možda zapravo neće pokazati stavku. Objekt na drugom kraju svijeta i dalje neće biti vidljiv. Kontrolna tačka u drugom virtuelnom svetu neće biti vidljiva čak i ako je odmah pored vas. Za svjetske entitete ovo samo govori da je igraču dozvoljeno da to vidi, a ne da oni trenutno mogu. Nasuprot tome, za HUD elemente kao što su meniji i dijalozi ovo ih trenutno prikazuje, a druge može sakriti kada je samo jedan dozvoljen.
IDovi
Sa uklanjanjem skupova po igraču i objedinjavanjem nevažećih ID-ova, ovo više nije problem.
Pametnije funkcije
Funkcije X_Display
prikazane gore mogu uzeti dva parametra - entitet i stanje prikaza, kako bi omogućili svakom igraču da ih vidi; ili alternativno tri parametra - entitet, igrač i stanje prikaza. Postoje i druge funkcije koje su također pametne u pogledu svojih parametara. Jedan skup primjera su različite funkcije rotacije. Kao što je spomenuto u uvodu, postoje najmanje četiri različita načina za dobivanje i postavljanje rotacija za različite entitete. Sada postoji jedan - X_SetRotation
i X_GetRotation
. Na primjer:
// Samo dobiti `z` kao povratni podatak.
z = Player_GetRotation(playerid);
// Dobiti x, y, i z kao Eulerove uglove.
Object_GetRotation(objectid, x, y, z);
// Dobivamo w, x, y, i z kvaternion uglove.
Vehicle_GetRotation(vehicleid, w, x, y, z);
Koji se koristi za koji entitet? Svi oni:
// Samo dobiti `z` kao povratni podatak.
z = Object_GetRotation(objectid);
// Dobiti x, y, i z kao Eulerove uglove.
Object_GetRotation(objectid, x, y, z);
// Dobivamo w, x, y, i z kao kvaternion uglove.
Object_GetRotation(objectid, w, x, y, z);
Značenja parametara i povratnih podataka određena su brojem datih parametara. Također za Set
:
// Samo postavi 'z'.
Vehicle_SetRotation(vehicleid, z);
// Postavi x, y, i z Eulerove uglove.
Vehicle_SetRotation(vehicleid, x, y, z);
// Postavi w, x, y, i z kvaternion uglove.
Vehicle_SetRotation(vehicleid, w, x, y, z);
Zaključak
Veoma smo se trudili da napravimo API koji je jednostavan za korišćenje, lak za učenje, ali i kompatibilan unazad. Veliki dio uspjeha SA:MP-a proizlazi iz njegove jednostavnosti korištenja, i mi to želimo zadržati, ali smo također svjesni da postoje i napredni korisnici koji žele da izvuku daleko više od svog koda. Postizanje ove ravnoteže je uvijek teško, posebno kada su najiskusniji oni koji najviše komentiraju - oni koji poznaju jezik iznutra i izvana i žele ga pogurati dalje. Ovo stvara sistem koji samostalno bira napredne funkcije na račun početnika. Ne želimo ovo, ali ipak želimo čuti vaše mišljenje. Koji jezik i API funkcije vam se sviđaju, a koje ne? Koje bi vam funkcije pomogle da izvučete maksimum iz svog koda? Mislite li da je novi dizajn jednostavan ili previše kompliciran? Da li vam odgovara slučajna imena trenutnih funkcija? Oni služe svojoj svrsi, pa zašto ih mijenjati? Da li biste kao početnik cijenili nešto što se radi drugačije?
- Molimo podijelite sve povratne informacije koje možda imate ovdje u temi burgershot ispod. Voljeli bismo čuti neke od primjedbi i komentara od vas:
https://forum.open.mp/showthread.php?tid=1036
* Zanimljiva sporedna napomena. Zahvaljujući načinu na koji smo apstrahovali API kod za skriptovanje, Dialog_IsValid
je implementiran kao:
SCRIPT_API(Dialog_IsValid, bool(Dialog_s)) { return true; }
To je doslovno to. Nije potrebna stvarna implementacija jer da bi funkcija bila pozvana, traženje entiteta mora biti uspješno, i stoga možemo odmah vratiti 'true'.