I have absolutely no idea which revision is this. Is a very stable version from 2011/2012 and it's TLA SDK. There is no drug_data.fos
// Author: cvet
// Original Fallout2 system
#include "_macros.fos"
#include "_msgstr.fos"
import void AffectRadiation( Critter& cr, int value ) from "radiation";
import void AffectPoison( Critter& cr, int value ) from "poison";
import void AffectHambre(Critter& cr, int value) from "Hambre";
// Chem Reliant
// benefit: Faster recovery from chem side effects
// penalty: Doubles chance for addiction
// Chem Resistant
// benefit: 50% less addiction chance
// penalty: 50% less duration for Chem effects
#define DRUG_EFFECT_DIV2_WITHDRAWAL ( 1 )
#define DRUG_EFFECT_MUL2_ADDICTION ( 2 )
#define DRUG_EFFECT_DIV2_ADDICTION ( 4 )
#define DRUG_EFFECT_DIV2_DURATION ( 8 )
#define RATE_TO_STAGE # (rate) ( ( rate ) & 0xFFFFFF )
#define RATE_TO_FLAGS # (rate) ( ( rate ) >> 24 )
#define FORM_RATE # ( stage, flags )( ( ( ( flags ) & 0xFF ) << 24 ) | ( ( stage ) & 0xFFFFFF ) )
#define FOOD_DRUG # (_pid) ((_pid) == PID_WATER_CLEAN)
const int[] DrugsIdentifiers =
{
PID_STIMPAK, PID_RADAWAY, PID_ANTIDOTE, PID_RAD_X, PID_SUPER_STIMPAK, PID_JET_ANTIDOTE, PID_HEALING_POWDER, PID_HYPO,
PID_NUKA_COLA, PID_BEER, PID_BOOZE, PID_GAMMA_GULP_BEER, PID_ROENTGEN_RUM, PID_ROT_GUT, PID_MENTATS, PID_BUFFOUT, PID_PSYCHO, PID_JET,
PID_MUTATED_FRUIT, PID_IGUANA_ON_A_STICK, PID_MEAT_ON_A_STICK, PID_COOKIE, PID_HYPO_POISON, PID_MUTATED_TOE, PID_KITTY_SEX_DRUG_AGILITY,
PID_KITTY_SEX_DRUG_INTELLIGENCE, PID_KITTY_SEX_DRUG_STRENGTH, PID_MONUMENT_CHUNCK, PID_BOX_OF_DOUGHNUTS, PID_WATER_PURE, PID_WATER_CLEAN,
PID_WATER_DIRTY, PID_WATER_RAD, PID_BRAHMIN_MEAT, PID_GECKO_MEAT, PID_BRAHMIN_STEAK, PID_GECKO_STEAK, PID_BEANS, PID_RAVIOLI, PID_CANNEDBEEF
};
const int[] DrugEffects =
{
// PID_STIMPAK
-1, 0, 0, 0, 0, 0,
-2, 10, 0, 0, 0, 0,
ST_CURRENT_HP, 40, 0, 0, 0, 0,
-1, 0, 0, 0, 0, 0,
-1, 0, 0, 0, 0, 0,
// PID_RADAWAY
ADDICTION_RADAWAY, 10, 120, 120, 14160, 10080,
ST_RADIATION_LEVEL, -25, -50, -75, 0, 0,
ST_RADIATION_RESISTANCE, 0, 0, 0, -20, 20,
-1, 0, 0, 0, 0, 0,
-1, 0, 0, 0, 0, 0,
};
// Table offsets
#define TABLE_DRUG_ADDICT ( 0 )
#define TABLE_DRUG_PROC ( 1 )
#define TABLE_DURATION # (stage) ( 2 + ( stage ) )
#define TABLE_STAT # (stat) ( 6 + ( stat ) * 6 )
#define TABLE_AMOUNT # ( stat, stage )( 7 + ( stage ) + ( stat ) * 6 )
void UseDrug(Critter& cr, Item& drug) // Export
{
uint pid = drug.GetProtoId();
SetDrug(cr, pid);
_SubItem(drug, 1);
if(pid == PID_STIMPAK || pid == PID_SUPER_STIMPAK)
{
cr.AddItem(PID_HYPODERMIC_NEEDLE, 1);
cr.Say(SAY_EMOTE_ON_HEAD,"injects something");
}
if(pid == PID_WATER_PURE || pid == PID_WATER_CLEAN || pid == PID_WATER_DIRTY || pid == PID_WATER_RAD)
{
cr.PlaySound( "gulping.ogg", true );
cr.AddItem(PID_EMPTY_WB, 1);
cr.Say(SAY_EMOTE_ON_HEAD,"drinks some water");
}
if(pid == PID_BRAHMIN_MEAT || pid == PID_GECKO_MEAT || pid == PID_BRAHMIN_STEAK || pid == PID_GECKO_STEAK || pid == PID_BEANS || pid == PID_RAVIOLI || pid == PID_CANNEDBEEF || pid == PID_MUTATED_FRUIT || pid == PID_IGUANA_ON_A_STICK || pid == PID_MEAT_ON_A_STICK || pid == PID_COOKIE || pid == PID_MEAT_JERKY)
{
cr.Say(SAY_EMOTE_ON_HEAD,"eats something");
}
if(pid == PID_NUKA_COLA)
{
cr.PlaySound( "gulping.ogg", true );
cr.AddItem(PID_BOTTLE_GLASS, 1);
cr.Say(SAY_EMOTE_ON_HEAD,"drinks NukaCola");
}
if(pid == PID_BEER || pid == PID_BOOZE || pid == PID_GAMMA_GULP_BEER || pid == PID_ROENTGEN_RUM || pid == PID_ROT_GUT)
{
cr.PlaySound( "gulping.ogg", true );
cr.AddItem(PID_BOTTLE_GLASS, 1);
cr.Say(SAY_EMOTE_ON_HEAD,"drinks something");
}
}
void UseDrugOn( Critter& cr, Critter& onCr, Item& drug ) // Export
{
if( onCr.IsDead() )
{
cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_NORESSURECT );
return;
}
bool isStimpak = ( drug.GetProtoId() == PID_STIMPAK || drug.GetProtoId() == PID_SUPER_STIMPAK );
if( isStimpak || onCr.IsKnockout() /* || Random(0,cr.Skill[SK_FIRST_AID])>onCr.Stat[ST_PERCEPTION]*20*/ )
{
onCr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_DRUG_USE_ON_SUCC );
SetDrug( onCr, drug.GetProtoId() );
}
else
{
onCr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_DRUG_USE_ON_FAIL );
}
_SubItem( drug, 1 );
if( not isStimpak )
cr.TimeoutBase[ TO_SK_FIRST_AID ] = FIRST_AID_TIMEOUT( cr );
}
void DropDrugEffects( Critter& cr ) // Export
{
// Clear effects of all active drugs
// Drop
// int[] identifiers;
// uint[] rates;
// uint count=cr.GetTimeEvents(DrugsIdentifiers,identifiers,null,null,rates);
// for(uint i=0;i<count;i++) DropDrug(cr,identifiers[i],rates[i]);
// Броня убирается в инвентарь, чтобы избежать эффекта снятия бонусов от armor perk
Item@[] items;
cr.GetItems( SLOT_ARMOR, items );
cr.GetItems( SLOT_HAND1, items );
cr.GetItems( SLOT_HAND2, items );
for( uint i = 0, l = items.length(); i < l; i++ )
_CritMoveItem( cr, items[ i ], SLOT_INV );
for( uint i = STAT_EXT_BEGIN; i <= STAT_EXT_END; i++ )
cr.StatBase[ i ] = 0;
// Erase all events
cr.EraseTimeEvents( DrugsIdentifiers );
// Unset addictions perks
for( uint i = ADDICTION_BEGIN; i <= ADDICTION_END; i++ )
cr.AddictionBase[ i ] = 0;
// Обратно возвращаем броню
for( uint i = 0, l = items.length(); i < l; i++ )
{
Item@ item = items[ i ];
if( valid( item ) && item.GetType() == ITEM_TYPE_ARMOR )
{
_CritMoveItem( cr, item, SLOT_ARMOR );
break; // может быть более 1 брони в руках
}
}
}
void SetDrug( Critter& cr, uint16 drugPid )
{
// Special drugs
if( drugPid == PID_BOX_OF_DOUGHNUTS )
{
GameVar@ counter = GetLocalVar( LVAR_doughnuts_counter, cr.Id );
if( valid( counter ) )
{
int max = counter.GetMax();
if( counter < max - 1 )
counter = counter.GetValue() + 1;
else if( counter.GetValue() == max - 1 )
{
cr.SayMsg( SAY_NETMSG, TEXTMSG_TEXT, 70061 ); // Вы съели столько пончиков, что почуствовали себя немного сильнее. Переносимый вес увеличен на +1 пункт.}
counter = max;
cr.StatBase[ ST_CARRY_WEIGHT ] += LBS_TO_GRAMM( 1 );
}
}
}
if( drugPid == PID_JET_ANTIDOTE )
{
uint[] rates;
uint count = cr.GetTimeEvents( PID_JET, null, null, rates );
for( uint i = 0; i < count; i++ )
DropDrug( cr, PID_JET, RATE_TO_STAGE( rates[ i ] ) );
cr.EraseTimeEvents( PID_JET );
if( count > 0 && cr.Addiction[ ADDICTION_JET ] != 0 )
{
cr.AddictionBase[ ADDICTION_JET ] = 0;
cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_DRUG_ADDICTION_END );
}
else
cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_DRUG_NOTHING_HAPPENS );
return;
}
// Chem flags
uint flags = 0;
if( cr.Trait[ TRAIT_CHEM_RELIANT ] != 0 )
flags |= DRUG_EFFECT_DIV2_WITHDRAWAL | DRUG_EFFECT_MUL2_ADDICTION;
if( cr.Trait[ TRAIT_CHEM_RESISTANT ] != 0 )
flags |= DRUG_EFFECT_DIV2_ADDICTION | DRUG_EFFECT_DIV2_DURATION;
// Other drugs
uint[] indexes;
uint[] rates;
uint count = cr.GetTimeEvents( drugPid, indexes, null, rates );
int index = GetDrugTableIndex( drugPid );
// Check
if( DrugEffects[ index + TABLE_DRUG_ADDICT ] >= 0 ) // Addiction perk aviability
{
uint positive = 0;
uint negative = 0;
for( uint i = 0; i < count; i++ )
{
uint stage = RATE_TO_STAGE( rates[ i ] );
if( stage <= 1 )
positive++;
else if( stage == 2 )
negative++;
}
if( positive >= 2 - negative / 2 )
{
int addict = DrugEffects[ index + TABLE_DRUG_ADDICT ];
int addictProc = DrugEffects[ index + TABLE_DRUG_PROC ];
if( FLAG( flags, DRUG_EFFECT_MUL2_ADDICTION ) )
addictProc *= 2;
if( FLAG( flags, DRUG_EFFECT_DIV2_ADDICTION ) )
addictProc /= 2;
if( cr.Addiction[ addict ] == 0 && Random( 1, 100 ) <= addictProc )
cr.AddictionBase[ addict ] = 1;
else
cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_DRUG_NOTHING_HAPPENS );
return;
}
}
// Clear active addictions
uint deleted = 0; // After erase indexes decrement on one position
for( uint i = 0; i < count; i++ )
{
uint stage = RATE_TO_STAGE( rates[ i ] );
if( stage >= 3 )
{
DropDrug( cr, drugPid, stage );
cr.EraseTimeEvent( indexes[ i ] - deleted );
deleted++;
}
}
// Set critter time event
cr.AddTimeEvent( "cte_Drug", 0, drugPid, FORM_RATE( 0, flags ) );
}
int GetDrugTableIndex( uint16 drugPid )
{
int index = 0;
switch( drugPid )
{
case PID_STIMPAK:
index = 0;
break;
case PID_RADAWAY:
index = 1;
break;
case PID_ANTIDOTE:
index = 2;
break;
case PID_RAD_X:
index = 3;
break;
case PID_SUPER_STIMPAK:
index = 4;
break;
case PID_JET_ANTIDOTE:
index = 5;
break;
case PID_HEALING_POWDER:
index = 6;
break;
case PID_HYPO:
index = 7;
break;
case PID_NUKA_COLA:
index = 8;
break;
case PID_BEER:
index = 9;
break;
case PID_BOOZE:
index = 10;
break;
case PID_GAMMA_GULP_BEER:
index = 11;
break;
case PID_ROENTGEN_RUM:
index = 12;
break;
case PID_ROT_GUT:
index = 13;
break;
case PID_MENTATS:
index = 14;
break;
case PID_BUFFOUT:
index = 15;
break;
case PID_PSYCHO:
index = 16;
break;
case PID_JET:
index = 17;
break;
case PID_MUTATED_FRUIT:
index = 18;
break;
case PID_IGUANA_ON_A_STICK:
index = 19;
break;
case PID_MEAT_ON_A_STICK:
index = 20;
break;
case PID_COOKIE:
index = 21;
break;
case PID_HYPO_POISON:
index = 22;
break;
case PID_MUTATED_TOE:
index = 23;
break;
case PID_KITTY_SEX_DRUG_AGILITY:
index = 24;
break;
case PID_KITTY_SEX_DRUG_INTELLIGENCE:
index = 25;
break;
case PID_KITTY_SEX_DRUG_STRENGTH:
index = 26;
break;
case PID_MONUMENT_CHUNCK:
index = 27;
break;
case PID_BOX_OF_DOUGHNUTS:
index = 28;
break;
case PID_WATER_PURE:
index = 29;
break;
case PID_WATER_CLEAN:
index = 30;
break;
case PID_WATER_DIRTY:
index = 31;
break;
case PID_WATER_RAD:
index = 32;
break;
case PID_BRAHMIN_MEAT:
index = 33;
break;
case PID_GECKO_MEAT:
index = 34;
break;
case PID_BRAHMIN_STEAK:
index = 35;
break;
case PID_GECKO_STEAK:
index = 36;
break;
case PID_BEANS:
index = 37;
break;
case PID_RAVIOLI:
index = 38;
break;
case PID_CANNEDBEEF:
index = 39;
break;
default:
break;
}
index *= 39;
return index;
}
uint ProcessDrug( Critter& cr, uint16 drugPid, uint& rate )
{
uint stage = RATE_TO_STAGE( rate );
uint flags = RATE_TO_FLAGS( rate );
if( cr.IsDead() )
return REAL_MINUTE( 5 ); // Stop drug processing
if( drugPid == PID_JET && stage >= 4 )
return REAL_HOUR( 5 ); // Only after Jet Antidote was used
int index = GetDrugTableIndex( drugPid );
uint duration = 0;
if( stage == 0 || // Instant effect
stage == 1 || // Withdrawal
stage == 2 || // Normalize
stage == 3 || // Addiction begin
stage == 4 ) // Addiction end
{
for( uint i = 0; i < 4; i++ )
{
int stat = DrugEffects[ index + TABLE_STAT( i ) ];
int amount = DrugEffects[ index + TABLE_AMOUNT( i, stage ) ];
if( i == 1 && DrugEffects[ index + TABLE_STAT( 0 ) ] == -2 )
amount = Random( DrugEffects[ index + TABLE_AMOUNT( 0, stage ) ], amount ); // Take first
if( stat < 0 )
continue;
int statVal = cr.Stat[ stat ];
if( stat < STAT_EXT_BEGIN )
cr.StatBase[ STAT_EXT_BEGIN + stat ] += amount;
else if( stat == ST_CURRENT_HP )
{
cr.StatBase[ ST_CURRENT_HP ] = CLAMP( statVal + amount, -9999, cr.Stat[ ST_MAX_LIFE ] );
if( amount < 0 && cr.Stat[ ST_CURRENT_HP ] < 0 )
cr.ToDead( Random( 0, 1 ) == 0 ? ANIM2_DEAD_FRONT : ANIM2_DEAD_BACK, null );
}
else if( stat == ST_POISONING_LEVEL )
AffectPoison( cr, amount ); // cr.SetStat(ST_POISONING_LEVEL,CLAMP(statVal+amount,0,2000));
else if( stat == ST_RADIATION_LEVEL )
AffectRadiation( cr, amount ); // cr.SetStat(ST_RADIATION_LEVEL,CLAMP(statVal+amount,0,2000));
else if( stat == ST_HAMBRE_LEVEL )
AffectHambre( cr, amount ); // cr.SetStat(ST_RADIATION_LEVEL,CLAMP(statVal+amount,0,2000));
else
continue;
statVal = cr.Stat[ stat ] - statVal;
if( statVal > 0 )
cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_DRUG_STAT_GAIN, "$name @msg game " + STR_PARAM_NAME( stat ) + "@$value" + statVal );
else if( statVal < 0 )
cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_DRUG_STAT_LOSE, "$name @msg game " + STR_PARAM_NAME( stat ) + "@$value" + ( -statVal ) );
}
int addict = DrugEffects[ index + TABLE_DRUG_ADDICT ];
if( stage == 0 ) // To withdrawal
{
duration = DrugEffects[ index + TABLE_DURATION( stage ) ];
if( FLAG( flags, DRUG_EFFECT_DIV2_DURATION ) )
duration /= 2;
// Try set addiction perk
int addictProc = DrugEffects[ index + TABLE_DRUG_PROC ];
if( FLAG( flags, DRUG_EFFECT_MUL2_ADDICTION ) )
addictProc *= 2;
if( FLAG( flags, DRUG_EFFECT_DIV2_ADDICTION ) )
addictProc /= 2;
if( stage == 0 && addict >= 0 && Random( 1, 100 ) <= addictProc )
cr.AddictionBase[ addict ] = 1;
}
else if( stage == 1 ) // To normalize
{
duration = DrugEffects[ index + TABLE_DURATION( stage ) ];
if( FLAG( flags, DRUG_EFFECT_DIV2_DURATION ) )
duration /= 2;
}
else if( stage == 2 && addict >= 0 && cr.Addiction[ addict ] != 0 ) // To addiction
{
// Find already processed addiction
uint[] rates;
uint count = cr.GetTimeEvents( drugPid, null, null, rates );
bool isPresent = false;
for( uint i = 0; i < count; i++ )
{
if( RATE_TO_STAGE( rates[ i ] ) >= 3 )
{
isPresent = true;
break;
}
}
if( not isPresent )
duration = DrugEffects[ index + TABLE_DURATION( stage ) ];
}
else if( stage == 3 ) // To end of addiction
{
duration = DrugEffects[ index + TABLE_DURATION( stage ) ];
if( FLAG( flags, DRUG_EFFECT_DIV2_WITHDRAWAL ) )
duration /= 2;
}
else if( stage == 4 ) // End of addiction
{
cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_DRUG_ADDICTION_END );
if( addict >= 0 && cr.Addiction[ addict ] != 0 )
cr.AddictionBase[ addict ] = 0;
}
}
stage++;
rate = FORM_RATE( stage, flags );
return duration * 60;
}
void DropDrug( Critter& cr, uint16 drugPid, uint stage )
{
if( stage == 0 || stage == 3 )
return; // Instant effect not happens or already normalize or wait Addidional effect
int index = GetDrugTableIndex( drugPid );
if( stage == 1 || // Instant effect already
stage == 2 || // Withdrawal already
stage == 4 ) // Addiction already
{
for( uint i = 0; i < 4; i++ )
{
int stat = DrugEffects[ index + TABLE_STAT( i ) ];
int amount = DrugEffects[ index + TABLE_AMOUNT( i, stage + ( stage == 1 ? -1 : 0 ) ) ]; // Turn
if( stage == 1 )
amount = -amount;
if( stat < STAT_EXT_BEGIN )
cr.StatBase[ STAT_EXT_BEGIN + stat ] += amount;
}
}
}
uint cte_Drug( Critter& cr, int identifier, uint& rate )
{
return ProcessDrug( cr, identifier, rate );
}