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 2 von 2 « Erste 12
Ergebnis 21 bis 35 von 35
  1. Beiträge anzeigen #21 Zitieren
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.246
     
    Milky-Way ist offline
    Zitat Zitat von Bisasam Beitrag anzeigen
    Auf kurz oder lang würde ich aber auch besser finden, das Inventar einfach durchzuzählen. Die Frage ist: Wie macht man das bei dieser Fülle an Items am besten? Am liebsten würde ich ja Kategorie für Kategorie durchgehen, abfragen wie viele Items mit wie viel Gewicht sich darin befinden und dann alles zusammenrechnen. ich find nur keine Funktion, die mir erlaubt, Items ohne konkrete Instanzangabe aus dem Inv zu bekommen.
    Gibt es die Funktion Npc_GetInvItemBySlot in Gothic 1?
    Hier ein Beispiel:
    Code:
    func void Npc_ClearInventoryExceptArmor(var C_Npc victim, var int Slotnumber)
    {
        if (Npc_GetInvItemBySlot (victim, 0, Slotnumber) > 0)
        {
    		if !(item.mainflag&ITEM_KAT_ARMOR){ // Ist keine Rüstung
    			var int itemid;
    			itemid = Hlp_GetInstanceID (item);
    			NPC_RemoveInvItems (victim, itemid, NPC_HasItems (victim, itemid));
    			Npc_ClearInventoryExceptArmor(victim, Slotnumber);
    		}else{
    			Npc_ClearInventoryExceptArmor(victim, Slotnumber+1);
    		};
        };
    };
    Wenn das Item keine Rüstung ist, wird es aus dem Inventar entfernt und wir gehen zum nächsten Item, das nun im gleichen Slot sitzt (da wir das vorherige entfernt haben). Wenn es sich um eine Rüstung handelt, dann gehen wir direkt zum nächsten Item, das in Slotnumber+1 zu finden ist.

    Wir starten die Rekursion dann mit Npc_ClearInventoryExceptArmor(hero,0), da Nummer 0 der erste Slot ist. Die Funktion rekursiert sich dann selbst durch alle Items im Helden-Inventar durch.

  2. Beiträge anzeigen #22 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Zitat Zitat von Bisasam Beitrag anzeigen
    Auf kurz oder lang würde ich aber auch besser finden, das Inventar einfach durchzuzählen. Die Frage ist: Wie macht man das bei dieser Fülle an Items am besten? Am liebsten würde ich ja Kategorie für Kategorie durchgehen, abfragen wie viele Items mit wie viel Gewicht sich darin befinden und dann alles zusammenrechnen. ich find nur keine Funktion, die mir erlaubt, Items ohne konkrete Instanzangabe aus dem Inv zu bekommen.
    Ikarus macht's möglich. NPCs haben die Eigenschaft inventory2_oCItemContainer_contents vom Typ zCListSort<oCItem*> (ich nehme zumindest an, dass es oCItem* ist - in Ikarus steht nur oCItem), die wiederum kannst du z.B. mit dem Listen-Paket1 von LeGo ergonomisch durchzählen/Gewichte aufsummieren. Insbesondere interessant für dich ist List_ForFS.

    Edit: Eventuell musst du in G1 alle Kategorien einzeln durchzählen - einfach mal die oCNpc.d öffnen.

    1Das S-Suffix nicht vergessen, weil es sich um eine sortierte Liste handelt.
    Geändert von Lehona (07.08.2019 um 18:36 Uhr)

  3. Beiträge anzeigen #23 Zitieren
    Hero Avatar von lali
    Registriert seit
    Feb 2016
    Beiträge
    5.473
     
    lali ist offline
    Sind die Inventory Capacities dann für diesen Zweck also nicht mehr nutzbar oder habe ich den Hintergrund nicht richtig verstanden?
    Phoenix Dev | Website | Discord

  4. Beiträge anzeigen #24 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Zitat Zitat von lali Beitrag anzeigen
    Sind die Inventory Capacities dann für diesen Zweck also nicht mehr nutzbar oder habe ich den Hintergrund nicht richtig verstanden?
    Dazu müsste ich erstmal wissen, was genau du damit meinst :P Falls das Überbleibsel aus der Engine selber sind, ist es meistens einfacher, das selber nochmal richtig zu implementieren.

  5. Beiträge anzeigen #25 Zitieren
    Serima Avatar von Fisk2033
    Registriert seit
    Dec 2010
    Ort
    Dresden
    Beiträge
    5.803
     
    Fisk2033 ist offline
    Zitat Zitat von Lehona Beitrag anzeigen
    Edit: Eventuell musst du in G1 alle Kategorien einzeln durchzählen - einfach mal die oCNpc.d öffnen.

    1Das S-Suffix nicht vergessen, weil es sich um eine sortierte Liste handelt.
    Vielleicht hilft auch das ItemScript
    Zitat Zitat von F a w k e s Beitrag anzeigen
    Hello folks,
    Today I would like to share scripts which improve players experience with Followers in Gothic 1.
    Some bits and pieces (torch detection in hand, torch use by NPC, improved G1 trading, where you don't have to take care of ore, new ZS states triggered on player, trade multiplier values and more) were already presented in different threads on this forum, I will repost them, as code for followers is closely tied with these.

    With this package you can:
    Instruct follower to wait for you / follow you.
    Manage inventory of your follower.

    Followers behavior will be more immersive:
    If player is sneaking, they will be sneaking as well.
    During night they will use torches. (if they have them in inventory) They will hide torches, if player is in a portal room or sneaking.

    Most interesting part is I believe inventory management of your follower. This is how it works:
    1. Followers have available dialog option '(inventory management)' - this opens standard Trading
    2. In this 'trade' both sell & buy multiplier value is set to 0. This way trading serves as an item exchange interface.
    3. When trade is closed (detected by _HOOK_TRADE_ONEXIT), ZS state 'ZS_InvManagement' is applied to hero. ZS state will wait for InfoManager_HasFinished ().
    4. ZS_InvManagement then uses NPC_SetAsPlayer function on follower. (same as if you would use 'o' in marvin)
    5. ZS_InvManagement opens inventory - player can then equip/unequip/drop/eat/drink items in Followers inventory.
    6. ZS_InvManagement + several other hooks are checking if player closed inventory.
    7. If inventory is closed, ZS_InvManagement script will use NPC_SetAsPlayer on 'true hero' and will start ZS_Talk on follower to reopen dialog options.

    [Video]

    Complete code:

    Variables and constants:

    Code:
    INSTANCE Follower (C_NPC);
    INSTANCE Guide (C_NPC);
    
    var int InvManagement_Dialog;
        const int cIM_Dialog_Default        = 0;
        const int cIM_Dialog_Trade        = 1;
        const int cIM_Dialog_Trade_Closing    = 2;
        const int cIM_Dialog_Inventory        = 3;
        const int cIM_Dialog_Inventory_Reopen    = 4;
        const int cIM_Dialog_Exit        = 5;
    Couple of required (and very useful) functions - borrowed from this forum:

    Code:
    //Author: Dalai Zoll
    //https://forum.worldofplayers.de/forum/threads/1090721-Testschleichen?p=17909902&viewfull=1#post17909902
    /*===================================================================
    NPC_GetWalkMode 
    Den Walkmode eines NPC zurückgeben.
    "RUN" - normales laufen
    "WALK" - gehen
    "SNEAK" - schleichen
    "" - schwimmen, durch Wasser waten
    ==================================================================*/
    
    const int NPC_INWATER = 3;
    
    FUNC INT NPC_GetWalkMode (VAR C_NPC slf)
    {
        const int oCAniCtrl_Human__GetWalkModestring = 6432560;    //00622730  
        var oCNpc oCslf; oCslf = Hlp_GetNPC (slf);
    
        CALL_RetValIszString();
        CALL__thiscall(oCslf.AniCtrl, oCAniCtrl_Human__GetWalkModestring);
    
        var string result;
        result = CALL_RetValAszstring ();
    
        if (Hlp_StrCmp (result, "RUN"))        {     return NPC_RUN;     } else
        if (Hlp_StrCmp (result, "WALK"))    {     return NPC_WALK;    } else
        if (Hlp_StrCmp (result, "SNEAK"))    {    return NPC_SNEAK;    } else
        if (Hlp_StrCmp (result, ""))        {    return NPC_INWATER;    };
        
        //MOD - just in case, return something.
        return -1;
    };
    
    //Author: Sektenspinner
    //https://forum.worldofplayers.de/forum/threads/942338-Raum-einem-NPC-einer-Gilde-zuweisen?p=15078301&viewfull=1#post15078301
    FUNC STRING Wld_GetPlayerPortalRoom ()
    {
        MEM_InitGlobalInst();
        var oCPortalRoomManager portalman;
        portalman = MEM_PtrToInst (MEM_Game.portalman);
        var oCPortalRoom playerRoom;
    
        if (portalMan.curPlayerPortal)
        {
            playerRoom = MEM_PtrToInst (portalMan.curPlayerPortal);
    
            return playerRoom.portalName;
        } else
        {
            return "";
        };
    };
    
    //Author: OrcWarriorPL
    //https://forum.worldofplayers.de/forum/threads/879891-Skriptpaket-Ikarus-2/page12?p=14836995&viewfull=1#post14836995
    FUNC VOID Trade_ChangeSellMultiplier(var int mul)
    {    var int ptr;
        ptr = MEMINT_oCInformationManager_Address;
        ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.dlgTrade
        ptr = MEM_ReadInt (ptr + 260);    //dlgTrade.oCViewDialogItemContainer
        MEM_WriteInt (ptr + 268, mul);    //oCViewDialogItemContainer.Multiplier = mul
    };
    
    FUNC VOID Trade_ChangeBuyMultiplier(var int mul)
    {    var int ptr;
        ptr = MEMINT_oCInformationManager_Address;
        ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.dlgTrade
        ptr = MEM_ReadInt (ptr + 252);    //dlgTrade.oCViewDialogStealContainer
        MEM_WriteInt (ptr + 268, mul);    //oCViewDialogStealContainer.Multiplier = mul    
    };
    Torch handling - allows you to recognize if NPC is holding torch. By calling NPC_TorchSwitchOn / NPC_TorchSwitchOff you can put in / remove from hand of NPC torch.

    Code:
    //Originally posted here (slightly rewritten):
    //https://forum.worldofplayers.de/forum/threads/1532847-Fackel-via-Tastendruck-in-die-Hand?p=26018719&viewfull=1#post26018719
    /***
        These functions will fill global variable item 'pointing' to specific item in inventory
        NPC_GetInventoryCategoryItem - invCategory only
        NPC_GetInventoryItem - all invCategory
    ***/
    FUNC INT NPC_GetInventoryCategoryItem (var C_NPC slf, var int invCategory, var int itm)
    {
        var int p;
        var int itmInstance;
        
        var int itmSlot; itmSlot = 0;
        
        //Loop
        p = MEM_StackPos.position;
    
        //Je nejaky item v prvom slote tejto kategorie?
        if (NPC_GetInvItemBySlot (slf, invCategory, itmSlot) > 0)
        {
            itmInstance = Hlp_GetInstanceID (item);
    
            if (itm == itmInstance)
            {
                return TRUE;
            } else
            {
                itmSlot = itmSlot + 1;
                MEM_StackPos.position = p;
            };
        };
        
        return FALSE;
    };
    
    FUNC INT NPC_GetInventoryItem (var int slfInstance, var int itm)
    {
        var C_NPC slf;
        slf = Hlp_GetNPC (slfInstance);
        
        if (NPC_GetInventoryCategoryItem (slf, INV_WEAPON, itm) == FALSE)
        {
            if (NPC_GetInventoryCategoryItem (slf, INV_ARMOR, itm) == FALSE)
            {
                if (NPC_GetInventoryCategoryItem (slf, INV_RUNE, itm) == FALSE)
                {
                    if (NPC_GetInventoryCategoryItem (slf, INV_MAGIC, itm) == FALSE)
                    {
                        if (NPC_GetInventoryCategoryItem (slf, INV_FOOD, itm) == FALSE)
                        {
                            if (NPC_GetInventoryCategoryItem (slf, INV_POTION, itm) == FALSE)
                            {
                                if (NPC_GetInventoryCategoryItem (slf, INV_DOC, itm) == FALSE)
                                {
                                    if (NPC_GetInventoryCategoryItem (slf, INV_MISC, itm) == FALSE)
                                    {
                                        return FALSE;
                                    };
                                };
                            };
                        };
                    };
                };
            };
        };
        
        return TRUE;
    };
    
    /***
        Returns pointer to item in slotName
    ***/
    FUNC INT NPC_GetSlotItem (var int slfInstance, var string slotName) 
    {
        var C_NPC slf;
        slf = Hlp_GetNPC (slfInstance);
        
        //0068F4F0  .text     Debug data           ?GetSlotItem@oCNpc@@QAEPAVoCItem@@ABVzSTRING@@@Z
        const int oCNPC__GetSlotItem = 6878448;
        
        CALL_zStringPtrParam (slotName);
        CALL__thiscall (_@ (slf), oCNPC__GetSlotItem);
        return CALL_RetValAsPtr ();
    };
    
    /***
        NPC will use item
    ***/
    FUNC VOID NPC_UseItem (var int slfInstance, var int itmInstance)
    {
        //00698810  .text     Debug data           ?UseItem@oCNpc@@QAEHPAVoCItem@@@Z
        const int oCNPC__UseItem_G1 = 6916112;
    
        //0x0073BC10 public: int __thiscall oCNpc::UseItem(class oCItem *)
        const int oCNPC__UseItem_G2 = 7584784;
        
        var C_NPC slf;
        slf = Hlp_GetNPC (slfInstance);
    
        CALL_PtrParam (MEM_InstToPtr (itmInstance));
        CALL__thiscall (MEM_InstToPtr (slf), MEMINT_SwitchG1G2 (oCNPC__UseItem_G1, oCNPC__UseItem_G1));
    };
    
    /***
        If NPC has in hand ItLsTorchBurning / ItLsTorchBurned - it will put it back to inventory, otherwise it will  lit ItLsTorch / ItLsTorchBurned and put ItLsTorchBurning in hand
    ***/
    FUNC VOID NPC_TorchSwitchOnOff (var int slfInstance)
    {
        var int ptr;
        var C_NPC slf;
        
        slf = Hlp_GetNPC (slfInstance);
    
        //Get pointer to ZS_LEFTHAND
        ptr = NPC_GetSlotItem (slf, "ZS_LEFTHAND");
        
        //Is there anything in hand?
        if (ptr) {
            var C_Item itm;
            itm = _^ (ptr);
            
            //Is it ItLsTorchBurning / ItLsTorchBurned?
            if (Hlp_GetInstanceID (itm) == Hlp_GetInstanceID (ItLsTorchBurning))
            || (Hlp_GetInstanceID (itm) == Hlp_GetInstanceID (ItLsTorchBurned))
            {
                //Use itm - will put ItLsTorch back to inventory and remove from inventory
                NPC_UseItem (slf, itm);
                NPC_RemoveInvItem (slf,  ptr);
            };
        } else
        {
            //Careful! NPC_GetInventoryItem is not actually returning pointer. I am using ptr variable just to 'save' variables :)
            
            //Search for ItLsTorchBurned
            ptr = NPC_GetInventoryItem (slf, ItLsTorchBurned);
            
            //Search for ItLsTorch
            if (!ptr) {
                ptr = NPC_GetInventoryItem (slf, ItLsTorch); 
            };
            
            //Fill item with pointer to some ItLsTorch in inventory
            if (ptr) {
                //Use it - puts ItLsTorchBurning in hand and remove from inventory
                NPC_UseItem (slf,  item);
                NPC_RemoveInvItem (slf,  _@ (item));
            };
        };
    };
    
    /***
        If NPC has torch in inventory function will put torch in NPCs hand
    ***/
    FUNC VOID NPC_TorchSwitchOn (var int slfInstance)
    {
        var int ptr;
        var C_NPC slf;
        
        slf = Hlp_GetNPC (slfInstance);
    
        //Get pointer to ZS_LEFTHAND
        ptr = NPC_GetSlotItem (slf, "ZS_LEFTHAND");
        
        if (ptr) {
            var C_Item itm;
            itm = _^ (ptr);
            
            //Is it ItLsTorchBurned ?
            if (Hlp_GetInstanceID (itm) == Hlp_GetInstanceID (ItLsTorchBurned)) {
                NPC_UseItem (slf, itm);
                NPC_RemoveInvItem (slf,  ptr);
                
                ptr = 0;
            };
        };
        
        //Is hand empty?
        if (!ptr)
        {
            //Careful! NPC_GetInventoryItem is not actually returning pointer. I am using ptr variable just to 'save' variables :)
            
            //Search for ItLsTorchBurned
            ptr = NPC_GetInventoryItem (slf, ItLsTorchBurned);
            
            //Search for ItLsTorch
            if (!ptr) {
                ptr = NPC_GetInventoryItem (slf, ItLsTorch);
            };
            
            if (ptr) {
                //item is either ItLsTorchBurned or ItLsTorch from NPC_GetInventoryItem
                //Use it - puts ItLsTorchBurning in hand and remove it from inventory
                NPC_UseItem (slf, item);
                NPC_RemoveInvItem (slf, _@ (item));
            };
        };
    };
    
    /***
        If NPC is holding torch in hand it will put it back to inventory
    ***/
    FUNC VOID NPC_TorchSwitchOff (var int slfInstance)
    {
        var int ptr;
        var C_NPC slf;
        
        slf = Hlp_GetNPC (slfInstance);
    
        //Get pointer to ZS_LEFTHAND
        ptr = NPC_GetSlotItem (slf, "ZS_LEFTHAND");
        
        //Is there anything in hand?
        if (ptr) {
            var C_Item itm;
            itm = _^ (ptr);
            
            //Is it ItLsTorchBurning ?
            if (Hlp_GetInstanceID (itm) == Hlp_GetInstanceID (ItLsTorchBurning))
            || (Hlp_GetInstanceID (itm) == Hlp_GetInstanceID (ItLsTorchBurned))
            {
                //Use ItLsTorchBurning - will put ItLsTorch back to inventory - remove ItLsTorchBurning from inventory
                NPC_UseItem (slf, itm);
                NPC_RemoveInvItem (slf,  ptr);
            };
        };
    };
    
    /***
        Returns true if NPC is holding torch
    ***/
    FUNC INT NPC_UsesTorch (var int slfInstance)
    {
        var int ptr;
        var C_NPC slf;
        
        slf = Hlp_GetNPC (slfInstance);
    
        //Get pointer to ZS_LEFTHAND
        ptr = NPC_GetSlotItem (slf, "ZS_LEFTHAND");
        
        //Is there anything in hand?
        if (ptr) {
            var C_Item itm;
            itm = _^ (ptr);
            
            //Is it ItLsTorchBurning / ItLsTorchBurned ?
            if (Hlp_GetInstanceID (itm) == Hlp_GetInstanceID (ItLsTorchBurning))
            || (Hlp_GetInstanceID (itm) == Hlp_GetInstanceID (ItLsTorchBurned))
            {
                return TRUE;
            };
        };
        
        return FALSE;
    };


    [Hooks]


    Enabling more ZS_States for player:

    Code:
    //Originally posted here (slightly rewritten - 'inventory management' for followers integrated):
    //https://forum.worldofplayers.de/forum/threads/1533803-G1-AI_StartState-hardcoded-ZS-states-for-Player?p=26035799&viewfull=1#post26035799
    
    //Cannibalized from Ikarus 1.2.1 oCNPC class definition:
    class oCNpc_States {
            var int        state_vfptr;                                // 0x0470
            var string     state_name;                                 // 0x0474 zSTRING
            var int        state_npc;                                  // 0x0488 oCNpc*
    //      TNpcAIState curState {
                var int    state_curState_index;                       // 0x048C int
                var int    state_curState_loop;                        // 0x0490 int
                var int    state_curState_end;                         // 0x0494 int
                var int    state_curState_timeBehaviour;               // 0x0498 int
                var int    state_curState_restTime;                    // 0x049C zREAL
                var int    state_curState_phase;                       // 0x04A0 int
                var int    state_curState_valid;                       // 0x04A4 zBOOL
                var string state_curState_name;                        // 0x04A8 zSTRING
                var int    state_curState_stateTime;                   // 0x04BC zREAL
                var int    state_curState_prgIndex;                    // 0x04C0 int
                var int    state_curState_isRtnState;                  // 0x04C4 zBOOL
    //      }
    //      TNpcAIState nextState {
                var int    state_nextState_index;                      // 0x04C8 int
                var int    state_nextState_loop;                       // 0x04CC int
                var int    state_nextState_end;                        // 0x04D0 int
                var int    state_nextState_timeBehaviour;              // 0x04D4 int
                var int    state_nextState_restTime;                   // 0x04D8 zREAL
                var int    state_nextState_phase;                      // 0x04DC int
                var int    state_nextState_valid;                      // 0x04E0 zBOOL
                var string state_nextState_name;                       // 0x04E4 zSTRING
                var int    state_nextState_stateTime;                  // 0x04F8 zREAL
                var int    state_nextState_prgIndex;                   // 0x04FC int
                var int    state_nextState_isRtnState;                 // 0x0500 zBOOL
    };
    
    /*
        ZS_ASSESSMAGIC is allowed state for player, but it is not defined in G1 -> we can reserve it for ourselves
        When this function will be called from _HOOK_NPC_STATES_DOAISTATE for player - it will
            call npc.state_nextState_name (intended function)
            overwrite npc.state_nextState_loop with npc.state_nextState_name_LOOP
            overwrite npc.state_nextState_end with npc.state_nextState_name_END
    */        
    FUNC VOID ZS_ASSESSMAGIC ()
    {
        var oCNPC npc;
        npc = Hlp_GetNPC (self);
        
        //Disable further calls
        npc.state_nextState_index = -1;
        
        //Call npc.state_nextState_name (ZS_WHIRLWIND or others, if specified in _HOOK_NPC_STATES_DOAISTATE)
        MEM_CallByString (npc.state_nextState_name);
        
        //Overwrite npc.state_nextState_loop with npc.state_nextState_name_LOOP
        var string fNameLoop;
        fNameLoop = ConcatStrings (npc.state_nextState_name, "_LOOP");
        npc.state_nextState_loop = MEM_FindParserSymbol (fNameLoop);
        
        //Overwrite npc.state_nextState_end with npc.state_nextState_name_END
        var string fNameEnd;
        fNameEnd = ConcatStrings (npc.state_nextState_name, "_END");
        npc.state_nextState_end = MEM_FindParserSymbol (fNameEnd);
    };
    
    FUNC INT ZS_ASSESSMAGIC_Loop ()    {};
    FUNC VOID ZS_ASSESSMAGIC_End ()    {};
    
    //006C5C60  .text     Debug data           ?DoAIState@oCNpc_States@@QAEHXZ
    const int oCNpc_States__DoAIState_G1 = 7101536;
    
    //0x0076D1A0 public: int __thiscall oCNpc_States::DoAIState(void)
    const int oCNpc_States__DoAIState_G2 = 7786912;
    FUNC VOID _HOOK_NPC_STATES_DOAISTATE ()
    {
        var oCNpc_States state;
        state = _^ (ECX);
    
        var oCNPC npc;
        npc = _^ (state.state_npc);
    
        if (NPC_IsPlayer (npc))
        {
            if (state.state_nextState_index != 0)
            && (state.state_nextState_index != -1)
            {
                //Here you can specify what states are allowed for player
                if (Hlp_StrCmp (state.state_nextState_name, "ZS_WHIRLWIND"))
                || (Hlp_StrCmp (state.state_nextState_name, "ZS_INVMANAGEMENT"))
                {
                    //Overwrite state_nextState_index - this will point to function ZS_ASSESSMAGIC
                    //npc.state_nextState_name will still contain string "ZS_WHIRLWIND")
                    npc.state_nextState_index = MEM_FindParserSymbol ("ZS_ASSESSMAGIC");
                };
            };
        };
    };
    G1 Improved trading system:
    Code:
    //Originally posted here (slightly rewritten - 'inventory management' for followers integrated):
    //https://forum.worldofplayers.de/forum/threads/1505251-Skriptpaket-LeGo-4/page5?p=25782129&viewfull=1#post25782129
    
    //0072A870  .text     Debug data           ?OnAccept@oCViewDialogTrade@@IAIXXZ
    const int oCViewDialogTrade__OnAccept        = 7514224;
    
    //00729390  .text     Debug data           ?TransferAccept@oCViewDialogTrade@@IAIXXZ
    const int oCViewDialogTrade__TransferAccept    = 7508880;
    
    FUNC VOID _HOOK_TRADE_ONACCEPT ()
    {
        var int ptr;
        
        var oCNpcInventory npcInventory_Trader;
        var oCNpcInventory npcInventory_Trader_Offer;
        var oCNpcInventory npcInventory_Buyer_Offer;
        var oCNpcInventory npcInventory_Buyer;
    
        //If we are currently in inventory management for followers - exit
        if (InvManagement_Dialog == cIM_Dialog_Trade) {
            return;
        };
    
    //--- Get Item containers (oCItemContainer)
    
        //Traders Inventory
        ptr = MEMINT_oCInformationManager_Address;
        ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.ocViewDialogTrade
        if (ptr) {
            ptr = MEM_ReadInt (ptr + 248);    //oCViewDialogItemInventory
            if (ptr) {
                ptr = MEM_ReadInt (ptr + 256);    //oCItemContainer
                npcInventory_Trader = _^ (ptr);
            };
        };
    
        //Traders 'offer'
        ptr = MEMINT_oCInformationManager_Address;
        ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.ocViewDialogTrade
        if (ptr) {
            ptr = MEM_ReadInt (ptr + 252);    //oCViewDialogStealContainer
            if (ptr) {
                ptr = MEM_ReadInt (ptr + 256);    //oCItemContainer
                npcInventory_Trader_Offer = _^ (ptr);
            };
        };
    
        //Buyers 'offer'
        ptr = MEMINT_oCInformationManager_Address;
        ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.ocViewDialogTrade
        if (ptr) {
            ptr = MEM_ReadInt (ptr + 260);    //oCViewDialogItemContainer
            if (ptr) {
                ptr = MEM_ReadInt (ptr + 256);    //oCItemContainer
                npcInventory_Buyer_Offer = _^ (ptr);
            };
        };
    
        //Buyers inventory
        ptr = MEMINT_oCInformationManager_Address;
        ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.ocViewDialogTrade
        if (ptr) {
            ptr = MEM_ReadInt (ptr + 264);    //oCViewDialogItemInventory
            if (ptr) {
                ptr = MEM_ReadInt (ptr + 256);    //oCItemContainer
                npcInventory_Buyer = _^ (ptr);
            };
        };
        
    //--- Get Offer Value + Ore amount for both Trader and Buyer
    
        var int Value_Traders_Offer;
        var int Value_Buyers_Offer;
    
        var int Ore_Trader;
        var int Ore_Buyer;
    
        var int Delta;
    
        //inventory2_oCItemContainer_textCategoryStatic contains Total Value of offers - we can exploit that
        Value_Traders_Offer = STR_ToInt (npcInventory_Trader_Offer.inventory2_oCItemContainer_textCategoryStatic);
        Value_Buyers_Offer = STR_ToInt (npcInventory_Buyer_Offer.inventory2_oCItemContainer_textCategoryStatic);
        
        var C_NPC Trader;
        Trader = _^ (npcInventory_Trader.inventory2_owner);
        Ore_Trader = NPC_HasItems (Trader, ItMiNugget);
        
        var C_NPC Buyer;
        Buyer = _^ (npcInventory_Buyer.inventory2_owner);
        Ore_Buyer = NPC_HasItems (Buyer, ItMiNugget);
        
        Delta = Value_Traders_Offer - Value_Buyers_Offer;
        
        var string msg;
        
        if (Delta == 0) {
            //Trade went smoothly :)
        } else
        //Buyer has to supply ore !
        if (Delta > 0) {
            if (Delta > Ore_Buyer) {
                PrintScreen ("You don't have enough ore.", -1, _YPOS_MESSAGE_LOGENTRY, "font_old_20_white.tga", _TIME_MESSAGE_LOGENTRY);
            } else
            {
                //Accept Transfer anyway!            
                ptr = MEMINT_oCInformationManager_Address;
                ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.ocViewDialogTrade
    
                CALL__thiscall (ptr, oCViewDialogTrade__TransferAccept);
    
                //'Move' ore from Buyer to Trader
                NPC_RemoveInvItems (Buyer, ItMiNugget, Delta);
                CreateInvItems (Trader, ItMiNugget, Delta);
                
                msg = ConcatStrings (IntToString (Delta), "x ore given.");
                PrintScreen (msg, -1, _YPOS_MESSAGE_GIVEN, "font_old_10_white.tga", _TIME_MESSAGE_TAKEN);
            };
        } else
        //Trader has to supply ore !
        {
            //Abs (Delta)
            Delta = 0 - Delta;
            
            if (Delta > Ore_Trader) {
                PrintScreen ("Trader does not have enough ore.", -1, _YPOS_MESSAGE_LOGENTRY, "font_old_20_white.tga", _TIME_MESSAGE_LOGENTRY);
            } else
            {
                //Accept Transfer anyway!
                ptr = MEMINT_oCInformationManager_Address;
                ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.ocViewDialogTrade
    
                CALL__thiscall (ptr, oCViewDialogTrade__TransferAccept);
    
                //'Move' ore from Trader to Buyer
                NPC_RemoveInvItems (Trader, ItMiNugget, Delta);
                CreateInvItems (Buyer, ItMiNugget, Delta);
                
                msg = ConcatStrings (IntToString (Delta), "x ore taken.");
                PrintScreen (msg, -1, _YPOS_MESSAGE_GIVEN, "font_old_10_white.tga", _TIME_MESSAGE_TAKEN);
            };
        };
    };
    Hooks detecting closed inventory.
    Only by using hooks we can properly detect if player closed trading & inventory.
    If inventory is opened, one would expect when it is closed, that function oCNPC__CloseInventory would be called. BUT, there are several cases, where inventory closes and function oCNPC__CloseInventory is not called.
    So player would f*k up our system by:
    Drawing a weapon (oCNpc__EV_DrawWeapon, oCNpc__EV_DrawWeapon1, oCNpc__EV_DrawWeapon2)
    By pressing 'M' button to show map (I hate this hotkey) (oCMapScreen__Show)
    By pressing 'S', 'B' button to show Status screen (oCStatusScreen__Show) - this can be handled without hook by checking MEM_Game.singleStep
    By pressing 'L', 'N' button to show Log screen (oCLogScreen__Show) - this can also be handled without hook by checking MEM_Game.singleStep

    Code:
    /***
        Function is called when trading is closed
    ***/
    //0072AAB0  .text     Debug data           ?OnExit@oCViewDialogTrade@@IAIXXZ
    const int oCViewDialogTrade__OnExit        = 7514800;
    FUNC VOID _HOOK_TRADE_ONEXIT ()
    {
        //If we are currently in inventory management for followers ...
        if (InvManagement_Dialog == cIM_Dialog_Trade) {
            InvManagement_Dialog = cIM_Dialog_Trade_Closing;
    
            //End dialog for Follower
            AI_StopProcessInfos (Follower);
            
            //Start ZS_InvManagement for hero (Guide) 
            AI_StartState (Guide, ZS_InvManagement, 1, "");
    
            //Restore default trade values
            Trade_ChangeSellMultiplier (divf (mkf (3), mkf (10)));    //30%
            Trade_ChangeBuyMultiplier (FLOATONE);            //100%
        };
    };
    
    /***
        Function overwrites container total-value.
        Without this total value of players sold items would still be calculated and would not disappear, when you would be trading.
        Player could generate infinite amount of $ by moving back and forward items between himself and follower.
        For some reason Gothic ignores fact, that we used FLOATNULL in Trade_ChangeSellMultiplier ! ;(
    ***/
    //00727900  .text     Debug data           ?UpdateValue@oCViewDialogItemContainer@@IAIXXZ
    const int oCViewDialogItemContainer__UpdateValue = 7502080;
    FUNC VOID _HOOK_TRADESELL_UPDATEVALUE ()
    {
        if (InvManagement_Dialog == cIM_Dialog_Trade)
        {
            var int ptr;
            
            //Buyers 'offer'
            ptr = MEMINT_oCInformationManager_Address;
            ptr = MEM_ReadInt (ptr + 24);    //oCInformationManager.ocViewDialogTrade
            if (ptr) {
                ptr = MEM_ReadInt (ptr + 260);    //oCViewDialogItemContainer
                if (ptr) {
                    //Overwrite container - total value - write 0
                    MEM_WriteInt (ptr + 264, 0);
                };
            };
        };
    };
    
    /***
        Function is called when player presses 'M' for to show Map
    ***/
    //00478490  .text     Debug data           ?Show@oCMapScreen@@QAEXXZ
    const int oCMapScreen__Show = 4686992;
    FUNC VOID _HOOK_MAPSCREEN_SHOW ()
    {
        if (InvManagement_Dialog != cIM_Dialog_Default) {
            InvManagement_Dialog = cIM_Dialog_Inventory_Reopen;
        };
    };
    
    /***
        Weapon drawing closes inventory, also - we don't want to drop torch, put it back to inventory
    ***/
    
    //006A8500  .text     Debug data           ?EV_DrawWeapon@oCNpc@@QAEHPAVoCMsgWeapon@@@Z
    //006A8B80  .text     Debug data           ?EV_DrawWeapon1@oCNpc@@QAEHPAVoCMsgWeapon@@@Z
    //006A8E20  .text     Debug data           ?EV_DrawWeapon2@oCNpc@@QAEHPAVoCMsgWeapon@@@Z
    const int oCNpc__EV_DrawWeapon            = 6980864;
    const int oCNpc__EV_DrawWeapon1            = 6982528;
    const int oCNpc__EV_DrawWeapon2            = 6983200;
    
    FUNC VOID _HOOK_NPC_DRAWWEAPON ()
    {
        var oCNPC npc;
        npc = _^ (ECX);
        
        //Just in case
        if (Hlp_IsValidNPC (npc)) {
            //Is this our Follower?
            if (Hlp_GetInstanceID (npc) == Hlp_GetInstanceID (Follower)) {
                //Exit
                if (InvManagement_Dialog != cIM_Dialog_Default) {
                    InvManagement_Dialog = cIM_Dialog_Exit;
                };
            };
            
            //Remove torch from hand and put it to inventory. Don't drop it.
            NPC_TorchSwitchOff (npc);
        };
    };
    
    /***
        Function is closed when inventory is closed (only when properly closed ;( )
    ***/
    FUNC VOID _HOOK_NPC_CLOSEINVENTORY ()
    {
        var oCNPC npc;
        npc = _^(ECX);
        
        if (InvManagement_Dialog != cIM_Dialog_Default) {
            InvManagement_Dialog = cIM_Dialog_Exit;
        };
    };
    
    //Improved trading
    HookEngine (oCViewDialogTrade__OnAccept, 6, "_HOOK_TRADE_ONACCEPT");
    
    //Hook overwriting total value of players items for selling
    HookEngine (oCViewDialogItemContainer__UpdateValue, 7, "_HOOK_TRADESELL_UPDATEVALUE");
    
    //Hook detecting closed trading
    HookEngine (oCViewDialogTrade__OnExit, 5, "_HOOK_TRADE_ONEXIT");
    
    //Hooks detecting closed inventory
    HookEngine (oCMapScreen__Show, 7, "_HOOK_MAPSCREEN_SHOW");
    
    HookEngine (oCNpc__EV_DrawWeapon, 6, "_HOOK_NPC_DRAWWEAPON");
    HookEngine (oCNpc__EV_DrawWeapon1, 5, "_HOOK_NPC_DRAWWEAPON");
    HookEngine (oCNpc__EV_DrawWeapon2, 6, "_HOOK_NPC_DRAWWEAPON");
    
    HookEngine (oCNPC__CloseInventory, 6, "_HOOK_NPC_CLOSEINVENTORY");
    Modified ZS_FollowPC () to improve 'AI':

    Code:
    FUNC VOID B_FollowPC_AssessSC ()
    {
        var int removeTorch; removeTorch = FALSE;
    
    //--- Immersive AI
    
        if (NPC_GetDistToNPC (self, hero) < HAI_DIST_ACTIONRANGE)
        {
            if (NPC_GetWalkMode (hero) == NPC_SNEAK)
            {
                if (NPC_GetWalkMode (self) != NPC_SNEAK)
                {
                    B_FullStop (self);
                    AI_SetWalkMode (self, NPC_SNEAK);
                };
                
                //If player is sneaking - remove torch
                removeTorch = TRUE;
            } else
            if (NPC_GetWalkMode (self) == NPC_SNEAK)
            {
                B_FullStop (self);
                AI_SetWalkMode (self, NPC_RUN);
            };
        };
    
        //If player is in portal room - remove torch
        if (!Hlp_StrCmp (Wld_GetPlayerPortalRoom (), ""))
        {
            removeTorch = TRUE;
        };
    
        //If it is night time - and there is no reason to remove torch - use it. Otherwise remove it.
        if (Wld_IsTime (21, 30, 05, 30))
        && (removeTorch == FALSE) {
            NPC_TorchSwitchOn (self);
        } else {
            NPC_TorchSwitchOff (self);
        };
        
    //--- ZS_Talk
        
        if (NPC_CheckInfo (self, 1))
        {
            if (!C_BodyStateContains (hero, BS_FALL))
            {
                if (!C_BodyStateContains (hero, BS_SWIM))
                {
                    if (!C_BodyStateContains (hero, BS_DIVE))
                    {
                        //Is player close enough?
                        if (NPC_GetDistToNpc (self, hero) <= 800)
                        {
                            NPC_PercDisable (self, PERC_ASSESSPLAYER);
                            
                            hero.aivar[AIV_IMPORTANT] = TRUE;
                            
                            B_FullStop (self);
                            B_FullStop (hero);
                            AI_StartState (self, ZS_Talk, 0, "");
                            
                            return;
                        };
                    };
                };
            };
        };
    };
    
    FUNC VOID ZS_FollowPC ()
    {
        NPC_PercEnable (self, PERC_ASSESSENEMY, B_AssessEnemy);
        Perc_SetRange (PERC_ASSESSPLAYER, 5000);
        NPC_PercEnable (self, PERC_ASSESSPLAYER, B_FollowPC_AssessSC);
    
        NPC_SetPercTime (self, 1);
        self.senses = SENSE_SEE | SENSE_HEAR | SENSE_SMELL;
    
        NPC_PercEnable (self, PERC_ASSESSDAMAGE, ZS_ReactToDamage);
        NPC_PercEnable (self, PERC_ASSESSCASTER, B_AssessCaster);
        NPC_PercEnable (self, PERC_ASSESSMAGIC, B_AssessMagic);
        NPC_PercEnable (self, PERC_ASSESSMURDER, ZS_AssessMurder);
        NPC_PercEnable (self, PERC_ASSESSDEFEAT, ZS_AssessDefeat);
        NPC_PercEnable (self, PERC_ASSESSFIGHTSOUND, B_AssessFightSound);
        NPC_PercEnable (self, PERC_ASSESSTALK, B_AssessTalk);
        NPC_PercEnable (self, PERC_MOVEMOB, B_MoveMob);
    };
    
    FUNC INT ZS_FollowPC_Loop ()
    {
        //Call this perception anyway ...
        B_FollowPC_AssessSC ();
        
        if (NPC_GetDistToNpc (self, hero) > HAI_DIST_FOLLOWPC)
        {
            if (!C_BodyStateContains (self, BS_SWIM))
            {
                if (NPC_GetWalkMode (self) != NPC_RUN)
                && (NPC_GetWalkMode (self) != NPC_SNEAK)
                {
                    AI_SetWalkMode (self, NPC_RUN);
                };
            };
    
            AI_GotoNpc (self, hero);
        } else
        {
            B_SmartTurnToNpc (self, hero);
        };
        
        AI_Wait (self, 1);
        return LOOP_CONTINUE;
    };
    
    FUNC VOID ZS_FollowPC_End ()
    {
        self.senses = hero.senses;
    };
    ZS_InvManagement:
    Code:
    FUNC VOID NPC_SetAsPlayer (var int slfInstance)
    {
        //0069EAE0  .text     Debug data           ?SetAsPlayer@oCNpc@@UAEXXZ
        const int oCNPC__SetAsPlayer = 6941408;
        
        var C_NPC slf;
        slf = Hlp_GetNPC (slfInstance);
        
        CALL__thiscall (MEM_InstToPtr (slf), oCNPC__SetAsPlayer);
    };
    
    FUNC VOID ZS_InvManagement () { };
    
    FUNC INT ZS_InvManagement_Loop ()
    {
        //LogScreen, StatusScreen - both close inventory, and in case of both MEM_Game.singleStep == TRUE
        if (MEM_Game.singleStep) {
            //Reopen inventory
            InvManagement_Dialog = cIM_Dialog_Inventory_Reopen;
        } else {
            //If trading was closed (accept / exit)
            if (InvManagement_Dialog == cIM_Dialog_Trade_Closing) {
                //Wait for InfoManager to disappear
                if (InfoManager_HasFinished ())
                {
                    //Re-apply AIV_INVINCIBLE (we don't want to be approached by any NPC)
                    Guide.aivar[AIV_INVINCIBLE] = TRUE;
                    Follower.aivar[AIV_INVINCIBLE] = TRUE;
                    
                    //Set as player and open inventory
                    NPC_SetAsPlayer (Follower);
                    NPC_OpenInventory (Follower);
                    InvManagement_Dialog = cIM_Dialog_Inventory;
                };
            };
    
            //If inventory was closed but _HOOK_NPC_CLOSEINVENTORY was not called (so inventory was closed incorrectly by weapon drawing or by MapScreen), reopen it
            if (InvManagement_Dialog == cIM_Dialog_Inventory_Reopen)
            {
                NPC_OpenInventory (Follower);
                InvManagement_Dialog = cIM_Dialog_Inventory;
            };
            
            //If player closed inventory
            if (InvManagement_Dialog == cIM_Dialog_Exit)
            {
                //Set as player our 'true' hero
                NPC_SetAsPlayer (Guide);
    
                //Restore self & other
                self = Hlp_GetNPC (Follower);
                other = Hlp_GetNPC (Guide);
                
                //Reset AIV_INVINCIBLE so ZS_Talk can start
                self.aivar[AIV_INVINCIBLE] = FALSE;
                other.aivar[AIV_INVINCIBLE] = FALSE;
    
                //Start ZS_Talk
                AI_StartState (self, ZS_Talk, 1, "");
                
                return LOOP_END;
            };
        };
    
        return LOOP_CONTINUE;
    };
    
    FUNC VOID ZS_InvManagement_End () { };
    Dialog instances - add it to all NPCs within ZS_Talk (condition will make sure it is visible only for Mud & Shrat):

    Code:
    //Inventory management
    INSTANCE DIA_Follower_InvManagement (C_Info)
    {
        nr        = 776;
        condition    = DIA_Follower_InvManagement_Condition;
        information    = DIA_Follower_InvManagement_Info;
        permanent    = TRUE;
        trade        = TRUE;
        description    = "(inventory management)";
    };
    
    FUNC INT DIA_Follower_InvManagement_Condition ()
    {
        //Is this follower?
        if (self.aivar [AIV_PARTYMEMBER])
        {
            if (self.ID == 574)    //Mud
            || (self.ID == 1356)    //Shrat
            {
                return TRUE;
            };
        };
        
        return FALSE;
    };
    
    FUNC VOID DIA_Follower_InvManagement_Info ()
    {
        //Set sell & buy multiplier to 0
        Trade_ChangeSellMultiplier (FLOATNULL);
        Trade_ChangeBuyMultiplier (FLOATNULL);
        
        //Remember who is Guide (hero) and who is our Follower
        Follower = Hlp_GetNPC (self);
        Guide = Hlp_GetNPC (other);
        
        //We are currently trading
        InvManagement_Dialog = cIM_Dialog_Trade;
    };
    
    //Wait here / follow me
    //Originally posted here:
    //https://forum.worldofplayers.de/forum/threads/1532032-G1-Monster-kehren-IMMER-zum-Spawnpunkt-zur%C3%BCck%21?p=26003997&viewfull=1#post26003997
    /*
    Routine for waiting followers
    */
    FUNC VOID Follower_WaitHere_Routine ()
    {
        TA (self, 0, 23, ZS_StandAround, self.WP);
    };
    
    /*
    Pointer adjusted for G1, G2A code originally posted by Lehona:
    https://forum.worldofplayers.de/forum/threads/1483066-Aktuellen-Routinennamen-eines-Npcs-herausbekommen?p=25220976&viewfull=1#post25220976
    */
    FUNC STRING NPC_GetRoutineName (var C_NPC slf)
    {
        var int npcPtr; npcPtr = _@ (slf);
        
        var int symbID; symbID = MEM_ReadInt (npcPtr + 536);
        var zCPar_symbol symb; symb = _^ (MEM_GetSymbolByIndex (symbID));
        return symb.name;
    };
    
    /*
    Function checks if NPC_GetRoutineName is in RTN_ rtnName _NPC.ID format
    */
    FUNC INT NPC_IsInRoutineName (var C_NPC slf, var string rtnName)
    {
        var string curRtnName;
        curRtnName = STR_Upper (rtnName);
        curRtnName = ConcatStrings ("RTN_", curRtnName);
        curRtnName = ConcatStrings (curRtnName, "_");
        curRtnName = ConcatStrings (curRtnName, IntToString (slf.ID));
        
        return Hlp_StrCmp (NPC_GetRoutineName (slf), curRtnName);
    };
    
    //--- Dialogs below are added via ZS_Talk to any NPC with AIV_PARTYMEMBER flag
    
    INSTANCE DIA_Follower_Wait_Here (C_Info)
    {
        nr        = 778;
        condition    = DIA_Follower_Wait_Here_Condition;
        information    = DIA_Follower_Wait_Here_Info;
        permanent    = TRUE;
        description    = "(wait here)";
    };
    
    FUNC INT DIA_Follower_Wait_Here_Condition ()
    {
        //Is this follower?
        if (self.aivar [AIV_PARTYMEMBER])
        {
            //Is it human?
            if (C_NPCIsHuman (self))
            {
                //Is NPC in FOLLOW routine ? (for example RTN_FOLLOW_1234)
                if (NPC_IsInRoutineName (self, "FOLLOW"))
                {
                    return TRUE;
                };
            };
        };
    
        return FALSE;
    };
    
    FUNC VOID DIA_Follower_Wait_Here_Info ()
    {
        //Get Nearest waypoint
        var string WP;    WP = NPC_GetNearestWP (self);
        self.WP = WP;
    
        //Overlay current routine
        TA_BeginOverlay (self);
        TA (self, 0, 23, ZS_StandAround, self.WP);
        TA_EndOverlay (self);
    
        //Change daily_routine for Save/Load
        self.daily_routine = Follower_WaitHere_Routine;
    };
    
    INSTANCE DIA_Follower_Wait_Here_Come (C_Info)
    {
        nr        = 778;
        condition    = DIA_Follower_Wait_Here_Come_Condition;
        information    = DIA_Follower_Wait_Here_Come_Info;
        permanent    = TRUE;
        description    = "(follow me)";
    };
    
    FUNC INT DIA_Follower_Wait_Here_Come_Condition ()
    {
        //Is this follower?
        if (self.aivar [AIV_PARTYMEMBER])
        {
            //Is it human?
            if (C_NPCIsHuman (self))
            {
                //Is NPC waiting in FOLLOWER_WAITHERE_ROUTINE routine ?
                if (Hlp_StrCmp (NPC_GetRoutineName (self), "FOLLOWER_WAITHERE_ROUTINE"))
                {
                    return TRUE;
                };
            };
        };
    
        return FALSE;
    };
    
    FUNC VOID DIA_Follower_Wait_Here_Come_Info ()
    {
        NPC_ExchangeRoutine (self, "FOLLOW");
    };
    
    /*
    B_Assign_Follower_Dialogs added in ZS_Talk to any NPC with AIV_PARTYMEMBER flag:
    
        FUNC VOID ZS_Talk ()
        {
            ...
    
            if (self.aivar [AIV_PARTYMEMBER])    {    B_Assign_Follower_Dialogs (self);    };
    
            ...
        };
    */
    
    FUNC VOID B_Assign_Follower_Dialogs (var C_Npc slf)
    {
        DIA_Follower_InvManagement.npc = Hlp_GetInstanceID (slf);
    
        DIA_Follower_Wait_Here.npc = Hlp_GetInstanceID (slf);
        DIA_Follower_Wait_Here_Come.npc = Hlp_GetInstanceID (slf);
    };
    And that's it. Hopefully I didn't forget any piece of code.

  6. Beiträge anzeigen #26 Zitieren
    Knight Avatar von GenerationLost
    Registriert seit
    Apr 2009
    Ort
    Whereabouts Unknown
    Beiträge
    1.774
     
    GenerationLost ist offline
    Zitat Zitat von Bisasam Beitrag anzeigen
    Das Skript-Update ist immernoch auf G1 abgestimmt und dort werden die Zustände genau so gesetzt, wie ich sie brauche. Ich hab das an allen möglichen und unmöglichen Mobs und Vobs versucht und es gibt kein geploppe oder Verhaltensbugs mehr. Ob das Skript uneingeschränkt G2 tauglich ist weiß ich nicht.

    Auf kurz oder lang würde ich aber auch besser finden, das Inventar einfach durchzuzählen. Die Frage ist: Wie macht man das bei dieser Fülle an Items am besten? Am liebsten würde ich ja Kategorie für Kategorie durchgehen, abfragen wie viele Items mit wie viel Gewicht sich darin befinden und dann alles zusammenrechnen. ich find nur keine Funktion, die mir erlaubt, Items ohne konkrete Instanzangabe aus dem Inv zu bekommen.
    Soweit ich weiß hatte ich in meinem Paket schon eine Funktion die einmal über das gesamte Inv des Hero iteriert und das Gewicht entsprechend aufsummiert.
    "CarryWeight_RecalcInventory" zusammen mit "CarryWeight_AddInvCategoryWeight"
    Dabei werden die Items pro Kategorie durchgezählt.
    Wenn das Inventar sehr sehr groß wird, könnte man das ganze evtl. auf mehrere Ticks verteilen, anstatt alles in einem Rutsch zu rechnen. Als Test nahm ich einfach mal die Fülle an Items vom Rockefeller und dabei hat das Neuberechnen so eine Art "micro stutter" ausgelöst, also nur für den Bruchteil einer Sekunde.

    EDIT: Schön zu sehen, dass hier doch noch ein bisschen was los ist^^ Und vielen Dank für die Verbesserungen @Bisasam
    Geändert von GenerationLost (09.08.2019 um 10:40 Uhr)

  7. Beiträge anzeigen #27 Zitieren
    Legende der Amazonen Avatar von Bisasam
    Registriert seit
    Dec 2006
    Ort
    Meine Faust in Sinis Gesicht
    Beiträge
    9.639
     
    Bisasam ist offline
    Ja diese Kategory-Funktionen hab ich gesehen aber irgendwie wurden die nie ausgelöst. Außerdem bräuchte man die ganzen Hooks nicht, wenn man eh regelmäßig durchzählt.


    "Das erinnert doch sehr erfreulich an das, was man sich als Gothicfan wünscht!"
    -Korallenkette

  8. Beiträge anzeigen #28 Zitieren
    Knight Avatar von GenerationLost
    Registriert seit
    Apr 2009
    Ort
    Whereabouts Unknown
    Beiträge
    1.774
     
    GenerationLost ist offline
    Ein Anwendungsfall dafür hatte ich z.B. beim Ohnmächtigwerden des Helden im Kampf, wobei er die gezogene Waffe fallen lässt (wo ein Lag auch nicht den Spielfluss stört, da der Spieler in dem Moment eh keine Kontrolle mehr hat). Da hatte ich zunächst keinen zuverlässigen Weg gefunden, die Waffe zu ermitteln, bevor sie fallen gelassen wird (wie ich sehe hast du dafür ja bereits eine bessere Lösung). Was anderes fällt mir grad im Stehgreif nicht ein. Und ja, generell halt als Notlösung, falls alle Stricke reißen.

    Ich dachte eben, es sei bzgl. der Performanz besser, gleich an den richtigen Stellen zu hooken, anstatt jedes Mal übers gesamte Inventory zu iterieren und Letzteres nur für eben die "Notfälle" zu nutzen, wie z.B. oben erwähnt.
    Geändert von GenerationLost (09.08.2019 um 15:38 Uhr)

  9. Beiträge anzeigen #29 Zitieren
    Legende der Amazonen Avatar von Bisasam
    Registriert seit
    Dec 2006
    Ort
    Meine Faust in Sinis Gesicht
    Beiträge
    9.639
     
    Bisasam ist offline
    Ja die Performance ist schon so ne Sache, da hast du Recht. Einfach alle Items jeden Frame durchzuzählen würde wohl allen, die mit nem älteren Rechner spielen, das Spiel richtig krass verruckeln. Wie wärs wenn wir stattdessen bei den Hooks das Signal zum zählen geben und so bei jedem Aufheben etc. das Inventar schnell richtig stellen? Gibt aktuell nämlich zu viele Sachen die bei Plus und Minus falsch laufen können.


    "Das erinnert doch sehr erfreulich an das, was man sich als Gothicfan wünscht!"
    -Korallenkette

  10. Beiträge anzeigen #30 Zitieren
    Knight Avatar von GenerationLost
    Registriert seit
    Apr 2009
    Ort
    Whereabouts Unknown
    Beiträge
    1.774
     
    GenerationLost ist offline
    Ja das klingt eigentlich nicht schlecht. Da kann man ja auch gerade anhand des jeweiligen Items die Inv-Kategorie bestimmen und nur einmal darüber laufen. Dabei müsste man dann wohl einen eigenen Gewichtszähler pro Kategorie haben (da das gesamte Gewicht ja zu Beginn genullt wird, um alles neu durchzuzählen), das Gewicht aller Items dieser Kategorie neu ausrechnen und zu den anderen wieder aufaddieren. Das sollte dann eigentlich schon ein Stückchen performanter sein als alles zu zählen.

    Wobei gab es bei meiner Methode denn jetzt eigentlich konkret die Probleme? Bis auf evtl. fehlende Fälle. Das würde mich mal interessieren.

  11. Beiträge anzeigen #31 Zitieren
    Provinzheld
    Registriert seit
    Mar 2010
    Ort
    Berlin
    Beiträge
    201
     
    withmorten ist offline
    Es gibt oCNpcInventory::Insert und diverse Remove Variationen ... würde das helfen?

    Davon abgesehen: Ihr glaubt gar nicht was das Spiel so pro Frame alles macht Mit C++ und vernünftiger Compiler Optimierung würde das vermutlich auch gar nicht ruckeln.

  12. Beiträge anzeigen #32 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.447
     
    Lehona ist offline
    Zitat Zitat von withmorten Beitrag anzeigen
    Es gibt oCNpcInventory::Insert und diverse Remove Variationen ... würde das helfen?

    Davon abgesehen: Ihr glaubt gar nicht was das Spiel so pro Frame alles macht Mit C++ und vernünftiger Compiler Optimierung würde das vermutlich auch gar nicht ruckeln.
    Eine VM ohne JIT, wie es Daedalus ist, ist nunmal kein nativer Code. Ich beschäftige mich momentan mit einer minimalistischen VM und selbst die hat einen Overhead-Faktor von ca. 25x (d.h. 25 Cycles pro VM-Instruktion).

    Aber im Zweifelsfall könnte man den Kram natürlich in Assembly schreiben, so kompliziert sind die Inventarlisten nicht. Dann ist es plötzlich wieder performant

  13. Beiträge anzeigen #33 Zitieren
    Provinzheld
    Registriert seit
    Mar 2010
    Ort
    Berlin
    Beiträge
    201
     
    withmorten ist offline
    Wenn C++ schon sone Riesenbarriere ist, dann ist es Assembly aber doch erst recht

  14. Beiträge anzeigen #34 Zitieren
    Legende der Amazonen Avatar von Bisasam
    Registriert seit
    Dec 2006
    Ort
    Meine Faust in Sinis Gesicht
    Beiträge
    9.639
     
    Bisasam ist offline
    Zitat Zitat von GenerationLost Beitrag anzeigen
    Ja das klingt eigentlich nicht schlecht. Da kann man ja auch gerade anhand des jeweiligen Items die Inv-Kategorie bestimmen und nur einmal darüber laufen. Dabei müsste man dann wohl einen eigenen Gewichtszähler pro Kategorie haben (da das gesamte Gewicht ja zu Beginn genullt wird, um alles neu durchzuzählen), das Gewicht aller Items dieser Kategorie neu ausrechnen und zu den anderen wieder aufaddieren. Das sollte dann eigentlich schon ein Stückchen performanter sein als alles zu zählen.

    Wobei gab es bei meiner Methode denn jetzt eigentlich konkret die Probleme? Bis auf evtl. fehlende Fälle. Das würde mich mal interessieren.
    Also nach meinem Bugfix scheint es nichts mehr zu geben was noch schief laufen kann. Ich finds nur doof, dass man wirklich in jede blöde UseWithItem und in jeden Herstelldialog nochmal das mit dem Gewicht reinschreiben muss.
    Vom Skriptaufwand wäre es erheblich einfacher, wenn man z.B. dann durchzählt wenn der Held einen Dialog beendet (kann man mit AIV_INVINCIBLE abfragen oder PLAYER_MOBSI_PRODUCTION) und wenn eine Verwendungs-Animation benutzt wird. Ich bin grad dabei eine Funktion zu schreiben, die ausliest, welche Ani der Held durchführt (diesen Teil hat Fawkes bereitgestellt) und diese dann abgleicht mit den vom Modder eingetragenen Anis (z.B. MAPSEALED, etc.). Dann kann bei Dialogende nochmal kurz drübergelaufen werden und gut ist. Führt dann zu nem mikroruckler aber der fällt beim Kameraschwenk nicht besonders auf.


    "Das erinnert doch sehr erfreulich an das, was man sich als Gothicfan wünscht!"
    -Korallenkette
    Geändert von Bisasam (11.08.2019 um 23:17 Uhr)

  15. Beiträge anzeigen #35 Zitieren
    Legende der Amazonen Avatar von Bisasam
    Registriert seit
    Dec 2006
    Ort
    Meine Faust in Sinis Gesicht
    Beiträge
    9.639
     
    Bisasam ist offline
    Soo das ist die Animationsabfragefunktion:

    Code:
    ///// Npc_GetAniName ist von FAWKES /////
    
    FUNC STRING NPC_GetAniName (var int slfInstance)
    {
        var oCNPC slf;
        slf = Hlp_GetNPC (slfInstance);
        
        var string str_Ani;
        str_Ani = oCAniCtrl__GetCurrentAniName (slf.AniCtrl);
    
        return str_Ani;
    };
    
    
    ///// Ab hier Bisasam /////
    
    func string BuildAniString(var string Beginning,var string Middle, var string End)
    {
    	var string ReturnString;
    	ReturnString=Concatstrings(Beginning,Middle);
    	ReturnString=Concatstrings(ReturnString,End);
    	
    	return ReturnString;
    };
    
    func int Npc_IsInAnimation(var int NscInst, var string CurrAni, var int IgnoreState)
    {
    	var string HerosAniName;
    	HerosAniName=NPC_GetAniName(NscInst);
    	
    	var string ConcAni;
    	
    	////////////////////////////////
    	//		FALLS MAN IN DER ABFRAGE SCHON SO GENAU WAR DASS ES SOFORT PASST
    
    	
    	if Hlp_StrCmp(HerosAniName,CurrAni)==TRUE
    	{
    		return TRUE;	
    	};	
    	
    	//////////////////////////////////////////
    	//		STATE und TRANSITION, nicht für Bewegungen wie Waffenlauf, Schleichen etc. geeignet!
    	//		Für obige Spezialfälle bitte schon bei Varübergabe konkret werden!
    	//		Es wird deswegen nicht mit || gearbeitet weil Gothic 1 ein Problem mit zu vielen 
    	// 		Verschachtelungen kriegt wenn ein und dieselbe Funktion oft aufgerufen wird
    	
    	
    	if IgnoreState==FALSE //Sollen S_X_S1 etc. Funktionen ignoriert werden?
    	{
    			
    	ConcAni=BuildAniString("S_",CurrAni,"_S0");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};	
    	
    	ConcAni=BuildAniString("S_",CurrAni,"_S1");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("S_",CurrAni,"_S2");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("S_",CurrAni,"_S3");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("S_",CurrAni,"_S4");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	
    	};
    	
    	/////////////////////////////////////////////////
    	//		State Trans
    	
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S0_2_S1");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S1_2_S0");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S1_2_S2");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S2_2_S1");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S2_2_S3");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S3_2_S2");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S3_2_S4");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S4_2_S3");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	//////////////////////////////////////////
    	//		Zurück zu stand
    	
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_S0_2_STAND");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_STAND_2_S0");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	
    	
    	
    	
    	//////////////////////////////////////
    	//		SPEZIALFALL T_STAND_2_SIT u.ä.
    	//
    	
    	ConcAni=BuildAniString("T_STAND_2_",CurrAni,"");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("T_",CurrAni,"_2_STAND");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	if IgnoreState==FALSE
    	{
    			
    	ConcAni=BuildAniString("S_",CurrAni,"");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	};
    	
    	//////////////////////////////////////////////////
    	//		RANDOM ANIS BENCH, CHAIR, ETC.
    	
    	ConcAni=BuildAniString("R_",CurrAni,"_RANDOM_1");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("R_",CurrAni,"_RANDOM_2");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("R_",CurrAni,"_RANDOM_3");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	ConcAni=BuildAniString("R_",CurrAni,"_RANDOM_4");
    	
    	if Hlp_StrCmp(HerosAniName,ConcAni)==TRUE
    	{
    		return TRUE;	
    	};
    	
    	/////////////////////////////
    	// Passt alles nicht?
    	
    	return FALSE;
    	
    };
    
    func int Npc_IsInTransitionAni(var int NscInst)
    {
    	
    	//					INSTANZ , ANINAME     , S_Anis Ignorieren?
    	if Npc_IsInAnimation(NscInst,"SLEEPGROUND",TRUE)==TRUE
    	|| Npc_IsInAnimation(NscInst,"WASH",TRUE)==TRUE
    	|| Npc_IsInAnimation(NscInst,"SIT",TRUE)==TRUE
    	|| Npc_IsInAnimation(NscInst,"LGUARD",TRUE)==TRUE
    	|| Npc_IsInAnimation(NscInst,"HGUARD",TRUE)==TRUE
    	{
    		return TRUE;	
    	};	
    	
    	return FALSE;
    };

    Die Abfrage nimmt eine Instanz entgegen, ermittelt deren aktuelle Animation und gleicht ab, ob sie mit der gesuchten Animation übereinstimmt.
    var int IgnoreState ist für S-Animationen zuständig. Also z.B. S_SIT, S_BATHTUB_S1 etc. Wenn ihr dort TRUE reinschreibt, wird die Überprüfung auf die genannten Animationen NICHT stattfinden. Das ist vor allem dann wichtig, wenn ihr nur die T- oder R-Animationen überprüfen möchtet. Ich benutze das um rauszukriegen, ob sich mein Held gerade in einer Übergangsanimation befindet. Dann sollen bestimmte Dinge nicht möglich sein um Bugs auszumerzen.
    Wenn ihr FALSE schreibt, wird die Prüfung auf S_BATHTUB_S1 stattfinden.

    Für Kampf- und Bewegungsanimationen konnte ich kein Standardschema erstellen da diese immer unterschiedlich sind. Aus diesem Grund solltet ihr diese Fälle konkret eingeben statt nur den Mittelteil. Statt "RUN_2_WALK" also lieber "T_RUN_2_WALK"


    "Das erinnert doch sehr erfreulich an das, was man sich als Gothicfan wünscht!"
    -Korallenkette
    Geändert von Bisasam (26.09.2019 um 18:21 Uhr)

Seite 2 von 2 « Erste 12

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