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 25
  1. Homepage besuchen Beiträge anzeigen #1
    Team Velen
    Registriert seit
    Aug 2015
    Beiträge
    952
     
    Bloodfly91 ist offline

    Sämtliche schlafende NPCs und Monster ignorieren den Spieler trotz BS_RUN

    Hallo,
    im Gothic 2 Hauptspiel gibt es einen Bug, der das schleichen so ziemlich unnötig macht, da man einfach das seitwärts laufen stattdessen benutzen kann, das letztendlich ähnlich vom Spiel behandelt wird, wie das schleichen selbst. Scheinbar liegt dies am Bodystate, das seitwärts laufen scheint nämlich gar keinen eigenen zu haben und einfach als BS_STAND erkannt zu werden, das NPCs wohl - wie das schleichen auch - ignorieren, bis sie den "Feind", der gerade schleicht, direkt sehen.
    Deshalb habe ich einfach probiert, per FF_ApplyOnce den Bodystate des Helden auf BS_RUN zu setzen, wenn dieser eine seitwärts laufen Animation abspielt. Den BS_RUN Bodystate bemerken NPCs normalerweise auch. Und das scheint so auch tatsächlich zu funktionieren, betritt man einen Portalraum per seitwärts laufen, bemerken die NPCs das nun auch.
    Problem das ich habe ist allerdings, dass sämtliche schlafende NPCs und schlafende Monster den Helden trotzdem nicht wahrnehmen, wenn dieser seitwärts läuft, obwohl sein Bodystate BS_RUN ist. Rennt der Spieler allerdings, bemerken sie das kurze Zeit später sofort, obwohl hier ja ebenfalls der Bodystate auf BS_RUN gesetzt wird. Meine Vermutung war ja eigentlich, dass irgendwo in den Skripten das ganze abgefragt werden muss, was seltsam wäre, da NPCs den Bodystate BS_RUN bereits wahrnehmen und beim seitwärts laufen somit theoretisch auch wahrnehmen müssten. Auch konnte ich so etwas nicht finden...
    Ich habe schon den kompletten Skripte-Ordner nach BS_RUN, BS_SNEAK etc. durchsuchen lassen, aber nichts gefunden, was für dieses Problem verantwortlich sein könnte. Komisch ist auch, dass in der B_MM_AssessEnemy und in der ZS_MM_RTN_Sleep nichts zu finden ist, wo abgefragt wird, in welchem Bodystate der Gegner sich gerade befindet. Denn schlafende Monster nehmen den Spieler ja nicht wahr, wenn dieser schleicht.

    So sehen meine Skripte aus:

    Code:
    func void DIV_SetBodyState (var c_npc slf, var int BodyState) 
    {
        MEM_InitAll();
    
    
        CALL_PtrParam (MEM_InstToPtr (BodyState));
        CALL__thiscall (MEM_InstToPtr (slf), 7727392);
    };
    Code:
    func void DIV_PrintHeroBodystate(){
        if (AniIsActive(hero, "T_RUNSTRAFEL"))
        || (AniIsActive(hero, "T_RUNSTRAFER"))
        || (AniIsActive(hero, "T_WALKSTRAFEL"))
        || (AniIsActive(hero, "T_WALKSTRAFER"))
        {
            if (!C_BodyStateContains (hero, BS_RUN))
            {
                Print ("Bodystate zu BS_RUN geändert!");
            
                DIV_SetBodyState (hero, BS_RUN);
            };
        };
    };
    Ich kann mir das einfach nicht erklären, warum es nicht mit dem Bodystate BS_RUN funktioniert, wenn der Spieler seitwärts läuft, aber wenn er rennt, was ja ebenfalls den Bodystate BS_RUN setzt, schon. Hat da jemand 'ne Idee? Oder eventuell hat schon mal jemand diesen Bug komplett behoben und/oder kennt eine Möglichkeit, diesen komplett zu beheben?


    Edit: Nach mehreren erfolglosen Versuchen habe ich das ganze nun vorerst aufgegeben. Ich schätze, dass das irgendwo in der Engine verankert ist und dort eher die Animation abgefragt wird, statt dem Bodystate. Jetzt noch länger herumzuprobieren, ist mir das einfach nicht wert. Sollte allerdings noch jemand eine Idee haben oder diesen Bug sogar schon komplett behoben haben, dann gerne her damit. ^^
    Geändert von Bloodfly91 (18.04.2019 um 17:56 Uhr)

  2. Beiträge anzeigen #2
    Knight Avatar von Draxes
    Registriert seit
    Aug 2007
    Ort
    Mainz
    Beiträge
    1.920
     
    Draxes ist offline
    Zitat Zitat von Bloodfly91 Beitrag anzeigen
    Hallo,
    im Gothic 2 Hauptspiel gibt es einen Bug, der das schleichen so ziemlich unnötig macht, da man einfach das seitwärts laufen stattdessen benutzen kann, das letztendlich ähnlich vom Spiel behandelt wird, wie das schleichen selbst. Scheinbar liegt dies am Bodystate, das seitwärts laufen scheint nämlich gar keinen eigenen zu haben und einfach als BS_STAND erkannt zu werden, das NPCs wohl - wie das schleichen auch - ignorieren, bis sie den "Feind", der gerade schleicht, direkt sehen.
    Deshalb habe ich einfach probiert, per FF_ApplyOnce den Bodystate des Helden auf BS_RUN zu setzen, wenn dieser eine seitwärts laufen Animation abspielt. Den BS_RUN Bodystate bemerken NPCs normalerweise auch. Und das scheint so auch tatsächlich zu funktionieren, betritt man einen Portalraum per seitwärts laufen, bemerken die NPCs das nun auch.
    Problem das ich habe ist allerdings, dass sämtliche schlafende NPCs und schlafende Monster den Helden trotzdem nicht wahrnehmen, wenn dieser seitwärts läuft, obwohl sein Bodystate BS_RUN ist. Rennt der Spieler allerdings, bemerken sie das kurze Zeit später sofort, obwohl hier ja ebenfalls der Bodystate auf BS_RUN gesetzt wird. Meine Vermutung war ja eigentlich, dass irgendwo in den Skripten das ganze abgefragt werden muss, was seltsam wäre, da NPCs den Bodystate BS_RUN bereits wahrnehmen und beim seitwärts laufen somit theoretisch auch wahrnehmen müssten. Auch konnte ich so etwas nicht finden...
    Ich habe schon den kompletten Skripte-Ordner nach BS_RUN, BS_SNEAK etc. durchsuchen lassen, aber nichts gefunden, was für dieses Problem verantwortlich sein könnte. Komisch ist auch, dass in der B_MM_AssessEnemy und in der ZS_MM_RTN_Sleep nichts zu finden ist, wo abgefragt wird, in welchem Bodystate der Gegner sich gerade befindet. Denn schlafende Monster nehmen den Spieler ja nicht wahr, wenn dieser schleicht.

    So sehen meine Skripte aus:

    Code:
    func void DIV_SetBodyState (var c_npc slf, var int BodyState) 
    {
        MEM_InitAll();
    
    
        CALL_PtrParam (MEM_InstToPtr (BodyState));
        CALL__thiscall (MEM_InstToPtr (slf), 7727392);
    };
    Code:
    func void DIV_PrintHeroBodystate(){
        if (AniIsActive(hero, "T_RUNSTRAFEL"))
        || (AniIsActive(hero, "T_RUNSTRAFER"))
        || (AniIsActive(hero, "T_WALKSTRAFEL"))
        || (AniIsActive(hero, "T_WALKSTRAFER"))
        {
            if (!C_BodyStateContains (hero, BS_RUN))
            {
                Print ("Bodystate zu BS_RUN geändert!");
            
                DIV_SetBodyState (hero, BS_RUN);
            };
        };
    };
    Ich kann mir das einfach nicht erklären, warum es nicht mit dem Bodystate BS_RUN funktioniert, wenn der Spieler seitwärts läuft, aber wenn er rennt, was ja ebenfalls den Bodystate BS_RUN setzt, schon. Hat da jemand 'ne Idee? Oder eventuell hat schon mal jemand diesen Bug komplett behoben und/oder kennt eine Möglichkeit, diesen komplett zu beheben?


    Edit: Nach mehreren erfolglosen Versuchen habe ich das ganze nun vorerst aufgegeben. Ich schätze, dass das irgendwo in der Engine verankert ist und dort eher die Animation abgefragt wird, statt dem Bodystate. Jetzt noch länger herumzuprobieren, ist mir das einfach nicht wert. Sollte allerdings noch jemand eine Idee haben oder diesen Bug sogar schon komplett behoben haben, dann gerne her damit. ^^
    Ich weiß nicht, ob du noch interessiert an einer Lösung bist. Aber falls ja, wäre hier ein recht primitiver Ansatz, sicherlich nicht besonders performant.

    Code:
    func void handleQuietSoundExtensions() {
    	if ((zCModel_AniIsActive(hero, "T_RUNSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_RUNSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_WALKSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_WALKSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_FISTRUNSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_FISTRUNSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_FISTWALKSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_FISTWALKSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_1HRUNSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_1HRUNSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_1HWALKSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_1HWALKSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_2HRUNSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_2HRUNSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_2HWALKSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_2HWALKSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_MAGRUNSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_MAGRUNSTRAFER"))
    		|| (zCModel_AniIsActive(hero, "T_MAGWALKSTRAFEL"))
    		|| (zCModel_AniIsActive(hero, "T_MAGWALKSTRAFER")))
    	|| (C_BodyStateContains(hero, BS_JUMP)) {
    		Npc_SendPassivePerc	(hero, PERC_ASSESSQUIETSOUND, NULL, hero);
    	};
    };
    Die Reaktion auf das Verhalten ist nicht vom Bodystate abhängig, sondern von PERC_ASSESSQUIETSOUND, der (engineintern?) beim Laufen und Item Fallen lassen getriggert wird. Das kann man aber auch manuell anstoßen über die Npc_SendPassivePerc-Methode.

  3. Beiträge anzeigen #3
    Veteran Avatar von N1kX
    Registriert seit
    Aug 2018
    Ort
    Serov
    Beiträge
    648
     
    N1kX ist offline
    Or you must install Union and its plugin.
    Fixes for strafe and sneaking in mod:
    - Fixed an incorrect BS state when using the strafe, which will now refer not to BS_STAND, but to the correct BS States according to the current strafe animations.
    - Fixed bugs for sneak, which will now become really necessary to use both for hunting and stealing. Fix adds the reaction of sleeping NPCs to the following actions of the player:
    a) landing after jumping and jumping from a height
    b) movement using the strafe
    c) moving backwards in all possible modes with and without weapons
    d) drawing melee weapons
    e) using light spells
    f) using a torch

    https://worldofplayers.ru/threads/41768/post-1076027

  4. Homepage besuchen Beiträge anzeigen #4
    Team Velen
    Registriert seit
    Aug 2015
    Beiträge
    952
     
    Bloodfly91 ist offline
    Zitat Zitat von Draxes Beitrag anzeigen
    Ich weiß nicht, ob du noch interessiert an einer Lösung bist. Aber falls ja, wäre hier ein recht primitiver Ansatz, sicherlich nicht besonders performant.

    Code:
    func void handleQuietSoundExtensions() {
        if ((zCModel_AniIsActive(hero, "T_RUNSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_RUNSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_WALKSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_WALKSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_FISTRUNSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_FISTRUNSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_FISTWALKSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_FISTWALKSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_1HRUNSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_1HRUNSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_1HWALKSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_1HWALKSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_2HRUNSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_2HRUNSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_2HWALKSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_2HWALKSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_MAGRUNSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_MAGRUNSTRAFER"))
            || (zCModel_AniIsActive(hero, "T_MAGWALKSTRAFEL"))
            || (zCModel_AniIsActive(hero, "T_MAGWALKSTRAFER")))
        || (C_BodyStateContains(hero, BS_JUMP)) {
            Npc_SendPassivePerc    (hero, PERC_ASSESSQUIETSOUND, NULL, hero);
        };
    };
    Die Reaktion auf das Verhalten ist nicht vom Bodystate abhängig, sondern von PERC_ASSESSQUIETSOUND, der (engineintern?) beim Laufen und Item Fallen lassen getriggert wird. Das kann man aber auch manuell anstoßen über die Npc_SendPassivePerc-Methode.
    Vielen Dank. Erst kürzlich habe ich wieder nach einer Lösung für das Problem gesucht, das kommt mir also gerade recht. Funktioniert einwandfrei.

    Zitat Zitat von N1kX Beitrag anzeigen
    Or you must install Union and its plugin.
    Fixes for strafe and sneaking in mod:
    - Fixed an incorrect BS state when using the strafe, which will now refer not to BS_STAND, but to the correct BS States according to the current strafe animations.
    - Fixed bugs for sneak, which will now become really necessary to use both for hunting and stealing. Fix adds the reaction of sleeping NPCs to the following actions of the player:
    a) landing after jumping and jumping from a height
    b) movement using the strafe
    c) moving backwards in all possible modes with and without weapons
    d) drawing melee weapons
    e) using light spells
    f) using a torch

    https://worldofplayers.ru/threads/41768/post-1076027
    Thank you. But that would mean that the player of the modification also needs Union. I guess that Draxes' solution is the better one for me, because I can't rely on that every player will be using Union and / or this plugin, even if it's mentioned in the installation guide.

  5. #5
    Falugify
    Gast
     
    Zitat Zitat von Draxes Beitrag anzeigen
    Ich weiß nicht, ob du noch interessiert an einer Lösung bist. Aber falls ja, wäre hier ein recht primitiver Ansatz, sicherlich nicht besonders performant.

    ...
    Weißt du vielleicht näher, inwiefern sich das Abfragen der aktuellen Animationen auf die Performance auswirkt?

  6. Beiträge anzeigen #6
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Falugify Beitrag anzeigen
    Weißt du vielleicht näher, inwiefern sich das Abfragen der aktuellen Animationen auf die Performance auswirkt?
    Das hängt wohl ganz davon ab, wie die Deadalus-Funktion AniIsActive, bzw. zCModel_AniIsActive aussieht. Die Abfrage innerhalb der Engine ist nicht ausschlaggebend. Je nach dem wie es also Skriptseitig implementiert ist, kann das schon etwas teuer sein.

    Abgesehen davon ist Draxes Lösung sehr "Engine-ähnlich". Was man verbessern könnte, wäre anstatt der zig Animiationsnamen fürs Strafen der verschiedenen Kampfmodi, kann man einfach oCAniCtrl_Human._t_strafel und oCAniCtrl_Human._t_strafer übergeben. Das verringert die Anzahl der If-Abfragen auf zwei und ersetzt das Übergeben von Strings mit Integern was auch bedeutend billiger ist. Wenn die Deadalus-Funktion AniIsActive, bzw. zCModel_AniIsActive nun mit einem recycle-baren Engine-Call ausgestattet ist, wäre es ideal. Wenn man dann noch einzelne If-Blöcke anstatt ||-Verkettungen macht ist es perfekt.

    Wem das besser schlafen lässt, kann aber auch direkt in die Strafe-Funktion der Engine hooken und dort (analog wie beim normalen Gehen/Rennen) die Perception auslösen:
    Code:
    func void StrafePerception() {
        const int PERC_INVERVAL = 1000; // As in oCAIHuman::CreateFootStepSound
        var int percTimer; percTimer += MEM_Timer.frameTime;
        if (percTimer >= PERC_INVERVAL) {
            percTimer -= PERC_INVERVAL;
            Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, hero, hero);
        };
    };
    func void StrafePerception_Init() {
        const int oCNpc__EV_STRAFE_player_G1 = 7662588; //0x74EBFC
        const int oCNpc__EV_STRAFE_player_G2 = 6834660; //0x6849E4
        HookEngineF(MEMINT_SwitchG1G2(oCNpc__EV_STRAFE_player_G1, oCNpc__EV_STRAFE_player_G2), 7, StrafePerception);
    };
    Hier kann man zwar auch noch etwas an Performance herumschrauben (z.B. gar nicht erst Hooken, sondern direkt innerhalb oCAIHuman::CreateFootStepSound umleiten), allerdings ist der Unterschied kaum merklich und den Arbeitsaufwand nicht wert.

    Dieser Ansatz bezieht sich nur aufs Strafen und berücksichtigt nicht das Springen.




    Zitat Zitat von Bloodfly91 Beitrag anzeigen
    So sehen meine Skripte aus:

    Code:
    func void DIV_SetBodyState (var c_npc slf, var int BodyState) 
    {
        MEM_InitAll();
    
    
        CALL_PtrParam (MEM_InstToPtr (BodyState));
        CALL__thiscall (MEM_InstToPtr (slf), 7727392);
    };
    
    // ...
    
                DIV_SetBodyState (hero, BS_RUN);
            };
        };
    };
    Hier noch der Hinweis, dass dieser Aufruf nicht funktioniert. Die Funktion oCNpc::SetBodyState arbeitet nicht mit den Konstanten aus den Skripten. Man muss vorher die "Interruptable Flags" abziehen. Der obige Aufruf würde außer einer Warnung im zSpy nichts unternehmen. Vgl. mit den Skripten aus GFA.

    Übrigens ist es besser Magic Numbers zu vermeiden und stattdessen eine benannte Konstante anzulegen (siehe mein Code-Schnipsel oben). Das fördert die Leserlichkeit. Ich musste z.B. erst einmal die Zahl 7727392 in hexadezimal umwandeln und dann in IDA nachschauen, bevor ich wusste welche Enginefunktion da überhaupt aufgerufen wird.

  7. #7
    Falugify
    Gast
     
    Zitat Zitat von mud-freak Beitrag anzeigen
    Das hängt wohl ganz davon ab, wie die Deadalus-Funktion AniIsActive, bzw. zCModel_AniIsActive aussieht. Die Abfrage innerhalb der Engine ist nicht ausschlaggebend. Je nach dem wie es also Skriptseitig implementiert ist, kann das schon etwas teuer sein.

    Abgesehen davon ist Draxes Lösung sehr "Engine-ähnlich". Was man verbessern könnte, wäre anstatt der zig Animiationsnamen fürs Strafen der verschiedenen Kampfmodi, kann man einfach oCAniCtrl_Human._t_strafel und oCAniCtrl_Human._t_strafer übergeben. Das verringert die Anzahl der If-Abfragen auf zwei und ersetzt das Übergeben von Strings mit Integern was auch bedeutend billiger ist. Wenn die Deadalus-Funktion AniIsActive, bzw. zCModel_AniIsActive nun mit einem recycle-baren Engine-Call ausgestattet ist, wäre es ideal. Wenn man dann noch einzelne If-Blöcke anstatt ||-Verkettungen macht ist es perfekt.

    Wem das besser schlafen lässt, kann aber auch direkt in die Strafe-Funktion der Engine hooken und dort (analog wie beim normalen Gehen/Rennen) die Perception auslösen:
    Code:
    func void StrafePerception() {
        const int PERC_INVERVAL = 1000; // As in oCAIHuman::CreateFootStepSound
        var int percTimer; percTimer += MEM_Timer.frameTime;
        if (percTimer >= PERC_INVERVAL) {
            percTimer -= PERC_INVERVAL;
            Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, hero, hero);
        };
    };
    func void StrafePerception_Init() {
        const int oCNpc__EV_STRAFE_player_G1 = 7662588; //0x74EBFC
        const int oCNpc__EV_STRAFE_player_G2 = 6834660; //0x6849E4
        HookEngineF(MEMINT_SwitchG1G2(oCNpc__EV_STRAFE_player_G1, oCNpc__EV_STRAFE_player_G2), 7, StrafePerception);
    };
    Hier kann man zwar auch noch etwas an Performance herumschrauben (z.B. gar nicht erst Hooken, sondern direkt innerhalb oCAIHuman::CreateFootStepSound umleiten), allerdings ist der Unterschied kaum merklich und den Arbeitsaufwand nicht wert.

    Dieser Ansatz bezieht sich nur aufs Strafen und berücksichtigt nicht das Springen.





    Hier noch der Hinweis, dass dieser Aufruf nicht funktioniert. Die Funktion oCNpc::SetBodyState arbeitet nicht mit den Konstanten aus den Skripten. Man muss vorher die "Interruptable Flags" abziehen. Der obige Aufruf würde außer einer Warnung im zSpy nichts unternehmen. Vgl. mit den Skripten aus GFA.

    Übrigens ist es besser Magic Numbers zu vermeiden und stattdessen eine benannte Konstante anzulegen (siehe mein Code-Schnipsel oben). Das fördert die Leserlichkeit. Ich musste z.B. erst einmal die Zahl 7727392 in hexadezimal umwandeln und dann in IDA nachschauen, bevor ich wusste welche Enginefunktion da überhaupt aufgerufen wird.
    Ich benutze das hier:

    Code:
     FUNC STRING oCAniCtrl__GetCurrentAniName (var int oCAniCtrl_Ptr)
    {
    	var int hlp;
    	hlp = MEM_ReadInt (oCAniCtrl_Ptr + 104);            //zCModel     oCAniCtrl_Human._zCAIPlayer_model
    	
    	if (hlp) 
    	{
    		hlp = MEM_ReadInt (hlp + 56);                //*ActiveAniLayer1
    
    		if (hlp)
            	{
    			hlp = MEM_ReadInt (hlp);            //*oCAni                
    			if (hlp)
    			{ 
                    		return MEM_Readstring (hlp + 36);    // This will read active ani name(?)
    			};    //aniname(zstring)
    		};
    	};
    	return "ERROR";
    };
    
    FUNC STRING NPC_GetAniName (var int slfInstance)
    {
    	var oCNPC slf;
    	slf = Hlp_GetNPC (slfInstance);
        
    	var string str_Ani;
    	str_Ani = oCAniCtrl__GetCurrentAniName (slf.AniCtrl);
    
    	return str_Ani;
    };

    Woran genau erkennt er das Strafing? Berücksichtigt dein Script auch verstecktes Strafing? Wenn der Held im anderen Bodystates (bzw. Grund Ani steht) zum Beispiel S_BOWWALK. Durch Bewegen und sofortiges Strafen, wird sie nicht geändert, aber der Held straft dennoch.

  8. Beiträge anzeigen #8
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.251
     
    Milky-Way ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Abgesehen davon ist Draxes Lösung sehr "Engine-ähnlich". Was man verbessern könnte, wäre anstatt der zig Animiationsnamen fürs Strafen der verschiedenen Kampfmodi, kann man einfach oCAniCtrl_Human._t_strafel und oCAniCtrl_Human._t_strafer übergeben. Das verringert die Anzahl der If-Abfragen auf zwei und ersetzt das Übergeben von Strings mit Integern was auch bedeutend billiger ist. Wenn die Deadalus-Funktion AniIsActive, bzw. zCModel_AniIsActive nun mit einem recycle-baren Engine-Call ausgestattet ist, wäre es ideal. Wenn man dann noch einzelne If-Blöcke anstatt ||-Verkettungen macht ist es perfekt.

    Wem das besser schlafen lässt, kann aber auch direkt in die Strafe-Funktion der Engine hooken und dort (analog wie beim normalen Gehen/Rennen) die Perception auslösen:
    Code:
    func void StrafePerception() {
        const int PERC_INVERVAL = 1000; // As in oCAIHuman::CreateFootStepSound
        var int percTimer; percTimer += MEM_Timer.frameTime;
        if (percTimer >= PERC_INVERVAL) {
            percTimer -= PERC_INVERVAL;
            Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, hero, hero);
        };
    };
    func void StrafePerception_Init() {
        const int oCNpc__EV_STRAFE_player_G1 = 7662588; //0x74EBFC
        const int oCNpc__EV_STRAFE_player_G2 = 6834660; //0x6849E4
        HookEngineF(MEMINT_SwitchG1G2(oCNpc__EV_STRAFE_player_G1, oCNpc__EV_STRAFE_player_G2), 7, StrafePerception);
    };
    Hier kann man zwar auch noch etwas an Performance herumschrauben (z.B. gar nicht erst Hooken, sondern direkt innerhalb oCAIHuman::CreateFootStepSound umleiten), allerdings ist der Unterschied kaum merklich und den Arbeitsaufwand nicht wert.

    Dieser Ansatz bezieht sich nur aufs Strafen und berücksichtigt nicht das Springen.
    Ich bin gerade dabei, diesen Ansatz soweit auszubauen, dass er möglichst viele Tricks der Spieler abdeckt und auch schlafende Npc weckt. Eine Schwierigkeit habe ich aktuell noch: wie erkenne ich es (-> wo muss ich einen Hook ansetzen), dass der Spieler mit gezogener Waffe rückwärts springt?

    Mit
    Code:
        const int oCAniCtrl_Human__PC_GoBackward_G2 = 7019968; //0x6B1DC0
        HookEngineF(oCAniCtrl_Human__PC_GoBackward_G2, 5, GoBackwardPerception);
    komme ich dran, wenn der Spieler ohne gezogene Waffe rückwärts geht.

    Bereits (vergeblich) ausprobiert habe ich:
    oCAniCtrl_Human::_Backward (0x006B7BC0)
    oCNpc::CanJumpBack (0x006888D0)
    zCAIPlayer::CheckEnoughSpaceMoveBackward (0x00511740)
    oCNpc::EV_Dodge (0x00685290)
    oCNpc::EV_Jump (0x00683C20)


    Gibt es da irgendeinen Trick, um herauszufinden, wo man da einen Hook ansetzen kann? Wenn ich mir z.B. die Adresse für Strafing anschaue: 0x6849E4, dann sehe ich da in IDA nichts besonderes, was ich durch suchen hätte finden können. Aber irgendein System steckt doch bestimmt dahinter

    Wenn es fertig ist, teile ich natürlich auch den kompletten Code hier.

  9. Beiträge anzeigen #9
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.251
     
    Milky-Way ist offline
    Irgendwie muss das Spiel ja feststellen, dass der Spieler gerade mit einer Waffe in der Hand rückwärts geht, um die passende Animation abzuspielen.

    Aber wie kann ich solch eine Stelle finden? Wie kann ich z.B. in IDA suchen, ob irgendwo ein Tastendruck mit der Taste für zurück (vermutlich
    Code:
    zSTRING const ZOPT_GAMEKEY_DOWN	00000000008CD5A0
    ?) verglichen wird?

    Und / oder wie kann ich finden, wo im IDA-view die "Objekte" aus dem Strings window verwendet werden? (z.B. Npc_HasReadiedWeapon)

    Und ein dritter Ansatz über die Animationen: es gibt in der HumanS.mds z.B.
    Code:
    //	1h-Run Backwards Movement
    			ani			("T_1HJumpB" 				1 	"S_1HRun" 		0.2 0.3 M. 	"HUM_1HParadeJumpT0_A01.asc"	F	1	14)
    aber ich kann T_1HJumpB nicht in IDA finden (oder ich suche nicht "richtig")

  10. Beiträge anzeigen #10
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.448
     
    Lehona ist offline
    Wenn du im Strings-Window auf einen Eintrag klickst, sollte der dich in den Disassembly-View bringen, wo dann wiederum die xrefs für diesen String zu sehen sind (also alle Orte wo dieser String referenziert wird). Externals haben leider keinen Namen sondern heißen nur sub_xxxxx (vermutlich sind die in C++ als Lambda oder so definiert?), da muss man sich dann ein wenig umschauen bis man die richtige hat. Irgendwo gibt es aber einen Funktionsaufruf, der den String der External mit so einer sub_xxxxx verbindet. Da die xrefs aber nur sub_6E6240 aufgelistet haben, dürfte das auch die richtige Funktion sein (lässt sich auch nachprüfen mit einem Blick in die oCGame::DefineExternals, für die ebenfalls eine xref gelistet wird. Da wird sub_6E6240 zusammen mit" Npc_HasReadiedWeapon" als Parameter an oCGame::DefineExternal übergeben).

    Du solltest aber daran denken, dass in der Engine nicht die Implementierung der External verwendet wird, weil die natürlich mit der Daedalus-VM interagiert. Wie genau jetzt entschieden wird ob der NPC gerade eine Waffe gezogen hat ist mir nicht direkt ersichtlich, aber ein besserer Einstiegspunkt ist vielleicht oCNpc::GetWeapon (das wird auch in der External-Implementierung verwendet). Da wiederum gibt es viele xrefs auf irgendwelche AI-Funktionen, die sich durchzuschauen eventuell lohnt.

    Den Animationsnamen konnte ich übrigens auch nicht finden, ich denke der wird nicht einfach so hardcoded sein.

  11. Beiträge anzeigen #11
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.251
     
    Milky-Way ist offline
    Zitat Zitat von Lehona Beitrag anzeigen
    Wenn du im Strings-Window auf einen Eintrag klickst, sollte der dich in den Disassembly-View bringen, wo dann wiederum die xrefs für diesen String zu sehen sind (also alle Orte wo dieser String referenziert wird). Externals haben leider keinen Namen sondern heißen nur sub_xxxxx (vermutlich sind die in C++ als Lambda oder so definiert?), da muss man sich dann ein wenig umschauen bis man die richtige hat. Irgendwo gibt es aber einen Funktionsaufruf, der den String der External mit so einer sub_xxxxx verbindet. Da die xrefs aber nur sub_6E6240 aufgelistet haben, dürfte das auch die richtige Funktion sein (lässt sich auch nachprüfen mit einem Blick in die oCGame:efineExternals, für die ebenfalls eine xref gelistet wird. Da wird sub_6E6240 zusammen mit" Npc_HasReadiedWeapon" als Parameter an oCGame:efineExternal übergeben).

    Du solltest aber daran denken, dass in der Engine nicht die Implementierung der External verwendet wird, weil die natürlich mit der Daedalus-VM interagiert. Wie genau jetzt entschieden wird ob der NPC gerade eine Waffe gezogen hat ist mir nicht direkt ersichtlich, aber ein besserer Einstiegspunkt ist vielleicht oCNpc:etWeapon (das wird auch in der External-Implementierung verwendet). Da wiederum gibt es viele xrefs auf irgendwelche AI-Funktionen, die sich durchzuschauen eventuell lohnt.

    Den Animationsnamen konnte ich übrigens auch nicht finden, ich denke der wird nicht einfach so hardcoded sein.
    Danke für die Erklärung. Leider habe ich mit GetWeapon keine Stelle gefunden, die mir wirklich passend erschien.

    Falls jemand noch Ideen hat, wo das "mit Waffe in der Hand rückwärts bewegen" passieren könnte, bin ich weiterhin dran interessiert.
    Allgemein scheinen die Bewegungen des Spielers, wenn ich das in IDA richtig verstehe, in etwa hier zu erfolgen:
    Code:
    .text:0069AFCF E8 EC 6D 01 00                              call    ?PC_GoBackward@oCAniCtrl_Human@@QAEXXZ ; oCAniCtrl_Human::PC_GoBackward(void)
    aber da finde ich leider nichts, was für mich nach "Waffe gezogen?" aussieht.

    Alternativ vielleicht irgendwo innerhalb von:
    Code:
    .text:0069A9B0                             ; protected: void __thiscall oCAIHuman::Moving(void)
    da werden wohl Tastendrücke abgefragt und auch die Waffe. Aber da wird zu viel herumgesprungen für mein Verständnis, so dass ich nicht weiß, in welchem Fall man wo landet, und sehe auch nichts, wo ich mir direkt denke, da wird eine passende Animation / Bewegung gestartet.

  12. Beiträge anzeigen #12
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Dem Rückwärtsspringen mit Nahkampfwaffe liegt keine explizite Funktion in der Engine zugrunde (also etwas wie "oCNpc::SpringRückwärts" existiert nicht). Das ist Teil der HitCombo (oCAniCtrl_Human::HitCombo) und lässt sich nicht so leicht ermitteln. Das musste ich feststellen als ich die Stamina-Skripte geschrieben habe. Ebenso wie HitCombos wird eine Parade oder das Rückwärtsspringen nur angestoßen. Sobald das geschehen ist, lässt sich das Laufen dieser Aktion schwer ermitteln, außer über die Animation.

    Deine beste Möglichkeit ist es also die laufende Animation abzufragen.


    EDIT: Bzw. wenn es dir reicht zu wissen, ob ein Rückwärtssprung gestartet wurde: Ausgelöst wird die Aktion in oCAIHuman::FightMelee. Ich weiß aber gerade nicht mehr genau wo und habe keine Zeit das noch einmal nachzuschauen.
    Geändert von mud-freak (27.09.2020 um 09:30 Uhr)

  13. Beiträge anzeigen #13
    Ritter Avatar von Kirides
    Registriert seit
    Jul 2009
    Ort
    Norddeutschland
    Beiträge
    1.781
     
    Kirides ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    EDIT: Bzw. wenn es dir reicht zu wissen, ob ein Rückwärtssprung gestartet wurde: Ausgelöst wird die Aktion in oCAIHuman::FightMelee. Ich weiß aber gerade nicht mehr genau wo und habe keine Zeit das noch einmal nachzuschauen.
    oCAIHuman :: FightMelee() Stellen wo die Tastatureingabe geprüft wird. (nur G2 dNdR)

    Code:
    HookEngineF(6911940 /* 006977c4 */, 5, Hook_KeyDownPressed); // Gothic 1 Steuerung
    HookEngineF(6914029 /* 00697fed */, 5, Hook_KeyDownPressed); // Spezial Taste
    HookEngineF(6914536 /* 006981e8 */, 6, Hook_KeyDownPressed); // Gothic 2 Steuerung
    Geändert von Kirides (28.09.2020 um 06:53 Uhr)

  14. Beiträge anzeigen #14
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.251
     
    Milky-Way ist offline
    Vielen Dank euch beiden! Damit bin ich schon mal einen großen Schritt weiter (oder zurück?)

    Zitat Zitat von Kirides Beitrag anzeigen
    oCAIHuman :: FightMelee() Stellen wo die Tastatureingabe geprüft wird.

    Code:
    HookEngineF(6911940 /* 006977c4 */, 5, Hook_KeyDownPressed); // Gothic 1 Steuerung (?)
    HookEngineF(6914029 /* 00697fed */, 5, Hook_KeyDownPressed); // Gothic 2 Steuerung (?)
    HookEngineF(6914536 /* 006981e8 */, 6, Hook_KeyDownPressed); // Spezial Taste (?)
    006977c4 scheint mir beim Testen G1 Steuerung zu sein, 006981e8 G2. (Für Mitlesende: in den Gothic Optionen in Gothic 2 -- ich nehme stark an, dass keine dieser Adressen in G1 funktionieren würde.) Ich habe es noch nicht geschafft, 00697fed auszulösen, lasse es für den Fall der Fälle aber mal drin.


    Der Code oben wird sowohl bei 1H als auch bei 2H Waffen ausgelöst. In der Engine gibt es zusätzlich noch oCAIHuman :: BowMode(), was vermutlich (?) statt FightMelee verwendet wird, wenn ein Bogen (und Armbrust?) gezogen sind. Z.B. habe ich gefunden, dass 0x006966FE aufgerufen wird, wenn man mit dem Bogen blockt (?) in G2 (?) Steuerung. Rückwärtsspringen habe ich leider nicht finden können. Es gibt einige input Stellen, aber an den meisten habe ich keine für einen Hook passende Stellen gefunden (>= 5 Byte ohne call und jump). Falls da jemand mit mehr Erfahrung noch was herausfindet, bin ich natürlich weiterhin dankbar

  15. Beiträge anzeigen #15
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Milky-Way Beitrag anzeigen
    Der Code oben wird sowohl bei 1H als auch bei 2H Waffen ausgelöst. In der Engine gibt es zusätzlich noch oCAIHuman :: BowMode(), was vermutlich (?) statt FightMelee verwendet wird, wenn ein Bogen (und Armbrust?) gezogen sind. Z.B. habe ich gefunden, dass 0x006966FE aufgerufen wird, wenn man mit dem Bogen blockt (?) in G2 (?) Steuerung. Rückwärtsspringen habe ich leider nicht finden können. Es gibt einige input Stellen, aber an den meisten habe ich keine für einen Hook passende Stellen gefunden (>= 5 Byte ohne call und jump). Falls da jemand mit mehr Erfahrung noch was herausfindet, bin ich natürlich weiterhin dankbar
    BowMode ist ausschließlich für Schießen zuständig. Dort werden keine Bewegungen abgehandelt. D.h. Rückwärtslaufen ist im Fernkampf das normale Rückwärtslaufen. Ist es überhaupt nötig, dass abzufangen? Löst Rückwärtslaufen keine Sound-Wahrnehmung aus?

  16. Beiträge anzeigen #16
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.251
     
    Milky-Way ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    BowMode ist ausschließlich für Schießen zuständig. Dort werden keine Bewegungen abgehandelt. D.h. Rückwärtslaufen ist im Fernkampf das normale Rückwärtslaufen. Ist es überhaupt nötig, dass abzufangen? Löst Rückwärtslaufen keine Sound-Wahrnehmung aus?
    Danke für den Hinweis! Rückwärtslaufen löst in Gothic kein Geräusch aus, aber ich habe dafür bereits einen passenden Hook (hatte nur die unterschiedlichen Hooks einzeln getestet). Ich probiere das gleich mal aus, aber vermutlich habe ich nun dank eurer Hilfe alle (mir bekannten) Fälle abgedeckt.

  17. Beiträge anzeigen #17
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.251
     
    Milky-Way ist offline
    Edit 10. Oktober 2020: statt der FrameFunction / Schleife lässt es sich auch nur mit Hooks regeln, indem man nach der Kollisionsabfrage in _Backwards hookt. Dank an Kirides für die Adressen. Siehe hier für die Anpassung:
    https://forum.worldofplayers.de/foru...1#post26557935

    Edit 4. Oktober 2020: das Senden einer Wahrnehmung innerhalb des Hooks von oCAniCtrl_Human::PC_GoBackward erscheint problematisch (Held fällt durch Steigungen hindurch). Stattdessen kann man in den Hooks nur Variablen setzen und stattdessen in einer FrameFunction die Variablen abfragen und ggf. Wahrnehmung senden (und Variablen zurücksetzen). Der Code in diesem Beitrag ist daraufhin angepasst. Dank an Falugify, dem das Problem aufgefallen ist!

    Hier dann der Code, mit dem schlafende NPCs geweckt werden, wenn der Spieler straft, rückwärts geht (mit oder ohne Wafe) oder springt (mit oder ohne Akrobatik). Gedacht ist der Code für Modder, die diese Funktionalität erzwingen wollen, damit sich z.B. schleichen lernen tatsächlich lohnt. Für Spieler gibt es bereits den Union Patch, wenn sie sich selbst diese Möglichkeit nehmen wollen. Ich habe mich nur dafür interessiert, Menschen zu wecken. Für Monster muss man eventuell auch noch in der B_MM_AssessEnemy ansetzen.

    Der Code besteht aus 2 3 Teilen:
    1) Hooks senden Wahrnehmung und setzen Variable
    2) FrameFunction sendet Wahrnehmung falls Variable gesetzt ist
    3) in B_AssessEnterRoom wird ein Sonderfall für die Variable eingebaut, um trotz BS_Stand am Helden den Npc zu wecken

    Hooks initialisieren:
    Code:
        // prevent player from strafing his way to avoid detection without sneaking
        StrafePerception_Init();
        // prevent player from jumping his way to avoid detection without sneaking
        JumpPerception_Init();
        // prevent player from going backwards to avoid detection without sneaking
        GoBackwardPerception_Init();
    rufen Funktionen auf:
    Code:
    var int hero_strafing;
    var int hero_jumping;
    var int hero_gobackward;
    
    func void StrafePerception() {
    
        // exit function if player is in sneak mode
        if Npc_GetWalkMode(hero) == NPC_SNEAK
        {
            return;
        };
    
        const int PERC_INVERVAL = 1000; // As in oCAIHuman::CreateFootStepSound
        var int percTimer; percTimer += MEM_Timer.frameTime;
        if (percTimer >= PERC_INVERVAL) {
            percTimer -= PERC_INVERVAL;
            hero_strafing = TRUE;
        };
    };
    func void StrafePerception_Init() {
        const int oCNpc__EV_STRAFE_player_G1 = 7662588; //0x74EBFC
        const int oCNpc__EV_STRAFE_player_G2 = 6834660; //0x6849E4
        HookEngineF(MEMINT_SwitchG1G2(oCNpc__EV_STRAFE_player_G1, oCNpc__EV_STRAFE_player_G2), 7, StrafePerception);
    };
    
    func void JumpPerception() {
        hero_jumping = TRUE;
    };
    func void JumpPerception_Init() {
        const int oCAniCtrl_Human__PC_JumpForward_G2 = 7020032; //0x6B1E00
        HookEngineF(oCAniCtrl_Human__PC_JumpForward_G2, 5, JumpPerception);
    };
    
    func void GoBackwardPerception() {
    
        // exit function if player is in sneak mode
        if Npc_GetWalkMode(hero) == NPC_SNEAK
        {
            return;
        };
        hero_gobackward = TRUE;
    };
    func void GoBackwardPerception_Init() {
        const int oCAniCtrl_Human__PC_GoBackward_G2 = 7019968; //0x6B1DC0
        HookEngineF(oCAniCtrl_Human__PC_GoBackward_G2, 5, GoBackwardPerception);
    
        // hooks in oCAIHuman::FightMelee (backwards with weapon drawn)
        HookEngineF(6911940 /* 006977c4 */, 5, GoBackwardPerception); // Gothic 1 Steuerung (?)
        HookEngineF(6914029 /* 00697fed */, 5, GoBackwardPerception); // (?)
        HookEngineF(6914536 /* 006981e8 */, 6, GoBackwardPerception); // Gothic 2 Steuerung (?)
    };
    In einer FrameFunction ggf. die Wahrnehmung senden:
    Code:
    func void SendQuietSoundIfNoisy()
    {
        // hero making noise since last call?
        if hero_strafing || hero_jumping || hero_gobackward
        {
            // send perception
            Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, hero, hero);
            // reset variables
            hero_strafing = FALSE;
            hero_jumping = FALSE;
            hero_gobackward = FALSE;
        };
    };
    SendQuietSoundIfNoisy als FrameFunction starten, z.B. alle 1/4 Sekunden aufrufen für halbwegs zügige Reaktion der Npc:
    Code:
    FF_ApplyOnceExt(SendQuietSoundIfNoisy, 250, -1); // catch player strafing, walking backwards, jumping
    (an passender Stelle, so dass sie für alle relevanten Welten aktiv ist)

    und dann die B_AssessEnterRoom anpassen:
    Code:
    	if (C_BodyStateContains (other, BS_SNEAK) 
    		|| (C_BodyStateContains (other, BS_STAND) 
    			&& !hero_strafing 
    			&& !hero_jumping 
    			&& !hero_gobackward))
    	{
    		if (!Npc_CanSeeNpc (self, other) && !Npc_IsInState (self, ZS_ObservePlayer))
    		{
    			return FALSE;
    		};
    	};
    statt
    Code:
    	if (C_BodyStateContains (other, BS_SNEAK) || (C_BodyStateContains (other, BS_STAND))
    	{
    		if (!Npc_CanSeeNpc (self, other) && !Npc_IsInState (self, ZS_ObservePlayer))
    		{
    			return FALSE;
    		};
    	};
    Nur die Wahrnehmung auszulösen reicht nicht aus, da sonst in B_AssessEnterRoom die Abfrage immernoch an C_BodyStateContains (other, BS_STAND) hängenbleibt, wenn der Npc in einem Bett schläft und so ausgerichtet ist, dass er den Spieler nicht "sehen" kann.

    Falls obiger Code nicht in der originalen B_AssessEnterRoom steht, müsste man im unveränderten Code noch mal gucken, wo man da ansetzen kann / muss.

    PS: es fehlte noch die Funktion Npc_GetWalkMode. Im Forum findet man eine ältere Variante, die ich mal mit Sektenspinners Hilfe geschrieben hatte. Etwas performanter ist vermutlich:
    Code:
    FUNC int Npc_GetWalkMode (VAR C_NPC slf)
    {
        var oCNpc oCNpc_slf;
        oCNpc_slf = Hlp_GetNpc(slf);
    
        var int wmode;
        const int oCAniCtrl_Human_walkmode_offset            = 352; //0x0160
        return MEM_ReadInt(oCNpc_slf.anictrl+oCAniCtrl_Human_walkmode_offset);
    };
    basierend auf mud-freaks GFA code. Daher stammt auch die verwendete Konstante oCAniCtrl_Human_walkmode_offset.
    Geändert von Milky-Way (11.10.2020 um 00:29 Uhr)

  18. #18
    Falugify
    Gast
     
    Zitat Zitat von Milky-Way Beitrag anzeigen
    Hier dann der Code, mit dem schlafende NPCs geweckt werden, wenn der Spieler straft, rückwärts geht (mit oder ohne Wafe) oder springt (mit oder ohne Akrobatik). Gedacht ist der Code für Modder, die diese Funktionalität erzwingen wollen, damit sich z.B. schleichen lernen tatsächlich lohnt. Für Spieler gibt es bereits den Union Patch, wenn sie sich selbst diese Möglichkeit nehmen wollen. Ich habe mich nur dafür interessiert, Menschen zu wecken. Für Monster muss man eventuell auch noch in der B_MM_AssessEnemy ansetzen.

    Der Code besteht aus 2 Teilen:
    1) Hooks senden Wahrnehmung und setzen Variable
    2) in B_AssessEnterRoom wird ein Sonderfall für die Variable eingebaut, um trotz BS_Stand am Helden den Npc zu wecken

    Hooks initialisieren:
    Code:
        // prevent player from strafing his way to avoid detection without sneaking
        StrafePerception_Init();
        // prevent player from jumping his way to avoid detection without sneaking
        JumpPerception_Init();
        // prevent player from going backwards to avoid detection without sneaking
        GoBackwardPerception_Init();
    rufen Funktionen auf:
    Code:
    var int hero_strafing;
    var int hero_jumping;
    var int hero_gobackward;
    
    func void StrafePerception() {
    
        // exit function if player is in sneak mode
        if Npc_GetWalkMode(hero) == NPC_SNEAK
        {
            return;
        };
    
        const int PERC_INVERVAL = 1000; // As in oCAIHuman::CreateFootStepSound
        var int percTimer; percTimer += MEM_Timer.frameTime;
        if (percTimer >= PERC_INVERVAL) {
            percTimer -= PERC_INVERVAL;
            hero_strafing = TRUE;
            Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, hero, hero);
            hero_strafing = FALSE;
        };
    };
    func void StrafePerception_Init() {
        const int oCNpc__EV_STRAFE_player_G1 = 7662588; //0x74EBFC
        const int oCNpc__EV_STRAFE_player_G2 = 6834660; //0x6849E4
        HookEngineF(MEMINT_SwitchG1G2(oCNpc__EV_STRAFE_player_G1, oCNpc__EV_STRAFE_player_G2), 7, StrafePerception);
    };
    
    func void JumpPerception() {
        hero_jumping = TRUE;
        Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, hero, hero);
        hero_jumping = FALSE;
    };
    func void JumpPerception_Init() {
        const int oCAniCtrl_Human__PC_JumpForward_G2 = 7020032; //0x6B1E00
        HookEngineF(oCAniCtrl_Human__PC_JumpForward_G2, 5, JumpPerception);
    };
    
    func void GoBackwardPerception() {
    
        // exit function if player is in sneak mode
        if Npc_GetWalkMode(hero) == NPC_SNEAK
        {
            return;
        };
        hero_gobackward = TRUE;
        Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, hero, hero);
        hero_gobackward = FALSE;
    };
    func void GoBackwardPerception_Init() {
        const int oCAniCtrl_Human__PC_GoBackward_G2 = 7019968; //0x6B1DC0
        HookEngineF(oCAniCtrl_Human__PC_GoBackward_G2, 5, GoBackwardPerception);
    
        // hooks in oCAIHuman::FightMelee (backwards with weapon drawn)
        HookEngineF(6911940 /* 006977c4 */, 5, GoBackwardPerception); // Gothic 1 Steuerung (?)
        HookEngineF(6914029 /* 00697fed */, 5, GoBackwardPerception); // (?)
        HookEngineF(6914536 /* 006981e8 */, 6, GoBackwardPerception); // Gothic 2 Steuerung (?)
    };
    und dann die B_AssessEnterRoom anpassen:
    Code:
    	if (C_BodyStateContains (other, BS_SNEAK) 
    		|| (C_BodyStateContains (other, BS_STAND) 
    			&& !hero_strafing 
    			&& !hero_jumping 
    			&& !hero_gobackward))
    	{
    		if (!Npc_CanSeeNpc (self, other) && !Npc_IsInState (self, ZS_ObservePlayer))
    		{
    			return FALSE;
    		};
    	};
    statt
    Code:
    	if (C_BodyStateContains (other, BS_SNEAK) || (C_BodyStateContains (other, BS_STAND))
    	{
    		if (!Npc_CanSeeNpc (self, other) && !Npc_IsInState (self, ZS_ObservePlayer))
    		{
    			return FALSE;
    		};
    	};
    Nur die Wahrnehmung auszulösen reicht nicht aus, da sonst in B_AssessEnterRoom die Abfrage immernoch an C_BodyStateContains (other, BS_STAND) hängenbleibt, wenn der Npc in einem Bett schläft und so ausgerichtet ist, dass er den Spieler nicht "sehen" kann.

    Falls obiger Code nicht in der originalen B_AssessEnterRoom steht, müsste man im unveränderten Code noch mal gucken, wo man da ansetzen kann / muss.

    PS: es fehlte noch die Funktion Npc_GetWalkMode. Im Forum findet man eine ältere Variante, die ich mal mit Sektenspinners Hilfe geschrieben hatte. Etwas performanter ist vermutlich:
    Code:
    FUNC int Npc_GetWalkMode (VAR C_NPC slf)
    {
        var oCNpc oCNpc_slf;
        oCNpc_slf = Hlp_GetNpc(slf);
    
        var int wmode;
        const int oCAniCtrl_Human_walkmode_offset            = 352; //0x0160
        return MEM_ReadInt(oCNpc_slf.anictrl+oCAniCtrl_Human_walkmode_offset);
    ;
    basierend auf mud-freaks GFA code. Daher stammt auch die verwendete Konstante oCAniCtrl_Human_walkmode_offset.
    Vielen Dank fürs teilen
    Ich habe es fix ausprobiert (auch B_MM_AssessEnemy) und im ersten Eindruck scheint die Lösung um einiges eleganter zu sein als das was ich vorher hatte (Animationen in einer FrameFunction und ähnliches abfragen) Dadurch konnte ich bisher nur das Verhalten in Räumen anpassen, mit der Wahrnehmung der Tiere war ich mit meiner Lösung aber nicht ganz zufrieden. Mit deiner Methode klappt es nun auch, dass Monster den Helden wahrnehmen - zum Beispiel, wenn ein Schattenläufer schläft und der Held gerade straft.

    Ein kleiner Hinweis: bei Npc_GetWalkMode hast du zum Abschluss die Klammer vergessen.
    Code:
    FUNC int Npc_GetWalkMode (VAR C_NPC slf)
    {
        var oCNpc oCNpc_slf;
        oCNpc_slf = Hlp_GetNpc(slf);
    
        var int wmode;
        const int oCAniCtrl_Human_walkmode_offset            = 352; //0x0160
        return MEM_ReadInt(oCNpc_slf.anictrl+oCAniCtrl_Human_walkmode_offset);
    };


    Welchen Unterschied gibt es zwischen den beiden Varianten?
    Code:
    Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, hero, hero);
    und
    Code:
    Npc_SendPassivePerc(hero, PERC_ASSESSQUIETSOUND, NULL, hero);

    Hero ist in dem Sinne kein Opfer. Wird aber wohl keinen Unterschied machen, oder?

    Wenn man es ganz genau machen möchte: Durch das nach Vorne schlagen mit Waffen könnte man sich eventuell noch heranschleichen (Es wird wohl nur beim Betreten eines Portals erkannt), vielleicht kannst du dir das mal ansehen? Ansonsten würde ich sagen, es wurde alles abgedeckt, was mir bekannt ist.
    Geändert von Falugify (28.09.2020 um 03:41 Uhr)

  19. #19
    Falugify
    Gast
     
    Ich habe die Hooks wieder entfernt. Ich musste feststellen, dass es Probleme mit der Kollision gibt. Wenn das Gebiet hinter einem höher geht und man zurück geht, glitcht man durch die Welt.

  20. Beiträge anzeigen #20
    now also in your universe  Avatar von Milky-Way
    Registriert seit
    Jun 2007
    Beiträge
    15.251
     
    Milky-Way ist offline
    Zitat Zitat von Falugify Beitrag anzeigen
    Ich habe die Hooks wieder entfernt. Ich musste feststellen, dass es Probleme mit der Kollision gibt. Wenn das Gebiet hinter einem höher geht und man zurück geht, glitcht man durch die Welt.
    Danke für die Warnung! Das war mir bei meinen kurzen Tests nicht aufgefallen und ich verstehe auch nich ganz, was da passiert. (Sollten die hooks die sonstigen Funktionen nicht unangetastet lassen? Oder überschreibt das Senden der perception interne Variablen, die LeGo nicht automatisch zurücksetzt / zurücksetzen kann?)

    mit oder ohne Waffe?

    rückwärts gegen eine Wand oder eine Steigung, die man vorwärts normal hoch gehen könnte?

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