Skip to content

Commit

Permalink
Instances for Postal configurable (#3504)
Browse files Browse the repository at this point in the history
- No longer named 'QUICK' instances, now looking at 'postal priority'.
- Modders can have their new instances be used when going postal.
- The priority value is used to determine which instance to use when there is multiple available.
- You can set melee attacks to go postal with.

---------

Co-authored-by: Loobinex <[email protected]>
  • Loading branch information
Shinthoras0815 and Loobinex authored Nov 10, 2024
1 parent 612de79 commit d0e95d0
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 57 deletions.
33 changes: 23 additions & 10 deletions config/fxdata/creature.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ RangeMax = 0
; 3,4 will only target other players, 5,6 only own player.
; 7 will target traps.
PrimaryTarget = 0
; Instance used by creature "going postal" while working in a room
; Creature will only use available instances with the highest priority,
; If multiple instances have the same PostalPriority, one is chosen randomly
; 0 disables the instance for beeing used while "going postal".
PostalPriority = 0
; Instance properties flags:
; REPEAT_TRIGGER allows player to hold down the mouse button to cast.
; DISPLAY_SWIPE shows the swipe in possession loaded from the creatures 'PossessSwipeIndex'.
Expand All @@ -81,7 +86,6 @@ PrimaryTarget = 0
; SELF_BUFF can be applied to caster.
; RANGED_BUFF can be applied to another friendly creature.
; NEEDS_TARGET Cannot be used in possession without a target to cast it on.
; QUICK allows the instance to be used by creatures going postal.
; DISARMING allows the instance to be used against traps.
Properties =
; Function used as the instance action, and its parameters.
Expand Down Expand Up @@ -181,7 +185,8 @@ Graphics = RANGEDATTACK
RangeMin = 156
RangeMax = MAX
PrimaryTarget = 3
Properties = DESTRUCTIVE QUICK RANGED_ATTACK
PostalPriority = 16
Properties = DESTRUCTIVE RANGED_ATTACK
Function = creature_fire_shot SHOT_ARROW 0

[instance5]
Expand All @@ -200,7 +205,8 @@ Graphics = RANGEDATTACK
RangeMin = 156
RangeMax = MAX
PrimaryTarget = 3
Properties = DESTRUCTIVE QUICK RANGED_ATTACK
PostalPriority = 18
Properties = DESTRUCTIVE RANGED_ATTACK
Function = creature_fire_shot SHOT_FIREBALL 0

[instance6]
Expand Down Expand Up @@ -276,7 +282,8 @@ Graphics = RANGEDATTACK
RangeMin = 768
RangeMax = 5120
PrimaryTarget = 3
Properties = DESTRUCTIVE QUICK RANGED_ATTACK
PostalPriority = 10
Properties = DESTRUCTIVE RANGED_ATTACK
Function = creature_fire_shot SHOT_LIGHTNING 0

[instance10]
Expand Down Expand Up @@ -333,7 +340,8 @@ Graphics = RANGEDATTACK
RangeMin = 156
RangeMax = MAX
PrimaryTarget = 4
Properties = QUICK RANGED_ATTACK
PostalPriority = 2
Properties = RANGED_ATTACK
Function = creature_cast_spell SPELL_POISON_CLOUD 0

[instance13]
Expand Down Expand Up @@ -464,7 +472,8 @@ Graphics = RANGEDATTACK
RangeMin = 156
RangeMax = MAX
PrimaryTarget = 3
Properties = DESTRUCTIVE QUICK RANGED_ATTACK
PostalPriority = 14
Properties = DESTRUCTIVE RANGED_ATTACK
Function = creature_fire_shot SHOT_MISSILE 0

[instance20]
Expand All @@ -483,7 +492,8 @@ Graphics = RANGEDATTACK
RangeMin = 156
RangeMax = MAX
PrimaryTarget = 3
Properties = DESTRUCTIVE QUICK RANGED_ATTACK
PostalPriority = 0
Properties = DESTRUCTIVE RANGED_ATTACK
Function = creature_fire_shot SHOT_NAVI_MISSILE 0

[instance21]
Expand Down Expand Up @@ -594,7 +604,8 @@ TooltipTextID = 234
SymbolSprites = 440
Graphics = RANGEDATTACK
PrimaryTarget = 3
Properties = DANGEROUS DESTRUCTIVE QUICK RANGED_ATTACK
PostalPriority = 4
Properties = DANGEROUS DESTRUCTIVE RANGED_ATTACK
Function = creature_fire_shot SHOT_GRENADE 0

[instance27]
Expand All @@ -613,7 +624,8 @@ Graphics = RANGEDATTACK
RangeMin = 156
RangeMax = MAX
PrimaryTarget = 3
Properties = DESTRUCTIVE QUICK RANGED_ATTACK MELEE_ATTACK
PostalPriority = 8
Properties = DESTRUCTIVE RANGED_ATTACK MELEE_ATTACK
Function = creature_cast_spell SPELL_HAILSTORM 0

[instance28]
Expand Down Expand Up @@ -831,7 +843,8 @@ Graphics = RANGEDATTACK
RangeMin = 1000
RangeMax = MAX
PrimaryTarget = 3
Properties = DANGEROUS DESTRUCTIVE QUICK RANGED_ATTACK
PostalPriority = 6
Properties = DANGEROUS DESTRUCTIVE RANGED_ATTACK
Function = creature_fire_shot SHOT_LIZARD 0

[instance41]
Expand Down
17 changes: 16 additions & 1 deletion src/config_creature.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const struct NamedCommand creaturetype_instance_commands[] = {
{"ValidateSourceFunc", 18},
{"ValidateTargetFunc", 19},
{"SearchTargetsFunc", 20},
{"PostalPriority", 21},
{NULL, 0},
};

Expand All @@ -108,7 +109,6 @@ const struct NamedCommand creaturetype_instance_properties[] = {
{"SELF_BUFF", InstPF_SelfBuff},
{"DANGEROUS", InstPF_Dangerous},
{"DESTRUCTIVE", InstPF_Destructive},
{"QUICK", InstPF_Quick},
{"DISARMING", InstPF_Disarming},
{"DISPLAY_SWIPE", InstPF_UsesSwipe},
{"RANGED_BUFF", InstPF_RangedBuff},
Expand Down Expand Up @@ -841,6 +841,7 @@ TbBool parse_creaturetype_instance_blocks(char *buf, long len, const char *confi
inst_inf->validate_target_func = 0;
inst_inf->validate_target_func_params[0] = 0;
inst_inf->validate_target_func_params[1] = 0;
inst_inf->postal_priority = 0;
}
}
instance_desc[INSTANCE_TYPES_MAX - 1].name = NULL; // must be null for get_id
Expand Down Expand Up @@ -1212,6 +1213,19 @@ TbBool parse_creaturetype_instance_blocks(char *buf, long len, const char *confi
}
}
break;
case 21: // Postal Instance priority
if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0)
{
k = atoi(word_buf);
inst_inf->postal_priority = k;
n++;
}
if (n < 1)
{
CONFWRNLOG("Couldn't read \"%s\" parameter in [%.*s] block of %s file.",
COMMAND_TEXT(cmd_num), blocknamelen, blockname, config_textname);
}
break;
case ccr_comment:
break;
case ccr_endOfFile:
Expand Down Expand Up @@ -1693,6 +1707,7 @@ TbBool load_creaturetypes_config_file(const char *textname, const char *fname, u
game.conf.magic_conf.instance_info[i].reset_time = 0;
game.conf.magic_conf.instance_info[i].fp_reset_time = 0;
game.conf.magic_conf.instance_info[i].graphics_idx = 0;
game.conf.magic_conf.instance_info[i].postal_priority = 0;
game.conf.magic_conf.instance_info[i].instance_property_flags = 0;
game.conf.magic_conf.instance_info[i].force_visibility = 0;
game.conf.magic_conf.instance_info[i].primary_target = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/config_creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ enum InstancePropertiesFlags {
InstPF_RangedDebuff = 0x0010,
InstPF_Dangerous = 0x0020,
InstPF_Destructive = 0x0040,
InstPF_Quick = 0x0080,
InstPF_Unused = 0x0080, //Quick
InstPF_Disarming = 0x0100,
InstPF_UsesSwipe = 0x0200,
InstPF_RangedBuff = 0x0400,
Expand Down
13 changes: 9 additions & 4 deletions src/creature_instances.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,15 @@ TbBool instance_is_ranged_weapon_vs_objects(CrInstance inum)
return (((inst_inf->instance_property_flags & InstPF_RangedAttack) != 0) && ((inst_inf->instance_property_flags & InstPF_Destructive) != 0) && !(inst_inf->instance_property_flags & InstPF_Dangerous));
}

TbBool instance_is_quick_range_weapon(CrInstance inum)
/**
* Informs whether the creature has an instance which can be used when going postal.
* Going Postal is the behavior where creatures attack others at their job, like warlocks in the library
* @return True if it has a postal_priority value > 0.
*/
TbBool instance_is_used_for_going_postal(CrInstance inum)
{
struct InstanceInfo* inst_inf = creature_instance_info_get(inum);
return (((inst_inf->instance_property_flags & InstPF_RangedAttack) != 0) && ((inst_inf->instance_property_flags & InstPF_Quick) != 0));
return (inst_inf->postal_priority > 0);
}

TbBool instance_is_melee_attack(CrInstance inum)
Expand Down Expand Up @@ -402,15 +407,15 @@ TbBool creature_has_ranged_object_weapon(const struct Thing *creatng)
return false;
}

