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

 

Ergebnis 1 bis 18 von 18
  1. Beiträge anzeigen #1 Zitieren
    Veteran
    Registriert seit
    Jan 2012
    Beiträge
    681
     
    Frank-95 ist offline

    Creating every item via scripts

    Hi all!

    I would like to spawn in the inventory every item in the game. This could be a very useful thing for compiling or other stuff.

    I remember that I already tried to do a thing like this much time ago, so I went conservative and first made some basic test.
    First thing was to get zCClassDef of C_ITEM or oCItem, and check their length:

    Code:
    instance TestPC3(C_Item)
    {
        name = "every item";
        mainflag = ITEM_KAT_DOCS;
        flags = ITEM_MISSION;
        value = 0;
        visual = "ItWr_Scroll_01.3DS";
        material = MAT_LEATHER;
        on_state[0] = UseTestPC3;
        scemeName = "MAP";
        description = name;
    };
    
    func void UseTestPC3()
    {
        Wld_InsertItem(ItRu_SumWolf,"START"); //just a random item, spawned to be sure that the pointer did exist
        
        var zCClassDef x1;    x1 = _^(MEM_GetClassDef(MEM_InstToPtr(ItRu_SumWolf)));
        printi(x1.objectList_numInArray); //printi = Print(IntToString(xxx))
        
        var oCItem itm;    itm = Hlp_GetItem(ItRu_SumWolf);
        var zCClassDef x2;    x2 = _^(MEM_GetClassDef(MEM_InstToPtr(itm)));
        printi(x2.objectList_numInArray);
    };
    The Hlp_GetItem function works exactly like Hlp_GetNpc:

    Code:
    func MEMINT_HelperClass Hlp_GetItem(var C_ITEM itm)
    {
        var int ptr;    ptr = MEM_InstToPtr(itm);
        
        var MEMINT_HelperClass hlp;
        const int hlpOffsetPtr = 0;
        if (!hlpOffsetPtr)
        {
            hlpOffsetPtr = MEM_ReadIntArray (currSymbolTableAddress, hlp) + zCParSymbol_offset_offset;
        };
        
        if(ptr <= 0)
        {
            MEM_WriteInt(hlpOffsetPtr, 0);
        }
        else
        {
            MEM_WriteInt(hlpOffsetPtr, ptr);
        };
        
         MEMINT_StackPushInst (hlp);
    };
    So here a problem. The function above print two zeroes. This means that either I mistake something, or there is a problem with zCClassDef. So basically no item found in neither oCItem nor C_ITEM class definition...

    Does anyone have some idea?

  2. Beiträge anzeigen #2 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Code:
    //alle benannten (!) Objekte von genau (!) dieser Klasse (!) //Ausrufezeichenanmerkungen: 1.) unbenannte sind nicht drin 2.) Objekte von Unterklassen sind nicht drin 3.) diese Eigenschaft kann sehr nützlich sein.
    This comment says only objects of exactly that class (there's no class inheriting from oCItem I think, so that's fine) that have a name are in there. Don't know how that works out exactly for oCItems, but I'd assume it to be the reason why you can't seem to find any.

    Edit: I went to find a more thorough explanation: Only objects that are meant to be shared (e.g. zCMaterial) have all their objects indexed (I didn't know this, either). Neither NPCs nor oCItems posess that flag.

    Your approach was wrong, anyway You'd have created every item that is currently existing in the world or inventories. If you want to create every item that has an instance in the scripts, you'll have to loop through the symbol table and look for symbols that are items and their instance function ptr is not null.
    Geändert von Lehona (15.09.2016 um 17:28 Uhr)

  3. Beiträge anzeigen #3 Zitieren
    Ritter Avatar von Degenerated
    Registriert seit
    Aug 2014
    Beiträge
    1.982
     
    Degenerated ist offline
    In REGoth I have written a function, which finds all instances of a given base-class and basically does what Lehona suggested:

    Code:
    void DATFile::iterateSymbolsOfClass(const std::string& className, std::function<void(size_t, PARSymbol&)> callback)
    {
        // First, find the parent-symbol
        size_t baseSym = getSymbolIndexByName(className);
    
        for(size_t i = 0; i< m_SymTable.symbols.size(); i++)
        {
            PARSymbol& s = getSymbolByIndex(i);
            if(s.parent == -1 || s.properties.elemProps.type != EParType_Instance)
                continue;
    
            PARSymbol& p = getSymbolByIndex(s.parent);
            uint32_t pBase = s.parent;
    
            // In case this is also just a prototype, go deeper one more level
            if(p.properties.elemProps.type == EParType_Prototype && p.parent != -1)
            {
                pBase = p.parent;
            }
    
            // If the parent-classes match, we found an instance of our class
            if(baseSym == pBase)
                callback(i, s);
         }
    }
    I haven't really looked into how the original game stores it's symbols, but I'm guessing you'll find similar entries for that.
    You can use my code to get some ideas, though.

    Basically, every symbol should have a field which denotes the parent-instance. This could either say "no parent" (-1), which means it's something like C_ITEM, or set to an other symbol.

    This then can either be a prototype (We don't want that, so take the parent of that instead) or C_ITEM itself. (Or an other class, of course)

    If the parent matches C_ITEM, then you got an actual instance.
    Geändert von Degenerated (15.09.2016 um 18:05 Uhr)

  4. Beiträge anzeigen #4 Zitieren
    Veteran
    Registriert seit
    Jan 2012
    Beiträge
    681
     
    Frank-95 ist offline
    Thank you both for clarification and code sharing

    This is my current attempt:

    Code:
    func void UseTestPC3()
    {
        var int baseSymbol; baseSymbol = MEM_GetSymbolIndex("OCITEM");
        
        var int i;    i = 0;
        while(i < MEM_Parser.symtab_table_numInArray);
            var zCPar_Symbol symb1;    symb1 = _^(MEM_GetSymbolByIndex(i));
            if((symb1.parent == -1) || /*condition for prototypes*/)
            {
                continue;
            };
            
            var zCPar_Symbol symb2;    symb2 = _^(MEM_GetSymbolByIndex(symb1.parent));
            var int base;    base = symb1.parent;
            if(/*condition for prototypes*/ && (symb2.parent != -1))
            {
                base = symb2.parent;
            };
            
            if(baseSymbol == base)
            {
                var oCItem itm;    itm = _^(MEM_FindParserSymbol(symb1.name));
                MEM_Debug(itm.name);
            };
            
            i += 1;
        end;    
    };
    I think I reproduced the code correctly, but I miss the Ikarus equivalent condition to check whether the symbol is a prototype. Any idea?

  5. Beiträge anzeigen #5 Zitieren
    Ritter Avatar von Degenerated
    Registriert seit
    Aug 2014
    Beiträge
    1.982
     
    Degenerated ist offline
    Looks good!

    You can get the prototype/instance-flags from the symbol:

    Here's the definition of Ikarus:
    Code:
    const int zPAR_TYPE_VOID        = 0 << 12;
    const int zPAR_TYPE_FLOAT       = 1 << 12;
    const int zPAR_TYPE_INT         = 2 << 12;
    const int zPAR_TYPE_STRING      = 3 << 12;
    const int zPAR_TYPE_CLASS       = 4 << 12;
    const int zPAR_TYPE_FUNC        = 5 << 12;
    const int zPAR_TYPE_PROTOTYPE   = 6 << 12;
    const int zPAR_TYPE_INSTANCE    = 7 << 12;
    
    /*
    enum {  zPAR_FLAG_CONST     = 1,zPAR_FLAG_RETURN = 2,   zPAR_FLAG_CLASSVAR=4,
            zPAR_FLAG_EXTERNAL  = 8,zPAR_FLAG_MERGED =16 };*/
            
    const int zPAR_FLAG_CONST       =  1 << 16;
    const int zPAR_FLAG_RETURN      =  2 << 16;
    const int zPAR_FLAG_CLASSVAR    =  4 << 16;
    const int zPAR_FLAG_EXTERNAL    =  8 << 16;
    const int zPAR_FLAG_MERGED      = 16 << 16;
    
    const int zCParSymbol_content_offset                = 24; //0x18
    const int zCParSymbol_offset_offset                 = 28; //0x1C
    
    class zCPar_Symbol {
        var string name;                        //0x0000 zSTRING        //Name des Symbols, so wie im Script
        var int next;                           //0x0014 zCPar_Symbol*  //Scheinbar sind die Symbole verkettet. Erscheint mir nutzlos, es gibt ja die Symboltabelle.
        
        //Inhalt des Symbols, Pointer oder Primitiver Typ
        var int content;                        //0x0018 void* oder int* oder float* oder zSTRING* oder int oder float. Bei Funktionen / Instanzen / Prototypen: Stackpointer. Sonst Daten oder Datenpointer.
        var int offset;                         //0x001C Offset bei Klassenvariablen // Adresse bei Instanzen // Rückgabewert bei Funktionen
        
        var int bitfield;                       //0x0020 siehe oben
        var int filenr;                         //0x0024
        var int line;                           //0x0028
        var int line_anz;                       //0x002C
        var int pos_beg;                        //0x0030
        var int pos_anz;                        //0x0034
        
        var int parent;                         //0x0038 zCPar_Symbol*  // Parent-Verweis
    };
    The bitfield should contain the flags you need to know.

  6. Beiträge anzeigen #6 Zitieren
    Veteran
    Registriert seit
    Jan 2012
    Beiträge
    681
     
    Frank-95 ist offline
    Code:
    func void UseTestPC3()
    {
        var int baseSymbol; baseSymbol = MEM_GetSymbolIndex("OCITEM");
        
        var int i;    i = 0;
        while(i < MEM_Parser.symtab_table_numInArray);
            var zCPar_Symbol symb1;    symb1 = _^(MEM_GetSymbolByIndex(i));
            if((symb1.parent == -1) || ((symb1.bitfield & zPAR_TYPE_PROTOTYPE) == zPAR_TYPE_PROTOTYPE))
            {
                continue;
            };
            
            var zCPar_Symbol symb2;    symb2 = _^(MEM_GetSymbolByIndex(symb1.parent));
            var int base;    base = symb1.parent;
            if(((symb2.bitfield & zPAR_TYPE_PROTOTYPE) == zPAR_TYPE_PROTOTYPE) && (symb2.parent != -1))
            {
                base = symb2.parent;
            };
            
            if(baseSymbol == base)
            {
                var oCItem itm;    itm = _^(MEM_FindParserSymbol(symb1.name));
                MEM_Debug(itm.name);
            };
            
            i += 1;
        end;    
    };
    This should be the final code, but it makes gothic freeze, with no error. I don't know if it is a performance problem or not, but I don't think so since I replaced that while condition with while(i < 2), and still freezes. Unless it take more than 30 seconds to complete 2 cycles

    @Degenerated, how much time does your function take finish the iteration?

  7. Beiträge anzeigen #7 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    You have created an infinite loop. Use repeat() instead of while() and it will go away

    You don't increment your counter when the continue-case is reached.

    Edit:

    Code:
    var oCItem itm;    itm = _^(MEM_FindParserSymbol(symb1.name));
    This is wrong as well, MEM_FindParserSymbol returns a zCPar_Symbol*. The offset-attribute points to an object (oCItem in this case), if there is one (otherwise it's null).

  8. Beiträge anzeigen #8 Zitieren
    Ritter Avatar von Degenerated
    Registriert seit
    Aug 2014
    Beiträge
    1.982
     
    Degenerated ist offline
    Zitat Zitat von Frank-95 Beitrag anzeigen
    @Degenerated, how much time does your function take finish the iteration?
    A few milliseconds, at most.

    Edit:
    Code:
    func void UseTestPC3()
    {
        var int baseSymbol; baseSymbol = MEM_GetSymbolIndex("OCITEM");
        
        var int i;    i = 0;
        while(i < MEM_Parser.symtab_table_numInArray);
            var zCPar_Symbol symb1;    symb1 = _^(MEM_GetSymbolByIndex(i));
            if((symb1.parent == -1) || ((symb1.bitfield & zPAR_TYPE_PROTOTYPE) == zPAR_TYPE_PROTOTYPE))
            {
                continue;
            };
            
            var zCPar_Symbol symb2;    symb2 = _^(MEM_GetSymbolByIndex(symb1.parent));
            var int base;    base = symb1.parent;
            if(((symb2.bitfield & zPAR_TYPE_PROTOTYPE) == zPAR_TYPE_PROTOTYPE) && (symb2.parent != -1))
            {
                base = symb2.parent;
            };
            
            if(baseSymbol == base)
            {
                var oCItem itm;    itm = _^(MEM_FindParserSymbol(symb1.name));
                MEM_Debug(itm.name);
            };
            
            i += 1;
        end;    
    };
    You want to check for the instance-flag here. Mind the != in my code:
    Code:
    if(s.parent == -1 || s.properties.elemProps.type != EParType_Instance)
                continue;
    Geändert von Degenerated (15.09.2016 um 22:49 Uhr)

  9. Beiträge anzeigen #9 Zitieren
    Veteran
    Registriert seit
    Jan 2012
    Beiträge
    681
     
    Frank-95 ist offline
    I might have found a difference between REGoth and Daedalus, but I'm not that sure.

    Basically I kept on getting error of index out of bounds and made some tests. Then I checked the zCPar_Symbol instance and noted that parent-attribute was the pointer, and not the index of the parent, like it seems to be in Degenerated code, am I right?

    In fact, getSymbolByIndex(s.parent), get the symbol instance from the index stored in s.parent, while zCPar_Symbol.parent is a zCPar_Symbol* pointer which is clearly greater than the length of the symbol table, which is less of 20k in my gothic; on the other hand pointers are some order of magnitude greater than that. Thus the out of bound error risen by MEM_GetSymbolByIndex.

    I tried to change it a bit, please tell me if it makes any sense:

    Code:
    func void UseTestPC3()
    {
        var int baseSymbol; baseSymbol = MEM_GetSymbolIndex("OCITEM");
    
        var int found;    found = 0;
        
        var int i;    i = 0;
        while(i < MEM_Parser.symtab_table_numInArray);
            var zCPar_Symbol symb1;    symb1 = _^(MEM_GetSymbolByIndex(i));
            
            var zCPar_Symbol symb2;    symb2 = _^(symb1.parent);
            var int symb2_index;    symb2_index = MEM_GetSymbolIndex(symb2.name);
            
            if((symb2_index == -1) || ((symb1.bitfield & zPAR_TYPE_PROTOTYPE) != zPAR_TYPE_PROTOTYPE))
            {
                i += 1;
                continue;
            };
            
            var zCPar_Symbol symb3;    symb3 = _^(symb2.parent);
            var int symb3_index;    symb3_index = MEM_GetSymbolIndex(symb3.name);
            if(((symb2.bitfield & zPAR_TYPE_PROTOTYPE) == zPAR_TYPE_PROTOTYPE) && (symb3_index != -1))
            {
                symb2_index = symb3_index;
            };
            
            if(baseSymbol == symb2_index)
            {
                MEM_Debug("ITEM FOUND");
                found += 1;
            };
            
            i += 1;
        end;
        
        MEM_Debug(IntToString(found));
        
        ExitGame();
    };
    Practically this code does not raise any error, and takes several seconds to be executed. Anyway found seems to be just = 9 at the end of iteration :/
    Geändert von Frank-95 (16.09.2016 um 18:01 Uhr)

  10. Beiträge anzeigen #10 Zitieren
    Veteran
    Registriert seit
    Jan 2012
    Beiträge
    681
     
    Frank-95 ist offline
    Oh well, you know what? I always try to automatise things via daedalus, but those rarely work, and in the end I end up creating scripts that do what I need via python

    Code:
    #Code to spawn every item (1x per item)
    #It is made according to the convention that prototype and instance declaration
    #are lowercase, and there is no open bracket inside the symbol name
    #Change directories according to your configuration
    
    import os
    
    filelist = list(os.walk('C:\\Program Files (x86)\\Gothic 2 Gold\\_work\\data\\Scripts\\CONTENT'))
    
    finallist = set()
    
    validproto = set()
    
    for directory in filelist[1:]:
            for script in directory[2]:
                    if (script.upper() == 'OU.BIN') or (script.upper() == 'OU.CSL'):
                            continue
                    with open(directory[0] + '\\' + script, mode='r') as scr:
                            lines = scr.readlines()
                            for line in lines:
                                    if 'prototype' in line:
                                            prototype = line.split('(')[1][:-2].upper()
                                            if prototype == 'C_ITEM':
                                                    validproto.add(line.split('(')[0].replace('prototype ','').upper())
                                    if 'instance' in line:
                                            insttype = line.split('(')[-1][:-2].upper()
                                            if (insttype == 'C_ITEM') or (insttype in validproto):
                                                    instancename = line.split('(')[0].replace('instance ','')
                                                    if ('/' not in instancename) and ('*' not in instancename) and ('Sharped' not in instancename) and ('Better' not in instancename):
                                                            finallist.add(instancename)
    
    with open('C:\\Users\\Francesco\\Desktop\\GiveEveryItem.txt', mode='w') as f:
            for item in finallist:
                    f.write('\tCreateInvItem(self,%s);\n' %item)
    This works for me

  11. Beiträge anzeigen #11 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Although that's obviously a totally valid solution, in this case you simply made a mistake transcribing Degenerated's code. In post #8 he noted your mistake, but you only fixed the (in-)equality and didn't change prototype to instance.

    Daedalus isn't some sort of inaccurate science; as long as you circumvent a couple of pitfalls, it's mostly C with a bit more boilerplate code.

  12. Beiträge anzeigen #12 Zitieren
    Veteran
    Registriert seit
    Jan 2012
    Beiträge
    681
     
    Frank-95 ist offline
    You're right, damn distraction

  13. Beiträge anzeigen #13 Zitieren
    Apprentice Avatar von Auronen
    Registriert seit
    Dec 2020
    Beiträge
    25
     
    Auronen ist offline
    I know this is a very old thread, but if anyone still needs a function like this, I made it work

    Spoiler:(zum lesen bitte Text markieren)
    Code:
    func int NPC_InsertAllItems(var c_npc npc_inst)
    {
        // rewritten from Degenerated's post https://forum.worldofplayers.de/forum/threads/1476153-Creating-every-item-via-scripts?p=25089817&viewfull=1#post25089817
        // many thanks to mud-freak (@szapp) for pointing out my mistake with the data stack :)
        var int baseSym; baseSym = MEM_GetSymbolIndex("C_ITEM");
    
        var int found; found = 0;
    
        while(1); var int i;
    		
            if (i == MEM_Parser.symtab_table_numInArray -1 ) { break; };
    		
            var zCPar_Symbol s;
            s = _^(MEM_GetSymbolByIndex(i));
    		
            var int s_parent_index;
            if (!s.parent) 
            { 
                s_parent_index = 0;
            } 
            else
            {
                var zCPar_Symbol symb2;
                symb2 = _^(s.parent);
                s_parent_index = MEM_GetSymbolIndex( symb2.name );
            };
    
            if((s_parent_index == 0) || ((s.bitfield & zPAR_TYPE_INSTANCE) != zPAR_TYPE_INSTANCE))
            {
                i += 1;
                continue;
            };
    		
            var zCPar_Symbol p;
            p = _^(MEM_GetSymbolByIndex(s_parent_index));
    		
            var int p_parent_index;
            if (!p.parent) 
            {
                p_parent_index = 0;
            }
            else
            {
                var zCPar_Symbol symb3;
                symb3 = _^(p.parent);
                p_parent_index = MEM_GetSymbolIndex( symb3.name );
            };
            
            var int pBase;
            pBase = s_parent_index;
    
            if(((p.bitfield & zPAR_TYPE_PROTOTYPE) == zPAR_TYPE_PROTOTYPE) && (p_parent_index != 0))
            {
                pBase = p_parent_index;	
            };
            
            if(baseSym == pBase)
            {
                // Not item instances, but c_item variables in the scripts
                /*
                if ( s.content == s.offset && s.offset == 0 )
                {
                    PVs("oCItem.name", s.name);
                };
                */
    			
                // Creates one item in npc_inst inventory  ---> for every item
                // If s.offset uncommented --> only for items that don't have instance yet (they are not in someone's inventory or in the world)
                if ( s.content != 0 /*&& s.offset == 0*/ )
                {
                    found += 1;
                    CreateInvItem (npc_inst, i);
    
                };
            };
            i += 1;
        end;
    
        MEM_Info( ConcatStrings("Number of items found and inserted: ", IntToString(found) ) );
        return found;
    };
    Geändert von Auronen (25.07.2021 um 19:32 Uhr) Grund: Fixed the bug, thanks to mud-freak

  14. Beiträge anzeigen #14 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    I haven't looked thoroughly at the code but just a note: Why are you clearing the data stack? That should never be done. Take this example:
    Code:
    if (NPC_InsertAllItems(PC_Hero) && TRUE) {
        // This will never be reached
    };
    The if-condition pushes TRUE on the stack to be later evaluated with &&. But by that time the stack will have been cleared, causing to pop from an empty stack which always returns zero.

    The data stack should never be altered manually. (Aside from some very targeted parser stack hacking in Ikarus and LeGo.)

    If the stack overflows, as the comment in your code suggests, it's better to fix the problem's origin (prevent the overflow where it is happening) instead of addressing the symptoms.

  15. Beiträge anzeigen #15 Zitieren
    Apprentice Avatar von Auronen
    Registriert seit
    Dec 2020
    Beiträge
    25
     
    Auronen ist offline
    Hello mud-freak,
    I am not quite sure, what is causing it to overflow, though I might have an idea - I haven't looked how it really works, but isn't the while constructing the whole loop on the stack and then calls it?
    How could I do it differently, to prevent from overflowing?

    Auronen

  16. Beiträge anzeigen #16 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Auronen Beitrag anzeigen
    Hello mud-freak,
    I am not quite sure, what is causing it to overflow, though I might have an idea - I haven't looked how it really works, but isn't the while constructing the whole loop on the stack and then calls it?
    How could I do it differently, to prevent from overflowing?

    Auronen
    No, the data stack only contains data, no code is executed there. The while loop just rewrites/creates some new byte code to implement the condition and the jumps. It does not alter the Daedalus data stack.

    The data stack typically overflows if you don't "use" the return value of a function. It's best to investigate the signatures of the functions you are calling. You can, nevertheless, use MEM_Parser.datastack_sptr to debug and inspect the current size of the data stack to narrow down at which point it grows uncontrolled.

  17. Beiträge anzeigen #17 Zitieren
    Apprentice Avatar von Auronen
    Registriert seit
    Dec 2020
    Beiträge
    25
     
    Auronen ist offline
    Thank you for explaining, I learned something new. I found it, there was a typo (I accidently had a comparation insted of assignment, == insted of =). Thank you for suggesting the method.

    I will update the code here and also in the new damage types feature I am using it.

    Many thanks, once again!

    Auronen

  18. Beiträge anzeigen #18 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Nice! Great to hear that you found the cause.

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