From d14f2ed06fc90c4cee5cd757c7b4eeaf027e96d1 Mon Sep 17 00:00:00 2001
From: Ed Gonzalez <ed.gonzalez3@gmail.com>
Date: Tue, 25 Jun 2013 03:03:51 -0700
Subject: [PATCH 1/5] Make Spectral Weapon reset on player's next turn if it
 hasn't attacked.

This parallels how battlesphere already resets if it couldn't carry out
the order.  Now a trapped spectral weapon won't still have an attack
order if it is later freed.
---
 crawl-ref/source/mon-act.cc       |    1 +
 crawl-ref/source/spl-summoning.cc |   20 ++++++++++++++++++++
 crawl-ref/source/spl-summoning.h  |    1 +
 3 files changed, 22 insertions(+)

diff --git a/crawl-ref/source/mon-act.cc b/crawl-ref/source/mon-act.cc
index b2f6ca0..9ae0d5e 100644
--- a/crawl-ref/source/mon-act.cc
+++ b/crawl-ref/source/mon-act.cc
@@ -1799,6 +1799,7 @@ static void _pre_monster_move(monster* mons)
     }
 
     reset_battlesphere(mons);
+    reset_spectral_weapon(mons);
 
     // This seems to need to go here to actually get monsters to slow down.
     // XXX: Replace with a new ENCH_LIQUEFIED_GROUND or something.
diff --git a/crawl-ref/source/spl-summoning.cc b/crawl-ref/source/spl-summoning.cc
index 10645b6..b9ccfe9 100644
--- a/crawl-ref/source/spl-summoning.cc
+++ b/crawl-ref/source/spl-summoning.cc
@@ -3058,3 +3058,23 @@ bool confirm_attack_spectral_weapon(monster* mons, const actor *defender)
 
     return false;
 }
+
+// Called at the start of each round. Cancels attack order given in the
+// previous round, if the weapon was not able to execute them fully
+// before the next player action
+void reset_spectral_weapon(monster* mons)
+{
+    if (!mons || mons->type != MONS_SPECTRAL_WEAPON)
+        return;
+
+    if (mons->props.exists("ready"))
+    {
+        mons->props.erase("ready");
+        if (mons->props.exists("sw_mid"))
+        {
+            actor *owner = actor_by_mid(mons->props["sw_mid"].get_int());
+            mons->target = owner->pos();
+        }
+        mons->foe = MHITNOT;
+    }
+}
diff --git a/crawl-ref/source/spl-summoning.h b/crawl-ref/source/spl-summoning.h
index 92f988f..fd237c8 100644
--- a/crawl-ref/source/spl-summoning.h
+++ b/crawl-ref/source/spl-summoning.h
@@ -97,4 +97,5 @@ void end_spectral_weapon(monster* mons, bool killed, bool quiet = false);
 bool trigger_spectral_weapon(actor* agent, const actor* target);
 bool check_target_spectral_weapon(const actor* mons, const actor *defender);
 bool confirm_attack_spectral_weapon(monster* mons, const actor *defender);
+void reset_spectral_weapon(monster* mons);
 #endif
-- 
1.7.10.4


From 7852070ff1f2f04c130009a2b167acc796606929 Mon Sep 17 00:00:00 2001
From: Ed Gonzalez <ed.gonzalez3@gmail.com>
Date: Tue, 25 Jun 2013 03:35:44 -0700
Subject: [PATCH 2/5] Allow spectral weapons to be placed by mons spec, like
 dancing weapons.

The syntax is 'spectral weapon ; (weapon)'.  Note that the AI is
non-functional when placed by anything but the spell.  No vault should
place a bare spectral weapon and expect it to do anything useful.

This change is mostly so that the monster utility can generate spectral
weapons.  However, the stats are currently only set properly by the
actual casting code.
---
 crawl-ref/source/mapdef.cc   |    3 ++-
 crawl-ref/source/mon-gear.cc |    1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc
index 75d405e..7709326 100644
--- a/crawl-ref/source/mapdef.cc
+++ b/crawl-ref/source/mapdef.cc
@@ -4046,7 +4046,8 @@ mons_list::mons_spec_slot mons_list::parse_mons_spec(string spec)
             }
             else if (mons_class_itemuse(type) < MONUSE_STARTING_EQUIPMENT)
             {
-                if (type != MONS_DANCING_WEAPON || mspec.items.size() > 1)
+                if ((type != MONS_DANCING_WEAPON && type != MONS_SPECTRAL_WEAPON)
+                    || mspec.items.size() > 1)
                     error = make_stringf("Monster '%s' can't use items.",
                                          mon_str.c_str());
             }
