Język skryptowania w LUA

LUA - Język skryptowy pozwalający na dodawanie pewnych elementów do programu bez ingeracji w jego główny kod źródłowy. Polega on na dodawaniu skryptów, które potem wykorzystuje sam program.

Czemu nie użyć by go w VCMI? Pomyślmy… Byłby dobrą alternatywą dla ERM. Pozwoliłby na szersze rozbudowanie gry, bez ingeracji w główny kod. Dam nawet dobry przykład, z którego wpadłem właśnie na pomysł dodania LUA do VCMI.

Jest pewien mod do gry na silniku Source (firmy Valve, twórców Counter-Strike: Source oraz Half-Life 2. Jeśli kojarzycie te tytuły, Valve też powinniście kojarzyć), który właśnie rozwija grę o tryb “piaskownicy” (sandbox) oraz właśnie język skryptowy LUA.

Na co on pozwala (w tym przykładzie)?

NP. Dodanie własnej broni z użyciem nowego lub istniejącego w grze modelu. Potwora o całkiem nowych właściwościach, oraz masę innych rzeczy, które są prawie nieograniczone (np. nowy tryb gry).

Na co on pozwalałby w VCMI?

Czy ja wiem… Patrząc na to, że projekt jest w 100% otwarty, można by było poprosić twórców o całkowite wspomaganie przy pomocy LUA. Czyli mielibyśmy:

  • Nowe potwory
  • Nowe zamki
  • Nowe obiekty
  • Nowe, rozbudowane mapy o własnych możliwościach
  • Nowe artefakty
  • i wiele, wiele więcej…

Wszystko to ograniczałoby to, w ilu stopniach LUA byłby użyty. Ja uważam, iż dodanie go byłoby znakomitym pomysłem, gdyż wspierałby on pomysł samych twórców, czyli tworzenie w 100% kompatybilnych z grą modyfikacji. Teraz nowy zamek można by było trzymać w jednym katalogu, oraz włączać/wyłączać go w opcjach gry! (Nie wiem w ilu stopniach dałoby się to wykonać, szczerze to ja wciąż jadę tylko na przykładzie moda do Source Engine (tak przy okazji to “Garry’s Mod”)).

Co myślicie? Łatwiej tworzyłoby się modyfikacje, oraz lepiej powoływałoby się je do życia, ponieważ nie trzeba by było ingerować w sam główny kod gry.

Przejdzie?

Akurat LUA jest jednym z pierwszych planowanych językiem zkryptowej.Niestety aby została w pełni zaimpletowana ,musi wyjść wersja w pełni grywalna ( czyli tak jak orginalny HoMM 3).

Tak, obsługa języków skryptowych jest w naszych planach i to jest jeden z kluczowych elementów projektu, na którym skupimy się po ogólnym zaimplementowaniu oryginalnych funkcjonalności. Takoż dodawanie nowej zawartości (potwory, zamki, itd.) do gry poprzez paczki.

Wybór samego języka nie jest zbyt istotny (Lua / Python / Ruby / ERM / coś-jeszcze-zabawniejszego) – istotne jest, jakie możliwości tym językom damy :slight_smile:

Ok, chciałem tylko pomóc. :wink: W LUA siedzę już trochę i pomyślałem, że ten skryptowanik na coś się wam przyda. :slight_smile:

Osobiście polecałbym jakąś formę opartą na języku skryptów NWN - jest on jednym z najprostszych w nauce języków, ma możliwość tworzenia olbrzymich skryptów i jest bardzo podobny do C++. Mogę wysłać przykładowe skrypty…
Tak, wiem że NWN to cRPG, a Heroes to strategia, ale chodzi o podobieństwo języka.

Myślę, że tak jak napisał Tow język jako taki nie ma większego znaczenia, chodzi głównie o funkcjonalność i o to żeby był w miarę przejrzysty (a nie taki jak ERM).

