Add ai_check_drugs fixes and improvements (#29)
This commit is contained in:
parent
4b137dac5f
commit
0e11569397
128
src/combat_ai.cc
128
src/combat_ai.cc
|
@ -41,6 +41,16 @@ namespace fallout {
|
||||||
|
|
||||||
#define AI_MESSAGE_SIZE 260
|
#define AI_MESSAGE_SIZE 260
|
||||||
|
|
||||||
|
static constexpr int kChemUseStimsWhenHurtLittleHpRatio = 60;
|
||||||
|
static constexpr int kChemUseStimsWhenHurtLotsHpRatio = 30;
|
||||||
|
static constexpr int kChemUseStimsHpRatio = 50;
|
||||||
|
|
||||||
|
static constexpr int kChemUseSometimesChance = 25;
|
||||||
|
static constexpr int kChemUseAnytimeChance = 75;
|
||||||
|
static constexpr int kChemUseAlwaysChance = 100;
|
||||||
|
|
||||||
|
static constexpr int kRandomDrugPickingArraySize = 3;
|
||||||
|
|
||||||
typedef struct AiMessageRange {
|
typedef struct AiMessageRange {
|
||||||
int start;
|
int start;
|
||||||
int end;
|
int end;
|
||||||
|
@ -934,31 +944,29 @@ static int _ai_check_drugs(Object* critter)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hpRatio = 50;
|
int hpRatio = kChemUseStimsHpRatio;
|
||||||
int chemUseChance = 0;
|
int chemUseChance = 0;
|
||||||
switch (ai->chem_use) {
|
switch (ai->chem_use) {
|
||||||
case CHEM_USE_CLEAN:
|
case CHEM_USE_CLEAN:
|
||||||
return 0;
|
return 0;
|
||||||
case CHEM_USE_STIMS_WHEN_HURT_LITTLE:
|
case CHEM_USE_STIMS_WHEN_HURT_LITTLE:
|
||||||
hpRatio = 60;
|
hpRatio = kChemUseStimsWhenHurtLittleHpRatio;
|
||||||
break;
|
break;
|
||||||
case CHEM_USE_STIMS_WHEN_HURT_LOTS:
|
case CHEM_USE_STIMS_WHEN_HURT_LOTS:
|
||||||
hpRatio = 30;
|
hpRatio = kChemUseStimsWhenHurtLotsHpRatio;
|
||||||
break;
|
break;
|
||||||
case CHEM_USE_SOMETIMES:
|
case CHEM_USE_SOMETIMES:
|
||||||
if ((_combatNumTurns % 3) == 0) {
|
if ((_combatNumTurns % 3) == 0) {
|
||||||
chemUseChance = 25;
|
chemUseChance = kChemUseSometimesChance;
|
||||||
}
|
}
|
||||||
hpRatio = 50;
|
|
||||||
break;
|
break;
|
||||||
case CHEM_USE_ANYTIME:
|
case CHEM_USE_ANYTIME:
|
||||||
if ((_combatNumTurns % 3) == 0) {
|
if ((_combatNumTurns % 3) == 0) {
|
||||||
chemUseChance = 75;
|
chemUseChance = kChemUseAnytimeChance;
|
||||||
}
|
}
|
||||||
hpRatio = 50;
|
|
||||||
break;
|
break;
|
||||||
case CHEM_USE_ALWAYS:
|
case CHEM_USE_ALWAYS:
|
||||||
chemUseChance = 100;
|
chemUseChance = kChemUseAlwaysChance;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -996,24 +1004,80 @@ static int _ai_check_drugs(Object* critter)
|
||||||
|
|
||||||
if (!drugUsed) {
|
if (!drugUsed) {
|
||||||
if (chemUseChance > 0 && randomBetween(0, 100) < chemUseChance) {
|
if (chemUseChance > 0 && randomBetween(0, 100) < chemUseChance) {
|
||||||
while (critter->data.critter.combat.ap >= 2) {
|
// CE: Slightly improve and randomize drug picking.
|
||||||
|
inventoryItemIndex = -1;
|
||||||
|
searchCompleted = false;
|
||||||
|
|
||||||
|
Object* primaryDrugs[kRandomDrugPickingArraySize];
|
||||||
|
int primaryDrugsCount = 0;
|
||||||
|
|
||||||
|
Object* secondaryDrugs[kRandomDrugPickingArraySize];
|
||||||
|
int secondaryDrugsCount = 0;
|
||||||
|
|
||||||
|
// Collect drugs into buckets - primary desires and everything
|
||||||
|
// else.
|
||||||
|
//
|
||||||
|
// Strictly speaking NPCs can have more drugs than buckets can
|
||||||
|
// store. Together with `CHEM_USE_ALWAYS` it can lead to
|
||||||
|
// unexpected behaviour. In vanilla NPCs will eat drugs on their
|
||||||
|
// turns while they have action points. With this implementation
|
||||||
|
// NPCs will eat at most 2x `kRandomDrugPickingArraySize` drugs
|
||||||
|
// every turn (exhausting both primary and secondary buckets)
|
||||||
|
// and then continue to other activities (assuming they have
|
||||||
|
// action points to do so). In practice this sitation is very
|
||||||
|
// rare if even exists in vanilla or popular mods.
|
||||||
|
//
|
||||||
|
// The alternative is to use a pair of `std::vector`s and let
|
||||||
|
// them manage the memory.
|
||||||
|
while (true) {
|
||||||
Object* drug = _inven_find_type(critter, ITEM_TYPE_DRUG, &inventoryItemIndex);
|
Object* drug = _inven_find_type(critter, ITEM_TYPE_DRUG, &inventoryItemIndex);
|
||||||
if (drug == NULL) {
|
if (drug == NULL) {
|
||||||
searchCompleted = true;
|
searchCompleted = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int drugPid = drug->pid;
|
if (!itemIsHealing(drug->pid)) {
|
||||||
int index;
|
bool isPrimary = false;
|
||||||
for (index = 0; index < AI_PACKET_CHEM_PRIMARY_DESIRE_COUNT; index++) {
|
for (int index = 0; index < AI_PACKET_CHEM_PRIMARY_DESIRE_COUNT; index++) {
|
||||||
// TODO: Find out why it checks for inequality at 0x4286B1.
|
if (ai->chem_primary_desire[index] == drug->pid) {
|
||||||
if (ai->chem_primary_desire[index] != drugPid) {
|
isPrimary = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < AI_PACKET_CHEM_PRIMARY_DESIRE_COUNT) {
|
if (isPrimary) {
|
||||||
if (!itemIsHealing(drugPid)) {
|
if (primaryDrugsCount < kRandomDrugPickingArraySize) {
|
||||||
|
primaryDrugs[primaryDrugsCount++] = drug;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (secondaryDrugsCount < kRandomDrugPickingArraySize) {
|
||||||
|
secondaryDrugs[secondaryDrugsCount++] = drug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume drugs one-by-one in random order with primary desires
|
||||||
|
// first.
|
||||||
|
while (critter->data.critter.combat.ap >= 2) {
|
||||||
|
Object** availableDrugs;
|
||||||
|
int* availableDrugsCountPtr;
|
||||||
|
|
||||||
|
if (primaryDrugsCount > 0) {
|
||||||
|
availableDrugs = primaryDrugs;
|
||||||
|
availableDrugsCountPtr = &primaryDrugsCount;
|
||||||
|
} else if (secondaryDrugsCount > 0) {
|
||||||
|
availableDrugs = secondaryDrugs;
|
||||||
|
availableDrugsCountPtr = &secondaryDrugsCount;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = randomBetween(0, *availableDrugsCountPtr - 1);
|
||||||
|
Object* drug = availableDrugs[index];
|
||||||
|
availableDrugs[index] = availableDrugs[*availableDrugsCountPtr - 1];
|
||||||
|
*availableDrugsCountPtr -= 1;
|
||||||
|
|
||||||
if (itemRemove(critter, drug, 1) == 0) {
|
if (itemRemove(critter, drug, 1) == 0) {
|
||||||
if (_item_d_take_drug(critter, drug) == -1) {
|
if (_item_d_take_drug(critter, drug) == -1) {
|
||||||
itemAdd(critter, drug, 1);
|
itemAdd(critter, drug, 1);
|
||||||
|
@ -1039,8 +1103,6 @@ static int _ai_check_drugs(Object* critter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastItem != NULL || (!drugUsed && searchCompleted)) {
|
if (lastItem != NULL || (!drugUsed && searchCompleted)) {
|
||||||
do {
|
do {
|
||||||
|
@ -1991,10 +2053,32 @@ static bool aiCanUseItem(Object* critter, Object* item)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int itemPid = item->pid;
|
// SFALL: Check healing items.
|
||||||
if (itemPid != PROTO_ID_STIMPACK
|
if (!itemIsHealing(item->pid)) {
|
||||||
&& itemPid != PROTO_ID_SUPER_STIMPACK
|
return false;
|
||||||
&& itemPid != PROTO_ID_HEALING_POWDER) {
|
}
|
||||||
|
|
||||||
|
// CE: Make sure critter actually need healing item.
|
||||||
|
//
|
||||||
|
// Sfall has similar fix implemented differently in `ai_check_drugs`. It
|
||||||
|
// does so after healing item is returned from `ai_search_environ`, so it
|
||||||
|
// does not a have a chance to look for other items.
|
||||||
|
int hpRatio = kChemUseStimsHpRatio;
|
||||||
|
switch (aiGetChemUse(critter)) {
|
||||||
|
case CHEM_USE_CLEAN:
|
||||||
|
hpRatio = 0;
|
||||||
|
break;
|
||||||
|
case CHEM_USE_STIMS_WHEN_HURT_LITTLE:
|
||||||
|
hpRatio = kChemUseStimsWhenHurtLittleHpRatio;
|
||||||
|
break;
|
||||||
|
case CHEM_USE_STIMS_WHEN_HURT_LOTS:
|
||||||
|
hpRatio = kChemUseStimsWhenHurtLotsHpRatio;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentHp = critterGetStat(critter, STAT_CURRENT_HIT_POINTS);
|
||||||
|
int stimsHp = critterGetStat(critter, STAT_MAXIMUM_HIT_POINTS) * hpRatio / 100;
|
||||||
|
if (currentHp > stimsHp) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue