diff --git a/src/act.movement.cpp b/src/act.movement.cpp index 449ad0f1..8c39a152 100644 --- a/src/act.movement.cpp +++ b/src/act.movement.cpp @@ -12,6 +12,7 @@ #include "act.hpp" +#include "ai.hpp" #include "board.hpp" #include "casting.hpp" #include "charsize.hpp" @@ -600,6 +601,10 @@ bool do_simple_move(CharData *ch, int dir, int need_specials_check) { if (DECEASED(actor)) return true; + if (was_in != IN_ROOM(actor)) { + queue_glory_event(actor); + } + return true; } @@ -1734,7 +1739,7 @@ ACMD(do_drag) { act("&3Your meditation is interrupted as %N grabs you.&0", false, ch, 0, 0, TO_CHAR); if (IS_NPC(ch)) REMOVE_FLAG(MOB_FLAGS(ch), MOB_MEDITATE); - else + else REMOVE_FLAG(PLR_FLAGS(ch), PLR_MEDITATE); } diff --git a/src/ai.hpp b/src/ai.hpp index fa888251..c8fa35c6 100644 --- a/src/ai.hpp +++ b/src/ai.hpp @@ -14,6 +14,7 @@ #include "skills.hpp" #include "structs.hpp" #include "sysdep.hpp" +#include #define MOB_ASSISTER(ch) \ (!MOB_FLAGGED((ch), MOB_PEACEFUL) && \ @@ -34,10 +35,15 @@ void perform_remove(CharData *ch, int pos); int appraise_item(CharData *ch, ObjData *obj); bool will_assist(CharData *ch, CharData *vict); CharData *find_aggr_target(CharData *ch); -void glorion_distraction(CharData *ch, CharData *glorion); int appraise_opponent(CharData *ch, CharData *vict); bool is_aggr_to(CharData *ch, CharData *tch); +std::vector find_glorions(CharData* mob); +void glorion_distraction(CharData *ch, CharData *glorion); +void check_glory(CharData* ch, CharData* caster = nullptr); +void check_glory_around(CharData* ch); +void queue_glory_event(CharData* ch); + /* Class AI functions */ bool sorcerer_ai_action(CharData *ch, CharData *victim); bool cleric_ai_action(CharData *ch, CharData *victim); diff --git a/src/ai_utils.cpp b/src/ai_utils.cpp index 1ec40b19..29188934 100644 --- a/src/ai_utils.cpp +++ b/src/ai_utils.cpp @@ -624,7 +624,7 @@ void glorion_distraction(CharData *ch, CharData *glorion) { event_create(EVENT_QUICK_AGGRO, quick_aggro_event, mkgenericevent(ch, glorion, 0), true, &(ch->events), 0); } else { /* Glory wins: no attack. */ - if (random_number(1, 8) == 1) { + if (true || random_number(1, 8) == 1) { act("$n looks upon $N with awe in $s eyes.", true, ch, 0, glorion, TO_NOTVICT); act("$n gazes at you in wonder.", true, ch, 0, glorion, TO_VICT); act("You are distracted by $N's unearthly beauty.", true, ch, 0, glorion, TO_CHAR); @@ -632,50 +632,72 @@ void glorion_distraction(CharData *ch, CharData *glorion) { } } -#define MAX_TARGETS 10 -CharData *find_aggr_target(CharData *ch) { - CharData *tch; - struct aggr_target { - CharData *target; - int difficulty; - } targets[MAX_TARGETS + 1]; - int i, j, k, num_targets, chosen_targets; - - CharData *glorion = nullptr; - int glorion_count; +std::vector find_glorions(CharData* mob) { + std::vector glorions; + if (!mob || DECEASED(mob) || !IS_NPC(mob)) { + return glorions; + } + + for (CharData* tch = world[IN_ROOM(mob)].people; tch; tch = tch->next_in_room) { + /* If there are any folks in the room with GLORY, they will + * interfere with aggression. One will be chosen at random, + * because there's a chance the mob will attack it. But no + * one else will be. */ + if (mob != tch && CAN_SEE(mob, tch)) { + if (EFF_FLAGGED(tch, EFF_GLORY) && PLAYERALLY(tch)) { + glorions.push_back(tch); + } + } + } + return glorions; +} +static bool able_to_fight(CharData* ch, bool no_aggr_check = false) { if (!ch || CH_NROOM(ch) == NOWHERE) - return nullptr; + return false; if (ROOM_FLAGGED(CH_NROOM(ch), ROOM_PEACEFUL)) - return nullptr; + return false; - if (!IS_NPC(ch) && !EFF_FLAGGED(ch, EFF_BERSERK)) { + if (!IS_NPC(ch) && !EFF_FLAGGED(ch, EFF_BERSERK) && !no_aggr_check) { if (GET_WIMP_LEV(ch) >= GET_HIT(ch)) - return nullptr; + return false; if (GET_AGGR_LEV(ch) <= 0 || GET_AGGR_LEV(ch) > GET_HIT(ch)) - return nullptr; + return false; } if (PLR_FLAGGED(ch, PLR_BOUND) || !AWAKE(ch) || EFF_FLAGGED(ch, EFF_MINOR_PARALYSIS) || EFF_FLAGGED(ch, EFF_MAJOR_PARALYSIS)) + return false; + + return true; +} + +#define MAX_TARGETS 10 +CharData *find_aggr_target(CharData *ch) { + CharData *tch; + struct aggr_target { + CharData *target; + int difficulty; + } targets[MAX_TARGETS + 1]; + int i, j, k, num_targets, chosen_targets; + + if (!able_to_fight(ch)) { return nullptr; + } /* Your intelligence determines how many targets you will evaluate. */ num_targets = std::max(1, GET_INT(ch) * MAX_TARGETS / 100); /* Choose #num_targets characters at random */ - glorion_count = 0; for (i = 0, tch = world[IN_ROOM(ch)].people; tch; tch = tch->next_in_room) { /* If there are any folks in the room with GLORY, they will - * interfere with aggression. One will be chosen at random, - * because there's a chance the mob will attack it. But no - * one else will be. */ + * interfere with aggression. This check is only done the + * first time that a mob is exposed to the spell, so + * subsequent aggression checks should fail. */ if (ch != tch && CAN_SEE(ch, tch)) { if (EFF_FLAGGED(tch, EFF_GLORY) && PLAYERALLY(tch)) { - glorion_count++; - if (random_number(1, glorion_count) == 1) - glorion = tch; + return nullptr; } else if (is_aggr_to(ch, tch)) { if (i >= num_targets) j = random_number(0, i); @@ -690,11 +712,6 @@ CharData *find_aggr_target(CharData *ch) { } } - if (glorion_count) { - glorion_distraction(ch, glorion); - return nullptr; - } - if (i == 0) /* Didn't find anyone I was aggro to. */ return nullptr; @@ -728,3 +745,49 @@ CharData *find_aggr_target(CharData *ch) { return targets[k].target; } + +void check_glory(CharData* ch, CharData* caster) { + if (!able_to_fight(ch)) { + return; + } + std::vector glorions; + CharData* target = caster; + if (!caster) { + glorions = find_glorions(ch); + if (glorions.empty()) { + return; + } + target = glorions[random_number(0, glorions.size() - 1)]; + } + glorion_distraction(ch, target); +} + +void check_glory_around(CharData* ch) { + if (!able_to_fight(ch, true)) { + return; + } + for (CharData* tch = world[IN_ROOM(ch)].people; tch; tch = tch->next_in_room) { + if (!IS_NPC(tch)) { + continue; + } + check_glory(tch, ch); + } +} + +EVENTFUNC(witness_glory_event) { + GenericEventData *data = (GenericEventData *)event_obj; + CharData *ch = data->ch; + + if (EFF_FLAGGED(ch, EFF_GLORY)) { + check_glory_around(ch); + } else { + check_glory(ch); + } + + return EVENT_FINISHED; +} + +void queue_glory_event(CharData* ch) { + cancel_event(ch->events, EVENT_WITNESS_GLORY); + event_create(EVENT_WITNESS_GLORY, witness_glory_event, mkgenericevent(ch, nullptr, 0), true, &(ch->events), 1 RL_SEC); +} diff --git a/src/events.cpp b/src/events.cpp index 820d9f9e..b0ed3f95 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -64,6 +64,7 @@ const char *eventnames[MAX_EVENT + 1] = {"!INVALID EVENT!", /* 0 - reserved */ "command", "start_editor", "get_money", /* 30 */ + "witness_glory", "\n"}; /*************************************************************************/ diff --git a/src/events.hpp b/src/events.hpp index c5148bd4..fa0fd627 100644 --- a/src/events.hpp +++ b/src/events.hpp @@ -85,8 +85,9 @@ void delayed_command(CharData *ch, char *command, int delay, bool repeatable); #define EVENT_COMMAND 28 #define EVENT_EDITOR_START 29 #define EVENT_GET_MONEY 30 +#define EVENT_WITNESS_GLORY 31 /* Update MAX_EVENT to be last event value + 1, please */ -#define MAX_EVENT 31 +#define MAX_EVENT 32 EVENTFUNC(extract_event); EVENTFUNC(hurt_event); diff --git a/src/magic.cpp b/src/magic.cpp index 881b3a3f..59f37557 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -12,6 +12,7 @@ #include "magic.hpp" +#include "ai.hpp" #include "casting.hpp" #include "chars.hpp" #include "charsize.hpp" @@ -3348,6 +3349,10 @@ int mag_affect(int skill, CharData *ch, CharData *victim, int spellnum, int save if (to_char == nullptr && ch != victim) act(to_room, false, ch, 0, victim, TO_CHAR); } + + if (spellnum == SPELL_GLORY) { + queue_glory_event(ch); + } } else { /* Add generic refresh message */ SkillDef *spell;