From 46e683f9b3bf643bc23c347b3fc736bc6eb3acee Mon Sep 17 00:00:00 2001
From: gammafunk <gammafunk@gmail.com>
Date: Sat, 8 Feb 2014 23:00:37 -0600
Subject: [PATCH] Convert alignment of a monster avatar when its summoner's
 alignment changes.

When the alignment of the summoner of a spectral weapon, battlesphere, or grand
avatar changes, previously these avatars were not changing alignment along with
the summoner.  This adds monster::align_avatars() which is called appropriately
in mons_att_changed() and in monster::add_ench() for the case of ENCH_CHARM.
When summoners go to any form of ATT_*NEUTRAL, we remove all their avatars.  For
grand avatars, I've added an end_grand_avatar() function to help with this.
Monsters with ENCH_GRAND_AVATAR that change attitude but who are not the
summoner also lose that enchantment.

There is also a bugfix in setting of ENCH_GRAND_AVATAR upon casting where it
would skip setting enchantment on the summoner if summoner was charmed.  Always
set the enchant on the owner, and never set the enchant on any kind of avatar to
avoid having an avatar of an avatar.
---
 crawl-ref/source/mon-cast.cc      |  6 +++--
 crawl-ref/source/mon-ench.cc      |  2 ++
 crawl-ref/source/mon-stuff.cc     |  1 +
 crawl-ref/source/monster.cc       | 56 +++++++++++++++++++++++++++++++++++++++
 crawl-ref/source/monster.h        |  1 +
 crawl-ref/source/spl-summoning.cc | 44 +++++++++++++++++++++++++-----
 crawl-ref/source/spl-summoning.h  |  1 +
 7 files changed, 102 insertions(+), 9 deletions(-)

diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc
index e2843b0..6905d83 100644
--- a/crawl-ref/source/mon-cast.cc
+++ b/crawl-ref/source/mon-cast.cc
@@ -5734,10 +5734,12 @@ void mons_cast(monster* mons, bolt &pbolt, spell_type spell_cast,
         if (avatar)
         {
             simple_monster_message(mons, " calls forth a grand avatar!");
+            mons->add_ench(mon_enchant(ENCH_GRAND_AVATAR, 1, avatar));
             for (monster_near_iterator mi(mons, LOS_NO_TRANS); mi; ++mi)
             {
-                if (mons_aligned(mons, *mi) && !mi->has_ench(ENCH_CHARM)
-                    && *mi != avatar)
+                if (*mi != mons && mons_aligned(mons, *mi)
+                    && !mi->has_ench(ENCH_CHARM)
+                    && !mons_is_avatar((*mi)->type))
                 {
                     mi->add_ench(mon_enchant(ENCH_GRAND_AVATAR, 1, avatar));
                 }
diff --git a/crawl-ref/source/mon-ench.cc b/crawl-ref/source/mon-ench.cc
index d20b1a1..5ff32a0 100644
--- a/crawl-ref/source/mon-ench.cc
+++ b/crawl-ref/source/mon-ench.cc
@@ -151,6 +151,8 @@ bool monster::add_ench(const mon_enchant &ench)
     if (new_enchantment)
         add_enchantment_effect(ench);
 
+    if (ench.ench == ENCH_CHARM)
+        this->align_avatars(true);
     return true;
 }
 
diff --git a/crawl-ref/source/mon-stuff.cc b/crawl-ref/source/mon-stuff.cc
index 96c2f89..1431882 100644
--- a/crawl-ref/source/mon-stuff.cc
+++ b/crawl-ref/source/mon-stuff.cc
@@ -4813,6 +4813,7 @@ void mons_att_changed(monster* mon)
     {
         remove_companion(mon);
     }
+    mon->align_avatars();
 }
 
 void debuff_monster(monster* mon)
diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc
index f92d0c3..e3a9355 100644
--- a/crawl-ref/source/monster.cc
+++ b/crawl-ref/source/monster.cc
@@ -6311,3 +6311,59 @@ int monster::spell_hd(spell_type spell) const
 {
     return hit_dice + 2 * aug_amount();
 }
+
+void monster::align_avatars(bool force_friendly)
+{
+    mon_attitude_type new_att = (force_friendly ? ATT_FRIENDLY
+                                   : this->attitude);
+    // Neutral monsters don't need avatars, and in same cases would attack their
+    // own avatars if they had them.
+    bool remove_avatars = new_att == ATT_NEUTRAL
+        || new_att == ATT_STRICT_NEUTRAL || new_att == ATT_GOOD_NEUTRAL;
+    monster* avatar = find_spectral_weapon(this);
+    if (avatar)
+    {
+        if (remove_avatars)
+        {
+            end_spectral_weapon(avatar, false, false);
+        }
+        else
+        {
+            avatar->attitude = new_att;
+            reset_spectral_weapon(avatar);
+        }
+    }
+    avatar = find_battlesphere(this);
+    if (avatar)
+    {
+        if (remove_avatars)
+        {
+            end_battlesphere(avatar, false);
+        }
+        else
+        {
+            avatar->attitude = new_att;
+            reset_battlesphere(avatar);
+        }
+    }
+
+    actor* gavatar = this->get_ench(ENCH_GRAND_AVATAR).agent();
+    if (!gavatar)
+        return;
+    avatar = gavatar->as_monster();
+    monster *owner = monster_by_mid(avatar->summoner);
+    if (avatar->summoner == this->mid)
+    {
+        if (remove_avatars)
+        {
+            end_grand_avatar(avatar, false);
+        }
+        else
+        {
+            avatar->attitude = new_att;
+            grand_avatar_reset(avatar);
+        }
+    }
+    else if (!owner || !mons_aligned(this, owner))
+        this->del_ench(ENCH_GRAND_AVATAR);
+}
diff --git a/crawl-ref/source/monster.h b/crawl-ref/source/monster.h
index 04012dd..14858d2 100644
--- a/crawl-ref/source/monster.h
+++ b/crawl-ref/source/monster.h
@@ -502,6 +502,7 @@ public:
 
     int  aug_amount() const;
     int  spell_hd(spell_type spell = SPELL_NO_SPELL) const;
+    void align_avatars(bool force_friendly = false);
 
 private:
     void init_with(const monster& mons);
diff --git a/crawl-ref/source/spl-summoning.cc b/crawl-ref/source/spl-summoning.cc
index 1fb0f8d..549b443 100644
--- a/crawl-ref/source/spl-summoning.cc
+++ b/crawl-ref/source/spl-summoning.cc
@@ -3118,6 +3118,22 @@ void trigger_grand_avatar(monster* mons, actor* victim, spell_type spell,
     const bool melee = (spell == SPELL_MELEE);
     ASSERT(mons->has_ench(ENCH_GRAND_AVATAR));
 
+    actor* avatar = mons->get_ench(ENCH_GRAND_AVATAR).agent();
+    if (!avatar)
+    {
+        mons->del_ench(ENCH_GRAND_AVATAR);
+        return;
+    }
+
+    ASSERT(avatar->is_monster());
+    monster* av = avatar->as_monster();
+    monster* owner = monster_by_mid(av->summoner);
+    if (!owner || !mons_aligned(mons, owner))
+    {
+        mons->del_ench(ENCH_GRAND_AVATAR);
+        return;
+    }
+
     if (!victim
         || !victim->alive()
         || (!melee && !battlesphere_can_mirror(spell))
@@ -3126,13 +3142,6 @@ void trigger_grand_avatar(monster* mons, actor* victim, spell_type spell,
         return;
     }
 
-    actor* avatar = mons->get_ench(ENCH_GRAND_AVATAR).agent();
-    if (!avatar)
-        return;
-
-    ASSERT(avatar->is_monster());
-    monster* av = avatar->as_monster();
-
     av->props[GA_TARGET_MID].get_int() = victim->mid;
     if (melee)
     {
@@ -3146,6 +3155,27 @@ void trigger_grand_avatar(monster* mons, actor* victim, spell_type spell,
     }
 }
 
+void end_grand_avatar(monster* mons, bool killed)
+{
+    // Should only happen if you dismiss it in wizard mode, I think
+    if (!mons)
+        return;
+
+    actor* agent = actor_by_mid(mons->summoner);
+    if (agent)
+    {
+        monster* owner = agent->as_monster();
+        owner->del_ench(ENCH_GRAND_AVATAR);
+    }
+
+    if (!killed)
+    {
+        place_cloud(CLOUD_MAGIC_TRAIL, mons->pos(),
+                    3 + random2(3), mons);
+        monster_die(mons, KILL_RESET, NON_MONSTER);
+    }
+}
+
 spell_type summons_index::map(const summons_desc* val)
 {
     return val->which;
diff --git a/crawl-ref/source/spl-summoning.h b/crawl-ref/source/spl-summoning.h
index d81bed0..5eb6b84 100644
--- a/crawl-ref/source/spl-summoning.h
+++ b/crawl-ref/source/spl-summoning.h
@@ -111,6 +111,7 @@ void reset_spectral_weapon(monster* mons);
 
 void grand_avatar_reset(monster* mons);
 bool grand_avatar_check_melee(monster* mons, actor* target);
+void end_grand_avatar(monster* mons, bool killed);
 void trigger_grand_avatar(monster* mons, actor* victim, spell_type spell,
                           const int old_hp);
 
-- 
1.8.3.2

