Hook scripts are specially named scripts that are run by sfall at specific points to allow mods to override normally hardcoded behaviour in a more flexible way than sfall's normal ini configuration.
In addition to the bit of code it overrides, the script will be run once when first loaded and again at each player reload to allow for setup. Hook scripts have access to a set of arguments supplied to sfall, but aren't required to use them all. They also return one or more values, but again they're optional, and you only need to return a value if you want to override the default.
### Hooks compatibility
To aid in mods compatibility, avoid using `hs_xxx.int` scripts. Instead it is recommended to use a normal global script combined with `register_hook_proc` or `register_hook`.
There are script functions available, specific to hook scripts:
#### `int init_hook()`
The hook script equivalent of `game_loaded`; it returns 1 when the script is loaded for the first time or when the player reloads the game, and 0 otherwise.
#### `mixed get_sfall_arg()`
Gets the next argument from sfall. Each time it's called it returns the next argument, or otherwise it returns 0 if there are no more arguments left.
You can arbitrarily get the value of any argument using the `sfall_func1("get_sfall_arg_at", argNum)` function.
#### `array get_sfall_args()`
Returns all hook arguments as a new temp array.
#### `void set_sfall_return(int value)`
Used to return the new values from the script. Each time it's called it sets the next value, or if you've already set all return values it does nothing.
#### `void set_sfall_arg(int argNum, int value)`
Changes argument value. The argument number (argNum) is 0-indexed. This is useful if you have several hook scripts attached to one hook point (see below).
#### `void register_hook(int hookID)`
Used from a normal global script if you want to run it at the same point a full hook script would normally run. In case of this function, `start` proc will be executed in the current global script. You can use all above functions like normal.
The same as `register_hook`, except that you specifically define which procedure in the current script should be called as a hook (instead of "start" by default). Pass procedure the same as how you use dialog option functions.
This IS the recommended way to use hook scripts, as it gives both modularity (each mod logic in a separate global script, no conflicts if you don't use `hs_*.int` scripts) and flexibility (you can place all related hook scripts for specific mod in a single script!).
Use zero (0) as the second argument to unregister the hook from the current global script.
Works very similar to `register_hook_proc`, except that it registers the current script at the end of the hook script execution chain (i.e. the script will be executed after all previously registered scripts for the same hook, including the `hs_*.int` script). All scripts hooked to a single hook point with this function are executed in exact order of how they were registered, as opposed to the description below, which refers to using `register_hook` and `register_hook_proc` functions.
__NOTE:__ You can hook several scripts to a single hook point, for example, if it's different mods from different authors or just some different aspects of one larger mod. In this case scripts are executed in reverse order of how they were registered. When one of the scripts in a chain returns value with `set_sfall_return`, the next script may override this value if calls `set_sfall_return` again. Sometimes you need to multiply certain value in a chain of hook scripts.
Example: let's say we have a Mod A which reduces all "to hit" chances by 50%. The code might look like this:
```js
original_chance=get_sfall_arg;
set_sfall_return(original_chance/2);
```
Mod B also want to affect hit chances globally, by increasing them by 50%. Now in order for both mods to work well together, we need to add this line to **Mod A** hook script:
```js
set_sfall_arg(0,(original_chance/2));
```
This basically changes hook argument for the next script. **Mod B** code:
```js
original_chance=get_sfall_arg;
set_sfall_return(original_chance*1.5);
set_sfall_arg(0,(original_chance*1.5));
```
So if you combine both mods together, they will run in chain and the end result will be a 75% from original hit chance (hook register order doesn't matter in this case, if you use `set_sfall_arg` in both hooks).
The defines to use for the hookID are in **sfall.h**.
int arg0 - If the attack will hit: 0 - critical miss, 1 - miss, 2 - hit, 3 - critical hit
Critter arg1 - The attacker
Critter arg2 - The target of the attack
int arg3 - The bodypart
int arg4 - The hit chance
int ret0 - Override the hit/miss
int ret1 - Override the targeted bodypart
Critter ret2 - Override the target of the attack
```
-------------------------------------------
#### `HOOK_CALCAPCOST (hs_calcapcost.int)`
Runs whenever Fallout is calculating the AP cost of using the weapon (or unarmed attack). Doesn't run for using other item types or moving.
Note that the first time a game is loaded, this script doesn't run before the initial interface is drawn, so if the script effects the AP cost of whatever is in the player's hands at the time the wrong AP cost will be shown. It will be fixed the next time the interface is redrawn.
You can get the weapon object by checking item slot based on attack type (`ATKTYPE_LWEP1`, `ATKTYPE_LWEP2`, etc) and then calling `critter_inven_obj`.
```
Critter arg0 - The critter performing the action
int arg1 - Attack Type (see ATKTYPE_* constants)
int arg2 - Is aimed attack (1 or 0)
int arg3 - The default AP cost
Item arg4 - The weapon for which the cost is calculated. If it is 0, the pointer to the weapon can still be obtained by the aforementioned method
int ret0 - The new AP cost
```
-------------------------------------------
#### `HOOK_DEATHANIM1 (hs_deathanim1.int)`
Runs before Fallout tries to calculate the death animation. Lets you switch out which weapon Fallout sees.
Does not run for critters in the knockdown/out state.
```
int arg0 - The pid of the weapon performing the attack. (May be -1 if the attack is unarmed)
Critter arg1 - The attacker
Critter arg2 - The target
int arg3 - The amount of damage
int arg4 - Unused, always -1. Use this if you are using the same procedure for HOOK_DEATHANIM1 and HOOK_DEATHANIM2 (since sfall 4.1/3.8.24)
int ret0 - The pid of an object to override the attacking weapon with
Runs after Fallout has calculated the death animation. Lets you set your own custom frame id, so more powerful than `HOOK_DEATHANIM1`, but performs no validation.
When using `critter_dmg` function, this script will also run. In that case weapon pid will be -1 and attacker will point to an object with `obj_art_fid == 0x20001F5`.
Does not run for critters in the knockdown/out state.
```
Item arg0 - The pid of the weapon performing the attack. (May be -1 if the attack is unarmed)
Critter arg1 - The attacker
Critter arg2 - The target
int arg3 - The amount of damage
int arg4 - The death anim id calculated by Fallout
int ret0 - The death anim id to override with
```
-------------------------------------------
#### `HOOK_COMBATDAMAGE (hs_combatdamage.int)`
Runs when:
1) Game calculates how much damage each target will get. This includes primary target as well as all extras (explosions and bursts). This happens BEFORE the actual attack animation.
2) AI decides whether it is safe to use area attack (burst, grenades), if he might hit friendlies.
Does not run for misses, or non-combat damage like dynamite explosions.
```
Critter arg0 - The target
Critter arg1 - The attacker
int arg2 - The amount of damage to the target
int arg3 - The amount of damage to the attacker
int arg4 - The special effect flags for the target (use bwand DAM_* to check specific flags)
int arg5 - The special effect flags for the attacker (use bwand DAM_* to check specific flags)
int arg6 - The weapon used in the attack
int arg7 - The bodypart that was struck
int arg8 - Damage Multiplier (this is divided by 2, so a value of 3 does 1.5x damage, and 8 does 4x damage. Usually it's 2; for critical hits, the value is taken from the critical table; with Silent Death perk and the corresponding attack conditions, the value will be doubled)
int arg9 - Number of bullets actually hit the target (1 for melee attacks)
int arg10 - The amount of knockback to the target
int arg11 - Attack Type (see ATKTYPE_* constants)
mixed arg12 - computed attack data (see C_ATTACK_* for offsets and use get/set_object_data functions to get/set the data)
int ret0 - The damage to the target
int ret1 - The damage to the attacker
int ret2 - The special effect flags for the target
int ret3 - The special effect flags for the attacker
int ret4 - The amount of knockback to the target
```
-------------------------------------------
#### `HOOK_ONDEATH (hs_ondeath.int)`
Runs immediately after a critter dies for any reason. No return values; this is just a convenience for when you need to do something after death for a large number of different critters and don't want to have to script each and every one of them.
```
Critter arg0 - The critter that just died
```
-------------------------------------------
#### `HOOK_FINDTARGET (hs_findtarget.int)`
Runs when the AI is trying to pick a target in combat. Fallout first chooses a list of 4 likely suspects, then normally sorts them in order of weakness/distance/etc depending on the AI caps of the attacker.
This hook replaces that sorting function, allowing you to sort the targets in some arbitrary way.
The return values can include critters that weren't in the list of possible targets, but the additional targets may still be discarded later on in the combat turn if they are out of the attackers perception or the chance of a successful hit is too low. The list of possible targets often includes duplicated entries, but this is fixed in sfall 4.2.3/3.8.23.
Use `set_sfall_return` to give the 4 targets, in order of preference. If you want to specify less than 4 targets, fill in the extra spaces with 0's or pass -1 to skip the return value.
__NOTE:__ The engine can choose targets by the following criteria:
1) The nearest enemy to the attacker.
2) The enemy that attacked the attacker.
3) The enemy that attacked an NPC from the same team as the attacker.
4) The enemy that is attacked by an NPC from the same team as the attacker.
__NOTE:__ The hook is executed twice when entering the barter screen or after transaction: the first time is for the player and the second time is for NPC.
To add proper check for ammo before shooting and proper calculation of number of burst rounds (hook type 1 and 2 in `arg3`), set **CheckWeaponAmmoCost=1** in **Misc** section of ddraw.ini.
int arg1 - Number of bullets in burst or 1 for single shots
int arg2 - The amount of ammo that will be consumed, calculated by the original ammo cost function (this is basically 2 for Super Cattle Prod and Mega Power Fist)
NOTE: for hook type 2, this value is the ammo cost per round (default is always 1)
int arg3 - Type of hook:
0 - when subtracting ammo after single shot attack
1 - when checking for "out of ammo" before attack
2 - when calculating number of burst rounds
3 - when subtracting ammo after burst attack
int ret0 - The new amount of ammo to be consumed, or ammo cost per round for hook type 2 (set to 0 for unlimited ammo)
```
-------------------------------------------
#### `HOOK_KEYPRESS (hs_keypress.int)`
Runs once every time when any key was pressed or released.
int arg2 - key VK code (very similar to ASCII codes)
int ret0 - overrides the pressed key (a new key DX scancode or 0 for no override)
```
-------------------------------------------
#### `HOOK_MOUSECLICK (hs_mouseclick.int)`
Runs once every time when a mouse button was pressed or released.
```
int arg0 - event type: 1 - pressed, 0 - released
int arg1 - button number (0 - left, 1 - right, up to 7)
```
-------------------------------------------
#### `HOOK_USESKILL (hs_useskill.int)`
Runs when using any skill on any object.
This is fired before the default handlers are called, which you can override. In this case you should write your own skill use handler entirely, or otherwise nothing will happen (this includes fade in/fade out, time lapsing and messages - all of this can be scripted; to get vanilla text messages - use `message_str_game` along with `sprintf`).
Suggested use - override first aid/doctor skills to buff/nerf them, override steal skill to disallow observing NPCs inventories in some cases.
Doesn't seem to run when lock picking.
```
Critter arg0 - The user critter
Obj arg1 - The target object
int arg2 - skill being used
int arg3 - skill bonus from items such as first aid kits
int ret0 - overrides hard-coded handler (-1 - use engine handler, any other value - override)
```
-------------------------------------------
#### `HOOK_STEAL (hs_steal.int)`
Runs when checking an attempt to steal or plant an item in other inventory using Steal skill.
This is fired before the default handlers are called, which you can override. In this case you MUST provide message of the result to player ("You steal the %s", "You are caught planting the %s", etc.).
__NOTE:__`obj_can_see_obj` calls this first when deciding if critter can possibly see another critter with regard to perception, lighting, sneak factors.<br>
If check fails, the end result is false. If check succeeds (e.g. critter is within perception range), another check is made if there is any blocking tile between two critters (which includes stuff like windows, large bushes, barrels, etc.) and if there is - check still fails.<br>
You can override "within perception" check by returning 0 or 1, OR, as a convenience, you can also override blocking check after the perception check by returning 2 instead. In this case you should add "line of sight" check inside your hook script, otherwise critters will detect you through walls.