Nwscript spełnia to kryterium. Przykładowy skrypt, dodający naszej postaci 500 sztuk złota i zabierający 500 PD, jeśli ma powyżej 2 poziomu. Jeśli ma 1 poziom, dostajemy komunikat i odkrywa nam się obszar.

Czy ktoś zna prostszy język? :stuck_out_tongue:

Aha. No niby ładnie to wygląda, ale fajnie by było, gdyby ludzie, którzy nie znają C++, a chcieliby coś zrobić też mogliby coś zdziałać (z tego co widzę to chwilowo dużo nie zdziałają. :/). Dlatego, skoro prosisz o łatwiejszy skrypt proszę (przykład oczywiście z Garry’s Moda, którego wciąż kurczowo się trzymam xD):

if( SERVER ) then
	AddCSLuaFile( "shared.lua" );
end

if( CLIENT ) then
	SWEP.PrintName = "Spartan Leg";
	SWEP.Slot = 3;
	SWEP.SlotPos = 3;
	SWEP.DrawAmmo = false;
	SWEP.DrawCrosshair = true;
	SWEP.WepSelectIcon = surface.GetTextureID("weapons/spartan_kick_leg") 
end

SWEP.Author			= "-[SB]- Spy & Kogitsune - Lune66 > Editing"
SWEP.Instructions	= "REMEMBER TO HAVE CHEATS >ON<!!"
SWEP.Contact		= "[email protected]"
SWEP.Purpose		= "Kick the living f*ck out of anything in range."

SWEP.ViewModelFOV	= 75
SWEP.ViewModelFlip	= false

SWEP.Spawnable			= false
SWEP.AdminSpawnable		= true

SWEP.NextStrike = 0;
  
SWEP.ViewModel      = "models/weapons/v_kick.mdl"
SWEP.WorldModel   = ""

Zapewne pocieszające jest to, że to najtrudniejsze do wykonania co znalazłem. xD Oto jak Garry Newman (twórca GModa) SAM (!) skompresował LUA do takiego stopnia prostoty użycia… Co myślicie?

Oprócz elegancji języka bardzo ważną sprawą jest istnienie opensource’owego parsera w C/C++ (najlepiej C++) umożliwiającego od razu wykonywanie skryptów, podłączanie wywołań itd.; taka biblioteka powinna być jeszcze dość rozpowszechniona, aby nie trzeba było myśleć “a może ten bug jest z biblioteki” (istotnych bugów w boost.python na przykład bym się nie spodziewał). Kolejną sprawą jest istnienie bibliotek dla samego języka skryptowego… mówiąc krótko: język, którego użyjemy, powinien być popularny.

Jak nie znają C++'a, to i przy skryptach za dużo dobrej pracy nie zrobią. Początkowo trzeba będzie budować całą architekturę skryptów w C++. A później… jakoś wolałbym, aby skrypty pisały osoby wiedzące cokolwiek o C++.

Czy to ma znaczyć “nikt nie próbował dotąd robić w tym niczego bardziej skomplikowanego” ? Jeśli tak, to jest to mało pocieszające…

Znaczy się, że LUA nie jest popularny, lub nie ma parsera? Według mnie spełnia oba wymagania. O biblioteczki się nie martw… Są one naprawdę dobrze przygotowane, a jak coś nie jest tak, to z problemem do autora.

Aha… O ile się nie mylę, to VCMI ma być projektem otwartym na nowe rzeczy, które każdy będzie mógł wykonać. Nie mylę się? Jeśli język skrypowy ma być podobny do C++, to po co robić VCMI? Każdy sam może sobie napisać od nowa silnik znając C++.

Nie. Znaczy to, że nikt NIE POTRAFIŁ zrobić nic bardziej skomplikowanego, gdyż taka jest tutaj prostota pisania. Oczywiście każdy może w GModzie rozwinąć swoje skrypty o coś bardziej zaawansowanego, jednak autor przy powiązywaniu LUA z jego modem, pomyślał o tym, żeby każdy mógł coś wykonać, a jak ktoś zna LUA w 100% i chce wykonać co bardziej zaawansowanego - droga wolna!

