Pokaż wiadomości

Ta sekcja pozwala Ci zobaczyć wszystkie wiadomości wysłane przez tego użytkownika. Zwróć uwagę, że możesz widzieć tylko wiadomości wysłane w działach do których masz aktualnie dostęp.


Wiadomości - Khantor

Strony: [1] 2 3
1
Spacer / [G2NK] Niewidzialna ściana i powolne animacje
« dnia: 2024-01-17, 02:00 »
Dx11 faktycznie pomógł, a konkretnie to włączenie synchronizacji pionowej. Niemniej dziwne, że w normalnego Gothica gram bez Dx11 i problemu nie ma a tu nagle bez Dx11 nie da się nic zrobić. Co do niewidzialnej ściany - wygląda na to, że jak podłoże ma luźny wierzchołek leżący dokładnie na krawędzi, który ściśle przylega, ale wciąż stanowi dziurę, jeśliby go przemieścić to dzieją się takie jaja. Wywalenie wszystkich zbędnych wierzchołków wyeliminowało problem. Swoją drogą aż niepojęte jak bardzo Piranie miały wywalone w geometrie jak tylko nie musiały gdzieś wstawiać portali - jako tako na 30% i fajrant.

2
Spacer / [G2NK] Niewidzialna ściana i powolne animacje
« dnia: 2024-01-15, 11:48 »
Witam, nie jestem pewny czy to właściwy dział, ale problem pojawił się po skleceniu nowego świata, więc wstawiam tutaj.

Otóż skleciłem nowy świat w blenderze, skompilowałem go w Spacerze i po odpaleniu go w Gothicu dzieje się to co na filmie.

https://www.youtube.com/watch?v=C6SkCUL_su4


Postać biega jak mucha w smole a dodatkowo w jednym czy dwóch miejscach są niewidzialne ściany na środku pomieszczenia. Nie wstawiałem tam nic w spacerze, w blenderze też żadnej ściany tam nie ma, na filmiku widać nawet podgląd samej siatki - nic tam nie ma. Widać też, że portale chyba działają dobrze, więc to raczej nie ich wina. Ma ktoś jakieś pomysły o co chodzi z tym drewnem? Oczywiście ten sam Gothic tylko z oryginalnym światem działa normalnie.

3
Spacer / Modyfikacja świata z innego moda.
« dnia: 2023-07-26, 19:30 »
No i wszystko jasne, rozpakowane  pliki trzeba było wsadzić do folderu z grą a nie tylko sam świat i wszystko działa. Wielkie dzięki za pomoc z tym drewnem! A istnieje możliwość rozbicia świata z powrotem na party przed Marcowaniem? Tak jak dostajemy zaraz po dekompilacji G2MDK, czy to już za dużo? :P

4
Spacer / Modyfikacja świata z innego moda.
« dnia: 2023-07-26, 14:50 »
W jakim sensie mam mieć pliki anims? Mam rozpakowanego całego moda - foldery anims, mesh, worlds etc. a przy otwieraniu świata w spacerze wyskakuje okno kompilacji, które rzecz jasna nic nie zmienia.


5
Spacer / Modyfikacja świata z innego moda.
« dnia: 2023-07-25, 17:55 »
No dzień dobry. Mam szybkie pytanie - czy istnieje możliwość edytowania (w spacerze) skompilowanego już świata z innego moda? Jak rozpakuję dajmy na to VarusBikera i chcę otworzyć ZEN świata w spacerze to wyskakuje okienko kompilacji i świat się nie otwiera. Pytanie moje brzmi - czy poza otrzymaniem nieskompilowanych plików od autora da się coś z tym zrobić, czy szkoda sobie zawracać w ogóle głowę?

6
No dzień dobry. Chciałem podpiąć banalną funkcję powodującą, że NPC A atakuje NPC B pod środkowy przycisk myszy. Napisałem banalną, przykładową funkcję:
b_attack(sentenza,hero,AR_NONE,0);

I wsadziłem ją do FrameFunction i zainicjowałem co klatkę - działa perfekcyjnie.

Idąc krok dalej dołożyłem na razie nic nie robiący warunek z klawiszem:
if (MEM_KeyState(MOUSE_BUTTONMID) == KEY_UP)
{
b_attack(sentenza,hero,AR_NONE,0);
};

I także działa perfekcyjnie, tzn. tak jak powinno się stać - Sentenza rzuca się od razu na gracza. Ale to by było na tyle, jeśli o działanie przy próbie podpięcia pod klawisz. Jeśli zamienię warunek na cokolwiek innego określającego czy przycisk jest naciśnięty czy nie, nawet na MEM_KeyState(MOUSE_BUTTONMID) != KEY_UP To już się robi problem. Ten przykładowy Sentenza który sobie stoi w miejscu i je, przy wywołaniu przyciskiem funkcji po prostu zamiera w miejscu jakby kija połknął, ale atakuje może raz na 100 prób. Wsadzony do tej funkcji print działa, że więc ten warunek spełniony wg gry jest. Wie ktoś o co chodzi i jak okiełznać to drewno? Dodam tylko, że w ten sam sposób napisałem i podpiąłem pod PPM funkcję podnoszącą itemy z ziemi jak w returningu i działa bez problemu, a tu się dzieją jaja.

7
No dobra, jakby ktoś takiego bajeru kiedyś potrzebował to podaje rozwiązanie:

func void FocusItemInTradeWIndow_Init()
{
    HookEngineF(7370416, 6, FocusItemInTradeWIndowAndMore);
};

