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
    Local Hero
    Registriert seit
    Feb 2017
    Beiträge
    270
     
    F a w k e s ist offline

    G1 weapon stacking/splitting

    Hello folks,
    With Auronen we were working on weapon-stacking, well actually on weapon-splitting feature for G1. Weapon-stacking is something that you can easily achieve by adding ITEM_MULTI flag to your weapons:
    Code:
    INSTANCE ItMw_1H_Club_01 (C_Item){    
        name = "Club";  
        mainflag = ITEM_KAT_NF;
        flags = ITEM_AXE | ITEM_MULTI;
    Disadvantage with weapon-stacking is - when you equip weapon with ITEM_MULTI flag, all items in same inventory slot are 'equipped'. If you have more pieces of same weapon and you would like to sell extra pieces, you have to unequip, sell and then re-equip it. With this feature equipped weapon will always go into its own item slot. (same behaviour as in G2A) Demonstration here:
    [Video]

    If you would like to try it out - we have opened GitHub repository for this little project:
    https://github.com/auronen/AF-Script...ket-status-wip

    Going forward we will add more features into this repository. Auronen is working on a cool G2A feature, he will probably let us know more about it shortly

    Please be aware this feature requires both Ikarus and Lego. Also it is experimental - there might be some flaws, if you face any issues - let us know
    Geändert von F a w k e s (23.12.2021 um 08:20 Uhr) Grund: Updating title

  2. Beiträge anzeigen #2 Zitieren
    Apprentice Avatar von Auronen
    Registriert seit
    Dec 2020
    Beiträge
    25
     
    Auronen ist offline

    AF_EnhancedDamage.d

    Hello people,
    I wanted to have a relatively easy way to add new damage types and damage calculation functions to the game, and this is my first attempt.
    I tried to polish it as much as possible, I took inspiration for the "API" from LeGo (amazing thing).
    The whole feature uses hash tables from LeGo to store PermMem handles for items that have new damage types assigned. (a InstanceID and PermMem handle pair)


    It is still work in progress I want to add functionality for spells as well + dynamic item instances for sharpening/enchantment type of effects.

    It is going to be a part of the next version of AFSP.




    What it does:
    This script allows you to define your own damage types and define your own damage calculation functions for said damage types.
    You can also overwrite damage calculation for built-in gothic damage (divided by fists/mosnters, melee and ranged).
    Automatic on_equip and on_unequip function for new damage types.


    What it doesn't do (yet):
    doesn't print(log) nice debug messages on hit
    doesn't handle spells
    doesn't have dynamic instances

    You also need some class definitions (github link) and it's G1 counterpart (github link)

    Spoiler:(zum lesen bitte Text markieren)
    Code:
    /* AF_EnhancedDamage*
    * Authors: Auronen & Fawkes
    * Version: 0.01 first draft
    *
    * Description:
    *         This script allows you to define your own damage types and define
    *         your own damage calculation functions for said damage types.
    *        You can also overwrite damage calculation for built-in gothic damage.
    *        Automatic on_equip and on_unequip function for new damage types.
    * 
    * How to use:
    *    1. First define your damage types indices as such:
    *        User defined constants:
    *            AFED_Innos    = 0;
    *            AFED_Beliar    = 1;
    *            AFED_Adanos    = 2;
    *            AFED_Demon    = 3;
    *            AFED_Max    = 4;
    * 
    *  ==== in AF_EnhancedDamage_InitWeapons() ====
    * 
    *    2. If desired, register your custom damage calculation functions 
    *            
    *            AFED_SetFunc(AFED_Innos, calcInnosDmg);
    *    3. Define your custom damage and armour values for items
    *            AFED_SetDamage_Item (ItMw_2H_Blessed_03, AFED_Innos , 35);            
    *            AFED_SetArmour_Item (ITAR_PAL_H        , AFED_Beliar, 45);
    *            AFED_SetArmour_Item (ITAR_PAL_H        , AFED_Demon , 35);
    *    4. Assign attack damage value and type for monsters    in their respective instance definitions        
    *            INSTANCE Molerat    (Mst_Default_Molerat)
    *            {
    *                B_SetVisuals_Molerat();
    *                Npc_SetToFistMode(self);
    *                CreateInvItems (self, ItFoMuttonRaw, 1);
    *
    *                // add damage and armour values here 
    *                AFED_SetArmour_Npc (self, AFED_Holy, 9);
    *                AFED_SetDamage_Npc (self, AFED_Holy, 5);
    *            };
    *            
    *            
    *    "API":
    *        Damage calculation functions:
    *            There are two types of damage calculation functions you can define
    *                * Vanilla Gothic damage calculation functions
    *                    - fists/monsters
    *                    - melee
    *                     - ranged
    *                * Damage calculation functions for damage types defined by you
    *                    - comes with predefined damage calculation function with this formula
    *                      (all have minimal damage of 5 points)
    *                        fists/monsters -> damage - resistance
    *                      melee          -> weapon damage - resistance
    *                      ranged         -> weapon damage + arrow damage - resistance
    *
    *        Registering new damage functions
    *            If you wish to register your custom damage calculation function (with the correct signature!) you can use this function
    *                    
    *                Overwriting base game damage calculation functions:
    *
    *                    AFED_OverwriteVanillaDamageFucn (var int weaponType, var func fnc)
    *                         weaponType - choose from these three constants 
    *                                    AFED_Fists
    *                                    AFED_Melee
    *                                    AFED_Ranged
    *                        fnc - function with the correct signature
    *                            int calcVanilla(var C_ITEM weapon, var C_NPC attacker, var C_NPC victim)
    *
    *
    *                Registering your damage calculation functions for newly defined damage types:
    *
    *                    AFED_SetFunc (var int damageType, var func fnc)
    *                        damageType - damage type defined by you (see "User defined constants")
    *                        
    *                        fnc - function with the correct signature
    *                            int calcAFED (var C_ITEM weapon, var C_NPC attacker, var C_NPC victim, var int damageType, var c_item arrow)
    *
    *        Assigning damage/armour values
    *            AFED_SetDamage_Item (var C_ITEM npc, var int damageType, var int value)
    *            AFED_SetArmour_Item (var C_ITEM item, var int damageType, var int value)
    *            
    *            AFED_SetDamage_Npc  (var C_NPC npc, var int damageType, var int value)
    *            AFED_SetArmour_Npc  (var C_NPC npc, var int damageType, var int value)
    *    
    *        Retrieving assigned values
    *            AFED_GetDamage_Item (var C_ITEM item, var int damageType)
    *            AFED_GetArmour_Item (var C_ITEM item, var int damageType)
    *    
    *            AFED_GetDamage_Npc  (var C_NPC npc, var int damageType)
    *            AFED_GetArmour_Npc  (var C_NPC npc, var int damageType)
    *
    *
    *
    *
    * =======================================================================
    * ======================== debug print functions ========================
    * =======================================================================
    *
    *        These are functions that print out damage/armour with zSpy as output,
    *        names should be self-explanatory :)
    *
    *            AFED_PrintArmour (var c_npc instance)        
    *            AFED_PrintDamage (var c_npc instance)
    *          AFED_PrintMelee  (var c_npc instance)
    *          AFED_PrintRanged (var c_npc instance)
    * 
    * =================================================================
    * ======================== extra functions ========================
    * =================================================================
    *
    *    Functions that I made during the development of this "feature"
    *        int NPC_InsertAllItems(var c_npc npc_inst)
    *            Inserts all items defined in the symbol table into inventory of npc_inst
    *            return: number of items inserted
    *
    *        int Hlp_IsEmptyInstance(var int symbolID)
    *            Checks if instance is empty instance -> created by MEM_NullToInst
    *            return: TRUE or FALSE
    *
    */
     
     
    /* NOTES:
    *     I cannot wrap my head around the symbol table and instances and PermMem to save 
    *    newly created symbols of item instances :( - Auronen
    * 
    * 
    */
    
    /* TODO:
    *     [x] Damage calculation functions for vanilla damage
    *    [] Handle spells and allow to add new damage types to spells
    *     [] Dynamic instances for sharpening/enchantment type of effects
    *    
    */
    
    
    
    func int Hlp_IsEmptyInstance(var int symbolID)
    {
        if (symbolID == 0) 
        { 
            MEM_Info("Symbol not found!");
            return -1;
        };
        return !(MEM_ReadInt(MEM_GetSymbolByIndex(symbolID) + zCParSymbol_content_offset) == MEM_ReadInt(MEM_GetSymbolByIndex(symbolID) + zCParSymbol_offset_offset ) == 0);
    };
    
    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;
    };
    
    
    
    //========================================
    // [User defined] Damage types
    //========================================
    
    const int AFED_Holy = 0;
    const int AFED_Pure = 1;
    
    // !Do not change the name, just the value!
    const int AFED_Max  = 2;
    
    
    //========================================
    // Constants
    //========================================
    
    const int oSDamageDescriptor_attackerNpc     = 8;
    const int oSDamageDescriptor_itemWeapon     = 20;
    const int oSDamageDescriptor_spellID        = 24;
    
    /* if it is the same, we don't have to switch :)
    const int oSDamageDescriptor_attackerNpc_G2 = 8;
    const int oSDamageDescriptor_itemWeapon_G2     = 20;
    const int oSDamageDescriptor_spellID_G2     = 24;
    */
    const int AFED_CalcFuncArray         = 0; // array - holds damage calculation functions
    const int AFED_CalcFuncArrayVanilla    = 0; // array - holds vanilla damage calculation functions
    const int AFED_HT                     = 0; // hash table - holds PermMem handles for AFDamage objects
    const int AFED_LST_Ammunition         = 0; // list - hold instaceIDs of ammunition
    
    const int AFED_Fists     = 0;
    const int AFED_Melee     = 1;
    const int AFED_Ranged      = 2;
    
    
    //========================================
    // class, holds two array handles
    //========================================
    class AFDamage
    {
        var int damageHndl;
        var int armourHndl;
    };
    
    instance AFDamage@(AFDamage);
    
    /*
    
    I don't think I need this (eve though it would probably save some load time)
    The init function reconstructs the damage objects based on the AF_EnhancedDamage_InitWeapons() function
    To "renew" registered damage to monsters that are already on the map, I should 
    insert and then delete every monster on the TOT waypoint, to run their instance "functions"
    to insert their defined damage objects into the HT
    
    //========================================
    // Archiver function
    //========================================
    func void AFDamage_Archiver(var AFDamage this)
    {
        PM_SaveInt ("damageHndl"    , this.damageHndl    );
        PM_SaveInt ("armourHndl"    , this.armourHndl    );
    };
    
    //========================================
    // Unarchiver function
    //========================================
    func void AFDamage_Unarchiver(var AFDamage this)
    {
        if (PM_Exists("damageHndl"    ))        { this.damageHndl     = PM_Load        ("damageHndl"    ); };
        if (PM_Exists("armourHndl"    ))        { this.armourHndl    = PM_Load        ("armourHndl"    ); };
    };
    */
    
    //========================================
    // Creates array to hold handles of damage objects
    //========================================
    func int _AFED_CreateArray () 
    {
        var int array; array = new(zCArray@);
        var zCArray arr; arr = get(array);
    
        arr.array = MEM_Alloc(AFED_Max*4); 
        arr.numInArray = AFED_Max;
        arr.numAlloc = AFED_Max;
        
        return array;
    };
    
    //========================================
    // Registers damage calculation function
    //========================================
    func void AFED_SetFunc (var int damageType, var func fnc) 
    {
        if (damageType >= AFED_Max)
        {
            MEM_Warn("Trying to set a function for a non valid damage type.");
            return;
        };
        var int fncPtr; fncPtr = MEM_GetFuncPtr(fnc);
        MEM_ArrayWrite(getPtr(AFED_CalcFuncArray), damageType, fncPtr);
    };
    
    //========================================
    // Overrides vanilla damage calculation function
    //========================================
    func void AFED_OverwriteVanillaDamageFucn (var int weaponType, var func fnc)
    {
        if (weaponType > 3 || weaponType < 0)
        {
            MEM_Warn("Vanilla functions overwrite out of bounds. Use 0 - fists/monsters, 1 - melee, 2 - ranged");
        };
        
        var int fncPtr; 
        fncPtr = MEM_GetFuncPtr(fnc);
        MEM_ArrayWrite(getPtr(AFED_CalcFuncArrayVanilla), weaponType, fncPtr);
        
    };
    
    //========================================
    // Inits the array for damage calculation functions
    //========================================
    func void AFED_InitDamCalcArr()
    {
        // create array
        AFED_CalcFuncArray = _AFED_CreateArray();
        
        // fill array with default damage calc functions
        var int fncPtr; fncPtr = MEM_GetFuncPtr(calcDefault);
        repeat(i, AFED_Max); var int i;
            MEM_ArrayWrite(getPtr(AFED_CalcFuncArray), i, fncPtr);
        end;
    };
    
    //========================================
    // Inits the array for vanilla damage calculation functions
    //========================================
    func void AFED_InitVanillaDamCalcArr()
    {
        var int array; array = new(zCArray@);
        var zCArray arr; arr = get(array);
    
        arr.array = MEM_Alloc(3*4); 
        arr.numInArray = 3;
        arr.numAlloc = 3;
        
        AFED_CalcFuncArrayVanilla = array;
    };
    
    //========================================
    // Calculates damage using 
    // the registered functions
    //========================================
    func int AFED_CalculateDamage(var c_item weaponSymb, var c_npc attackerSymb, var c_npc victimSymb, var c_item arrowSymb)
    {
        var int dmg; dmg = 0;
        var int funcPtr;
        repeat(i, AFED_Max); var int i;
            funcPtr = MEM_ArrayRead(getPtr(AFED_CalcFuncArray), i);
            if (funcPtr)
            {
                MEM_PushInstParam(weaponSymb     );  //    oCItem    - weapon doing damage 
                MEM_PushInstParam(attackerSymb   );  //    oCNpc   - attacker NPC
                MEM_PushInstParam(victimSymb     );  //    oCNpc   - victim NPC
                MEM_PushIntParam (i                 );     //    int     - damage type
                MEM_PushInstParam(arrowSymb          );  //    oCItem  - arrow
                MEM_CallByPtr(funcPtr);
                dmg += MEM_PopIntResult();
            } 
            else 
            { 
                MEM_Info("Ptr not found!");
            };
        end;
        
        return dmg;
    };
    
    //========================================
    // Calculates damage using 
    // the registered functions
    //========================================
    func int AFED_CalculateVanillaDamage(var c_item weaponSymb, var c_npc attackerSymb, var c_npc victimSymb, var int weaponType)
    {
        var int dmg; dmg = -1;
        var int funcPtr;
        funcPtr = MEM_ArrayRead(getPtr(AFED_CalcFuncArrayVanilla), weaponType); //    weaponType     - fists/melee/ranged
        if (funcPtr)
        {
            MEM_PushInstParam(weaponSymb     );  //    oCItem        - weapon doing damage 
            MEM_PushInstParam(attackerSymb   );  //    oCNpc       - attacker NPC
            MEM_PushInstParam(victimSymb     );  //    oCNpc       - victim NPC
            MEM_CallByPtr(funcPtr);
            dmg = MEM_PopIntResult();
        };
        
        return dmg;
    };
    
    //========================================
    // Creates instance, return PermMem handle
    //========================================
    func int AFED_Create () 
    {
        var int damHndl; damHndl = new(AFDamage@);
        var AFDamage objDam; objDam = get(damHndl);
        
        objDam.damageHndl = _AFED_CreateArray();
        objDam.armourHndl = _AFED_CreateArray();
    
        return damHndl;
    };
    
    //========================================
    // Intern - set value
    //========================================
    func void _AFED_SetValue (var int instID, var int damageType, var int value, var int damage)
    {    
        // if itemID - oCItem or oCNpc - is not in hash table, create damage object
        // for this specific instanceID
        if (!_HT_Has(getPtr(AFED_HT), instID))
        {
            HT_Insert(AFED_HT, AFED_Create(), instID);
        };
        
        var AFDamage damObj; damObj = get(HT_Get(AFED_HT, instID));
    
        if (!damage)
        {
            MEM_ArrayWrite(getPtr(damObj.damageHndl), damageType, value);
            return;
        }
        else 
        {
            MEM_ArrayWrite(getPtr(damObj.armourHndl), damageType, value);
            return;
        };
    };
        
    //========================================
    // Sets damage types and their values
    //========================================
    func void AFED_SetDamage_Item (var C_ITEM item, var int damageType, var int value)
    {
        var int instID; instID = Hlp_GetInstanceID(item);
        _AFED_SetValue (instID, damageType, value, 0);
    };
    
    func void AFED_SetDamage_Npc (var C_NPC npc, var int damageType, var int value)
    {
        var int instID; instID = Hlp_GetInstanceID(npc);
        _AFED_SetValue (instID, damageType, value, 0);
    };
    
    //========================================
    // Sets armour against damage type and its value 
    //========================================
    func void AFED_SetArmour_Item (var C_ITEM item, var int damageType, var int value)
    {
        var int instID; instID = Hlp_GetInstanceID(item);
        _AFED_SetValue (instID, damageType, value, 1);
    };
    
    func void AFED_SetArmour_Npc (var C_NPC npc, var int damageType, var int value)
    {
        var int instID; instID = Hlp_GetInstanceID(npc);
        _AFED_SetValue (instID, damageType, value, 1);
    };
    
    //========================================
    // Intern - get value
    //========================================
    func int _AFED_GetValue (var int symbIndex, var int damageType, var int damage)
    {
        // if the handle is assigned (the array already exists) 
        if (!_HT_Has(getPtr(AFED_HT), symbIndex))
        {
            // return 0, in order to continue the calculations
            return 0;
        };
        
        var AFDamage damObj; damObj = get(HT_Get(AFED_HT, symbIndex));
    
        if (!damage)
        {
            return MEM_ArrayRead(getPtr(damObj.damageHndl), damageType);
        }
        else 
        {
            return MEM_ArrayRead(getPtr(damObj.armourHndl), damageType);
        };
    };    
    
    //========================================
    // Gets a value of the damage type
    //========================================
    func int AFED_GetDamage_Item (var C_ITEM item, var int damageType)
    {
        var int instID; instID = Hlp_GetInstanceID(item);
        return _AFED_GetValue (instID, damageType, 0);
    };
    
    func int AFED_GetDamage_Npc (var C_NPC npc, var int damageType)
    {
        var int instID; instID = Hlp_GetInstanceID(npc);
        return _AFED_GetValue (instID, damageType, 0);
    };
    
    //========================================
    // Gets armour against specified damage type 
    //========================================
    func int AFED_GetArmour_Item (var C_ITEM item, var int damageType)
    {
        var int instID; instID = Hlp_GetInstanceID(item);
        return _AFED_GetValue (instID, damageType, 1);
    };
    
    func int AFED_GetArmour_Npc (var C_NPC npc, var int damageType)
    {
        var int instID; instID = Hlp_GetInstanceID(npc);
        return _AFED_GetValue (instID, damageType, 1);
    };
    
    //========================================
    // [HOOK] onDamage_Event 
    //========================================
    func void _hook_OnDmg_Event () 
    {
        MEM_Info("=====================================_hook_OnDmg_Event=====================================");
        var int victimPtr;   // oCNpc*         - for reading resistance values/buffs/debuffs
        var int attackerPtr; // oCNpc*       - for reading perks/buffs/debuffs
        var int weaponPtr;   // oCItem*      - for reading damage types + dynamic things (in the future - possibly with the help of DII?)
        var int ddPtr;       // oSDamageDescriptor*
        var int dmg;         // original damage
    
        var c_npc attacker;
        var c_npc   victim;
        var c_item  weapon;
        var c_item   arrow;
        
        if (MEMINT_SwitchG1G2 (TRUE, FALSE)) //G1 & G2A both have different parameters
        {
            victimPtr = EDI;
            ddPtr = MEM_ReadInt (ESP + 548);
            dmg = EAX;
        } 
        else //G2A not verified!! It works :smirk:
        {
            victimPtr = EBP;
            ddPtr = MEM_ReadInt (ESP + 644);
            dmg = EDI;
        };
    
        MEM_Info(ConcatStrings("Vanilla damage: ", IntToString(dmg)));
        
        weaponPtr   = MEM_ReadInt(ddPtr + oSDamageDescriptor_itemWeapon);
    // =============== Burn ticks ===============    
        if (!MEM_ReadInt(ddPtr + oSDamageDescriptor_attackerNpc))
        {
            // for burning damage ticks?? - they do nothing in G2A
            return;
        };
    
        attacker     = _^(MEM_ReadInt(ddPtr + oSDamageDescriptor_attackerNpc));
        victim       = _^(victimPtr);
    
    // =============== Magic ===============        
        /*
        * NOTE:
        *    Magic is not yet implemented, so we just skip it :)
        */
        var int spellid; spellid = MEM_ReadInt(ddPtr + oSDamageDescriptor_spellID);
        // PV("Spell ID", spellid);
        MEM_Info(ConcatStrings("Spell ID : ", IntToString(spellid)));
        if (spellid >= 1) // ID 0 spell is SPL_PalLight (G2) and Light spell (G1) - they don't have a target :)
        {
            // PVs("Spell name", MEM_ReadStringArray(_@s(TXT_SPELLS), spellid));
            MEM_Info(ConcatStrings("Spell name : ", MEM_ReadStringArray(_@s(TXT_SPELLS), spellid)));
            // PV ("Magic damage", dmg);
            MEM_Info(ConcatStrings("Magic damage : ", IntToString(dmg)));
            return;
        };
        
        
    // =============== No weapon ===============
        // weapon pointer is zero if the attacker is attacking without a weapon - animals (=) fists
        if (!weaponPtr && spellid <= 0) 
        {
            MEM_Info("=============== No weapon ===============");
            weapon = MEM_NullToInst();
            arrow  = MEM_NullToInst();
            /* Fists and animals are based on strength
            * so we just add extra damage on top of the "raw physical" damage
            */
            
            if ( -1 != AFED_CalculateVanillaDamage(weapon, attacker, victim, AFED_Fists)) // if damage function is defined
            {
                dmg = AFED_CalculateVanillaDamage(weapon, attacker, victim, AFED_Fists);
            };
            MEM_Info(ConcatStrings("Base damage: ", IntToString(dmg)));
            // gets added to the base damage (calculated from strength)
            dmg += AFED_CalculateDamage(weapon, attacker, victim, arrow);
            MEM_Info(ConcatStrings("Total damage: ", IntToString(dmg)));
            if (MEMINT_SwitchG1G2 (TRUE, FALSE)) 
            {
                EAX = dmg;
                return;
            } 
            else 
            {
                EDI = dmg;
                return;
            };
        }
        else
        {
            weapon = _^(weaponPtr);
        };
    
    // =============== Ranged weapon ===============
        /*
        * ItRw_Bolt
        * ItRw_Arrow
        * and so on
        */
        if (List_Contains(AFED_LST_Ammunition, Hlp_GetInstanceID(weapon)))
        {
            MEM_Info("=======================Ranged weapon=======================");
            
            arrow = _^(_@(weapon));
            
            // getting bow/crossbow from attacker (even if he already put it away)
            if (NPC_IsInFightMode(attacker, FMODE_FAR)) 
            {
                weapon = NPC_GetReadiedWeapon (attacker);
            } 
            else if (NPC_HasEquippedRangedWeapon (attacker)) 
            {
                weapon = NPC_GetEquippedRangedWeapon (attacker);
            };
    
            if ( -1 != AFED_CalculateVanillaDamage(weapon, attacker, victim, AFED_Ranged)) // if damage func is defined
            {
                dmg = AFED_CalculateVanillaDamage(weapon, attacker, victim, AFED_Ranged);
            };
            MEM_Info(ConcatStrings("Base damage: ", IntToString(dmg)));
            if (MEMINT_SwitchG1G2 (TRUE, FALSE)) 
            {
                EAX = dmg + AFED_CalculateDamage(weapon, attacker, victim, arrow);
                MEM_Info(ConcatStrings("Total damage: ", IntToString(EAX)));
                return;
            } 
            else 
            {
                EDI = dmg + AFED_CalculateDamage(weapon, attacker, victim, arrow);
                MEM_Info(ConcatStrings("Total damage: ", IntToString(EDI)));
                return;
            };
            
        };
        
    // =============== Melee weapon ===============
        arrow = MEM_NullToInst();
        MEM_Info("=============== Melee weapon ===============");    
        
        if ( -1 != AFED_CalculateVanillaDamage(weapon, attacker, victim, AFED_Melee)) // if damage func is defined
        {
            dmg = AFED_CalculateVanillaDamage(weapon, attacker, victim, AFED_Melee);
        };
        MEM_Info(ConcatStrings("Base damage: ", IntToString(dmg)));
        
        if (MEMINT_SwitchG1G2 (TRUE, FALSE)) 
        {
            EAX = dmg + AFED_CalculateDamage(weapon, attacker, victim, arrow);
            MEM_Info(ConcatStrings("Total damage: ", IntToString(EAX)));
            return;
        } 
        else 
        {
            EDI = dmg + AFED_CalculateDamage(weapon, attacker, victim, arrow);
            MEM_Info(ConcatStrings("Total damage: ", IntToString(EDI)));
            return;
        };
    };
    
    
    //========================================
    // [TEMPLATE] Default damage calculation function 
    //========================================
    func int calcDefault (var C_ITEM weaponSymb, var C_NPC attackerSymb, var C_NPC victimSymb, var int damageType, var c_item arrowSymb)
    {    
        var int dmg;
        var int resistance;
        
        if (Hlp_IsEmptyInstance(weaponSymb)) // if weapon is empty instance - calculate damage from attacker (fists and animals)
        {
            dmg = AFED_GetDamage_Npc(attackerSymb, damageType);    
            resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        }
        else if (Hlp_IsEmptyInstance(arrowSymb)) // if arrow is empty -> it's melee weapon
        {
            dmg = AFED_GetDamage_Item(weaponSymb, damageType);    
            resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        }
        else // if weapon and arrow are valid instances -> it's ranged weapon 
        {
            dmg =  AFED_GetDamage_Item(weaponSymb, damageType);
            dmg += AFED_GetDamage_Item(arrowSymb, damageType);
            resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        };
        
        if (dmg - resistance > 5)
        {
            return dmg - resistance;
        }
        else
        {
            return 5;
        };
    };
    
    //========================================
    // Finds all instances with ITEM_KAT_MUN
    // flag and puts their instanceIDs into a list
    //========================================
    func void AFED_Ammo(var int npc)
    {
        var oCNpc npc_; npc_ = _^(npc);
        var zCListSort list;
        var oCItem item;
        
        list = _^(npc_.inventory2_oCItemContainer_contents);
        
        while (list.next != 0);
            list = _^(list.next);
            item = _^(list.data);
            
            if (item.mainflag == ITEM_KAT_MUN)
            {
                if (!AFED_LST_Ammunition)
                {
                    AFED_LST_Ammunition = List_Create(Hlp_GetInstanceID(item));
                }
                else
                {
                    List_Add(AFED_LST_Ammunition, Hlp_GetInstanceID(item));
                };
            };
        end;
    };
    
    //========================================
    // [HOOK] On equip item add the resistances
    // to the AFED_Damage object
    //========================================
    func void _hook_oCNpc_EquipItem ()
    {    
        // 0068F940  .text     Debug data           ?EquipItem@oCNpc@@QAEXPAVoCItem@@@Z
        // 0x007323C0 public: void __thiscall oCNpc::EquipItem(class oCItem *)
        // MEM_Info("===== _hook_oCNpc_EquipItem =====");
        
        var int itemPtr; itemPtr = MEM_ReadInt (ESP + 4);
        var int instID_Item;
        var int instID_Npc;
        
        var C_NPC npc; npc = _^(ECX);
        instID_Npc = Hlp_GetInstanceID(npc);
        
        if (itemPtr)
        {
            var C_ITEM itm; itm = _^(itemPtr);
            instID_Item = Hlp_GetInstanceID(itm);
            
            // if npc is not yet registered in the hash table
            if (!_HT_Has(getPtr(AFED_HT), instID_Npc))
            {
                HT_Insert(AFED_HT, AFED_Create(), instID_Npc);
            };
            
            repeat(i, AFED_Max); var int i;
                AFED_SetArmour_Npc(npc, i, AFED_GetArmour_Npc(npc, i) + AFED_GetArmour_Item(itm, i) );
            end;    
        };
    };
    
    //========================================
    // [HOOK] On unequip item subtract the resistances
    // to the AFED_Damage object
    //========================================
    func void _hook_oCNpc_UnequipItem ()
    {    
        // 0068FBC0  .text     Debug data           ?UnequipItem@oCNpc@@QAEXPAVoCItem@@@Z
        // 0x007326C0 public: void __thiscall oCNPC::UnequipItem(class oCItem *)
        // MEM_Info("===== _hook_oCNpc_UnequipItem =====");
        
        var int itemPtr; itemPtr = MEM_ReadInt (ESP + 4);
        var int instID_Item;
        var int instID_Npc;
        
        var C_NPC npc; npc = _^(ECX);
    
        instID_Npc = Hlp_GetInstanceID(npc);
    
        if (itemPtr)
        {
            var C_ITEM itm; itm = _^(itemPtr);
            instID_Item = Hlp_GetInstanceID(itm);
            
            // if npc is not yet registered in the hash table
            if (!_HT_Has(getPtr(AFED_HT), instID_Npc))
            {
                HT_Insert(AFED_HT, AFED_Create(), instID_Npc);
            };
            
            repeat(i, AFED_Max); var int i;
                AFED_SetArmour_Npc(npc, i, AFED_GetArmour_Npc(npc, i) - AFED_GetArmour_Item(itm, i) );
            end;    
        };
    };
    
    //========================================
    // [User defined] Here you define:
    //     * damage calculation functions
    //     * overwrite vanilla damage calculation functions
    //     * damage/armour for items
    //========================================
    func void AF_EnhancedDamage_InitWeapons()
    {
        MEM_Info("=====================================AF_EnhancedDamage_InitWeapons=====================================");
        
        MEM_Info("Registering damage calculation functions.");
        AFED_SetFunc(AFED_Holy, calcHoly);
        AFED_SetFunc(AFED_Pure, calcPure);
        
        MEM_Info("Setting damage.");
        AFED_SetDamage_Item(ItMw_1h_Vlk_Dagger, AFED_Holy, 10);
        AFED_SetDamage_Item(ItMw_1h_Vlk_Dagger, AFED_Pure, 20);
        
        AFED_SetArmour_Item(ItSh_RoundShield, AFED_Holy, 15);
        AFED_SetArmour_Item(ItSh_RoundShield, AFED_Pure, 25);
        
        AFED_SetDamage_Item(ItRw_Bow_L_01, AFED_Holy, 9);
        AFED_SetDamage_Item(ItRw_Bow_L_01, AFED_Pure, 22);
        
        // These ore G1 items :)
        // AFED_SetDamage_Item(ItMw_1H_Sword_Short_01, AFED_Holy, 10);
        // AFED_SetDamage_Item(ItMw_1H_Sword_Short_01, AFED_Pure, 20);
        
        // AFED_SetArmour_Item(VLK_ARMOR_L, AFED_Holy, 15);
        // AFED_SetArmour_Item(VLK_ARMOR_L, AFED_Pure, 25);
        
        // AFED_SetDamage_Item(ItRw_Bow_Small_01, AFED_Holy, 9);
        // AFED_SetDamage_Item(ItRw_Bow_Small_01, AFED_Pure, 22);
    };
    
    //========================================
    // Initialisation function for AF_EnhancedDamage
    //========================================
    func void AF_EnhancedDamage_Init ()
    {
        MEM_Info("=====================================G12_OnDmgEvent_Init=====================================");
        //Addresses taken from Lehona, original post:
        //https://forum.worldofplayers.de/forum/threads/1434640-Erh%C3%B6hter-Schaden-f%C3%BCr-Untote-durch-Nahkampfwaffen?p=24356119&viewfull=1#post24356119
        const int once = 0;
        if (!once) 
        {
            // Insert all items to ItemHelper's inventory
            // Items need to exist to have InstanceID :)
            GetItemHelper();
            NPC_InsertAllItems(ITEM_HELPER_INST);
            
            // Create a list that holds the ammunition instanceIDs
            // ItRw_Bolt, ItRw_Arrow, etc.
            AFED_Ammo(_@(ITEM_HELPER_INST));
            
            /* This was previous version - not as clean :)
            // Creates new AIVar - to hold handle of the AFDamage object for NPCs
            AFED_AIVAR_Hndl = AF_AIVAR_CreateAIVar(); */
            
            // Create hash table to hold handles for items and NPCs (instanceIDs are used as keys)
            AFED_HT = HT_Create();
    
            // Initiates the array for damage calculation functions
            AFED_InitDamCalcArr();
            
            // Initiates the array for vanilla damage calculation functions
            AFED_InitVanillaDamCalcArr();
            
            // Engine hooks
            HookEngine(MEMINT_SwitchG1G2(7567329, 6736583), MEMINT_SwitchG1G2(6, 5), "_hook_OnDmg_Event");
            // HookEngine(7798240, 6, "_hook_oCObjectFactory_CreateItem");
            HookEngine(MEMINT_SwitchG1G2(6879552, 7545792), MEMINT_SwitchG1G2(7, 7), "_hook_oCNpc_EquipItem");
            HookEngine(MEMINT_SwitchG1G2(6880192, 7546560), MEMINT_SwitchG1G2(7, 6), "_hook_oCNpc_UnequipItem");
            // I hook this function, and then use FF to run a function that refreshes items in the status screen only once :)
            // HookEngine(MEMINT_SwitchG1G2(4683824, 4713824), MEMINT_SwitchG1G2(7, 7), "_hook_oCStatusScreen_Show");
            once = 1;
        };
        
        // Initiates all assigned weapon damage/armour
        AF_EnhancedDamage_InitWeapons();
    };
    
    
    //========================================
    // Default damage calc functions, that can be changed by the user
    // source: https://forum.worldofplayers.de/forum/threads/127320-Damage-System?p=2198181&viewfull=1#post2198181
    //========================================
    func int calcFistDefault(var C_ITEM weaponSymb, var C_NPC attackerSymb, var C_NPC victimSymb) 
    {    // total damage = attacker strength - victim protection
        return attackerSymb.attribute[ATR_STRENGTH] - MEM_ReadIntArray(_@(victimSymb.protection), attackerSymb.damagetype);
    };
    
    func int calcMeleeDefault(var C_ITEM weaponSymb, var C_NPC attackerSymb, var C_NPC victimSymb) 
    {
        // MEM_Info("============== calcMeleeDefault ==============");
        var int rndm; rndm = r_MinMax(1, 100);
        var int critical;
        var int dmgType; dmgType = weaponSymb.damagetype;
        var int totalDmg;
        if (weaponSymb.flags & ITEM_SWD || weaponSymb.flags & ITEM_AXE)
        {
            if (Npc_GetTalentValue(attackerSymb, NPC_TALENT_1H) >= rndm)
            {
                critical = TRUE;
            };
        }
        else if (weaponSymb.flags & ITEM_2HD_SWD || weaponSymb.flags & ITEM_2HD_AXE)
        {
            if (Npc_GetTalentValue(attackerSymb, NPC_TALENT_2H) >= rndm)
            {
                critical = TRUE;
            };
        };
        if (!critical)
        { // Normal  : (weapon damage + strength - enemy armour protection - 1) / 10 = total damage, if (total damage < 5) then total damage = 5
            totalDmg = (weaponSymb.damageTotal + attackerSymb.attribute[ATR_STRENGTH] - MEM_ReadIntArray(_@(victimSymb.protection), dmgType) - 1) / 10;
            if (totalDmg < 5) 
            { 
                return 5;
            };
            return totalDmg;
        }
        else
        { // Critical: weapon damage + strength - enemy armour protection = total damage, if (total damage < 5) then total damage = 5
            totalDmg = (weaponSymb.damageTotal + attackerSymb.attribute[ATR_STRENGTH] - MEM_ReadIntArray(_@(victimSymb.protection), dmgType) );
            if (totalDmg < 5) 
            { 
                return 5;
            };
            return totalDmg;
        };
    };
    
    func int calcRangedDefault(var C_ITEM weaponSymb, var C_NPC attackerSymb, var C_NPC victimSymb) 
    {    // total damage = weapon damage + attacker dexterity - victim armour protection 
        return weaponSymb.damageTotal + attackerSymb.attribute[ATR_STRENGTH] - MEM_ReadIntArray(_@(victimSymb.protection), weaponSymb.damagetype);
    };
    
    
    //========================================
    // Debug print - prints NPCs armour values
    // into zSpy in order of definition
    //========================================
    func void AFED_PrintArmour(var c_npc inst)
    {
        repeat(i, AFED_Max); var int i;
            MEM_Info(IntToString(AFED_GetArmour_Npc(inst, i)));
        end;    
    };
    
    func void AFED_PrintDamage(var c_npc inst)
    {
        repeat(i, AFED_Max); var int i;
            MEM_Info(IntToString(AFED_GetDamage_Npc(inst, i)));
        end;    
    };
    
    func string AFED_PrintMelee(var c_npc inst)
    {
        var c_item itm; 
        
        if ( NPC_IsInFightMode(inst, FMODE_MELEE) ) 
        {
            itm = NPC_GetReadiedWeapon (inst);
        } 
        else if ( NPC_HasEquippedMeleeWeapon(inst) ) 
        {
            itm = NPC_GetEquippedMeleeWeapon (inst);
        } 
        else
        {
            return "Defined NPC does not have melee weapon equipped or in hand.";
        };
        
        repeat(i, AFED_Max); var int i;
            MEM_Info(IntToString(AFED_GetDamage_Item(itm, i)));
        end;
        return "Printed to zSpy";
    };
    
    func string AFED_PrintRanged(var c_npc inst)
    {
        var c_item itm; 
        
        if (NPC_IsInFightMode(inst, FMODE_FAR)) 
        {
            itm = NPC_GetReadiedWeapon (inst);
        } 
        else if (NPC_HasEquippedRangedWeapon (inst)) 
        {
            itm = NPC_GetEquippedRangedWeapon (inst);
        } 
        else
        {
            return "Defined NPC does not have melee weapon equipped or in hand.";
        };
        
        repeat(i, AFED_Max); var int i;
            MEM_Info(IntToString(AFED_GetDamage_Item(itm, i)));
        end;
        return "Printed to zSpy";
    };
    
    //========================================
    // DEMO
    //========================================
    
    func int calcHoly (var C_ITEM weaponSymb, var C_NPC attackerSymb, var C_NPC victimSymb, var int damageType, var c_item arrowSymb)
    {    
        var int dmg;
        var int resistance;
        
        if (Hlp_IsEmptyInstance(weaponSymb)) // if weapon is empty instance - calculate damage from attacker (fists and animals)
        {
            dmg = AFED_GetDamage_Npc(attackerSymb, damageType);    
            resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        }
        else if (Hlp_IsEmptyInstance(arrowSymb)) // if arrow is empty -> its melee weapon
        {
            dmg = AFED_GetDamage_Item(weaponSymb, damageType);    
            resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        }
        else
        {
            dmg =  AFED_GetDamage_Item(weaponSymb, damageType);
            dmg += AFED_GetDamage_Item(arrowSymb, damageType);
            resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        };
    
        if (dmg - resistance > 0)
        {
            MEM_Info(ConcatStrings("Holy damage: ", IntToString(dmg - resistance)));
            return dmg - resistance;
        }
        else
        {
            MEM_Info(ConcatStrings("Holy damage: ", IntToString(0)));
            return 0;
        };
    };
    
    func int calcPure (var C_ITEM weaponSymb, var C_NPC attackerSymb, var C_NPC victimSymb, var int damageType, var c_item arrowSymb)
    {
        // pure damage ignores resistances :)
        var int dmg;
        var int resistance;
        
        if (Hlp_IsEmptyInstance(weaponSymb)) // if weapon is empty instance - calc damage from attacker (fists and animals)
        {
            dmg = AFED_GetDamage_Npc(attackerSymb, damageType);    
            // resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        }
        else if (Hlp_IsEmptyInstance(arrowSymb)) // if arrow is empty -> its melee weapon
        {
            dmg = AFED_GetDamage_Item(weaponSymb, damageType);    
            // resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        }
        else
        {
            dmg =  AFED_GetDamage_Item(weaponSymb, damageType);
            dmg += AFED_GetDamage_Item(arrowSymb, damageType);
            // resistance = AFED_GetArmour_Npc(victimSymb, damageType);
        };
        MEM_Info(ConcatStrings("Pure damage: ", IntToString(dmg)));
        return dmg;
    };


    Please let me know if there are any problems and what you think. How do I improve my code or how do I improve my usage of LeGo, especially PermMem (it is amazing, but I still don't understand it that much)
    Geändert von Auronen (25.07.2021 um 19:34 Uhr) Grund: Nota about buggy function

  3. Beiträge anzeigen #3 Zitieren
    Apprentice Avatar von Auronen
    Registriert seit
    Dec 2020
    Beiträge
    25
     
    Auronen ist offline

    AF_ItemPreview

    Hello guys ,
    I made another feature. I always disliked, how, when you are selecting a reward in a dialogue, you cannot see what the item is. Modders then resort to strings such as Digger's Trousers. Protection: 15, 5, 5. So I made a feature that allows you to preview the item (like you can see it in your inventory) you are going to get when you choose that dialogue option.

    Video demonstration:

    This is going to be a part of the next version of AFSP.

    Here is the code of the feature:
    Code:
    /* AF_ItemPreview*
    * Authors: Fawkes & Auronen
    * Version: 0.01 first draft
    *
    * Description:
    *     This feature allows you to preview items during dialogues, when 
    *    choosing a reward (Torrez in G1, Fisk (digger's trousers G1))
    * 
    * How to use:
    *     For "full" dialogues you have to declare a variable "var int AFIP_ShowItem"
    *     and assign a item instance to it 
    *         Example: var int AFIP_ShowItem; AFIP_ShowItem = ItMw_1h_Vlk_Dagger;
    *
    *     And that's it! 
    * 
    *     For Info_AddChoice types of dialogues it is a bit more elaborate
    *         In the same function you are declaring your Info choices you have to use
    *        AFIP_SetChoiceItem function, it takes two parameters:
    *            1. function that runs when you choose that option
    *            2. item instance
    *        In the function that runs when you select the option you declare a variable "var int AFIP_ShowItem"
    *
    *     And that's it! 
    *
    *            
    *
    *
    *
    */
     
    /* NOTES:
    * 
    * 
    * 
    */
    
    /* TODO:
    * 
    *    
    *     implement "rotate for inventory" function with user rotation?
    *  
    *     hook this to "oCItemContainer::DrawItemInfo" to have more rows for AF_EnhancedDamage.d?
    *        - this would need a more detailed API to set individual rows and other stuff
    * 
    */
     
    
    
    //========================================
    // Some wrapper functions for easier 
    // item positioning for LeGo Render.d
    //========================================
    func int Render_AddItemCenter(var int itemInst, var int x, var int y, var int w, var int h) 
    {
        return +Render_AddItemPrio(itemInst, x-(w>>1), y-(h>>1), x+((w+1)>>1), y+((h+1)>>1), 0);
    };
    
    func int Render_AddItemCenterPrio(var int itemInst, var int x, var int y, var int w, var int h, var int priority) 
    {
        return +Render_AddItemPrio(itemInst, x-(w>>1), y-(h>>1), x+((w+1)>>1), y+((h+1)>>1), priority);
    };
    
    func int Render_IsOpen(var int rndrHndl) 
    {
        var RenderItem itm; itm = get(rndrHndl);
        if (itm.view_open) {
            return TRUE;
        } else {
            return FALSE;
        };
    };
    
    //========================================
    // Constants
    //========================================
    
    /* NOTE: I got these values from the game 
    * set to 1280x720 at 1.000 scale in SP
    * This needs to be dynamic, but I do not 
    * know, how to get to the real formula
    */
    const int AFIP_view         = 0;
    const int AFIP_rndObj         = 0;
    
    // dimensions
    const int AFIP_height         = 1845;
    const int AFIP_width         = 4608;
    
    // position
    const int AFIP_x             = 1792;
    const int AFIP_y             = 6006;
    
    const int AFIP_currentItem    = 0;
    
    const string AFIP_texture    = "Inv_Back_Steal.tga";
    const string AFIP_font        = "FONT_OLD_10_WHITE.TGA";
    const string AFIP_varName    = "AFIP_SHOWITEM";
    
    
    //========================================
    // returns int value as string, returns 
    // empty string, when 0
    //========================================
    func string countValue (var int val)
    {
        if (val) { return IntToString(val); }
        else { return ""; };
    };
    
    //========================================
    // Deletes both of the views
    //========================================
    func void AFIP_Delete()
    {
        if (AFIP_rndObj) { Render_Remove(AFIP_rndObj); };
        if (AFIP_view) { View_Delete(AFIP_view); };
        AFIP_rndObj = 0;
        AFIP_view = 0;
    };
    
    //========================================
    // Creates and shows the item preview
    //========================================
    
    /* NOTES: 
        - item is passed into the function as a parser ID 
        (just the name of the item instance)
        - it fits one more row (not used here)
        - it is set up (hardcoded) to mimic the in game item preview (roughly)
    */
    
    func void AFIP_Create(var int itemIn)
    {
        AFIP_currentItem = itemIn;
        var int itemPtr; itemPtr = Itm_GetPtr(itemIn);
        var oCItem itm; itm = _^(itemPtr);
        Print_GetScreenSize();
        
        if (!AFIP_view) // not really needed, but just to be sure
        {
            AFIP_view = View_Create(1, 1, 1000, 1000);
        };
        
        View_SetTexture(AFIP_view, AFIP_texture);
        
        var string heading; heading = itm.description;
        
        var int width; width     = Print_ToVirtual(Print_GetStringWidth(heading, AFIP_font), PS_X) * PS_VMAX / AFIP_width;
        var int height; height  = 900; // this should be calculated based on the height of the view?
        
        var int head; head = PS_VMax/30;
        var int marg; marg = PS_VMax/64;
        var int marg1; marg1 = PS_VMax/32;
        
        View_AddText(AFIP_view, PS_VMax/2 - width / 2, head, heading, AFIP_font);
        var string txt;
        
        // Lines - left side (text)
        
        repeat(i, 6); var int i;
            txt = MEM_ReadStringArray(_@s(itm.text), i);
            View_AddText(AFIP_view, PS_VMax/15 - head, head + marg + (i+2)*height, txt, AFIP_font);    
        end;
    
        // Lines - right side (values)
        
        repeat(j, 6); var int j;
            txt = countValue(MEM_ReadIntArray(_@(itm.count), j));
            View_AddText(AFIP_view, PS_VMax - marg1 - 2*Print_ToVirtual(Print_GetStringWidth(txt, AFIP_font), PS_X), head + marg + (j+2)*height,txt , AFIP_font);    
        end;
        
        // Resizing and moving 
        View_Resize(AFIP_view, AFIP_width, AFIP_height);
        View_Move  (AFIP_view, AFIP_x, PS_VMax/2);
        
        if (!AFIP_rndObj) // not really needed, but just to be sure
        {
            AFIP_rndObj = Render_AddItemCenterPrio(itemIn , PS_VMax/2 + 1500, PS_VMax/2 + AFIP_height/2, AFIP_height - 400, Print_ToRatio(AFIP_height - 400, PS_Y), 1);
        };
        
        Render_AddView(AFIP_view);
    };
    
    // Taken from Ikarus_doc.d
    func void SetVarTo (var string variableName, var int value) {
        var int symPtr;
        symPtr = MEM_GetParserSymbol (variableName);
        
        if (symPtr) { //!= 0 
            var zCPar_Symbol sym;
            sym = MEM_PtrToInst (symPtr);
            
            if ((sym.bitfield & zCPar_Symbol_bitfield_type)
                                        == zPAR_TYPE_INT) {
                sym.content = value;
            } else {
                MEM_Error ("SetVarTo: Die Variable ist kein Integer!");
            };
        } else {
            MEM_Error ("SetVarTo: Das Symbol existiert nicht!");
        };
    };
    
    //========================================
    // Sets the item, for Info_AddChoice type 
    // dialogues
    //========================================
    func void AFIP_SetChoiceItem(var func action, var int item )
    {
        var int fncID; fncID = MEM_GetFuncID(action);
        
        var zCPar_Symbol symfunc; symfunc = _^(MEM_ReadIntArray (contentSymbolTableAddress, fncID));
        
        var string infoFuncName; infoFuncName = symfunc.name;
        
        // building the symbol name
        var string symbName; symbName = ConcatStrings(infoFuncName, ".");
        symbName = ConcatStrings(symbName, AFIP_varName);
        
        SetVarTo(symbName, item);
    };
    
    //========================================
    // Unused because it doesn't work, but it 
    // would be cleaner (I think :) )
    //========================================
    func void AFIP_Info_AddChoice(var int menu, var string text, var func action, var int item )
    {
        // this doesn't work for this particular technique - func action has a different symbol -> AFIP_Info_AddChoice.action
        MEM_Info("== AFIP_Info_AddChoice ==");
        /*
        var int fncID; fncID = MEM_GetFuncID(action);
        var zCPar_Symbol asd; asd = _^(MEM_ReadIntArray (contentSymbolTableAddress, fncID));
        PVs("action.name", asd.name);
        
        var zCPar_Symbol menuSymb; menuSymb = _^(MEM_ReadIntArray (contentSymbolTableAddress, menu));
        
        Info_AddChoice(menu, text, action);
    
        // zCViewDialogChoice__AddChoice( text, fncID);
        
        var zCPar_Symbol symfunc; symfunc = _^(MEM_ReadIntArray (contentSymbolTableAddress, fncID));
        
        var string infoFuncName; infoFuncName = symfunc.name;
        
        var string symbName; symbName = ConcatStrings(infoFuncName, ".");
        symbName = ConcatStrings(symbName, AFIP_varName);
        PVs("symbName", symbName);
        
        SetVarTo(symbName, item);
        */
    };
    
    //========================================
    // function by Fawkes
    // returns oCInfo * to the info that is 
    // currently highlighted in a dialogue
    //========================================
    func int InfoManager_GetSelectedInfo () 
    {
        if (InfoManager_HasFinished ()) { return 0; };
    
        var int choiceView; choiceView = MEM_InformationMan.DlgChoice;
    
        if (!choiceView) { return 0; };
    
        var zCViewDialogChoice dlg; dlg = _^ (choiceView);
    
        const int cINFO_MGR_MODE_IMPORTANT    = 0;
        const int cINFO_MGR_MODE_INFO        = 1;
        const int cINFO_MGR_MODE_CHOICE        = 2;
        const int cINFO_MGR_MODE_TRADE        = 3;
    
        var zCArray arr; arr = _^ (choiceView + 172);
    
        if (arr.array) 
        {
            if (MEM_InformationMan.Mode == cINFO_MGR_MODE_INFO) 
            {
                var C_NPC slf; slf = _^ (MEM_InformationMan.npc);
                var C_NPC her; her = _^ (MEM_InformationMan.player);
    
                return oCInfoManager_GetInfoUnimportant (slf, her, dlg.ChoiceSelected);
            } else
            //Choices - have to be extracted from oCInfo.listChoices_next
            //MEM_InformationMan.Info is oCInfo pointer
            if (MEM_InformationMan.Mode == cINFO_MGR_MODE_CHOICE) 
            {
                return MEM_InformationMan.Info;
            };
        };
        
        return 0;
    };
    
    //========================================
    // function by Fawkes
    // returns oCInfoChoice * to the info that is 
    // currently highlighted in a dialogue
    //========================================
    func int InfoManager_GetSelectedInfoChoice () {
        if (InfoManager_HasFinished ()) { return 0; };
    
        var int choiceView; choiceView = MEM_InformationMan.DlgChoice;
    
        if (!choiceView) { return 0; };
    
        var zCViewDialogChoice dlg; dlg = _^ (choiceView);
    
        const int cINFO_MGR_MODE_IMPORTANT    = 0;
        const int cINFO_MGR_MODE_INFO        = 1;
        const int cINFO_MGR_MODE_CHOICE        = 2;
        const int cINFO_MGR_MODE_TRADE        = 3;
    
        var zCArray arr; arr = _^ (choiceView + 172);
    
        if (arr.array) {
            //Choices - have to be extracted from oCInfo.listChoices_next
            //MEM_InformationMan.Info is oCInfo pointer
            if (MEM_InformationMan.Mode == cINFO_MGR_MODE_CHOICE) {
                if (MEM_InformationMan.Info) {
                    var oCInfo dlgInstance;
                    dlgInstance = _^ (MEM_InformationMan.Info);
    
                    if (dlgInstance.listChoices_next) {
    
                        var oCInfoChoice dlgChoice;
                        var int list; list = dlgInstance.listChoices_next;
                        var zCList l;
    
                        var int i; i = 0;
                        while (list);
                            l = _^ (list);
                            
                            if (l.data) {
                                if (i == dlg.ChoiceSelected) {
                                    return l.data;
                                };
                            };
                            
                            list = l.next;
                            i += 1;
                        end;
                    };
                };
            };
        };
        
        return 0;
    };
    
    //========================================
    // returns item (its symbol table ID), that 
    // is set to be shown in selected dialogue's
    // condition function or in case of oCInfoChoice
    // in its "action" function
    //========================================
    func int AFIP_HasItem()
    {
        // get selected oCInfo
        var int infoPtr; infoPtr = InfoManager_GetSelectedInfo();
        // get selected oCInfoChoice
        var int infoChoicePtr; infoChoicePtr = InfoManager_GetSelectedInfoChoice();
        if (infoPtr && !infoChoicePtr) 
        {
            // MEM_Info("Info");
            var oCInfo diaInfo; diaInfo = _^(infoPtr);
            
            // getting the name of dialogues condition function 
            /*
                Ideally this would in the info function (it would work even for Info_AddChoice dialogues) but in that 
                case the var is only initialized by Ikarus (to be 0), since the function didn't run - no value assigned
                Should I be saving oCInfo* and oCInfoChoice* in a hash table?? Similarly like I do in AF_EnhancedDamage
            */
            var zCPar_Symbol symfunc; symfunc = _^(MEM_ReadIntArray (contentSymbolTableAddress,  /*diaInfo.information */ diaInfo.conditions));
            
            var string infoFuncName; infoFuncName = symfunc.name;
            // building the variables symbol name
            var string symbName; symbName = ConcatStrings(infoFuncName, ".");
            symbName = ConcatStrings(symbName, AFIP_varName);
    
            // getting and checking if symbol with this name exists (if user defined item to be shown)
            var int symPtr; symPtr = MEM_GetParserSymbol (symbName);
            if (symPtr == 0) 
            {
                return 0;
            } 
            else 
            {
                var zCPar_Symbol sym;
                sym = _^(symPtr);
                return sym.content;
            };
        } 
        else if ((infoPtr && infoChoicePtr) || (!infoPtr  && infoChoicePtr))
        {
            /*    class oCInfoChoice {
                    var string Text;    //zSTRING 
                    var int Function;    //int     //symbolindex    
                };*/
            var oCInfoChoice diaInfoChoice; diaInfoChoice = _^(infoChoicePtr);
            var zCPar_Symbol symChoiceFunc; symChoiceFunc = _^(MEM_ReadIntArray (contentSymbolTableAddress, diaInfoChoice.Function));
            
            var string infoChoiceFuncName; infoChoiceFuncName = symChoiceFunc.name;
            
            // building the variables symbol name
            var string symbChoiceName; symbChoiceName = ConcatStrings(infoChoiceFuncName, ".");
            symbChoiceName = ConcatStrings(symbChoiceName, AFIP_varName);
            
            // getting and checking if symbol with this name exists (if user defined item to be shown)
            var int symbPtr; symbPtr = MEM_GetParserSymbol (symbChoiceName);
            if (symbPtr == 0) 
            {
                return 0;
            } 
            else 
            {
                var zCPar_Symbol symb;
                symb = _^(symbPtr);
                return symb.content;
            };
        }
        else
        {
            return 0;
        };    
    };
    
    //========================================
    // HOOK: runs the frame function at the
    // beginning of the dialogue
    //========================================
    func void _hook_oCInformationManager__OnInfoBegin()
    {
        FF_ApplyOnce(FF_AF_ItemPreview);
    };
    
    //========================================
    // HOOK: removes the frame function at the
    // end of the dialogue
    //========================================
    func void _hook_oCInformationManager__OnTermination()
    {
        FF_Remove(FF_AF_ItemPreview);
    };
    
    //========================================
    // FF: checks if there is item to be 
    // shown and shows it
    //========================================
    func void FF_AF_ItemPreview()
    {
        // show only when the dialogue selection is active
        if (MEM_InformationMan.IsWaitingForSelection) 
        {
            // get the item, if there is any
            var int itm; itm = AFIP_HasItem();
            if (itm == 0 ) // if there is no item, delete view (if there's one) and end
            {
                AFIP_currentItem = 0;
                AFIP_Delete();
                return;
            }
            if (AFIP_currentItem != itm) // if the item changed delete the old view and create new one
            {
                AFIP_Delete();
                AFIP_currentItem = 0;
                AFIP_Create(itm);
                return;
            }
            else // there is item to show
            {
                if (!AFIP_view) // == 0 // if the view doesn't already exists
                {
                    AFIP_Create(itm);
                };
            };
        }
        else // if dialogue selection is inactive delete the view (checks are in the AFIP_Delete function)
        {
            AFIP_currentItem = 0;
            AFIP_Delete();
            return;
        };
    };
    
    //========================================
    // Init function
    //========================================
    func void AF_ItemPreview_Init() 
    {
        const int once = 0;
        if (!once) {
            // 0072E430  .text     Debug data           ?OnTermination@oCInformationManager@@IAIXXZ
            // 0x006631A0 protected: void __fastcall oCInformationManager::OnTermination(void)
            HookEngine(MEMINT_SwitchG1G2(7529520, 6697376), MEMINT_SwitchG1G2(6, 7), "_hook_oCInformationManager__OnTermination");
            // 0072D2E0  .text     Debug data           ?OnInfoBegin@oCInformationManager@@IAIXXZ
            // 0x00661FF0 protected: void __fastcall oCInformationManager::OnInfoBegin(void)
            HookEngine(MEMINT_SwitchG1G2(7525088, 6692848), 6, "_hook_oCInformationManager__OnInfoBegin");
            
            once = 1;
        };
    };
    
    
    /*
    Nice try, but doesn't work :(
    Maybe I have a bug somwhere down here?
    
    // 0x00706E40 protected: virtual void __thiscall oCItemContainer::DrawItemInfo(class oCItem *,class zCWorld *)
    func int oCItemContainer__DrawItemInfo(var int this, var int itemPtr) {
        const int oCItemContainer__DrawItemInfo = 7368256;
        const int call = 0;
        if(CALL_Begin(call)) {
            CALL_IntParam(MEM_GetIntAddress(_render_wld));
            CALL_IntParam(MEM_GetIntAddress(itemPtr));
            CALL__thiscall(MEM_GetIntAddress(this), oCItemContainer__DrawItemInfo);
            call = CALL_End();
        };
    }; 
    
    func void AFIP_Show(var c_npc npcInst, var c_item itm)
    {
        var int itemPtr; itemPtr = Itm_GetPtr(itm);
        var oCNpc npc; npc = Hlp_GetNpc(npcInst);
        
        oCItemContainer__DrawItemInfo(npc.inventory2_vtbl, itemPtr);
    };
    
    */
    And here is, how you use the feature in Daedalus
    oCInfo type dialogue:
    Spoiler:(zum lesen bitte Text markieren)
    Code:
    instance DIA_Xardas_Reward (C_INFO)
    {
        npc            = NONE_100_Xardas;
        nr            = 1; //3;
        condition    = DIA_Xardas_Reward_Condition;
        information    = DIA_Xardas_Reward_Info;
        permanent    = TRUE;
        description    = "I'll take the dagger.";
    };
    
    func int DIA_Xardas_Reward_Condition () {
        // This is how you define a item to be shown when this dialogue is highlighted
        var int AFIP_ShowItem; AFIP_ShowItem = ItMw_1h_Vlk_Dagger;
        
        // if ( /* condition */ ) {
            return TRUE;
        // };
    };
    
    func void DIA_Xardas_Reward_Info () {
        CreateInvItem (other, ItMw_1h_Vlk_Dagger);
        AI_Output (other, self, "DIA_Xardas_Reward_14_00"); //I'll take dagger.
        AI_Output (self, other, "DIA_Xardas_Reward_15_01"); //Here it is.
        AI_StopProcessInfos    (self);
    };

    oCInfoChoice type dialogue:
    Spoiler:(zum lesen bitte Text markieren)
    Code:
    instance DIA_Torrez_Belohnung(C_Info)
    {
        npc         = NONE_100_Xardas;
        nr             = 5;
        condition     = DIA_Torrez_Belohnung_Condition;
        information = DIA_Torrez_Belohnung_Info;
        permanent     = 0;
        description = "I am here to choose my reward.";
    };
    
    
    func int DIA_Torrez_Belohnung_Condition()
    {
        return TRUE;
    };
    
    func void DIA_Torrez_Belohnung_Info()
    {
        AI_Output(other,self,"DIA_Torrez_Belohnung_15_00");    //I am here to choose my reward.
        AI_Output(self,other,"DIA_Torrez_Belohnung_04_01");    //Choose wisely.
        Info_ClearChoices(DIA_Torrez_Belohnung);
    
        Info_AddChoice(DIA_Torrez_Belohnung,     "Elixir of the spirit",        DIA_Torrez_Belohnung_ManaMax );
        // Assigns item to a choice
        AFIP_SetChoiceItem(DIA_Torrez_Belohnung_ManaMax, ItPo_Perm_Mana);
        
        Info_AddChoice(DIA_Torrez_Belohnung,     "Elixir of life",    DIA_Torrez_Belohnung_Scrolls );
        // Assigns item to a choice
        AFIP_SetChoiceItem(DIA_Torrez_Belohnung_Scrolls, ItPo_Perm_Health);
        
        Info_AddChoice(DIA_Torrez_Belohnung,     "Ring of dexterity",    DIA_Torrez_Belohnung_Dex );
        // Assigns item to a choice
        AFIP_SetChoiceItem(DIA_Torrez_Belohnung_Dex, ItRi_Dex_02);
        
        Info_AddChoice(DIA_Torrez_Belohnung,     "Ring of strength",            DIA_Torrez_Belohnung_Str );
        // Assigns item to a choice
        AFIP_SetChoiceItem(DIA_Torrez_Belohnung_Str, ItRi_Str_02);
    };
    
    func void DIA_Torrez_Belohnung_Str()
    {
        var int AFIP_ShowItem; // You have to define a variable, that is going to contain the item
        AI_Output(other,self,"DIA_Torrez_Belohnung_Str_15_00");    //I'll take the ring of strength.
        AI_Output(self,other,"DIA_Torrez_Belohnung_Str_04_01");    //Experienced choice. Here it is.
        
        // A nice coincidence - we don't have to manually select which item to insert, since we have it in AFIP_ShowItem variable
        CreateInvItem (other, AFIP_ShowItem);
        
        Info_ClearChoices(DIA_Torrez_Belohnung);
    };
    
    func void DIA_Torrez_Belohnung_Dex()
    {
        var int AFIP_ShowItem; // You have to define a variable, that is going to contain the item
        AI_Output(other,self,"DIA_Torrez_Belohnung_Dex_15_00");    //I would like this ring of dexterity.
        AI_Output(self,other,"DIA_Torrez_Belohnung_Dex_04_01");    //Dexterity wins against strength. Good choice.
        
        // A nice coincidence - we don't have to manually select which item to insert, since we have it in AFIP_ShowItem variable
        CreateInvItem (other, AFIP_ShowItem);
        
        Info_ClearChoices(DIA_Torrez_Belohnung);
    };
    
    func void DIA_Torrez_Belohnung_Scrolls()
    {
        var int AFIP_ShowItem; // You have to define a variable, that is going to contain the item
        AI_Output(other,self,"DIA_Torrez_Belohnung_Scrolls_15_00");    //Give me the elixir of life .
        AI_Output(self,other,"DIA_Torrez_Belohnung_Scrolls_04_01");    //Use it well.
        
        // A nice coincidence - we don't have to manually select which item to insert, since we have it in AFIP_ShowItem variable
        CreateInvItem (other, AFIP_ShowItem);
        
        Info_ClearChoices(DIA_Torrez_Belohnung);
    };
    
    func void DIA_Torrez_Belohnung_ManaMax()
    {
        var int AFIP_ShowItem; // You have to define a variable, that is going to contain the item
        AI_Output(other,self,"DIA_Torrez_Belohnung_ManaMax_15_00");    //I would like to have the elixir of spirit!
        AI_Output(self,other,"DIA_Torrez_Belohnung_ManaMax_04_01");    //A wise choice! You have picked the most precious of all gifts. Drink the potion and your power will grow!
        
        // A nice coincidence - we don't have to manually select which item to insert, since we have it in AFIP_ShowItem variable
        CreateInvItem (other, AFIP_ShowItem);
        
        Info_ClearChoices(DIA_Torrez_Belohnung);
    };



    For it to run you need Ikarus and LeGo, as well as class definitions from AFSP (github link).

    Please let me know what you think. I am a begginer at this How can I improve my code, is this a good way to do this? Is the hash table way better/safer?

    I am looking forward for your feedback!

    Auronen

  4. Beiträge anzeigen #4 Zitieren
    banned
    Registriert seit
    Jan 2009
    Ort
    Oberösterreich
    Beiträge
    2.393
     
    Moe ist offline
    Cool feature! Many thanks.

  5. Beiträge anzeigen #5 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von Auronen Beitrag anzeigen
    Hello guys ,
    I made another feature. I always disliked, how, when you are selecting a reward in a dialogue, you cannot see what the item is. Modders then resort to strings such as Digger's Trousers. Protection: 15, 5, 5. So I made a feature that allows you to preview the item (like you can see it in your inventory) you are going to get when you choose that dialogue option.

    ....
    I am looking forward for your feedback!

    Auronen
    It doesn't quite work right with Gothic 2.

    OnInfoBegin isn't always called.

    Talking to Npc that says something at the start (e.g. Harad when you're his apprentice)
    -> OnInfoBegin is not called

    Talking to Orlan
    -> OnInfoBegin is called.


    To fix the issue on my side, i added one additional hook for oCInformationManager::SetNpc at the end of the method, after the npc check and other things, just before the (inlined!) call to ::OnImportantBegin

    Code:
    //========================================
    // Init function
    //========================================
    func void AF_ItemPreview_Init() 
    {
        const int once = 0;
        if (!once) {
            // ...
            HookEngine(MEMINT_SwitchG1G2(7525088, 6692848), 6, "_hook_oCInformationManager__OnInfoBegin");
            if (GOTHIC_BASE_VERSION == 2) {
                // in G2 OnInfoBegin is sometimes not called.
                // oCInformationManager::SetNpc @ 00660b74, size 6 <- always called at the beginning of a dialog
                HookEngine(6687604, 6, "_hook_oCInformationManager__OnInfoBegin");
            };
            
            once = 1;
        };
    };

  6. Beiträge anzeigen #6 Zitieren
    Ehrengarde Avatar von neocromicon
    Registriert seit
    Jan 2019
    Beiträge
    2.548
     
    neocromicon ist offline
    I become this error, if i use the ItemPreview Script, with and without Kirides changes (Gothic 2):

    Spoiler:(zum lesen bitte Text markieren)
    Code:
    ======================================= UNHANDLED EXCEPTION OCCURED ======================================================
    ======================================= CRASH INFOS: =====================================================================
    Gothic II - 2.6 (fix), Parser Version: 50
    User:  neocr,  CPUType: 586,  Mem: 0 MB total, 0 MB free
    Camera: Pos(-488.196686/200.665192/1904.34558), At(0.277977854/-0.116683684/0.953474343)
    Startup Options:-game:daemonejaeger_der_nacht.ini -zreparse -devmode
    =============================================== CALLSTACK : ==============================================================
    0023:007918D4 (0x00AB4108 0x00000000 0x00AB40C0 0x0001E61C) Gothic2.exe, zCParser::PopDataValue()+36 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1275+7 byte(s)
    0023:007925B5 (0x0001E5EF 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+3157 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1446+11 byte(s)
    0023:00792504 (0x0001E536 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x0001E607 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x0003528C 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x00000AFA 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x00000AFA 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x0000DA17 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x0000DA12 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x00000AFA 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x00003CA5 0x0E3AC660 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792504 (0x0000C195 0x0DE25E10 0x00000000 0x0135FAC0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415
    0023:00792CBF (0x00400000 0x013E74FD 0x0135FCE4 0x00000000) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1551
    0023:00425E6E (0x00000000 0x00503270 0x0000002C 0x0135FBC4) Gothic2.exe, CGameManager::Run()+1598 byte(s), P:\dev\g2addon\release\Gothic\_bert\oGameManager.cpp, line 767+47 byte(s)
    0023:0082933B (0x00000000 0x00000000 0x00000000 0x00000000) Gothic2.exe, SetFileAttributesA()+284535 byte(s)


    NPC Dialog Code:
    Code:
    ///////////////////////////////////////////////////////////////////////
    // Item Preview
    ///////////////////////////////////////////////////////////////////////
    INSTANCE DIA_Testdummy_ItemPreview (C_INFO)
    {
        npc			= PC_Testdummy;
        nr			= 8;
        condition	= DIA_Testdummy_ItemPreview_Condition;
        information	= DIA_Testdummy_ItemPreview_Info;
        permanent	= TRUE;
        description = "Item Preview Test";
    };  
    
    func int DIA_Testdummy_ItemPreview_Condition () {
        var int AFIP_ShowItem; AFIP_ShowItem = ItMw_1h_Vlk_Dagger;
        return TRUE;
    };
    
    func void DIA_Testdummy_ItemPreview_Info () {
        CreateInvItem (other, ItMw_1h_Vlk_Dagger);
        AI_StopProcessInfos    (self);
    };
    Startup.d:
    Code:
    func void INIT_GLOBAL()
    {	
        // wird fuer jede Welt aufgerufen (vor INIT_<LevelName>)
        Game_InitGerman();
    	
        //Ikarus and Lego Init
        MEM_InitAll();
        LeGo_Init(LeGo_All & ~LeGo_Bloodsplats);
    
        .....
    
        AF_ItemPreview_Init();
    
        .....
    Gothic.src:
    Code:
    _INTERN\CONSTANTS.D
    _INTERN\CLASSES.D
    
    //Mod
    Ikarus_G2.src
    LeGo\Header_G2.src
    
    AF-Script-Packet\_headers_G2_EnhancedInfoManager.src
    AF-Script-Packet\_headers_G2_LogDialogues.src
    AF-Script-Packet\_headers_G2_Debugging.src
    AF-Script-Packet\_headers_G2_NoAmmoPrint.src
    Scripts\AF_ItemPreview.d
    .......
    Thanks for Help

  7. Beiträge anzeigen #7 Zitieren
    Apprentice Avatar von Auronen
    Registriert seit
    Dec 2020
    Beiträge
    25
     
    Auronen ist offline
    I'll look into it, thank you for your comments

    EDIT:
    @Kirides is, of course, right. It has to be hooked reliably.

    @neocromicon
    That is very intereting, I just tested your code, and it runs, but I noticed something in the crash report. It looks like you are running the compilation with -zreparse, are you using zParserExtender Union plugin? I use that one too, but the proper parameter is -zReparse_GAME (for only the game parser to be parsed and compiled).
    Do you have any other plugins installed?
    Geändert von Auronen (28.12.2021 um 22:17 Uhr)

  8. Beiträge anzeigen #8 Zitieren
    Ehrengarde Avatar von neocromicon
    Registriert seit
    Jan 2019
    Beiträge
    2.548
     
    neocromicon ist offline
    Zitat Zitat von Auronen Beitrag anzeigen
    I'll look into it, thank you for your comments

    EDIT:
    @Kirides is, of course, right. It has to be hooked reliably.

    @neocromicon
    That is very intereting, I just tested your code, and it runs, but I noticed something in the crash report. It looks like you are running the compilation with -zreparse, are you using zParserExtender Union plugin? I use that one too, but the proper parameter is -zReparse_GAME (for only the game parser to be parsed and compiled).
    Do you have any other plugins installed?
    I Use Union 1.0k in my Mod Installation. But not any plugin or patch ect. The only think what i have found is Union.patch in System.

    [Bild: 1l1k9i.png]

  9. Beiträge anzeigen #9 Zitieren
    Apprentice Avatar von Auronen
    Registriert seit
    Dec 2020
    Beiträge
    25
     
    Auronen ist offline
    Zitat Zitat von neocromicon Beitrag anzeigen
    I Use Union 1.0k in my Mod Installation. But not any plugin or patch ect. The only think what i have found is Union.patch in System.

    [Bild: 1l1k9i.png]
    When does the crash happen exactly? I just run it again, and Fawkes tested it, and it works. Very interesting.

  10. Beiträge anzeigen #10 Zitieren
    Ehrengarde Avatar von neocromicon
    Registriert seit
    Jan 2019
    Beiträge
    2.548
     
    neocromicon ist offline
    Zitat Zitat von Auronen Beitrag anzeigen
    When does the crash happen exactly? I just run it again, and Fawkes tested it, and it works. Very interesting.
    As soon as I go over the entry, the game crashes. I don't even need to confirm the dialog entry. Should I send you my complete code privately?

  11. Beiträge anzeigen #11 Zitieren
    Local Hero
    Registriert seit
    Feb 2017
    Beiträge
    270
     
    F a w k e s ist offline
    Hello Neocromicon,
    Please make sure that you are using all required LeGo flags in LeGo_Init function (without LeGo_Render it crashed for me as well). I think these are required:
    Code:
    LeGo_HookEngine | LeGo_View | LeGo_FrameFunctions | LeGo_Render

  12. Beiträge anzeigen #12 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von F a w k e s Beitrag anzeigen
    Hello Neocromicon,
    Please make sure that you are using all required LeGo flags in LeGo_Init function (without LeGo_Render it crashed for me as well). I think these are required:
    Code:
    LeGo_HookEngine | LeGo_View | LeGo_FrameFunctions | LeGo_Render
    And also LeGo_Gamestate for LeGo_Render.

  13. Beiträge anzeigen #13 Zitieren
    Ehrengarde Avatar von neocromicon
    Registriert seit
    Jan 2019
    Beiträge
    2.548
     
    neocromicon ist offline
    With
    Code:
    LeGo_HookEngine | LeGo_View | LeGo_FrameFunctions | LeGo_Gamestate | LeGo_Render
    Now it Works But why not with LeGo_All?

  14. Beiträge anzeigen #14 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von neocromicon Beitrag anzeigen
    With
    Code:
    LeGo_HookEngine | LeGo_View | LeGo_FrameFunctions | LeGo_Gamestate | LeGo_Render
    Now it Works But why not with LeGo_All?
    LeGo_All does not include experimental packages, such as LeGo_Render or LeGo_Buffs

    Steht auch so in der LeGo.d bei der Definition von LeGo_All

  15. Beiträge anzeigen #15 Zitieren
    Ehrengarde Avatar von neocromicon
    Registriert seit
    Jan 2019
    Beiträge
    2.548
     
    neocromicon ist offline
    Zitat Zitat von Kirides Beitrag anzeigen
    LeGo_All does not include experimental packages, such as LeGo_Render or LeGo_Buffs

    Steht auch so in der LeGo.d bei der Definition von LeGo_All
    Ja hätte man auch dran denken können, es selber zu lesen... Danke für dich Erklärung

  16. Beiträge anzeigen #16 Zitieren
    Local Hero
    Registriert seit
    Feb 2017
    Beiträge
    270
     
    F a w k e s ist offline

    Enhanced InfoManager

    Hello folks,
    Quite some time ago, I presented a feature that would allow modders to easily change font and color of a dialogue description (and, later on, to use dialogue descriptions as input fields and spinners) in this thread:
    https://forum.worldofplayers.de/foru...d-color-change

    But trust me this is not reposting - meanwhile we have added these scripts to GitHub repository and created new features. All of the features work with both dialogue descriptions (C_Info.description) as well as with dialogue choices (Info_AddChoice):

    1. Changing dialogue color

    Add the "h@HexCode " (default color) and "hs@HexCode " (color when selected) modifiers to your dialogue description text:
    Code:
    instance DIA_EIM_Demo_01 (C_Info) {
        description = "h@FF3030 hs@FF4646 This text will be red.";
    };

    2. Text alignment of individual dialogue choices

    Add the "al@ " modifier for left alignment, "ac@ " for center alignment or "ar@ " for right alignment of the text:
    Code:
    instance DIA_EIM_Demo_02 (C_Info) {
        description = "ac@ This is text with text alignment - to the center.";
    };

    3. Answer system / input fields

    EIM can change a dialogue choice into an 'input field' - your input will be stored in the global variable InfoManagerAnswer.
    Add the "a@ " modifier to your dialogue description:
    Code:
    func void Info_SLD_723_FirstWarn_Info() {
        AI_Output (self, hero,"Info_SLD_723_FirstWarn_11_01"); //STOP! Nobody may pass without the password!
    
        if (Npc_KnowsInfo(hero, Info_Cronos_SLEEPER)) {
            Info_Clearchoices (Info_SLD_723_FirstWarn);
            Info_Addchoice (Info_SLD_723_FirstWarn,"a@ Cronos has given me permission!", Info_SLD_723_Parole_CRONOS);  
            Info_Addchoice (Info_SLD_723_FirstWarn,"No idea, I've forgotten it!", Info_SLD_723_Parole_FORGOT);  
        } else {
            AI_StopProcessInfos (self);
        };
    };
    
    func void Info_SLD_723_Parole_CRONOS () {
        //Convert to uppercase
        var string password; password = STR_Upper (InfoManagerAnswer);
    
        //Password correct:
        if (Hlp_StrCmp (password, "TETRIANDOCH")) {
            AI_Output (hero, self,"Info_SLD_723_Parole_TRUE_15_01"); //The password is TETRIANDOCH.
            AI_Output (self, hero,"Info_SLD_723_Parole_TRUE_11_02"); //That's right! You may pass!
    
            var C_NPC guard; guard = Hlp_GetNpc(Sld_732_Soeldner);
            Npc_SetAIVar (hero, AIV_GUARDPASSAGE_STATUS, AIV_GPS_BEGIN);
            Npc_SetAIVar (self, AIV_PASSGATE, TRUE);
            Npc_SetAIVar (Guard, AIV_PASSGATE, TRUE);
            B_GiveXP (XP_SayCorrectParole);
        } else {
            AI_Output (hero, self,"Info_SLD_723_Parole_CRONOS_15_01"); //Cronos has given me permission!
            AI_Output (self, hero,"Info_SLD_723_Parole_CRONOS_11_02"); //If that's so, he'd have given you the password. Get lost, you liar!
        };
    
        AI_StopProcessInfos (self);
    };

    4. Overlays

    Using overlays, we can change the formatting for only a certain part of a text.
    Add the "o@ customFormat : yourText ~" modifier to your dialogue description (customFormat = modifiers for color or text alignment):
    Code:
    instance Info_Diego_Brief (C_INFO) {
        npc = PC_Thief;
        nr = 10;
        condition = Info_Diego_Brief_Condition;
        information = Info_Diego_Brief_Info;
        permanent = 0;
        description = "I have a o@h@00CC66 hs@66FFB2:letter~ for the o@h@00CCCC hs@66FFFF:High Magician~ of the Circle of o@h@FF8000 hs@FFFF7F:Fire~.";
    };

    How it works:
    zCViewDialogChoice.m_listLines_array is an array containing all dialogue choices visible in the dialog choice box.
    Number of dialogue choices is stored in zCViewDialogChoice.Choices.
    I tried to extend the array zCViewDialogChoice.m_listLines_array and engine drew new dialogue choice on screen ... while cursor ignored my new dialogue choice - because it is most likely looking only at zCViewDialogChoice.Choices!
    Code that handles overlays, splits dialogue description and inserts each 'overlay' into an array zCViewDialogChoice.m_listLines_array with the correct X/Y coordinates and required color.

    5. Spinners

    Spinners allow you to use the left/right arrow keys to decrease/increase numerical value of the global variable InfoManagerSpinnerValue. There is a lot you can do with such a feature, e. g. improve cooking - select how much meat you want to cook without going through dialogue choices. In order to add a spinner to your dialogue, you have to add the "s@spinnerID " modifier to a dialogue description/choice, where spinnerID has to be a unique ID not containing any spaces. Working with spinners is more complex; the example below shows how you can use the _condition function to work with spinners (you can use this one as a template):
    Code:
    instance DIA_EIM_Spinner_01 (C_INFO) {
        nr        = 1;
        npc        = PC_Hero;
        condition    = DIA_EIM_Spinner_01_Condition;
        information    = DIA_EIM_Spinner_01_Info;
        important    = 0;
        permanent    = 1;
        description    = "dummy";
    };
    
    func int DIA_EIM_Spinner_01_Condition() {
    
        //if (PLAYER_MOBSI_PRODUCTION == MOBSI_DIALOG_PAN) {
            //These are in fact Global variables - we can exploit that for this feature - they will retain their value ;-)
            var string lastSpinnerID;
    
            var int value;
    
            var int min;
            var int max;
    
            //Min/max values
            min = 1;
            max = NPC_HasItems (self, ItFoMuttonRaw);
    
            //Check boundaries
            if (value < min) { value = min; };
            if (value > max) { value = max; };
    
            var int isActive; isActive = Hlp_StrCmp (InfoManagerSpinnerID, "CookMeat");
    
            //Setup spinner if spinner ID has changed
            if (isActive) {
                //What is current InfoManagerSpinnerID ?
                if (!Hlp_StrCmp (InfoManagerSpinnerID, lastSpinnerID)) {
                    //Update value
                    InfoManagerSpinnerValue = value;
                };
    
                //Page Up/Down quantity
                InfoManagerSpinnerPageSize = 5;
    
                //Min/max value (Home/End keys)
                InfoManagerSpinnerValueMin = min;
                InfoManagerSpinnerValueMax = max;
    
                //Update
                value = InfoManagerSpinnerValue;
            };
    
            lastSpinnerID = InfoManagerSpinnerID;
    
            var string newDescription; newDescription = "";
    
            //if (max == 0) {
            //    newDescription = "d@ "; //disabled
            //};
    
            //Spinner ID CookMeat
            newDescription = ConcatStrings (newDescription, "s@CookMeat Cook some meat: "); //Cook some meat
    
            //Manually typed in number:
            if (InfoManagerSpinnerNumberEditMode)
            && (TRUE) //change to FALSE if you don't want to allow manual typing
            && (isActive)
            {
                var string editedNumber;
    
                editedNumber = InfoManagerSpinnerNumber;
                editedNumber = ConcatStrings (editedNumber, "_");
    
                //Check boundaries - if value is out, add red color overlay
                if ((STR_ToInt (InfoManagerSpinnerNumber) < min) || (STR_ToInt (InfoManagerSpinnerNumber) > max)) {
                    editedNumber = ConcatStrings ("o@h@FF3030 hs@FF4646 :", editedNumber);
                    editedNumber = ConcatStrings (editedNumber, "~");
                };
    
                newDescription = ConcatStrings (newDescription, editedNumber);
            } else {
                newDescription = ConcatStrings (newDescription, IntToString (value));
            };
    
            newDescription = ConcatStrings (newDescription, " / ");
            newDescription = ConcatStrings (newDescription, IntToString (max));
    
            //Update description
            DIA_EIM_Spinner_01.description = newDescription;
    
            return TRUE;
        //};
    
        return FALSE;
    };
    
    func void DIA_EIM_Spinner_01_Info() {
    };

    You can also type a number in manually using numerical keyboard - if the number typed in is out of bounds, then the code above will use a red overlay to indicate a problem:

    We don't want to use it just to work with numbers - let's make a 'choice' spinner:

    [Bild: giphy.gif]

    Code:
    instance DIA_EIM_Spinner_02 (C_INFO) {
        nr        = 1;
        npc        = PC_Hero;
        condition    = DIA_EIM_Spinner_02_Condition;
        information    = DIA_EIM_Spinner_02_Info;
        important    = 0;
        permanent    = 1;
        description    = "dummy";
    };
    
    func int DIA_EIM_Spinner_02_Condition() {
        //These are in fact Global variables - we can exploit that for this feature - they will retain their value ;-)
        var string lastSpinnerID;
    
        var int value;
    
        var int min;
        var int max;
    
        //Min/max values
        min = 1;
        max = 3;
    
        //Check boundaries
        if (value < min) { value = min; };
        if (value > max) { value = max; };
    
        var int isActive; isActive = Hlp_StrCmp (InfoManagerSpinnerID, "ColorPicker");
    
        //Setup spinner if spinner ID has changed
        if (isActive) {
            //What is current InfoManagerSpinnerID ?
            if (!Hlp_StrCmp (InfoManagerSpinnerID, lastSpinnerID)) {
                //Update value
                InfoManagerSpinnerValue = value;
            };
    
            //Page Up/Down quantity
            InfoManagerSpinnerPageSize = 5;
    
            //Min/max value (Home/End keys)
            InfoManagerSpinnerValueMin = min;
            InfoManagerSpinnerValueMax = max;
    
            //Update
            value = InfoManagerSpinnerValue;
        };
    
        lastSpinnerID = InfoManagerSpinnerID;
    
        var string newDescription; newDescription = "";
    
        //Spinner ID ColorPicker
        newDescription = ConcatStrings (newDescription, "s@ColorPicker ");
    
        if (value == 1) {
            newDescription = ConcatStrings (newDescription, "This is o@h@FF3030 hs@FF4646 :red~ color.");
        } else
        if (value == 2) {
            newDescription = ConcatStrings (newDescription, "This is o@h@00CC66 hs@66FFB2 :green~ color.");
        } else
        if (value == 3) {
            newDescription = ConcatStrings (newDescription, "This is o@h@6699FF hs@99CCFF :blue~ color.");
        };
    
        //Update description
        DIA_EIM_Spinner_02.description = newDescription;
    
        return TRUE;
    };
    
    func void DIA_EIM_Spinner_02_Info() {
    };

    We want to have all of our features available for both C_INFO dialogues and dialogue choices. Spinner functionality is also available for dialogue choices - however, it requires different handling. While with C_INFO dialogues we can easily work with the C_INFO.description property, with choices we have to use the custom function InfoManager_SetInfoChoiceText_BySpinnerID to update the choice text:

    Code:
    instance DIA_EIM_Spinner_Choices_01(C_Info) {
        nr        = 2;
        npc        = PC_Hero;
        condition    = DIA_EIM_Spinner_Choices_01_Condition;
        information    = DIA_EIM_Spinner_Choices_01_Info;
        important    = 0;
        permanent    = 1;
        description    = "Let's cook with choices ...";
    };
    
    func int DIA_EIM_Spinner_Choices_01_Condition() {
        //if (PLAYER_MOBSI_PRODUCTION == MOBSI_DIALOG_PAN) {
        //These are in fact Global variables - we can exploit that for this feature - they will retain their value ;-)
            var string lastSpinnerID;
    
            var int min;
            var int max;
    
            var int isActive;
            var string newDescription;
            var string editedNumber;
    
    //-- Spinner Choice #1
            var int value1; //Spinner #1 value
    
            //Min/max values
            min = 1;
            max = NPC_HasItems (self, ItFoMuttonRaw);
    
            //Check boundaries
            if (value1 < min) { value1 = min; };
            if (value1 > max) { value1 = max; };
    
            isActive = Hlp_StrCmp (InfoManagerSpinnerID, "CookMeat");
    
            //Setup spinner if spinner ID has changed
            if (isActive) {
                //What is current InfoManagerSpinnerID ?
                if (!Hlp_StrCmp (InfoManagerSpinnerID, lastSpinnerID)) {
                    //Update value
                    InfoManagerSpinnerValue = value1;
                };
    
                //Page Up/Down quantity
                InfoManagerSpinnerPageSize = 5;
    
                //Min/max value (Home/End keys)
                InfoManagerSpinnerValueMin = min;
                InfoManagerSpinnerValueMax = max;
    
                //Update
                value1 = InfoManagerSpinnerValue;
            };
    
            newDescription = "";
    
            if (max == 0) {
                //newDescription = "d@ "; //disabled
            };
    
            //Spinner ID CookMeat
            newDescription = ConcatStrings (newDescription, "s@CookMeat Cook some Meat: "); //Cook some meat
    
            //Manually typed in number:
            if (InfoManagerSpinnerNumberEditMode)
            && (TRUE) //change to FALSE if you don't want to allow manual typing
            && (isActive)
            {
                editedNumber = InfoManagerSpinnerNumber;
                editedNumber = ConcatStrings (editedNumber, "_");
    
                //Check boundaries - if value is out, add red color overlay
                if ((STR_ToInt (InfoManagerSpinnerNumber) < min) || (STR_ToInt (InfoManagerSpinnerNumber) > max)) {
                    editedNumber = ConcatStrings ("o@h@FF3030 hs@FF4646 :", editedNumber);
                    editedNumber = ConcatStrings (editedNumber, "~");
                };
    
                newDescription = ConcatStrings (newDescription, editedNumber);
            } else {
                newDescription = ConcatStrings (newDescription, IntToString (value1));
            };
    
            newDescription = ConcatStrings (newDescription, " / ");
            newDescription = ConcatStrings (newDescription, IntToString (max));
    
            //Update choice description!
            InfoManager_SetInfoChoiceText_BySpinnerID (newDescription, "CookMeat");
    
    //-- Spinner Choice #2
            var int value2; //Spinner #2 value
    
            //Min/max values
            min = 1;
            max = NPC_HasItems (self, ItFoMutton);
    
            //Check boundaries
            if (value2 < min) { value2 = min; };
            if (value2 > max) { value2 = max; };
    
            isActive = Hlp_StrCmp (InfoManagerSpinnerID, "CookFish");
    
            //Setup spinner if spinner ID has changed
            if (isActive) {
                //What is current InfoManagerSpinnerID ?
                if (!Hlp_StrCmp (InfoManagerSpinnerID, lastSpinnerID)) {
                    //Update value
                    InfoManagerSpinnerValue = value2;
                };
    
                //Page Up/Down quantity
                InfoManagerSpinnerPageSize = 5;
    
                //Min/max value (Home/End keys)
                InfoManagerSpinnerValueMin = min;
                InfoManagerSpinnerValueMax = max;
    
                //Update
                value2 = InfoManagerSpinnerValue;
            };
    
            newDescription = "";
    
            if (max == 0) {
                //newDescription = "d@ "; //disabled
            };
    
            //Spinner ID CookMeat
            newDescription = ConcatStrings (newDescription, "s@CookFish Cook some Fish: ");
    
            //Manually typed in number:
            if (InfoManagerSpinnerNumberEditMode)
            && (TRUE) //change to FALSE if you don't want to allow manual typing
            && (isActive)
            {
                editedNumber = InfoManagerSpinnerNumber;
                editedNumber = ConcatStrings (editedNumber, "_");
    
                //Check boundaries - if value is out, add red color overlay
                if ((STR_ToInt (InfoManagerSpinnerNumber) < min) || (STR_ToInt (InfoManagerSpinnerNumber) > max)) {
                    editedNumber = ConcatStrings ("o@h@FF3030 hs@FF4646 :", editedNumber);
                    editedNumber = ConcatStrings (editedNumber, "~");
                };
    
                newDescription = ConcatStrings (newDescription, editedNumber);
            } else {
                newDescription = ConcatStrings (newDescription, IntToString (value2));
            };
    
            newDescription = ConcatStrings (newDescription, " / ");
            newDescription = ConcatStrings (newDescription, IntToString (max));
    
            //Update choice description!
            InfoManager_SetInfoChoiceText_BySpinnerID (newDescription, "CookFish");
    
            //--
            lastSpinnerID = InfoManagerSpinnerID;
            return TRUE;
        //};
    
        return FALSE;
    };
    
    func void DIA_EIM_Spinner_Choices_01_Info() {
        Info_ClearChoices(DIA_EIM_Spinner_Choices_01);
        Info_AddChoice(DIA_EIM_Spinner_Choices_01, DIALOG_BACK, DIA_EIM_Spinner_Choices_01_Back);
        Info_AddChoice(DIA_EIM_Spinner_Choices_01, "s@CookFish Cook some Fish: ", DIA_EIM_Spinner_Choices_01_CookFish);
        Info_AddChoice(DIA_EIM_Spinner_Choices_01, "s@CookMeat Cook some Meat: ", DIA_EIM_Spinner_Choices_01_CookMeat);
    };
    
    func void DIA_EIM_Spinner_Choices_01_Back () {
        Info_ClearChoices(DIA_EIM_Spinner_Choices_01);
    };
    
    func void DIA_EIM_Spinner_Choices_01_CookMeat () {
        PrintS (IntToString (InfoManagerSpinnerValue));
        DIA_EIM_Spinner_Choices_01_Info ();
    };
    
    func void DIA_EIM_Spinner_Choices_01_CookFish () {
        PrintS (IntToString (InfoManagerSpinnerValue));
        DIA_EIM_Spinner_Choices_01_Info ();
    };

    Options are limitless - as everything is handled from the condition function, you can get more creative with what you actually do ...

    EIM 'passive' features

    There are mainly 3 things that EIM does automatically:
    1. it constantly evaluates _condition functions for available dialogues (you might have figured that out from the spinner examples - in which spinner dialogue condition updates description text)
    2. it takes care of horizontal text scrolling; if given dialogue text is too long, EIM will scroll through it in a loop

    [Bild: giphy.gif]

    3. it overrides controls for Key_Tab - this no longer confirms dialogue choice; instead it is now possible to confirm choices with Key_Space.

    EIM customization

    There are several options for customization; you can change the default text alignment or text color for all dialogues (in enhancedInfoManager.d):
    Code:
    //Default dialog colors
    const string InfoManagerDefaultDialogColorSelected = "FFFFFF";        //G1 standard dialog - white color FFFFFF
    const string InfoManagerDefaultColorDialogGrey = "C8C8C8";        //G1 standard dialog - grey color C8C8C8
    
    const string InfoManagerDefaultFontDialogSelected = "";            //Default font for selected dialog choice (if blank default Gothic version will be used)
    const string InfoManagerDefaultFontDialogGrey = "";            //Default font for greyed (if blank default Gothic version will be used)
    
    const string InfoManagerDisabledDialogColorSelected = "808080";        //Disabled color - selected
    const string InfoManagerDisabledColorDialogGrey = "808080";        //Disabled color - grey
    
    //Default text alignment
    const int InfoManagerDefaultDialogAlignment = ALIGN_LEFT;        //ALIGN_CENTER, ALIGN_LEFT, ALIGN_RIGHT defined in LeGo
    Once again this feature is available on GitHub, feel free to check it out:
    https://github.com/auronen/AF-Script...mation-manager

    We will try to document all features from script-packet in next couple of weeks

  17. Beiträge anzeigen #17 Zitieren
    Ehrengarde Avatar von neocromicon
    Registriert seit
    Jan 2019
    Beiträge
    2.548
     
    neocromicon ist offline
    Best Feature Script Pack i have ever see! I using it a lot in my mod, thanks for the Hard work!!

  18. Beiträge anzeigen #18 Zitieren
    Local Hero
    Registriert seit
    Feb 2013
    Beiträge
    236
     
    pawbuj ist offline
    I tried to adapt ur code into Gothic 1, but I got the syntax error in folowing part of inventory_ocNPC.

    Code:
    var oCItemContainer container
    nevermind it may happened due to not updated ninja to current ikarus/lego version.
    Geändert von pawbuj (21.03.2023 um 12:00 Uhr)

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