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 2 von 2
  1. Beiträge anzeigen #1 Zitieren
    Local Hero
    Registriert seit
    Feb 2017
    Beiträge
    270
     
    F a w k e s ist offline

    G1/G2 Telekinesis

    Opening new thread - as a continuation of this original post: link

    Hello folks,
    As per several suggestions from mud-freak I have adjusted script for telekinesis spell below:
    SPL_SENDCAST is now used to remove scroll properly from inventory.
    Item was previously moved towards player via frame-based function (with higher FPS item moved faster) - now item is moved directly by Spell_Logic_ function itself, speed should be consistent now.

    Bloodfly91 did some testing on G2A (I am using only G1) and found out 3 things:
    1. Items were not focusable. By default in G2A Focus_Magic.item_prio value is set to -1. (disabled)
    2. Mana was not consumed.
    3. Sound effects were missing. (I forgot to copy-paste G1 values, added below)
    1. & 2. Issues fixed after I looked into mud-freaks PickLock spell (link) and basically copied his solution.

    If you want to try this spell by yourself - you will need to adjust several files:
    File PFXMagic.d
    (Copy-Paste from G1 file)
    Code:
    ///                                                       XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    ///                                                       XX  T E L E K I N E S I S  XX
    ///                                                       XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    
    INSTANCE MFX_TELEKINESIS_INIT (C_PARTICLEFX)
    {
         ppsvalue = 600.000000000;
         ppsscalekeys_s = "1";
         ppsislooping = 1;
         ppsissmooth = 1;
         ppsfps = 10.000000000;
         shptype_s = "SPHERE";
         shpfor_s = "object";
         shpoffsetvec_s = "0 0 0";
         shpdistribtype_s = "RAND";
         shpdim_s = "10";
         shpscalekeys_s = "1";
         shpscaleislooping = 1;
         shpscaleissmooth = 1;
         shpscalefps = 2.000000000;
         dirmode_s = "TARGET";
         dirfor_s = "object";
         dirmodetargetfor_s = "OBJECT";
         dirmodetargetpos_s = "0 0 0";
         dirangleheadvar = 180.000000000;
         dirangleelevvar = 180.000000000;
         velavg = 0.001000000;
         lsppartavg = 150.000000000;
         flygravity_s = "0 0 0";
         flycolldet_b = 0;
         visname_s = "MFX_SLEEP_STAR.TGA";
         visorientation_s = "NONE";
         vistexisquadpoly = 1;
         vistexanifps = 18.000000000;
         vistexaniislooping = 1;
         vistexcolorstart_s = "100 255 255";
         vistexcolorend_s = "255 255 0";
         vissizestart_s = "10 10";
         vissizeendscale = 0.010000000;
         visalphafunc_s = "ADD";
         visalphastart = 255.000000000;
         visalphaend = 255.000000000;
    };
    
    INSTANCE MFX_TELEKINESIS_BRIDGE (C_PARTICLEFX)
    {
         ppsvalue = 250.000000000;
         ppsscalekeys_s = "1 2 3 2";
         ppsislooping = 1;
         ppsissmooth = 1;
         ppsfps = 2.000000000;
         shptype_s = "SPHERE";
         shpfor_s = "WORLD";
         shpoffsetvec_s = "0 0 0";
         shpdistribtype_s = "RAND";
         shpdim_s = "4";
         shpscalekeys_s = "1 3 5 3 1";
         shpscaleislooping = 1;
         shpscaleissmooth = 1;
         shpscalefps = 2.000000000;
         dirmode_s = "TARGET";
         dirfor_s = "WORLD";
         dirmodetargetfor_s = "WORLD";
         dirmodetargetpos_s = "0 0 0";
         dirangleheadvar = 180.000000000;
         dirangleelevvar = 180.000000000;
         velavg = 0.800000012;
         velvar = 0.200000003;
         lsppartavg = 500.000000000;
         flygravity_s = "0 0 0";
         visname_s = "MFX_SLEEP_STAR.TGA";
         visorientation_s = "NONE";
         vistexisquadpoly = 1;
         vistexanifps = 18.000000000;
         vistexaniislooping = 1;
         vistexcolorstart_s = "100 255 255";
         vistexcolorend_s = "100 255 255";
         vissizestart_s = "10 10";
         vissizeendscale = 2.000000000;
         visalphafunc_s = "ADD";
         visalphastart = 255.000000000;
         trltexture_s = "WHITECLOUD.TGA";
    };
    
    INSTANCE MFX_TELEKINESIS_TARGET (C_PARTICLEFX)
    {
         ppsvalue = 1500.000000000;
         ppsislooping = 1;
         shptype_s = "SPHERE";
         shpfor_s = "object";
         shpdistribtype_s = "RAND";
         shpdistribwalkspeed = 0.000300000;
         shpdim_s = "40";
         dirmode_s = "RAND";
         dirfor_s = "OBJECT";
         dirmodetargetfor_s = "OBJECT";
         velavg = 0.001000000;
         lsppartavg = 200.000000000;
         flygravity_s = "0";
         visname_s = "MFX_SLEEP_STAR.TGA";
         visorientation_s = "VELO";
         vistexisquadpoly = 1;
         vistexanifps = 10.000000000;
         vistexaniislooping = 1;
         vistexcolorstart_s = "255 255 255";
         vistexcolorend_s = "100 255 255";
         vissizestart_s = "30 30";
         vissizeendscale = 1.000000000;
         visalphafunc_s = "ADD";
         visalphaend = 255.000000000;
    };
    
    INSTANCE MFX_TELEKINESIS_TARGETEND (C_PARTICLEFX)
    {
         ppsvalue = 1500.000000000;
         ppsscalekeys_s = "1";
         ppsfps = 10.000000000;
         shptype_s = "SPHERE";
         shpfor_s = "object";
         shpdistribtype_s = "RAND";
         shpdistribwalkspeed = 0.000300000;
         shpdim_s = "40";
         dirmode_s = "RAND";
         dirfor_s = "OBJECT";
         dirmodetargetfor_s = "OBJECT";
         velavg = 0.050000001;
         lsppartavg = 2000.000000000;
         flygravity_s = "0 -0.0008 0";
         flycolldet_b = 1;
         visname_s = "MFX_SLEEP_STAR.TGA";
         visorientation_s = "VELO";
         vistexisquadpoly = 1;
         vistexanifps = 10.000000000;
         vistexaniislooping = 1;
         vistexcolorstart_s = "200 255 255";
         vistexcolorend_s = "100 255 255";
         vissizestart_s = "30 30";
         vissizeendscale = 1.000000000;
         visalphafunc_s = "ADD";
         visalphastart = 255.000000000;
    };
    Sound effects SFXInst.d
    (Copy-Paste from G1 file)
    Code:
    /* Telekinese*/    INSTANCE MFX_Telekinesis_StartInvest    (C_SFX_DEF) {file= "MFX_Telekinesis_Cast.wav";        vol = 127; };
            INSTANCE MFX_Telekinesis_Invest        (C_SFX_DEF) {file= "MFX_Telekinesis_Bridge.wav";    vol = 127;    loop = 1; };
    File Visualfxinst.d
    (Copy paste of G1 spellFX_telekinesis instance, only suffix 'New' added)
    Code:
    INSTANCE spellFX_telekinesisNew(CFx_Base_Proto)
    {
            visname_S         = "MFX_Telekinesis_INIT";
            emtrjmode_s         = "TARGET";
            emTrjOriginNode     = "ZS_RIGHTHAND";
            emtrjnumkeys         = 2;
            emtrjnumkeysvar     = 1;
            emtrjangleelevvar     = 2.;
            emtrjangleheadvar     = 0.;
            emtrjeasefunc_s     = "LINEAR";
            emtrjloopmode_s     = "HALT";
            emtrjdynupdatedelay    = 0.;
            emFXInvestOrigin_S     = "spellFX_telekinesisNew_ORIGIN";
            //emFXInvestTarget_S     = "spellFX_telekinesisNew_TARGET";
            //lightPresetname     = "POISON";
            emTrjTargetRange    = 0;
            emTrjTargetElev     = 0;
    };
    
    INSTANCE spellFX_telekinesisNew_KEY_INIT (C_ParticleFXEmitKey)
    {
            visname_s        = "MFX_Telekinesis_INIT";
            emtrjeasevel          = 0.01;
    };
    
    INSTANCE spellFX_telekinesisNew_KEY_INVEST_1 (C_ParticleFXEmitKey)
    {
            visname_s        = "MFX_Telekinesis_TARGET";
            emtrjeasevel          = 2000;
            sfxid            = "MFX_TELEKINESIS_STARTINVEST";
            sfxisambient        = 1;
    };
    
    INSTANCE spellFX_telekinesisNew_KEY_CAST (C_ParticleFXEmitKey)
    {
            visname_s        = "MFX_Telekinesis_TargetEnd";       
    };
    
    /*INSTANCE spellFX_telekinesisNew_KEY_STOP (C_ParticleFXEmitKey)
    {
            visname_s            = "MFX_Telekinesis_TargetEnd";
    };*/
    
    INSTANCE spellFX_telekinesisNew_Origin (CFx_Base_Proto)
    {
            visname_S         = "MFX_Telekinesis_BRIDGE";
            emtrjmode_s         = "TARGET LINE";
            emtrjeasevel          = 0.001;
            emTrjOriginNode     = "BIP01 R Hand";
            emtrjdynupdatedelay    = 0.;
            lightPresetname     = "AURA";
            sfxid            = "MFX_TELEKINESIS_INVEST";
            sfxisambient        = 1;
    };
    File magic_intern.d
    (add new spell number, extend MAX_SPELL number)
    Code:
    ...
        const int SPL_TELEKINESISNEW            = 83;
        const int MAX_SPELL = 84;
    File text.d
    (add new spell name)
    Code:
    const string TXT_SPELLS[MAX_SPELL] =
    { 
    ...,
        "Telekinesis"
    };
    File constants.d
    (add new spell instance & ani name)
    Code:
    const string spellFxInstanceNames[MAX_SPELL] =
    {
    ...,
        "TelekinesisNew
    };
    
    const string spellFxAniLetters[MAX_SPELL] =
    {
    ...,
        "TEL"
    };
    Global variables used by new Telekinesis:
    Code:
    //--- telekinesis
    var int vobTKItemPtr;            //item pointer
    var int vobTKMana;        //required for artificial increase of mana (see below)
    var int vobTKStage;        //stage (first we will elevate item, then we will move it towards player)
    var int vobTKStartPos[3];    //starting position of item
        const int cvobTKStageMaxElevation    = 45;
    Scroll item instance:
    (I have custom visual, you might have to change to some default model)
    Code:
    INSTANCE ItArScrollTKNew (C_Item)
    {
        name        = NAME_Spruchrolle;
        mainflag    = ITEM_KAT_RUNE;
        Flags        = ITEM_MULTI;
        value        = 100;
    
        visual        = "ItAr_Scroll_Telekinesis.3DS";
        
        material    = MAT_STONE;
        spell        = SPL_TELEKINESISNEW;
    
        cond_atr [2]    = ATR_MANA;
        cond_value [2]    = 10;
        
        description    = "Telekinesis";
        text [1]    = NAME_ManaPerSec;    count [1] = 1;
        text [2]    = NAME_Mana_Needed;    count [2] = cond_value [2];
        text [4]    = NAME_Spell_Invest;
        text [5]    = NAME_Value;        count [5] = value;
    };
    In order to move item towards player - we need to understand how movement works in 3D space.
    I wrote below functions which work with points & vectors after I read this Introduction to vectors - it was pretty easy reading with great practical examples.

    If you are same as me - new to 3D world - I recommend to read this, in ~30 mins you should be done and blown away how easy it is, when math is applied!
    https://www.khanacademy.org/computin...tro-to-vectors

    Code:
    //copy vector v1 to v2
    //v1 - pointer to vector 1
    //v2 - pointer to vector 2
    FUNC VOID copyVector (var int v1, var int v2)
    {
        MEM_CopyBytes (v1, v2, 12);
    };
    
    //adds vector v2 to v1
    //v - output pointer
    //v1 - pointer to vector 1
    //v2 - pointer to vector 2
    FUNC VOID addVectors (var int v, var int v1, var int v2)
    {
        MEM_WriteIntArray (v, 0, addf(MEM_ReadIntArray(v1,  0), MEM_ReadIntArray(v2,  0)));
        MEM_WriteIntArray (v, 1, addf(MEM_ReadIntArray(v1,  1), MEM_ReadIntArray(v2,  1)));
        MEM_WriteIntArray (v, 2, addf(MEM_ReadIntArray(v1,  2), MEM_ReadIntArray(v2,  2)));
    };
    
    //subtracts vector v2 from v1
    //v - output pointer
    //v1 - pointer to vector 1
    //v2 - pointer to vector 2
    FUNC VOID subVectors (var int v, var int v1, var int v2)
    {
        MEM_WriteIntArray (v, 0, subf(MEM_ReadIntArray(v1,  0), MEM_ReadIntArray(v2,  0)));
        MEM_WriteIntArray (v, 1, subf(MEM_ReadIntArray(v1,  1), MEM_ReadIntArray(v2,  1)));
        MEM_WriteIntArray (v, 2, subf(MEM_ReadIntArray(v1,  2), MEM_ReadIntArray(v2,  2)));
    };
    
    //scale the vector with division
    //v1 - pointer to vector
    //m - division scalar
    FUNC VOID divVector (var int v1, var int d)
    {
        if (!d) { return; };
        MEM_WriteIntArray (v1, 0, divf(MEM_ReadIntArray(v1,  0), d));
        MEM_WriteIntArray (v1, 1, divf(MEM_ReadIntArray(v1,  1), d));
        MEM_WriteIntArray (v1, 2, divf(MEM_ReadIntArray(v1,  2), d));
    };
    
    //scale the vector with multiplication
    //v1 - pointer to vector
    //m - multiplication scalar
    FUNC VOID mulVector (var int v1, var int m)
    {
        MEM_WriteIntArray (v1, 0, mulf(MEM_ReadIntArray(v1,  0), m));
        MEM_WriteIntArray (v1, 1, mulf(MEM_ReadIntArray(v1,  1), m));
        MEM_WriteIntArray (v1, 2, mulf(MEM_ReadIntArray(v1,  2), m));
    };
    
    //returns magnitude of a vector
    //v1 - pointer to vector
    FUNC INT magVector (var int v1)
    {
        return sqrtf (addf (addf (sqrf (MEM_ReadIntArray(v1,  0)), sqrf (MEM_ReadIntArray(v1,  1))), sqrf (MEM_ReadIntArray(v1,  2))));
    };
    
    //normalize vector
    //v - output pointer
    //v1 - pointer to vector 1
    FUNC VOID normalizeVector (var int v1)
    {
        var int m; m = magVector (v1);
        if (!m) { return; };
        divVector (v1, m);
    };
    Functions for getting/setting object position in the world, applying physics, adding velocity:
    Code:
    //Get Vob position (zVec3)
    FUNC INT zCVob_GetPositionWorld (var int vobPtr)
    {
        //0051B3C0  .text     Debug data           ?GetPositionWorld@zCVob@@QBE?AVzVEC3@@XZ
        const int zCVob__GetPositionWorld_G1 = 5354432;
    
        //005EE380  .text     Debug data           ?GetPositionWorld@zCVob@@QBEXAAM00@Z
        //const int zCVob__GetPositionWorld = 6218624;
    
        //0x0052DC90 public: class zVEC3 __thiscall zCVob::GetPositionWorld(void)const 
        const int zCVob__GetPositionWorld_G2 = 5430416;
        
        //void __thiscall zCVob::GetPositionWorld(float & float & float &)const
    
        if (!vobPtr) { return 0; };
        
        CALL_RetValIsStruct (12);
        CALL__thiscall (vobPtr, MEMINT_SwitchG1G2 (zCVob__GetPositionWorld_G1, zCVob__GetPositionWorld_G1));
        return CALL_RetValAsPtr ();    //crash ak zavolane bez CALL_RetValIsStruct
    };
    
    //Set Vob position (zVec3)
    FUNC VOID zCVob_SetPositionWorld (var int vobPtr, var int vecPtr)
    {
        //005EE650  .text     Debug data           ?SetPositionWorld@zCVob@@QAEXABVzVEC3@@@Z
        const int zCVob__SetPositionWorld_G1 = 6219344;
        
        //0x0061BB70 public: void __thiscall zCVob::SetPositionWorld(class zVEC3 const &)
        const int zCVob__SetPositionWorld_G2 = 6404976;
    
        if (!vobPtr) || (!vecPtr) { return; };
    
        var zCVob Vob;
        Vob = _^ (vobPtr);
    
        CALL_PtrParam (vecPtr);
        CALL__thiscall (vobPtr, MEMINT_SwitchG1G2 (zCVob__SetPositionWorld_G1, zCVob__SetPositionWorld_G2));
    };
    
    //Enable/disable physics on Vob
    FUNC VOID zCVob_SetPhysicsEnabled (var int vobPtr, var int enabled)
    {
        //005EFC20  .text     Debug data           ?SetPhysicsEnabled@zCVob@@QAEXH@Z
        const int zCVob__SetPhysicsEnabled_G1 = 6224928;
    
        //0x0061D190 public: void __thiscall zCVob::SetPhysicsEnabled(int)
        const int zCVob__SetPhysicsEnabled_G2 = 6410640;
        
        if (!vobPtr) { return; };
    
        CALL_IntParam (enabled);
        CALL__thiscall (vobPtr, MEMINT_SwitchG1G2 (zCVob__SetPhysicsEnabled_G1, zCVob__SetPhysicsEnabled_G2));    
    };
    
    //Enable/disable Vob sleeping mode
    FUNC VOID zCVob_SetSleeping (var int vobPtr, var int sleeping)
    {
        //005D7250  .text     Debug data           ?SetSleeping@zCVob@@QAEXH@Z
        const int zCVob__SetSleeping_G1 = 6124112;
        
        //0x00602930 public: void __thiscall zCVob::SetSleeping(int)
        const int zCVob__SetSleeping_G2 = 6302000;
        
        if (!vobPtr) { return; };
    
        CALL_IntParam (sleeping);
        CALL__thiscall (vobPtr, MEMINT_SwitchG1G2 (zCVob__SetSleeping_G1, zCVob__SetSleeping_G2));
    };
    
    //Get Vob RigidBody
    FUNC INT zCVob_GetRigidBody (var int vobPtr)
    {
        //005D37A0  .text     Debug data           ?GetRigidBody@zCVob@@QAEPAVzCRigidBody@@XZ
        const int zCVob__GetRigidBody_G1 = 6109088;
    
        //0x005FE960 public: class zCRigidBody * __thiscall zCVob::GetRigidBody(void)
        const int zCVob__GetRigidBody_G2 = 6285664;
    
        if (!vobPtr) { return 0; };
    
        CALL__thiscall (vobPtr, MEMINT_SwitchG1G2 (zCVob__GetRigidBody_G1, zCVob__GetRigidBody_G2));
    
        return CALL_RetValAsPtr ();
    };
    
    //Add velocity to RigidBody
    FUNC VOID zCRigidBody_SetVelocity (var int rigidBodyPtr, var int vecPtr)
    {
        //00595380  .text     Debug data           ?SetVelocity@zCRigidBody@@QAEXABVzVEC3@@@Z
        const int zCRigidBody__SetVelocity_G1 = 5854080;
    
        //0x005B66D0 public: void __thiscall zCRigidBody::SetVelocity(class zVEC3 const &)
        const int zCRigidBody__SetVelocity_G2 = 5990096;
        
        if (!rigidBodyPtr) || (!vecPtr) { return; };
    
        CALL_PtrParam (vecPtr);
        CALL__thiscall (rigidBodyPtr, MEMINT_SwitchG1G2 (zCRigidBody__SetVelocity_G1, zCRigidBody__SetVelocity_G2));
    };
    File Spell_Logic_TelekinesisNew.d
    Spell Instance, Spell Logic
    (!I assume that only Player will be using this magic spell - so I am using hero instance when referring to caster.!)
    Code:
    instance Spell_TelekinesisNew (C_Spell_Proto)
    {
        time_per_mana            = 50;
        spellType            = SPELL_NEUTRAL;
        targetCollectAlgo        = TARGET_COLLECT_FOCUS;
        targetCollectType        = TARGET_TYPE_ITEMS; //Collect prio - items
        canTurnDuringInvest        = FALSE;
        canChangeTargetDuringInvest    = FALSE;
    };
    
    FUNC INT Spell_Logic_TelekinesisNewStop ()
    {
        var oCNPC npc;
        npc = Hlp_GetNPC (hero);
        
        if (vobTKItemPtr) {
            zCVob_SetPhysicsEnabled (vobTKItemPtr, 1);
            zCVob_SetSleeping (vobTKItemPtr, 0);
            
            var int rigidBodyPtr;
            rigidBodyPtr = zCVob_GetRigidBody (vobTKItemPtr);
            
            var int vecVel[3];
            vecVel[0] = mkf(Hlp_Random (50));    //X random 'bump'
            vecVel[1] = mkf(300);            //Y solid 'bump' upwards
            vecVel[2] = mkf(Hlp_Random (50));    //Z random 'bump'
            
            zCRigidBody_SetVelocity (rigidBodyPtr, _@ (vecVel));
        };
        
        npc.focus_vob = 0;
    
        vobTKItemPtr = 0;
        
        return SPL_SENDCAST;
    };
    
    FUNC INT Spell_Logic_TelekinesisNew (var int manaInvested)
    {
        var int speed;
    
        var int posSelf[3];    //position of hero
        var int posItem[3];    //position of item
        var int dir[3];        //direction - item to hero
    
        if (!vobTKItemPtr) {
            var oCNPC npc;
            npc = Hlp_GetNPC (hero);
    
            var int ptr;
            ptr = npc.focus_vob;
    
            if (Hlp_Is_ocItem (ptr)) {
                vobTKItemPtr = ptr;
            } else {
                if (Hlp_Is_oCMobSwitch (ptr)) {
                    B_Msg_Add ("yes");
                    return SPL_DONTINVEST;
                } else
                {
                    vobTKItemPtr = 0;
                    return SPL_DONTINVEST;
                };
            };
            
            vobTKStage = 0;
        };
    
    //--- Mana consumption vs smooth movement - 1 point of mana is consumed per 1 movement of item. With this code we can 'convert' 1 point of mana to 10 movements of item
        vobTKMana += 1;
        if (vobTKMana < 11) {
            self.attribute [ATR_MANA] += 1;
        } else {
            vobTKMana = 0;
        };
    //---
        
        if (GOTHIC_BASE_VERSION == 2) {
            self.attribute[ATR_MANA] -= 1;
        };
    
        //get position of hero
        copyVector (zCVob_GetPositionWorld (_@ (hero)), _@ (posSelf));
        
        //get position of item
        copyVector (zCVob_GetPositionWorld (vobTKItemPtr), _@ (posItem));
        
        //subtract pos hero from pos item - to get 'direction vector'
        subVectors (_@(dir), _@ (posItem), _@ (posSelf));
        
        //check vector magnitude (distance) of 'direction vector' - if we are too close to player - stop magic
        if (lf (magVector (_@ (dir)), mkf (250))) {
            var int retVal; retVal = Spell_Logic_TelekinesisNewStop ();
            return SPL_SENDCAST;
        };
        
        //Telekinesis stage - adds 1 to vobTKStage every cycle
        if (vobTKStage < cvobTKStageMaxElevation) {
            vobTKStage += 1;
        };
        
        //First move item upwards - to startpos + Y 90 (only first cvobTKStageMaxElevation cycles)
        if (vobTKStage < cvobTKStageMaxElevation) {
            speed = mkf (3);
            
            //Remember starting position
            if (vobTKStage == 1) {
                //copy position of item to vobTKStartPos
                copyVector (_@ (posItem), _@ (vobTKStartPos));
                
                //Y move upwards
                vobTKStartPos [1] = addf (vobTKStartPos [1], mkf (90));
            };
            
            //Get direction vector - posItem to posItem elevated
            subVectors (_@ (dir), _@ (posItem), _@ (vobTKStartPos));
        } else
        {
            speed = mkf (10);
            
            //Get direction vector - posItem to posSelf
            subVectors (_@ (dir), _@ (posItem), _@ (posSelf));
        };
        
        //normalize vector
        normalizeVector (_@ (dir));
        
        //multiply by 'speed'
        mulVector (_@ (dir), speed);
    
    
        //move position in required direction (subtract posItem by normalized dir vector - will get us closer to NPC)
        subVectors (_@ (posItem), _@ (posItem), _@ (dir));
        
        //position vob to new position 
        zCVob_SetPositionWorld (vobTKItemPtr, _@ (posItem));
    
        return SPL_NEXTLEVEL; //SPL_RECEIVEINVEST;
    };
    
    FUNC VOID Spell_Cast_TelekinesisNew(var int spellLevel) {};
    File Spell_ProcessMana_Release.d
    Add new spell entry
    Code:
    FUNC INT Spell_ProcessMana_Release (VAR INT manaInvested)
    {
        ...
    
        if (NPC_GetActiveSpell (self) == SPL_TELEKINESISNEW)
        {
            return Spell_Logic_TelekinesisNewStop ();
        };
    
        return FALSE;
    };
    File Spell_ProcessMana_Release.d
    Add new spell entry
    Code:
    FUNC INT Spell_ProcessMana (VAR INT manaInvested)
    {
        ...
        
        if (NPC_GetActiveSpell (self) == SPL_TELEKINESISNEW)
        {
            return Spell_Logic_TelekinesisNew (manaInvested);
        };
        return FALSE;
    };
    Hooked function oCSpell__Setup - taken from mud-freaks PickLock spell link
    (original function name Spell_PickLock_Prio - I changed name here to _HOOK_SPELL_SETUP)
    Code:
    /*
     * Enable focusing mob/item when using the spell
     */
    func void _HOOK_SPELL_SETUP () {
    //---
    // SPL_PickLock
        var int caster; caster = MEM_ReadInt(ESP+4);
        if (!caster) {
            return;
        };
        var C_Npc slf; slf = _^(caster);
        if (!Npc_IsPlayer(slf)) {
            return;
        };
    
        const int mob_prio_backup = 42; // 42 == not initialized yet
        if (mob_prio_backup == 42) {
            mob_prio_backup = Focus_Magic.mob_prio;
        };
    
        var int spellID; spellID = MEM_ReadInt(/*oCSpell*/ECX+/*spellID*/84);
        if (spellID == SPL_PickLock) {
            // Adjust the global(!) focus priorities temporarily(!)
            Focus_Magic.mob_prio = 1;
        } else if (mob_prio_backup != 42) {
            // Reset the focus priorities for all other spells!
            Focus_Magic.mob_prio = mob_prio_backup;
        };
    
    //--- 
    // Telekinesis - enable focusing items when using spell
    
        const int item_prio_backup = 42;
    
        if (item_prio_backup == 42) {
            item_prio_backup = Focus_Magic.item_prio;
        };
        
        if (spellID == SPL_TELEKINESISNEW) {
            Focus_Magic.item_prio = 1;
        } else if (item_prio_backup != 42) {
            Focus_Magic.item_prio = item_prio_backup;
        };
    //---
    };
    
    const int oCSpell__Setup_G1                = 4703664; //0x47C5B0
    const int oCSpell__Setup_G2                = 4737328; //0x484930
    
    HookEngineF(+MEMINT_SwitchG1G2(oCSpell__Setup_G1, oCSpell__Setup_G2), 7, _HOOK_SPELL_SETUP);
    @mud-freak hope you can help me further - I tried to play around with casting spell on focused oCMobSwitch - but ended up always with t_CastFail animation and Spell_Logic_ functions were never called.

    Is there maybe another additional check of target that needs to be hooked? From your PickLock spell I understand you are hooking address 47DBC4, to see if target is Hlp_Is_oCMobLockable. Did you figure out you need to hook here by using IDA ? Do I need to hook also IsValidTarget function ?
    Code:
    0047DBC0  .text     Debug data           ?IsTargetTypeValid@oCSpell@@SAHPAVzCVob@@H@Z
    0047DD80  .text     Debug data           ?IsValidTarget@oCSpell@@QAEHPAVzCVob@@@Z
    One last question - number 42 - is it meaning of life ref, or is there a reason behind for this specific number?
    Geändert von F a w k e s (06.01.2020 um 02:23 Uhr) Grund: updating Spell_TelekinesisNew.time_per_mana value to 50

  2. Beiträge anzeigen #2 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist gerade online
    Forgive me that I didn't have time to play around with your scripts, but I will add some notes anyway.
    1. You can rename the function "Spell_Logic_TelekinesisNewStop" to "Spell_Cast_TelekinesisNew". In Gothic 2 it will then be called automatically on SPL_SENDCAST and you can change "Spell_ProcessMana_Release" to just return SPL_SENDCAST - at least for the Gothic 2 version of the script, for Gothic 1 you can add the GOTHIC_BASE_VERSION == 1 condition to explicitly call the function before SPL_SENDCAST. This would be more in line with how spells are designed in Gothic 2 (and you may avoid strange bugs).
    2. You sould probably not use SPL_NEXTLEVEL at the end of the logic loop (=every time), because you only have one effect level (L1) in the VFX script. Also I had previously noticed that SPL_NEXTLEVEL starts at different offsets for Gothic 1 and Gothic 2. I think there are some comments on this in the code of the lockpicking spell script. That is most likely also the reason why there is no sound in Gothic 2 (it advances beyond level 1 or never reached it). I advise to only use SPL_NEXTLEVEL once in the beginning when setting up the spell and then use SPL_RECEIVEINVEST.
    3. The reason for different mana consumption in Gothic 1 and Gothic 2 is, that SPL_NEXTLEVEL does not consume mana in Gothic 2, but does in Gothic 1. This should be resolved with the point above and you can drop the manual mana decreasing for Gothic 2, because this seems a bit hacky and might decrease mana below 0.
    4. "npc.focus_vob = 0" is a no go, because changing the focus vob internally involves a lot more. With this code you are also creating a memory leak. There is code how to do this securely in GFA: click
    5. What happens to "vobTKItemPtr" if the spell is somehow interrupted? Will it be set to zero somewhere? If not this will lead to crashes when interrupted -> saving -> loading -> casting the spell, because it is a variable. Make sure it is always reset to zero, when the spell ends - both successfully and unsuccessfully.
    6. I don't quite understand "_HOOK_SPELL_SETUP". Why do you have the code and condition from SPL_PickLock in there? It's good that you use a separate hooking-function, but you should just leave out that code. If it is part of a mod, it is still functional, because of its own hooking-function. Also you need to reinitialize the focus list on every load/level change, like it is done in the lockpicking script. Since it is just calling an engine function to reinitialize all the focus instances, you can just copy that part from the lockpicking spell without any worries about collisions between the two scripts.
    7. Concerning making a mob a valid spell target: Yes, this is done in the lockpicking spell with a hook to "oCSpell::IsTargetTypeValid". This is a bit tricky in your case, because you want to focus both items and mobs. I don't want to get "involved", so I won't check how to do this. Two ideas
      1. "targetCollectType" might be a bitfield and is always compared bit-wise instead of by equality. If that is the case, then you could use "targetCollectType = TARGET_TYPE_ITEMS | 128". Then hook "oCSpell::IsTargetTypeValid" as done in the lockpicking spell with your own hooking-function. In it, similar to the lockpicking script, check if (ECX == TARGET_TYPE_ITEMS | 128). This will only be the case for your particular spell. Like in the lockpicking spell, set ECX = 1 if the focus is either an item or a mobswitch/mobinter with trigger entries.
      2. If "targetCollectType" is always compared as an integer, however, then you could use the hook to figure out which spell is used and so on. This would require some research in IDA, because you need to find the spell in question.
    Hope this helps.

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