func int oCItemContainer_GetSelectedItem(var int containerPtr)
{   
    const int call = 0;
    if (CALL_Begin(call))
{
        CALL_PutRetValTo(_@(ret));
        CALL__thiscall(_@(containerPtr), 7377600);
        call = CALL_End();
    };

    var int ret;
    return +ret;
};

func int oCItemContainer_GetNextContainerLeft(var int containerPtr, var int currentPtr)
{
    const int call = 0;
    if (CALL_Begin(call))
{
        CALL_PutRetValTo(_@(ret));
        CALL_PtrParam(_@(currentPtr));
        CALL__thiscall(_@(containerPtr), 7359424);
        call = CALL_End();
    };

    var int ret;
    return +ret;
};

func int IsPlayerContainerSelected()
{
her = MEM_CpyInst(hero);

    return her.inventory2_oCItemContainer_frame != 0;
};

func void FocusItemInTradeWIndowAndMore()
{
her = MEM_CpyInst(hero);
//var C_ITEM  citm;
var oCItem oItm;
var int itemPtr;
var int contLeft;

if (IsPlayerContainerSelected() == 0)
{
contLeft = oCItemContainer_GetNextContainerLeft(_@(her.inventory2_vtbl), _@(her.inventory2_vtbl));
if (contLeft)
{
itemPtr = oCItemContainer_GetSelectedItem(contLeft);
};
}
else if (IsPlayerContainerSelected() == 1)
{
if (ECX != contLeft)
{
itemPtr = oCItemContainer_GetSelectedItem(ECX);
};
};

if (itemPtr)
{
//citm = _^(itemPtr);
oItm = _^(itemPtr);
};
};

Jak ktoś dodatkowo jest zainteresowany, żeby funkcja działała tylko przy konkretnym otworzeniu ekwipunku to może dołożyć warunki:

1) Tylko ręczne otworzenie ekwipunku:
Patrz punkt 2 (żeby wiedzieć skąd dostać "InventoryIsOpenWithMode(5)") oraz:

https://themodders.org/index.php?topic=30411.0
Przy czym do ostatniej funkcji podopisywałem parę warunków, żeby na pewno się wywoływała tylko przy ręcznym otworzeniu:

func void Is_Inventory_Opened_Manually()
{
KeyInventory1 = ReadKeyFromIni ("keyInventory", 1);
KeyInventory2 = ReadKeyFromIni ("keyInventory", 2);

if (IsInventoryOpenedManually == 1)
{
her = Hlp_GetNpc(hero);
inv = _@(her.inventory2_vtbl);

const int call = 0;
if (CALL_Begin(call))
{
CALL_PutRetValTo(_@(ret_InventoryIsOpen));
CALL__thiscall(_@(inv), 7377408);
call = CALL_End();
};

if (ret_InventoryIsOpen == 0)
{
IsInventoryOpenedManually = 0;
};
};

if ((Npc_GetBodyState(hero) == BS_INVENTORY)
&& (InventoryIsOpenWithMode(5) == 0)
&& (IsInventoryKeyPressed == 1))
{
IsInventoryOpenedManually = 1;
};

IsInventoryKeyPressed = 0;

if (((MEM_KeyState(KeyInventory1) == KEY_PRESSED)
|| (MEM_KeyState(KeyInventory2) == KEY_PRESSED))
&& ((Npc_GetBodyState(hero) == BS_STAND)
|| (Npc_GetBodyState(hero) == BS_RUN)
|| (Npc_GetBodyState(hero) ==  BS_FALL))
&& (hero.aivar[AIV_INVINCIBLE] == FALSE))
{
IsInventoryKeyPressed = 1;
};
};

2) Tylko podczas handlu:
func int InventoryIsOpen()
{
    var oCNpc her; her = Hlp_GetNpc(hero);
    var int inv; inv = _@(her.inventory2_vtbl);
    var int ret;
    const int call = 0;

    if (CALL_Begin(call))
{
        CALL_PutRetValTo(_@(ret));
        CALL__thiscall(_@(inv), 7377408);
        call = CALL_End();
    };

    return +ret;
};

func int GetInventoryMode()
{
    var oCNpc her; her = Hlp_GetNpc(hero);
    var int inv; inv = _@(her.inventory2_vtbl);
    var int ret;
    const int call = 0;

    if (CALL_Begin(call))
{
        CALL_PutRetValTo(_@(ret));
        CALL__thiscall(_@(inv), 7360672);
        call = CALL_End();
    };

    return +ret;
};

//INV_MODE_DEFAULT = 0
//INV_MODE_CONTAINER = 1
//INV_MODE_PLUNDER = 2
//INV_MODE_STEAL = 3
//INV_MODE_BUY = 4
//INV_MODE_SELL = 5 !!!!!!!!
//INV_MODE_MAX = 6

func int InventoryIsOpenWithMode(var int mode)
{
    return InventoryIsOpen() && GetInventoryMode() == mode;
};

if (InventoryIsOpenWithMode(5) == 1) // <- to jest warunek jakby co

3) Tylko podczas przeszukiwania NPC:
Patrz punkt 2 oraz:
var c_npc trgt;

if ((IsInventoryOpenedManually == 0)// <- to jest warunek jakby co
&& (InventoryIsOpenWithMode(5) == 0)// <- to jest warunek jakby co
&& (Hlp_GetInstanceID(trgt))// <- to jest warunek jakby co
&& (Hlp_Is_oCNpc(her.focus_vob)))// <- to jest warunek jakby co

4) Tylko podczas przeszukiwania skrzyń:
Patrz punkt 2 oraz:
var c_npc trgt;

if ((IsInventoryOpenedManually == 0)// <- to jest warunek jakby co
&& (InventoryIsOpenWithMode(5) == 0)// <- to jest warunek jakby co
&& (Hlp_Is_oCNpc(her.focus_vob) == 0))// <- to jest warunek jakby co