Tak, każdy, spędzając nad projektem 5 lat.

VCMI raczej nie ma sprawić, że nawet 5-latek będzie mógł dodać do gry frakcję nilowych krokodyli, ale że dodanie tej frakcji będzie w ogóle możliwe. Jak łatwo? To będzie zależało od zdolności potencjalnego modera :slight_smile:

Chodziło mi o ten język skryptów NWN i ten nieokreślony którego próbkę wkleiłeś. Lua akurat jest dość popularna.

Jasne, jak napiszę do autorów “zbindujcie mi tu całego SDLa” to w kilka dni mi przygotują co trzeba…

Tak, nie mylisz się - a raczej nie myliłbyś się, gdybyś dodał “w miarę swoich możliwości”. Oczywiście, nie powinniśmy wymagać za dużych możliwości, ale bez przesady - jakieś trzeba będzie posiadać.

  1. jakąś składnię ten język musi mieć… a składnia typu C++ jest szeroko rozpowszechniona
  2. czy obecnie możesz pisać skrypty do Heroesa III w języku na tyle normalnym co C++? ERM jest dużo gorszy i generalnie nierozszerzalny.
  3. chyba nie rozumiesz idei skryptów i VCMI… pisanie skryptów ma docelowo ma się mieć nijak do pisania silnika - czy to skrypty będą miały składnię podobną do C++ czy też nie.Skrypty mają rozbudowywać mechanikę, podczas gdy silnik ma dostarczać podstawę. Nie można porównywać tworzenia podstawy do tworzenia malutkich w porównaniu z nią dodatków…

