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 19 von 19
  1. Beiträge anzeigen #1 Zitieren
    Apprentice
    Registriert seit
    May 2017
    Beiträge
    38
     
    LootaBox ist offline

    Large Firestorm AoE fix?

    Hey,

    As you may know, charging the large firestorm does not increase the damage caused by the AoE. I set out to try and fix this for my mod some week ago. This seems to be because the spell information transmitted by the VisualFX is lost somewhere along the way. I couldn't find much in the way of solutions or fixes for this, and so far I have only been able to find a rather roundabout way to get the missing spell ID (code below) using the "OnDMG" scripts originally created by Lehona. I thought I would just share what I found here, perhaps someone has some good ideas or even knows a much better approach to fixing this issue, here's hoping ...

    Here are some things that I found out:
    • This seems to only be an issue where AOE/spread collision VFX is created by a projectile (with base game spells only small/large firestorm do this)
    • If the projectile VFX has static collision (ground/world), then the OnDMG for NPCs hit by the AOE has lost the spell level / damage information.
    • If the projectile VFX has dynamic collision (e.g. direct hit on npc), then the OnDMG for other NPCs hit by the AOE has lost all spell information.
    • In both cases (static/dynamic collision) there seems to be absolutely no way to figure out the original spell level / charged damage from the OnDMG event or VFX associated with the OnDMG event.


    Below is an adjusted version of the "OnDMG" script left by Kirides here: link. With this the damage can be calculated using the correct spell ID, which is enough for small firestorm, but in both cases (static and dynamic collision) the spell level / charged damage information is lost. This is problematic for large firestorm and any similar custom spells, causing them to deal charged damage only on direct hit.
    Spoiler:(zum lesen bitte Text markieren)

    For the sake of conciseness, I left out *DMG_Calculate*. Lets just say it simply returns some integer.

    Code:
    
    func int DMG_OnDmg(var int victimPtr, var int attackerPtr, var int dmg, var int dmgDescriptorPtr, var int bHasHit) {
        // ???
        if (!victimPtr) { return dmg; };
        var oSDamageDescriptor dmgDesc; dmgDesc = _^(dmgDescriptorPtr);
    
        // If there is hitPfx but no spellId, probably AoE spell _SPREAD hitting other NPCs after a direct hit on NPC with the original projectile
        if (dmgDesc.hitPfx && dmgDesc.spellId <= 0) {
            var oCVisualFX hitPfx; hitPfx = _^(dmgDesc.hitPfx);
    
            // If we still have no attacker, try to take him from PFX inflictor (e.g. AoE spells)
            if (!attackerPtr) {
                 attackerPtr = hitPfx.inflictor;
            };
    
            // Set lost spell ID for AoE spells, based on hitPfx.
            if (Hlp_StrCmp(hitPfx.fxName, "spellFX_Firestorm_SPREAD")) {
                 dmgDesc.spellId = SPL_Firestorm;
             } else if (Hlp_StrCmp(hitPfx.fxName, "spellFX_Pyrokinesis_SPREAD")) {
                 dmgDesc.spellId = SPL_Pyrokinesis;
             };
        };
    
       if (attackerPtr) {
           var c_npc att; att = _^(attackerPtr);
           var c_npc vic; vic = _^(victimPtr);
    
           return DMG_Calculate(att, vic, dmgDesc, bHasHit);
        };
    
        return dmg;
    };
    
    func void _DMG_OnDmg_Post() {
        const int dmgDesc = 0; dmgDesc = MEM_ReadInt((ESP + 640) + 4/* &oSDamageDescriptor */); // oCNpc :: OnDamage_Hit ( oSDamageDescriptor& descDamage )
        const int bHasHit = 0; bHasHit = MEM_ReadInt((ESP + 640) - 356/* zBOOL bHasHit */);
        EDI = DMG_OnDmg(EBP, MEM_ReadInt(dmgDesc+8), EDI, dmgDesc, bHasHit);
    };
    func void InitCustomDamageHook() {
        HookEngineF(6736583/*0x66CAC7*/, 5, _DMG_OnDmg_Post);
    };
    
    PS. Is there some good way to copy/paste code into the messages here? Tabs get all messed up and some spaces are lost, I have to go through the code and try to make it readable each time.



    Any pointers or ideas would be much appreciated!

  2. Beiträge anzeigen #2 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    @Someone with knowledge.

    How can i read/write these "flags" ?

    specifically level/queueSetLevel ...

    Code:
    // { - 0x055C
      // collisionOccured;           // zBOOL : 1
    	
      // showVisual;                 // zBOOL : 1
      // isChild;                    // zBOOL : 1
      // isDeleted;                  // zBOOL : 1
    
      // initialized;                // zBOOL : 1
      // shouldDelete;               // zBOOL : 1
      // lightning;                  // zBOOL : 1
      // fxInvestOriginInitialized;  // zBOOL : 1
      // fxInvestTargetInitialized;  // zBOOL : 1
      // fxInvestStopped;            // zBOOL : 1
      // timeScaled;                 // zBOOL : 1
      // fovMorph;                   //       : 2
      // level;                      //       : 5
      // collisionCtr;               //       : 3
      // queueSetLevel;              //       : 5
      var int bitfield;
    // }

  3. Beiträge anzeigen #3 Zitieren
    Provinzheld Avatar von TopLayer
    Registriert seit
    Nov 2020
    Beiträge
    285
     
    TopLayer ist offline
    Zitat Zitat von Kirides Beitrag anzeigen
    How can i read/write these "flags" ?
    General algorithm (check it):
    Code:
    // field mask
    mask = ((1 << field_bit_size) - 1) << bits_before_field;
    
    // field value
    value = (bitfield & mask) >> bits_before_field;
    
    // write value
    bitfield = bitfield & ~mask; // set 0
    bitfield = bitfield | (value << bits_before_field);

  4. Beiträge anzeigen #4 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von TopLayer Beitrag anzeigen
    General algorithm (check it):
    ...
    Thanks, for some reason, my previous attempts failed with something like that.
    Worked now though.

    This is code for Gothic 2 Addon that sets the SpellLevel for the particleFx correctly.
    The Engine sets the level in "CreateAndPlay" at "pfx->SetLevel(lvl, 0)", but resets it in the next line "Init(org, target, 0)"

    This code hooks right after "Init()" and before "Cast()" and sets the level to the provided spellLevel (which is passed as function argument to "CreateAndPlay")

    I use this code for my custom damage script to properly calculate damage and additional effects based on the spellLevel.

    you can read the level using "(visFx.bitfield & oCVisualFX_bitfield_level) >> 13;" where visFX is and instance of oCVisualFx.

    Spoiler:(zum lesen bitte Text markieren)
    Code:
    func void _Hook_oCVisualFX__CreateAndPlay_PostInit() {
    	const int stack_offset     = 172;
    	const int param_lvl_offset = 16;
    	
    	var int childPtr; childPtr = ESI;
    	var int level;    level    = MEM_ReadInt(ESP + stack_offset + param_lvl_offset);
    
    	var oCVisualFX _current; _current = _^(childPtr);
    
    	if (Hlp_StrCmp(_current.fxName, "spellFX_Firestorm_SPREAD"))
    	|| (Hlp_StrCmp(_current.fxName, "spellFX_Pyrokinesis_SPREAD"))
    	{
    		_current.bitfield = _current.bitfield & ~oCVisualFX_bitfield_level;
    		_current.bitfield = _current.bitfield | (level << 13);
    	};
    };
    
    func void InitHook() {
    	HookEngineF(4778368/*0048e980*/, 8, _Hook_oCVisualFX__CreateAndPlay_PostInit);
    };


    oCVisualFX-class:
    Spoiler:(zum lesen bitte Text markieren)
    Code:
    const int oCVisualFX_bitfield_offset                      = 1372;
    const int oCVisualFX_bitfield_collisionOccured            = ((1 << 1) - 1) <<  0;
    const int oCVisualFX_bitfield_showVisual                  = ((1 << 1) - 1) <<  1;
    const int oCVisualFX_bitfield_isChild                     = ((1 << 1) - 1) <<  2;
    const int oCVisualFX_bitfield_isDeleted                   = ((1 << 1) - 1) <<  3;
    const int oCVisualFX_bitfield_initialized                 = ((1 << 1) - 1) <<  4;
    const int oCVisualFX_bitfield_shouldDelete                = ((1 << 1) - 1) <<  5;
    const int oCVisualFX_bitfield_lightning                   = ((1 << 1) - 1) <<  6;
    const int oCVisualFX_bitfield_fxInvestOriginInitialized   = ((1 << 1) - 1) <<  7;
    
    const int oCVisualFX_bitfield_fxInvestTargetInitialized   = ((1 << 1) - 1) <<  8;
    const int oCVisualFX_bitfield_fxInvestStopped             = ((1 << 1) - 1) <<  9;
    const int oCVisualFX_bitfield_timeScaled                  = ((1 << 1) - 1) << 10;
    const int oCVisualFX_bitfield_fovMorph                    = ((1 << 2) - 1) << 11;
    
    const int oCVisualFX_bitfield_level                       = ((1 << 5) - 1) << 13;
    
    const int oCVisualFX_bitfield_collisionCtr                = ((1 << 3) - 1) << 18;
    const int oCVisualFX_bitfield_queueSetLevel               = ((1 << 5) - 1) << 21;
    
    class oCVisualFX
    {
    //zCVob {
      //zCObject {
      var int    _vtbl;
      var int    _zCObject_refCtr;
      var int    _zCObject_hashIndex;
      var int    _zCObject_hashNext;
      var string _zCObject_objectName; // zSTRING
      //}
      var int    _zCVob_globalVobTreeNode;
      var int    _zCVob_lastTimeDrawn;
      var int    _zCVob_lastTimeCollected;
      var int    _zCVob_vobLeafList_array;
      var int    _zCVob_vobLeafList_numAlloc;
      var int    _zCVob_vobLeafList_numInArray;
      var int    _zCVob_trafoObjToWorld[16];
      var int    _zCVob_bbox3D_mins[3];
      var int    _zCVob_bbox3D_maxs[3];
      var int    _zCVob_bsphere3D_center[3];
      var int    _zCVob_bsphere3D_radius;
      var int    _zCVob_touchVobList_array;
      var int    _zCVob_touchVobList_numAlloc;
      var int    _zCVob_touchVobList_numInArray;
      var int    _zCVob_type;
      var int    _zCVob_groundShadowSizePacked;
      var int    _zCVob_homeWorld;
      var int    _zCVob_groundPoly;
      var int    _zCVob_callback_ai;
      var int    _zCVob_trafo;
      var int    _zCVob_visual;
      var int    _zCVob_visualAlpha;
      var int    _zCVob_m_fVobFarClipZScale;
      var int    _zCVob_m_AniMode;
      var int    _zCVob_m_aniModeStrength;
      var int    _zCVob_m_zBias;
      var int    _zCVob_rigidBody;
      var int    _zCVob_lightColorStat;
      var int    _zCVob_lightColorDyn;
      var int    _zCVob_lightDirectionStat[3];
      var int    _zCVob_vobPresetName;
      var int    _zCVob_eventManager;
      var int    _zCVob_nextOnTimer;
      var int    _zCVob_bitfield[5];
      var int    _zCVob_m_poCollisionObjectClass;
      var int    _zCVob_m_poCollisionObject;
      
    //}
    
    // public:
      var string	visName_S;                  // zSTRING
      var string	visSize_S;                  // zSTRING
      var int		  visAlpha;				            // float
      var string	visAlphaBlendFunc_S;        // zSTRING
      var int		  visTexAniFPS;			          // float
      var int		  visTexAniIsLooping;         // int
      var string	emTrjMode_S;                // zSTRING
      var string  emTrjOriginNode_S;          // zSTRING
      var string	emTrjTargetNode_S;          // zSTRING
      var int		  emTrjTargetRange;		        // float
      var int		  emTrjTargetAzi;		          // float
      var int		  emTrjTargetElev;		        // float
      var int		  emTrjNumKeys;
      var int		  emTrjNumKeysVar;
      var int		  emTrjAngleElevVar;	        // float
      var int		  emTrjAngleHeadVar;	        // float
      var int		  emTrjKeyDistVar;		        // float
      var string	emTrjLoopMode_S;            // zSTRING
      var string	emTrjEaseFunc_S;            // zSTRING
      var int		  emTrjEaseVel;			          // float
      var int		  emTrjDynUpdateDelay;	      // float
      var int		  emTrjDynUpdateTargetOnly;
      var string	emFXCreate_S;               // zSTRING
      var string	emFXInvestOrigin_S;         // zSTRING
      var string	emFXInvestTarget_S;         // zSTRING
      var int		  emFXTriggerDelay;		        // float
      var int		  emFXCreatedOwnTrj;          // int
      var string	emActionCollDyn_S;	        // zSTRING		// CREATE, BOUNCE, COLLIDE
      var string	emActionCollStat_S;	        // zSTRING		// CREATE, CREATEONCE, BOUNCE, COLLIDE, CREATEQUAD
      var string	emFXCollStat_S;             // zSTRING
      var string	emFXCollDyn_S;              // zSTRING
      var string	emFXCollDynPerc_S;          // zSTRING
      var string	emFXCollStatAlign_S;        // zSTRING			// TRAJECTORY, COLLISIONNORMAL
      var string	emFXCollDynAlign_S;         // zSTRING
      var int		  emFXLifeSpan;			          // float
      var int		  emCheckCollision;
      var int		  emAdjustShpToOrigin;
      var int		  emInvestNextKeyDuration;	  // float
      var int		  emFlyGravity;				        // float
      var string	emSelfRotVel_S;             // zSTRING
      var string	userString[5];              // zSTRING
      var string	lightPresetName;            // zSTRING
      var string	sfxID;                      // zSTRING
      var int		  sfxIsAmbient;		            // zBOOL
      var int		  sendAssessMagic;            // int
      var int		  secsPerDamage;	            // float
      var int		  dScriptEnd;		              // zBYTE
      var int		  visSize[3];			            // zVEC3
      var int		  emTrjMode;
      var int		  emActionCollDyn;
      var int		  emActionCollStat;
      var int		  emSelfRotVel[3]; // zVEC3
      var int		  emTrjEaseFunc; // enum TEaseFunc
      var int		  emTrjLoopMode; // enum TTrjLoopMode
      var int		  fxState; // enum zTVFXState
    
    //  static zCParser*		fxParser;
    //  static oCVisualFX*		actFX;
    
      var int root; // oCVisualFX*
      var int parent; 		// oCVisualFX*
      var int fxInvestOrigin; // oCVisualFX*
      var int fxInvestTarget; // oCVisualFX*
      var int ai;				// oCVisualFXAI*
    //zCArray <oCVisualFX *> fxList {
      var int fxList_array;
      var int fxList_numAlloc;
      var int fxList_numInArray;
    // }
    //zCArray <oCVisualFX *> childList {
      var int childList_array;
      var int childList_numAlloc;
      var int childList_numInArray;
    // }
    //zCArray <oCEmitterKey *> emKeyList {
      var int emKeyList_array;
      var int emKeyList_numAlloc;
      var int emKeyList_numInArray;
    // }
    //zCArray <zCVob *> vobList {
      var int vobList_array;
      var int vobList_numAlloc;
      var int vobList_numInArray;
    // }
    //zCArray <zCVob *> ignoreVobList {
      var int ignoreVobList_array;
      var int ignoreVobList_numAlloc;
      var int ignoreVobList_numInArray;
    // }
    //zCArray <zCVob *> allowedCollisionVobList {
      var int allowedCollisionVobList_array;
      var int allowedCollisionVobList_numAlloc;
      var int allowedCollisionVobList_numInArray;
    // }
    //zCArray <zCVob *> collidedVobs {
      var int collidedVobs_array;
      var int collidedVobs_numAlloc;
      var int collidedVobs_numInArray;
    // }
    //zCArray <zSVisualFXColl> queuedCollisions {
      var int queuedCollisions_array;
      var int queuedCollisions_numAlloc;
      var int queuedCollisions_numInArray;
    // }
    // oCTrajectory trajectory {
    	//zCArray <zCPositionKey *> keyList {
    	  var int trajectory_keyList_array;
    	  var int trajectory_keyList_numAlloc;
    	  var int trajectory_keyList_numInArray;
    	// }
      var int spl; // zCKBSpline*
      var int mode;
      var int length; // float
      // zMAT4 res {
        var int trajectory_res_v0[4]; //zREAL[4]
        var int trajectory_res_v1[4]; //zREAL[4]
        var int trajectory_res_v2[4]; //zREAL[4]
        var int trajectory_res_v3[4]; //zREAL[4]
      // }
      var int lastKey;
    // }
      var int earthQuake;   // zCEarthquake*
      var int screenFX;     // zCVobScreenFX*
      var int screenFXTime; // float
      var int screenFXDir;
    	
      var int orgNode;    // zCModelNodeInst*
      var int targetNode; // zCModelNodeInst*
    
      var int lastSetVisual;    // zCVisual*
      var int origin;           // zCVob*
      var int inflictor;        // zCVob*
      var int target;           // zCVob*
      var int light;            // zCVobLight*
      var int lightRange;       // float	
      var int sfx;              // zCSoundFX*
      var int sfxHnd;           // zTSoundHandle	
    
      var string fxName; // zSTRING
    
      var int fxBackup;   // oCEmitterKey*
      var int lastSetKey; // oCEmitterKey*
      var int actKey;     // oCEmitterKey*
      var int frameTime;      // float
      var int collisionTime;  // float
      var int deleteTime;     // float
      var int damageTime;     // float
    
      var int targetPos[3];     // zPOINT3
      var int lastTrjDir[3];    // zVEC3
      var int keySize[3];       // zVEC3
      var int actSize[3];       // zVEC3
      var int castEndSize[3];   // zVEC3
    
      var int nextLevelTime;    // float
      var int easeTime;         // float
      var int age;              // float
      var int trjUpdateTime;    // float
      var int emTrjDist;        // float
      var int trjSign;          // float
      var int levelTime;        // float
      var int lifeSpanTimer;    // float
      var int fxStartTime;      // float
      var int oldFovX;          // float 
      var int oldFovY;          // float - 0x0558
    
    // { - 0x055C
      // collisionOccured;           // zBOOL : 1
    	
      // showVisual;                 // zBOOL : 1
      // isChild;                    // zBOOL : 1
      // isDeleted;                  // zBOOL : 1
    
      // initialized;                // zBOOL : 1
      // shouldDelete;               // zBOOL : 1
      // lightning;                  // zBOOL : 1
      // fxInvestOriginInitialized;  // zBOOL : 1
      // fxInvestTargetInitialized;  // zBOOL : 1
      // fxInvestStopped;            // zBOOL : 1
      // timeScaled;                 // zBOOL : 1
      // fovMorph;                   //       : 2
      // level;                      //       : 5
      // collisionCtr;               //       : 3
      // queueSetLevel;              //       : 5
      var int bitfield;
    // }
    
    // protected:
      var int damage;     // float - 0x0560
    	var int damageType;
    
    // private:
    	var int spellType;
    	var int spellCat;
    	var int spellTargetTypes;
    
    	var int savePpsValue;         // float
    	var int saveVisSizeStart[2];  // zVEC2
      var int transRing_v0[3];      // zPOINT3
      var int transRing_v1[3];      // zPOINT3
      var int transRing_v2[3];      // zPOINT3
      var int transRing_v3[3];      // zPOINT3
      var int transRing_v4[3];      // zPOINT3
      var int transRing_v5[3];      // zPOINT3
      var int transRing_v6[3];      // zPOINT3
      var int transRing_v7[3];      // zPOINT3
      var int transRing_v8[3];      // zPOINT3
      var int transRing_v9[3];      // zPOINT3
    
    	var int ringPos;
    	var int emTrjFollowHitLastCheck;  // zBOOL
    	var int bIsProjectile;            // zBOOL
    	var int bPfxMeshSetByVisualFX;    // zBOOL
    	var int m_bAllowMovement;         // zBOOL
    	var int m_fSleepTimer;            // zREAL
    };
    Geändert von Kirides (07.01.2021 um 08:32 Uhr)

  5. Beiträge anzeigen #5 Zitieren
    Apprentice
    Registriert seit
    May 2017
    Beiträge
    38
     
    LootaBox ist offline
    I did explore this bitfield, the values did not seem to change with different charge levels of Large Firestorm (when NPCs get hit by the "wave").

    EDIT: did not see Kirides' new post before posting this, will check later today...

    EDIT2: the solution from Kirides works perfectly with the dynamic collision case. I adjusted it slightly, so that spell ID is likewise corrected for the "OnDMG" hook:
    Spoiler:(zum lesen bitte Text markieren)

    Code:
    
    func void _Hook_oCVisualFX__CreateAndPlay_PostInit() {
    const int stack_offset     = 172;
    const int param_lvl_offset = 16;
    
    var int childPtr; childPtr = ESI;
    var int level;    level    = MEM_ReadInt(ESP + stack_offset + param_lvl_offset);
    
    var oCVisualFX _current; _current = _^(childPtr);
    
    if (Hlp_StrCmp(_current.fxName, "spellFX_Firestorm_SPREAD")) {
            _current.bitfield = _current.bitfield & ~oCVisualFX_bitfield_level;
            _current.bitfield = _current.bitfield | (level << 13);
            _current.spellType = SPL_Firestorm;
        } else if (Hlp_StrCmp(_current.fxName, "spellFX_Pyrokinesis_SPREAD")) {
            _current.bitfield = _current.bitfield & ~oCVisualFX_bitfield_level;
            _current.bitfield = _current.bitfield | (level << 13);
            _current.spellType = SPL_Pyrokinesis;
        };
    };
    
    


    However, this does not work for the the static collision case (e.g. hit ground next to NPC). The VFX that deals the damage seems to be different, "VOG_MAGICBURN" instead of the "_SPREAD" vfx. Changing the condition to include that won't help, as when this effect is created in the static collision case the level at ESP + stack_offset + param_lvl_offset seems to always be 1.
    Geändert von LootaBox (07.01.2021 um 10:14 Uhr)

  6. Beiträge anzeigen #6 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von LootaBox Beitrag anzeigen
    I did explore this bitfield, the values did not seem to change with different charge levels of Large Firestorm (when NPCs get hit by the "wave").

    EDIT: did not see Kirides' new post before posting this, will check later today...

    EDIT2: the solution from Kirides works perfectly with the dynamic collision case. I adjusted it slightly, so that spell ID is likewise corrected for the "OnDMG" hook:
    Spoiler:(zum lesen bitte Text markieren)

    Code:
    
    func void _Hook_oCVisualFX__CreateAndPlay_PostInit() {
    const int stack_offset     = 172;
    const int param_lvl_offset = 16;
    
    var int childPtr; childPtr = ESI;
    var int level;    level    = MEM_ReadInt(ESP + stack_offset + param_lvl_offset);
    
    var oCVisualFX _current; _current = _^(childPtr);
    
    if (Hlp_StrCmp(_current.fxName, "spellFX_Firestorm_SPREAD")) {
            _current.bitfield = _current.bitfield & ~oCVisualFX_bitfield_level;
            _current.bitfield = _current.bitfield | (level << 13);
            _current.spellType = SPL_Firestorm;
        } else if (Hlp_StrCmp(_current.fxName, "spellFX_Pyrokinesis_SPREAD")) {
            _current.bitfield = _current.bitfield & ~oCVisualFX_bitfield_level;
            _current.bitfield = _current.bitfield | (level << 13);
            _current.spellType = SPL_Pyrokinesis;
        };
    };
    
    


    However, this does not work for the the static collision case (e.g. hit ground next to NPC). The VFX that deals the damage seems to be different, "VOG_MAGICBURN" instead of the "_SPREAD" vfx. Changing the condition to include that won't help, as when this effect is created in the static collision case the level at ESP + stack_offset + param_lvl_offset seems to always be 1.
    i imagine this is because i just hook the "vob" case of this function, and not the "position" case. I'll test it and update this post once i figured the neccessery stuff out.

    EDIT: now i see...

    The spell consists of three major parts (and a few others)
    1. The main Attack
    2. The Area of Effect damage attack
    3. The additional burn effect

    1 + 2 are mutually exclusive and 3 is a bonus on-top of either.

    2 has a very short range, about 2-3 characters wide, while 3 has a much bigger range, about 4-5 characters wide.

    idk if i can or want to make 3 count as a real hit.
    Geändert von Kirides (07.01.2021 um 11:41 Uhr)

  7. Beiträge anzeigen #7 Zitieren
    Apprentice
    Registriert seit
    May 2017
    Beiträge
    38
     
    LootaBox ist offline
    I think I was wrong in my previous post about the VOB_MAGICBURN, which is the "part 3" in your categorization. This does not seem to be the vfx that deals damage.

    However, I'm not sure we are on the same page still. I don't have the means to explore the engine code, I don't quite understand everything you mentioned, but do you mean to say that your solution works in both dynamic and static collision case? It does not seem to be the case for me...

    I will attach 3 screenshots that hopefully explain what I mean better. In the screenshots I am printing the _current.fxName and level in your provided CreateAndPlay hook. In each screenshot I have cast a fully charged Large Firestorm (spell level 4).

    In the dynamic collision (I hit the NPC), the level 4 is there and your solution works perfectly:
    [Bild: attachment.php?s=e5f9fe1f8ad5895e3872bb452bd52c7d&attachmentid=49983&d=1610033760&thumb=1][Bild: attachment.php?s=e5f9fe1f8ad5895e3872bb452bd52c7d&attachmentid=49984&d=1610033760&thumb=1]
    In the static collision (I hit the ground), the level is always 1:
    [Bild: attachment.php?s=e5f9fe1f8ad5895e3872bb452bd52c7d&attachmentid=49985&d=1610033760&thumb=1]

    Additionally, in the static collision case the CreateAndPlay hook never triggers for the _SPREAD VisualFX. In this way, the fix not having an effect for the static collision case makes sense; the spell level is not corrected at the "main attack" stage since the hook does not trigger for the _SPREAD VisualFX and is therefore still wrong in the "area of effect" attack stage.
    Geändert von LootaBox (07.01.2021 um 15:45 Uhr)

  8. Beiträge anzeigen #8 Zitieren
    Apprentice
    Registriert seit
    May 2017
    Beiträge
    38
     
    LootaBox ist offline
    So, I finally took some time and installed IDA. I set out to try and fix this issue for the static collision case.

    I think I managed to find a similar point near the end of oCVisualFX::CreateAndCastFX at 0x0048f50f. At least it triggers when the ground is hit with a firestorm spell and its creating the correct _SPREAD VFX which can be found in ESI. The other version of CreateAndPlay method was not triggering at all at this point.

    The problem I am facing is that I don't really know what I am doing. I think I am missing something obvious, I don't see the engine code as clearly as Kirides' makes it seem like... e.g. "pfx->SetLevel(lvl, 0)" or anything like that, its just all assembly with very sparse hints of what names things might have. I'm completely new to IDA... Perhaps I am missing some symbol table available somewhere, or maybe just some setting? I don't see how I can find the address where the level might be stored in this function, as its not given as a function parameter.

    Here is my current code (WIP, includes Kirides' fix for the dynamic collision case):
    Spoiler:(zum lesen bitte Text markieren)

    Code:
    
    func void _Hook_oCVisualFX__CreateAndPlay_PostInit() {
    const int stack_offset     = 172;
    const int param_lvl_offset = 16;
    
    var int childPtr; childPtr = ESI;
    var int level;    level    = MEM_ReadInt(ESP + stack_offset + param_lvl_offset);
    
    var oCVisualFX _current; _current = _^(childPtr);
    
    if (Hlp_StrCmp(_current.fxName, "spellFX_Firestorm_SPREAD")) {
            _current.bitfield = _current.bitfield & ~oCVisualFX_bitfield_level;
            _current.bitfield = _current.bitfield | (level << 13);
            _current.spellType = SPL_Firestorm;
        } else if (Hlp_StrCmp(_current.fxName, "spellFX_Pyrokinesis_SPREAD")) {
            _current.bitfield = _current.bitfield & ~oCVisualFX_bitfield_level;
            _current.bitfield = _current.bitfield | (level << 13);
            _current.spellType = SPL_Pyrokinesis;
        };
    };
    
    func void _Hook_oCVisualFX__CreateAndCastFX_PostInit() {
    
    var int childPtr; childPtr = ESI;
    
    var oCVisualFX _current; _current = _^(childPtr);
    var int level; level = (_current.bitfield & oCVisualFX_bitfield_level) >> 13;
    Print (ConcatStrings ("_current:", ConcatStrings(_current.fxName,ConcatStrings(" level:",IntToString(level)))));
    };
    
    func void oCVisualFX_CreateAndPlay_Init() {
    HookEngineF(4778368/*0048e980*/, 8, _Hook_oCVisualFX__CreateAndPlay_PostInit);
    HookEngineF(4781327/*0048f50f*/, 8, _Hook_oCVisualFX__CreateAndCastFX_PostInit);
    };
    Geändert von LootaBox (14.02.2021 um 07:22 Uhr)

  9. Beiträge anzeigen #9 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von LootaBox Beitrag anzeigen
    So, I finally took some time and installed IDA. I set out to try and fix this issue for the static collision case.

    I think I managed to find a similar point near the end of oCVisualFX::CreateAndCastFX at 0x0048f50f. At least it triggers when the ground is hit with a firestorm spell and its creating the correct _SPREAD VFX which can be found in ESI. The other version of CreateAndPlay method was not triggering at all at this point.

    The problem I am facing is that I don't really know what I am doing. I think I am missing something obvious, I don't see the engine code as clearly as Kirides' makes it seem like... e.g. "pfx->SetLevel(lvl, 0)" or anything like that, its just all assembly with very sparse hints of what names things might have. I'm completely new to IDA... Perhaps I am missing some symbol table available somewhere, or maybe just some setting? I don't see how I can find the address where the level might be stored in this function, as its not given as a function parameter.

    Here is my current code (WIP, includes Kirides' fix for the dynamic collision case):
    ...
    i use IDA and Ghidra aswell as zEngine sourcecode material from Union.
    This way i can use IDA to find proper stack-offsets and with Ghidra i can take a look at decompiled assembly in C++ from the game.
    That's ... not clearly seeing it and also very time consuming to fill out every single class that i want to look at using the zEngine data.

    From the looks of it, i don't see any valid "level" value anywhere in that path :/

    maybe by hooking somewhere earlier and providing the "level" all the way down, or looking at every parent-oCVisualFx there is a way to find the proper value...

  10. Beiträge anzeigen #10 Zitieren
    Provinzheld Avatar von TopLayer
    Registriert seit
    Nov 2020
    Beiträge
    285
     
    TopLayer ist offline
    Zitat Zitat von LootaBox Beitrag anzeigen
    In the static collision (I hit the ground), the level is always 1
    It it set to 1 in oCVisualFX::Init(const zCVob *orgVob, const zCVob *trgtVob, const zCVob *inflictorVob).
    I dont know if it has impact on other spells/effects and we need to rewrite the caller function (maybe just reorder SetLevel and Init calls), but you can just disable this assignment:
    Code:
    #engine [G2A]
        #patch
            MemSet(0x004924E6, '90', 6)
        #/patch
    #/engine

  11. Beiträge anzeigen #11 Zitieren
    Apprentice
    Registriert seit
    May 2017
    Beiträge
    38
     
    LootaBox ist offline
    So, I tested that overriding the spell level in the bitfield at 0x0048f50f for the VFX stored in ESI does affect the ultimate damage dealt to the NPCs hit by the _SPREAD VFX (in the static collision case). So it does seem to be the right place to set the level.

    I tried to find a previous address where I could get the spell level. I did trial&error with the assembly code, but as I didn't really know what any of the code was about I didn't find a solution or really any useful information this way.

    I also explored the VFX stored in ESI, but there appears to be no link to the projectile VFX shot from the player/caster, where I could potentially copy the level from. Root and parent both seem to point at the same VFX stored in ESI.

    TopLayers suggestion seems plausible, but I think this is a bit beyond my abilities.

  12. Beiträge anzeigen #12 Zitieren
    Provinzheld Avatar von TopLayer
    Registriert seit
    Nov 2020
    Beiträge
    285
     
    TopLayer ist offline
    Zitat Zitat von LootaBox Beitrag anzeigen
    TopLayers suggestion seems plausible, but I think this is a bit beyond my abilities.
    It is just about writing bytes in memory. The solution I gave can be easy applied if you rely on Union: just add .patch file with the text I provided. If you dont rely on Union you have Ikarus methods such MEM_WriteByte or something.

    More complete solution:
    -fixed spell level reset
    -fixed C_CanCollideWith argument (was focus vob instead of collided vob)
    -dynamic collision effects created the same way as static

    Still not tested well
    Spoiler:(zum lesen bitte Text markieren)
    Code:
    #engine [G2A]
        #patch
            MemSet(0x004924E6, '90', 6) // disable `level = 1` in oCVisualFX::Init
            HEX @0x00495B9A = '8B 74 24 14 90 90' // C_CanCollideWidth(focusNpc) -> C_CanCollideWidth(npcHit)
            INT @(0x00495B65+1) += (0x0048EE80 - 0x0048E760) // Replace oCVisualFX::CreateAndPlay by oCVisualFX::CreateAndCastFX
    
    
            INT From = 0x00495B29
            INT To = 0x00495B65;
            MemSet(From, '90', To - From)
            HEX @(To - 2) = '8B CD' // ecx = this
            HEX @(To - 2 - 7) = '8D 95 78 02 00 00 52' // zSTRING&
            HEX @(To - 2 - 7 - 5) = '8B 4C 24 18 51' // VobHit
            HEX @(To - 2 - 7 - 5 - 7) = '8B 85 A8 04 00 00 50' // this->origin
            HEX @0x00495B6C = '90 90 90' // __cdecl -> __thiscall: do not add esp
        #/patch
    #/engine

  13. Beiträge anzeigen #13 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von TopLayer Beitrag anzeigen
    It is just about writing bytes in memory. The solution I gave can be easy applied if you rely on Union: just add .patch file with the text I provided. If you dont rely on Union you have Ikarus methods such MEM_WriteByte or something.

    More complete solution:
    -fixed spell level reset
    -fixed C_CanCollideWith argument (was focus vob instead of collided vob)
    -dynamic collision effects created the same way as static
    ....

    For the Firestorm AoE, it seems that just overriding the "level = 1" assignment in oCVisualFx::Init fixes the issue for Static aswell as dynamic collision.

    This is the daedalus code for that: (I do some sanity check at the beginning)

    Code:
    const int oCVisualFX__Init__Set_level_1      = 4793574; // 0x4924E6
    
    // Override oCVisualFx::Init() -> "level = 1" with NOP
    if (MEM_ReadInt(oCVisualFX__Init__Set_level_1 + 0) == 89949833) // 0x05 5c 86 89
    && (MEM_ReadByte(oCVisualFX__Init__Set_level_1 +4) ==   0) // 0x00
    && (MEM_ReadByte(oCVisualFX__Init__Set_level_1 +5) ==   0) // 0x00
    {
        // Override Memory protection
        MemoryProtectionOverride(oCVisualFX__Init__Set_level_1, 6);
    
        // Override MOV dword ptr [ESI + 0X55c], EAX | 89 86 5c 05 00 00
        MEM_WriteInt(oCVisualFX__Init__Set_level_1, -1869574000); // NOPx4 | 0x90 90 90 90
        MEM_WriteByte(oCVisualFX__Init__Set_level_1 +4, 144); // NOP | 0x90
        MEM_WriteByte(oCVisualFX__Init__Set_level_1 +5, 144); // NOP | 0x90
    };
    This can be used instead of hooking the engine. It should also be way more performant that way, since you only override the engine once at the start.

    EDIT:
    Regarding the "C_CanNpcCollideWithSpell" wrong target:
    @mud-freak already wrote a fix for the GothicFreeAim Patch/Scripts.
    I took the code and modified it to work without GFA (only a few checks).

    EDIT2: And removed a check for "only do it if spell is casted from the player"

    It properly checks for the collided vob to be oCNpc before setting "target = collidedVob"

    Spoiler:(zum lesen bitte Text markieren)
    Code:
    func void _Hook_Fix_oCVisualFX_SpellTarget_CollisionTarget() {
        var int visualFX; visualFX = EBP;
        const int oCVisualFX_targetVob_offset =     1200; //0x04B0
        const int oCVisualFX_originVob_offset =     1192; //0x04A8
        const int oCVisualFX__SetTarget       =  4788960; //0x4912E0
        const int oCNpc__player               = 11216516; // 00ab2684
    
        // Only if player is caster and free aiming is enabled
        if (MEM_ReadInt(visualFX+oCVisualFX_originVob_offset) != MEM_ReadInt(oCNpc__player)) {
            return;
        };
    
        // Get collision vob and target vob
        var int collisionVob; collisionVob = MEM_ReadInt(MEM_ReadInt(ESP+360)); // esp+164h+4h
        var int target;       target       = MEM_ReadInt(visualFX+oCVisualFX_targetVob_offset);
    
        // Update target (increase/decrease reference counters properly)
        if (Hlp_Is_oCNpc(collisionVob)) && (collisionVob != target) {
            const int call = 0; var int zero;
            if (CALL_Begin(call)) {
                if (GOTHIC_BASE_VERSION == 2) {
                    CALL_IntParam(_@(zero)); // Do not re-calculate new trajectory
                };
                CALL_PtrParam(_@(collisionVob));
                CALL__thiscall(_@(visualFX), oCVisualFX__SetTarget);
                call = CALL_End();
            };
        };
    };
    Geändert von Kirides (16.02.2021 um 11:38 Uhr)

  14. Beiträge anzeigen #14 Zitieren
    Apprentice
    Registriert seit
    May 2017
    Beiträge
    38
     
    LootaBox ist offline
    Superb solution(s)! Thank you both for the help/info. I tested the solution from Kirides' quite thoroughly (as I don't use Union) and nothing seems to break due to skipping that setting of level to 1.

    Regarding the additional fix for C_CanNpcCollideWithSpell; I understand what the fix does, but can you specify what exactly is the reason to do this, when is the target not correct in the collide check function? Do I need to modify GFA code to make this work with it?

    I should also point out that if damage calculation in the OnDMG hook require the spell ID, the dynamic collision case will have lost it in the damage calculation hook. Since this is not an issue in the static collision case, the previous solution can be used to fix this issue as well.

  15. Beiträge anzeigen #15 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von LootaBox Beitrag anzeigen
    Regarding the additional fix for C_CanNpcCollideWithSpell; I understand what the fix does, but can you specify what exactly is the reason to do this, when is the target not correct in the collide check function? Do I need to modify GFA code to make this work with it?
    For the player, the target of a spell corresponds to the focused NPC. In the context of free aiming in GFA, there may not necessarily be any NPC in focus when firing off a spell. On collision, C_CanNpcCollideWithSpell would then give incorrect results as the collision NPC is not passed to the function. Under normal circumstances (without GFA), this happens only rarely.

    The source of the code snippet can be found here. As posted here, it is already adjusted that you do not need to worry about GFA.

    However, I would suggest to leave the check for the player in there. I remember there was infact a good reason for this check, although I don't recall what it was exactly. If I had to guess, it had to do something with ensuring that NPCs only damage the NPCs they specifically target. This is found throughout all of Gothic (ranged and melee combat). It is important to avoid, for instance, that NPCs would accidentally attack their allies resulting in random quarrels/killings between allied NPCs while hostile NPCs are no longer attacked.

  16. Beiträge anzeigen #16 Zitieren
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.780
     
    Kirides ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    ...
    However, I would suggest to leave the check for the player in there. I remember there was infact a good reason for this check, although I don't recall what it was exactly. If I had to guess, it had to do something with ensuring that NPCs only damage the NPCs they specifically target. This is found throughout all of Gothic (ranged and melee combat). It is important to avoid, for instance, that NPCs would accidentally attack their allies resulting in random quarrels/killings between allied NPCs while hostile NPCs are no longer attacked.
    Well that explains the angry orc shaman attacking his Elite brother

    totally forgot that enemy mages actually exist. ( seekers, shamans anyone? )
    I modified the snippet to include the check again.

  17. Beiträge anzeigen #17 Zitieren
    Provinzheld Avatar von TopLayer
    Registriert seit
    Nov 2020
    Beiträge
    285
     
    TopLayer ist offline
    So, what is the reason to change oCVisualFX::target property? Why not just push collided NPC in C_CanNpcCollideWithSpell function instead of target?

    Setting the target property breaks oCVisualFX::CanThisCollideWith logic, where non player spells move freely through other non-monster NPCs.

  18. Beiträge anzeigen #18 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von TopLayer Beitrag anzeigen
    So, what is the reason to change oCVisualFX::target property? Why not just push collided NPC in C_CanNpcCollideWithSpell function instead of target?
    I hadn't read the earlier replies to this thread. The solution you suggest here seems to be complete and addressing the original issue of this thread well. I was only commenting on the code snippet from GFA that showed up here. The intention of that function was in fact to overwrite the target property and may not be the best idea for the intention here.

  19. Beiträge anzeigen #19 Zitieren
    Apprentice
    Registriert seit
    May 2017
    Beiträge
    38
     
    LootaBox ist offline
    For those who are interested; I took the time to convert TopLayer's full solution to Ikarus code. Its a bit ugly, partially missing sanity checks and I don't really know what all the steps are for, but it does seem to work.

    Code:
    /** Replace: oCVisualFX::CreateAndPlay -> oCVisualFX::CreateAndCastFX */
    func void _Override_oCVisualFX__ProcessCollision__CreateVFX() {
        const int oCVisualFX__ProcessCollision__CreateVFX_Start = 4807465; // 0x495B29
        const int oCVisualFX__ProcessCollision__CreateVFX_End   = 4807525; // 0x495B65
    
        // NOPs
        repeat(i, oCVisualFX__ProcessCollision__CreateVFX_End - oCVisualFX__ProcessCollision__CreateVFX_Start); var int i;
            MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_Start + i, 144); // NOP | 0x90
        end;
    
        // ecx = this
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  1, 205); // ... | 0xcd
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  2, 139); // ... | 0x8b
    
        // zSTRING&
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  3,  82); // ... | 0x52
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  4,   0); // ... | 0x00
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  5,   0); // ... | 0x00
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  6,   2); // ... | 0x02
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  7, 120); // ... | 0x78
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  8, 149); // ... | 0x95
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End -  9, 141); // ... | 0x8d
    
        // VobHit
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 10,  81); // ... | 0x51
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 11,  24); // ... | 0x18
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 12,  36); // ... | 0x24
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 13,  76); // ... | 0x4c
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 14, 139); // ... | 0x8b
    
        // this->origin
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 15,  80); // ... | 0x50
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 16,   0); // ... | 0x00
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 17,   0); // ... | 0x00
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 18,   4); // ... | 0x04
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 19, 168); // ... | 0xa8
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 20, 133); // ... | 0x85
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_End - 21, 139); // ... | 0x8b
    
        // CreateAndPlay -> CreateAndCastFX
        var int orig_pos; orig_pos = MEM_ReadInt(oCVisualFX__ProcessCollision__CreateVFX_End + 1);
        var int offset; offset = 1824; // (0x0048EE80 - 0x0048E760)
        MEM_WriteInt(oCVisualFX__ProcessCollision__CreateVFX_End + 1, orig_pos+offset);
    
        // __cdecl -> __thiscall: do not add esp
        const int oCVisualFX__ProcessCollision__CreateVFX_Post = 4807532; // 0x495B6C
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_Post +0, 144); // NOP | 0x90
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_Post +1, 144); // NOP | 0x90
        MEM_WriteByte(oCVisualFX__ProcessCollision__CreateVFX_Post +2, 144); // NOP | 0x90
    };
    
    /** Replace: C_CanCollideWith(focusNpc) -> C_CanCollideWith(npcHit) */
    func void _Override_oCVisualFX__ProcessCollision__CollideFix() {
        const int oCVisualFX__ProcessCollision__CanCollideWith = 4807578; // 0x495B9A
    
        if (MEM_ReadByte(oCVisualFX__ProcessCollision__CanCollideWith +0) == 139) // 0x8b
        && (MEM_ReadByte(oCVisualFX__ProcessCollision__CanCollideWith +1) == 181) // 0xb5
        && (MEM_ReadByte(oCVisualFX__ProcessCollision__CanCollideWith +2) == 176) // 0xb0
        && (MEM_ReadByte(oCVisualFX__ProcessCollision__CanCollideWith +3) ==   4) // 0x04
        && (MEM_ReadByte(oCVisualFX__ProcessCollision__CanCollideWith +4) ==   0) // 0x00
        && (MEM_ReadByte(oCVisualFX__ProcessCollision__CanCollideWith +5) ==   0) // 0x00
        {
            // Override Memory protection
            MemoryProtectionOverride(oCVisualFX__ProcessCollision__CanCollideWith, 6);
    
            // Override MOV ESI, [EBP + 0x4b0] | 8b b5 b0 04 00 00
            MEM_WriteByte(oCVisualFX__ProcessCollision__CanCollideWith +0, 139); // ... | 0x8b
            MEM_WriteByte(oCVisualFX__ProcessCollision__CanCollideWith +1, 116); // ... | 0x74
            MEM_WriteByte(oCVisualFX__ProcessCollision__CanCollideWith +2,  36); // ... | 0x24
            MEM_WriteByte(oCVisualFX__ProcessCollision__CanCollideWith +3,  20); // ... | 0x14
            MEM_WriteByte(oCVisualFX__ProcessCollision__CanCollideWith +4, 144); // NOP | 0x90
            MEM_WriteByte(oCVisualFX__ProcessCollision__CanCollideWith +5, 144); // NOP | 0x90
        };
    };
    
    /** Disable 'level = 1' in oCVisualFX::Init */
    func void _Override_oCVisualFX__Init__LevelFix() {
        const int oCVisualFX__Init__Set_level_1 = 4793574; // 0x4924E6
    
        // Override oCVisualFx::Init() -> "level = 1" with NOP
        if (MEM_ReadByte(oCVisualFX__Init__Set_level_1 +0) == 137) // 0x89
        && (MEM_ReadByte(oCVisualFX__Init__Set_level_1 +1) == 134) // 0x86
        && (MEM_ReadByte(oCVisualFX__Init__Set_level_1 +2) ==  92) // 0x5c
        && (MEM_ReadByte(oCVisualFX__Init__Set_level_1 +3) ==   5) // 0x05
        && (MEM_ReadByte(oCVisualFX__Init__Set_level_1 +4) ==   0) // 0x00
        && (MEM_ReadByte(oCVisualFX__Init__Set_level_1 +5) ==   0) // 0x00
        {
            // Override Memory protection
            MemoryProtectionOverride(oCVisualFX__Init__Set_level_1, 6);
    
            // Override MOV [ESI + 0x55c], EAX | 89 86 5c 05 00 00
            MEM_WriteByte(oCVisualFX__Init__Set_level_1 +0, 144); // NOP | 0x90
            MEM_WriteByte(oCVisualFX__Init__Set_level_1 +1, 144); // NOP | 0x90
            MEM_WriteByte(oCVisualFX__Init__Set_level_1 +2, 144); // NOP | 0x90
            MEM_WriteByte(oCVisualFX__Init__Set_level_1 +3, 144); // NOP | 0x90
            MEM_WriteByte(oCVisualFX__Init__Set_level_1 +4, 144); // NOP | 0x90
            MEM_WriteByte(oCVisualFX__Init__Set_level_1 +5, 144); // NOP | 0x90
        };
    };
    
    func void FixSpellInfoForVFX() {
        _Override_oCVisualFX__Init__LevelFix();
        _Override_oCVisualFX__ProcessCollision__CollideFix();
        _Override_oCVisualFX__ProcessCollision__CreateVFX();
    };

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