8
Siemka, niestety nie jestem obeznany w temacie kodów, skryptów itp. Powie mi ktoś może jak mam dodać to do gry żeby bronie skalowały się ze zręcznością?

Ogarniasz ten temat
https://themodders.org/index.php?topic=16580.0i robisz wg instrukcji (pobierasz oczywiście najnowsze wersje skryptów jakie obecnie są, bo temat trochę ma a nie wiem czy był aktualizowany, pewnie nie.

Następnie ogarniasz ten temat
http://themodders.org/index.php?topic=23935.0I znowu robisz wg instrukcji. Zamiast kodu Bogdan Zwei wstawiasz ten Lobosa z poprawkami Splasha, napisane masz nawet gdzie tą funkcję i jak zainicjować. Większej filozofii w tym nie ma.

9
Pytania i problemy / jak zapisać plik na MRM?
« dnia: 2021-06-01, 15:36 »
Po prostu przywołaj kodem item z nowym modelem i niech Gothic go sam skompiluje. Najprostsza i najlepsza metoda.

10
Skrypty / B_GiveInvItems i ikarus
« dnia: 2021-05-31, 17:01 »
A nie lepiej skorzystać po prostu z infoboxów które udostępnił Splash?

https://themodders.org/index.php?topic=30247.0

11
Skrypty / [G2NK] Quickloot zdejmuje zbroje z NPC
« dnia: 2021-05-23, 13:57 »
Na unionie się nie znam kompletnie, dlatego chciałem to zrobić Ikarusem.

12
Skrypty / [G2NK] Quickloot zdejmuje zbroje z NPC
« dnia: 2021-05-22, 18:57 »
Dzień dobry. Napisałem funkcję która ma przeszukiwać ciała pobitych/zabitych NPC.

func void Remove_Focused_Empty_Body()
{
        var int index; index = 0;
var int loopStart; loopStart = MEM_StackPos.position;

if((Hlp_GetInstanceID(trgt))
&& (Hlp_Is_oCNpc(her.focus_vob))
&& ((MEM_KeyState(MOUSE_BUTTONRIGHT) == KEY_PRESSED)
|| (MEM_KeyState(MOUSE_BUTTONRIGHT) == KEY_HOLD))
&& (((Npc_GetBodyState(trgt) == BS_UNCONSCIOUS)
|| (Npc_GetBodyState(trgt) ==  BS_DEAD))
|| (trgt.attribute[ATR_HITPOINTS] < 1))
&& (Npc_GetInvItemBySlot (trgt, 0, index) != 0))
{
var C_Item meelee; meelee = Npc_GetEquippedMeleeWeapon(trgt);
var C_Item rangee; rangee = Npc_GetEquippedRangedWeapon(trgt);

if ((item.mainflag & (ITEM_KAT_ARMOR))
|| (Hlp_GetInstanceId(meelee) == Hlp_GetInstanceId(item))
|| (Hlp_GetInstanceId(rangee) == Hlp_GetInstanceId(item)))
{
index += 1;
MEM_StackPos.position = loopStart;
}
else if (Npc_GetInvItemBySlot (trgt, 0, index) != 0)
{
var int amount2; amount2 = Npc_GetInvItemBySlot (trgt, 0, index);

CreateInvItems (hero, Hlp_GetInstanceID (item), amount2);
NPC_RemoveInvItems (trgt, Hlp_GetInstanceID (item), amount2);

var string concatText;
concatText = concatstrings("Zebrano: ", item.description);
concatText = concatstrings(concatText, " x ");
concatText = concatstrings(concatText, inttostring(amount2));
InfoBox_Create(concatText, TRUE, FONT_ScreenSmall, RGBA(255, 255, 255, 255), InfoBox_DefaultTexture);

index += 1;
MEM_StackPos.position = loopStart;
};
};
};

Podpiąłem ją do FrameFunction

        if (!FF_Active (Remove_Focused_Empty_Body))
{
FF_ApplyOnceExt (Remove_Focused_Empty_Body, 0, -1);
};

 i działa z tym, że czasami przy przeszukiwaniu NPCa, pomimo wstawionego warunku zabiera mu też zbroję. Nie zawsze tak się dzieje i nie zauważyłem nawet żadnej reguły, która pomogłaby mi namierzyć problem. Co może być tego przyczyną i jak temu zaradzić?



Swoją drogą jest jakaś dostępna zwykłemu śmiertelnikowi metoda na zrobienie quickloota dla skrzyń?

13
Skrypty / Blokada skoku na PPM
« dnia: 2020-12-28, 16:51 »
Po prostu zmień w ustawieniach klawisz skoku na inny?

14
Skrypty / Orc do rozmowy i do walki
« dnia: 2020-12-28, 13:25 »
Ponieważ kolega nie raczył się podzielić jak sobie z tym problemem poradzić to napiszę, może komuś kiedyś się to przyda.

Wchodzimy na pliku MST naszego potwora. Sprawdzamy w jego prototypie albo instancji jaką rutynę wykonuje. Nie chodzi o "start_aistate            = ZS_MM_AllScheduler;", tylko np. dla tego topielca "aivar[AIV_MM_RoamStart]    = OnlyRoutine;". Oznacza to, że topielec będzie wykonywał rutynę "ZS_MM_RTN_Roam". Otwieramy zatem na plik z rutyną, który znajduje się w "_Work/Data/Scripts/Content/AI/Monster/RTN_Monster" i na sam początek funkcji naszej rutyny wstawiamy "Npc_PercEnable   (self,    PERC_ASSESSTALK         ,   B_AssessTalk);". Jak nie chcemy, żeby dało się rozmawiać z każdym np. topielcem tylko jakimś konkretnym to wstawiamy odpowiedni warunek.