TbBool creature_has_quick_range_weapon(const struct Thing *creatng)
TbBool creature_has_weapon_for_postal(const struct Thing *creatng)
{
TRACE_THING(creatng);
const struct CreatureControl* cctrl = creature_control_get_from_thing(creatng);
for (long inum = 1; inum < game.conf.crtr_conf.instances_count; inum++)
{
if (cctrl->instance_available[inum])
{
if (instance_is_quick_range_weapon(inum))
if (instance_is_used_for_going_postal(inum))
return true;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/creature_instances.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ struct InstanceInfo {
long reset_time;
long fp_reset_time;
unsigned char graphics_idx;
char postal_priority;
short instance_property_flags;
short force_visibility;
unsigned char primary_target;
Expand Down Expand Up @@ -150,7 +151,7 @@ void creature_increase_available_instances(struct Thing *thing);
TbBool creature_has_ranged_weapon(const struct Thing *thing);
TbBool creature_has_disarming_weapon(const struct Thing* creatng);
TbBool creature_has_ranged_object_weapon(const struct Thing *creatng);
TbBool creature_has_quick_range_weapon(const struct Thing *creatng);
TbBool creature_has_weapon_for_postal(const struct Thing *creatng);
TbBool creature_has_melee_attack(const struct Thing *creatng);

int creature_instance_get_available_pos_for_id(struct Thing *thing, CrInstance req_inst_id);
Expand Down
131 changes: 100 additions & 31 deletions src/creature_states_combt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1781,16 +1781,6 @@ long ranged_combat_move(struct Thing *thing, struct Thing *enmtng, MapCoordDelta
return thing_in_field_of_view(thing, enmtng);
}

#define INSTANCE_RET_IF_AVAIL(thing, inst_id) \
if (creature_instance_is_available(thing, inst_id) \
&& creature_instance_has_reset(thing, inst_id)) { \
return inst_id; \
}
#define INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, inst_id) \
if (creature_instance_is_available(thing, inst_id)) { \
return -inst_id; \
}

TbBool creature_would_benefit_from_healing(const struct Thing* thing)
{
struct CreatureControl* cctrl = creature_control_get_from_thing(thing);
Expand Down Expand Up @@ -1852,29 +1842,108 @@ CrInstance get_self_spell_casting(const struct Thing *thing)
return CrInst_NULL;
}

CrInstance get_best_quick_range_instance_to_use(const struct Thing *thing)
{
INSTANCE_RET_IF_AVAIL(thing, CrInst_FIREBALL);
INSTANCE_RET_IF_AVAIL(thing, CrInst_FIRE_ARROW);
INSTANCE_RET_IF_AVAIL(thing, CrInst_MISSILE);
INSTANCE_RET_IF_AVAIL(thing, CrInst_NAVIGATING_MISSILE);
INSTANCE_RET_IF_AVAIL(thing, CrInst_LIGHTNING);
INSTANCE_RET_IF_AVAIL(thing, CrInst_HAILSTORM);
INSTANCE_RET_IF_AVAIL(thing, CrInst_GRENADE);
INSTANCE_RET_IF_AVAIL(thing, CrInst_POISON_CLOUD);
INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, CrInst_FIREBALL);
INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, CrInst_FIRE_ARROW);
INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, CrInst_MISSILE);
INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, CrInst_NAVIGATING_MISSILE);
INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, CrInst_LIGHTNING);
INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, CrInst_HAILSTORM);
INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, CrInst_GRENADE);
INSTANCE_RET_NEG_IF_AVAIL_ONLY(thing, CrInst_POISON_CLOUD);
return CrInst_NULL;
// Static array to store the IDs of "postal" instances
static CrInstance postal_inststance[INSTANCE_TYPES_MAX];
// Counter for the number of "postal" instances found
static short postal_inst_num = 0;
// Flag to indicate if the cache has been initialized
static TbBool initial = false;