diff --git a/crawl-ref/source/mon-gear.cc b/crawl-ref/source/mon-gear.cc
index 83dc819..6f828ac 100644
--- a/crawl-ref/source/mon-gear.cc
+++ b/crawl-ref/source/mon-gear.cc
@@ -623,6 +623,7 @@ static item_make_species_type _give_weapon(monster* mon, int level,
         // deliberate fall-through {dlb}
 
     case MONS_DANCING_WEAPON:   // give_level may have been adjusted above
+    case MONS_SPECTRAL_WEAPON:  // Necessary for placement by mons spec
     case MONS_FRANCES:
     case MONS_HAROLD:
     case MONS_LOUISE:
-- 
1.7.10.4


From c39495903ca24215ed154074450ea2170fd5b6c9 Mon Sep 17 00:00:00 2001
From: Ed Gonzalez <ed.gonzalez3@gmail.com>
Date: Tue, 25 Jun 2013 03:48:44 -0700
Subject: [PATCH 3/5] Make spectral weapon speed 30.

For the moment, it still won't follow every attack from a hasted quick
blade user attacking at the minimum delay cap, but this is as fast as a
monster can get without funny energy usage tweaks.
---
 crawl-ref/source/mon-data.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index 43d388e..26c3c3f 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -4341,7 +4341,7 @@ static monsterentry mondata[] = {
     { {AT_HIT, AF_PLAIN, 6}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
     { 5, 3, 0, 0},
     5, 10, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT,
-    I_PLANT, HT_LAND, FL_LEVITATE, 25, DEFAULT_ENERGY,
+    I_PLANT, HT_LAND, FL_LEVITATE, 30, DEFAULT_ENERGY,
     MONUSE_NOTHING, MONEAT_NOTHING, SIZE_MEDIUM
 },
 
-- 
1.7.10.4


From 180a7db1b612f60d1178b11f85458508c4dbde23 Mon Sep 17 00:00:00 2001
From: Ed Gonzalez <ed.gonzalez3@gmail.com>
Date: Tue, 25 Jun 2013 04:46:58 -0700
Subject: [PATCH 4/5] Moved the setting of Spectral Weapon stats to ghost
 code, like dancing.

By initialising the weapon's stats using a ghost, more properties can be
easily set.  Also vault/wiz-mode placed spectral weapons now get
appropriate stats, as if the spell had been cast at max power and max
weapon skill.

Spectral weapons still shouldn't be placed directly in normal games.
---
 crawl-ref/source/dungeon.cc       |    7 +++++++
 crawl-ref/source/ghost.cc         |   27 +++++++++++++++++++++++++++
 crawl-ref/source/ghost.h          |    1 +
 crawl-ref/source/mgen_data.h      |    2 ++
 crawl-ref/source/mon-place.cc     |   17 +++++++++++++++++
 crawl-ref/source/mon-util.cc      |    1 +
 crawl-ref/source/spl-summoning.cc |    8 +-------
 7 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 0e1c292..08710f5 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -4883,6 +4883,13 @@ monster* dgn_place_monster(mons_spec &mspec, coord_def where,
         mons->ghost->init_dancing_weapon(*wpn, 100);
         mons->ghost_demon_init();
     }
+    else if (mons->type == MONS_SPECTRAL_WEAPON)
+    {
+        item_def *wpn = mons->mslot_item(MSLOT_WEAPON);
+        ASSERT(wpn);
+        mons->ghost->init_spectral_weapon(*wpn, 100, 27);
+        mons->ghost_demon_init();
+    }
 
     for (unsigned int i = 0; i < mspec.ench.size(); i++)
         mons->add_ench(mspec.ench[i]);
diff --git a/crawl-ref/source/ghost.cc b/crawl-ref/source/ghost.cc
index 4630bd8..1c428f7 100644
--- a/crawl-ref/source/ghost.cc
+++ b/crawl-ref/source/ghost.cc
@@ -709,6 +709,33 @@ void ghost_demon::init_dancing_weapon(const item_def& weapon, int power)
     damage = max(1, damage * power / 100);
 }
 
+void ghost_demon::init_spectral_weapon(const item_def& weapon,
+                                       int power, int wpn_skill)
+{
+    int damg  = property(weapon, PWPN_DAMAGE);
+
+    if (power > 100)
+        power = 100;
+
+    if (wpn_skill > 27)
+        wpn_skill = 27;
+
+    colour = weapon.colour;
+    fly = FL_LEVITATE;
+
+    // Offense scales with weapon skill.
+    // Defenses scale with spell power.
+    // Appropriate investment is rewarded with a stronger spectral weapon.
+
+    xl = wpn_skill;
+
+    speed   = 30;
+    ev      = 2 + div_rand_round(power,12);
+    ac      = 2 + div_rand_round(power,12);
+    damage  = damg;
+    max_hp  = 10 + div_rand_round(power,3);
+}
+
 static bool _know_spell(spell_type spell)
 {
     return (you.has_spell(spell) && spell_fail(spell) < 50);
diff --git a/crawl-ref/source/ghost.h b/crawl-ref/source/ghost.h
index 37d8424..06fadaa 100644
--- a/crawl-ref/source/ghost.h
+++ b/crawl-ref/source/ghost.h
@@ -44,6 +44,7 @@ public:
     void init_ugly_thing(bool very_ugly, bool only_mutate = false,
                          colour_t force_colour = BLACK);
     void init_dancing_weapon(const item_def& weapon, int power);
+    void init_spectral_weapon(const item_def& weapon, int power, int wpn_skill);
     void ugly_thing_to_very_ugly_thing();
 
 public:
diff --git a/crawl-ref/source/mgen_data.h b/crawl-ref/source/mgen_data.h
index e98bb67..2f8c326 100644
--- a/crawl-ref/source/mgen_data.h
+++ b/crawl-ref/source/mgen_data.h
@@ -8,6 +8,8 @@
 // a dancing weapon.
 #define TUKIMA_WEAPON "tukima-weapon"
 #define TUKIMA_POWER "tukima-power"
+// Technically only used for spectral weapon, not dancing weapons
+#define TUKIMA_SKILL "tukima-skill"
 
 // A structure with all the data needed to whip up a new monster.
 struct mgen_data
diff --git a/crawl-ref/source/mon-place.cc b/crawl-ref/source/mon-place.cc
index 55f4f72..3b7f5b2 100644
--- a/crawl-ref/source/mon-place.cc
+++ b/crawl-ref/source/mon-place.cc
@@ -1719,6 +1719,23 @@ static monster* _place_monster_aux(const mgen_data &mg, const monster *leader,
         mon->set_ghost(ghost);
         mon->ghost_demon_init();
     }
+    else if (mon->type == MONS_SPECTRAL_WEAPON)
+    {
+        ghost_demon ghost;
+        // We can't use monster::weapon here because it wants to look
+        // at attack types, which are in the ghost structure we're
+        // building.
+        ASSERT(mon->mslot_item(MSLOT_WEAPON));
+        // Spectral weapons are placed at pretty high power.
+        // They shouldn't ever be placed in a normal game.
+        ghost.init_spectral_weapon(*(mon->mslot_item(MSLOT_WEAPON)),
+                                   mg.props.exists(TUKIMA_POWER) ?
+                                       mg.props[TUKIMA_POWER].get_int() : 100,
+                                   mg.props.exists(TUKIMA_SKILL) ?
+                                       mg.props[TUKIMA_SKILL].get_int() : 27);
+        mon->set_ghost(ghost);
+        mon->ghost_demon_init();
+    }
 
     tile_init_props(mon);
 
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 70f36ea..e77e0be 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -1343,6 +1343,7 @@ bool mons_is_ghost_demon(monster_type mc)
             || mc == MONS_PLAYER_GHOST
             || mc == MONS_PLAYER_ILLUSION
             || mc == MONS_DANCING_WEAPON
+            || mc == MONS_SPECTRAL_WEAPON
             || mc == MONS_PANDEMONIUM_LORD;
 }
 
diff --git a/crawl-ref/source/spl-summoning.cc b/crawl-ref/source/spl-summoning.cc
index b9ccfe9..69ee419 100644
--- a/crawl-ref/source/spl-summoning.cc
+++ b/crawl-ref/source/spl-summoning.cc
@@ -2934,6 +2934,7 @@ spret_type cast_spectral_weapon(actor *agent, int pow, god_type god, bool fail)
 
     mg.props[TUKIMA_WEAPON] = cp;
     mg.props[TUKIMA_POWER] = pow;
+    mg.props[TUKIMA_SKILL] = skill_with_weapon;
 
     monster *mons = create_monster(mg);
     if (!mons)
@@ -2964,13 +2965,6 @@ spret_type cast_spectral_weapon(actor *agent, int pow, god_type god, bool fail)
     mons->props["sw_mid"].get_int() = agent->mid;
     agent->props["spectral_weapon"].get_int() = mons->mid;
 
-    mons->hit_dice = skill_with_weapon;
-    int hp = 10 + div_rand_round(pow,3);
-    mons->hit_points = hp;
-    mons->max_hit_points = hp;
-    mons->ac = 2 + div_rand_round(pow,12);
-    mons->ev = 2 + div_rand_round(pow,12);
-
     return SPRET_SUCCESS;
 }
 