15
Witam. Jak chcę sobie powyciągać zmienne namierzonego przedmiotu w ekwipunku no to używam

container = ECX;

const int call = 0;

if (CALL_Begin(call))
{
CALL_PutRetValTo(_@(selectedItemPtr));
CALL__thiscall(_@(container), 7377600);
call = CALL_End();
};

itm = _^(selectedItemPtr);

i hookuje to na

HookEngineF(7370416, 6, nazwa_funkcji);

Działa to na otwartym ekwipunku. A może ktoś powiedzieć takiemu laikowi jak ja co mam zrobić, żeby móc wyciągnąć zmienne namierzonego itemu ale w polu ekwipunku otwartej skrzyni, polu ekwipunku handlarza podczas handlu i polu ekwipunka przeszukiwanego npc? Byłbym wdzięczny i postawiłbym kratę piwa w zamian ale zaraza i nie wolno.

16
Podeślij skrypt nowej postaci i napisz w jakiej lokalizacji się on znajduje.

17
Skrypty / [G2NK] Dodatkowe wolne AIVARy.
« dnia: 2020-11-01, 19:31 »
Witam. Często można spotkać opinię, że w Gothicu jest za mało wolnych AIVarów do wykorzystania, ale chyba nie znalazłem nigdzie sposobu, żeby te dodatkowe AIVary sobie zwolnić. Jak wiemy, w G2NK mamy do dyspozycji 11 wolnych AIVarów (89-99 gdzie 89 jest wykorzystywany przez LeGo do tworzenia nowej umiejętności), na upartego 12, bo AIVar 72 "AIV_NPCIsRanger" nie jest w ogóle wykorzystywany w skryptach. Pokażę więc dzisiaj, w jaki sposób zwolnić sobie dodatkowe 52 AIVary. Jak ktoś bardzo potrzebuje i się uprze to może sobie zwolnić ich jeszcze więcej. Z góry uprzedzam, że wymaga to dosyć sporo monotonnej roboty, ale nie ma nic za darmo. Potrzebny Ikarus i LeGo.

Jeśli przyjrzymy się AIVarom które mamy w Gothicu to możemy zauważyć, że duża ich część przyjmuje tylko wartości 0-1, a większość przyjmuje wartości nie większe niż 23. Jeśli mamy do wykorzystania ograniczoną ilość AIVarów, z czego każdy ma wielkość 32 bitów, to zużycie go na zapisanie tylko 1-5 bitów to straszne marnotractwo miejsca. Jako, że chyba żaden, poza "AIV_INVINCIBLE" nie jest w żaden sposób czytany przez silnik, to jedyne co musimy zrobić, to skompresować z góry ograniczone AIVary o długości 1-5 bitów w jeden. Poniżej wyznaczona przeze mnie lista AIVarów do kompresji.

AIVary 0-1; - 1 bit

AIV_NpcStartedTalk
AIV_TalkedToPlayer
AIV_PlayerHasPickedMyPocket
AIV_PursuitEnd
AIV_RANSACKED
AIV_DeathInvGiven
AIV_PASSGATE
AIV_PARTYMEMBER
AIV_VictoryXPGiven
AIV_Gender
AIV_SeenLeftRoom
AIV_ToughGuyNewsOverride
AIV_MM_ThreatenBeforeAttack
AIV_MM_FollowInWater
AIV_MM_PRIORITY
AIV_DuelLost
AIV_MM_Packhunter
AIV_MagicUser
AIV_DropDeadAndKill
AIV_IGNORE_Murder
AIV_IGNORE_Theft
AIV_IGNORE_Sheepkiller
AIV_ToughGuy
AIV_NewsOverride
AIV_EnemyOverride
AIV_LOADGAME
AIV_DefeatedByPlayer
AIV_KilledByPlayer
AIV_IgnoresFakeGuild
AIV_NoFightParker
AIV_NPCIsRanger
AIV_IgnoresArmor
AIV_StoryBandit
AIV_StoryBandit_Esteban


AIVary 0-2; - 2 bity

AIV_Guardpassage_Status
AIV_TAPOSITION


AIVary 0-3; - 2 bity

AIV_LastFightAgainstPlayer
AIV_Food
AIV_ArenaFight


AIVary 0-4; - 3 bity

AIV_NpcSawPlayerCommit


AIVary 0-7; - 3 bity

AIV_ChapterInv


AIVary 0-19; - 5 bitów

AIV_ATTACKREASON
AIV_LastPlayerAR


AIVary 0-23 godziny; - 5 bitów

AIV_MM_SleepStart
AIV_MM_SleepEnd
AIV_MM_RestStart
AIV_MM_RestEnd
AIV_MM_RoamStart
AIV_MM_RoamEnd
AIV_MM_EatGroundStart
AIV_MM_EatGroundEnd
AIV_MM_WuselStart
AIV_MM_WuselEnd
AIV_MM_OrcSitStart
AIV_MM_OrcSitEnd


AIVar 0-14; - 4 bity

AIV_MM_ShrinkState

56 powyższych AIVarów można skompresować do 4. Zaczynamy od napisania funkcji, która nam będzie:
a) Kompresować wiele AIVarów w jeden i w razie potrzeby zmieniać ich wartości w obrębie skompresowanego AIVaru.
b) Odczytywać ze skompresowanego AIVara wartości AIVarów składowych.

Gotowiec:

func int PowerFunction (var int Basic_of_Power, var int Power)
{
var int Powered_Number; Powered_Number = 1;
var int index; index = 0;

if (Power < 0)
{
Power = 0;
};

if (Power == 0)
{
return Powered_Number;
};

var int loopStart; loopStart = MEM_StackPos.position;
if (index < Power)
{
Powered_Number = Powered_Number * Basic_of_Power;

index += 1;
MEM_StackPos.position = loopStart;
};

return Powered_Number;
};

func int Byte_for_AIVars (var C_NPC AIVar_Taker, var int AIVar_Input, var int Value_to_AIVar, var int Write_or_Read) //var int Write_or_Read = 0 - wpisanie w skompresowany AIVar AIVaru składowego AIVar_Input którego wartość wynosi Value_to_AIVar; var int Write_or_Read = 1 - odczytanie ze skompresowanego AIVara wartości AIVara składowego AIVar_Input.
// Value_to_AIVar - AIVary 5 - bitowe określające godziny mogą przyjmować wartość "-1". U nas rolę "-1" będzie pełnić "31".
{
var int Nr_CompressedAIVar; Nr_CompressedAIVar = 0; // Na którym z 4 skompresowanych AIVarów będzie znajował się nam nasz AIVar kompresowany
var int Bit_AIVar_CompressedAIVar; Bit_AIVar_CompressedAIVar = 0; // Od którego bitu w AIVarze skompresowanym zaczyna się nasz AIVar kompresowany
var int Bit_Position_AIVar; Bit_Position_AIVar = 0; // Długość w bitach AIVaru kompresowanego

if (AIVar_Input == AIV_MM_OrcSitEnd) // 41
{
Nr_CompressedAIVar = 1;
Bit_AIVar_CompressedAIVar = 1;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_SleepStart) // 30
{
Nr_CompressedAIVar = 1;
Bit_AIVar_CompressedAIVar = 6;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_SleepEnd) // 31
{
Nr_CompressedAIVar = 1;
Bit_AIVar_CompressedAIVar = 11;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_RestStart) // 32
{
Nr_CompressedAIVar = 1;
Bit_AIVar_CompressedAIVar = 16;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_RestEnd) // 33
{
Nr_CompressedAIVar = 1;
Bit_AIVar_CompressedAIVar = 21;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_RoamStart) // 34
{
Nr_CompressedAIVar = 1;
Bit_AIVar_CompressedAIVar = 26;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_IgnoresFakeGuild) // 70
{
Nr_CompressedAIVar = 1;
Bit_AIVar_CompressedAIVar = 31;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_MM_RoamEnd) // 35
{
Nr_CompressedAIVar = 2;
Bit_AIVar_CompressedAIVar = 1;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_EatGroundStart) // 36
{
Nr_CompressedAIVar = 2;
Bit_AIVar_CompressedAIVar = 6;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_EatGroundEnd) // 37
{
Nr_CompressedAIVar = 2;
Bit_AIVar_CompressedAIVar = 11;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_WuselStart) // 38
{
Nr_CompressedAIVar = 2;
Bit_AIVar_CompressedAIVar = 16;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_WuselEnd) // 39
{
Nr_CompressedAIVar = 2;
Bit_AIVar_CompressedAIVar = 21;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_OrcSitStart) // 40
{
Nr_CompressedAIVar = 2;
Bit_AIVar_CompressedAIVar = 26;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_DeathInvGiven) // 11
{
Nr_CompressedAIVar = 2;
Bit_AIVar_CompressedAIVar = 31;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_NoFightParker) // 71
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 1;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_NPCIsRanger) // 72
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 2;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_IgnoresArmor) // 73
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 3;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_StoryBandit) // 74
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 4;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_StoryBandit_Esteban) // 75
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 5;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_ATTACKREASON) // 9
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 6;
Bit_Position_AIVar = 5;
}
else if (AIVar_Input == AIV_MM_ShrinkState) // 42
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 11;
Bit_Position_AIVar = 4;
}
else if (AIVar_Input == AIV_ChapterInv) // 49
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 15;
Bit_Position_AIVar = 3;
}
else if (AIVar_Input == AIV_NpcSawPlayerCommit) // 1
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 18;
Bit_Position_AIVar = 3;
}
else if (AIVar_Input == AIV_ArenaFight) // 45
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 21;
Bit_Position_AIVar = 2;
}
else if (AIVar_Input == AIV_Guardpassage_Status) // 12
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 23;
Bit_Position_AIVar = 2;
}
else if (AIVar_Input == AIV_TAPOSITION) // 19
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 25;
Bit_Position_AIVar = 2;
}
else if (AIVar_Input == AIV_NpcStartedTalk) // 3
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 27;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_TalkedToPlayer) // 5
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 28;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_PlayerHasPickedMyPocket) // 6
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 29;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_PursuitEnd) // 8
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 30;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_RANSACKED) // 10
{
Nr_CompressedAIVar = 3;
Bit_AIVar_CompressedAIVar = 31;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_PASSGATE) // 14
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 1;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_PARTYMEMBER) // 15
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 2;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_VictoryXPGiven) // 16
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 3;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_Gender) // 17
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 4;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_SeenLeftRoom) // 21
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 5;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_ToughGuyNewsOverride) // 25
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 6;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_MM_ThreatenBeforeAttack) // 26
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 7;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_MM_FollowInWater) // 28
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 8;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_MM_PRIORITY) // 29
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 9;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_DuelLost) // 48
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 10;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_MM_Packhunter) // 50
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 11;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_MagicUser) // 51
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 12;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_DropDeadAndKill) // 52
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 13;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_IGNORE_Murder) // 54
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 14;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_IGNORE_Theft) // 55
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 15;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_IGNORE_Sheepkiller) // 56
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 16;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_ToughGuy) // 57
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 17;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_NewsOverride) // 58
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 18;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_EnemyOverride) // 61
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 19;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_KilledByPlayer) // 67
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 20;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_LOADGAME) // 65
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 21;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_DefeatedByPlayer) // 66
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 22;
Bit_Position_AIVar = 1;
}
else if (AIVar_Input == AIV_Food) // 18
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 23;
Bit_Position_AIVar = 2;
}
else if (AIVar_Input == AIV_LastFightAgainstPlayer) // 0
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 25;
Bit_Position_AIVar = 2;
}
else if (AIVar_Input == AIV_LastPlayerAR) // 47
{
Nr_CompressedAIVar = 4;
Bit_AIVar_CompressedAIVar = 27;
Bit_Position_AIVar = 5;
};

