Portal-Zone Gothic-Zone Gothic II-Zone Gothic 3-Zone Gothic 4-Zone Modifikationen-Zone Download-Zone Foren-Zone RPG-Zone Almanach-Zone Spirit of Gothic

 

Seite 5 von 34 « Erste 1234567891216 ... Letzte »
Ergebnis 81 bis 100 von 668
  1. Beiträge anzeigen #81 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Am Moddertreffen habe ich die Gelegenheit genutzt "live" auf der Leinwand zu demonstrieren, wie viel einfacher das Erstellen eines Patches mit der kommenden Ninja Version sein wird - anhand des Beispiels von Manaregeneration. Davon gibt es sogar ein Foto auf der Moddertreffen-Webseite. Das nur als kleine Info und Vorgeschmack. Die Nutzung von LeGo Framefunctions ist in Ninja 2.0 kein Problem mehr, was das alles einfacher macht. Natürlich fehlen in dem Beispiel im Foto die Regenerations-Anpassungsmöglichkeiten, aber das kurze Skript zeigt wie wenig Code grundsätzlich nur nötig sein wird und das Skript/der Patch ist von Vornherein mit Gothic 1 und Gothic 2 kompatibel.

  2. Beiträge anzeigen #82 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Am Moddertreffen habe ich die Gelegenheit genutzt "live" auf der Leinwand zu demonstrieren, wie viel einfacher das Erstellen eines Patches mit der kommenden Ninja Version sein wird - anhand des Beispiels von Manaregeneration. Davon gibt es sogar ein Foto auf der Moddertreffen-Webseite. Das nur als kleine Info und Vorgeschmack. Die Nutzung von LeGo Framefunctions ist in Ninja 2.0 kein Problem mehr, was das alles einfacher macht. Natürlich fehlen in dem Beispiel im Foto die Regenerations-Anpassungsmöglichkeiten, aber das kurze Skript zeigt wie wenig Code grundsätzlich nur nötig sein wird und das Skript/der Patch ist von Vornherein mit Gothic 1 und Gothic 2 kompatibel.
    Das ist ja wunderschön
    Kann es kaum erwarten.
    Dann wird's hoffentlich noch mehr Ninjas geben. (Manareg, Sprinten, Lebensreg, Waffenmods (z.B. Gesegnete Innos Waffen brennen), UI, kleinere bugfixes für spezielles)


    EDIT:
    Ist es eigentlich möglich "Ninja-Patches" von Ikarus und LeGo zu machen, welche dann separat ausgeliefert werden können, als Basis für andere Ninja-Patches?
    Geändert von Kirides (17.06.2019 um 17:42 Uhr)

  3. Beiträge anzeigen #83 Zitieren
    Kämpfer Avatar von ZeroLord
    Registriert seit
    Mar 2012
    Ort
    Ignadon
    Beiträge
    312
     
    ZeroLord ist offline
    ist es eigentlich normal das wenn eine mod gfa als nicht kompatibel flaggt sie trotzdem geladen wird ? habe das gerade bei ashhun erst wenn ich die gfa auf disabeld setzte wird es halt nicht geladen (egal ob spine oder nicht) hab auch schon versucht in den mod inis doe einträge zu erweitern mit freeaiming ninja_gfa ,,, trotzdem gleiches ergebnis
    🎵 ... Kampfkraft und Wendigkeit sind die Vorraussetzung für jeden Kämpfer ... 🎶

  4. Beiträge anzeigen #84 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von johnnyboyy Beitrag anzeigen
    Ist es eigentlich möglich "Ninja-Patches" von Ikarus und LeGo zu machen, welche dann separat ausgeliefert werden können, als Basis für andere Ninja-Patches?
    Das wird mit Ninja 2.0 nicht mehr nötig sein. Das Foto vom Skript oben ist bspw. der gesamte Inhalt dieses Patches (kein Ikarus oder LeGo muss mit gepackt werden).


    Zitat Zitat von ZeroLord Beitrag anzeigen
    ist es eigentlich normal das wenn eine mod gfa als nicht kompatibel flaggt sie trotzdem geladen wird ? habe das gerade bei ashhun erst wenn ich die gfa auf disabeld setzte wird es halt nicht geladen (egal ob spine oder nicht) hab auch schon versucht in den mod inis doe einträge zu erweitern mit freeaiming ninja_gfa ,,, trotzdem gleiches ergebnis
    Das ist ein "Feature", das erst in der neuen Ninja Version enthalten sein wird. Ich hatte es jetzt vorher schon erwähnt, damit Modteams entsprechende INI-Einträge schon einmal aufnehmen können. Bisher haben diese Einträge noch keinen Effekt.

  5. Beiträge anzeigen #85 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Das wird mit Ninja 2.0 nicht mehr nötig sein. Das Foto vom Skript oben ist bspw. der gesamte Inhalt dieses Patches (kein Ikarus oder LeGo muss mit gepackt werden).
    Das ist ja schön

    Heißt das im Umkehrschluss dass LeGo und Ikarus (und weitere?) als Abhängigkeit von Ninja daherkommen? Oder wie wird man auf diese zugreifen können?

  6. Beiträge anzeigen #86 Zitieren
    Union - Testphase Avatar von Niko5511
    Registriert seit
    Jan 2018
    Beiträge
    2.570
     
    Niko5511 ist offline
    Laufen deine patches auch alle mit Union?

  7. Beiträge anzeigen #87 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von johnnyboyy Beitrag anzeigen
    Heißt das im Umkehrschluss dass LeGo und Ikarus (und weitere?) als Abhängigkeit von Ninja daherkommen? Oder wie wird man auf diese zugreifen können?
    Ich weiß nicht genau was du mit "als Abhängigkeit von Ninja" meinst. Kannst du die Frage noch einmal anders stellen?


    Zitat Zitat von Niko5511 Beitrag anzeigen
    Laufen deine patches auch alle mit Union?

  8. Beiträge anzeigen #88 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Ich weiß nicht genau was du mit "als Abhängigkeit von Ninja" meinst. Kannst du die Frage noch einmal anders stellen?
    Wie werden Ninja-Patches Funktionen aus LeGo verwenden können, wenn man die LeGo Scripte nicht mitliefert?

  9. Homepage besuchen Beiträge anzeigen #89 Zitieren
    Ritter Avatar von MaGoth
    Registriert seit
    May 2007
    Ort
    Russland (Samara)
    Beiträge
    1.407
     
    MaGoth ist offline
    Zitat Zitat von Niko5511 Beitrag anzeigen
    Laufen deine patches auch alle mit Union?
    Union don't support any script extensions, only AST.


    MfG MaGoth,
    |: WOG.de :|: WOG.en :|: WOG.ru :|: WOG.ro :|||: MAGIC-Team :|
    [Bild: 106462_941c3dcc88ff9e9b5597d9f24d9aea88.jpg]
    |: WOR.de :|: WOR.en :|: WOR.ru :|: WOR.ro :|||: Piranha-Bytes :|
    -=GOTHIC UND DIE FREUNDSCHAFT FÜR ALLE ZEITEN!=-

  10. Beiträge anzeigen #90 Zitieren
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.243
     
    Milky-Way ist offline
    Bei einem LoA-Spieler kommt es zu einer AccessViolation (siehe unten). Ganz oben im Callstack stehen DoStack-Operationen mit Offset etwas größer als der maximale Offset unserer Gothic.dat. Zum Beispiel Offset 4424076, während unsere letzte Funktion Offset 4422526 hat (und sehr kurz ist, siehe unten).

    Deutet das darauf hin, dass der Absturz in einem Ninja-Patch geschieht? Ich habe jetzt einfach mal geraten, dass sich die Ninja-Patch Offsets direkt nach den Gothic.dat Offsets einreihen. Falls dem nicht so ist, darf ich gerne darauf verwiesen werden, dass das hier nichts mit den Ninja-Patches zu tun hat

    Funktion an Offset 4422526
    Spoiler:(zum lesen bitte Text markieren)
    Code:
    func void init_welttest() {
        b_initmonsterattitudes();
        b_initguildattitudes();
        b_initnpcglobals();
        init_sub_welttest();
    };


    AccessViolation, soweit ich es dokumentieren konnte:
    Spoiler:(zum lesen bitte Text markieren)

    Code:
    ======================================= UNHANDLED EXCEPTION OCCURED ======================================================
    ======================================= CRASH INFOS: =====================================================================
    Gothic II - 2.6 (fix), Parser Version: 50
    User:  mach1,  CPUType: 586,  Mem: 0 MB total, 0 MB free
    Camera: Pos(-17076.3965/-1283.5531/-28463.5391), At(0.788438499/-0.413696885/0.455213875)
    Startup Options:-game:loa.ini
    =============================================== CALLSTACK : ==============================================================
    0023:00791B6A (0x0043818C 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+522 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1369+14 byte(s)
    0x0043818C - 4424076 -- ??? -- zPAR_OP_IS
    
    0023:00792504 (0x0043818C 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x0043818C - 4424076 -- ??? -- zPAR_TOK_CALL
    
    0023:00792504 (0x004382C2 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x004382C2 - 4424386 -- ??? -- zPAR_TOK_CALL
    
    0023:00792504 (0x0043A248 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x0043A248 - 4432456 -- ??? -- zPAR_TOK_CALL
    
    0023:00792504 (0x0043BF90 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x0043BF90 - 4439952 -- ??? -- zPAR_TOK_CALL
    
    0023:00792504 (0x00004849 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x00004849 - 18505 -- memint_prepareloopsandjumps -- zPAR_TOK_CALL
    
    0023:00792504 (0x000053AB 0x220361A4 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x000053AB - 21419 -- while -- zPAR_TOK_CALL
    
    0023:00792504 (0x001CB744 0x256601A4 0x6E43EBF8 0x00000000) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x001CB744 - 1881924 -- _mob_scrolls_isanycircleactive -- 2980
    
    0023:00792CBF (0x00AB40C0 0x0000827B 0x452441C8 0x00AB40C0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1551
    0x0000827B - 33403 -- pc_scroll_circle1_condition -- CallFunc
    
    0023:007028E0 (0x4524AB18 0x452441C8 0x00000000 0x006DC7B0) Gothic2.exe, oCInfoManager::GetInformation()+304 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oInfo.cpp, line 157+14 byte(s)
    
    0023:006DC8A0 (0x00AB4108 0x00000000 0x00AB40C0 0x0043B95F) Gothic2.exe, zSTRING::Init()+11552 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGameExternal.cpp, line 1074+36 byte(s)
    
    0023:00792568 (0x006DC7B0 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+3080 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1433
    0x006DC7B0 - 7194544 -- ??? -- zPAR_TOK_CALLEXTERN
    
    0023:00792504 (0x00438733 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x00438733 - 4425523 -- ??? -- zPAR_TOK_CALL
    
    0023:00792504 (0x0043B87F 0x221A7F24 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x0043B87F - 4438143 -- ??? -- zPAR_TOK_CALL
    
    0023:00792504 (0x00443308 0x00000EAF 0x1793D488 0x42200000) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0x00443308 - 4469512 -- ??? -- zPAR_TOK_CALL
    
    0023:00792CBF (0x00000000 0x006C89DD 0x0135FCA0 0x00000000) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1551
    
    0023:00819E80 (0xCCCCCCCC 0xCCCCCCCC 0xFF8BCCCC 0x83DC8B53) Gothic2.exe, SetFileAttributesA()+221884 byte(s)
    Die Funktionen, die ich finden konnte, werden in Zusammenhang mit einem Mob (Schreibpult) aufgerufen:
    Code:
    //////////////////////////////////////////////////////////////////
    //////////////////////  Schreibpult Mobsi   //////////////////////
    //////////////////////////////////////////////////////////////////
    var int MOBVAR_SCROLLS_CIRCLE[MAX_SCROLL_CIRCLES];
    
    //********************************************************************
    //  Utility functions
    //********************************************************************
    
    /**
     * Checks if the hero has learned a certain scroll circle
     */
    func int _MOB_SCROLLS_isCircleLearned(var int circle) {
      if (circle == 0) {
        return Player_Talent_Scroll[0];
      } else if (circle == 1) {
        return Player_Talent_Scroll[1];
      } else if (circle == 2) {
        return Player_Talent_Scroll[2];
      }else if (circle == 3) {
        return Player_Talent_Scroll[3];
      }else if (circle == 4) {
        return Player_Talent_Scroll[4];
      }else if (circle == 5) {
        return Player_Talent_Scroll[5];
      }else if (circle == 6) {
        return Player_Talent_Scroll[6];
      };
      
      MEM_AssertFail(ConcatStrings("_MOB_SCROLLS_isCircleLearned: Index couldn't be matched: ", IntToString(circle)));
      return FALSE;
    };
    
    
    /**
     * Check if a certain circle sub category is active on the mobsi dialog
     */
    func int _MOB_SCROLLS_isCircleActive(var int circle) {
      if (circle == 0) {
        return MOBVAR_SCROLLS_CIRCLE[0];
      } else if (circle == 1) {
        return MOBVAR_SCROLLS_CIRCLE[1];
      } else if (circle == 2) {
        return MOBVAR_SCROLLS_CIRCLE[2];
      }else if (circle == 3) {
        return MOBVAR_SCROLLS_CIRCLE[3];
      }else if (circle == 4) {
        return MOBVAR_SCROLLS_CIRCLE[4];
      }else if (circle == 5) {
        return MOBVAR_SCROLLS_CIRCLE[5];
      }else if (circle == 6) {
        return MOBVAR_SCROLLS_CIRCLE[6];
      };
      
      MEM_AssertFail(ConcatStrings("_MOB_SCROLLS_isCircleActive: Index couldn't be matched: ", IntToString(circle)));
      return FALSE;
    };
    
    func void _MOB_SCROLLS_resetCircles() {
      MOBVAR_SCROLLS_CIRCLE[0] = FALSE;
      MOBVAR_SCROLLS_CIRCLE[1] = FALSE;
      MOBVAR_SCROLLS_CIRCLE[2] = FALSE;
      MOBVAR_SCROLLS_CIRCLE[3] = FALSE;
      MOBVAR_SCROLLS_CIRCLE[4] = FALSE;
      MOBVAR_SCROLLS_CIRCLE[5] = FALSE;
      MOBVAR_SCROLLS_CIRCLE[6] = FALSE;
    };
    
    func void _MOB_SCROLLS_setCircleActive(var int circle) {
      _MOB_SCROLLS_resetCircles();
      
      // set the active circle sub category
      if (circle == 0) {
        MOBVAR_SCROLLS_CIRCLE[0] = TRUE;
      } else if (circle == 1) {
        MOBVAR_SCROLLS_CIRCLE[1] = TRUE;
      } else if (circle == 2) {
        MOBVAR_SCROLLS_CIRCLE[2] = TRUE;
      }else if (circle == 3) {
        MOBVAR_SCROLLS_CIRCLE[3] = TRUE;
      }else if (circle == 4) {
        MOBVAR_SCROLLS_CIRCLE[4] = TRUE;
      }else if (circle == 5) {
        MOBVAR_SCROLLS_CIRCLE[5] = TRUE;
      }else if (circle == 6) {
        MOBVAR_SCROLLS_CIRCLE[6] = TRUE;
      };
    };
    
    func int _MOB_SCROLLS_isAnyCircleActive() {
      var int i;
      i = 0;
      while(i < MAX_SCROLL_CIRCLES);
        if (_MOB_SCROLLS_isCircleActive(i) != FALSE) {
          return TRUE;
        };
        i += 1;
      end;
      
      return FALSE;
    };
    
    //**************************************************************
    //  Main Mobsi dialogs
    //**************************************************************
    
    func void SCHREIBPULT_S1()
    {
    	var C_Npc her;
    	her = Hlp_GetNpc(PC_Hero);
    	if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(her))
    	{
    		self.aivar[AIV_INVINCIBLE] = TRUE;
    		PLAYER_MOBSI_PRODUCTION = MOBSI_Schreibpult;
        
        // reset scrolls circle state variables
        _MOB_SCROLLS_resetCircles();
        
        // give hero 2 blank paper again (strangely it have to be 2!)
        CreateInvItems(her, ItMi_BlankPaper, 2);
    
    		AI_ProcessInfos(her);
    	};
    };
    
    instance PC_Schreibpult_End(C_Info)
    {
    	npc = PC_Hero;
    	nr = 999;
    	condition = PC_Schreibpult_End_Condition;
    	information = PC_Schreibpult_End_Info;
    	permanent = TRUE;
    	description = Dialog_Ende;
    };
    
    
    func int PC_Schreibpult_End_Condition()
    {
    	if((PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult) 
      && _MOB_SCROLLS_isAnyCircleActive() == FALSE)
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Schreibpult_End_Info()
    {
    	//CreateInvItems(self, ItMi_Feder, 1);
    	b_endproductiondialog();
    };
    
    
    instance PC_Schreibpult_BACK(C_Info)
    {
    	npc = PC_Hero;
    	nr = 99;
    	condition = PC_Schreibpult_BACK_Condition;
    	information = PC_Schreibpult_BACK_Info;
    	permanent = TRUE;
    	description = Dialog_Back;
    };
    
    
    func int PC_Schreibpult_BACK_Condition()
    {
    	if((PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult) 
      && _MOB_SCROLLS_isAnyCircleActive()) // Any circle sub category has to be open for a back option
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Schreibpult_BACK_Info()
    {
      _MOB_SCROLLS_resetCircles();
    };
    
    ///////////////////////////////////////////////////////////////////
    //  Sub categories                                               // 
    ///////////////////////////////////////////////////////////////////
    
    //*****************************************************************
    //  Circle 0 scrolls
    //*****************************************************************
    /////////////////////////////////////////////////////////////////////////////////
    instance PC_Scroll_Circle0(C_Info)
    {
    	npc = PC_Hero;
      nr = 1;
    	condition = PC_Scroll_Circle0_Condition;
    	information = PC_Scroll_Circle0_Info;
    	permanent = TRUE;
      description = "Kreisunabhängige Zauber";
    };
    
    
    func int PC_Scroll_Circle0_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isAnyCircleActive() == FALSE) // no sub category is active!
      && _MOB_SCROLLS_isCircleLearned(0)
      && Npc_HasItems(self, ItMi_BlankPaper) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Circle0_Info()
    {
      //show all suitable scrolls
      _MOB_SCROLLS_setCircleActive(SCROLL_CIRCLE_0);
    };
    /////////////////////////////////////////////////////////////////////////////////
    
    instance PC_Scroll_Light(C_Info)
    {
    	npc = PC_Hero;
    	nr = 1;
      condition = PC_Scroll_Light_Condition;
    	information = PC_Scroll_Light_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Light, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Light_Condition()
    {
    	if (PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
      && _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_0)
      && Npc_HasItems(self, ItSc_Light) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper) >= 1
      && Npc_HasItems(self, ItMi_Gold) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Light_Info()
    {
      Npc_RemoveInvItems(self, ItMi_Gold, 1);
      Npc_RemoveInvItems(self, ItMi_BlankPaper, 1);
    	CreateInvItems(hero, ItSc_Light, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    
    //*****************************************************************
    //  Circle 1 scrolls
    //*****************************************************************
    
    /////////////////////////////////////////////////////////////////////////////////
    instance PC_Scroll_Circle1(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Circle1_Condition;
    	information = PC_Scroll_Circle1_Info;
    	permanent = TRUE;
      description = "Kreis 1";
    };
    
    
    func int PC_Scroll_Circle1_Condition()
    {
    	if PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isAnyCircleActive() == FALSE // no sub category is active!
      && _MOB_SCROLLS_isCircleLearned(1)
      && Npc_HasItems(self, ItMi_BlankPaper_Circle1) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Circle1_Info()
    {
      //show all suitable scrolls
      _MOB_SCROLLS_setCircleActive(SCROLL_CIRCLE_1);
    };
    
    /////////////////////////////////////////////////////////////////////////////////
    
    
    instance PC_Scroll_Firebolt(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Firebolt_Condition;
    	information = PC_Scroll_Firebolt_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Firebolt, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Firebolt_Condition()
    {
    	if PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_1)
      && Npc_HasItems(self, ItSc_Firebolt) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle1) >= 1
      && Npc_HasItems(self, ItMi_Sulfur) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Firebolt_Info()
    {
      Npc_RemoveInvItems(self, ItMi_Sulfur, 1);
      Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle1, 1);
    	CreateInvItems(self, ItSc_Firebolt, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_Zap(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Zap_Condition;
    	information = PC_Scroll_Zap_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Zap, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Zap_Condition()
    {
    	if PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_1)
      && Npc_HasItems(self, ItSc_Zap) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle1) >= 1
      && Npc_HasItems(self, ItMi_Rockcrystal) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Zap_Info()
    {
      Npc_RemoveInvItems(self, ItMi_Rockcrystal, 1);
      Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle1, 1);
    	CreateInvItems(self, ItSc_Zap, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_LightHeal(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_LightHeal_Condition;
    	information = PC_Scroll_LightHeal_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_LightHeal, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_LightHeal_Condition()
    {
    	if PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_1)
      && Npc_HasItems(self, ItSc_LightHeal) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle1) >= 1
      && Npc_HasItems(self, ItPl_Health_Herb_01) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_LightHeal_Info()
    {
      Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle1, 1);
      Npc_RemoveInvItems(self, ItPl_Health_Herb_01, 1);
    	CreateInvItems(self, ItSc_LightHeal, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_SumWolf(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_SumWolf_Condition;
    	information = PC_Scroll_SumWolf_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_SummonWolf, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_SumWolf_Condition()
    {
    	if PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_1)
      && Npc_HasItems(self, ItSc_SumWolf) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle1) >= 1
      && Npc_HasItems(self, ItAt_WolfFur) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_SumWolf_Info()
    {
      Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle1, 1);
      Npc_RemoveInvItems(self, ItAt_WolfFur, 1);
    	CreateInvItems(self, ItSc_SumWolf, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_GreenTentacle(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_GreenTentacle_Condition;
    	information = PC_Scroll_GreenTentacle_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_GreenTentacle, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_GreenTentacle_Condition()
    {
    	if PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_1)
      && Npc_HasItems(self, ItSc_GreenTentacle) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle1) >= 1
      && Npc_HasItems(self, ItAt_Sting) >= 1
      && Npc_HasItems(self, ItPl_Speed_Herb_01) >= 2
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_GreenTentacle_Info()
    {
      Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle1, 1);
      Npc_RemoveInvItems(self, ItAt_Sting, 1);
      Npc_RemoveInvItems(self, ItPl_Speed_Herb_01, 2);
    	CreateInvItems(self, ItSc_GreenTentacle, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    //*****************************************************************
    //  Circle 2 scrolls
    //*****************************************************************
    /////////////////////////////////////////////////////////////////////////////////
    instance PC_Scroll_Circle2(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Circle2_Condition;
    	information = PC_Scroll_Circle2_Info;
    	permanent = TRUE;
      description = "Kreis 2";
    };
    
    
    func int PC_Scroll_Circle2_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isAnyCircleActive() == FALSE) // no sub category is active!
      && _MOB_SCROLLS_isCircleLearned(2)
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Circle2_Info()
    {
      //show all suitable scrolls
      _MOB_SCROLLS_setCircleActive(SCROLL_CIRCLE_2);
    };
    /////////////////////////////////////////////////////////////////////////////////
    
    instance PC_Scroll_Fireball(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Fireball_Condition;
    	information = PC_Scroll_Fireball_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_InstantFireball, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Fireball_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_2)
      && Npc_HasItems(self, ItSc_InstantFireball) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
      && Npc_HasItems(self, ItMi_Pitch) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Fireball_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle2, 1);
      Npc_RemoveInvItems(self, ItMi_Pitch, 1);
    	CreateInvItems(self, ItSc_InstantFireball, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_Icebolt(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Icebolt_Condition;
    	information = PC_Scroll_Icebolt_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Icebolt, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Icebolt_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_2)
      && Npc_HasItems(self, ItSc_Icebolt) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
      && Npc_HasItems(self, ItMi_Quartz) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Icebolt_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle2, 1);
      Npc_RemoveInvItems(self, ItMi_Quartz, 1);
    	CreateInvItems(self, ItSc_Icebolt, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_Icelance(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Icelance_Condition;
    	information = PC_Scroll_Icelance_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Icelance, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Icelance_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_2)
      && Npc_HasItems(self, ItSc_Icelance) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
      && Npc_HasItems(self, ItMi_Rockcrystal) >= 1
      && Npc_HasItems(self, ItAt_Teeth) >= 4
      && Npc_HasItems(self, ItFo_Water) >= 2
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Icelance_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle2, 1);
      Npc_RemoveInvItems(self, ItMi_Rockcrystal, 1);
      Npc_RemoveInvItems(self, ItAt_Teeth, 4);
      Npc_RemoveInvItems(self, ItFo_Water, 2);
    	CreateInvItems(self, ItSc_Icelance, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_Whirlwind(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Whirlwind_Condition;
    	information = PC_Scroll_Whirlwind_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Whirlwind, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Whirlwind_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_2)
      && Npc_HasItems(self, ItSc_Whirlwind) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
      && Npc_HasItems(self, ItAt_Wing) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Whirlwind_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle2, 1);
      Npc_RemoveInvItems(self, ItAt_Wing, 1);
    	CreateInvItems(self, ItSc_Whirlwind, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_WindFist(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_WindFist_Condition;
    	information = PC_Scroll_WindFist_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_WindFist, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_WindFist_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_2)
      && Npc_HasItems(self, ItSc_WindFist) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
      && Npc_HasItems(self, ItMi_Coal) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_WindFist_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle2, 1);
      Npc_RemoveInvItems(self, ItMi_Coal, 1);
    	CreateInvItems(self, ItSc_WindFist, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_Sleep(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Sleep_Condition;
    	information = PC_Scroll_Sleep_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Sleep, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Sleep_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_2)
      && Npc_HasItems(self, ItSc_Sleep) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
      && Npc_HasItems(self, ItPl_SwampHerb) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Sleep_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle2, 1);
      Npc_RemoveInvItems(self, ItPl_SwampHerb, 1);
    	CreateInvItems(self, ItSc_Sleep, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_SuckEnergy(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_SuckEnergy_Condition;
    	information = PC_Scroll_SuckEnergy_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_SuckEnergy, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_SuckEnergy_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_2)
      && Npc_HasItems(self, ItSc_SuckEnergy) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
      && Npc_HasItems(self, ItMi_HumanBlood) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_SuckEnergy_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle2, 1);
      Npc_RemoveInvItems(self, ItMi_HumanBlood, 1);
    	CreateInvItems(self, ItSc_SuckEnergy, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_SumGobSkel(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_SumGobSkel_Condition;
    	information = PC_Scroll_SumGobSkel_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_SummonGoblinSkeleton, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_SumGobSkel_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_2)
      && Npc_HasItems(self, ItSc_SumGobSkel) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle2) >= 1
      && Npc_HasItems(self, ItAt_GoblinBone) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_SumGobSkel_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle2, 1);
      Npc_RemoveInvItems(self, ItAt_GoblinBone, 1);
    	CreateInvItems(self, ItSc_SumGobSkel, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    //*****************************************************************
    //  Circle 3 scrolls
    //*****************************************************************
    /////////////////////////////////////////////////////////////////////////////////
    instance PC_Scroll_Circle3(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Circle3_Condition;
    	information = PC_Scroll_Circle3_Info;
    	permanent = TRUE;
      description = "Kreis 3";
    };
    
    
    func int PC_Scroll_Circle3_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isAnyCircleActive() == FALSE) // no sub category is active!
      && _MOB_SCROLLS_isCircleLearned(3)
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Circle3_Info()
    {
      //show all suitable scrolls
      _MOB_SCROLLS_setCircleActive(SCROLL_CIRCLE_3);
    };
    /////////////////////////////////////////////////////////////////////////////////
    
    
    instance PC_Scroll_IceCube(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_IceCube_Condition;
    	information = PC_Scroll_IceCube_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_IceCube, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_IceCube_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_IceCube) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ITMI_Saphir_Raw) >= 1
      && Npc_HasItems(self, ItMi_Quartz) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_IceCube_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ITMI_Saphir_Raw, 1);
      Npc_RemoveInvItems(self, ItMi_Quartz, 1);
    	CreateInvItems(self, ItSc_IceCube, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_SummonGuardian(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_SummonGuardian_Condition;
    	information = PC_Scroll_SummonGuardian_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_SummonGuardian, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_SummonGuardian_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_SummonGuardian) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ItMi_SandStone) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_SummonGuardian_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ItMi_SandStone, 1);
    	CreateInvItems(self, ItSc_SummonGuardian, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_Firestorm(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Firestorm_Condition;
    	information = PC_Scroll_Firestorm_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Firestorm, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Firestorm_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_Firestorm) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ItMi_Salpeter) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Firestorm_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ItMi_Salpeter, 1);
    	CreateInvItems(self, ItSc_Firestorm, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_Geyser(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Geyser_Condition;
    	information = PC_Scroll_Geyser_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Geyser, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Geyser_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_Geyser) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ItMi_Sodium) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Geyser_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ItMi_Sodium, 1);
    	CreateInvItems(self, ItSc_Geyser, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_BeliarsRage(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_BeliarsRage_Condition;
    	information = PC_Scroll_BeliarsRage_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_BeliarsRage, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_BeliarsRage_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_BeliarsRage) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ITMI_Rubin_Raw) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_BeliarsRage_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ITMI_Rubin_Raw, 1);
    	CreateInvItems(self, ItSc_BeliarsRage, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_ChargeZap(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_ChargeZap_Condition;
    	information = PC_Scroll_ChargeZap_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_ChargeZap, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_ChargeZap_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_ThunderBall) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ItMi_Rockcrystal) >= 1
      && Npc_HasItems(self, ItMi_Sulfur) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_ChargeZap_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ItMi_Rockcrystal, 1);
      Npc_RemoveInvItems(self, ItMi_Sulfur, 1);
    	CreateInvItems(self, ItSc_ThunderBall, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_MediumHeal(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_MediumHeal_Condition;
    	information = PC_Scroll_MediumHeal_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_MediumHeal, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_MediumHeal_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_MediumHeal) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ItPl_Health_Herb_02) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_MediumHeal_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ItPl_Health_Herb_02, 1);
    	CreateInvItems(self, ItSc_MediumHeal, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_Fear(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Fear_Condition;
    	information = PC_Scroll_Fear_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Fear, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Fear_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_Fear) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ItAt_GoblinBone) >= 1
      && Npc_HasItems(self, ItMi_Coal) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Fear_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ItAt_GoblinBone, 1);
      Npc_RemoveInvItems(self, ItMi_Coal, 1);
    	CreateInvItems(self, ItSc_Fear, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_Thunderstorm(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Thunderstorm_Condition;
    	information = PC_Scroll_Thunderstorm_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Thunderstorm, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Thunderstorm_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_Thunderstorm) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ItMi_Quartz) >= 1
      && Npc_HasItems(self, ItMi_Aquamarine) >= 5
      && Npc_HasItems(self, ItAt_Wing) >= 2
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Thunderstorm_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ItMi_Quartz, 1);
      Npc_RemoveInvItems(self, ItMi_Aquamarine, 5);
      Npc_RemoveInvItems(self, ItAt_Wing, 2);
    	CreateInvItems(self, ItSc_Thunderstorm, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_SumSkel(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_SumSkel_Condition;
    	information = PC_Scroll_SumSkel_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_SummonSkeleton, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_SumSkel_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_3)
      && Npc_HasItems(self, ItSc_SumSkel) >= 1
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle3) >= 1
      && Npc_HasItems(self, ItAt_SkeletonBone) >= 1
      && Npc_HasItems(self, ITMI_Smaragd_Raw) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_SumSkel_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle3, 1);
      Npc_RemoveInvItems(self, ItAt_SkeletonBone, 1);
      Npc_RemoveInvItems(self, ITMI_Smaragd_Raw, 1);
    	CreateInvItems(self, ItSc_SumSkel, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    //*****************************************************************
    //  Circle 4 scrolls
    //*****************************************************************
    /////////////////////////////////////////////////////////////////////////////////
    instance PC_Scroll_Circle4(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Circle4_Condition;
    	information = PC_Scroll_Circle4_Info;
    	permanent = TRUE;
      description = "Kreis 4";
    };
    
    
    func int PC_Scroll_Circle4_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isAnyCircleActive() == FALSE) // no sub category is active!
      && _MOB_SCROLLS_isCircleLearned(4)
      && Npc_HasItems(self, ItMi_BlankPaper_Circle4) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Circle4_Info()
    {
      //show all suitable scrolls
      _MOB_SCROLLS_setCircleActive(SCROLL_CIRCLE_4);
    };
    /////////////////////////////////////////////////////////////////////////////////
    
    instance PC_Scroll_LightningFlash(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_LightningFlash_Condition;
    	information = PC_Scroll_LightningFlash_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_LightningFlash, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_LightningFlash_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_4)
      && Npc_HasItems(self, ItRu_LightningFlash) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle4) >= 1
      && Npc_HasItems(self, ItMi_Rockcrystal) >= 2
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_LightningFlash_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle4, 1);
      Npc_RemoveInvItems(self, ItMi_Rockcrystal, 2);
    	CreateInvItems(self, ItSc_LightningFlash, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_ChargeFireball(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_ChargeFireball_Condition;
    	information = PC_Scroll_ChargeFireball_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_ChargeFireball, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_ChargeFireball_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_4)
      && Npc_HasItems(self, ItRu_ChargeFireball) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle4) >= 1
      && Npc_HasItems(self, ItMi_Pitch) >= 1
      && Npc_HasItems(self, ItMi_Sulfur) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_ChargeFireball_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle4, 1);
      Npc_RemoveInvItems(self, ItMi_Pitch, 1);
      Npc_RemoveInvItems(self, ItMi_Sulfur, 1);
    	CreateInvItems(self, ItSc_ChargeFireball, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_WaterFist(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_WaterFist_Condition;
    	information = PC_Scroll_WaterFist_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_WaterFist, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_WaterFist_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_4)
      && Npc_HasItems(self, ItRu_WaterFist) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle4) >= 1
      && Npc_HasItems(self, itat_LurkerSkin) >= 2
      && Npc_HasItems(self, ItMi_Quartz) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_WaterFist_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle4, 1);
      Npc_RemoveInvItems(self, itat_LurkerSkin, 2);
      Npc_RemoveInvItems(self, ItMi_Quartz, 1);
    	CreateInvItems(self, ItSc_WaterFist, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_Swarm(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Swarm_Condition;
    	information = PC_Scroll_Swarm_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Swarm, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Swarm_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_4)
      && Npc_HasItems(self, ItRu_Swarm) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle4) >= 1
      && Npc_HasItems(self, ItAt_Sting) >= 3
      && Npc_HasItems(self, ItAt_Wing) >= 6
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Swarm_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle4, 1);
      Npc_RemoveInvItems(self, ItAt_Sting, 3);
      Npc_RemoveInvItems(self, ItAt_Wing, 6);
    	CreateInvItems(self, ItSc_Swarm, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_HarmUndead(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_HarmUndead_Condition;
    	information = PC_Scroll_HarmUndead_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_DestroyUndead, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_HarmUndead_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_4)
      && Npc_HasItems(self, ItRu_HarmUndead) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle4) >= 1
      && Npc_HasItems(self, ItMi_HolyWater) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_HarmUndead_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle4, 1);
      Npc_RemoveInvItems(self, ItMi_HolyWater, 1);
    	CreateInvItems(self, ItSc_HarmUndead, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_SummonZombie(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_SummonZombie_Condition;
    	information = PC_Scroll_SummonZombie_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_SummonZombie, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_SummonZombie_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_4)
      && Npc_HasItems(self, ItRu_SummonZombie) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle4) >= 1
      && Npc_HasItems(self, ItMi_UndeadHead) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_SummonZombie_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle4, 1);
      Npc_RemoveInvItems(self, ItMi_UndeadHead, 1);
    	CreateInvItems(self, ItSc_SummonZombie, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_SumGol(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_SumGol_Condition;
    	information = PC_Scroll_SumGol_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_SummonGolem, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_SumGol_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_4)
      && Npc_HasItems(self, ItRu_SumGol) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle4) >= 1
      && Npc_HasItems(self, ItAt_StoneGolemHeart) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_SumGol_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle4, 1);
      Npc_RemoveInvItems(self, ItAt_StoneGolemHeart, 1);
    	CreateInvItems(self, ItSc_SumGol, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    
    //*****************************************************************
    //  Circle 5 scrolls
    //*****************************************************************
    /////////////////////////////////////////////////////////////////////////////////
    instance PC_Scroll_Circle5(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Circle5_Condition;
    	information = PC_Scroll_Circle5_Info;
    	permanent = TRUE;
      description = "Kreis 5";
    };
    
    
    func int PC_Scroll_Circle5_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isAnyCircleActive() == FALSE) // no sub category is active!
      && _MOB_SCROLLS_isCircleLearned(5)
      && Npc_HasItems(self, ItMi_BlankPaper_Circle5) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Circle5_Info()
    {
      //show all suitable scrolls
      _MOB_SCROLLS_setCircleActive(SCROLL_CIRCLE_5);
    };
    /////////////////////////////////////////////////////////////////////////////////
    
    instance PC_Scroll_SumDemon(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_SumDemon_Condition;
    	information = PC_Scroll_SumDemon_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_SummonDemon, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_SumDemon_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_5)
      && Npc_HasItems(self, ItRu_SumDemon) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle5) >= 1
      && Npc_HasItems(self, ItAt_DemonHeart) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_SumDemon_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle5, 1);
      Npc_RemoveInvItems(self, ItAt_DemonHeart, 1);
    	CreateInvItems(self, ItSc_SumDemon, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_Pyrokinesis(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Pyrokinesis_Condition;
    	information = PC_Scroll_Pyrokinesis_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Pyrokinesis, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Pyrokinesis_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_5)
      && Npc_HasItems(self, ItRu_Pyrokinesis) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle5) >= 1
      && Npc_HasItems(self, ItMi_Pitch) >= 1
      && Npc_HasItems(self, ItMi_Sulfur) >= 1
      && Npc_HasItems(self, ItAt_WaranFiretongue) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Pyrokinesis_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle5, 1);
      Npc_RemoveInvItems(self, ItMi_Pitch, 1);
      Npc_RemoveInvItems(self, ItMi_Sulfur, 1);
      Npc_RemoveInvItems(self, ItAt_WaranFiretongue, 1);
    	CreateInvItems(self, ItSc_Pyrokinesis, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_FullHeal(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_FullHeal_Condition;
    	information = PC_Scroll_FullHeal_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_FullHeal, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_FullHeal_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_5)
      && Npc_HasItems(self, ItRu_FullHeal) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle5) >= 1
      && Npc_HasItems(self, ItPl_Health_Herb_03) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_FullHeal_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle5, 1);
      Npc_RemoveInvItems(self, ItPl_Health_Herb_03, 1);
    	CreateInvItems(self, ItSc_FullHeal, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    instance PC_Scroll_IceWave(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_IceWave_Condition;
    	information = PC_Scroll_IceWave_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_IceWave, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_IceWave_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_5)
      && Npc_HasItems(self, ItRu_IceWave) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle5) >= 1
      && Npc_HasItems(self, ITMI_Diamant_Raw) >= 1
      && Npc_HasItems(self, ItAt_IceGolemHeart) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_IceWave_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle5, 1);
      Npc_RemoveInvItems(self, ITMI_Diamant_Raw, 1);
      Npc_RemoveInvItems(self, ItAt_IceGolemHeart, 1);
    	CreateInvItems(self, ItSc_IceWave, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    //*****************************************************************
    //  Circle 6 scrolls
    //*****************************************************************
    /////////////////////////////////////////////////////////////////////////////////
    instance PC_Scroll_Circle6(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Circle6_Condition;
    	information = PC_Scroll_Circle6_Info;
    	permanent = TRUE;
      description = "Kreis 6";
    };
    
    
    func int PC_Scroll_Circle6_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isAnyCircleActive() == FALSE) // no sub category is active!
      && _MOB_SCROLLS_isCircleLearned(6)
      && Npc_HasItems(self, ItMi_BlankPaper_Circle6) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Circle6_Info()
    {
      //show all suitable scrolls
      _MOB_SCROLLS_setCircleActive(SCROLL_CIRCLE_6);
    };
    /////////////////////////////////////////////////////////////////////////////////
    
    
    instance PC_Scroll_Firerain(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Firerain_Condition;
    	information = PC_Scroll_Firerain_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Firerain, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Firerain_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_6)
      && Npc_HasItems(self, ItRu_Firerain) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle6) >= 1
      && Npc_HasItems(self, ItMi_Pitch) >= 1
      && Npc_HasItems(self, ItMi_SulfuricAcid) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Firerain_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle6, 1);
      Npc_RemoveInvItems(self, ItMi_Pitch, 1);
      Npc_RemoveInvItems(self, ItMi_SulfuricAcid, 1);
    	CreateInvItems(self, ItSc_Firerain, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_MasterOfDisaster(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_MasterOfDisaster_Condition;
    	information = PC_Scroll_MasterOfDisaster_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_MasterOfDisaster, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_MasterOfDisaster_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_6)
      && Npc_HasItems(self, ItRu_MasterOfDisaster) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle6) >= 1
      && Npc_HasItems(self, ItMi_HolyWater) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_MasterOfDisaster_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle6, 1);
      Npc_RemoveInvItems(self, ItMi_HolyWater, 1);
    	CreateInvItems(self, ItSc_MasterOfDisaster, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    
    instance PC_Scroll_Shrink(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Shrink_Condition;
    	information = PC_Scroll_Shrink_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Shrink, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Shrink_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_6)
      && Npc_HasItems(self, ItRu_Shrink) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle6) >= 1
      && Npc_HasItems(self, ItAt_TrollTooth) >= 1
      && Npc_HasItems(self, ItAt_GoblinBone) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Shrink_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle6, 1);
      Npc_RemoveInvItems(self, ItAt_TrollTooth, 1);
      Npc_RemoveInvItems(self, ItAt_GoblinBone, 1);
    	CreateInvItems(self, ItSc_Shrink, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_ArmyOfDarkness(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_ArmyOfDarkness_Condition;
    	information = PC_Scroll_ArmyOfDarkness_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_ArmyOfDarkness, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_ArmyOfDarkness_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_6)
      && Npc_HasItems(self, ItRu_ArmyOfDarkness) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle6) >= 1
      && Npc_HasItems(self, ItMi_Skull) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_ArmyOfDarkness_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle6, 1);
      Npc_RemoveInvItems(self, ItMi_Skull, 1);
    	CreateInvItems(self, ItSc_ArmyOfDarkness, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_BreathOfDeath(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_BreathOfDeath_Condition;
    	information = PC_Scroll_BreathOfDeath_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_BreathOfDeath, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_BreathOfDeath_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_6)
      && Npc_HasItems(self, ItRu_BreathOfDeath) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle6) >= 1
      && Npc_HasItems(self, ItAt_DemonHeart) >= 1
      && Npc_HasItems(self, ItAt_SkeletonBone) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_BreathOfDeath_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle6, 1);
      Npc_RemoveInvItems(self, ItAt_DemonHeart, 1);
      Npc_RemoveInvItems(self, ItAt_SkeletonBone, 1);
    	CreateInvItems(self, ItSc_BreathOfDeath, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_MassDeath(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_MassDeath_Condition;
    	information = PC_Scroll_MassDeath_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_MassDeath, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_MassDeath_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_6)
      && Npc_HasItems(self, ItRu_MassDeath) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle6) >= 1
      && Npc_HasItems(self, ItMi_DarkPearl) >= 3
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_MassDeath_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle6, 1);
      Npc_RemoveInvItems(self, ItMi_DarkPearl, 3);
    	CreateInvItems(self, ItSc_MassDeath, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_Skull(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Skull_Condition;
    	information = PC_Scroll_Skull_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_Skull, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Skull_Condition()
    {
    	if(PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult)
    	&& _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_6)
      && Npc_HasItems(self, ItRu_Skull) >= 1  // from the 4. circle on, one needs the rune equivalent, scroll isn't enough
      // Ingredients
      && Npc_HasItems(self, ItMi_BlankPaper_Circle6) >= 1
      && Npc_HasItems(self, ItMi_Pitch) >= 1
      && Npc_HasItems(self, ItAt_SkeletonBone) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Skull_Info()
    {
    	Npc_RemoveInvItems(self, ItMi_BlankPaper_Circle6, 1);
      Npc_RemoveInvItems(self, ItMi_Pitch, 1);
      Npc_RemoveInvItems(self, ItAt_SkeletonBone, 1);
    	CreateInvItems(self, ItSc_Skull, 1);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    //////////////////////////////////////////////////////////////////
    ////////////////////////////Story/////////////////////////////////
    //////////////////////////////////////////////////////////////////
    instance PC_Scroll_Drache(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_Drache_Condition;
    	information = PC_Scroll_Drache_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_TRFDragon, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_Drache_Condition()
    {
    	if PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_0)
    	&& PLAYER_TALENT_SCROLL[SCROLL_DRAGON] == TRUE
    	&& Npc_HasItems(self, ItPl_Strength_Herb_01) >= 3
    	&& Npc_HasItems(self, ItAt_WaranFiretongue) >= 1
    	&& Npc_HasItems(self, ItPl_Mana_Herb_03) >= 12
    	&& Npc_HasItems(self, ItPl_Drachenseele) >= 1
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_Drache_Info()
    {
    	Npc_RemoveInvItems(self, ItPl_Strength_Herb_01, 3);
    	Npc_RemoveInvItems(self, ItAt_WaranFiretongue, 1);
    	Npc_RemoveInvItems(self, ItPl_Mana_Herb_03, 12);
    	Npc_RemoveInvItems(self, ItPl_Drachenseele, 1);
    	CreateInvItems(hero, ItSc_TrfDragon, 2);
    	Print(PRINT_ScrollSuccess);
    };
    
    
    instance PC_Scroll_OpenLock(C_Info)
    {
    	npc = PC_Hero;
    	condition = PC_Scroll_OpenLock_Condition;
    	information = PC_Scroll_OpenLock_Info;
    	permanent = TRUE;
      description = ConcatStrings(NAME_SPL_OpenLock, ConcatStrings("-", NAME_Write_Scroll));
    };
    
    
    func int PC_Scroll_OpenLock_Condition()
    {
      if PLAYER_MOBSI_PRODUCTION == MOBSI_Schreibpult
      && _MOB_SCROLLS_isCircleActive(SCROLL_CIRCLE_0)
    	&& PLAYER_TALENT_SCROLL[SCROLL_OpenLock] == TRUE
    	&& Npc_HasItems(self, ItPl_Perm_Herb) >= 1
    	&& Npc_HasItems(self, ItAt_WaranFiretongue) >= 2
    	&& Npc_HasItems(self, itmi_loa_kristall_dexterity) >= 3
    	&& Npc_HasItems(self, ItMi_Alkohol) >= 1    
    	{
    		return TRUE;
    	};
    };
    
    func void PC_Scroll_OpenLock_Info()
    {
    	Npc_RemoveInvItems(self, ItPl_Perm_Herb, 1);
    	Npc_RemoveInvItems(self, ItAt_WaranFiretongue, 2);
    	Npc_RemoveInvItems(self, itmi_loa_kristall_dexterity, 3);
    	Npc_RemoveInvItems(self, ItMi_Alkohol, 1);
    	CreateInvItems(hero, ItSc_OpenLock, 1);
    	Print(PRINT_ScrollSuccess);
    };


    Bericht des Spielers
    Spoiler:(zum lesen bitte Text markieren)
    Zitat Zitat von ZeroLord Beitrag anzeigen
    tja hab jetzt eine komplete neue saubere gog g2 installation nach anleitung installiert (ohne spine) und bekomme bei ashuun trotzdem seltsame fehler mit dem renderer.
    mein char freezte einfach öfter fest und nach einiger zeit kommt der acces violation mit:
    jede hilfe wär echt gut. vielleicht kann ja jemand bei dem die mod mit renderer läuft bitte mal die systempack und gothic ini hochladen nur damit ich mal vergleichen kann ...


    ======================================= UNHANDLED EXCEPTION OCCURED ======================================================
    ======================================= CRASH INFOS: =====================================================================
    Gothic II - 2.6 (fix), Parser Version: 50
    User: mach1, CPUType: 586, Mem: 0 MB total, 0 MB free
    Camera: Pos(-17076.3965/-1283.5531/-28463.5391), At(0.788438499/-0.413696885/0.455213875)
    Startup Options:-game:loa.ini
    =============================================== CALLSTACK : ==============================================================
    0023:00791B6A (0x0043818C 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser:oStack()+522 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1369+14 byte(s)
    0023:00792504 (0x0043818C 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x004382C2 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x0043A248 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x0043BF90 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x00004849 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x000053AB 0x220361A4 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x001CB744 0x256601A4 0x6E43EBF8 0x00000000) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792CBF (0x00AB40C0 0x0000827B 0x452441C8 0x00AB40C0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1551
    0023:007028E0 (0x4524AB18 0x452441C8 0x00000000 0x006DC7B0) Gothic2.exe, oCInfoManager:etInformation()+304 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oInfo.cpp, line 157+14 byte(s)
    0023:006DC8A0 (0x00AB4108 0x00000000 0x00AB40C0 0x0043B95F) Gothic2.exe, zSTRING::Init()+11552 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGameExternal.cpp, line 1074+36 byte(s)
    0023:00792568 (0x006DC7B0 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser:oStack()+3080 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1433
    0023:00792504 (0x00438733 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x0043B87F 0x221A7F24 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x00443308 0x00000EAF 0x1793D488 0x42200000) Gothic2.exe, zCParser:oStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792CBF (0x00000000 0x006C89DD 0x0135FCA0 0x00000000) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1551
    0023:00819E80 (0xCCCCCCCC 0xCCCCCCCC 0xFF8BCCCC 0x83DC8B53) Gothic2.exe, SetFileAttributesA()+221884 byte(s)


    edit : achjaa und dann ignoriert die mod einen bereits vorhandenen freeaim ninja nicht obwohl ja in der loa ini dort die gfa steht. erst gfa auf disabeld umzubennen brint hier abhilfe ...
    Geändert von Milky-Way (21.06.2019 um 04:05 Uhr)

  11. Beiträge anzeigen #91 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Lehona Beitrag anzeigen
    Wie werden Ninja-Patches Funktionen aus LeGo verwenden können, wenn man die LeGo Scripte nicht mitliefert?
    Danke fürs Aufklären der Frage.
    Hier dazu ein paar Punkte:
    1. Wenn ein Patch Ikarus/LeGo benötigt, müsste er sie enthalten. Weil (mindestens) LeGo sich aber noch in aktiver Entwicklung befindet, stellt das ein Problem da: Wenn ein (älterer) Patch eine LeGo Version enthält, die irgendwann älter ist als die der neueren unterliegenden Mod oder einer der anderen Patches, führt das zu Inkompatibilitäten.
      Jeder Patch müsste also fortlaufend auf die neusten Ikarus/LeGo Versionen aktualisiert werden damit er benutzbar bleibt. So war das bisher der Fall und das ist schlicht weg nicht machbar. Es kann von niemandem erwartet werden, dass er "Life-time Support" für einen Patch bietet. So würde Ninja einen undurchdringlichen Brei von inkompatiblen Patches ins Leben rufen.
    2. Ikarus/LeGo müssen für die Benutzung in Patches leicht angepasst werden. Das hat größtenteils etwas mit Kompatibilität zu tun. Von daher bedarf es für Patches eine "spezielle Version". Diese Version müsste jeder Patch beinhalten, der Ikarus/LeGo verwendet. Das kann aber auch sehr leicht schief gehen, wenn jemand einen Patch mit der "Standardversion" von Ikarus/LeGo erstellt oder sich an veralteten Patches orientiert. Genau so kann das nervig werden, wenn jemand diese angepasste Version versehentlich in seine Mod einbaut. Es wäre wenig erfreulich, wenn verschiedene LeGo Versionen kursieren, die nur für Verwirrung sorgen.
    3. Für die meisten Anwendungen in Patches werden Ikarus und LeGo zwingend benötigt, weil man sonst keine großen Änderungen in den bestehenden Skriptfluss integrieren kann.
    Die logische Schlussfolgerung aus diesen Voraussetzungen ist es, dass Ninja diese angepassten Versionen von Ikarus und LeGo einfach mitliefert und Patches zur Verfügung stellt. Weil Ninja ab der neuen Version von den Patches abgekoppelt ist, brauche ich dann nur zentral Ninja aktualisieren, während die Patches für immer (hah!) aktuell und kompatibel bleiben.

    Ich hatte mich sehr davor gesträubt Ikarus und LeGo mit in Ninja zu verpacken, denn Ninja ist kein Skriptpaket und hängt auch in keinster Weise von Ikarus/LeGo ab und hat auch einfach nichts mit ihnen zu tun. Außerdem könnte der Eindruck entstehen, Ninja würde Ikarus und LeGo in jede Mod einführen. Das ist aber nicht richtig: Ninja allein nimmt keine Änderungen vor und lässt Ikarus und LeGo der Mod unangetastet. Erst wenn ein Patch sagt "Hallo, einmal Ikarus/LeGo bitte!" werden sie geparst.

    Für Ninja habe ich vorm Moddertreffen eine Dokumentation angelegt. Dort steht schon alles - sehr umfangreich - was man für Ninja 2.0 wissen muss. Hier der entsprechende Wiki-Eintrag zu dem Thema: https://github.com/szapp/Ninja/wiki/...karus-and-lego.


    Zitat Zitat von Milky-Way Beitrag anzeigen
    Bei einem LoA-Spieler kommt es zu einer AccessViolation (siehe unten). Ganz oben im Callstack stehen DoStack-Operationen mit Offset etwas größer als der maximale Offset unserer Gothic.dat. Zum Beispiel Offset 4424076, während unsere letzte Funktion Offset 4422526 hat (und sehr kurz ist, siehe unten).

    Deutet das darauf hin, dass der Absturz in einem Ninja-Patch geschieht?
    Nein, nicht zwingend, denn Ikarus erstellt dynamisch generierten Code, der z.B. durch das Aufrufen von Enginefunktionen mit CALL_* oder durchs Überschreiben von Funktionen wie MEM_ReadInt entsteht, jenseits der Offsets, die man in der Gothic.dat findet. Das gilt auch für while und repeat Schleifen, wie das hier der Fall ist.

    Du konntest ja schon einen Zusammenhang mit dem Schreibpult feststellen. Der Absturz passiert beim einmaligen Generieren des Codes für die while Schleife in _MOB_SCROLLS_isAnyCircleActive. Aus MEMINT_PrepareLoopsAndJumps wird scheinbar in einen dieser dynamisch generierten Codeabschnitte gesprungen (wahrscheinlich eine überschriebene Funktion) und da verliert sich dann die Spur. Ich bin mir nicht sicher, ob man dem Stacktrace ab da an systematisch nachverfolgen kann.

    While hat (hatte: im Ikarus Repo wurde das mittlerweile behoben) einige Probleme mit solchem dynamisch generiertem Code. Was ich mir vorstellen könnte ist, dass durch einen Patch mit Ninja die entsprechenden Ikarus-Funktionen überschrieben werden (an einen neuen Offset verschoben werden). Das mag in diesem Fall eines der Probleme in Ikarus hervorrufen, die wir mittlerweile in Ikarus behoben haben (rekursives Folgen von Jumps). Wenn ich mit meiner Spekulation also richtig liegen sollte, sollte das Problem spätestens mit der neuen Ninja Version behoben sein. Dort werden diese neusten Änderungen von Ikarus erzwungen.

    Eurem Spieler könnt ihr allerdings einfach sagen, dass weder der Manaregenerations-, noch der Workaround-, noch der Freies Zielen Patch in LoA sinnvoll sind, weil diese Mechaniken alle schon in der Mod enthalten sind (wenn ich mich recht erinnere). Er sollte also einfach alle Patches mit Ninja deaktivieren. Von einer Ankündigung wie "Alle Patches die Ninja benutzen sind mit LoA inkompatibel" würde ich abraten, weil diese Probleme mit Ninja 2.0 schon alle behoben sind.

  12. Beiträge anzeigen #92 Zitieren
    Local Hero Avatar von Mark56
    Registriert seit
    Sep 2010
    Beiträge
    254
     
    Mark56 ist offline
    Hey, I really like overall changes, but one has me troubled. Maybe I do not understand it entirely, but for now, LeGo archives "objects" in separate file "scriptsave.sav". Why not to store objects for each patch in its own file "patchname.sav" ?
    Correct me if I am misinterpreting something here

  13. Beiträge anzeigen #93 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Mark56 Beitrag anzeigen
    Hey, I really like overall changes, but one has me troubled. Maybe I do not understand it entirely, but for now, LeGo archives "objects" in separate file "scriptsave.sav". Why not to store objects for each patch in its own file "patchname.sav" ?
    Correct me if I am misinterpreting something here
    Well, the major problem with using LeGo with Ninja so far was, that if you create (and save) a PermMem handle you would in most case break the game save when you remove the patch afterwards. So instead, PermMem handles that are created in conjunction with a patch are no longer stored when saving a game. This solves this problem, but demands more thorough thinking on the end of the patch developer on how to implement save-load-persistent changes that don't compromise the game.

    The approach you suggest, of saving the handles in different files for each patch might sound appealing but does not solve the problem of unique handle numbers.

  14. Beiträge anzeigen #94 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    The approach you suggest, of saving the handles in different files for each patch might sound appealing but does not solve the problem of unique handle numbers.
    We would "just" need a unique "allocator", i.e. PermMem "instance" per Patch. LeGo is in no way equipped to handle that and it sounds like even more of a mess than it already is.
    We could maybe add a new function that takes some sort of patch identifier when creating a handle, such that the handles are deleted upon loading if the patch is no longer present... That would allow the patch to at least persist arbitrary information, but any LeGo function that calls 'new' internally could (should) no longer be used.


    I think there are ways to make this work, but it would rely on the patch creator to get everything right and not brick the savegame. Ultimately I don't think there's any way to make this opaque to the patch creator without a lot more work on Ninja's side (e.g. wrapping any code introduced by a ninja patch in something that sets/resets some sort of global patch identifier, so any handles created by that code are "tagged").

  15. Beiträge anzeigen #95 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Lehona Beitrag anzeigen
    We would "just" need a unique "allocator", i.e. PermMem "instance" per Patch. LeGo is in no way equipped to handle that and it sounds like even more of a mess than it already is.
    We could maybe add a new function that takes some sort of patch identifier when creating a handle, such that the handles are deleted upon loading if the patch is no longer present... That would allow the patch to at least persist arbitrary information, but any LeGo function that calls 'new' internally could (should) no longer be used.


    I think there are ways to make this work, but it would rely on the patch creator to get everything right and not brick the savegame. Ultimately I don't think there's any way to make this opaque to the patch creator without a lot more work on Ninja's side (e.g. wrapping any code introduced by a ninja patch in something that sets/resets some sort of global patch identifier, so any handles created by that code are "tagged").
    I had honestly not thought that far, thanks for the suggestions. The conclusion that you arrive at, as well, is "it would rely on the patch creator to get everything right and not brick the savegame" - which will never work and is the reason why I had decided to just "disallow" the usage of PermMem altogether. So far, for all the patches (incl. dummy, demo and testing) I have made, I have gotten around this limitation quite well and I believe it should be generally possible.

    Keep in mind that all packages that rely on handles still work, the handles just won't be saved. If really necessary one could just save a variable indicating that a certain object had been created to re-create it (or similar) when loading the game save.

  16. Beiträge anzeigen #96 Zitieren
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.243
     
    Milky-Way ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Nein, nicht zwingend, denn Ikarus erstellt dynamisch generierten Code, der z.B. durch das Aufrufen von Enginefunktionen mit CALL_* oder durchs Überschreiben von Funktionen wie MEM_ReadInt entsteht, jenseits der Offsets, die man in der Gothic.dat findet. Das gilt auch für while und repeat Schleifen, wie das hier der Fall ist.

    Du konntest ja schon einen Zusammenhang mit dem Schreibpult feststellen. Der Absturz passiert beim einmaligen Generieren des Codes für die while Schleife in _MOB_SCROLLS_isAnyCircleActive. Aus MEMINT_PrepareLoopsAndJumps wird scheinbar in einen dieser dynamisch generierten Codeabschnitte gesprungen (wahrscheinlich eine überschriebene Funktion) und da verliert sich dann die Spur. Ich bin mir nicht sicher, ob man dem Stacktrace ab da an systematisch nachverfolgen kann.

    While hat (hatte: im Ikarus Repo wurde das mittlerweile behoben) einige Probleme mit solchem dynamisch generiertem Code. Was ich mir vorstellen könnte ist, dass durch einen Patch mit Ninja die entsprechenden Ikarus-Funktionen überschrieben werden (an einen neuen Offset verschoben werden). Das mag in diesem Fall eines der Probleme in Ikarus hervorrufen, die wir mittlerweile in Ikarus behoben haben (rekursives Folgen von Jumps). Wenn ich mit meiner Spekulation also richtig liegen sollte, sollte das Problem spätestens mit der neuen Ninja Version behoben sein. Dort werden diese neusten Änderungen von Ikarus erzwungen.

    Eurem Spieler könnt ihr allerdings einfach sagen, dass weder der Manaregenerations-, noch der Workaround-, noch der Freies Zielen Patch in LoA sinnvoll sind, weil diese Mechaniken alle schon in der Mod enthalten sind (wenn ich mich recht erinnere). Er sollte also einfach alle Patches mit Ninja deaktivieren. Von einer Ankündigung wie "Alle Patches die Ninja benutzen sind mit LoA inkompatibel" würde ich abraten, weil diese Probleme mit Ninja 2.0 schon alle behoben sind.
    Du beziehst dich vermutlich auf diesen Ikarus Commit:
    https://github.com/Lehona/Ikarus/com...63e44a3b1dec6f
    Tritt das Problem, dass dieser commit behebt, auch ohne Ninja-Patch auf, oder tendenziell meist wegen eines alten Ninja-Patches? Mir geht es hauptsächlich darum, wie der Spieler weiterspielen kann. In die nächste LoA-Version kann ich dann auch die aktuellste Ikarus-Version einbauen. (Die aktuelle Release-Version von LoA ist noch auf dem Stand von 8767e44c1cf7640f44c5cc7192e8aa04b110e752 (Nov 26, 2018).)

  17. Beiträge anzeigen #97 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Das Problem tritt immer dann auf, wenn Ikarus neuen Bytecode schreibt (d.h. bei Sachen wie while(x) oder repeat(x)), und das in der Funktion passiert, die ganz am Ende vom Codestack steht. Das ist also recht ungewöhnlich bei Scripten ohne Ninja, da zumindest bei mir die Startup.d ganz am Ende der Gothic.src steht und da üblicherweise keine von diesen Konstrukten verwendet werden.

  18. Beiträge anzeigen #98 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Lehona Beitrag anzeigen
    Das Problem tritt immer dann auf, wenn Ikarus neuen Bytecode schreibt (d.h. bei Sachen wie while(x) oder repeat(x)), und das in der Funktion passiert, die ganz am Ende vom Codestack steht. Das ist also recht ungewöhnlich bei Scripten ohne Ninja, da zumindest bei mir die Startup.d ganz am Ende der Gothic.src steht und da üblicherweise keine von diesen Konstrukten verwendet werden.
    Du beziehst dich glaube ich auf diesen Commit. Der von Milky-Way verlinkte Commit behebt aber das Problem, wenn überschriebene Funktionen in einer While-Condition auftauchen. Das hat Abstürze mit undurchsichtigen Stacktraces verursacht, weil die überschriebene Funktion nicht identifiziert werden konnte. Das Problem kann, wenn ich mich recht erinnere, auch ohne Ninja vorkommen, z.B. glaube ich wenn man MEM_ReadInt in der Condition einer While-Schleife aufruft (nicht mehr sicher ob das stimmt).

    Da es nach dem letzten Stoß von "Ausbesserungen" an Ikarus wieder seit über zwei Monaten still geworden ist (und mir auch nichts mehr einfällt was noch getan werden müsste), halte ich eine neue Ikarus Version für sinnvoll. Lehona, was meinst du?

  19. Beiträge anzeigen #99 Zitieren
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.243
     
    Milky-Way ist offline
    Mir ging es mehr um die Access violation als um den verlinkten commit. Ich hatte nur geraten, dass der gemeint war. Also gut, dann sollte es wohl ohne Ninja Patches eigentlich funktionieren

  20. Beiträge anzeigen #100 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Ich bin begeistert von der Dokumentation

    Einen Fehler habe ich aber gefunden:

    Replacing this file in a patch in order to add new script features also replaces compiled scripts from the underlying mod, which will thus seize to exist.
    Du meintest 'cease', nicht 'seize'

Seite 5 von 34 « Erste 1234567891216 ... Letzte »

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
Impressum | Link Us | intern
World of Gothic © by World of Gothic Team
Gothic, Gothic 2 & Gothic 3 are © by Piranha Bytes & Egmont Interactive & JoWooD Productions AG, all rights reserved worldwide