-- 
1.7.10.4


From 426a1f95b854e8a46f23f36bdf9dfc60a29d767d Mon Sep 17 00:00:00 2001
From: Ed Gonzalez <ed.gonzalez3@gmail.com>
Date: Tue, 25 Jun 2013 05:10:13 -0700
Subject: [PATCH 5/5] Add spectral weapons to more shared special cases with
 dancing weapons.

They can no longer be disarmed or self-unwielded.  Nor should they have
a body.
---
 crawl-ref/source/melee_attack.cc |    1 +
 crawl-ref/source/mon-util.cc     |    4 +++-
 crawl-ref/source/monster.cc      |    4 ++--
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/crawl-ref/source/melee_attack.cc b/crawl-ref/source/melee_attack.cc
index e16caf2..6ca779e 100644
--- a/crawl-ref/source/melee_attack.cc
+++ b/crawl-ref/source/melee_attack.cc
@@ -5106,6 +5106,7 @@ void melee_attack::tendril_disarm()
     if (you.mutation[MUT_TENDRILS]
         && attacker->alive()
         && mon->type != MONS_DANCING_WEAPON
+        && mon->type != MONS_SPECTRAL_WEAPON
         && adjacent(you.pos(), mon->pos())
         && you.can_see(mon)
         && one_chance_in(5)
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index e77e0be..07fae0b 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -654,7 +654,8 @@ bool mons_has_body(const monster* mon)
     if (mon->type == MONS_FLYING_SKULL
         || mon->type == MONS_CURSE_SKULL
         || mon->type == MONS_CURSE_TOE
-        || mon->type == MONS_DANCING_WEAPON)
+        || mon->type == MONS_DANCING_WEAPON
+        || mon->type == MONS_SPECTRAL_WEAPON)
     {
         return false;
     }
