mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec4ba13a28 | ||
|
|
14a6ef7fc6 | ||
|
|
c3a74070ca | ||
|
|
302fd4bb51 | ||
|
|
c7520c71cf | ||
|
|
45632d6887 | ||
|
|
866c9165c0 | ||
|
|
813abbf452 | ||
|
|
c6864e01f6 | ||
|
|
0eab48932a | ||
|
|
6c3efc5b84 | ||
|
|
cb8d7dc94a | ||
|
|
5786cfb267 | ||
|
|
78dfb530a9 | ||
|
|
d588f2e691 | ||
|
|
daf7a82997 | ||
|
|
9ad2b19e1a | ||
|
|
4b66dd3682 | ||
|
|
4899aeece1 | ||
|
|
946266e367 | ||
|
|
438ea071fc | ||
|
|
89760235c7 | ||
|
|
321f1c6034 | ||
|
|
513338f160 | ||
|
|
08b92542f4 | ||
|
|
46013c94b0 | ||
|
|
7a4ab59089 | ||
|
|
e505e204cb | ||
|
|
7343754d53 | ||
|
|
ae1e17d359 | ||
|
|
32dbe5581d | ||
|
|
a93ad5a9ba | ||
|
|
874b047ce1 | ||
|
|
afa590c5a8 | ||
|
|
b89c52fcec | ||
|
|
ec2d323f75 | ||
|
|
586679b07f |
@@ -46,8 +46,6 @@ nb-configuration.xml
|
|||||||
*.user
|
*.user
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
*.VC.db
|
|
||||||
*.VC.opendb
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|||||||
@@ -14,18 +14,17 @@ Part_0=1 ; Tell sfall that we want to modify the head entries
|
|||||||
|
|
||||||
[c_00_0] ; The entries for the head table of the men critter type.
|
[c_00_0] ; The entries for the head table of the men critter type.
|
||||||
e0_DamageMultiplier=100 ;First level critical
|
e0_DamageMultiplier=100 ;First level critical
|
||||||
e0_EffectFlags=0 ; The value of the flags (see DAM_* in Define.h)
|
e0_EffectFlags=0
|
||||||
; For multiple effects, use the sum of the flags (e.g. 2050 = knocked down + bypass armor)
|
|
||||||
e0_StatCheck=0
|
e0_StatCheck=0
|
||||||
e0_StatMod=0
|
e0_StatMod=0
|
||||||
e0_FailureEffect=0
|
e0_FailureEffect=0
|
||||||
e0_Message=0 ; This is an index into the msg file, not a string
|
e0_Message=0 ; This is an index into the msg file, not a string
|
||||||
e0_FailMessage=0
|
e0_FailMessage=0
|
||||||
;e1_... ; Second level critical
|
e1_... ; Second level critical
|
||||||
;... ; And so on up to the hardest hitting level 5 critical
|
... ; And so on up to the hardest hitting level 5 critical
|
||||||
;e5_...
|
e5_...
|
||||||
|
|
||||||
;You can skip any entries from this section to leave them at their defaults
|
;You can skip any entries from this section to leave them at their defaults
|
||||||
;Any missing entries for critters 19-37 will be replaced by a 0, since they have no default
|
;Any missing entries for critters 19-37 will be replaced by a 0, since they have no default
|
||||||
;Note that the UOP fixes are included in the defaults, and do not need to be
|
;Note that the UOP fixes are included in the defaults, and do not need to be
|
||||||
; added manually when using this override format, as opposed to the old one
|
; added manually when using this override format, as opposed to the old one
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
; Allows you to configure advanced settings for drugs.
|
|
||||||
; Maximum 50 drugs are allowed
|
|
||||||
; Note: for vanilla drugs, you can only override the values of NumEffect and AddictTime parameters
|
|
||||||
|
|
||||||
[main]
|
|
||||||
; set to 1 to change Jet addiction to be removed after the time expires
|
|
||||||
JetWithdrawal=0
|
|
||||||
|
|
||||||
; total number of drugs in this file
|
|
||||||
Count=0
|
|
||||||
|
|
||||||
; count starts from 1
|
|
||||||
[1]
|
|
||||||
; drug item PID
|
|
||||||
PID=0
|
|
||||||
; set a limit on the count of first and second delayed effects when using the drug, i.e. if the queue has delayed effects from the drug,
|
|
||||||
; and their count is greater or equal to NumEffects, it is not possible to obtain new effects from using the drug
|
|
||||||
; this count is 4 for Buffout/Mentats/Psycho/Jet
|
|
||||||
; set -1 to leave this parameter unchanged
|
|
||||||
NumEffects=-1
|
|
||||||
; the duration of the addiction effect in game minutes (i.e. 1440 = 24 game hours)
|
|
||||||
; set to 0 to use the default time (7 days for all drugs except Jet)
|
|
||||||
AddictTime=0
|
|
||||||
; the number of the global variable from vault13.gam responsible for displaying addiction in the character screen
|
|
||||||
; set to 0 if the addiction is not required
|
|
||||||
GvarID=0
|
|
||||||
; the index number from editor.msg to display the title of the addiction (the description uses the index number of TextID + 100)
|
|
||||||
TextID=-1
|
|
||||||
; the line number (0-indexed) of the corresponding FRM in skilldex.lst
|
|
||||||
FrmID=-1
|
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
; have an additional NoHardcode option in this file which can
|
; have an additional NoHardcode option in this file which can
|
||||||
; be used to remove their hardcoded effects, and add new stat/skill effects
|
; be used to remove their hardcoded effects, and add new stat/skill effects
|
||||||
|
|
||||||
;Name=The name of the perk (max 63 characters)
|
;Name=The name of the perk
|
||||||
;Desc=The description of the perk (max 255 characters)
|
;Desc=The description of the perk
|
||||||
;Image=The line number (0-indexed) of the corresponding FRM in skilldex.lst
|
;Image=The line number (0-indexed) of the corresponding FRM in skilldex.lst
|
||||||
;Ranks=The number of perk levels
|
;Ranks=The number of perk levels
|
||||||
;Level=The minimum required level
|
;Level=The minimum required level
|
||||||
@@ -35,18 +35,10 @@
|
|||||||
|
|
||||||
;If the value is set to -99999, the variable will be ignored (similar to comment out that line)
|
;If the value is set to -99999, the variable will be ignored (similar to comment out that line)
|
||||||
|
|
||||||
;You can add simple extra perks with ID numbers from 119 to 255
|
;This is a modification to perk 119
|
||||||
; that do not require using scripting functions to change player stats, and can also be used for Armor perks
|
[119]
|
||||||
;These parameters are only for extra perks
|
|
||||||
;Stat1-Stat2 = same as Stat
|
|
||||||
;Stat1Mag-Stat2Mag = same as StatMag
|
|
||||||
;Skill3-Skill5 = The skill to be modified (or -1 for none)
|
|
||||||
;Skill3Mod-Skill5Mod = The increased/decreased value to the modified skill
|
|
||||||
|
|
||||||
;This is a modification to vanilla perk 0
|
|
||||||
[0]
|
|
||||||
Name=Example
|
Name=Example
|
||||||
Desc=This is an example perk description
|
Desc=This is an example perk~Second line
|
||||||
Image=72
|
Image=72
|
||||||
Ranks=1
|
Ranks=1
|
||||||
Level=1
|
Level=1
|
||||||
@@ -65,39 +57,6 @@ INT=0
|
|||||||
AGL=0
|
AGL=0
|
||||||
LCK=0
|
LCK=0
|
||||||
|
|
||||||
;Example for creating extra perks
|
|
||||||
[119]
|
|
||||||
Name=Extra Perk
|
|
||||||
Desc=This perk can change player stats and skills
|
|
||||||
Image=72
|
|
||||||
Ranks=1
|
|
||||||
Level=2
|
|
||||||
Type=0
|
|
||||||
Stat=-1
|
|
||||||
StatMag=0
|
|
||||||
Skill1=-1
|
|
||||||
Skill1Mag=0
|
|
||||||
Skill2=-1
|
|
||||||
Skill2Mag=0
|
|
||||||
STR=0
|
|
||||||
PER=0
|
|
||||||
END=0
|
|
||||||
CHR=0
|
|
||||||
INT=0
|
|
||||||
AGL=0
|
|
||||||
LCK=0
|
|
||||||
; for extra perks only
|
|
||||||
Stat1=-1
|
|
||||||
Stat1Mag=0
|
|
||||||
Stat2=-1
|
|
||||||
Stat2Mag=0
|
|
||||||
Skill3=-1
|
|
||||||
Skill3Mod=0
|
|
||||||
Skill4=-1
|
|
||||||
Skill4Mod=0
|
|
||||||
Skill5=-1
|
|
||||||
Skill5Mod=0
|
|
||||||
|
|
||||||
;This is a modification to trait 0
|
;This is a modification to trait 0
|
||||||
[t0]
|
[t0]
|
||||||
NoHardcode=0
|
NoHardcode=0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[sfall]
|
[sfall]
|
||||||
SaveInCombat=Cannot save at this time.
|
SaveInCombat=Cannot save at this time
|
||||||
KarmaGain=You gained %d karma.
|
KarmaGain=You gained %d karma.
|
||||||
KarmaLoss=You lost %d karma.
|
KarmaLoss=You lost %d karma.
|
||||||
HighlightFail1=You aren't carrying a motion sensor.
|
HighlightFail1=You aren't carrying a motion sensor.
|
||||||
@@ -7,11 +7,8 @@ HighlightFail2=Your motion sensor is out of charge.
|
|||||||
SuperStimExploitMsg=You cannot use a super stim on someone who is not injured!
|
SuperStimExploitMsg=You cannot use a super stim on someone who is not injured!
|
||||||
BlockedCombat=You cannot enter combat at this time.
|
BlockedCombat=You cannot enter combat at this time.
|
||||||
SaveSfallDataFail=ERROR saving extended savegame information! Check if other programs interfere with savegame files/folders and try again.
|
SaveSfallDataFail=ERROR saving extended savegame information! Check if other programs interfere with savegame files/folders and try again.
|
||||||
PartyLvlMsg=Lvl:
|
|
||||||
PartyACMsg=AC:
|
|
||||||
PartyAddictMsg=Addict
|
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=Race
|
RaceText=Race
|
||||||
StyleText=Style
|
StyleText=Style
|
||||||
MainButtonText=Appearance
|
MainButtonText=Appearance
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
; Allows to reassign books to different object PIDs, text messages, and skills.
|
; Allows to reassign books to different object PIDs, text messages.
|
||||||
; Maximum 50 books are allowed
|
; Maximum 30 books are allowed (25 if overrideVanilla is 0)
|
||||||
|
|
||||||
[main]
|
[main]
|
||||||
; total number of books in this file
|
; total number of books in this file
|
||||||
count=1
|
count=5
|
||||||
|
|
||||||
; set to 1 to override all vanilla books, so you will have to define ALL the necessary books in this file;
|
; set to 1 to override all vanilla books, so you will have to define ALL books in this file;
|
||||||
; otherwise only new books are defined here. To just override the text/skill of a vanilla book,
|
; otherwise only new books are defined here
|
||||||
; specify its PID in the new book and set the parameters
|
|
||||||
overrideVanilla=0
|
overrideVanilla=0
|
||||||
|
|
||||||
; count starts from 1
|
; count starts from 1
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
;Controls the elevators
|
;Controls the elevators
|
||||||
;Image must match up with the image of an existing elevator (can be overrided in sfall 4.1.4/3.8.14 or newer)
|
;Image must match up with the image of an existing elevator
|
||||||
;Make sure you specify the correct number of exit targets
|
;Make sure you specify the correct number of exit targets
|
||||||
;The maximum number of elevators is currently capped at 50
|
;The maximum number of elevators is currently capped at 50
|
||||||
;Use the line number (0-indexed) of the corresponding FRM in intrface.lst to set the appearance of the elevator
|
|
||||||
|
|
||||||
;Override elevator 0
|
;Override elevator 0
|
||||||
[0]
|
[000]
|
||||||
;This elevator will use the settings of the original forth elevator
|
;This elevator uses the frm of the original forth elevator
|
||||||
Image=4
|
Image=4
|
||||||
|
|
||||||
;Set up the first exit point
|
;Set up the first exit point
|
||||||
@@ -24,23 +23,4 @@ ID3=50
|
|||||||
Elevation3=0
|
Elevation3=0
|
||||||
Tile3=12944
|
Tile3=12944
|
||||||
|
|
||||||
;No forth exit point
|
;No forth exit point
|
||||||
|
|
||||||
;Override FRM images of elevator 0. Set ButtonsFrm to -1 to use default buttons from MainFrm
|
|
||||||
MainFrm=143
|
|
||||||
ButtonsFrm=-1
|
|
||||||
|
|
||||||
|
|
||||||
;An example of a new elevator
|
|
||||||
[24]
|
|
||||||
;Set Image to 24 to create a new elevator type (0-23 are the original elevator types)
|
|
||||||
Image=24
|
|
||||||
|
|
||||||
;Set the number of buttons
|
|
||||||
ButtonCount=3
|
|
||||||
|
|
||||||
;Set the appearance of the elevator
|
|
||||||
MainFrm=148
|
|
||||||
ButtonsFrm=151
|
|
||||||
|
|
||||||
;Set up the exit points for all three buttons (see examples above)
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
;WeaponAnims codes
|
|
||||||
; 1 - Knife (D)
|
|
||||||
; 2 - Club (E)
|
|
||||||
; 3 - Sledgehammer (F)
|
|
||||||
; 4 - Spear (G)
|
|
||||||
; 5 - Pistol (H)
|
|
||||||
; 6 - SMG (I)
|
|
||||||
; 7 - Rifle (J)
|
|
||||||
; 8 - Big Gun (K)
|
|
||||||
; 9 - Minigun (L)
|
|
||||||
; 10 - Rocket Launcher (M)
|
|
||||||
; 11-15 - sfall additional weapon animation codes
|
|
||||||
|
|
||||||
; This section maps 7 armor types to corresponding armor item PIDs
|
|
||||||
[ArmorTypes]
|
|
||||||
Jacket = 74,265
|
|
||||||
Leather = 1,379
|
|
||||||
Metal = 2,240,380
|
|
||||||
Power = 3,232
|
|
||||||
AdvPower = 348,349
|
|
||||||
Combat = 17,239,381
|
|
||||||
Robe = 113,524
|
|
||||||
|
|
||||||
; Default armor FIDs (the same for all NPCs)
|
|
||||||
[Default]
|
|
||||||
Power=16777217
|
|
||||||
AdvPower=16777287
|
|
||||||
Robe=16777218
|
|
||||||
|
|
||||||
|
|
||||||
; Sulik
|
|
||||||
[1]
|
|
||||||
PID=16777313
|
|
||||||
WeaponAnims=1,3,4,6
|
|
||||||
Default=16777280
|
|
||||||
Leather=16777325
|
|
||||||
Power=16777324
|
|
||||||
Metal=16777323
|
|
||||||
Jacket=16777321
|
|
||||||
Combat=16777322
|
|
||||||
|
|
||||||
; Vic
|
|
||||||
[2]
|
|
||||||
PID=16777278
|
|
||||||
WeaponAnims=1,5,7
|
|
||||||
Default=16777307
|
|
||||||
Jacket=16777329
|
|
||||||
Combat=16777330
|
|
||||||
Metal=16777331
|
|
||||||
Power=16777332
|
|
||||||
Leather=16777333
|
|
||||||
|
|
||||||
; Cassidy
|
|
||||||
[3]
|
|
||||||
PID=16777305
|
|
||||||
WeaponAnims=4,5,7
|
|
||||||
Default=16777354
|
|
||||||
Leather=16777260
|
|
||||||
Power=16777328
|
|
||||||
Metal=16777327
|
|
||||||
Jacket=16777351
|
|
||||||
Combat=16777326
|
|
||||||
|
|
||||||
; Myron
|
|
||||||
[4]
|
|
||||||
PID=16777376
|
|
||||||
WeaponAnims=1,5
|
|
||||||
Default=16777304
|
|
||||||
Leather=
|
|
||||||
Power=16777349
|
|
||||||
Combat=16777350
|
|
||||||
|
|
||||||
; Cat Jules
|
|
||||||
[5]
|
|
||||||
PID=16777734
|
|
||||||
WeaponAnims=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
||||||
Default=16777353
|
|
||||||
Leather=16777347
|
|
||||||
Metal=16777348
|
|
||||||
Jacket=16777346
|
|
||||||
Combat=16777226
|
|
||||||
|
|
||||||
; Kitsune
|
|
||||||
[6]
|
|
||||||
PID=16777724
|
|
||||||
WeaponAnims=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
|
||||||
Default=16777222
|
|
||||||
Leather=16777221
|
|
||||||
Metal=16777223
|
|
||||||
Jacket=16777222
|
|
||||||
Combat=16777219
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
[Highlighting]
|
|
||||||
|
|
||||||
; DX scancode of a key to press to highlight items on the ground
|
|
||||||
; 42 - SHIFT key
|
|
||||||
Key=42
|
|
||||||
|
|
||||||
; Set to 1 to also highlight containers
|
|
||||||
Containers=1
|
|
||||||
|
|
||||||
; Set to 1 to also highlight lootable corpses
|
|
||||||
Corpses=1
|
|
||||||
|
|
||||||
; Set to 1 to only highlight objects in the player's line-of-sight
|
|
||||||
CheckLOS=0
|
|
||||||
|
|
||||||
; Set the color of outlines, available colors:
|
|
||||||
; 1 - glowing red
|
|
||||||
; 2 - red
|
|
||||||
; 4 - grey
|
|
||||||
; 8 - glowing green
|
|
||||||
; 16 - bright yellow
|
|
||||||
; 32 - dark yellow
|
|
||||||
; 64 - purple
|
|
||||||
OutlineColor=16
|
|
||||||
|
|
||||||
; Motion Scanner mode:
|
|
||||||
; 0 - ignored
|
|
||||||
; 1 - requires Motion Scanner present in player inventory to activate highlighting
|
|
||||||
; 2 - requires Motion Scanner and also requires 1 charge on every use (depleted scanner will not work anymore)
|
|
||||||
MotionScanner=0
|
|
||||||
|
|
||||||
|
|
||||||
[CombatControl]
|
|
||||||
;Allows you to directly control other critters in combat
|
|
||||||
;Set to 0 to disable
|
|
||||||
;Set to 1 to control all critters in combat
|
|
||||||
;Set to 2 to control all party members
|
|
||||||
;If you want to control only specific critters, uncomment the PIDList line and set a comma delimited list of PIDs
|
|
||||||
Mode=0
|
|
||||||
;PIDList=62,89,97,107,160,161
|
|
||||||
|
|
||||||
;Choose a notification box to display the name of the controlled critter above the interface bar
|
|
||||||
;Must be between 5 and (4 + the value of BoxBarCount in ddraw.ini)
|
|
||||||
;For sfall 4.1.6+ the number for the notification box is set automatically, so just use any value
|
|
||||||
;Set to 0 to disable
|
|
||||||
DisplayName=0
|
|
||||||
|
|
||||||
;Set the color of display name, available colors:
|
|
||||||
;0 - green
|
|
||||||
;1 - red
|
|
||||||
;2 - white
|
|
||||||
;3 - yellow
|
|
||||||
;4 - dark yellow
|
|
||||||
;5 - blue
|
|
||||||
;6 - purple
|
|
||||||
;7 - dull pink
|
|
||||||
DisplayNameColor=0
|
|
||||||
+72
-193
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Auto Doors mod v1.1 for Fallout 2 by Mr.Stalin
|
Auto Doors mod for Fallout 2 by Mr.Stalin
|
||||||
----------------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
- allows the player to automatically open/walk through unlocked doors when not in combat
|
- allows the player to automatically open/walk through unlocked doors when not in combat
|
||||||
|
|
||||||
@@ -12,91 +12,66 @@ NOTE: this script requires compiler from sfall modderspack with -s option
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Include Files */
|
|
||||||
#include "..\headers\define.h"
|
#include "..\headers\define.h"
|
||||||
//#include "..\headers\command.h"
|
#include "..\headers\command.h"
|
||||||
#include "..\..\scripting_docs\headers\sfall.h"
|
#include "..\headers\sfall\sfall.h"
|
||||||
#include "..\..\scripting_docs\headers\define_extra.h"
|
#include "..\headers\sfall\define_extra.h"
|
||||||
|
|
||||||
/* Standard Script Procedures */
|
|
||||||
procedure start;
|
procedure start;
|
||||||
procedure map_enter_p_proc;
|
procedure map_enter_p_proc;
|
||||||
|
|
||||||
procedure set_door_flag(variable state);
|
procedure set_door_flag(variable state);
|
||||||
procedure set_door_flag_array(variable state);
|
|
||||||
procedure combatturn_handler;
|
procedure combatturn_handler;
|
||||||
|
|
||||||
/* Defines */
|
|
||||||
#define PORTAL (0)
|
#define PORTAL (0)
|
||||||
#define DOOR_FLAGS (0x24)
|
#define DOOR_FLAGS (0x24)
|
||||||
|
|
||||||
variable only_once := 0;
|
variable only_once := 0;
|
||||||
variable arrayPid;
|
|
||||||
|
|
||||||
procedure start begin
|
procedure start begin
|
||||||
if game_loaded then begin
|
if game_loaded then begin
|
||||||
if (sfall_ver_major >= 4) then
|
if (sfall_ver_major >= 4) then
|
||||||
register_hook_proc(HOOK_COMBATTURN, combatturn_handler);
|
register_hook_proc(HOOK_COMBATTURN, combatturn_handler);
|
||||||
else
|
else
|
||||||
set_global_script_repeat(40); // for sfall 3.x
|
set_global_script_repeat(60);
|
||||||
call map_enter_p_proc;
|
call map_enter_p_proc;
|
||||||
end else begin
|
end else begin
|
||||||
if (only_once == 1 and combat_is_initialized) then begin
|
if (only_once == 1 and combat_is_initialized) then begin
|
||||||
only_once := 2;
|
only_once := 2;
|
||||||
call set_door_flag(0); // reset flag when entering combat mode
|
call set_door_flag(false);
|
||||||
end else if (only_once == 2 and not(combat_is_initialized)) then begin
|
end else if (only_once == 2 and not(combat_is_initialized)) then begin
|
||||||
only_once := 1;
|
only_once := 1;
|
||||||
if (sfall_ver_major >= 4) then set_global_script_repeat(0);
|
if (sfall_ver_major >= 4) then
|
||||||
call set_door_flag(FLAG_WALKTHRU); // set flag after combat mode ends
|
set_global_script_repeat(0);
|
||||||
|
call set_door_flag(FLAG_WALKTHRU);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
procedure map_enter_p_proc begin
|
procedure map_enter_p_proc begin
|
||||||
if (arrayPid) then begin
|
|
||||||
// when changing maps, delete the current array to create a new one
|
|
||||||
free_array(arrayPid);
|
|
||||||
arrayPid := 0;
|
|
||||||
end
|
|
||||||
only_once := 1;
|
only_once := 1;
|
||||||
call set_door_flag(FLAG_WALKTHRU); // set flag when entering the map
|
call set_door_flag(FLAG_WALKTHRU);
|
||||||
end
|
end
|
||||||
|
|
||||||
procedure set_door_flag(variable state) begin
|
procedure set_door_flag(variable state) begin
|
||||||
variable objectMap, obj, objPid, i;
|
variable objectMap, obj, objPid, arrayPid, i;
|
||||||
|
|
||||||
if (arrayPid) then begin
|
|
||||||
call set_door_flag_array(state);
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
objectMap := list_as_array(LIST_SCENERY);
|
objectMap := list_as_array(LIST_SCENERY);
|
||||||
arrayPid := create_array(0, 0);
|
arrayPid := temp_array(1, 0);
|
||||||
|
|
||||||
foreach (obj in objectMap) begin
|
foreach (obj in objectMap) begin
|
||||||
objPid := obj_pid(obj);
|
objPid := obj_pid(obj);
|
||||||
|
|
||||||
if (proto_data(objPid, sc_type) != PORTAL) or is_in_array(objPid, arrayPid) then
|
if (proto_data(objPid, sc_type) != PORTAL) or is_in_array(objPid, arrayPid) then
|
||||||
continue; // next object
|
continue;
|
||||||
|
|
||||||
resize_array(arrayPid, i + 1);
|
|
||||||
arrayPid[i] := objPid;
|
arrayPid[i] := objPid;
|
||||||
i++;
|
i++;
|
||||||
set_proto_data(objPid, DOOR_FLAGS, state);
|
resize_array(arrayPid, i + 1);
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
procedure set_door_flag_array(variable state) begin
|
|
||||||
variable objPid;
|
|
||||||
foreach (objPid in arrayPid) begin
|
|
||||||
set_proto_data(objPid, DOOR_FLAGS, state);
|
set_proto_data(objPid, DOOR_FLAGS, state);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
procedure combatturn_handler begin
|
procedure combatturn_handler begin
|
||||||
if (only_once == 1) then begin
|
if (only_once == 1) then
|
||||||
only_once := 2;
|
|
||||||
call set_door_flag_array(0);
|
|
||||||
set_global_script_repeat(60);
|
set_global_script_repeat(60);
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Auto Doors mod v1.1 for Fallout 2 by Mr.Stalin
|
Auto Doors mod for Fallout 2 by Mr.Stalin
|
||||||
----------------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
- allows the player to automatically open/walk through unlocked doors when not in combat
|
- allows the player to automatically open/walk through unlocked doors when not in combat
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
Example implementation of the algorithm of how the game engine sets the explosive timer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "..\..\scripting_docs\headers\sfall.h"
|
|
||||||
#include "..\..\scripting_docs\headers\define_lite.h"
|
|
||||||
|
|
||||||
// from CONDTION.H
|
|
||||||
#define ROLL_CRITICAL_FAILURE (0)
|
|
||||||
#define ROLL_FAILURE (1)
|
|
||||||
#define ROLL_SUCCESS (2)
|
|
||||||
#define ROLL_CRITICAL_SUCCESS (3)
|
|
||||||
|
|
||||||
procedure start;
|
|
||||||
|
|
||||||
procedure start begin
|
|
||||||
if game_loaded then begin
|
|
||||||
register_hook(HOOK_EXPLOSIVETIMER);
|
|
||||||
end else begin
|
|
||||||
variable
|
|
||||||
time := get_sfall_arg,
|
|
||||||
result := ROLL_CRITICAL_FAILURE;
|
|
||||||
|
|
||||||
if has_trait(TRAIT_PERK, dude_obj, PERK_demolition_expert_perk) then
|
|
||||||
result := ROLL_SUCCESS;
|
|
||||||
else begin
|
|
||||||
result := roll_vs_skill(dude_obj, SKILL_TRAPS, 0);
|
|
||||||
end
|
|
||||||
|
|
||||||
if (result) then begin
|
|
||||||
if (result == ROLL_FAILURE) then
|
|
||||||
time /= 2;
|
|
||||||
else begin // success or critical success
|
|
||||||
result := ROLL_SUCCESS;
|
|
||||||
end
|
|
||||||
end else begin // critical failure
|
|
||||||
time := 0;
|
|
||||||
result := ROLL_FAILURE;
|
|
||||||
end
|
|
||||||
|
|
||||||
set_sfall_return(time);
|
|
||||||
set_sfall_return(result); // failure/success
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
Example implementation of the algorithm of how the game engine checks if one critter sees another critter.
|
|
||||||
|
|
||||||
NOTE: the AllowUnsafeScripting option must be enabled.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "..\headers\define.h"
|
|
||||||
#include "..\headers\command.h"
|
|
||||||
#include "..\headers\sfall\sfall.h"
|
|
||||||
#include "..\headers\sfall\define_extra.h"
|
|
||||||
|
|
||||||
#define can_see_(source, target) call_offset_r2(0x412BEC, source, target)
|
|
||||||
#define obj_dist_(target, source) call_offset_r2(0x48BBD4, target, source)
|
|
||||||
|
|
||||||
procedure start;
|
|
||||||
|
|
||||||
procedure start begin
|
|
||||||
if game_loaded then begin
|
|
||||||
register_hook(HOOK_WITHINPERCEPTION);
|
|
||||||
end else begin
|
|
||||||
variable
|
|
||||||
source := get_sfall_arg,
|
|
||||||
target := get_sfall_arg,
|
|
||||||
original := get_sfall_arg,
|
|
||||||
hookType := get_sfall_arg, /* new arg */
|
|
||||||
result := 0,
|
|
||||||
distance;
|
|
||||||
|
|
||||||
if target then begin
|
|
||||||
distance := get_critter_stat(source, STAT_pe);
|
|
||||||
|
|
||||||
if can_see_(source, target) then begin
|
|
||||||
distance *= 5;
|
|
||||||
if (get_flags(target) bwand FLAG_TRANSGLASS) then distance /= 2;
|
|
||||||
end else if combat_is_initialized then begin
|
|
||||||
distance *= 2;
|
|
||||||
end
|
|
||||||
|
|
||||||
if (target == dude_obj) then begin
|
|
||||||
if sneak_success then begin
|
|
||||||
distance /= 4;
|
|
||||||
if has_skill(target, SKILL_SNEAK) > 120 then distance -= 1;
|
|
||||||
end else if dude_is_sneaking then begin
|
|
||||||
distance := distance * 2 / 3;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if obj_dist_(target, source) <= distance then result := 1;
|
|
||||||
|
|
||||||
// example
|
|
||||||
if (result) then begin
|
|
||||||
display_msg("hs_withinperception: " + obj_name(source) + " sees " + obj_name(target) + " [original: " + original + " script: " + result + "]");
|
|
||||||
end else begin
|
|
||||||
display_msg("hs_withinperception: " + obj_name(source) + " does not see " + obj_name(target) + " [original: " + original + " script: " + result + "]");
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
//set_sfall_return(result);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Binary file not shown.
@@ -1,134 +0,0 @@
|
|||||||
/**
|
|
||||||
|
|
||||||
Item Highlight mod.
|
|
||||||
|
|
||||||
Previously was part of sfall itself, now a separate mod.
|
|
||||||
Features:
|
|
||||||
- highlighting items, containers (optional) and lootable corpses (optional) on the ground
|
|
||||||
- configurable hotkey is used to trigger highlight
|
|
||||||
- only objects in direct line-of-sight of player are highlighted (optional)
|
|
||||||
- motion scanner is required to enable highlight (optional)
|
|
||||||
- motion scanner charges are decreased on each use (optional)
|
|
||||||
|
|
||||||
NOTE: this script requires compiler from sfall modderspack with -s option
|
|
||||||
(short circuit evaluation)
|
|
||||||
|
|
||||||
version 1.1
|
|
||||||
|
|
||||||
**/
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
//#include "..\..\..\!SRC\headers\define.h"
|
|
||||||
|
|
||||||
#define CRITTER_IS_DEAD (1)
|
|
||||||
#define PID_MOTION_SENSOR (59)
|
|
||||||
#define NO_HIGHLIGHT(obj) (get_flags(obj) bwand FLAG_NOHIGHLIGHT)
|
|
||||||
#define NO_STEAL(obj) (get_proto_data(obj_pid(obj), PROTO_CR_FLAGS) bwand CFLG_NOSTEAL)
|
|
||||||
|
|
||||||
variable configSection := "Highlighting";
|
|
||||||
variable highlightKey;
|
|
||||||
variable isHighlight;
|
|
||||||
variable alsoContainer;
|
|
||||||
variable alsoCorpse;
|
|
||||||
variable checkLOS;
|
|
||||||
variable outlineColor;
|
|
||||||
variable motionScanner;
|
|
||||||
variable highlightFailMsg1;
|
|
||||||
variable highlightFailMsg2;
|
|
||||||
|
|
||||||
procedure ToggleHighlightObject(variable obj, variable enable) begin
|
|
||||||
if obj and (not enable or not checkLOS or not obj_blocking_line(dude_obj, tile_num(obj), BLOCKING_TYPE_SHOOT)) then begin
|
|
||||||
if (alsoContainer and obj_item_subtype(obj) == item_type_container) or (not NO_HIGHLIGHT(obj)) then begin
|
|
||||||
if (enable) then set_outline(obj, outlineColor);
|
|
||||||
else set_outline(obj, 0);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
procedure ToggleHighlight(variable enable) begin
|
|
||||||
variable obj;
|
|
||||||
foreach obj in list_as_array(LIST_GROUNDITEMS) begin
|
|
||||||
if obj != outlined_object then begin
|
|
||||||
call ToggleHighlightObject(obj, enable);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if (alsoCorpse) then begin
|
|
||||||
foreach obj in list_as_array(LIST_CRITTERS) begin
|
|
||||||
if critter_state(obj) == CRITTER_IS_DEAD and not NO_STEAL(obj) then begin
|
|
||||||
call ToggleHighlightObject(obj, enable);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tile_refresh_display;
|
|
||||||
end
|
|
||||||
|
|
||||||
procedure KeyPressHandler begin
|
|
||||||
variable pressed := get_sfall_arg,
|
|
||||||
scanCode := get_sfall_arg,
|
|
||||||
scanner, charges;
|
|
||||||
|
|
||||||
if scanCode == highlightKey and not(get_game_mode bwand (INTFACELOOT bwor BARTER)) then begin
|
|
||||||
if pressed then begin
|
|
||||||
isHighlight := true;
|
|
||||||
if motionScanner then begin
|
|
||||||
scanner := obj_carrying_pid_obj(dude_obj, PID_MOTION_SENSOR);
|
|
||||||
if scanner then begin
|
|
||||||
if motionScanner >= 2 then begin
|
|
||||||
charges := get_weapon_ammo_count(scanner);
|
|
||||||
if charges > 0 then begin
|
|
||||||
set_weapon_ammo_count(scanner, charges - 1);
|
|
||||||
intface_redraw;
|
|
||||||
call ToggleHighlight(true);
|
|
||||||
end else begin
|
|
||||||
display_msg(highlightFailMsg2);
|
|
||||||
end
|
|
||||||
end else begin
|
|
||||||
call ToggleHighlight(true);
|
|
||||||
end
|
|
||||||
end else begin
|
|
||||||
display_msg(highlightFailMsg1);
|
|
||||||
end
|
|
||||||
end else begin
|
|
||||||
call ToggleHighlight(true);
|
|
||||||
end
|
|
||||||
end else begin
|
|
||||||
isHighlight := false;
|
|
||||||
call ToggleHighlight(false);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
procedure CombatTurnHandler begin
|
|
||||||
if isHighlight then begin
|
|
||||||
isHighlight := false;
|
|
||||||
call ToggleHighlight(false);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
procedure GameModeChangeHandler begin
|
|
||||||
if isHighlight and (get_game_mode bwand (INTFACELOOT bwor BARTER)) then begin
|
|
||||||
isHighlight := false;
|
|
||||||
call ToggleHighlight(false);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
procedure start begin
|
|
||||||
if game_loaded and (sfall_ver_major >= 4) then begin
|
|
||||||
call InitConfigs;
|
|
||||||
|
|
||||||
highlightKey := GetConfig(configSection, "Key", 0);
|
|
||||||
alsoContainer := GetConfig(configSection, "Containers", 0);
|
|
||||||
alsoCorpse := GetConfig(configSection, "Corpses", 0);
|
|
||||||
checkLOS := GetConfig(configSection, "CheckLOS", 0);
|
|
||||||
outlineColor := GetConfig(configSection, "OutlineColor", 16);
|
|
||||||
if (outlineColor < 1) then outlineColor := 64;
|
|
||||||
motionScanner := GetConfig(configSection, "MotionScanner", 0);
|
|
||||||
|
|
||||||
highlightFailMsg1 := Translate("HighlightFail1", "You aren't carrying a motion sensor.");
|
|
||||||
highlightFailMsg2 := Translate("HighlightFail2", "Your motion sensor is out of charge.");
|
|
||||||
|
|
||||||
register_hook_proc(HOOK_KEYPRESS, KeyPressHandler);
|
|
||||||
register_hook_proc(HOOK_COMBATTURN, CombatTurnHandler);
|
|
||||||
register_hook_proc(HOOK_GAMEMODECHANGE, GameModeChangeHandler);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Binary file not shown.
@@ -1,193 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
NPC Armor Appearance mod
|
|
||||||
|
|
||||||
Used to replace the scripted part of B-Team mod.
|
|
||||||
Appropriate graphics are required for this mod to work.
|
|
||||||
Can be adopted to any mod by adjusting armor PIDs, allowed weapon anim codes, NPC PIDs and NPC FIDs in INI file.
|
|
||||||
|
|
||||||
NOTE: this script requires compiler from sfall modderspack with -s option
|
|
||||||
(short circuit evaluation)
|
|
||||||
|
|
||||||
version 1.1
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
#define IS_ARMOR(item) (obj_type(item) == OBJ_TYPE_ITEM and obj_item_subtype(item) == item_type_armor)
|
|
||||||
#define IS_WEAPON(item) (obj_type(item) == OBJ_TYPE_ITEM and obj_item_subtype(item) == item_type_weapon)
|
|
||||||
|
|
||||||
variable
|
|
||||||
modIni := "npcarmor.ini",
|
|
||||||
defaultFids,
|
|
||||||
armorPidMap, // maps armor PID to it's "type" - leather armor, metal, power armor, etc.
|
|
||||||
npcMap,
|
|
||||||
altWeapon,
|
|
||||||
unWieldWeapon;
|
|
||||||
|
|
||||||
procedure check_armor_change(variable critter, variable item, variable isWorn) begin
|
|
||||||
variable npc, armorType, fid;
|
|
||||||
if (npcMap[obj_pid(critter)]) then begin
|
|
||||||
npc := npcMap[obj_pid(critter)];
|
|
||||||
if (not isWorn) then begin
|
|
||||||
// display_msg("No armor fid: " + npc["Default"]);
|
|
||||||
return npc["Default"];
|
|
||||||
end else if (item) then begin
|
|
||||||
armorType := armorPidMap[obj_pid(item)];
|
|
||||||
fid := npc[armorType] or defaultFids[armorType];
|
|
||||||
if (fid == 0 or fid == -1) then fid := npc["Default"];
|
|
||||||
// display_msg("Change fid: " + fid + ", npc: " + npc[armorType] + ", default:" + defaultFids[armorType]);
|
|
||||||
return fid;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return -1;
|
|
||||||
end
|
|
||||||
|
|
||||||
procedure check_weapon_change(variable critter, variable item, variable isWield) begin
|
|
||||||
variable npc, newWeaponAnim, weaponAnimList, i;
|
|
||||||
if (npcMap[obj_pid(critter)]) then begin
|
|
||||||
npc := npcMap[obj_pid(critter)];
|
|
||||||
if isWield then begin
|
|
||||||
newWeaponAnim := get_proto_data(obj_pid(item), PROTO_WP_ANIM);
|
|
||||||
weaponAnimList := string_split(npc["WeaponAnims"], ",");
|
|
||||||
if newWeaponAnim then begin // anim code 0 - none/unarmed
|
|
||||||
foreach (i in weaponAnimList) begin
|
|
||||||
if (newWeaponAnim == atoi(i)) then return -1;
|
|
||||||
end
|
|
||||||
return 0;
|
|
||||||
end
|
|
||||||
end else begin
|
|
||||||
unWieldWeapon := obj_pid(item);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return -1;
|
|
||||||
end
|
|
||||||
|
|
||||||
procedure search_alt_weapon(variable critter) begin
|
|
||||||
variable obj, res, i := 0;
|
|
||||||
obj := inven_ptr(critter, 0);
|
|
||||||
while (obj) do begin
|
|
||||||
if (obj_item_subtype(obj) == item_type_weapon) then begin
|
|
||||||
if (unWieldWeapon == 0 or unWieldWeapon != obj_pid(obj)) then begin
|
|
||||||
res := check_weapon_change(critter, obj, 1);
|
|
||||||
if (res == -1) then begin
|
|
||||||
altWeapon := obj;
|
|
||||||
wield_obj_critter(critter, obj);
|
|
||||||
altWeapon := 0;
|
|
||||||
break;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
i++;
|
|
||||||
obj := inven_ptr(critter, i);
|
|
||||||
end
|
|
||||||
unWieldWeapon := 0;
|
|
||||||
end
|
|
||||||
|
|
||||||
// for NPCs when they change armor/weapon themselves
|
|
||||||
procedure invenwield_handler begin
|
|
||||||
variable critter, item, fid, slot, isWorn, canWield;
|
|
||||||
critter := get_sfall_arg;
|
|
||||||
item := get_sfall_arg;
|
|
||||||
slot := get_sfall_arg;
|
|
||||||
isWorn := get_sfall_arg;
|
|
||||||
|
|
||||||
if (critter and item and slot == INVEN_TYPE_WORN) then begin
|
|
||||||
fid := check_armor_change(critter, item, isWorn);
|
|
||||||
if (fid != -1) then begin
|
|
||||||
if art_exists(fid) then begin
|
|
||||||
art_change_fid_num(critter, fid);
|
|
||||||
end else begin
|
|
||||||
debug_msg("Error: NPC Armor mod: missing FID.");
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
if (critter and item and slot == INVEN_TYPE_RIGHT_HAND) then begin
|
|
||||||
if (altWeapon == item) then return;
|
|
||||||
canWield := check_weapon_change(critter, item, isWorn);
|
|
||||||
set_sfall_return(canWield);
|
|
||||||
if (canWield != -1) then begin
|
|
||||||
call search_alt_weapon(critter);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// when changing armor from inventory while controlling other NPCs
|
|
||||||
procedure adjustfid_handler begin
|
|
||||||
variable fid, armor;
|
|
||||||
if (dude_obj != real_dude_obj) then begin
|
|
||||||
armor := critter_inven_obj(dude_obj, INVEN_TYPE_WORN);
|
|
||||||
fid := check_armor_change(dude_obj, armor, armor != 0);
|
|
||||||
if (fid != -1) then begin
|
|
||||||
variable weapAnim := get_sfall_arg bwand 0xF000;
|
|
||||||
variable newFid := fid bwand 0xFFFF0FFF bwor weapAnim;
|
|
||||||
if art_exists(newFid) then begin
|
|
||||||
set_sfall_return(newFid);
|
|
||||||
end else begin
|
|
||||||
debug_msg("Error: NPC Armor mod: missing FID.");
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// when changing weapon from inventory while controlling other NPCs
|
|
||||||
procedure inventorymove_handler begin
|
|
||||||
variable slot, item, canWield;
|
|
||||||
slot := get_sfall_arg;
|
|
||||||
item := get_sfall_arg;
|
|
||||||
if (dude_obj != real_dude_obj) then begin
|
|
||||||
if (IS_WEAPON(item) and (slot == INVEN_TYPE_RIGHT_HAND or slot == INVEN_TYPE_LEFT_HAND)) then begin
|
|
||||||
canWield := check_weapon_change(dude_obj, item, item != 0);
|
|
||||||
set_sfall_return(canWield);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
procedure start begin
|
|
||||||
variable sect, sects, armorTypes, armorType, npc, pid, pids, i;
|
|
||||||
if game_loaded and (sfall_ver_major >= 4) then begin
|
|
||||||
register_hook_proc(HOOK_INVENWIELD, invenwield_handler);
|
|
||||||
register_hook_proc(HOOK_ADJUSTFID, adjustfid_handler);
|
|
||||||
register_hook_proc(HOOK_INVENTORYMOVE, inventorymove_handler);
|
|
||||||
|
|
||||||
defaultFids := get_ini_section(modIni, "Default");
|
|
||||||
fix_array(defaultFids);
|
|
||||||
foreach (armorType: i in defaultFids) begin
|
|
||||||
defaultFids[armorType] := atoi(i);
|
|
||||||
end
|
|
||||||
|
|
||||||
armorPidMap := create_array_map;
|
|
||||||
armorTypes := get_ini_section(modIni, "ArmorTypes");
|
|
||||||
foreach (armorType: pids in armorTypes) begin
|
|
||||||
pids := string_split(pids, ",");
|
|
||||||
foreach (pid in pids) begin
|
|
||||||
armorPidMap[atoi(pid)] := armorType;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
npcMap := create_array_map;
|
|
||||||
|
|
||||||
i := 1;
|
|
||||||
sect := get_ini_section(modIni, ""+i);
|
|
||||||
while (sect.PID) do begin
|
|
||||||
npc := create_array_map;
|
|
||||||
npc["WeaponAnims"] := sect["WeaponAnims"];
|
|
||||||
npc["Default"] := atoi(sect["Default"]);
|
|
||||||
foreach (armorType: pids in armorTypes) begin
|
|
||||||
if (sect[armorType]) then begin
|
|
||||||
npc[armorType] := atoi(sect[armorType]);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
npcMap[atoi(sect.PID)] := npc;
|
|
||||||
|
|
||||||
i += 1;
|
|
||||||
sect := get_ini_section(modIni, ""+i);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user