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 4 von 27 « Erste 123456781115 ... Letzte »
Ergebnis 61 bis 80 von 538
  1. Beiträge anzeigen #61 Zitieren
    Knight Commander Avatar von Neconspictor
    Registriert seit
    Jan 2009
    Beiträge
    2.749
     
    Neconspictor ist offline
    Zitat Zitat von Milky-Way Beitrag anzeigen
    In der neuesten LeGo-Version gibt es nicht mehr
    Code:
    const int ASMINT_OP_addMemToESP = 9475;  //0x2503
    const int ASMINT_OP_movESItoEAX = 61577; //0xF089
    const int ASMINT_OP_movEAXtoEDI = 51081; //0xC789
    als Teil der Hook-Engine. Hat es damit noch mehr auf sich?

    Ich frage, weil ich gerade in den LoA-Skripten das hier entdeckt habe:
    Code:
    var int RET;
    func void HookByConditionalFunctionI (var int address, var int oldInstr, var int jumpAddress, var int function) {
        var int SymbID;
        var int ptr;	//ptr to store old instruction
        var int relAdr;	//Difference for jmp instruction
    
    	
        // ----- Sicherheitsabfragen -----
        if(oldInstr < 5) {
            PrintDebug("HOOKENGINE: oldInstr ist zu kurz. Es werden mindestens 5 Bytes erwartet.");
            return;
        };
    
        SymbID = function;
        if(SymbID == -1) {
            PrintDebug("HOOKENGINE: Die gegebene Daedalusfunktion kann nicht gefunden werden.");
            return;
        };
    	
        // ----- Eventuell geschützen Speicher behandeln -----
        MemoryProtectionOverride (address, oldInstr+3);
    	
        // ----- Die alte Anweisung sichern -----
        ptr = MEM_Alloc(oldInstr);
        MEM_CopyBytes(address, ptr, oldInstr);
    	
        // ----- Einen neuen Stream für den Assemblercode anlegen -----
        ASM_Open(200 + oldInstr); // Play it safe.
    	
        // ----- Jump aus der Enginefunktion in den neuen Code einfügen -----
        relAdr = ASMINT_CurrRun-address-5;
        MEM_WriteInt(address, 233);
        MEM_WriteInt(address + 1, relAdr);
    	
        // Alle Register sichern
    
        // EAX in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movEAXToMem);
        ASM_4(_@(EAX));
        ASM_1(ASMINT_OP_pusha);
    
        // ECX in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movECXtoEAX);
        ASM_2(ASMINT_OP_movEAXToMem);
        ASM_4(_@(ECX));
    
        // ESP in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movESPtoEAX);
        ASM_2(ASMINT_OP_addImToEAX);
        ASM_1(4*8);                  // Wegen pushad [Danke an Sektenspinner]
        ASM_2(ASMINT_OP_movEAXToMem);
        ASM_4(_@(ESP));
    
        // EBX in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movEBXtoEAX);
        ASM_2(ASMINT_OP_movEAXtoMem);
        ASM_4(_@(EBX));
    
        // EBP in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movEBPtoEAX);
        ASM_2(ASMINT_OP_movEAXtoMem);
        ASM_4(_@(EBP));
    
        // EDI in Daedalus Variable sichern
        ASM_2(ASMINT_OP_movEDItoEAX);
        ASM_2(ASMINT_OP_movEAXtoMem);
        ASM_4(_@(EDI));
    	
        // ESI in Daedalus Variable sichern	
        ASM_2(ASMINT_OP_movESItoEAX);
        ASM_2(ASMINT_OP_movEAXtoMem);
        ASM_4(_@(ESI));
    	
        //Daedalus-Funktion aufrufen
    	
        ASM_1(ASMINT_OP_pushIm);
        ASM_4(SymbID);	
    
        ASM_1(ASMINT_OP_pushIm);
        ASM_4(parser);
    
        ASM_1(ASMINT_OP_call);
        ASM_4(zParser__CallFunc-ASM_Here()-4);
    	
        ASM_2(ASMINT_OP_addImToESP);
        ASM_1(8);
    	
        // teste die Rückgabe RET auf ihren Wahrheitsgehalt
        ASM_1(ASMINT_OP_movMemToEAX);
        ASM_4(_@(RET));
    	
        //85 c0                   test   eax,eax 
        ASM_1(133);
        ASM_1(192);
    	
        ASM_1(ASMINT_OP_popa);
    	
        // Register widerherstellen
        ASM_1(ASMINT_OP_movMemToEAX);
        ASM_4(_@(ECX));
        ASM_2(ASMINT_OP_movEAXtoECX);
    		
        ASM_1(ASMINT_OP_movMemToEax);
        ASM_4(_@(EDI));
        ASM_2(ASMINT_OP_movEAXtoEDI);
    
        ASM_1(ASMINT_OP_movMemToEAX);
        ASM_4(_@(EAX));
    	
        //Do conditional jump; execute original code, if RET is FALSE
        //74 05       jz     6
        ASM_1(116);
        ASM_1(6);
    	
        // RET ist wahr -> überspringe den originalen Code
        ASM_1(ASMINT_OP_pushIm);
        ASM_4(jumpAddress);
        ASM_1(ASMINT_OP_retn);
    	
        // RET ist falsch -> führe die Funktion normal aus
        // Alte Anweisung wieder einfügen
        MEM_CopyBytes(ptr, ASMINT_Cursor, oldInstr);
        MEM_Free(ptr);
    
        ASMINT_Cursor += oldInstr;
    	
        ASM_1(ASMINT_OP_pushIm);
        ASM_4(address + oldInstr);
        ASM_1(ASMINT_OP_retn);
    	
    	var int i; i = ASM_Close();
    };
    
    func void HookByConditionalFunctionF (var int address, var int oldInstr, var int jumpAddress, var func function) {
        HookByConditionalFunctionI(address, oldInstr, jumpAddress, MEM_GetFuncID(function));
    };
    
    func void HookByConditionalFunction(var int address, var int oldInstr, var int jumpAddress, var string function) {
        HookByConditionalFunctionI(address, oldInstr, jumpAddress, MEM_FindParserSymbol(STR_Upper(function)));
    };
    was diese Variablen genutzt hat. Allerdings kann ich auch nicht sagen, was dieser Code hier macht Vielleicht findet es ja jemand raus und findet es nützlich...



    Der Code ist von mir, deshalb gib'ts den nicht in der neuen LeGo Version Ich hätte den Code besser wo anders hingetan

    Die Funktion macht folgendes: Zuerst wird eine Deadalus Funktion als Hook ausgeführt. Diese Funktion muss zwingend einen boolschen Wert zurückgeben.
    Bei FALSE wird die gehookte Funktion normal ausgeführt. Bei TRUE wird die gehookte Funktion übersprungen/nicht ausgeführt. Stattdessen wird direkt zur Addresse "jumpAddress" gesprungen.

    Die Funktion kann man verwenden, wenn man eine Funktion nur zu bestimmten Bedingungen ausführen möchte. Man muss aber höllisch aufpassen, was man hookt. Z.B. wenn eine Funktion einen Rückgabewert hat, dann muss man das selbst regeln.

  2. Beiträge anzeigen #62 Zitieren
    Knight
    Registriert seit
    Aug 2009
    Ort
    Hessen
    Beiträge
    1.484
     
    Cryp18Struct ist offline
    Nach levelchange/.zen Wechsel bekomme ich
    Code:
    [w] 07:56 Warn:  0 C:     SCRIPT: Npc_SetPercTime(): illegal param: "PC_HERO" is NULL. .... <oGameExternal.cpp,#252>
    [w] 07:56 Warn:  0 C:     SCRIPT: last parser func-name: _HOOK .... <oGameExternal.cpp,#262>
    einmal pro frame(?) in den zSpy.

    Mir ist jetzt unklar warum PC_HERO an der Stelle NULL sein kann, ich steuere ihn ja noch. Auch welcher Aufruf von Npc_SetPercTime das auslöst ist mir unklar, soweit ich das sehen kann wird das nur zu Beginn von ZS für self gemacht wenn Perceptions gesetzt werden.

    Hat da jemand einen debug Ansatz?

    edit: Ich habe jetzt die ID
    Code:
            printDebug(ConcatStrings("before call", IntToString(MEM_ReadIntArray(a.array, i))));
            MEM_CallByID(MEM_ReadIntArray(a.array, i));
    	printDebug("after call");
    edit2: ist eine meiner FrameFunctions.
    kann ich in
    Code:
    func int FrameFunctions(var int hndl) {
    den Namen der Funktion aus itm.fncID rekonstruieren?
    Geändert von Cryp18Struct (20.02.2018 um 12:44 Uhr)

  3. Beiträge anzeigen #63 Zitieren
    Dea
    Registriert seit
    Jul 2007
    Beiträge
    10.446
     
    Lehona ist offline
    Code:
    var zCPar_Symbol symb; symb = _^(MEM_GetSymbolByIndex(fncID);
    print(symb.name);
    So solltest du den schuldigen finden. Ich sehe jetzt allerdings auch nicht, was das auslösen sollte. Aber vielleicht findest du ja was.

  4. Beiträge anzeigen #64 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    PC_Hero ist nach Levelchange "leer". Es sollte möglichst immer hero stattdessen verwendet werden.

  5. Beiträge anzeigen #65 Zitieren
    Schwertmeister Avatar von Ska-Ara
    Registriert seit
    Nov 2010
    Ort
    Bayern
    Beiträge
    793
     
    Ska-Ara ist offline
    Kann man mit Lego eigentlich eine Kolonie Barriere erschaffen? Sowohl optisch als auch mit HP Verlust?

  6. Beiträge anzeigen #66 Zitieren
    Held Avatar von GiftGrün
    Registriert seit
    Jun 2011
    Ort
    Gewächshaus des Assassinen
    Beiträge
    5.010
     
    GiftGrün ist offline
    Gibt es eine Möglichkeit, im Menü Werte wie Schwierigkeitsgrad für ein neues Spiel einzustellen, ohne den Umweg über die Gothic.ini zu gehen? Würde ungerne für einen Wert, den ich einmal brauche, anderer Leute ini-Dateien zumüllen.

    Ich habe bereits ein Menü, in der der Spieler eine Variable DifficultyLevel mit der gewünschten Schwierigkeit befüllt, allerdings ist diese Variable nur im Menü abrufbar, im Spiel ist sie gar nicht vorhanden, es sei denn, ich deklariere sie in, sagen wir, Story_Globals neu (ich kann also zwei Variablen mit demselben Namen haben, solange eine in System\Menu\ und die andere irgendwo im Content-Ordner ist, was mich zu dem Schluss führt, dass alle Menüvariablen beim Starten eines neuen Spiels gelöscht oder überschrieben werden).

    Irgendwelche Ideen?
    “Da ist auch noch ein anderer Geruch in der Luft, der Geruch von Feuern, die in der Ferne brennen, mit einem Hauch Zimt darin - so riecht das Abenteuer!”
    ― aus Walter Moers' "Die 13 1/2 Leben des Käpt'n Blaubär"

  7. Beiträge anzeigen #67 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von GiftGrün Beitrag anzeigen
    Gibt es eine Möglichkeit, im Menü Werte wie Schwierigkeitsgrad für ein neues Spiel einzustellen, ohne den Umweg über die Gothic.ini zu gehen? Würde ungerne für einen Wert, den ich einmal brauche, anderer Leute ini-Dateien zumüllen.

    Ich habe bereits ein Menü, in der der Spieler eine Variable DifficultyLevel mit der gewünschten Schwierigkeit befüllt, allerdings ist diese Variable nur im Menü abrufbar, im Spiel ist sie gar nicht vorhanden, es sei denn, ich deklariere sie in, sagen wir, Story_Globals neu (ich kann also zwei Variablen mit demselben Namen haben, solange eine in System\Menu\ und die andere irgendwo im Content-Ordner ist, was mich zu dem Schluss führt, dass alle Menüvariablen beim Starten eines neuen Spiels gelöscht oder überschrieben werden).

    Irgendwelche Ideen?
    Hier eine Veranschaulichung: Die Skripte in Gothic werden in verschiedenen Eimern gesammelt (insgesamt acht Stück). In einem Eimer sind die Content-Skripte (womit man am meisten arbeitet; Dialoge, Quests, NPCs,..) in einem anderen sind die Menü-Skripte, in einem anderen die PartikelFX-Skripte, usw.
    Sitzt man in einem Eimer kann man nicht über den "Eimerrand" hinausschauen, d.h. man kann die Variablen, Konstanten, Funktionen, Instanzen, Klassen, Prototypen vom Menü-Eimer nicht aus dem Content-Eimer sehen.
    Wir müssten also kurzzeitig in den Menü-Eimer springen suchen was wir brauchen und zurückspringen. Das Problem daran ist das Zurückspringen. Denn sobald wir einmal im Menü-Eimer sitzen schlägt das Ausführen weiteres Content-Skript-Codes logischerweise fehl (da müsste ein Fehler kommen wie "Error DoStack").
    Ich habe eine Funktion mit Ikarus geschrieben, die da Abhilfe schafft (sie springt nicht in einen anderen Eimer, sondern "guckt" durch die Eimer hindurch, bzw. lässt das die Engine selbst machen, wir müssen nicht selbst zurückspringen). Weil das recht nützlich ist, habe ich die Funktion GetAnyParserSymbol im ScriptBin-Thread gepostet.

    Hier ist, wie du das in deinem Fall nutzen kannst:

    Code:
    // Globale Variable für die Schwierigkeit (definiert in bspw. Stroy_Globals.d)
    var int diffLevel;
    
    
    func void INIT_Global() {
    
        // ...
    
        // Bei neuem Spielstart: DiffLevel ist noch nicht definiert
        if (!diffLevel) {
            // Suche in den Menü-Skripten nach "MENU_DIFFLEVEL" (gross geschrieben)
            var int symPtr; symPtr = GetAnyParserSymbol(MEM_ReadInt(menuParserPointerAddress), "MENU_DIFFLEVEL");
            if (symPtr) {
                var zCPar_Symbol symb; symb = _^(symPtr);
                diffLevel = symb.content;
            } else {
                /* Menü-Variable wurde nicht gefunden
                   Platz für eine Fehlermeldung oder das Setzten eines Default-Schwierigkeitsgrades */
            };
        };
    };
    Schwierigkeitsgrade sollten hier als nicht-null definiert werden, sonst könnte es zu Problemen kommen. Gut wäre z.B.
    Code:
    Einfach: diffLevel == 1
    Mittel:  diffLevel == 2
    Schwer:  diffLevel == 3

  8. Homepage besuchen Beiträge anzeigen #68 Zitieren
    General Avatar von Dada
    Registriert seit
    Jan 2007
    Ort
    Krefeld
    Beiträge
    3.729
     
    Dada ist offline
    Ich habe das (vielleicht) etwas einfacher gelöst.
    In den Menü-Skripten habe ich einen weiteren Einstellungspunkt angelegt, worüber man die Schwierigkeit einstellen kann.

    In einer FrameFunction lasse ich nun jeden Frame die gesetzte Einstellung überprüfen. Der Schwierigkeitsgrad wird ja in die Ini-Datei geschrieben und kann daher fix ausgelesen werden:

    Code:
    func void setDifficulty()
    {
        var int difficulty;
        difficulty = STR_ToInt(MEM_GetGothOpt("MAHLENDUR", "difficulty"));
    
        // Do Stuff...
    };
    Durch den ersten Start mit den geänderten Menü-Skripten, werden die Grundwerte automatisch eingetragen und daher ist auch keine Fehlerbehandlung ob "0" nötig.

  9. Beiträge anzeigen #69 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Dada Beitrag anzeigen
    Ich habe das (vielleicht) etwas einfacher gelöst.
    In den Menü-Skripten habe ich einen weiteren Einstellungspunkt angelegt, worüber man die Schwierigkeit einstellen kann.

    In einer FrameFunction lasse ich nun jeden Frame die gesetzte Einstellung überprüfen. Der Schwierigkeitsgrad wird ja in die Ini-Datei geschrieben und kann daher fix ausgelesen werden:

    Code:
    func void setDifficulty()
    {
        var int difficulty;
        difficulty = STR_ToInt(MEM_GetGothOpt("MAHLENDUR", "difficulty"));
    
        // Do Stuff...
    };
    Durch den ersten Start mit den geänderten Menü-Skripten, werden die Grundwerte automatisch eingetragen und daher ist auch keine Fehlerbehandlung ob "0" nötig.
    Ja ich denke deins ist die vernünftige Standardlösung. Die Alternative, die ich gepostet habe, bezog sich auf GiftGrüns Anliegen:

    Zitat Zitat von GiftGrün Beitrag anzeigen
    Gibt es eine Möglichkeit, im Menü Werte wie Schwierigkeitsgrad für ein neues Spiel einzustellen, ohne den Umweg über die Gothic.ini zu gehen? Würde ungerne für einen Wert, den ich einmal brauche, anderer Leute ini-Dateien zumüllen.

  10. Homepage besuchen Beiträge anzeigen #70 Zitieren
    General Avatar von Dada
    Registriert seit
    Jan 2007
    Ort
    Krefeld
    Beiträge
    3.729
     
    Dada ist offline
    Oha, dann habe ich das wohl überlesen.
    Verzeiht mir

  11. Homepage besuchen Beiträge anzeigen #71 Zitieren
    Clockwork Origins Avatar von Bonne6
    Registriert seit
    Jun 2004
    Ort
    Erlangen
    Beiträge
    11.826
     
    Bonne6 ist offline
    In Commit #166 wurde was beim Laden von QuickSaves behoben. Kann mir da jemand sagen, was genau das Problem war (also welche Auswirkungen das hatte)?

  12. Beiträge anzeigen #72 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Bonne6 Beitrag anzeigen
    In Commit #166 wurde was beim Laden von QuickSaves behoben. Kann mir da jemand sagen, was genau das Problem war (also welche Auswirkungen das hatte)?
    Hattest du das schon gelesen?

    Zitat Zitat von mud-freak Beitrag anzeigen
    Das Quicksave-Laden wird von LeGo nicht vernünftig erkannt. Der ermittelte Ladeslot ist bei Quicksaves inkorrekt, sodass SCRPTSAVE.SAV aus einem der anderen Save-Ordner geladen wird und somit sämtliche PermMem-Handles (meist) falsch sind.
    Zitat Zitat von mud-freak Beitrag anzeigen
    Das Problem ist die Verwendung des letzten Menü-Elements im Loading-Menu zur Bestimmung des Lade-Slots (_BR_GetSelectedSlot). Das stimmt im Falle von einem Quicksave natürlich nicht, da es meist nicht vom Menü gestartet wird.
    Vor dem Fix wurden also jegliche PermMem-Handles (gespeicherte FrameFunctions, Views, Anim8, ...) falsch oder gar nicht geladen, wenn man per Quickload (F9) geladen hat.
    Wenn eine Mod nur zu Spielbeginn solche Handles erstellt (z.B. FrameFunctions die von Anfang bis Ende des Spiels durchgängig laufen) dann war die Mod nicht merklich betroffen. Wenn so etwas aber Story abhängig benutzt wird (z.B. FrameFunctions, die erst nach einem Dialog an-/ausspringen), war es problematisch. Es war nur der Quickslot betroffen und nur wenn man nicht aus dem Menü, sondern per F9 geladen hat.
    Mittlerweile aber alles behoben, d.h. im dev-Branch.

  13. Homepage besuchen Beiträge anzeigen #73 Zitieren
    Clockwork Origins Avatar von Bonne6
    Registriert seit
    Jun 2004
    Ort
    Erlangen
    Beiträge
    11.826
     
    Bonne6 ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Hattest du das schon gelesen?





    Vor dem Fix wurden also jegliche PermMem-Handles (gespeicherte FrameFunctions, Views, Anim8, ...) falsch oder gar nicht geladen, wenn man per Quickload (F9) geladen hat.
    Wenn eine Mod nur zu Spielbeginn solche Handles erstellt (z.B. FrameFunctions die von Anfang bis Ende des Spiels durchgängig laufen) dann war die Mod nicht merklich betroffen. Wenn so etwas aber Story abhängig benutzt wird (z.B. FrameFunctions, die erst nach einem Dialog an-/ausspringen), war es problematisch. Es war nur der Quickslot betroffen und nur wenn man nicht aus dem Menü, sondern per F9 geladen hat.
    Mittlerweile aber alles behoben, d.h. im dev-Branch.
    Okay, das betrifft XR also und ist äußerst kritisch

  14. Beiträge anzeigen #74 Zitieren
    Knight
    Registriert seit
    Aug 2009
    Ort
    Hessen
    Beiträge
    1.484
     
    Cryp18Struct ist offline
    Ich möchte verhindern das nach Verschießen des letzten Pfeils der Bogen automatisch weggesteckt wird. An der Stelle möchte ich auf eine andere Munition wechseln falls im Inventar verfügbar.
    Kann mir da jemand einen hook für basteln?

  15. Beiträge anzeigen #75 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Cryp18Struct Beitrag anzeigen
    Ich möchte verhindern das nach Verschießen des letzten Pfeils der Bogen automatisch weggesteckt wird. An der Stelle möchte ich auf eine andere Munition wechseln falls im Inventar verfügbar.
    Kann mir da jemand einen hook für basteln?
    Dazu braucht man fast keinen Hook. Vielleicht ist dir schon einmal aufgefallen, dass sobald man keine Munition mehr hat im zSpy die Nachricht kommt "Engine calls an undefined function".
    Du kannst eine Daedalus-Funktion namens Player_Ranged_No_Ammo erstellen, die dann aufgerufen wird. Darin sind dann self der NPC in Frage (immer hero, weil die Funktion nur für den Spieler aufgerufen wird) und other ist auch hero. So etwas ist also ohne weiteres möglich:
    Code:
    func void Player_Ranged_No_Ammo() {
        var C_Item weapon;
        if (Npc_IsInFightMode(self, FMODE_FAR)) {
            weapon = Npc_GetReadiedWeapon(self);
        } else if (Npc_HasEquippedRangedWeapon(self)) {
            weapon = Npc_GetEquippedRangedWeapon(self);
        } else {
            return;
        };
    
        // Creating munition
        CreateInvItems(self, weapon.munition, 100);
    };
    Hier der Haken, um auf das "fast" im ersten Satz zurück zu kommen: Die Fernkampfwaffe wird trotzdem weggesteckt, man muss sie erneut ziehen. Eine Idee wäre da, diesen ungenutzten Funktionsaufruf in der Engine umzuschreiben (z.B. mit erwartetem Rückgabewert, ob neue/andere Munition bereit gestellt werden kann). Vielleicht schaue ich mir das heute Nachmittag mal an.

  16. Beiträge anzeigen #76 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Ich hab mir jetzt mal ein paar Minuten genommen, viel Arbeit war das nicht:

    Die Funktion Player_Ranged_No_Ammo wird nach wie vor dann aufgerufen, wenn der Spieler keine passende Munition mehr im Inventar hat. Unterschied ist, dass der Rückgabewert der Funktion (true/false) angibt, ob nun plötzlich doch Munition im Inventar vorhanden ist. Ausserdem werden jetzt self und other nicht vernünftig befüllt, sondern man sollte ausschliesslich auf hero zurückgreifen (wird so wie so nur für den Spieler aufgerufen).
    Das Schöne: Nach wie vor ist es kein Problem wenn die Funktion nicht existiert. Es ändert sich also erst einmal überhaupt nichts.

    Ein Code-Beispiel: Wenn der Spieler keine passende Munition hat, werden davon 100 neue ins Inventar gelegt und true zurückgegeben; der Spieler kann also nahtlos weiterschiessen. (Den Munitionstyp zu ändern klappt so auch ganz einfach.)
    Code:
    func int Player_Ranged_No_Ammo() {
        var C_Item weapon;
        if (Npc_IsInFightMode(hero, FMODE_FAR)) {
            weapon = Npc_GetReadiedWeapon(hero);
        } else if (Npc_HasEquippedRangedWeapon(hero)) {
            weapon = Npc_GetEquippedRangedWeapon(hero);
        } else {
            return FALSE;
        };
    
        // Creating munition
        CreateInvItems(hero, weapon.munition, 100);
        return TRUE;
    };


    Hier die nötige Änderung:
    Code:
    func void Player_Ranged_No_Ammo_Init() {
        const int once = 0;
        if (once) {
            return;
        };
        once = 1;
    
        const int oCNpc__IsMunitionAvailable_playerInv = 7587821; //0x73C7ED
        const int opcode[5] = { -1424059288, 1086372096, -722992981, -1962932895, -1874531536 };
        MemoryProtectionOverride(oCNpc__IsMunitionAvailable_playerInv, 20);
        MEM_CopyBytes(_@(opcode), oCNpc__IsMunitionAvailable_playerInv, 20);
    };

    Initialisieren in Init_Global mit: Player_Ranged_No_Ammo_Init();


    Ein kleines Wort der Warnung: Man kann damit Blödsinn anstellen, wenn man true zurück gibt, aber keine neue Munition zur Verfügung stellt. Was genau dann passiert, habe ich nicht getestet, aber sollte ja eigentlich nicht vorkommen. Ebenso ist es sinnlos, false zurück zu geben, wenn dennoch Munition vorhanden ist (in dem Fall wird die Waffe weggesteckt, aber der Held hält die Munition in der Hand). Beides bei korrekter Benutzung kein Problem.



    EDIT: Hab ich vergessen: Für mehr Transparenz ist hier der eingefügte Opcode in Assembly:
    Code:
    0x73C7ED   68 94 1E AB 00   push    0xAB1E94      # offset "PLAYER_RANGED_NO_AMMO"
    0x73C7F2   B9 C0 40 AB 00   mov     ecx, 0xAB40C0 # offset zCParser parser
    0x73C7F7   E8 D4 61 05 00   call    0x7929D0      # zCParser::CallFunc(zSTRING const &)
    0x73C7FC   8B 30            mov     esi, [eax]
    0x73C7FE   EB 44            jmp     short 0x73C844
    Geändert von mud-freak (27.02.2018 um 13:12 Uhr)

  17. Beiträge anzeigen #77 Zitieren
    Knight
    Registriert seit
    Aug 2009
    Ort
    Hessen
    Beiträge
    1.484
     
    Cryp18Struct ist offline
    Vielen Dank mud-freak!

    Schönheitsfehler: wenn ich False zurück gebe wird die Waffe nicht weggesteckt. Auch kann man, wenn keine Munition im Inventar ist, die Waffe zücken und "schießen"(Animation wird abgespielt aber kein Projektil wird abgefeuert).
    AI_RemoveWeapon(hero); vor return false; ergänzen behebt das in etwa.

    Code:
    func int Player_Ranged_No_Ammo() {
        var C_Item weapon;
        if (Npc_IsInFightMode(hero, FMODE_FAR)) {
            weapon = Npc_GetReadiedWeapon(hero);
        } else if (Npc_HasEquippedRangedWeapon(hero)) {
            weapon = Npc_GetEquippedRangedWeapon(hero);
        } else {
            return FALSE;
        };
    
        //AI_RemoveWeapon(hero);
        return false;
    };

  18. Beiträge anzeigen #78 Zitieren
    Ehrengarde Avatar von mud-freak
    Registriert seit
    Dec 2005
    Beiträge
    2.199
     
    mud-freak ist offline
    Zitat Zitat von Cryp18Struct Beitrag anzeigen
    Vielen Dank mud-freak!

    Schönheitsfehler: wenn ich False zurück gebe wird die Waffe nicht weggesteckt. Auch kann man, wenn keine Munition im Inventar ist, die Waffe zücken und "schießen"(Animation wird abgespielt aber kein Projektil wird abgefeuert).
    War ein bisschen mehr als ein Schönheitsfehler. Der Rückgabewert wurde "ignoriert". Jetzt sollte es klappen (Code oben korrigiert).
    Danke für die Rückmeldung!

  19. Beiträge anzeigen #79 Zitieren
    Knight
    Registriert seit
    Aug 2009
    Ort
    Hessen
    Beiträge
    1.484
     
    Cryp18Struct ist offline
    Funktioniert nun perfekt! Vielen Dank.

  20. Beiträge anzeigen #80 Zitieren
    Held Avatar von GiftGrün
    Registriert seit
    Jun 2011
    Ort
    Gewächshaus des Assassinen
    Beiträge
    5.010
     
    GiftGrün ist offline
    Zitat Zitat von mud-freak Beitrag anzeigen
    Hier eine Veranschaulichung: Die Skripte in Gothic werden in verschiedenen Eimern gesammelt (insgesamt acht Stück). In einem Eimer sind die Content-Skripte (womit man am meisten arbeitet; Dialoge, Quests, NPCs,..) in einem anderen sind die Menü-Skripte, in einem anderen die PartikelFX-Skripte, usw.
    Sitzt man in einem Eimer kann man nicht über den "Eimerrand" hinausschauen, d.h. man kann die Variablen, Konstanten, Funktionen, Instanzen, Klassen, Prototypen vom Menü-Eimer nicht aus dem Content-Eimer sehen.
    Wir müssten also kurzzeitig in den Menü-Eimer springen suchen was wir brauchen und zurückspringen. Das Problem daran ist das Zurückspringen. Denn sobald wir einmal im Menü-Eimer sitzen schlägt das Ausführen weiteres Content-Skript-Codes logischerweise fehl (da müsste ein Fehler kommen wie "Error DoStack").
    Ich habe eine Funktion mit Ikarus geschrieben, die da Abhilfe schafft (sie springt nicht in einen anderen Eimer, sondern "guckt" durch die Eimer hindurch, bzw. lässt das die Engine selbst machen, wir müssen nicht selbst zurückspringen). Weil das recht nützlich ist, habe ich die Funktion GetAnyParserSymbol im ScriptBin-Thread gepostet.

    Hier ist, wie du das in deinem Fall nutzen kannst:

    Code:
    // Globale Variable für die Schwierigkeit (definiert in bspw. Stroy_Globals.d)
    var int diffLevel;
    
    
    func void INIT_Global() {
    
        // ...
    
        // Bei neuem Spielstart: DiffLevel ist noch nicht definiert
        if (!diffLevel) {
            // Suche in den Menü-Skripten nach "MENU_DIFFLEVEL" (gross geschrieben)
            var int symPtr; symPtr = GetAnyParserSymbol(MEM_ReadInt(menuParserPointerAddress), "MENU_DIFFLEVEL");
            if (symPtr) {
                var zCPar_Symbol symb; symb = _^(symPtr);
                diffLevel = symb.content;
            } else {
                /* Menü-Variable wurde nicht gefunden
                   Platz für eine Fehlermeldung oder das Setzten eines Default-Schwierigkeitsgrades */
            };
        };
    };
    Sorry, habe ganz vergessen, mich bei dir zu bedanken, mud-freak! Es funktioniert hervorragend!
    “Da ist auch noch ein anderer Geruch in der Luft, der Geruch von Feuern, die in der Ferne brennen, mit einem Hauch Zimt darin - so riecht das Abenteuer!”
    ― aus Walter Moers' "Die 13 1/2 Leben des Käpt'n Blaubär"

Seite 4 von 27 « Erste 123456781115 ... 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