if ((Value_to_AIVar < PowerFunction(2, Bit_Position_AIVar))
&& (Value_to_AIVar >= 0))
{
var int AIVar_Initial_Value; AIVar_Initial_Value = 0;
if (Nr_CompressedAIVar == 1)
{
AIVar_Initial_Value = AIVar_Taker.aivar[AIV_FirstCompressed];
}
else if (Nr_CompressedAIVar == 2)
{
AIVar_Initial_Value = AIVar_Taker.aivar[AIV_SecondCompressed];
}
else if (Nr_CompressedAIVar == 3)
{
AIVar_Initial_Value = AIVar_Taker.aivar[AIV_ThirdCompressed];
}
else if (Nr_CompressedAIVar == 4)
{
AIVar_Initial_Value = AIVar_Taker.aivar[AIV_FourthCompressed];
};

var int AIVar_Value; AIVar_Value = 0;

var int Component1; Component1 = 0;
Component1 = AIVar_Initial_Value%(PowerFunction(2, Bit_AIVar_CompressedAIVar + Bit_Position_AIVar - 1));

var int Component2; Component2 = 0;
Component2 = PowerFunction(2, Bit_AIVar_CompressedAIVar - 1);

if (Write_or_Read == 0)
{
AIVar_Value = AIVar_Initial_Value - Component1 + (Value_to_AIVar * Component2) + Component1%Component2;

if (Nr_CompressedAIVar == 1)
{
AIVar_Taker.aivar[AIV_FirstCompressed] = AIVar_Value;
}
else if (Nr_CompressedAIVar == 2)
{
AIVar_Taker.aivar[AIV_SecondCompressed] = AIVar_Value;
}
else if (Nr_CompressedAIVar == 3)
{
AIVar_Taker.aivar[AIV_ThirdCompressed] = AIVar_Value;
}
else if (Nr_CompressedAIVar == 4)
{
AIVar_Taker.aivar[AIV_FourthCompressed] = AIVar_Value;
};
}
else if (Write_or_Read == 1)
{
AIVar_Value = (Component1 - Component1%Component2) / Component2;

return AIVar_Value;
};
};
};

Teraz czeka nas najbardziej monotonna i mozolna część - zamienianie w plikach wszystkich kompresowanych AIVarów na ich skompresowaną wersję. Tutaj trzeba uważać na jedną rzecz. Część AIVarów, szczególne te 5 bitowe z 1 i 2 skompresowanego oznaczające godziny, występują w 2 miejscach jako argumenty innych funkcji. Tak się składa, że nie możemy jako argumentu funkcji wsadzić innej funkcji bo Gothic głupieje. W takiej sytuacji, musimy jako argument wsadzić wartości AIVarów wyciągnęte ze skompresowanego ręcznie. O co chodzi - przykład pliku "_Work/Data/Scripts/Content/AI/Monster/RTN_Monster/ZS_MM_AllScheduler.d". W orginale funkcja wygląda tak:

func void ZS_MM_AllScheduler()
{
self.aivar[AIV_MM_PRIORITY] = PRIO_EAT;

//ADDON>
if (self.guild == GIL_STONEGUARDIAN)
&& (RavenIsDead == TRUE)
{
B_KillNpc (self);
};
//ADDON<

if (Wld_IsTime (self.aivar[AIV_MM_SleepStart],00,self.aivar[AIV_MM_SleepEnd],00) || (self.aivar[AIV_MM_SleepStart] == OnlyRoutine))
{
AI_StartState (self, ZS_MM_Rtn_Sleep, 1, "");
}
else if (Wld_IsTime (self.aivar[AIV_MM_RestStart],00,self.aivar[AIV_MM_RestEnd],00) || (self.aivar[AIV_MM_RestStart] == OnlyRoutine))
{
AI_StartState (self, ZS_MM_Rtn_Rest, 1, "");
}
else if (Wld_IsTime (self.aivar[AIV_MM_RoamStart],00,self.aivar[AIV_MM_RoamEnd],00) || (self.aivar[AIV_MM_RoamStart] == OnlyRoutine))
{
AI_StartState (self, ZS_MM_Rtn_Roam, 1, "");
}
else if (Wld_IsTime (self.aivar[AIV_MM_EatGroundStart],00,self.aivar[AIV_MM_EatGroundEnd],00) || (self.aivar[AIV_MM_EatGroundStart] == OnlyRoutine))
{
AI_StartState (self, ZS_MM_Rtn_EatGround, 1, "");
}
else if (Wld_IsTime (self.aivar[AIV_MM_WuselStart],00,self.aivar[AIV_MM_WuselEnd],00) || (self.aivar[AIV_MM_WuselStart] == OnlyRoutine))
{
AI_StartState (self, ZS_MM_Rtn_Wusel, 1, "");
}
else if (Wld_IsTime (self.aivar[AIV_MM_OrcSitStart],00,self.aivar[AIV_MM_OrcSitEnd],00) || (self.aivar[AIV_MM_OrcSitStart] == OnlyRoutine))
{
AI_StartState (self, ZS_MM_Rtn_OrcSit, 1, "");
}
else
{
AI_StartState (self, ZS_MM_Rtn_Rest, 1, ""); //Default = Rest
};
};