@@ -2480,6 +2481,7 @@ void define_monster(monster* mons)
     // Load with dummy values so certain monster properties can be queried
     // before placement without crashing (proper setup is done later here)
     case MONS_DANCING_WEAPON:
+    case MONS_SPECTRAL_WEAPON:
     {
         ghost_demon ghost;
         mons->set_ghost(ghost);
diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc
index 6447d52..88518f6 100644
--- a/crawl-ref/source/monster.cc
+++ b/crawl-ref/source/monster.cc
@@ -548,7 +548,7 @@ bool monster::can_wield(const item_def& item, bool ignore_curse,
 
     // These *are* weapons, so they can't wield another weapon or
     // unwield themselves.
-    if (type == MONS_DANCING_WEAPON)
+    if (type == MONS_DANCING_WEAPON || type == MONS_SPECTRAL_WEAPON)
         return false;
 
     // MF_HARD_RESET means that all items the monster is carrying will
@@ -619,7 +619,7 @@ bool monster::could_wield(const item_def &item, bool ignore_brand,
     ASSERT(item.defined());
 
     // These *are* weapons, so they can't wield another weapon.
-    if (type == MONS_DANCING_WEAPON)
+    if (type == MONS_DANCING_WEAPON || type == MONS_SPECTRAL_WEAPON)
         return false;
 
     // Monsters can't use unrandarts with special effects.
-- 
1.7.10.4

