summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/settings/init.txt7
-rw-r--r--crawl-ref/source/acr.cc2
-rw-r--r--crawl-ref/source/dat/database/quotes.txt17
-rw-r--r--crawl-ref/source/dat/descript/monsters.txt9
-rw-r--r--crawl-ref/source/debug.cc28
-rw-r--r--crawl-ref/source/dungeon.cc2
-rw-r--r--crawl-ref/source/enum.h11
-rw-r--r--crawl-ref/source/fight.cc3
-rw-r--r--crawl-ref/source/misc.cc1
-rw-r--r--crawl-ref/source/mon-data.h23
-rw-r--r--crawl-ref/source/mon-spll.h11
-rw-r--r--crawl-ref/source/mon-util.cc13
-rw-r--r--crawl-ref/source/monplace.cc7
-rw-r--r--crawl-ref/source/monstuff.cc83
-rw-r--r--crawl-ref/source/monstuff.h2
-rw-r--r--crawl-ref/source/mstuff2.cc45
-rw-r--r--crawl-ref/source/output.cc3
-rw-r--r--crawl-ref/source/spl-data.h13
18 files changed, 267 insertions, 13 deletions
diff --git a/crawl-ref/settings/init.txt b/crawl-ref/settings/init.txt
index 7a5643f3fe..44df7f194d 100644
--- a/crawl-ref/settings/init.txt
+++ b/crawl-ref/settings/init.txt
@@ -184,9 +184,14 @@ runrest_ignore_message = engulfed in a cloud of smoke
runrest_ignore_message = safely over a trap
runrest_ignore_message = You feel.*sick
runrest_ignore_poison = 2:30
+runrest_ignore_monster = butterfly:1
+# acquatic monsters
runrest_ignore_monster = fish:2
runrest_ignore_monster = shark:2
-runrest_ignore_monster = butterfly:1
+runrest_ignore_monster = kraken:2
+# special case for tentacled monstrosity
+runrest_ignore_monster = tentacled:8
+runrest_ignore_monster = tentacle:2
# runrest_ignore_monster = swamp worm:3
# trap_prompt = false
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index bfd881a43f..e79c2508a6 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -4244,7 +4244,7 @@ static void _compile_time_asserts()
COMPILE_CHECK(SP_VAMPIRE == 31 , c3);
COMPILE_CHECK(SPELL_DEBUGGING_RAY == 103 , c4);
COMPILE_CHECK(SPELL_PETRIFY == 156 , c5);
- COMPILE_CHECK(NUM_SPELLS == 199 , c6);
+ COMPILE_CHECK(NUM_SPELLS == 200 , c6);
//jmf: NEW ASSERTS: we ought to do a *lot* of these
COMPILE_CHECK(NUM_JOBS < JOB_UNKNOWN , c7);
diff --git a/crawl-ref/source/dat/database/quotes.txt b/crawl-ref/source/dat/database/quotes.txt
index 82d74129a9..f81db51f0f 100644
--- a/crawl-ref/source/dat/database/quotes.txt
+++ b/crawl-ref/source/dat/database/quotes.txt
@@ -698,6 +698,23 @@ jelly
A splotch, a blotch..."
-Burt Bacharach, "Beware of the Blob"
%%%%
+kraken
+
+"... Kraken, also called the Crab-fish, which [according to the pilots of
+Norway] is not that huge, for heads and tails counted, he is no larger than
+our Öland is wide [i.e. less than 16 km] ... He stays at the sea floor,
+constantly surrounded by innumerable small fishes, who serve as his food and
+are fed by him in return: for his meal, if I remember correctly what E.
+Pontoppidan writes, lasts no longer than three months, and another three are
+then needed to digest it. His excrements nurture in the following an army of
+lesser fish, and for this reason, fishermen plumb after his resting place
+... Gradually, Kraken ascends to the surface, and when he is at ten to
+twelve fathoms, the boats had better move out of his vicinity, as he will
+shortly thereafter burst up, like a floating island, spurting water from his
+dreadful nostrils and making ring waves around him, which can reach many
+miles. Could one doubt that this is the Leviathan of Job?"
+ -Jacob Wallenberg, "Min son på galejan", 1781.
+%%%%
Menkaure
"Ye men of Egypt, ye have heard your king!
diff --git a/crawl-ref/source/dat/descript/monsters.txt b/crawl-ref/source/dat/descript/monsters.txt
index a00b77bcdc..75fa132592 100644
--- a/crawl-ref/source/dat/descript/monsters.txt
+++ b/crawl-ref/source/dat/descript/monsters.txt
@@ -925,6 +925,10 @@ komodo dragon
An enormous monitor lizard. It's more than capable of preying on large animals. Bits of fetid and rotting flesh from its last few meals are stuck in its teeth.
%%%%
+kraken
+
+A gargantuan sea monster, capable of devouring entire ships. It embraces them with its tentacles and draws them into the depths. Fortunately, the tentacles are too big to embrace a small creature like you. Wait, did I say "fortunately"?
+%%%%
large abomination
A large and hideous form, created or summoned by some arcane process.
@@ -1373,6 +1377,11 @@ swamp worm
A large slimy worm, adept at swimming through the muck of this foul swamp.
%%%%
+tentacle
+
+This mighty tentacle is a part of a kraken.
+Any damage dealt to it hurts the whole creature.
+%%%%
tentacled monstrosity
A writhing mass of tentacles, all covered in putrid mucus.
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index 499d0686bf..a8d6a3cf5e 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -539,7 +539,33 @@ void wizard_create_spec_monster_name()
return;
}
- // Need to set a name for the player ghost.
+ if (mspec.mid == MONS_KRAKEN)
+ {
+ unsigned short mid = mgrd(place);
+
+ if (mid >= MAX_MONSTERS || menv[mid].type != MONS_KRAKEN)
+ {
+ for (mid = 0; mid < MAX_MONSTERS; mid++)
+ {
+ if (menv[mid].type == MONS_KRAKEN && menv[mid].alive())
+ {
+ menv[mid].colour = random_choose(GREEN, LIGHTGREY,
+ LIGHTGREEN, LIGHTCYAN,
+ LIGHTRED, YELLOW, WHITE,
+ -1);
+ return;
+ }
+ }
+ }
+ if (mid >= MAX_MONSTERS)
+ {
+ mpr("Couldn't find player kraken!");
+ return;
+ }
+ }
+
+ // FIXME: This is a bit useless, seeing how you cannot set the
+ // ghost's stats, brand or level.
if (mspec.mid == MONS_PLAYER_GHOST)
{
unsigned short mid = mgrd(place);
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index c6ea5a7122..e43b1633d7 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -3515,6 +3515,8 @@ static void _place_aquatic_monsters(int level_number, char level_type)
swimming_things[i] = MONS_MERMAID;
else if (one_chance_in(8))
swimming_things[i] = MONS_SIREN;
+ else if (one_chance_in(20))
+ swimming_things[i] = MONS_KRAKEN;
}
}
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 01e5d8fe5a..cbc4892717 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -1991,9 +1991,10 @@ enum monster_type // (int) menv[].type
MONS_ERESHKIGAL, // 344
MONS_ANCIENT_LICH = 356, // 356
- MONS_OOZE, // 357
-
- MONS_VAULT_GUARD = 360, // 360
+ MONS_OOZE,
+ MONS_KRAKEN,
+ MONS_KRAKEN_TENTACLE,
+ MONS_VAULT_GUARD, // 360
MONS_CURSE_SKULL,
MONS_VAMPIRE_KNIGHT,
MONS_VAMPIRE_MAGE,
@@ -2212,6 +2213,7 @@ enum mon_spellbook_type
MST_GIANT_ORANGE_BRAIN,
MST_RAKSHASA,
MST_GREAT_ORB_OF_EYES, // 55
+ MST_KRAKEN,
MST_ORC_SORCERER,
MST_STEAM_DRAGON,
MST_HELL_KNIGHT_I = 60,
@@ -2880,8 +2882,9 @@ enum spell_type
SPELL_DRACONIAN_BREATH,
SPELL_WATER_ELEMENTALS,
SPELL_PORKALATOR, // 198
+ SPELL_KRAKEN_TENTACLES,
- NUM_SPELLS // 199
+ NUM_SPELLS // 200
};
enum slot_select_mode
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index a4f3c9b3f6..58d4a45813 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -4280,6 +4280,9 @@ std::string melee_attack::mons_attack_verb(const mon_attack_def &attk)
return klown_attack[random2(num_attacks)];
}
+ if (attacker->id() == MONS_KRAKEN_TENTACLE)
+ return "slap";
+
static const char *attack_types[] =
{
"",
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 6181cea8f0..177e235569 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -2697,6 +2697,7 @@ bool mons_is_safe(const monsters *mon, bool want_move,
bool is_safe = (mons_wont_attack(mon)
|| mons_class_flag(mon->type, M_NO_EXP_GAIN)
+ && mon->type != MONS_KRAKEN_TENTACLE
|| mons_is_pacified(mon) && dist > 1
#ifdef WIZARD
// Wizmode skill setting enforces hiddenness.
diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index 03870e5fe3..342ad16205 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -3005,6 +3005,29 @@ static monsterentry mondata[] = {
HT_WATER, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LARGE
},
+ // A kraken and its tentacles get a random colour at creation.
+{
+ MONS_KRAKEN, 'X', DARKGREY, "kraken",
+ M_COLD_BLOOD | M_SPELLCASTER,
+ MR_NO_FLAGS,
+ 1500, 20, MONS_KRAKEN, MONS_KRAKEN, MH_NATURAL, -3,
+ { {AT_BITE, AF_PLAIN, 15}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
+ { 20, 10, 10, 0 },
+ 20, 0, MST_KRAKEN, CE_CLEAN, Z_NOZOMBIE, S_SILENT, I_ANIMAL,
+ HT_WATER, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_HUGE
+},
+
+{
+ MONS_KRAKEN_TENTACLE, 'w', DARKGREY, "tentacle",
+ M_COLD_BLOOD | M_NO_EXP_GAIN,
+ MR_RES_ASPHYX,
+ 0, 10, MONS_KRAKEN_TENTACLE, MONS_KRAKEN_TENTACLE, MH_NATURAL, MAG_IMMUNE,
+ { {AT_HIT, AF_PLAIN, 15}, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
+ { 5, 3, 5, 0 },
+ 5, 7, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_ANIMAL,
+ HT_WATER, 10, DEFAULT_ENERGY, MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LARGE
+},
+
// lava monsters
{
MONS_LAVA_WORM, 'w', RED, "lava worm",
diff --git a/crawl-ref/source/mon-spll.h b/crawl-ref/source/mon-spll.h
index 89da74e873..7a136b8873 100644
--- a/crawl-ref/source/mon-spll.h
+++ b/crawl-ref/source/mon-spll.h
@@ -210,6 +210,17 @@
}
},
+ { MST_KRAKEN,
+ {
+ SPELL_KRAKEN_TENTACLES,
+ SPELL_KRAKEN_TENTACLES,
+ SPELL_NO_SPELL,
+ SPELL_NO_SPELL,
+ SPELL_NO_SPELL,
+ SPELL_NO_SPELL
+ }
+ },
+
{ MST_ORC_SORCERER,
{
SPELL_BOLT_OF_FIRE,
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index b282b504bd..700438cacf 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -2550,6 +2550,9 @@ bool mons_is_summoned(const monsters *m, int *duration, int *summon_type)
// Clones aren't really summoned (though their equipment might be).
case MON_SUMM_CLONE:
+
+ // Nor are body parts.
+ case SPELL_KRAKEN_TENTACLES:
// Some object which was animated, and thus not really summoned.
case MON_SUMM_ANIMATE:
@@ -7443,7 +7446,7 @@ void monsters::apply_enchantment(const mon_enchant &me)
del_ench(ENCH_SUBMERGED);
break;
}
- else if (((type == MONS_ELECTRIC_EEL || type == MONS_LAVA_SNAKE)
+ else if (((type == MONS_ELECTRIC_EEL || type == MONS_LAVA_SNAKE || type == MONS_KRAKEN)
&& (one_chance_in(50) || (mons_near(this)
&& hit_points == max_hit_points
&& !one_chance_in(10))))
@@ -8330,6 +8333,14 @@ void monsters::react_to_damage(int damage, beam_type flavour)
}
}
}
+ else if (type == MONS_KRAKEN_TENTACLE && flavour != BEAM_TORMENT_DAMAGE)
+ {
+ if (!invalid_monster_index(number)
+ && menv[number].type == MONS_KRAKEN)
+ {
+ menv[number].hurt(&you, damage, flavour);
+ }
+ }
}
/////////////////////////////////////////////////////////////////////////
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 8b893680ce..a2136281da 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -1106,6 +1106,11 @@ static int _place_monster_aux(const mgen_data &mg,
// apply it now.
if (mg.colour != BLACK)
menv[id].colour = mg.colour;
+ else if (mg.cls == MONS_KRAKEN)
+ {
+ menv[id].colour = random_choose(GREEN, LIGHTGREEN, LIGHTCYAN,
+ LIGHTBLUE, RED, LIGHTRED, MAGENTA, -1);
+ }
// The return of Boris is now handled in monster_die()...
// not setting this for Boris here allows for multiple Borises
@@ -1810,7 +1815,7 @@ static band_type _choose_band(int mon_type, int power, int &band_size)
case MONS_DUVESSA:
band = BAND_DUVESSA;
band_size = 1;
- break;
+ break;
} // end switch
if (band != BAND_NO_BAND && band_size == 0)
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index efac7c5baa..c5bd78a850 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -1152,6 +1152,8 @@ static void _hogs_to_humans()
// tell them apart anyway.
// On the other hand, hogs which left the level are too far away to be
// affected by the magic of Kirke's death.
+ // FIXME: If another monster was polymorphed into a hog by Kirke's
+ // porkalator spell, they should be handled specially...
int any = 0;
for (int i = 0; i < MAX_MONSTERS; ++i)
@@ -1173,16 +1175,81 @@ static void _hogs_to_humans()
}
}
- if (any==1)
+ if (any == 1)
mpr("No longer under Kirke's spell, the hog turns into a human!");
- else if (any>1)
- mpr("No longer under Kirke's spell, all hogs revert to their human form!");
+ else if (any > 1)
+ {
+ mpr("No longer under Kirke's spell, all hogs revert to their human "
+ "form!");
+ }
// Revert the player as well.
if (you.attribute[ATTR_TRANSFORMATION] == TRAN_PIG)
untransform();
}
+static int _tentacle_too_far(monsters *head, monsters *tentacle)
+{
+ // The Shoals produce no disjoint bodies of water.
+ // If this ever changes, we'd need to check if the head and tentacle
+ // are still in the same pool.
+ // XXX: Actually, using Feawn's Sunlight power you can separate pools...
+ return grid_distance(head->pos(), tentacle->pos()) > LOS_RADIUS;
+}
+
+void mons_relocated(monsters *monster)
+{
+ if (monster->type == MONS_KRAKEN)
+ {
+ int headnum = monster_index(monster);
+
+ if (invalid_monster_index(headnum))
+ return;
+
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *tentacle = &menv[i];
+ if (tentacle->type == MONS_KRAKEN_TENTACLE
+ && (int) tentacle->number == headnum
+ && _tentacle_too_far(monster, tentacle))
+ {
+ monster_die(tentacle, KILL_RESET, -1, true, false);
+ }
+ }
+ }
+ else if (monster->type == MONS_KRAKEN_TENTACLE)
+ {
+ if (invalid_monster_index(monster->number)
+ || menv[monster->number].type!=MONS_KRAKEN
+ || _tentacle_too_far(&menv[monster->number], monster))
+ {
+ monster_die(monster, KILL_RESET, -1, true, false);
+ }
+ }
+}
+
+static int _destroy_tentacles(monsters *head)
+{
+ int tent = 0;
+ int headnum = monster_index(head);
+
+ if (invalid_monster_index(headnum))
+ return 0;
+
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *monster = &menv[i];
+ if (monster->type == MONS_KRAKEN_TENTACLE
+ && (int)monster->number == headnum)
+ {
+ if (mons_near(monster))
+ tent++;
+ monster->hurt(monster, INSTANT_DEATH);
+ }
+ }
+ return tent;
+}
+
int monster_die(monsters *monster, killer_type killer,
int killer_index, bool silent, bool wizard)
{
@@ -1816,6 +1883,14 @@ int monster_die(monsters *monster, killer_type killer,
{
_hogs_to_humans();
}
+ else if (monster->type == MONS_KRAKEN)
+ {
+ if (_destroy_tentacles(monster) && !in_transit)
+ {
+ mpr("The kraken is slain, and its tentacles slide "
+ "back into the water like the carrion they now are.");
+ }
+ }
else if (!mons_is_summoned(monster))
{
if (mons_genus(monster->type) == MONS_MUMMY)
@@ -2427,6 +2502,8 @@ bool monster_blink(monsters *monster, bool quiet)
monster->check_redraw(oldplace);
monster->apply_location_effects(oldplace);
+ mons_relocated(monster);
+
return (true);
}
diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h
index 22619313f9..cfe656ddcc 100644
--- a/crawl-ref/source/monstuff.h
+++ b/crawl-ref/source/monstuff.h
@@ -199,4 +199,6 @@ bool mons_avoids_cloud(const monsters *monster, cloud_type cl_type,
// to another.
bool mons_avoids_cloud(const monsters *monster, int cloud_num,
cloud_type *cl_type = NULL, bool placement = false);
+
+void mons_relocated(monsters *mons);
#endif
diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc
index 9ed3cf2e9c..f7b08a5523 100644
--- a/crawl-ref/source/mstuff2.cc
+++ b/crawl-ref/source/mstuff2.cc
@@ -254,6 +254,48 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
}
return;
+ case SPELL_KRAKEN_TENTACLES:
+ {
+ int kraken_index = monster_index(monster);
+ if (invalid_monster_index(duration))
+ {
+ mpr("Error! Kraken is not a part of the current environment!",
+ MSGCH_ERROR);
+ return;
+ }
+ sumcount2 = random2(9); // up to eight tentacles
+ if (sumcount2 == 0)
+ return;
+
+ for (sumcount = 0; sumcount < MAX_MONSTERS; ++sumcount)
+ if (menv[sumcount].type == MONS_KRAKEN_TENTACLE
+ && (int) menv[sumcount].number == kraken_index)
+ {
+ // Reduce by tentacles already placed.
+ sumcount2--;
+ }
+
+ for (sumcount = sumcount2; sumcount > 0; --sumcount)
+ {
+ // Tentacles aren't really summoned (controlled by spell_cast
+ // being passed to summon_type), so I'm not sure what the
+ // abjuration value (3) is doing there. (jpeg)
+ if (-1 == create_monster(
+ mgen_data(MONS_KRAKEN_TENTACLE, SAME_ATTITUDE(monster),
+ 3, spell_cast, monster->pos(), monster->foe, 0, god,
+ MONS_PROGRAM_BUG, kraken_index, monster->colour,
+ you.your_level, PROX_CLOSE_TO_PLAYER,
+ you.level_type)))
+ {
+ sumcount2--;
+ }
+ }
+ if (sumcount2 == 1)
+ mpr("A tentacle rises from the water!");
+ else if (sumcount2 > 1)
+ mpr("Tentacles burst out of the water!");
+ return;
+ }
case SPELL_FAKE_RAKSHASA_SUMMON:
sumcount2 = (coinflip() ? 2 : 3);
@@ -992,6 +1034,7 @@ void setup_mons_cast(monsters *monster, bolt &pbolt,
case SPELL_CANTRIP:
case SPELL_BERSERKER_RAGE:
case SPELL_WATER_ELEMENTALS:
+ case SPELL_KRAKEN_TENTACLES:
case SPELL_BLINK:
case SPELL_CONTROLLED_BLINK:
return;
@@ -1181,6 +1224,8 @@ void monster_teleport(monsters *monster, bool instan, bool silent)
monster->check_redraw(oldplace);
monster->apply_location_effects(oldplace);
+ mons_relocated(monster);
+
// Teleporting mimics change form - if they reappear out of LOS, they are
// no longer known.
if (mons_is_mimic(monster->type))
diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc
index 463a68c71c..2b192757ff 100644
--- a/crawl-ref/source/output.cc
+++ b/crawl-ref/source/output.cc
@@ -1595,7 +1595,8 @@ void get_monster_pane_info(std::vector<monster_pane_info>& mons)
for (unsigned int i = 0; i < visible.size(); i++)
{
if (Options.target_zero_exp
- || !mons_class_flag( visible[i]->type, M_NO_EXP_GAIN ))
+ || !mons_class_flag( visible[i]->type, M_NO_EXP_GAIN )
+ || visible[i]->type == MONS_KRAKEN_TENTACLE)
{
mons.push_back(monster_pane_info(visible[i]));
}
diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h
index 558e2a52da..155fc7b8c2 100644
--- a/crawl-ref/source/spl-data.h
+++ b/crawl-ref/source/spl-data.h
@@ -2475,6 +2475,19 @@
},
{
+ SPELL_KRAKEN_TENTACLES, "Spawn Tentacles",
+ SPTYP_SUMMONING,
+ SPFLAG_MONSTER,
+ 5,
+ 0,
+ -1, -1,
+ 0,
+ NULL,
+ false,
+ false
+},
+
+{
SPELL_NO_SPELL, "nonexistent spell",
0,
SPFLAG_TESTING,