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 18 von 18
  1. Beiträge anzeigen #1 Zitieren
    Apprentice
    Registriert seit
    Oct 2015
    Beiträge
    14
     
    Higor ist offline

    Reverse engineering and DLL injection...

    Some days ago decided to use Gothic 3 as a good test subject to get better at the mentioned subjects (and to take a break from UT99 lol).
    Since it's a c++ project I'm pretty much writing c++ files and headers all over place with classes, enums, etc reverse engineered from the game's libraries.

    After getting something done out of this I thought it'd be a shame if the ongoing progress ended up sitting doing nothing in my hard drive, which brings us to the next subject.
    I was planning to upload the ongoing progress into a Github repository but my legal knowledge here is virtually none, and I'm not sure if I'll be breaking any law of TOS.
    All I know this is being done for entirely recreational/educational purposes.

  2. Beiträge anzeigen #2 Zitieren
    Apprentice
    Registriert seit
    Oct 2015
    Beiträge
    14
     
    Higor ist offline
    Gonna be posting a few samples of code here.
    The first snippet let NPC's use any kind of arrows they have in their inventory, the second one makes exploding arrows knock enemies down.
    Jorn becomes a bit more useful in the furnace fight as he'll use the explosive arrows you give him (too bad it's only 12 arrows).


    Code:
    //********************************************************************************************
    // The game wants NPC's to have arrows when necessary, AssureItems not only creates the items
    // in question, but also returns the corresponding slot ID in the NPC's inventory.
    //
    // What we'll do here is let the game do as usual, then instead scan through said NPC's
    // inventory and select the slot ID of the arrows with highest possible damage amount.
    //
    // This will allow NPC's like Jorn to shoot the 12 Exploding arrows you give him
    //
    int G3HookPSInventory::AssureItems_Middleman( const Template& Model, uint32 Quality, int Quantity)
    {
        int Result = AssureItems( Model, Quality, Quantity);
        if ( PS && (Model == Template("Arrow")) ) //IsValid
        {
            int BestDamage = 0;
            int BestSlot = -1;
            int Count = PS->GetStackCount();
            for ( int i=0 ; i<Count ; i++ )
            {
                if ( PS->GetStackUseType(i) == gEUseType_Arrow )
                {
                    eCTemplateEntity* Entity = PS->GetStack(i)->Template.GetEntity();
                    gCDamage_PS* Damage;
                    if ( Entity 
                    && (Damage=(gCDamage_PS*)Entity->GetPropertySet(eEPropertySetType_Damage)) != nullptr
                    && Damage->DamageAmount > BestDamage )
                    {
                        BestDamage = Damage->DamageAmount;
                        BestSlot = i;
                    }
                }
            }
            if ( BestSlot >= 0 )
                Result = BestSlot;
        }
        return Result;
    }
    Code:
    //********************************************************************************************
    // This function processes the damage and the corresponding effects on a NPC/Player
    //
    // What we'll do here is wait for the game to tell us the kind of effect on the victim and
    // then modify said effects if certain conditions are met.
    //
    // Changes:
    // - Exploding arrows cause same knockdown effect as headknocks, but are deadly.
    //
    int G3HookGlobal::DamageAction_Wrapper( gCScriptProcessingUnit& SPU, Entity* InVictim, Entity* InDamageDealer, int I1)
    {
        //UGLY WRAPPER, original function just redirected here
        TCodeBackup<5> HookBackup( DamageAction_Backup.Address); //Make a scoped backup of the hook
        DamageAction_Backup.ApplyBackup(); //Restore original code
        //Call function again (but in it's original state)
        gEAction RetAction = (gEAction)(*((F_ScriptProc_EEI)(DamageAction_Backup.Address)))(SPU,InVictim,InDamageDealer,I1);
    
        Entity Victim       = SPU_GetSelf( SPU, InVictim);
        Entity DamageDealer = SPU_GetOther( SPU, InDamageDealer);
    
        if ( (RetAction == gEAction_Stumble)
        && (Victim.NPC->Species == gESpecies_Human || Victim.NPC->Species == gESpecies_Orc)
        && (DamageDealer.GetName() == "ExplosiveArrow") )
        {
            Victim.ScriptRoutine.SetTask( bCString("ZS_SitKnockDown"));
            RetAction = gEAction_SitKnockDown;
        }
    
        return RetAction;
        //Hook will be restored here
    }

  3. Beiträge anzeigen #3 Zitieren
    Veteran Avatar von llEill
    Registriert seit
    Feb 2014
    Beiträge
    554
     
    llEill ist offline
    Wow, what else is possible?
    Could you do something to make the performance better?^^

  4. Beiträge anzeigen #4 Zitieren
    Apprentice
    Registriert seit
    Oct 2015
    Beiträge
    14
     
    Higor ist offline
    That would be almost impossible.
    Some things I noticed about Gothic 3 (correct me if I'm wrong):
    - Textures appear to be kept lingering in the virtual memory instead of the GPU memory, which leads to over 4gb of memory usage on high detail.
    - Resource streaming isn't exactly 100% non-blocking, the main game thread will stop during/after the loading process.
    - Navigation code (it uses meshes by the looks of it) isn't exactly cheap, draw a few Sandcrawlers in the desert towards you and watch the framerate go down.
    - Non-point collisions appears to work on a point-check basis instead of line-check, I find no other explanation to NPC's going though ground and walls like that.

    Another thing I wonder is if the game was compiled using MinGW, there's almost little to no inlining on basic primitives such as vector math.
    While something can be done about that tt's not only about compiler tweaks or binary bytehacks, there's entire subsystems that need to be rewritten or outright replaced.
    Just impossible with the available resources (manpower, game source code, etc).

    If both Risen and Gothic 3 were open sourced, grabbing the improvements made over the engine on Risen and porting them to G3 would be the best thing to do.
    But that would be a long shot.

  5. Beiträge anzeigen #5 Zitieren
    Moderator Avatar von George
    Registriert seit
    Sep 2010
    Beiträge
    678
     
    George ist offline
    Zitat Zitat von Higor Beitrag anzeigen
    After getting something done out of this I thought it'd be a shame if the ongoing progress ended up sitting doing nothing in my hard drive, which brings us to the next subject.
    I was planning to upload the ongoing progress into a Github repository but my legal knowledge here is virtually none, and I'm not sure if I'll be breaking any law of TOS.
    All I know this is being done for entirely recreational/educational purposes.
    I don't think you have to worry about legal issues. NicoDE released a similar project, the RisenSDK, in 2010 that hasn't had any legal problems till now. I am also planning to release my "Gothic 3 SDK" for a quite a while now, but it would require some polishing and documentation, so...


    Zitat Zitat von llEill Beitrag anzeigen
    Wow, what else is possible?
    Could you do something to make the performance better?^^
    Zitat Zitat von Higor Beitrag anzeigen
    - Resource streaming isn't exactly 100% non-blocking, the main game thread will stop during/after the loading process.
    A while ago I worked on getting rid of the occasional stutters that occur when the game loads assets.
    They are caused by Gothic 3 being single threaded, therefore it can't render frames while loading assets.
    I already made some promising progress, but there is a remaining issue that I have to resolve.


    Zitat Zitat von Higor Beitrag anzeigen
    Another thing I wonder is if the game was compiled using MinGW, there's almost little to no inlining on basic primitives such as vector math.
    While something can be done about that tt's not only about compiler tweaks or binary bytehacks, there's entire subsystems that need to be rewritten or outright replaced.
    Just impossible with the available resources (manpower, game source code, etc).

    If both Risen and Gothic 3 were open sourced, grabbing the improvements made over the engine on Risen and porting them to G3 would be the best thing to do.
    But that would be a long shot.
    Gothic 3 was compiled using MSVC, but maybe without aggressive optimization options. Older Versions of the game (e.g. CP 1.5) are even worse in respect to compiler optimizations. Having the sources would be a real game changer, but I don't think it is going to happen...

  6. Beiträge anzeigen #6 Zitieren
    Veteran Avatar von llEill
    Registriert seit
    Feb 2014
    Beiträge
    554
     
    llEill ist offline
    Zitat Zitat von George Beitrag anzeigen
    A while ago I worked on getting rid of the occasional stutters that occur when the game loads assets.
    They are caused by Gothic 3 being single threaded, therefore it can't render frames while loading assets.
    I already made some promising progress, but there is a remaining issue that I have to resolve.
    What issue? Maybe someone here can help you?
    I would love to play Gothic 3 without this micro stuttering!

  7. Beiträge anzeigen #7 Zitieren
    Apprentice
    Registriert seit
    Oct 2015
    Beiträge
    14
     
    Higor ist offline
    Well, the code isn't exactly tidy and I'm not using a tool to autogenerate headers (even though I should be)
    I can be too functionality/results oriented, one little thing I did is NPC's being able to level up with kills or by being in the hero's party, but I seriously gotta balance and clean that up.

    Meanwhile, another simple and straightforward function (I wonder if that unknown bool thing is the alternate balancing switch)
    Code:
    //********************************************************************************************
    // Mana Regeneration.
    // This function is called every second and returns the amount of mana the hero should recover
    // Changes:
    // - Hero can regenerate mana during transformation (dragon, for example)
    // - Fixes regeneration being rounded down to integer.
    // ** So if you have 250 mana, you won't be stuck with recovering just 2 mana per second.
    // ** But instead recover 2-3 every second.
    static int ManaRegenAcc = 0;
    static int MasterMageActive = 0;
    int G3HookGlobal::ManaRegenTimer( Entity InEntity)
    {
        int Result = 0;
        Entity PlayerEntity = Entity::GetPlayer();
    
        if ( InEntity == PlayerEntity )
        {
            // Remember if PC_Hero has mana regeneration skill (gets lost during transformation)
            if ( !InEntity.NPC.IsTransformed() )
                MasterMageActive = InEntity.Inventory.IsSkillActive( Template("Perk_MasterMage"));
    
            if ( MasterMageActive )
            {
                // Only consider up to 500 mana points
                int Acc = 500;
                if ( UnknownEngineBool() ) //TODO: What's this?
                    Acc = Min( Acc, InEntity.PlayerMemory.GetManaPointsMax() );
    
                // Fixes integer rounding when dividing by 100
                ManaRegenAcc += Acc;
                Result = Min( 5, ManaRegenAcc / 100);
                ManaRegenAcc = ManaRegenAcc % 100;
            }
        }
        return Result;
    }

  8. Beiträge anzeigen #8 Zitieren
    Moderator Avatar von George
    Registriert seit
    Sep 2010
    Beiträge
    678
     
    George ist offline
    Zitat Zitat von Higor Beitrag anzeigen
    Meanwhile, another simple and straightforward function (I wonder if that unknown bool thing is the alternate balancing switch)
    Exactly, offset 0x1EA into the eSSetupEngine struct is the switch for alternative balancing.

  9. Beiträge anzeigen #9 Zitieren
    Apprentice
    Registriert seit
    Oct 2015
    Beiträge
    14
     
    Higor ist offline
    Well, been working on NPC's equipping items and weapons.

    NPC's that get their weapons looted no longer recover their old weapons or a set of rusty weapons right away.
    Instead they'll get starter level weapons and after a few days they'll start upgrading until they get their original weapon set.
    They can also use better weapons in their inventory if their level is high enough.

    This prevents a series of exploits such as slaves carrying orc halberds and knockdown on Gorn for infinite battle axes.
    Also made 'worn' weapons cost 40% of what they used to (with exception of orc weapons on orc/mercenary traders).

    Made a nice helmet equipping function only to be tremendously disappointed...
    Miniaturansichten angehängter Grafiken Miniaturansichten angehängter Grafiken Rhober_crown.jpg   Ejnar_Helmet.jpg  

  10. Beiträge anzeigen #10 Zitieren
    Ranger Avatar von Marek33
    Registriert seit
    Nov 2016
    Beiträge
    173
     
    Marek33 ist offline
    Zitat Zitat von Higor Beitrag anzeigen
    Well, been working on NPC's equipping items and weapons.

    NPC's that get their weapons looted no longer recover their old weapons or a set of rusty weapons right away.
    Instead they'll get starter level weapons and after a few days they'll start upgrading until they get their original weapon set.
    They can also use better weapons in their inventory if their level is high enough.

    This prevents a series of exploits such as slaves carrying orc halberds and knockdown on Gorn for infinite battle axes.
    Also made 'worn' weapons cost 40% of what they used to (with exception of orc weapons on orc/mercenary traders).

    Made a nice helmet equipping function only to be tremendously disappointed...
    That's quite an interesting work, which you have done so far, and it's a pity that it doesn't work properly with the helmets.

    However, I think that the worn weapon category, should be completely removed, since it doesn't make any sense anyway. Weapons and armor have never worn down in the Gothic 1 or 2, so it shouldn't in the third game as well. It doesn't add anything to the realism or immersion, it's just a dumb feature. Maybe, if it would be possible to have weapons and armor degree over time, how it will be maybe in the StrongHand Mod for Gothic 2, then it would be interesting, but without that it doesn't worth it, in my honest opinion.


    Also, good luck with more reverse engineering and modding!

  11. Beiträge anzeigen #11 Zitieren
    Apprentice
    Registriert seit
    Oct 2015
    Beiträge
    14
     
    Higor ist offline
    Well, the repository is up, but not exactly cleaned up.
    Also, this is not a SDK given that my methodology consists on solving problems and adding features when needed, and that includes classes and headers.
    https://github.com/CacoFFF/G3Hook

    Some extra changes done:
    - The sale price of the items in your inventory will change depending on the NPC you're focusing (consider it a real time preview)
    - Human NPC's do an additional 1/10th of their weapon's (+bolt/arrows) nominal damage, makes axe and crossbow wielders more dangerous against the player and orcs.
    - Furnace attackers (Thorald, Jorn, Lisk, Mort) can follow the player an additional 10.000 units of distance (about 15% more than original, but you can easily change that ).
    - Transformation transfers party members from PC_Hero to the transformed entity and back.
    - Kind of recreated updatepack's Level up function (even considers Quest XP scaling!), needed to do that to implement the experimental NPC level up feature.
    - Weapon equipping based on damage and use type, human NPC's only.
    ** NPC's will change weapons/shields based on their original loadout (dual wielders stay dual wielders, shield users keep using shields, axes with axes, etc)
    ** Merchant NPC's may equip weapons sold by the player as in G1/G2 (need Worn/Sharpened/Self Forged quality).
    ** NPC's can only equip weapons according to their level and class (warrior/ranger, STR,DEX,INT are 'estimated')
    - Ranger NPC's may back off a bit like mages do when firing arrows/bolts, maximum firing range depends on NPC class and level.

    Playing under these changes has been interesting and they've exposed other flaws in the game that could use some fixing up.
    For example:
    - NPC's that attempt to use items in their 'Sale' inventory will create a duplicate of it.
    -- Sell a NPC a healing potion and control him with 'Word of Dominance', he has infinite potions to heal up. (Osmund best example)
    -- Sell a NPC a single special arrow, he'll never run out of said kind of arrow.

    Got some more ideas in mind but I'd rather hear yours as well.

    EDIT:
    If you actually decide to compile this, there's two projects: EXE and DLL.
    The G3Hook.exe is the injector and G3HookDLL.dll is the injected file, both should go in the same directory and G3Hook.exe must be open when you launch Gothic 3.
    As long as the injector stays open, Gothic 3 can be hooked anytime it's opened.
    If Gothic 3 freezes for over a second during injection, then injection failed.

    The projects were built using Visual Studio 2015, but any C++11 compatible compiler should work (make sure __stdcall is default calling convention!)
    Geändert von Higor (14.05.2019 um 04:11 Uhr)

  12. Beiträge anzeigen #12 Zitieren
    Moderator Avatar von George
    Registriert seit
    Sep 2010
    Beiträge
    678
     
    George ist offline
    Zitat Zitat von George Beitrag anzeigen
    I am also planning to release my "Gothic 3 SDK" for a quite a while now, but it would require some polishing and documentation, so...
    Finally did it! Maybe it is useful to you Higor as its contains a lot of reconstructed classes.

  13. Beiträge anzeigen #13 Zitieren
    Neuling
    Registriert seit
    Aug 2023
    Beiträge
    2
     
    herrnamenlos123 ist offline
    Very interesting! I started to do a similar thing recently. I took a look at Higor's code, but it seems to only attach after the game has started, and patching things at runtime, more from a higher level. The Gothic3Sdk also wasn't exactly what I was looking for.

    I was thinking if you could do DLL injection at the lowest level, and today I confirmed that it is absolutely plausible. So that's what I did: Disassembling Gothic 3's DLLs, compiling my own DLL with the same interface, pretending it is the real one, and then forward all the calls to the actual DLL. That way I can optionally intercept every single function call of the Genome engine, that goes across DLLs.

    I planned to develop a system where you can then apply patches easier, that work on the lowest level, we are talking about things so low as the window creation code. You could replace entire parts of the engine with that. The system would be installed once and then you can redirect system calls from a higher level, giving every possibility for modding.

    But I am new to modding in Gothic: Is something like this already done? I habe no idea how all these patches work (Community patch, Parallel Universe Patch, System Patch, Widescreen patch, etc), or if they would conflict. Am I reinventing the wheel or is it worth to explore?

    At the same time, once you have all this direct memory access I was thinking you could develop a proper modkit with maybe python or lua scripting. I only saw there are not many resources about modding yourself and they're everything but beginner friendly. But again, I am new to developing mods, so tell me if i'm wrong.

  14. Beiträge anzeigen #14 Zitieren
    Moderator Avatar von George
    Registriert seit
    Sep 2010
    Beiträge
    678
     
    George ist offline
    Zitat Zitat von herrnamenlos123 Beitrag anzeigen
    Community patch
    The Community Patch team had access to the Gothic 3 source code, so they didn't have to do any reverse engineering or DLL patching/injection, they could just modify the source code and then compile it.

    Zitat Zitat von herrnamenlos123 Beitrag anzeigen
    Parallel Universe Patch
    The Parallel Universe Patch executes code at three different stages:
    1. paru_s1.dll: Directly when starting the Gothic3.exe, before any code from Gothic 3 own modules (SharedBase.dll, Engine.dll, Game.dll, ...) is executed.
    2. paru_s2.dll: After all of Gothic 3 own modules (SharedBase.dll, Engine.dll, Game.dll, ...) have executed, i.e. have finished their initialization.
    3. scripts/Script_paru.dll: After Script_Game.dll has been loaded and initialized (happens approximately when reaching the main menu).


    The first two stages are executed by adding paru_s1.dll and paru_s2.dll as additional imports to Gothic3.exe,
    where the order in the import directory is used to control the exact timing when they are loaded relative to each other and Gothic 3's own modules.

    Import of original Gothic3.exe
    [Bild: attachment.php?s=48652093b1d5138ee6ac03e055ec841f&attachmentid=52797&d=1692048472&thumb=1]

    With additional imports added by Parallel Universe Patch
    [Bild: attachment.php?s=48652093b1d5138ee6ac03e055ec841f&attachmentid=52798&d=1692048472&thumb=1]

    The third stage is loaded automatically by Gothic 3's script subsystem, which loads all DLL files that are located in the scripts folder.

    The script loading is the mechanism that the gothic3sdk examples use.
    However, as already mentioned, this only happens when reaching the main menu, which can already be too late depending on what we want to patch/modify.

    So if we want to run our own code earlier, we have to use another way to inject our DLL.
    One possibility is the approach described above that the Parallel Universe Patch uses for it first two stages.

    Another possibility is using a proxy DLL, as you have suggested.
    But here it probably makes more sense to proxy a DLL that is not directly part of Gothic 3.
    A popular target across many games seems to be dinput8.dll, for example.
    Using a Gothic 3 foreign DLL has the advantage that you don't conflict with mods like the Update Pack, which modifies the Gothic 3 DLLs directly.

    Functions from the Gothic 3 DLLs can also be intercepted from a foreign DLL without any problems, using a hooking library like PolyHook (there are many more) or with the hooking functionality included in gothic3sdk.
    In general, the Gothic 3 SDK, i.e. the enum, class and function declarations it provides plus the utility functions, will prove to be very helpful no matter which injection method we choose.

    Zitat Zitat von herrnamenlos123 Beitrag anzeigen
    At the same time, once you have all this direct memory access I was thinking you could develop a proper modkit with maybe python or lua scripting. I only saw there are not many resources about modding yourself and they're everything but beginner friendly. But again, I am new to developing mods, so tell me if i'm wrong.
    Some time ago I implemented a Python binding for Gothic 3 (using SWIG).
    The overhead of Python was pretty substantial though, so I didn't pursue it further in the end, but I could publish it if there's interest.
    I've also thought about trying Lua, hoping it will perform better than Python, but haven't gotten around to it yet.

    Example script with the Python binding that kills all entities near the player:
    Code:
    from script import cvar
    
    def message(msg):
        cvar.gui2.PrintGameMessage(msg, script.gEGameMessageType_Success)
    
    entities = Entity.GetEntities()
    killed = 0
    for i in range(entities.GetCount()):
        entity = entities.AccessAt(i)
        if entity.IsNPC() and not entity.IsDead() and not entity.IsPlayer():
            entity.Kill()
            message(f'{entity.GetName()}: {entity.Navigation.StartPosition.GetX()}')
            killed += 1
    
    message(f'Killed {killed} entities in ROI.')

  15. Beiträge anzeigen #15 Zitieren
    Waldläufer
    Registriert seit
    May 2022
    Beiträge
    107
     
    Jackydima ist offline
    Hello George, I have a question about your mod-kit:

    How are you declaring structures and functions of the other dlls loaded and exported in the Gothic 3 dlls and using them for you own code. For example you declared the G3 String type bCString and are using it.

    Iam kind of new to MCVS c++ programming in general. But I see you have the SharedBase Solution, that holds the ge_string.h file. There the bCString interface is defined with the dllimport declspec. But in general you have to define a function for an api entry, or provide a lib (in that case it would be the SharedBase.dll in the Gothic 3 directory). So how do you set it up? What is the way you used to accomplish that. Would you mind to elaburate it ?

  16. Beiträge anzeigen #16 Zitieren
    Moderator Avatar von George
    Registriert seit
    Sep 2010
    Beiträge
    678
     
    George ist offline
    Zitat Zitat von Jackydima Beitrag anzeigen
    Hello George, I have a question about your mod-kit:

    How are you declaring structures and functions of the other dlls loaded and exported in the Gothic 3 dlls and using them for you own code. For example you declared the G3 String type bCString and are using it.

    Iam kind of new to MCVS c++ programming in general. But I see you have the SharedBase Solution, that holds the ge_string.h file. There the bCString interface is defined with the dllimport declspec. But in general you have to define a function for an api entry, or provide a lib (in that case it would be the SharedBase.dll in the Gothic 3 directory). So how do you set it up? What is the way you used to accomplish that. Would you mind to elaburate it ?
    It is possible, in fact even rather straightforward, to create a .lib file from a .dll file. For detailed explanations of the required steps or even ready-made tools that do the entire .lib from .dll generation see the following resources:
    https://stackoverflow.com/questions/...-a-header-file
    https://www.asawicki.info/news_1420_...or_dll_library
    https://emsea.github.io/2018/07/13/lib-from-dll/

    The .lib files that I created from the corresponding Gothic 3 dlls can be found in the lib subdirectory of the gothic3sdk.

  17. Beiträge anzeigen #17 Zitieren
    Waldläufer
    Registriert seit
    May 2022
    Beiträge
    107
     
    Jackydima ist offline
    Zitat Zitat von George Beitrag anzeigen
    It is possible, in fact even rather straightforward, to create a .lib file from a .dll file. For detailed explanations of the required steps or even ready-made tools that do the entire .lib from .dll generation see the following resources:
    https://stackoverflow.com/questions/...-a-header-file
    https://www.asawicki.info/news_1420_...or_dll_library
    https://emsea.github.io/2018/07/13/lib-from-dll/

    The .lib files that I created from the corresponding Gothic 3 dlls can be found in the lib subdirectory of the gothic3sdk.

    Ah thank you for your answer, I got it working, with your help. I had it to set up correctly in the Linker Option of my project and directly add the lib file (before I had only the folder as addition libraries). Actually easy to get a lib out of a dll

  18. Beiträge anzeigen #18 Zitieren
    Veteran Avatar von llEill
    Registriert seit
    Feb 2014
    Beiträge
    554
     
    llEill ist offline
    Gibt es irgendwo ein Modding Tutorial (script mods) für Gothic 3 und eine Bibliothek der verfügbaren Funktionen, in die man sich einlesen kann?

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