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

 

Seite 1 von 2 12 Letzte »
Ergebnis 1 bis 20 von 21
  1. Beiträge anzeigen #1 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline

    Post [Skript] Tauchbalken zu universellem Ausdauersystem erweitern

    Mit LeGo lassen sich sehr bequem Bars erstellen. In den meisten Fällen ist die Absicht aber nur ein einzelner Balken: ein Ausdauersystem. Meist ist dann die Frage, ob der Tauchbalken ersetzt werden soll oder wie ein neuer Balken in das Schema passt, usw.

    Eine vertane Chance, denn der Tauchbalken beschreibt bereits nichts anderes als verbleibenden Atem und wird außer fürs Tauchen im Spiel nicht genutzt. Außerdem ist der Balken schon vollständig integriert und regeneriert bereits automatisch. Es reicht vollkommen, den Wert der Variable "divectr" des oCNpc-Objekts des Helden zu ändern - fertig ist das Ausauersystem inkl. Balken und Regeneration.

    Um diese Ausdauer (oder besser den Atem) für Modder besser offen zu legen, habe ich ein Skript geschrieben, dass eine komfortable Schnittstelle darstellt.

    Neben Breath_Decrease { Ms / Percent / Permille }, um den Atem manipulieren, gibt es eine Vielzahl von Funktionen, z.B. um die Regenerierung anzupassen, den maximalen Atem zu vergrößern oder um sich den verbleibenden Atem eines NPC zu holen. Dabei wird der verbleibende Atem des Helden mitgespeichert, sowie dessen maximal verfügbarer Atem. Entsprechende Werte von anderen NPCs werden nicht mitgespeichert. Der Regenerierungsfaktor bleibt ebenfalls über Spielstände hinweg erhalten. Dieser Faktor ist global für alle NPC. Da NPC normalerweise nicht tauchen (sondern maximal schwimmen) stellt das kein Problem da. Der Tauchbalken kann dazu angezeigt werden - entweder solange er nicht voll ist, oder durchgängig.

    Mehr gibt es kaum hinzuzufügen. Besser ist die Auflistung der Funktion am Anfang des Skripts zusammen mit den Kommentaren über den jeweiligen Funktionen.

    Code:
    /*
     * breath.d
     * Source: https://forum.worldofplayers.de/forum/threads/?p=26441844
     *
     * Collection of functions that expose the swim bar for generalized use (e.g. stamina, breath).
     *
     * - Requires Ikarus 1.2.2
     * - Compatible with Gothic 1 and Gothic 2
     *
     * Instructions
     * - Initialize from Init_Global with these two lines
     *     Breath_Init();
     *     Breath_SetBarVisibleNonEmpty();   - OR -   Breath_SetBarVisibleAlways();
     *
     *
     * Initialize the regeneration and bar visibility (call from Init_Global)
     *  func void Breath_Init()
     *
     * Set visibility of swim bar (to be called optionally AFTER Breath_Init)
     *  func void Breath_SetBarVisibleAlways()
     *  func void Breath_SetBarVisibleNonEmpty()
     *
     * Get/set maximum breath, load/save-persistent for player only. Default is 30000 ms (30 sec)
     *  func intf Breath_TotalF   (C_Npc slf)
     *  func int  Breath_Total    (C_Npc slf)
     *  func void Breath_SetTotalF(C_Npc slf, intf ms)
     *  func void Breath_SetTotal (C_Npc slf, int  ms)
     *
     * Get remaining breath
     *  func intf Breath_RemainingMsF     (C_Npc slf)
     *  func int  Breath_RemainingMs      (C_Npc slf)
     *  func int  Breath_RemainingPercent (C_Npc slf)
     *  func int  Breath_RemainingPermille(C_Npc slf)
     *
     * Check if enough breath is available
     *  func bool Breath_AvailableMsF     (C_Npc slf, intf ms)
     *  func bool Breath_AvailableMs      (C_Npc slf, int  ms)
     *  func bool Breath_AvailablePercent (C_Npc slf, int  percent)
     *  func bool Breath_AvailablePermille(C_Npc slf, int  permille)
     *
     * Set remaining breath
     *  func void Breath_SetMsF     (C_Npc slf, intf ms)
     *  func void Breath_SetMs      (C_Npc slf, int  ms)
     *  func void Breath_SetPercent (C_Npc slf, int  percent)
     *  func void Breath_SetPermille(C_Npc slf, int  permille)
     *
     * Decrease breath
     *  func void Breath_DecreaseMsF     (C_Npc slf, intf ms)
     *  func void Breath_DecreaseMs      (C_Npc slf, int  ms)
     *  func void Breath_DecreasePercent (C_Npc slf, int  percent)
     *  func void Breath_DecreasePermille(C_Npc slf, int  permille)
     *
     * Decrease breath if available, return TRUE if successful
     *  func bool Breath_RequestMsF     (C_Npc slf, intf ms,       intf required)
     *  func bool Breath_RequestMs      (C_Npc slf, int  ms,       int  required)
     *  func bool Breath_RequestPercent (C_Npc slf, int  percent,  int  required)
     *  func bool Breath_RequestPermille(C_Npc slf, int  permille, int  required)
     *
     * Set the regeneration: how many ms of breath are recovered within one sec. Default is 2000 ms
     *  func void Breath_SetRegeneration(int  ms)
     */
    
    /* Internal constants/variables */
    const int _Breath_DontRegen = 0;
    var   int _Breath_player_max;
    var   int _Breath_RegenFactor;
    
    /*
     * Obtain the corresponding number of milliseconds for a fraction of the total breath
     */
    func int Breath_ToMs(var C_Npc slf, var int amount, var int frac) {
        if (frac <= 1) { return amount; };
        if (!Hlp_IsValidNpc(slf)) { return FLOATNULL; };
        var oCNpc npc; npc = Hlp_GetNpc(slf);
        var int unit; unit = divf(npc.divetime, mkf(frac));
        return mulf(amount, unit);
    };
    
    /*
     * Obtain the corresponding fraction of the total breath for a number of milliseconds
     */
    func int Breath_FromMs(var C_Npc slf, var int ms, var int frac) {
        if (frac <= 1) { return ms; };
        if (!Hlp_IsValidNpc(slf)) { return FLOATNULL; };
        var oCNpc npc; npc = Hlp_GetNpc(slf);
        var int unit; unit = divf(mkf(frac), npc.divetime);
        return mulf(ms, unit);
    };
    
    /*
     * Return the total breath in milliseconds (integer float)
     */
    func int Breath_TotalF(var C_Npc slf) {
        if (!Hlp_IsValidNpc(slf)) { return FLOATNULL; };
        var oCNpc npc; npc = Hlp_GetNpc(slf);
        return npc.divetime;
    };
    func int Breath_Total(var C_Npc slf) {
        return roundf(Breath_TotalF(slf));
    };
    
    /*
     * Set the total breath in milliseconds (integer float)
     * The remaining breath value is not altered and remains as before
     */
    func void Breath_SetTotalF(var C_Npc slf, var int ms) {
        if (!Hlp_IsValidNpc(slf)) { return; };
        var oCNpc npc; npc = Hlp_GetNpc(slf);
    
        // If negative, decrease current total
        if (lf(ms, FLOATNULL)) {
            ms = addf(npc.divetime, ms);
            if (lf(ms, FLOATNULL)) {
                ms = FLOATNULL;
            };
        };
    
        npc.divetime = ms;
    
        // Save for loading/level change
        if (Npc_IsPlayer(slf)) {
            _Breath_player_max = npc.divetime;
        };
    };
    func void Breath_SetTotal(var C_Npc slf, var int ms) {
        Breath_SetTotalF(slf, mkf(ms));
    };
    
    /*
     * Return the remaining breath in milliseconds or fraction (integer float)
     */
    func int Breath_Remaining(var C_Npc slf, var int frac) {
        if (!Hlp_IsValidNpc(slf)) { return FLOATNULL; };
        var oCNpc npc; npc = Hlp_GetNpc(slf);
        return Breath_FromMs(slf, npc.divectr, frac);
    };
    func int Breath_RemainingMsF(var C_Npc slf) {
        return Breath_Remaining(slf, 1);
    };
    func int Breath_RemainingMs(var C_Npc slf) {
        return roundf(Breath_Remaining(slf, 1));
    };
    func int Breath_RemainingPercent(var C_Npc slf) {
        return roundf(Breath_Remaining(slf, 100));
    };
    func int Breath_RemainingPermille(var C_Npc slf) {
        return roundf(Breath_Remaining(slf, 1000));
    };
    
    /*
     * Check if enough breath (milliseconds or fraction) is available
     */
    func int Breath_Available(var C_Npc slf, var int amount, var int frac) {
        var int ms; ms = Breath_ToMs(slf, amount, frac);
        var int avail; avail = Breath_Remaining(slf, 1);
        return gef(avail, ms);
    };
    func int Breath_AvailableMsF(var C_Npc slf, var int ms) {
        return Breath_Available(slf, ms, 1);
    };
    func int Breath_AvailableMs(var C_Npc slf, var int ms) {
        return Breath_Available(slf, mkf(ms), 1);
    };
    func int Breath_AvailablePercent(var C_Npc slf, var int percent) {
        return Breath_Available(slf, mkf(percent), 100);
    };
    func int Breath_AvailablePermille(var C_Npc slf, var int permille) {
        return Breath_Available(slf, mkf(permille), 1000);
    };
    
    /*
     * Set the remaining breath in milliseconds or fraction (integer float)
     */
    func void Breath_Set(var C_Npc slf, var int amount, var int frac) {
        if (!Hlp_IsValidNpc(slf)) { return; };
        var int ms; ms = Breath_ToMs(slf, amount, frac);
        var oCNpc npc; npc = Hlp_GetNpc(slf);
    
        // If negative, decrease current remaining
        if (lf(ms, FLOATNULL)) {
            ms = addf(npc.divectr, ms);
            if (lf(ms, FLOATNULL)) {
                ms = FLOATNULL;
            };
        } else if (gf(ms, npc.divetime)) {
            ms = npc.divetime;
        };
    
        npc.divectr = ms;
    
        // Do not regenerate for this frame
        if (!_Breath_DontRegen) { _Breath_DontRegen = MEM_ArrayCreate(); };
        MEM_ArrayInsert(_Breath_DontRegen, _@(npc));
    };
    func void Breath_SetMsF(var C_Npc slf, var int ms) {
        Breath_Set(slf, ms, 1);
    };
    func void Breath_SetMs(var C_Npc slf, var int ms) {
        Breath_Set(slf, mkf(ms), 1);
    };
    func void Breath_SetPercent(var C_Npc slf, var int percent) {
        Breath_Set(slf, mkf(percent), 100);
    };
    func void Breath_SetPermille(var C_Npc slf, var int permille) {
        Breath_Set(slf, mkf(permille), 1000);
    };
    
    /*
     * Decrease the breath (by milliseconds or fraction)
     */
    func void Breath_Decrease(var C_Npc slf, var int amount, var int frac) {
        var int ms; ms = Breath_ToMs(slf, amount, frac);
        Breath_Set(slf, negf(ms), 1);
    };
    func void Breath_DecreaseMsF(var C_Npc slf, var int ms) {
        Breath_Decrease(slf, ms, 1);
    };
    func void Breath_DecreaseMs(var C_Npc slf, var int ms) {
        Breath_Decrease(slf, mkf(ms), 1);
    };
    func void Breath_DecreasePercent(var C_Npc slf, var int percent) {
        Breath_Decrease(slf, mkf(percent), 100);
    };
    func void Breath_DecreasePermille(var C_Npc slf, var int permille) {
        Breath_Decrease(slf, mkf(permille), 1000);
    };
    
    /*
     * Decrease the breath (by milliseconds or fraction) only if a required minimum is available
     */
    func int Breath_Request(var C_Npc slf, var int amount, var int required, var int frac) {
        if (Breath_Available(slf, required, frac)) {
            Breath_Decrease(slf, amount, frac);
            return TRUE;
        } else {
            return FALSE;
        };
    };
    func int Breath_RequestMsF(var C_Npc slf, var int ms, var int required) {
        return Breath_Request(slf, ms, required, 1);
    };
    func int Breath_RequestMs(var C_Npc slf, var int ms, var int required) {
        return Breath_Request(slf, mkf(ms), mkf(required), 1);
    };
    func int Breath_RequestPercent(var C_Npc slf, var int percent, var int required) {
        return Breath_Request(slf, mkf(percent), mkf(required), 100);
    };
    func int Breath_RequestPermille(var C_Npc slf, var int permille, var int required) {
        return Breath_Request(slf, mkf(permille), mkf(required), 1000);
    };
    
    /*
     * Set how many milliseconds it takes to regenerate one second of breath (for all NPC). Default is 2000 ms per second
     */
    func void Breath_SetRegeneration(var int ms) {
        _Breath_RegenFactor = fracf(ms, 1000);
    };
    
    /*
     * Change the visibility of the swim bar
     */
    func void Breath_SetBarVisible(var int always) {
        const int oCGame__UpdatePlayerStatus_swimBarJmp1_G1 = 6525142; //0x6390D6
        const int oCGame__UpdatePlayerStatus_swimBarJmp1_G2 = 7090988; //0x6C332C
        const int oCGame__UpdatePlayerStatus_swimBarJmp2_G1 = 6525154; //0x6390E2
        const int oCGame__UpdatePlayerStatus_swimBarJmp2_G2 = 7091000; //0x6C3338
        var int addr1; var int addr2;
        addr1 = MEMINT_SwitchG1G2(oCGame__UpdatePlayerStatus_swimBarJmp1_G1, oCGame__UpdatePlayerStatus_swimBarJmp1_G2);
        addr2 = MEMINT_SwitchG1G2(oCGame__UpdatePlayerStatus_swimBarJmp2_G1, oCGame__UpdatePlayerStatus_swimBarJmp2_G2);
    
        const int once = 0;
        if (!once) {
            MemoryProtectionOverride(addr1,  2);
            MemoryProtectionOverride(addr2, 14);
            once = 1;
        };
    
        if (always) {
            // Overwrite first conditional jump to unconditional jump
            if (MEM_ReadByte( addr1) ==  /*0F*/ 15) {
                MEM_WriteByte(addr1,     /*EB*/235);                     // jmp
                MEM_WriteByte(addr1+1,   /*1C*/ 28);                     // 0x28
            };
        } else {
            // Revert the above change
            if (MEM_ReadInt(  addr1) ==  /*EB 1C A4 00*/10755307) {      // Compare 4 bytes, because of third-party changes
                MEM_WriteByte(addr1,     /*0F*/ 15);                     // jz
                MEM_WriteByte(addr1+1,   /*84*/132);
            };
            // Overwrite second condition to be true if breath is non-full
            if (MEM_ReadByte( addr2) ==  /*E8*/232) {
                MEM_WriteByte(addr2,     /*8B*/139);                     // mov
                MEM_WriteByte(addr2+1,   /*81*/129);                     // eax,
                MEM_WriteInt( addr2+2,   MEMINT_SwitchG1G2(2064, 2000)); // [offset oCNpc.divetime]
                MEM_WriteByte(addr2+6,   /*3B*/ 59);                     // cmp
                MEM_WriteByte(addr2+7,   /*81*/129);                     // eax,
                MEM_WriteInt( addr2+8,   MEMINT_SwitchG1G2(2068, 2004)); // [offset oCNpc.divectr]
                MEM_WriteByte(addr2+12,  /*0F*/ 15);                     // jz
                MEM_WriteByte(addr2+13,  /*84*/132);
            };
        };
    };
    func void Breath_SetBarVisibleAlways() {
        Breath_SetBarVisible(TRUE);
    };
    func void Breath_SetBarVisibleNonEmpty() {
        Breath_SetBarVisible(FALSE);
    };
    
    
    /*
    This is the Daedalus-based (very slow) equivalent to the assembly-based initialization below.
    It's kept here for better understanding of the purpose and method.
    
    /*
     * Internal hook for regenerating breath
     *
    var int _Breath_player;
    func void _Breath_RegenHook() {
        var oCNpc npc; npc = _^(ESI);
        if (Npc_IsPlayer(npc)) {
            _Breath_player = npc.divectr;
        };
    
        var int factor; factor = _Breath_RegenFactor;
        if (_Breath_DontRegen) {
            var int i; i = MEM_ArrayIndexOf(_Breath_DontRegen, ESI);
            if (i != -1) {
                factor = 0;
                MEM_ArrayRemoveIndex(_Breath_DontRegen, i);
            };
        };
    
        // Additive term to divectr
        var int incr; incr = mulf(MEM_Timer.frameTimeFloat, factor);
        EAX = _@(incr);
    };
    
    /*
     * Modify the breath regeneration
     *
    func void Breath_Init_slow() {
        const int oCNpc_Process_diveRegen_G1 = 6926517; //0x69B0B5
        const int oCNpc_Process_diveRegen_G2 = 7595752; //0x73E6E8
        var int address; address = MEMINT_SwitchG1G2(oCNpc_Process_diveRegen_G1, oCNpc_Process_diveRegen_G2);
    
        // Only perform changes if bytes are not already modified
        if (MEM_ReadInt(address + 2) == _@(MEM_Timer.frameTimeFloat)) {
            const int ASMINT_OP_floatLoadFromEAX = 217; //0x00D9
            MemoryProtectionOverride(address, 8);
            MEM_WriteInt(address,     ASMINT_OP_nop + (ASMINT_OP_nop<<8) + (ASMINT_OP_nop<<16) + (ASMINT_OP_nop<<24));
            MEM_WriteInt(address + 4, ASMINT_OP_nop + (ASMINT_OP_nop<<8) + (ASMINT_OP_floatLoadFromEAX<<16));
            HookEngineF(address, 5, _Breath_RegenHook);
        };
    
        // Reset regenerate list
        if (_Breath_DontRegen) {
            MEM_ArrayClear(_Breath_DontRegen);
        };
    
        // Set default regeneration first time: regenerate 2 seconds of breath every second
        var int oncePerSave;
        if (!oncePerSave) {
            Breath_SetRegeneration(2000);
            _Breath_player     = mkf(30000); // NPC defaults
            _Breath_player_max = mkf(30000); // NPC defaults
            oncePerSave = TRUE;
        } else if (Hlp_IsValidNpc(hero)) {
            // Hero only exists at this point if a game is being loaded or the world is changed: exactly what we want
            var oCNpc her; her = Hlp_GetNpc(hero);
            if (her.divetime == mkf(30000)) {
                // Double check, in case it was changed elsewhere already (e.g. Init_Global)
                her.divetime = _Breath_player_max;
            };
            her.divectr  = _Breath_player;
        };
    
        // Set the swim bar to visible when non-empty
        Breath_SetBarVisibleNonEmpty();
    };*/
    
    
    /*
     * Modify the breath regeneration
     * Written in assembly for performance, because it is executed every frame for all NPC with non-full breath
     */
    func void Breath_Init() {
        const int oCNpc_Process_diveRegen_G1 = 6926517; //0x69B0B5
        const int oCNpc_Process_diveRegen_G2 = 7595752; //0x73E6E8
        var int address; address = MEMINT_SwitchG1G2(oCNpc_Process_diveRegen_G1, oCNpc_Process_diveRegen_G2);
        var int _Breath_player;
    
        // Only perform the following changes if the bytes are not already modified
        if (MEM_ReadInt(address + 2) == _@(MEM_Timer.frameTimeFloat)) {
    
            // Assembly instructions
            const int ASMINT_OP_pushESI          =    86; //0x56
            const int ASMINT_OP_jzShort          =   116; //0x74
            const int ASMINT_OP_jnzShort         =   117; //0x75
            // 2 bytes
            const int ASMINT_OP_testECXtoECX     = 51589; //0xC985
            const int ASMINT_OP_testEAXtoEAX     = 49285; //0xC085
            const int ASMINT_OP_floatLoadFromMem =  1497; //0x05D9
            const int ASMINT_OP_subSTfromST      = 57560; //0xE0D8
            const int ASMINT_OP_mulST0byST1pop   = 51678; //0xC9DE
            const int ASMINT_OP_cmpESItoMem      = 13627; //0x353B
            const int ASMINT_OP_movECXtoMem      =  3465; //0x0D89
            const int ASMINT_OP_movESIplusToECX  = 36491; //0x8E8B
    
            // Re-write regeneration (assembly for performance: called every frame for all NPC!)
            var int oCNpc_divectr_offset;
            var int oCNpc_player;
            var int zCArray_zCVob__IsInList;
            var int zCArray_zCVob__Remove;
            oCNpc_divectr_offset    = MEMINT_SwitchG1G2(2068, 2004);
            oCNpc_player            = MEMINT_SwitchG1G2(/*0x8DBBB0*/9288624, /*0xAB2684*/11216516);
            zCArray_zCVob__IsInList = MEMINT_SwitchG1G2(/*0x648EB0*/6590128, /*0x6D3D80*/ 7159168);
            zCArray_zCVob__Remove   = MEMINT_SwitchG1G2(/*0x648E60*/6590048, /*0x6D3D30*/ 7159088);
    
            ASM_Open(83);
            MemoryProtectionOverride(address, 8);
            MEM_WriteByte(address,     ASMINT_OP_jmp);
            MEM_WriteInt (address + 1, ASM_Here() - address - 5);
    
            // Save breath value of player
            ASM_2(ASMINT_OP_cmpESItoMem);      ASM_4(oCNpc_player);
            ASM_1(ASMINT_OP_jnzShort);         ASM_1(12);
            ASM_2(ASMINT_OP_movESIplusToECX);  ASM_4(oCNpc_divectr_offset);
            ASM_2(ASMINT_OP_movECXtoMem);      ASM_4(_@(_Breath_player));
    
            // Load regeneration factors
            ASM_2(ASMINT_OP_floatLoadFromMem); ASM_4(_@(MEM_Timer.frameTimeFloat));
            ASM_2(ASMINT_OP_floatLoadFromMem); ASM_4(_@(_Breath_RegenFactor));
    
            // Check if array exists
            ASM_2(ASMINT_OP_movMemToECX);      ASM_4(_@(_Breath_DontRegen));
            ASM_2(ASMINT_OP_testECXtoECX);
            ASM_1(ASMINT_OP_jzShort);          ASM_1(14+18);
    
            // Check if NPC is in array
            ASM_1(ASMINT_OP_pushESI);
            ASM_2(ASMINT_OP_movESPtoEAX);
            ASM_1(ASMINT_OP_pushEAX);
            ASM_1(ASMINT_OP_call);             ASM_4(zCArray_zCVob__IsInList - (ASM_Here()+4));
            ASM_1(ASMINT_OP_popECX);
            ASM_2(ASMINT_OP_testEAXtoEAX);
            ASM_1(ASMINT_OP_jzShort);          ASM_1(18);
    
            // Set one factor to zero
            ASM_2(ASMINT_OP_subSTfromST);
            ASM_1(ASMINT_OP_pushESI);
            ASM_2(ASMINT_OP_movESPtoEAX);
            ASM_1(ASMINT_OP_pushEAX);
            ASM_2(ASMINT_OP_movMemToECX);      ASM_4(_@(_Breath_DontRegen));
            ASM_1(ASMINT_OP_call);             ASM_4(zCArray_zCVob__Remove - (ASM_Here()+4));
            ASM_1(ASMINT_OP_popECX);
    
            // Multiply regeneration factors
            ASM_2(ASMINT_OP_mulST0byST1pop);
            ASM_1(ASMINT_OP_pushIm);           ASM_4(address+8);
            ASM_1(ASMINT_OP_retn);
            var int i; i = ASM_Close();
        };
    
        // Reset regenerate list
        if (_Breath_DontRegen) {
            MEM_ArrayClear(_Breath_DontRegen);
        };
    
        // Set default regeneration first time: regenerate 2 seconds of breath every second
        var int oncePerSave;
        if (!oncePerSave) {
            Breath_SetRegeneration(2000);
            _Breath_player     = mkf(30000); // NPC defaults
            _Breath_player_max = mkf(30000); // NPC defaults
            oncePerSave = TRUE;
        } else if (Hlp_IsValidNpc(hero)) {
            // Restore breath values for player
            // Hero only exists at this point if a game is being loaded or the world is changed: exactly what we want
            var oCNpc her; her = Hlp_GetNpc(hero);
            if (her.divetime == mkf(30000)) {
                // Double check, in case it was changed elsewhere already (e.g. Init_Global)
                her.divetime = _Breath_player_max;
            };
            her.divectr  = _Breath_player;
        };
    
        // Set the swim bar to visible when non-empty
        Breath_SetBarVisibleNonEmpty();
    };
    Geändert von mud-freak (31.01.2021 um 21:06 Uhr)

  2. Beiträge anzeigen #2 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Diese Schnittschnelle ist so einfach zu nutzen, dass ich in diesem Post zwei Beispiele zeige.

    (a) Ein komplettes Sprint System
    (b) Ein komplettes Stamina-System für den Nahkampf

    Beide Beispiele betten sich sehr gut in Gothic ein - mit nur wenigen Zeilen Code. Erstaunlicherweise bestehen die Beispiele jeweils aus nur einer zentralen sehr kurzen Funktion (neben jeweils zwei Hilfsfunktionen und einer Initialisierungsfunktion).

    Diese Beispiele sind sofort nutzbar und ohne Codeänderungen in jeder Mod einsatzbereit, da die Einstellungsmöglichkeiten über Ausdauer(oder Atem)-Verbrauch in Variablen zugänglich sind.

    Den Code lasse ich ansonsten ganz unkommentiert hier. Stattdessen möchte ich auf einen Patch verweisen, den ich damit erstellt habe. Er nutzt die drei Skripte wie hier vorgestellt ohne große Änderungen.
    Da der Code hier frei zugänglich ist und evtl. seinen Weg in eine Mod findet, habe ich im Patch darauf geachtet, das System unangetastet zu lassen, falls es bereits in einer Mod so enthalten ist.

    Link zum Releasethread vom Patch.



    (a)
    integratedSprint.d
    Spoiler:(zum lesen bitte Text markieren)
    Code:
    /*
     * integratedSprint.d
     * Source: https://forum.worldofplayers.de/forum/threads/?p=26441848
     *
     * Add sprint integrated into the internal breath (diving) system
     *
     * - Requires Ikarus 1.2.2, LeGo 2.6.0 (FrameFunctions), breath.d
     * - Compatible with Gothic 1 and Gothic 2
     *
     * Instructions
     * - Initialize from Init_Global with
     *     IntegratedSprint_Init();
     * - Set the variable IntegratedSprint_DurationMS from the scripts. This allows to dynamically change the duration. E.g.
     *     IntegratedSprint_DurationMS = 15000; // 15 seconds of total sprint
     */
    
    const string IntegratedSprint_Mds = "HUMANS_SPRINT_CLEAN.MDS"; // MDS overlay name
    var   int    IntegratedSprint_DurationMS;                      // Duration of sprint in milliseconds: Set in scripts
    
    /*
     * Check if an MDS overlay of given name is active
     */
    func int Mdl_OverlayMdsIsActive(var C_Npc slf, var string mdsName) {
        var oCNpc npc; npc = Hlp_GetNpc(slf);
        mdsName = STR_Upper(mdsName);
    
        repeat(i, npc.activeOverlays_numInArray); var int i;
            if (Hlp_StrCmp(mdsName, MEM_ReadStringArray(npc.activeOverlays_array, i))) {
                return TRUE;
            };
        end;
    
        return FALSE;
    };
    
    /*
     * Check if an NPC is running
     */
    func int Npc_IsRunning(var C_Npc slf) {
        var oCNpc npc; npc = Hlp_GetNpc(slf);
        var int aiPtr; aiPtr = npc.human_ai;
    
        if (GOTHIC_BASE_VERSION == 2) {
            const int oCAniCtrl_Human__IsRunning = 7004672; //0x6AE200
    
            const int call = 0;
            if (CALL_Begin(call)) {
                CALL_PutRetValTo(_@(ret));
                CALL__thiscall(_@(aiPtr), oCAniCtrl_Human__IsRunning);
                call = CALL_End();
            };
    
            var int ret;
            return +ret;
    
        } else {
            // Gothic 1 does not have the function oCAniCtrl_Human::IsRunning. Re-implement it here
            // These lines require at lease Ikarus 1.2.2
    
            var oCAniCtrl_Human ai; ai = _^(aiPtr);
            var int modelPtr; modelPtr = ai._zCAIPlayer_model;
    
            if (!modelPtr) {
                return FALSE;
            };
    
            // Modified from auxillary.d in GothicFreeAim
            // https://github.com/szapp/GothicFreeAim/blob/v1.2.0/_work/data/Scripts/Content/GFA/_intern/auxiliary.d#L169
            const int zCModel_numActAnis_offset = 52; //0x34
            const int zCModel_actAniList_offset = 56; //0x37
            const int zCModelAni_aniID_offset   = 76; //0x4C
            var int actAniOffset; actAniOffset = modelPtr+zCModel_actAniList_offset;
            repeat(i, MEM_ReadInt(modelPtr+zCModel_numActAnis_offset)); var int i;
                var int aniID; aniID = MEM_ReadInt(MEM_ReadInt(MEM_ReadInt(actAniOffset))+zCModelAni_aniID_offset);
                if (aniID == MEM_ReadStatArr(ai.s_runl,        npc.fmode))
                || (aniID == MEM_ReadStatArr(ai.s_runr,        npc.fmode))
                || (aniID == MEM_ReadStatArr(ai.t_run_2_runl,  npc.fmode))
                || (aniID == MEM_ReadStatArr(ai.t_runl_2_run,  npc.fmode))
                || (aniID == MEM_ReadStatArr(ai.t_runl_2_runr, npc.fmode))
                || (aniID == MEM_ReadStatArr(ai.t_runr_2_runl, npc.fmode))
                || (aniID == MEM_ReadStatArr(ai.t_runr_2_run,  npc.fmode)) {
                    return TRUE;
                };
                actAniOffset += 4;
            end;
        };
        return FALSE;
    };
    
    /*
     * Breath based sprinting. This function has to be called every single frame
     */
    func void IntegratedSprint() {
        const int THRESHOLD_MS             = 1161527296; // 3000.0f
        const int ACTION_WATERWALK         = 4;          // oCAniCtrl_Human.actionMode
        const int oCNpc__SetWeaponMode2_G1 = 6904416;    //0x695A60
        const int oCNpc__SetWeaponMode2_G2 = 7573120;    //0x738E80
    
        var oCNpc her; her = Hlp_GetNpc(hero);
        var oCAniCtrl_Human ai; ai = _^(her.human_ai);
    
        // Calculate cost per this frame (dynamically, because frame length varies and divetime might be updated)
        var int factor; factor = divf(her.divetime, mkf(IntegratedSprint_DurationMS));
        var int cost; cost = mulf(MEM_Timer.frameTimeFloat, factor);
    
        if ((MEM_KeyPressed(MEM_GetKey("keyIntSprint"))) || (MEM_KeyPressed(MEM_GetSecondaryKey("keyIntSprint"))))
        && (Breath_AvailableMsF(hero, cost))
        && (ai.actionMode < ACTION_WATERWALK) {
    
            // Decrease only while running
            if (Npc_IsRunning(hero)) {
                Breath_DecreaseMsF(hero, cost);
    
                // Apply overlay after refractory period
                if (!Mdl_OverlayMdsIsActive(hero, IntegratedSprint_Mds))
                && (Breath_AvailableMsF(hero, THRESHOLD_MS)) {
                    Mdl_ApplyOverlayMds(her, IntegratedSprint_Mds);
                };
            };
        } else if (Mdl_OverlayMdsIsActive(hero, IntegratedSprint_Mds)) {
            // Check if mid-air, copied from GothicFreeAim <http://github.com/szapp/GothicFreeAim>
            if (lf(ai._zCAIPlayer_aboveFloor, mkf(50))) {
                Mdl_RemoveOverlayMds(her, IntegratedSprint_Mds);
    
                // Fix ranged combat by re-initializing weapon mode
                if (Npc_HasReadiedRangedWeapon(hero)) {
                    var int herPtr; herPtr = _@(her);
                    var int fmode; fmode = her.fmode;
                    var int zero;
                    const int call = 0;
                    if (CALL_Begin(call)) {
                        CALL_IntParam(_@(zero));
                        CALL__thiscall(_@(herPtr), MEMINT_SwitchG1G2(oCNpc__SetWeaponMode2_G1, oCNpc__SetWeaponMode2_G2));
                        CALL_IntParam(_@(fmode));
                        CALL__thiscall(_@(herPtr), MEMINT_SwitchG1G2(oCNpc__SetWeaponMode2_G1, oCNpc__SetWeaponMode2_G2));
                        call = CALL_End();
                    };
                };
            };
        };
    };
    
    
    /*
     * Initialization function to be called from Init_Global
     */
    func void IntegratedSprint_Init() {
        // Requires LeGo FrameFunctions
        if (_LeGo_Flags & LeGo_FrameFunctions) {
            FF_ApplyOnceExtGT(IntegratedSprint, 0, -1);
        };
    };

    Ressourcen



    (b)
    integratedStamina.d
    Spoiler:(zum lesen bitte Text markieren)
    Code:
    /*
     * integratedStamina.d
     * Source: https://forum.worldofplayers.de/forum/threads/?p=26441848
     *
     * Add stamina regulation to melee fighting integrated into the internal breath (diving) system
     *
     * - Requires Ikarus 1.2.2, LeGo (HookEngine), breath.d
     * - Compatible with Gothic 1 and Gothic 2
     *
     * Instructions
     * - Initialize from Init_Global with
     *     IntegratedStamina_Init();
     * - Set the below variables somewhere from the scripts. This allows to dynamically change the percentage costs. E.g.
     *     IntegratedStamina_FIST_PARADE   = 10;
     *     IntegratedStamina_FIST_FIRSTHIT =  7;
     *     IntegratedStamina_FIST_COMBO    =  4;
     *     IntegratedStamina_2H_PARADE     = 18;
     *     IntegratedStamina_2H_FIRSTHIT   = 10;
     *     IntegratedStamina_2H_COMBO      =  8;
     *     IntegratedStamina_1H_PARADE     = 25;
     *     IntegratedStamina_1H_FIRSTHIT   = 15;
     *     IntegratedStamina_1H_COMBO      = 10;
     */
    
    const string IntegratedStamina_Mds = "HUMANS_DISABLE_MELEE.MDS"; // MDS overlay name
    var   int    IntegratedStamina_FIST_PARADE;                      // Breath cost in percent
    var   int    IntegratedStamina_FIST_FIRSTHIT;                    // Breath cost in percent
    var   int    IntegratedStamina_FIST_COMBO;                       // Breath cost in percent
    var   int    IntegratedStamina_2H_PARADE;                        // Breath cost in percent
    var   int    IntegratedStamina_2H_FIRSTHIT;                      // Breath cost in percent
    var   int    IntegratedStamina_2H_COMBO;                         // Breath cost in percent
    var   int    IntegratedStamina_1H_PARADE;                        // Breath cost in percent
    var   int    IntegratedStamina_1H_FIRSTHIT;                      // Breath cost in percent
    var   int    IntegratedStamina_1H_COMBO;                         // Breath cost in percent
    
    
    /*
     * Check if an MDS overlay of given name is active
     */
    func int Mdl_OverlayMdsIsActive(var C_Npc slf, var string mdsName) {
        var oCNpc npc; npc = Hlp_GetNpc(slf);
        mdsName = STR_Upper(mdsName);
    
        repeat(i, npc.activeOverlays_numInArray); var int i;
            if (Hlp_StrCmp(mdsName, MEM_ReadStringArray(npc.activeOverlays_array, i))) {
                return TRUE;
            };
        end;
    
        return FALSE;
    };
    
    
    /*
     * Unfortunately Gothic 1 does not have the option bShowWeaponTrails, skip the entire function call here
     */
    func void HideWeaponTrails(var int hide) {
        const int oCAniCtrl_Human__ShowWeaponTrail_G1 = 6452016; //0x627330
        const int oCAniCtrl_Human__ShowWeaponTrail_G2 = 7011952; //0x6AFE70
        var int addr; addr = MEMINT_SwitchG1G2(oCAniCtrl_Human__ShowWeaponTrail_G1, oCAniCtrl_Human__ShowWeaponTrail_G2);
        var int byte; byte = MEM_ReadByte(addr);
    
        if (hide) {
            if (byte == /*64*/100) {
                MemoryProtectionOverride(addr, 1);
                MEM_WriteByte(addr, /*C3*/195);
            };
        } else if (byte == /*C3*/195) {
            MEM_WriteByte(addr, /*64*/100);
        };
    };
    
    
    /*
     * Decrease stamina on melee fight actions
     */
    func void IntegratedStamina() {
        var oCAniCtrl_Human ai; ai = _^(ESI);
        var C_Npc slf; slf = _^(ai.npc);
    
        // For player only
        if (!Npc_IsPlayer(slf)) {
            return;
        };
    
        // Collect information about current action
        var oCNpc npc; npc = _^(ai.npc);
        var int fists; fists = (npc.fmode == FMODE_FIST);
        var int twoHanded; twoHanded = (npc.fmode == 4);
        var int start; start = ((ai.bitfield & /*endCombo*/2) != 0);
        var int firstHit; firstHit = (start) && (truncf(ai.lastHitAniFrame) < 6); // Tolerance for non-combo animations
    
        var int parade; parade = FALSE;
        if (start) {
            ai.hitAniID = EAX;
            if (ai.hitAniID != ai._t_hitl) && (ai.hitAniID != ai._t_hitr)
            && (ai.hitAniID != ai._t_hitf) && (ai.hitAniID != ai._t_hitfrun) {
                parade = TRUE;
    
                // Return if already running (only the case for long jump back parades)
                const int oCAniCtrl_Human__IsParadeRunning_G1 = 6456432; //0x628470
                const int oCAniCtrl_Human__IsParadeRunning_G2 = 7017808; //0x6B1550
                const int call = 0;
                if (CALL_Begin(call)) {
                    CALL__thiscall(_@(ESI), MEMINT_SwitchG1G2(oCAniCtrl_Human__IsParadeRunning_G1,
                                                              oCAniCtrl_Human__IsParadeRunning_G2));
                    call = CALL_End();
                };
                if (CALL_RetValAsInt()) {
                    return;
                };
            };
        };
    
        // Determine decrement/cost
        var int decr;
        if (fists) {
            if      (parade)   {  decr = IntegratedStamina_FIST_PARADE;   }
            else if (firstHit) {  decr = IntegratedStamina_FIST_FIRSTHIT; }
            else /*followHit*/ {  decr = IntegratedStamina_FIST_COMBO;    };
        } else if (twoHanded)  {
            if      (parade)   {  decr = IntegratedStamina_2H_PARADE;     }
            else if (firstHit) {  decr = IntegratedStamina_2H_FIRSTHIT;   }
            else /*followHit*/ {  decr = IntegratedStamina_2H_COMBO;      };
        } else   /*oneHanded*/ {
            if      (parade)   {  decr = IntegratedStamina_1H_PARADE;     }
            else if (firstHit) {  decr = IntegratedStamina_1H_FIRSTHIT;   }
            else /*followHit*/ {  decr = IntegratedStamina_1H_COMBO;      };
        };
    
        // Decrease breath or disable actions by animation
        if (!Breath_RequestPercent(slf, decr, decr)) {
            // End combo or disable melee
            if (!(ai.bitfield & /*endCombo*/2)) {
                ai.bitfield = ai.bitfield | /*endCombo*/2;
            } else if (!Mdl_OverlayMdsIsActive(slf, IntegratedStamina_Mds)) {
                Mdl_ApplyOverlayMds(slf, IntegratedStamina_Mds);
            };
    
            // Temporarily disable all(!) weapon trails
            HideWeaponTrails(TRUE);
    
        } else if (Mdl_OverlayMdsIsActive(slf, IntegratedStamina_Mds)) {
            // Back to normal
            Mdl_RemoveOverlayMds(slf, IntegratedStamina_Mds);
            HideWeaponTrails(FALSE);
        };
    };
    
    
    /*
     * Initialization function to be called from Init_Global
     */
    func void IntegratedStamina_Init() {
        const int oCAniCtrl_Human__StartHitCombo_G1 = 6452587; //0x62756B
        const int oCAniCtrl_Human__StartHitCombo_G2 = 7012555; //0x6B00CB
        const int oCAniCtrl_Human__HitCombo_next_G1 = 6453169; //0x6277B1
        const int oCAniCtrl_Human__HitCombo_next_G2 = 7013146; //0x6B031A
        HookEngineF(MEMINT_SwitchG1G2(oCAniCtrl_Human__StartHitCombo_G1,
                                      oCAniCtrl_Human__StartHitCombo_G2), 8, IntegratedStamina);
        HookEngineF(MEMINT_SwitchG1G2(oCAniCtrl_Human__HitCombo_next_G1,
                                      oCAniCtrl_Human__HitCombo_next_G2), 8, IntegratedStamina);
    };

    Ressourcen
    Geändert von mud-freak (01.11.2020 um 06:22 Uhr)

  3. Beiträge anzeigen #3 Zitieren
    banned
    Registriert seit
    Jan 2009
    Ort
    Oberösterreich
    Beiträge
    2.393
     
    Moe ist offline
    Super vielen Dank mud-freak!

    Ich hab damit leider ein kleines Problem:

    00:13 Fatal:-1 U: PAR: CONTENT\STORY\BREATH.D: Unknown identifier : ASMINT_OP_POPECX
    Geändert von Moe (21.06.2020 um 21:43 Uhr)

  4. Beiträge anzeigen #4 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Moe Beitrag anzeigen
    Super vielen Dank mud-freak!

    Ich hab damit leider ein kleines Problem:

    00:13 Fatal:-1 U: PAR: CONTENT\STORY\BREATH.D: Unknown identifier : ASMINT_OP_POPECX
    Das Skript erfordert die neuste Ikarusversion (1.2.2). Ich habe im Kommentarblock im Skript oben die Versionsnummer jetzt ergänzt.

  5. Beiträge anzeigen #5 Zitieren
    banned
    Registriert seit
    Jan 2009
    Ort
    Oberösterreich
    Beiträge
    2.393
     
    Moe ist offline
    Klasse, jetzt funktionierts.

    Hab mal eine Video davon gemacht:

    [Video]

    Nochmal vielen Dank mud-freak!

  6. Homepage besuchen Beiträge anzeigen #6 Zitieren
    Team Velen
    Registriert seit
    Aug 2015
    Beiträge
    952
     
    Bloodfly91 ist offline
    Auch von mir vielen Dank dafür!

    Wäre es eigentlich auch möglich, den Verbrauch der Ausdauer beim Tauchen zu manipulieren? Das ist soweit ich weiß ja in der Engine festgelegt. Ich möchte beispielsweise gerne die gesamte Ausdauer durch zwei teilen (ist mit deinem Skript ja bereits problemlos möglich), das soll dann die Zeit in Millisekunden ergeben, die man unter Wasser bleiben kann, ohne Schaden zu erleiden. Also ähnlich wie bei deinem Sprint-Skript.
    Geändert von Bloodfly91 (22.06.2020 um 09:18 Uhr)

  7. Beiträge anzeigen #7 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Bloodfly91 Beitrag anzeigen
    Wäre es eigentlich auch möglich, den Verbrauch der Ausdauer beim Tauchen zu manipulieren? Das ist soweit ich weiß ja in der Engine festgelegt. Ich möchte beispielsweise gerne die gesamte Ausdauer durch zwei teilen (ist mit deinem Skript ja bereits problemlos möglich), das soll dann die Zeit in Millisekunden ergeben, die man unter Wasser bleiben kann, ohne Schaden zu erleiden. Also ähnlich wie bei deinem Sprint-Skript.
    Ich verstehe deine Absicht nicht ganz. Das hört sich an als hättest du gerade deine eigene Frage beantwortet. Wenn du den maximalen Atem halbierst (von 30 zu 15 Sekunden), dann kann der Spieler nur noch 15 Sekunden tauchen. Da der Spieler diese Zahl nie zu Gesicht bekommt (es sei denn du hast vor sie z.B. im Charaktermenü anzeigen zu lassen), kannst du alles andere einfach entsprechend skalieren.
    Wahrscheinlich habe ich dich nicht vollständig verstanden. Könntest du das bitte noch etwas näher beschreiben mit konkreten Zahlen-Beispielen usw.?

  8. Homepage besuchen Beiträge anzeigen #8 Zitieren
    Team Velen
    Registriert seit
    Aug 2015
    Beiträge
    952
     
    Bloodfly91 ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Ich verstehe deine Absicht nicht ganz. Das hört sich an als hättest du gerade deine eigene Frage beantwortet. Wenn du den maximalen Atem halbierst (von 30 zu 15 Sekunden), dann kann der Spieler nur noch 15 Sekunden tauchen. Da der Spieler diese Zahl nie zu Gesicht bekommt (es sei denn du hast vor sie z.B. im Charaktermenü anzeigen zu lassen), kannst du alles andere einfach entsprechend skalieren.
    Wahrscheinlich habe ich dich nicht vollständig verstanden. Könntest du das bitte noch etwas näher beschreiben mit konkreten Zahlen-Beispielen usw.?
    Ja, du hast natürlich völlig recht. Ich hätte mir die Funktionsweise des Skripts erst mal genau anschauen sollen...
    Das darstellen im Charaktermenü war auch überhaupt kein Problem.

  9. Beiträge anzeigen #9 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Kleine Notiz: Das Integrated Stamina Skript funktioniert momentan nicht richtig für Gothic 1. Dort wird der "erste" Schlag nicht richtig ermittelt und das Animationsoverlay ist fehlerhaft. Das werde ich bei Zeiten beheben. Allerdings weiß ich noch nicht wann ich dazu komme. Das nur als Hinweis, falls jemand dieses Skript verwendet.

  10. Beiträge anzeigen #10 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Ich habe alle drei hier geposteten Skripte überarbeitet.

    In breath.d gab es nur Kleinigkeiten, in integratedSprint.d habe ich das Problem behoben, dass man mit gezogener Fernkampfwaffe nach dem Sprinten nicht schießen kann (dabei habe ich mich an dem Fix aus DirtySwamp von Draxes und Cryp18Struct orientiert - ich weiß aber nicht wo der ursprünglich herkommt), und die integratedStamina.d inkl. der Ressourcen habe ich aktualisiert, damit es nun auch in Gothic 1 vernünftig funktioniert.

  11. Beiträge anzeigen #11 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Ich habe das Skript vom integratedSprint aktualisiert.

    Nun wird eine gezogene Fernkampfwaffe nicht automatisch nach dem Sprinten weggesteckt, sondern ist direkt weiterhin nutzbar. Außerdem wird Springen und Fallen nicht mehr durch die Sprinttaste unterbrochen.

    Falls jemand dieses Skript in seiner Mod eingebunden hat, empfehle ich es zu aktualisieren.

  12. Beiträge anzeigen #12 Zitieren
    Ehrengarde Avatar von neocromicon
    Registriert seit
    Jan 2019
    Beiträge
    2.561
     
    neocromicon ist offline
    Hi MudFreak,

    da ich die selben Bugs in meinem Patch hatte, habe ich direkt mal die gelegenheit genutzt und sie bei mir mit eingebaut + ein paar schönheits Operationen

    Einen Fehler bekomme ich aber nicht behoben, wenn der Spieler tot ist und man Sprinten will, wird die Animation kurz gestartet, gleich darauf legt er sich aber wieder tot hin. Genau das will ich verhindern, bloß habe ich keine Ahnung welche Funktion ich dafür abfragen muss. Ich habe bis heute keine Funktions Referenz oder der gleichen finden können.

    Weißt du wo es sowas gibt? Das würde mir das Leben 1000x vereinfachen

    Ansonsten schau mal bei dir selber auch nach ob du den Bug hast, ich konnte jetzt nix eindeutiges in deinem Code finden.

    Mfg

  13. Beiträge anzeigen #13 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Ich habe eine kleinen Flüchtigkeitsfehler im integratedSprint-Skript behoben. Nun kann man auch nach Sprinten mit gezogenenr Armbrust diese weiterhin nutzen.

  14. Beiträge anzeigen #14 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Eigentlich hatte ich das dort wohl tatsächlich abgefragt. Jetzt habe ich dort aber einen kleinen Zahlendreher entdeckt. Der sollte unproblematisch sein und würde nur dafür sorgen, dass der Tauchbalken weiterhin dauerhaft angezeigt bliebe, würde man die Sichtbarkeit des Balkens während des Spiels ändern. Das werde ich korrigieren.
    Das Skript im Einleitungspost (breath.d) ist aktualisiert.

  15. Beiträge anzeigen #15 Zitieren
    banned
    Registriert seit
    Jan 2009
    Ort
    Oberösterreich
    Beiträge
    2.393
     
    Moe ist offline
    Hallo mud-freak,

    die Abfrage bei func int Mdl_OverlayMdsIsActive greift leider nicht auf Mdl_ApplyOverlayMdsTimed zu, hast du dafür vielleicht bitte eine Lösung? Ich brauche diese Abfrage nämlich, weil ich Mdl_ApplyOverlayMdsTimed bei alkoholischen Getränken verwende, wo ich die Humans_Drunken.mds für bestimmte Zeit aktiviere und während diese aktiv ist, soll der Spieler nicht sprinten können.



    LG Moe

  16. Beiträge anzeigen #16 Zitieren
    Local Hero
    Registriert seit
    Feb 2017
    Beiträge
    270
     
    F a w k e s ist offline
    Hello Moe, try this function: NPC_HasTimedOverlay (var int slfInstance, var string testOverlay)
    https://github.com/auronen/AF-Script...ons_NPC.d#L241
    Hope this helps

  17. Beiträge anzeigen #17 Zitieren
    banned
    Registriert seit
    Jan 2009
    Ort
    Oberösterreich
    Beiträge
    2.393
     
    Moe ist offline
    Zitat Zitat von F a w k e s Beitrag anzeigen
    Hello Moe, try this function: NPC_HasTimedOverlay (var int slfInstance, var string testOverlay)
    https://github.com/auronen/AF-Script...ons_NPC.d#L241
    Hope this helps
    Thanks F a w k e s but I get a Syntax error : OCNPCTIMEDOVERLAY

  18. Beiträge anzeigen #18 Zitieren
    Local Hero
    Registriert seit
    Feb 2017
    Beiträge
    270
     
    F a w k e s ist offline
    Ah, right, sorry about that, class definition here https://github.com/auronen/AF-Script...sses_G2/oNpc.d +
    Code:
    class oCNpcTimedOverlay {
        var string mdsOverlayName;    //0    zSTRING mdsOverlayName;
        var int timer;            //4    float timer; 
    };

  19. Beiträge anzeigen #19 Zitieren
    banned
    Registriert seit
    Jan 2009
    Ort
    Oberösterreich
    Beiträge
    2.393
     
    Moe ist offline
    Thank you F a w k e s. It works like a charm.

  20. Homepage besuchen Beiträge anzeigen #20 Zitieren
    Local Hero Avatar von Damianut
    Registriert seit
    Jul 2021
    Ort
    Poland
    Beiträge
    282
     
    Damianut ist offline
    Code:
    var int start; start = ((ai.bitfield & /*endCombo*/2) != 0);
    It's give false when it's first melee action after game loading. And as a result it gives false for "parade" variable, when player do parade.

    I'm talking about "integratedStamina.d".

Seite 1 von 2 12 Letzte »

Berechtigungen

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