Jak widać w funkcji "Wld_IsTime" jednymi z argumentów są wartości AIVarów, które po skompresowaniu będziemy wyciągać za pomocą funkcji. Mielibyśmy więc w tym przypadku funkcję "Byte_for_AIVars" jako argument funkcji "Wld_IsTime". Unikamy tego wpisując wartości AIVarów ręcznie:

func void ZS_MM_AllScheduler()
{
Byte_for_AIVars(self, AIV_MM_PRIORITY, PRIO_EAT, 0);

//ADDON>
if (self.guild == GIL_STONEGUARDIAN)
&& (RavenIsDead == TRUE)
{
B_KillNpc (self);
};
//ADDON<

if (Wld_IsTime ((self.aivar[AIV_FirstCompressed]%1024 - (self.aivar[AIV_FirstCompressed]%1024)%32) / 32,00,(self.aivar[AIV_FirstCompressed]%32768 - (self.aivar[AIV_FirstCompressed]%32768)%1024) / 1024,00) || ((self.aivar[AIV_FirstCompressed]%1024 - (self.aivar[AIV_FirstCompressed]%1024)%32) / 32 == 31))
{
AI_StartState (self, ZS_MM_Rtn_Sleep, 1, "");
}
else if (Wld_IsTime ((self.aivar[AIV_FirstCompressed]%1048576 - (self.aivar[AIV_FirstCompressed]%1048576)%32768) / 32768,00,(self.aivar[AIV_FirstCompressed]%33554432 - (self.aivar[AIV_FirstCompressed]%33554432)%1048576) / 1048576,00) || ((self.aivar[AIV_FirstCompressed]%1048576 - (self.aivar[AIV_FirstCompressed]%1048576)%32768) / 32768 == 31))
{
AI_StartState (self, ZS_MM_Rtn_Rest, 1, "");
}
else if (Wld_IsTime ((self.aivar[AIV_FirstCompressed]%1073741824 - (self.aivar[AIV_FirstCompressed]%1073741824)%33554432) / 33554432,00,(self.aivar[AIV_SecondCompressed]%32 - (self.aivar[AIV_SecondCompressed]%32)%1) / 1,00) || ((self.aivar[AIV_FirstCompressed]%1073741824 - (self.aivar[AIV_FirstCompressed]%1073741824)%33554432) / 33554432 == 31))
{
AI_StartState (self, ZS_MM_Rtn_Roam, 1, "");
}
else if (Wld_IsTime ((self.aivar[AIV_SecondCompressed]%1024 - (self.aivar[AIV_SecondCompressed]%1024)%32) / 32,00,(self.aivar[AIV_SecondCompressed]%32768 - (self.aivar[AIV_SecondCompressed]%32768)%1024) / 1024,00) || ((self.aivar[AIV_SecondCompressed]%1024 - (self.aivar[AIV_SecondCompressed]%1024)%32) / 32 == 31))
{
AI_StartState (self, ZS_MM_Rtn_EatGround, 1, "");
}
else if (Wld_IsTime ((self.aivar[AIV_SecondCompressed]%1048576 - (self.aivar[AIV_SecondCompressed]%1048576)%32768) / 32768,00,(self.aivar[AIV_SecondCompressed]%33554432 - (self.aivar[AIV_SecondCompressed]%33554432)%1048576) / 1048576,00) || ((self.aivar[AIV_SecondCompressed]%1048576 - (self.aivar[AIV_SecondCompressed]%1048576)%32768) / 32768 == 31))
{
AI_StartState (self, ZS_MM_Rtn_Wusel, 1, "");
}
else if (Wld_IsTime ((self.aivar[AIV_SecondCompressed]%1073741824 - (self.aivar[AIV_SecondCompressed]%1073741824)%33554432) / 33554432,00,(self.aivar[AIV_FirstCompressed]%32 - (self.aivar[AIV_FirstCompressed]%32)%1) / 1,00) || ((self.aivar[AIV_SecondCompressed]%1073741824 - (self.aivar[AIV_SecondCompressed]%1073741824)%33554432) / 33554432 == 31))
{
AI_StartState (self, ZS_MM_Rtn_OrcSit, 1, "");
}
else
{
AI_StartState (self, ZS_MM_Rtn_Rest, 1, ""); //Default = Rest
};
};

Podane wyżej wartości będą poprawne tylko, jeśli nie zmienicie wartości "Nr_CompressedAIVar", "Bit_AIVar_CompressedAIVar", "Bit_Position_AIVar" podanych przeze mnie. Jak zmienicie to musicie je sobie wyliczyć na nowo i wstawić. Oczywiście stałym "AIV_FirstCompressed", "AIV_SecondCompressed", "AIV_ThirdCompressed" i "AIV_FourthCompressed" nadajemy wartości jakichś wolnych AIVarów. Lista wolnych AIVarów po kompresji:

//const int AIV_TALENT_INDEX                                         = 89; - wykorzystywany przez LeGo do tworzenia nowej umiejętności, ale możemy go sobie zabrać i użyć do własnych celów.

const int AIV_90                         = 90;
const int AIV_91                         = 91;
const int AIV_92                         = 92;

const int AIV_93                 = 93;
const int AIV_94                 = 94;
const int AIV_95                         = 95;

const int AIV_96         = 96;
const int AIV_97                 = 97;
const int AIV_98                 = 98;

const int AIV_99           = 99;

const int AIV_FirstCompressed = 0;
const int AIV_SecondCompressed = 1;
const int AIV_ThirdCompressed = 3;
const int AIV_FourthCompressed = 5;

const int Free_AIVar_01 = 6;
const int Free_AIVar_02 = 8;
const int Free_AIVar_03 = 9;

const int Free_AIVar_04 = 10;
const int Free_AIVar_05 = 11;
const int Free_AIVar_06 = 12;
const int Free_AIVar_07 = 14;
const int Free_AIVar_08 = 15;
const int Free_AIVar_09 = 16;
const int Free_AIVar_10 = 17;

const int Free_AIVar_11 = 18;
const int Free_AIVar_12 = 19;
const int Free_AIVar_13 = 21;
const int Free_AIVar_14 = 25;
const int Free_AIVar_15 = 26;
const int Free_AIVar_16 = 28;
const int Free_AIVar_17 = 29;

const int Free_AIVar_18 = 30;
const int Free_AIVar_19 = 31;
const int Free_AIVar_20 = 32;
const int Free_AIVar_21 = 33;
const int Free_AIVar_22 = 34;
const int Free_AIVar_23 = 35;
const int Free_AIVar_24 = 36;

const int Free_AIVar_25 = 37;
const int Free_AIVar_26 = 38;
const int Free_AIVar_27 = 39;
const int Free_AIVar_28 = 40;
const int Free_AIVar_29 = 41;
const int Free_AIVar_30 = 42;
const int Free_AIVar_31 = 45;

const int Free_AIVar_32 = 47;
const int Free_AIVar_33 = 48;
const int Free_AIVar_34 = 49;
const int Free_AIVar_35 = 50;
const int Free_AIVar_36 = 51;
const int Free_AIVar_37 = 52;
const int Free_AIVar_38 = 54;

const int Free_AIVar_39 = 55;
const int Free_AIVar_40 = 56;
const int Free_AIVar_41 = 57;
const int Free_AIVar_42 = 58;
const int Free_AIVar_43 = 61;
const int Free_AIVar_44 = 65;
const int Free_AIVar_45 = 66;

const int Free_AIVar_46 = 67;
const int Free_AIVar_47 = 70;
const int Free_AIVar_48 = 71;
const int Free_AIVar_49 = 72;
const int Free_AIVar_50 = 73;
const int Free_AIVar_51 = 74;
const int Free_AIVar_52 = 75;

Po skończonej kompresji mamy łącznie 63 wolne AIVary, czyli więcej niż zajętych. Jak ktoś bardzo chce i potrzebuje, to może np. przyjąć, że AIVary określające poziom władania bronią nigdy nie przekroczy nam 127 ( 8 bitów) i 3 z nich skompresować w 1 zwalniając 2, itp.

Rozwiązanie to ma jedną wadę. Nie pamiętam dokładnie jak to było, ale zaraz po włączeniu Gothica, chyba LeGo i Ikarus (?) nie inicjują się do pierwszego wczytania gry. Oznacza to, że żeby wszystko działało, to po włączeniu nowej gry musimy dać nową grę jeszcze raz - odpalić nową grę z już załadowanej gry a nie z menu głównego, inaczej nie będzie działać, AIVary NPCtom się nie ustawią i będzie burdel kompletny. Wydaje mi się, że Siemekk kiedyś napisał jak sobie z tym poradzić, żeby Ikarus był zainicjowany od razu po włączeniu gry, ale nie zapisałem tego nigdzie a teraz znaleźć nie mogę. Może jak go ładnie poprosicie to to wrzuci.

18
Skrypty / Bandyci nie zabijają
« dnia: 2020-08-27, 21:03 »
Wchodzisz do
_Work/Data/Scripts/Content/AI/Human/C_Human/C_DropUnconscious.d

z 1 warunku w tej funkcji wywalasz

(other.guild == GIL_BDT)

teraz bandyci nie zabijają, z 2 warunku w tej funkcji wywalasz

(self.guild != GIL_BDT)

teraz bandyci nie są zabijani.

19
Formuła wyliczania expa potrzebnego do następnego lvla znajduje się w pliku
_Work/Data/Scripts/Content/Story/B_Story/B_GivePlayerXP.dmożesz to dowolnie zedytować dla własnych potrzeb. Początkową wartość równą 500 pd możesz zmienić tak jak mówili moi przedmówcy.

20
Skrypty / Regeneracja HP u wrogów (jak u smoków)
« dnia: 2020-07-27, 17:21 »
Ew. jak ci się nie chce to możesz na chama ustawić mu gildię smok, bo smocza regeneracja jest przypisana stricte do gildii. Ew. jak satysfakcjonuje cię regeneracja 1 HP/s to ustaw mu attribute[6] = 1. Ogólnie jednak wbudowane gothicowe regeneracje są straszne upośledzone i nie polecam z nich korzystać. Regeneracja z attribute[6] przywraca ci 1 HP co tyle sekund ile tam wpiszesz, więc max co możesz wyciągnąć to 1 HP/s. Smocza regeneracja działa na zasadzie ZS'ów, czyt. żeby następowała regeneracja to, tak jak to robią smoki co jest mega słabe, NPC musi zamrzeć w miejscu i nie robić kompletnie nic. Regeneracje można bez problemu zrobić z wykorzystaniem Ikarusa i LeGo, chociażby najprościej przez FrameFunction (bez ikarusa da się to też zrobić na pewno ticktockiem ale kto w dobie ikarusa korzysta z ticktocka).

Strony: [1] 2 3
Do góry