Mam więc rozumieć, że język jest tak genialny, że np. całe VCMI możnaby w nim napisać w kilkunastu linijkach? Łał.
Jak do tej pory wszystkie przypadki języków, w których ktoś “nie potrafił zrobić nic bardziej skomplikowanego” to były języki niesłychanie trudne (a nie niesłychanie łatwe), jak Malebolge; dla przykładu tu jest Hello World w Malebolgu:

 (=<`:9876Z4321UT.-Q+*)M'&%$H"!~}|Bzy?=|{z]KwZY44Eq0/{mlk**
 hKs_dG5[m_BA{?-Y;;Vb'rR5431M}/.zHGwEDCBA@98\6543W10/.R,+O<

Prostota pisania z reguły wynika z niewielkich możliwości języka. Powiedz mi lepiej, jak wyobrażasz sobie dodawania nowych typów obiektów (klas) i wsparcie dla modów z wykorzystaniem tego mechanizmu. Ba, chciałbym zobaczyć obsługę najprostszej tablicy dwuwymiarowej tudzież stringów wykorzystujących pliki tekstowe.
W praktyce wszelkie te skrypty nie są zawieszone w próżni, ale są silnie zintegrowane z mechaniką i silnikiem. Aby uzyskać sensowny rezultat, należy najpierw przygotować kilkaset (na dobry początek) funkcji wysokiego poziomu i parametrów obiektów, które będą przez ten skrypt obsługiwane. Wolałbym nie odkryć w połowie pracy, że jakiejś opcji nie da się zrealizować w sensowny z punktu widzenia użytkownika sposób.

Choćby po to, aby kodu nie trzeba było kompilować po każdej wprowadzonej zmianie. Właśnie z tego powodu Python i Lua są warte rozważenia.

Co do treści pierwszego postu jesteśmy zgodni, więc nie bardzo wiem, dokąd zmierza ta dyskusja. Zapewne chodzi o faktyczne rozwiązania? :stuck_out_tongue:

LUA jest dość rozpowszechnionym i co bardzo ważne szybkim(wykorzystuje standardy C na poziomie parser’a) językiem skryptowym jeśli kojażycie grę taka jak Tibia i jej niezależne prywatne serwery(OTS) tam od wielu lat wykorzystywane jest LUA i powstają najróżniejsze skrypty od 3 linijkowych badziew do potężnych skryptów rozszerzających możliwości i ciekawość gry język posiada metatablice fajna rzeczy do zbudowania wersji obiektowej dla skryptów, tu jeden z większych skryptów z OTS pokazujących jak fajnie można zastosować ów język w modach do gier LUA:

--- Perfect refine system by Mock the bear (MTB).
--- Email: [email][email protected][/email]
-- &a = weapon attack
-- &d = weapon defense
-- &s = shield defense
-- &p = armor defense
-- # = nivel do item
-- @ = max level
local gain = {
gainArmor='&p+(1)',loseArmor='&p-(1)',
gainShield='&s+#',loseShield='&s-(#+1)',
gainAttack='&a+(1*(#))',loseAttack='&a-(1*(#+1))',
gainDefense='&d+(1*(#))',loseDefense='&d-(1*(#+1))',
chance='(100/math.sqrt((((@/4)+(#*2))/@)*#))',
maxlvl = 17,
blocked_ids = {8881}
}
local it = {
--[itemid] = [percent]
[8306] = 0, -- 0% additional
[8305] = 100, -- 50%
}
if not setItemName then
    function setItemName(uid,name)
		return doItemSetAttribute(uid,'name',name)
    end
    function setItemArmor(uid,name)
		return doItemSetAttribute(uid,'armor',name)
    end
	function setItemDefense(uid,name)
		return doItemSetAttribute(uid,'defense',name)
	end
	function setItemAttack(uid,name)
		return doItemSetAttribute(uid,'attack',name)
	end
	function getItemAttack(uid)
		return getItemAttribute(uid,'attack')
	end
	function getItemDefense(uid)
		return getItemAttribute(uid,'defense')
	end
function getItemArmor(uid)
   if type(uid) == 'number' then
      return getItemAttribute(uid,'armor')
   else
      return getItemInfo(uid.itemid).armor
   end
end
end
 
local function isArmor(uid) -- Function by Mock the bear.
    if (getItemInfo(uid.itemid).armor ~= 0) and (getItemWeaponType(uid.uid) == 0) then
		return true
	end
	return false
end
local function isWeapon(uid) -- Function by Mock the bear.
	uid = uid or 0
	local f = getItemWeaponType(uid)
	if f == 1 or f == 2 or f == 3 then
		return true
	end
	return false
end
local function isShield(uid) -- Function by Mock the bear.
	uid = uid or 0
	if getItemWeaponType(uid) == 4 then
		return true
	end
	return false
end
local function isBow(uid) -- Function by Mock the bear.
	uid = uid or 0
	if getItemWeaponType(uid) == 5 then
		return true
	end
	return false
end
local function getWeaponLevel(uid) -- Function by Mock the bear.
   uid = uid or 0
   local name = getItemName(uid.uid) or getItemInfo(uid.itemid).name or ''
   local lvl = string.match(name,'%s%+(%d+)%s*')
   return tonumber(lvl) or 0
end
local function doTransform(s,i) -- Function by Mock the bear.
    local c = string.gsub(s,'@',gain.maxlvl)
    local c = string.gsub(c,'&a',(getItemAttack(i.uid) ~= 0 and getItemAttack(i.uid) or getItemInfo(i.itemid).attack))
    local c = string.gsub(c,'&d',(getItemDefense(i.uid) ~= 0 and getItemDefense(i.uid) or getItemInfo(i.itemid).defense))
    local c = string.gsub(c,'&s',(getItemDefense(i.uid) ~= 0 and getItemDefense(i.uid) or getItemInfo(i.itemid).defense))
    local c = string.gsub(c,'&p',(getItemArmor(i.uid) ~= 0 and getItemArmor(i.uid) or getItemInfo(i.itemid).armor))
    local c = string.gsub(c,'#',getWeaponLevel(i))
    local q = assert(loadstring('return '..c))
    return math.floor(assert(q()))
end
function onUse(cid, item, fromPosition, itemEx, toPosition)
         if item.uid == 0 or item.itemid == 0 then return false end
		 toPosition.stackpos = 255
         if isInArray(gain.blocked_ids, itemEx.itemid)
          or (not getItemWeaponType(itemEx.uid) or getItemWeaponType(itemEx.uid) > 5)
           or (getItemWeaponType(itemEx.uid) == 0 and not isArmor(itemEx))
             or itemEx.itemid == 0 or itemEx.type > 1 or isItemStackable(itemEx.uid) then
                doPlayerSendTextMessage(cid, 24,"You cant refine this item.")
                return TRUE
         end
         if isCreature(itemEx.uid) == TRUE then
            return FALSE
         end
        local level = getWeaponLevel(itemEx)
        local chance = doTransform(gain.chance,itemEx)
		if level == gain.maxlvl then
			doSendMagicEffect(toPosition, 2)
            return doPlayerSendTextMessage(cid, 24,"Your item is on max level, you can't upgrade it.")
		end
		doPlayerSendTextMessage(cid, 24,"Trying refine with "..(chance+it[item.itemid] > 100 and 100 or chance+it[item.itemid]).."% of sucess!")
        if chance+it[item.itemid] >= math.random(0,100) then
			local nm = getItemName(itemEx.uid)
			local slot = nm:match('(%.+%])') or '' ---If you server use slot system dont change it ^^
			slot = slot~='' and ' '..slot or slot
            setItemName(itemEx.uid, getItemNameById(itemEx.itemid)..' +'..(level+1)..slot)
            addEvent(doPlayerSendTextMessage,500,cid, 24,"Your item has been upgrated to +"..(level+1)..slot..".")
            doSendMagicEffect(toPosition, 12)
            if isArmor(itemEx) then
				local get = doTransform(gain.gainArmor,itemEx)
				setItemArmor(itemEx.uid,get)
			elseif isBow(itemEx.uid) then
				setItemAttack(itemEx.uid, doTransform(gain.gainAttack,itemEx))
			elseif isWeapon(itemEx.uid) then
				setItemAttack(itemEx.uid, doTransform(gain.gainAttack,itemEx))
				setItemDefense(itemEx.uid, doTransform(gain.gainDefense,itemEx))
            elseif isShield(itemEx.uid) then
				setItemDefense(itemEx.uid, doTransform(gain.gainShield,itemEx))
			end
        else
			if level == 0 then
				addEvent(doPlayerSendTextMessage,500,cid, 24,"No effect.")
				doSendMagicEffect(toPosition, 2)
			elseif level > 0 then
			local nm = getItemName(itemEx.uid)
			local slot = nm:match('(%.+%])') or '' ---If you server use slot system dont change it ^^
				slot = slot~='' and ' '..slot or slot
				if level == 1 then
					setItemName(itemEx.uid, getItemNameById(itemEx.itemid)..slot)
					addEvent(doPlayerSendTextMessage,500,cid, 24,"Your item back to normal.")
				else
					setItemName(itemEx.uid, getItemNameById(itemEx.itemid)..' +'..(level-1)..slot)
					addEvent(doPlayerSendTextMessage,500,cid, 24,"Your item back to +"..(level-1)..slot..".")
				end
				if isArmor(itemEx) then
					setItemArmor(itemEx.uid,doTransform(gain.loseArmor  ,itemEx))
				elseif isWeapon(itemEx.uid) then
					setItemAttack(itemEx.uid, doTransform(gain.loseAttack,itemEx))
					setItemDefense(itemEx.uid, doTransform(gain.loseDefense,itemEx))
				elseif isBow(itemEx.uid) then
					setItemAttack(itemEx.uid, doTransform(gain.loseAttack,itemEx))
				elseif isShield(itemEx.uid) then
					setItemDefense(itemEx.uid, doTransform(gain.loseShield,itemEx))
				end
			end
			doSendMagicEffect(toPosition, 9)
        end
	doRemoveItem(item.uid,1)
	return true
end

Ten skrypt w OTS tyczy się ulepszania broni.
W pixelatej Tibii stosuje się to masowo i działa doskonale to dlaczego VCMI miałoby być gorsze :wink:

Pozdrawiam,
Miziak :wink:

VCMI nie będzie gorsze, bo ma być lepsze :stuck_out_tongue: Luę znam nie od dziś, prawdę mówiąc skrypty Lua do piątej części herosów to moje pierwsze sensowne dzieło programistyczne :wink:

Nie sądzę, żeby komuś naprawdę chciało się emulować klasy za pomocą metatablic. O ile podstawowa koncepcja klasy może być dla niedzielnego programisty jeszcze do ogarnięcia, to metatablice z pewnością nie.

Python posiada nie tylko klasy, ale również potężne metaklasy z ducktypingiem oraz dekoratorami. Nie wszystkie one muszą być użyteczne z puktu widzenia użytkownika, ale z punktu widzenia dewelopera jak najbardziej.

Aby umożliwić modding z prawdziwego zdarzenia, należy użytkownikowi przedstawić podstawowe klasy, jak choćby te wykorzystujące uniwersalny system bonusów.
Jestem przekonany, że większość modyfikacji będzie się opierać na radosnym wpychaniu wielkiej liczby bonusów do wszystkich możliwych obiektów.

Drugą ważną gałęzią jest tworzenie interfejsu z kilku postawowych cegiełek.

Trzecią - tworzenie nowych funkcji operujących na obiektach różnego typu w celu dodania własnych funkcjonalności. Wymuszając na użytkowniku jawny dostęp do klas unikniemy tworzenia przez niego algorytmicznej sieczki, która będzie niekompatybilna z innymi modyfikacjami i dalszym rozwojem projektu.
Widać to wyraźnie, przeglądając fora poświęcone skryptom Lua w H5 i ERM w WoGu:
"-Czy jest sposób, aby zrobić X z Y? - Tak, mój kod mający 200 linii pozwala obejść to ograniczenie, po drodze mieląc pół systemu." Warto pokazać, że klasa np. CGHeroInstance ma pewne właściwości, które można zmieniać w zasadzie dowolnie w ramach przyjętej przez nas logiki, a inne pozostają dla użytkownika niewidoczne, żeby nie popsuł. Jeśli czegoś brakuje (a funkcjonalność modów i skryptów będzie z pewnością rozszerzana bardzo powoli), należy mieć pretensje do nas, a nie tworzyć setki linni zbędnego kodu na własną rękę.

Żeby całość miała sens, należy przede wszystkim nowe obiekty umieścić w grze. Nie ma nic prostrzego, niż stworzyć taki obiekt bazujący na dostępnej klasie i wepchnąć go na odpowiednią listę przy użyciu jednej funkcji. Znacznie bardziej to wygodne niż konstruowanie całych obiektów wywołaniem z pięćdziesięcioma argumentami, jak również bardziej intuicyjne, niż tworzenie metatablic.

Wiesz, to tylko moja sugestia co dla potencjalnych moderów bd najważniejsze to tj. mówisz bonusy, otwieranie okienek dialogowych dostęp w ograniczonym stopniu do podstawowych funkcji zamków sam skusiłbym się na pisanie modów bo z językami programowania nie od dziś mam do czynienia może jeszcze żaden ze mnie programista ale sporo umiem pisze programy w C++ + biblioteka wxWidgets(GUI) więc jakąś wiedzę mam a wsparcie gry wszech czasów byłoby dla mnie zaszczytem :slight_smile: