Script for the video game Gothic II: Night of the Raven enabling free aiming for ranged weapons and spells.
Note: This is a script (i.e. source code). If you are interested in a playable version instead, checkout this modification Gothic II - Freies Zielen (German language).
- Free aiming for ranged weapons (bows, crossbows)
- Free aiming for spells
- Shot projectiles (arrows, bolts) can be picked up and re-used
- Dynamic critical hit detection (e.g. head shots)
- Modifiable hit registration by different criteria (surface material and texture)
- True shooting accuracy
- Draw force (projectiles drop faster if the bow is not fully drawn)
- High customizability (see Examples and Customization below)
Examples
Here short list of some examples which are easily implementable (or already included) in config.d
. There is a lot
more freedom, however.
- Change the way the draw force (projectile gravity) is calculated (by weapon stats, skill level, ..)
- Change the way the accuracy is calculated (by weapon stats, skill level, ..)
- Change the initial damage when an arrow is shot (by draw force, accuracy, skill level, ..)
- Change the reticle style (by draw force, accuracy, weapon, aiming distance, enemy, ..)
- Different critical hit zones (body parts like head, torso, limbs) (by guild, weapon, ..)
- Different damage for critical hits
- Notification when getting a critical hit (sound, screen print, ..)
- Head shot counter that gives XP every 25 head shots.
- Disable friendly-fire for quest/party members
- Define collision behavior of projectiles by surface material or texture (e.g. arrows break or deflect when hitting stone or get stuck in wood)
- Change the projectiles when they have been shot (e.g. used arrows that need to be repaired)
- Enable/Disable retrieving projectiles (e.g. the player first has to learn to retrieve arrows from animals)
- And much more. Get creative!
Again, this is the installation for developers. If you are interested in a playable version instead, checkout this modification Gothic II - Freies Zielen (German language).
Requirements:
- Gothic II: Night of the Raven (Reportversion 2.6.0.0)
- System Pack for correct display of reticles
- Mod Development Kit (MDK) with scripts (+ patch 2.6a)
- Ikarus 1.2
- LeGo 2.3.7 or higher with HookEngine, FrameFunctions and ConsoleCommands
A setup is available to take care of the integration. Just run it, and all scripts should be fully working (originals will be backed up). Alternatively, you can do these following steps manually.
-
Make sure Ikarus and LeGo are installed and initialized with FrameFunctions and ConsoleCommands.
-
Copy all files from this repository into your Gothic II installation. Mind the relative paths. Do not forget the binary files (textures) that come with the release.
-
Have the files parsed:
- Add the line
FREEAIM\freeAim.src
to_work\data\Scripts\Content\Gothic.src
somewhere after Ikarus, LeGo andAI\AI_INTERN\FOCUS.D
. - Add the line
camera\caminstfreeaim.d
to the end of_work\data\Scripts\System\Camera.src
. - Add the line
Pfx\PfxInstFreeAim.d
to the end of_work\data\Scripts\System\PFX.src
. - Add the line
sfx\sfxinstfreeaim.d
to the end of_work\data\Scripts\System\SFX.src
. - Add the line
visualfx\visualfxfreeaim.d
to the end of_work\data\Scripts\System\VisualFX.src
. - Add the line
menu\menu_opt_game_freeaim.d
to the end of_work\data\Scripts\System\Menu.src
.
- Add the line
-
Add a new menu entry to the options game menu. Extend the instance
MENU_OPT_GAME
in_work\data\Scripts\System\Menu\Menu_Opt_Game.d
with these two lines just beforeitems[XX] = "MENUITEM_GAME_BACK";
.items[XX] = "MENUITEM_OPT_FREEAIM"; items[XX+1] = "MENUITEM_OPT_FREEAIM_CHOICE";
Where
XX
is the index. Of course, increaseXX
toXX+2
forMENUITEM_GAME_BACK
. With this you can enable and disable free aim from the options menu. -
In the same file change
posy = MENU_BACK_Y;
in the instanceMENUITEM_GAME_BACK
toposy = MENU_BACK_Y+300;
. This repositions the menu entries such that everything fits. -
Set the constant
MENU_ID_FREEAIM
either inMenu_Opt_Game.d
or inMenu_Opt_Game_FreeAim.d
to the next available slot in the menu, typically(XX-1)/2
. For example:const int MENU_ID_FREEAIM = 7; // Next available Y-spot in the menu
-
Finally initialize free aim by adding the line
freeAim_Init();
in to the functionINIT_GLOBAL()
in_work\data\Scripts\Content\Story\Startup.d
somewhere after Ikarus and LeGo.
Again: The setup will perform all these steps for you.
You will have to adjust the labels in _work\data\Scripts\System\Menu\Menu_Opt_Game_FreeAim.d
. By default they are in
German. After parsing the scripts g2freeAim should be fully implemented. Read on to find out how to adjust g2freeAim to
your preferences.
Note: By using these scripts, you agree to the terms of the GNU General Public License. Please respect my efforts and accredit my work in your project accordingly (i.e. "This modification utilizes G2 Free Aim written by mud-freak (@szapp)" in the credits). If you omit this, you are stating this was your own work which is effectively violating the license.
As the mechanisms of the script are rather complex, the script is divided into two parts in
_work\data\Scripts\Content\freeAim\
. One part is the core functionality and should not be edited: _intern.d
.
The other one holds all possible customizations and can be freely adjusted and changed: config.d
.
The customization is mostly implemented by calling outsourced functions. These are functions you can freely adjust. The only restrictions are the function signature and the type of return value. What happens inside the functions is fully up to you. Other binary settings are offered as constants that can simply be changed.
In the file config.d
the following things can be adjusted. For more details see config.d
and read the in-line
comments of each listed function. There are a lot of possibilities and a lot of information is already provided to each
function (in form of function arguments). For some examples and ideas see Examples above.
- Reusable Projectiles
- Draw Force
- Initial Damage
- Accuracy
- Hit Registration
- Critical Hits
- Reticles
- Additional Settings
- Eligible Spells
While free aiming technically has nothing to do with it, the ability to pick up and reuse projectiles was a side-product
of this script. When a projectile is shot, it will not be removed from the world. It can be collected from the world and
from killed targets' inventories. Although a great feature, this can be disabled by setting FREEAIM_REUSE_PROJECTILES
to zero in freeAimInitConstants()
. If it is enabled, however, the function freeAimGetUsedProjectileInstance()
may be
used to change/replace the instance of the landed projectile. This allows for "used" arrows/bolts that need repairment
or might be less effective on future usage. Projectiles can be destroyed the same way, by setting their instance to
-1
. See freeAimGetUsedProjectileInstance()
for more details.
Mind the balancing: If projectiles may be reused, you should re-balance the amount of them in the world. Otherwise their value and barely decreasing number can be exploited during trading.
Draw force is the time of drawing the weapon. The longer one draws the weapon the more power is behind the projectile.
This power manifests itself in the trajectory of the projectile. Full draw force results in the most straight flight
path. Anything below will decrease the arc of the projectile, leading to a faster dropping shot. The curved trajectory
is implemented by applying gravity to the projectile after a certain amount of time. The less the draw force, the
earlier the projectile drops. This is all calculated internally, all you need to supply is a percentage (integer between
0 and 100) of draw force in freeAimGetDrawForce()
. This function is called once you shoot your weapon to determine the
trajectory of the projectile, but it may also be called by yourself in any of the consecutive functions listed below
(e.g. to animate the reticle by draw force/draw time, or manipulate the accuracy). By default, this function returns the
time since drawing the bow (scaled between 0 and 100), while crossbows always have a draw force of 100% (since they are
mechanic). This can, of course, be changed. Possible implementations: Quick-draw talent, different draw time for
different bows. Additional settings are FREEAIM_DRAWTIME_MAX
, FREEAIM_TRAJECTORY_ARC_MAX
and
FREEAIM_PROJECTILE_GRAVITY
(already well balanced and should not be changed). See freeAimInitConstants()
for
details. To monitor the settings see Debugging below.
Once a projectile leaves the weapon, it is assigned a base damage (which is essentially the weapon damage). There is no
reason to ever change this base damage, as it could just be adjusted in the weapon-item script. Nevertheless, scaling
this base damage by draw force is very useful. This is done in freeAimScaleInitialDamage()
. To monitor the settings
see Debugging below.
Gothics native implementation of hit chance is to only register a percentage of the hits, while the others still visibly
collide. Here, this method is overwritten, with a true shooting accuracy. The accuracy is a percentage (integer between
0 and 100) that can be adjusted in freeAimGetAccuracy()
. This function is called once you shoot your weapon to
determine the deviation of the projectile. By default this function combines weapon talent and draw force. The amount of
maximum scattering (when the returned percentage of accuracy is 0) can be adjusted in FREEAIM_SCATTER_DEG
in
freeAimInitConstants()
. However, this value is already well balanced. To monitor the settings see Debugging below.
Related to accuracy is the hit registration. You may change the behavior of projectiles by the surface they collide
with. When a projectile collides with an npc the function freeAimHitRegNpc()
is called. There, you are supplied with
the target npc instance, the material of the armor they wear (as defined in Constants.d
) and more, to decide whether
the projectile should hit and cause damage, deflect and cause no damage or be destroyed and cause no damage. The
function freeAimHitRegWld()
is called when a projectile collides with the world. There, you can decide by material and
texture of the surface, whether the projectile should get stuck, deflect or be destroyed on impact. When a projectile is
destroyed, it is accompanied by a sound and subtle visual effect of bursting wood (collision with world only). Possible
implementations: Disabled friendly-fire, ineffective weapons, break-on-impact chance. Additionally, I implemented a hit
detection that sits on top of the engine's hit detection. Originally, the idea was to make the hit detection more
precise. However, this method is only experimental and if anything it only yields false negatives (no hits when there
should be hits). By default it is disabled. If you want to check it out, see FREEAIM_HITDETECTION_EXP
in
freeAimInitConstants()
. In Gothic, there is a bug by which projectiles collide with triggers, causing a loud metallic
clatter. This is prevented by a fix introduced
here. This fix is enabled here by default.
Should this compromise the functionality of your triggers you can disable it by setting FREEAIM_TRIGGER_COLL_FIX
to
zero in freeAimInitConstants()
.
Something that is a must-have in a free aiming implementation is a damage multiplier for critical hits. Instead of
pre-defining critical hits to be head shots for all creatures, the function freeAimCriticalHitDef()
allows for
dynamically defined critical hit zones ("weakspots"), their size and the damage multiplier. This function is called on
every positive hit on an npc and aids to decide whether a critical hit happened. When there was a critical hi, the
function freeAimCriticalHitEvent()
is called, in which you can define an event on getting a critical hit. Possible
implementations: Headshot counter, sound notification, hit marker, print on the screen. Both functions are supplied with
the target npc in question. Thus, it is very well possible to discern those and define varying cases. As it is very
difficult to guess the dimensions of a defined weakspot, the console command debug freeaim weakspot
will visualize the
projectile trajectory and the bounding box of the currently defined weakspot for the target. This will help deciding on
suitable sizes for weakspots. For thorough testing this debug visualization can be enabled by default with
FREEAIM_DEBUG_WEAKSPOT
in freeAimInitConstants()
.
Reticles can be dynamically customized. The functions freeAimGetReticleRanged()
(for ranged combat) and
freeAimGetReticleSpell()
(for magic combat) are called continuously while holding down the action button in the
respective fight mode. By weapon/spell, their properties, aiming distance and by calling any of the previously defined
functions (like draw force and accuracy), the visualization of reticles is extremely flexible. There are a lot of
possibilities waiting. See the mentioned functions for details.
There is a variety of miscellaneous configurations. While a large number is omitted here, as they should not be touched, a few are enumerated and explained here briefly.
FREEAIM_DISABLE_SPELLS
infreeAimInitConstants()
will disable free aiming for spells. (Free aiming only for ranged weapons.)FREEAIM_DEBUG_TRACERAY
infreeAimInitConstants()
will enable trace ray debug visualization by default (see below for more information).FREEAIM_ROTATION_SCALE
infreeAimInitConstants()
will adjust the rotation speed while aiming. This is not the mouse sensitivity. This setting should not be changed.FREEAIM_CAMERA
infreeAimInitConstants()
will set a different camera setting for aiming. The camera setting should not be changed, as the aiming becomes less accurate (intersection miss-match, parallax effect).FREEAIM_CAMERA_X_SHIFT
infreeAimInitConstants()
has to be set to true, if the camera has an X offset. This is not recommended at all (parallax effect), see above.freeAimShiftAimVob()
will shift the offset along the camera viewing axis. This is only useful for spells that visualize the aim vob. For an example (SPL_Blink) visit the forum thread (see link in Contact and Discussion or check out the branchSPL_Blink
).
There are quite some more things that can be changed (listed at the top of _intern.d
) but they should not be altered
under normal circumstances. Changing those settings will most certainly make g2freeAim unstable.
Gothic offers very different types of spells (attack spells, area of effect spells, summoning spells, enhancement spells). Of course, not all of those require free aiming, let alone a reticle. Here is a description of how g2freeAim decides which spell is eligible for free aiming.
- The property
targetCollectAlgo
in the spell instance needs to be set toTARGET_COLLECT_FOCUS_FALLBACK_NONE
- The property
canTurnDuringInvest
in the spell instance needs to be set toTRUE
- The property
canChangeTargetDuringInvest
in the spell instance needs to be set toTRUE
This list of conditions works without exception with all Gothic 2 tNotR spells and rather than ever changing these conditions (which will make them inconsistent across all Gothic spells) all newly created spells should meet/not meet these conditions, because they make sense.
Some steps have been taken to make debugging g2freeAim easier and to help with customizing it (see Customization above).
- Information about shots from bows and crossbows is sent to the zSpy at time of shooting, containing
- Draw force (percent)
- Accuracy (percent) and resulting scatter (X and Y offset in degrees)
- Adjusted base damage (see Customization above) and original base damage
- Information about critical hits from bows and crossbows is sent to the zSpy at time of collision, containing
- Whether or not it was a critical hit
- Critical base damage and original base damage
- Critical hit zone (body node/weakspot) and its dimensions
- For finding good critical hit zones (weakspots), the console command
debug freeaim weakspot
will visualize the projectile trajectory and the bounding box of the currently defined weakspot for the target. This will help deciding on suitable sizes for weakspots. - The console command
debug freeaim traceray
will visualize the aiming trace ray and the determined nearest intersection. This should only be useful if the underlying g2freeAim mechanics are modified (which is not recommended). - Additional information about the selected settings of g2freeAim can be displayed in the console by the commands
freeaim version
displays the current version of g2freeAim, e.g. "G2 Free Aim v0.1.0"freeaim license
displays the license information of g2freeAimfreeaim info
displays the settings of g2freeAim- Whether free aiming is enabled
- Whether focus collection is enabled (ini-file setting for performance)
- Whether projectiles are enabled to be reusable
- Whether free aiming is enabled for spells
These features were not fully implemented.
- Strafing while aiming
- Bow draw animations while aiming
Attempts, progress and unstable implementations for both features are available in the branches strafing
and
drawAni
. Keep in mind that they are far behind master
. If you think you can finish these features, feel free to
create a pull request. Consult with the issue tracker for information on possible limitations and different attempts.
Head over to the World of Gothic Forum to share your thoughts or questions that you might have or to join the discussion about this script. It is easiest to keep everything in one place.
This script will not be worked on after 2016. Nevertheless, feel free to create pull requests if you implement new features.
This script is only compatible with Gothic II: The Night of the Raven. It is not meant for Gothic 1. At the moment it is difficult to port it to Gothic 1, as there is no working version LeGo for Gothic 1. Also all memory addresses g2freeAim is using, would need to be adjusted. A lot of stack offsets are hard-coded at various places in the code.