/** @brief Retrieves a random available "postal" instance within range for a given creature.
*
* On the first call, the function creates a cache of all available "postal" instances.
* It then loops through the cache to find instances available for the creature and fitting within the given range.
* These available instances are added to a list.
* The function then chooses a random instance from this list.
*
* @param thing Pointer to the creature for which the instance is to be retrieved.
* @param dist Distance to the target.
* @return A random available "postal" CrInstance for the given range
*/
CrInstance get_postal_instance_to_use(const struct Thing *thing, unsigned long dist)
{
struct InstanceInfo* inst_inf;

// Initialize the cache only once
if (!initial)
{
// Loop through all available instances
for (short i = 0; i < game.conf.crtr_conf.instances_count; i++)
{
inst_inf = creature_instance_info_get(i);
// Check if the instance has a positive postal_priority
if (inst_inf->postal_priority > 0)
{
// Ensure we don't exceed the maximum array size
if (postal_inst_num < INSTANCE_TYPES_MAX)
{
// Add the instance ID to the cache
postal_inststance[postal_inst_num++] = i;
}
else {
break;
}
}
}
// Mark the cache as initialized
initial = true;
}

//List of usable instances
CrInstance av_postal_inst[INSTANCE_TYPES_MAX];
short av_postal_inst_num = 0;
char highest_prio = 0;
short highest_prio_idx = CrInst_NULL;

Check warning on line 1896 in src/creature_states_combt.c

View workflow job for this annotation

GitHub Actions / build

unused variable ‘highest_prio_idx’

Check warning on line 1896 in src/creature_states_combt.c

View workflow job for this annotation

GitHub Actions / build

unused variable ‘highest_prio_idx’
// Loop through the cached postal instances
for (short j = 0; j < postal_inst_num; j++)
{
inst_inf = creature_instance_info_get(postal_inststance[j]);

// Check if the instance is available
if (creature_instance_is_available(thing, postal_inststance[j]))
{
// If this instance has higher priority than current highest, reset the list
if (inst_inf->postal_priority > highest_prio)
{
highest_prio = inst_inf->postal_priority;
av_postal_inst_num = 0; // Clear the list as we found a higher priority
}

// If this instance matches the highest priority, check further conditions
if (inst_inf->postal_priority == highest_prio)
{
// Check if the instance is reset and in range
if (creature_instance_has_reset(thing, postal_inststance[j]) &&
inst_inf->range_min <= dist && dist <= inst_inf->range_max)
{
// Add to the list of available instances
av_postal_inst[av_postal_inst_num++] = postal_inststance[j];
}
}
}
}

// Choose a random index from the list of usable instances
if (av_postal_inst_num > 0)
{
short rand_inst_idx = CREATURE_RANDOM(thing, av_postal_inst_num);
return av_postal_inst[rand_inst_idx];
}
else
{
// Return NULL if no suitable instance is found
return CrInst_NULL;
}
}

void reset_postal_instance_cache()
{
// Reset the cache variables
postal_inst_num = 0;
initial = false;
memset(postal_inststance, 0, sizeof(postal_inststance));
}

#undef INSTANCE_RET_IF_AVAIL
#undef INSTANCE_RET_NEG_IF_AVAIL_ONLY

/**
* Gives combat weapon instance from given array which matches given distance.
Expand Down
3 changes: 2 additions & 1 deletion src/creature_states_combt.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ TbBool creature_would_benefit_from_healing(const struct Thing* thing);

long project_creature_attack_target_damage(const struct Thing *firing, const struct Thing *target);

CrInstance get_best_quick_range_instance_to_use(const struct Thing *thing);
void reset_postal_instance_cache();
CrInstance get_postal_instance_to_use(const struct Thing *thing, unsigned long dist);

TbBool creature_will_do_combat(const struct Thing *thing);
TbBool creature_look_for_combat(struct Thing *creatng);
Expand Down
Loading

0 comments on commit d0e95d0

Please sign in to comment.