summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-07-03 17:59:49 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-07-03 17:59:49 +0000
commit718202ee6b6445890db808f4420073e2cce506c2 (patch)
tree2640787ddfef39aca363946a32a05df0ae62c62e
parent589e206de3965240ac0475dcc98b339914ace70b (diff)
downloadcrawl-ref-718202ee6b6445890db808f4420073e2cce506c2.tar.gz
crawl-ref-718202ee6b6445890db808f4420073e2cce506c2.zip
Manual update (David).
Added javelins and sling bullets. Only Urug gets javelins at the moment. No monster gets sling bullets, but they can be randomly generated. Added deep elf blademasters and master archers to provide Silence-users some entertainment on Elf:7. I've adjusted the non-rogue-layout Elf:7s to use blademasters. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1732 c06c8d41-db1a-0410-9941-cceddc491573
-rw-r--r--crawl-ref/docs/crawl_manual.txt164
-rw-r--r--crawl-ref/source/acr.cc7
-rw-r--r--crawl-ref/source/beam.cc1
-rw-r--r--crawl-ref/source/dat/descript.txt52
-rw-r--r--crawl-ref/source/dat/elf.des28
-rw-r--r--crawl-ref/source/debug.cc58
-rw-r--r--crawl-ref/source/describe.cc76
-rw-r--r--crawl-ref/source/direct.cc58
-rw-r--r--crawl-ref/source/dungeon.cc3
-rw-r--r--crawl-ref/source/enum.h14
-rw-r--r--crawl-ref/source/externs.h6
-rw-r--r--crawl-ref/source/fight.cc3
-rw-r--r--crawl-ref/source/initfile.cc5
-rw-r--r--crawl-ref/source/item_use.cc134
-rw-r--r--crawl-ref/source/itemname.cc24
-rw-r--r--crawl-ref/source/itemprop.cc99
-rw-r--r--crawl-ref/source/itemprop.h7
-rw-r--r--crawl-ref/source/items.cc3
-rw-r--r--crawl-ref/source/libutil.cc66
-rw-r--r--crawl-ref/source/libutil.h2
-rw-r--r--crawl-ref/source/makeitem.cc88
-rw-r--r--crawl-ref/source/mon-data.h27
-rw-r--r--crawl-ref/source/mon-pick.cc9
-rw-r--r--crawl-ref/source/mon-util.cc40
-rw-r--r--crawl-ref/source/mon-util.h1
-rw-r--r--crawl-ref/source/monplace.cc5
-rw-r--r--crawl-ref/source/monstuff.cc13
-rw-r--r--crawl-ref/source/mstuff2.cc77
-rw-r--r--crawl-ref/source/mstuff2.h9
-rw-r--r--crawl-ref/source/stuff.cc25
-rw-r--r--crawl-ref/source/stuff.h1
31 files changed, 683 insertions, 422 deletions
diff --git a/crawl-ref/docs/crawl_manual.txt b/crawl-ref/docs/crawl_manual.txt
index 23bc2feb01..b280acfeef 100644
--- a/crawl-ref/docs/crawl_manual.txt
+++ b/crawl-ref/docs/crawl_manual.txt
@@ -259,18 +259,18 @@ are resting, you are assumed to be observing your surroundings, so you
have a chance of detecting any traps or secret doors adjacent to you.
Resting stops if a monster appears.
-Examining:
-----------
+Examining your surroundings:
+----------------------------
The section of the viewing window which is coloured (with the '@'
representing you at the centre) is what you can see around you. The
-dark grey around it is the parts of the level which you have visited,
+dark grey around it is the parts of the level which you have visited,
but cannot currently see. The 'x' command lets you move the cursor
-around to get a description of the various dungeon features, and typing
-'v' when the cursor is over a monster brings up a short description of
-that monster. You can get a map of the whole level (which shows where
-you've already been) by typing the 'X' key. On this map, stairs and
-known traps are specially colour-coded airs and known traps, even if
-something is on top of them.
+around to get a description of the various dungeon features, and
+typing 'v' when the cursor is over a monster or feature brings up a
+short description of that monster. You can get a map of the whole
+level (which shows where you've already been) by typing the 'X' key.
+On this map, stairs and known traps are specially colour-coded, even
+if there's something on top of them.
Staircases and Portals:
-----------------------
@@ -312,30 +312,47 @@ can be disarmed with the control-direction commands.
Shops:
------
-When you are in a shop, you are given a list of the shopkeeper's stock
-from which to choose goods. Unfortunately the shopkeepers all have an
-enterprise bargaining agreement with the dungeon teamsters union which
-prevents them using non-union labour to obtain stock, so you can't sell
-anything in a shop (but what shopkeeper would really buy goods from a
-disreputable adventurer like you, anyway?) In shops you have access to
-your inventory and to your list of already discovered items. You can
-purchase items by pressing the letter of each item in the shop menu; by
-pressing 'v' and then the letter, you can get information about the item
-you are considering purchasing.
+When you visit a shop, you are shown what the shopkeeper has in stock
+and can choose what to buy. Unfortunately the shopkeepers all have an
+exclusive deal with the Guild of Dungeon Procurers which prevents them
+using non-guild labour to obtain stock, so you can't sell anything in
+a shop (but what shopkeeper would buy stolen goods from a disreputable
+adventurer, anyway?)
+
+You can check your inventory and the items you've identified while
+you're shopping, which may help to decide if you really need that
+expensive item.
+
+To purchase an item, press the letter of the item in the shop menu;
+you can examine stuff before you buy it by pressing 'v' and then the
+letter of the item.
+
+If you've lost track of the shops in the dungeon, you can get a list
+of all the shops you've found in the dungeon overview (use 'O').
+
+You can also use the stash search: hitting 'Ctrl-F' and searching for
+"shop" will list all stores. The stash-search menu allows you travel
+quickly to a particular shop; if you just want to know what's in the
+shop, you can examine the shop's inventory from the search menu
+without having to travel all the way to the shop.
+
+Some shops are antique stores that sell items of unknown provenance,
+usually at a good discount. The dungeon overview screen displays these
+with yellow glyphs.
Automated Travel and Exploration:
---------------------------------
-Crawl has an extensive automated travel system: pressing Ctrl-G lets you
-chose any dungeon level; the game will then take the shortest path to
-reach this destination. You can also use autotravel on the level map
-('X'): move the cursor to the place where you want to go and hit Enter.
-There are several shortcuts when choosing destinations: try '<' and '>'
-to quickly reach the staircases.
-
-When your autotravel gets interrupted, Crawl will remember the previous
-destination. Hitting Ctrl-G again and following with Enter puts the
-cursor on that square. See Appendix 4 for all commands and shortcuts in
-level-map mode.
+Crawl has an extensive automated travel system: pressing Ctrl-G lets
+you choose any dungeon level; the game will then take the shortest
+path to reach this destination. You can also use autotravel on the
+level map ('X'): move the cursor to the place where you want to go and
+hit Enter. There are several shortcuts when choosing destinations: try
+'<' and '>' to quickly reach the staircases.
+
+When your autotravel gets interrupted, Crawl will remember the
+previous destination. Hitting Ctrl-G again and following with Enter
+puts the cursor on that square. See Appendix 4 for all commands and
+shortcuts in level-map mode.
Another use of autotravel is exploration: Ctrl-O makes your character
move to the nearest unexplored area. This can be dangerous - do not use
@@ -363,10 +380,11 @@ Furthermore, you can search skills like 'long blades' (this will find
all weapons training the long blades skill) or general terms like
'shop', 'altar', 'portal', 'artefact'.
-The above assumes that use of the default option 'stash_tracking = all'.
-If for some reasons (e.g. to speed up performance) the value has been
-changed, you can press Ctrl-S to tell Crawl that a given square is
-considered a stash. Ctrl-E will manually erase stashes.
+The above assumes that use of the default option 'stash_tracking =
+all'. If for some reasons (e.g. to speed up performance) you've
+changed the value of the stash_tracking option, you can press Ctrl-S
+to tell Crawl that a given square is considered a stash. Ctrl-E will
+manually erase stashes.
The Goal:
---------
@@ -919,7 +937,9 @@ The 'p' command lets you pray to your God. Anything you do while
praying, you do in your God's name - this is how you dedicate your kills
or corpse-sacrifices ('D' command) to your God, for example. Note that
not all gods like this. Praying also gives you a sense of what your God
-thinks of you, and can be used to sacrifice things at altars.
+thinks of you, and can be used to sacrifice things at altars. Not all
+gods need to be addressed by constant prayers - it's the cast mostly for
+gods who delight in kills or corpses.
To use any powers which your God deems you fit for, access the abilities
menu with the 'a' command; God-given abilities are listed as
@@ -932,6 +952,8 @@ temple somewhere near the surface. At an altar, you can read a
description of that god's general attitude by pressing 'p'. You will be
asked afterwards if you really want to attend this circle.
+Note that the good gods will not accept demonic or undead devotees.
+
If you like to start the game with a religion, choose your class from
healer, priest, paladin, berserker, or chaos knight.
@@ -1126,11 +1148,15 @@ It also serves to clearly differentiate the many species, thus providing
replayability, in particular since the class/race combinations are by no
means homogeneous in difficulty. Note that a rough idea about aptitudes
is definitely enough to win, yet players can optimise here, as well. It
-can be said that race differentiation is still not finished - what sets
-the High and Grey Elves really apart? A weak spot of the current skill
-system is 'victory dancing', where characters spend the experience
-accumulated in a big battle with stupid actions (like casting Magic Dart
-at the wall) in order to increase specific skills.
+can be said that race differentiation is still not finished - we try to
+make differentiation better by going beyond aptitudes alone. A weak spot
+of the current skill system is 'victory dancing', where characters spend
+the experience accumulated in a big battle with stupid actions (like
+casting Magic Dart at the wall) in order to increase specific skills.
+While this surely seems dubious, it also allows players to adapt their
+characters anytime during play - e.g. to make a transition from a pure
+melee fighter to a hybrid using enchantments. For this reason, changing
+the experience system is no easy task.
A very important point in Crawl is steering away from nobrainers.
Speaking about games in general, wherever there's a no-brainer, that
@@ -1147,8 +1173,8 @@ items in general. Likewise, there are no sure-fire means of life saving
standings for some deities).
Concerning replayability again, Crawl's dungeon layout was also
-constructed with this in mind: even veteran players will find the Hells
-exciting (which themselves are construed such that life endangering
+constructed with this in mind: even veteran players will find the Tomb or
+the Hells exciting (which are construed such that life endangering
situations can always pop up - this tries to avoid the walking tank
phenomenon). Another strong point is the religous system, because Crawl
allows you to choose gods in the game, regardless of class or race (and
@@ -1156,7 +1182,7 @@ even to switch to other gods).
Likewise, there are many fundamentally different playing styles to
discover (melee oriented fighter, stabber, etc.). There have been even
deliberate design choices that allow meta-styles: For example, Mummies
-do not need to eat and so are principally suited for a infinite play.
+do not need to eat and so are principally suited for an infinite play.
On the precise opposite end, players who prefer to be rewarded for
accepting a more severe "food clock" can play Demigods with their
near-godly stats, or centaurs, with amazing missile skills and the speed
@@ -1172,7 +1198,9 @@ monsters turns up, like a dragon on the second dungeon level. These are
not bugs! They serve as motivation, first of all: in many cases, such a
situation can be survived somehow and the mental bond to the character
will then surely grow. OOD monsters also help to keep more experienced
-players on their toes. The same can be said of uniques.
+players on their toes. The same can be said of uniques. Also, frequent
+and early trips to the Abyss are not deficits: there's more than one way
+out and possibly doing so should be exciting for all characters.
Finally, the interface of Crawl is designed to be understood at a glance
and to support gameplay as far as possible. In particular, it should
@@ -1271,14 +1299,6 @@ many a Mountain Dwarf started career as an elementalist in one of those
schools.
They advance in levels at a similar rate to Grey and Deep elves.
- Mountain Dwarves:
- -----------------
- Mountain dwarves come from the larger, more civilised communities of
- the mountains. They advance slightly more quickly than hill dwarves
- and are almost as robust while having similar aptitudes, but are
- slightly worse at fighting while being slightly better at more
- civilised pursuits.
-
Halflings:
----------
Halflings, who are named for being about half the size of humans, live
@@ -1674,10 +1694,11 @@ with a sword, a shield, a robe, and a healing potion.
Priests:
--------
Priests serve either Zin, the ancient and revered God of Law, or the
-rather less pleasant Death-God Yredelemnul. Although priests enter the
-dungeon with a mace (as well as a priestly robe and a few healing
-potions), this is purely the result of an archaic tradition the reason
-for which has been lost in the mists of time; Priests are not in any way
+rather less pleasant Death-God Yredelemnul. Hill Orcs may choose to
+follow the Orc god Beogh instead. Although priests enter the dungeon
+with a mace (as well as a priestly robe and a few healing potions),
+this is purely the result of an archaic tradition the reason for which
+has been lost in the mists of time; Priests are not in any way
restricted in their choice of weapon skills.
Healers:
@@ -1829,9 +1850,9 @@ with heavy armour or shields or very big weapons.
Ranged combat skills:
---------------------
-Ranged Combat is the basic skill used when throwing or shooting things,
-and there are a number of individual weapon skills for missile weapons
-as well:
+Ranged Combat is the basic skill used when throwing or shooting things.
+It affects in particular how effectively poisoned missiles are. There
+are a number of individual weapon skills for missile weapons as well:
o Darts
o Bows
@@ -1938,6 +1959,7 @@ damage as well as in terms of precision. Furthermore, with high
Evocations, you can easily deduce the number of charges in a wand
through usage. Similarly, all other items that have certain powers
(like crystal balls) work better for characters trained in this skill.
+Like Invocations, Evocations easier to learn than other skills.
If your character does not have a particular skill, s/he can gain it by
practising the activities mentioned above.
@@ -1971,7 +1993,7 @@ Movement:
Ctrl-G Interlevel travel (to arbitrary dungeon levels
or waypoints). Remembers old destinations if
interrupted. This command has its own set of
- shortcuts; use ? to get help on them.
+ shortcuts; use ? for help on them or see below.
Ctrl-O Auto-explore. Setting the option explore_greedy
to true makes Ctrl-O also run to interesting
items (those that get picked up automatically)
@@ -2048,9 +2070,14 @@ Item interaction (inventory):
Item interaction (floor):
d Drop an item.
+ Within the drop list, you can select slots based
+ on a regular expression by pressing Ctrl-F,
+ followed by the regex.
#d Drop exact number of items, where # is any number.
g or , Pick up items; press twice for pick up menu.
- Use a prefix to pick up smaller quantities.
+ Use a prefix to pick up smaller quantities.
+ As with dropping, Ctrl-F allows to pick up items
+ matching a regular expression.
D Dissect a corpse.
Other game-playing commands:
@@ -2132,11 +2159,12 @@ using Ctrl-G. Check the option show_waypoints. The commands are
W Cycle through waypoints.
Travel exclusions mark certain spots of the map as no-go areas for
-autotravel and explore (the radius is set by the option
-travel_exclude_radius2).
+autotravel and explore.
Ctrl-X Set travel exclusion.
Ctrl-E Erase all travel exclusions at once.
X Cycle through travel exclusions.
+ x If on an exclusion centre, changes the radius of
+ the exclusion disc: 8, 4, 1 squares.
Examining surroundings ('x')
----------------------------
@@ -2189,6 +2217,16 @@ work, with the exception of Space (which fires).
Note that target_unshifted_dirs is mutually
exclusive with default_target.
+Interlevel travel menu ('Ctrl-G')
+---------------------------------
+Interlevel travel allows you to specify a destination far away from your
+current place. Crawl will chose the shortest path to move there. Should
+you get interrupted, the old destination is saved and can be reused.
+Other shortcuts are:
+ < Travels to the nearest upstairs.
+ > Travels to the nearest downstairs.
+
+
Shortcuts in lists (like multidrop):
------------------------------------
When dropping (with the drop_mode=multi option), the drop menu accepts
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 527b14104a..74a12e39d7 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -145,9 +145,6 @@ char info[ INFO_SIZE ]; // messaging queue extern'd everywhere {dlb}
int stealth; // externed in view.cc
char use_colour = 1;
-// set to true once a new game starts or an old game loads
-bool game_has_started = false;
-
// Clockwise, around the compass from north (same order as enum RUN_DIR)
const struct coord_def Compass[8] =
{
@@ -3021,7 +3018,7 @@ static bool initialise(void)
// Load macros
macro_init();
- game_has_started = true;
+ crawl_state.need_save = true;
calc_hp();
calc_mp();
@@ -3087,8 +3084,6 @@ static bool initialise(void)
activate_notes(true);
- crawl_state.need_save = true;
-
return (newc);
}
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index 14f844329e..b2d69ba53a 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -2397,6 +2397,7 @@ void beam_drop_object( bolt &beam, item_def *item, int x, int y )
case MI_DART: chance = 3; break;
case MI_ARROW: chance = 4; break;
case MI_BOLT: chance = 4; break;
+ case MI_JAVELIN: chance = 8; break;
case MI_LARGE_ROCK:
default:
diff --git a/crawl-ref/source/dat/descript.txt b/crawl-ref/source/dat/descript.txt
index 66819e86db..efcf7b41b4 100644
--- a/crawl-ref/source/dat/descript.txt
+++ b/crawl-ref/source/dat/descript.txt
@@ -1,6 +1,8 @@
%%%%
program bug
-Program bug
+
+A ravenous and incredibly buggy monster. Please report its existence
+to the DevTeam.
%%%%
giant ant
A black ant with poisonous pincers, about the size of a large dog.
@@ -763,51 +765,75 @@ A very large and strong looking orc.
%%%%
deep elf soldier
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave - a common
+soldier of the deep elves.
%%%%
deep elf fighter
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave - a deep elf
+fighter, equipped with weapon and magic.
%%%%
deep elf knight
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave - a
+battle-hardened deep elf reaver.
%%%%
deep elf mage
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave; this is a
+deep elf wizard of subtle magic.
%%%%
deep elf summoner
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave. This one is
+skilled in the darker aspects of summoning.
%%%%
deep elf conjurer
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave; this is an
+elf well-versed in destructive magic.
%%%%
deep elf priest
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave; this elf is
+a servant of the deep elves' dark god.
%%%%
deep elf high priest
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave; this elf is
+an exalted servant of the deep elves' dark god.
%%%%
deep elf demonologist
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave. A master of
+demonology, this deep elf is marked heavily by long years of contact
+with unnatural demonic forces.
%%%%
deep elf annihilator
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave. This deep
+elf exults in conjured mayhem and destruction.
%%%%
deep elf sorcerer
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave. This mighty
+spellcaster draws power from the very Hells.
%%%%
deep elf death mage
-One of the race of elves which inhabits this dreary cave.
+One of the race of elves which inhabits this dreary cave. This one is
+surrounded by a shimmering aura of negative energy.
+%%%%
+deep elf blademaster
+
+Once in a very great while, there is a deep elf that scorns magic and
+devotes herself to the study of the sword. This is such a champion of
+the deep elves, wielding two blades with lethal grace.
+%%%%
+deep elf master archer
+
+This legendary deep elf archer has devoted her whole life to the
+pursuit of archery, dismissing magic as a frivolous distraction.
%%%%
brown ooze
diff --git a/crawl-ref/source/dat/elf.des b/crawl-ref/source/dat/elf.des
index 536908205c..46900c87e4 100644
--- a/crawl-ref/source/dat/elf.des
+++ b/crawl-ref/source/dat/elf.des
@@ -13,6 +13,7 @@ ORIENT: northwest
FLAGS: no_rotate
MONS: deep elf high priest, deep elf demonologist, deep elf annihilator
MONS: deep elf sorcerer, deep elf death mage
+MONS: deep elf blademaster, deep elf master archer
SUBST: 1=1., 2=2., 3=3., 4=4., 5=5.
SHUFFLE: 23, 45
KMONS: $ = weight:450 nothing / deep elf sorcerer / deep elf annihilator
@@ -37,7 +38,7 @@ xxxxxxxxxxxxxxxxcc*$*|*|*|*|||||c$$ccxxx
xxxxxxxxxxxxxxxcc*$|*$***$$|||||c|$$ccxx
xxxxxxxxxxxxxxcc*$*|**ccccccccccc$$$$ccx
xxxxxxxxxxxxxxc*|*$*$ccc..2..2..c+$|$$cx
-xxxxxxxxxxxxxxc$*$*ccc...........c$$$$cx
+xxxxxxxxxxxxxxc$*$*ccc7........7.c$$$$cx
xxxxxxxxxxxxxxc||**cc...5.5...4.4cc$|$cx
xxxxxxxxxxxxxxc*$$cc..3.....3..ccccccccx
xxxxxxxxxxxxxxc$+ccc.....2....cc.....5cx
@@ -51,7 +52,7 @@ xxxxxxxxxxxxxxc.......2..2....3.......cx
xxxxxxxxxxxxxxc..2................2..5cx
xxxxxxxxxxxxxxc......v.........v..2...cx
xxxxxxxxxxxxxxc..2..vv..1...1..vv.....cx
-xxxxxxxxxxxxxxc2...vvv....1....vvv.4.4cx
+xxxxxxxxxxxxxxc2..6vvv....1....vvv64.4cx
xxxxxxxxxxxxxxc..vvvv...........vvvv..cx
xxxxxxxxxxxxxxc.vvv.....cc.cc.....vvv.cx
xxxxxxxxxxxxxxc.v.3...cccc.cccc.3...v.cx
@@ -74,7 +75,7 @@ ORIENT: float
CHANCE: 5
MONS: deep elf high priest, deep elf demonologist
MONS: deep elf annihilator, deep elf sorcerer
-MONS: deep elf death mage
+MONS: deep elf death mage, deep elf blademaster
SHUFFLE: 345
SUBST: 4=4., 2=2., 5=5., 3=3.
KMONS: $ = weight:115 nothing / deep elf annihilator
@@ -82,6 +83,10 @@ KMONS: * = weight:105 nothing / deep elf death mage
KMONS: | = weight:290 nothing / deep elf sorcerer
SUBST: | = | *:2
SUBST: * = * |:3
+KMONS: A = deep elf master archer
+KFEAT: A = |
+KMONS: B = deep elf blademaster
+KFEAT: B = |
MAP
cccccccccccccccccccccccccc
c2ccccccc........ccccccccc
@@ -96,12 +101,12 @@ c2ccc.c.3.ww..ww.4.ccc...c
c.ccc..3..ww..ww..5......c
c$$$ccc...ww..ww...ccccc.c
c$$$ccc.1.ww..ww.5.ccccc.c
-c$$$cccccccc2.ccccccc122.c
+c$$$cccccccc2.cccccc1262.c
ccc+cccccccc..cccccccccc.c
-c|*|*|**|*cc..cc$|$*$|$|.c
+cB*|*|**|*cc..cc$|$*$|$|.c
c**|*|||||cc..cc|$|$*$*$.c
c*|*|||**|cc..cc$*$*$*$|.c
-c|**|*|||*cc..cc|$|$|$|$2c
+c|**|*|||Acc..ccA$|$|$|$2c
ccccccccccccWWcccccccccccc
ccccccccccccWWcccccccccccc
ccccccccccccWWcccccccccccc
@@ -118,7 +123,8 @@ ORIENT: float
CHANCE: 5
MONS: deep elf high priest, deep elf demonologist
MONS: deep elf annihilator, deep elf sorcerer
-MONS: deep elf death mage
+MONS: deep elf death mage, deep elf master archer
+MONS: deep elf blademaster
SHUFFLE: 345
SUBST: 4=4., 2=2., 5=5., 3=3.
SUBST: l=lw
@@ -132,11 +138,13 @@ KMONS: * = weight:105 nothing / deep elf death mage
KMONS: | = weight:290 nothing / deep elf sorcerer
SUBST: | = | *:2
SUBST: * = * |:3
+KITEM: 6 = |
+KITEM: 7 = |
MAP
ccccccccccccccccccccccccc
c2ccccccc.......ccccccccc
c.cccc2...5...5...2cccccc
-c.cc.................cccc
+c.cc.................cc7c
c.c....3....45...3....c3c
c.+.........U.........+.c
c.c......2.2.2.2......c.c
@@ -148,10 +156,10 @@ c$$$ccc...ww.ww...ccccc.c
c$$$ccc.1.ww.ww.5.ccccc.c
c$$$cccccccc.ccccccc122.c
ccc+cccccccc2cccccccccc.c
-c|*|*|**|*cc.cc$|$*$|$|.c
+c7*|*|**|*cc.cc$|$*$|$|.c
c**|*|||||cc.cc|$|$*$*$.c
c||*||***|cc.cc$*$*$*$|.c
-c|**|*|||*cc.cc|$|$|$|$2c
+c|**|*||6*cc.cc6$|$|$|$2c
cccccccccccclcccccccccccc
ccccccccccclllccccccccccc
cccccccccc..c..cccccccccc
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index 35847b7c6a..da8a8a77ad 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -45,6 +45,7 @@
#include "fight.h"
#include "files.h"
#include "invent.h"
+#include "it_use2.h"
#include "itemname.h"
#include "itemprop.h"
#include "item_use.h"
@@ -1615,8 +1616,7 @@ static bool debug_fight_simulate(FILE *out, int wskill, int mi, int miss_slot)
int weapon = you.equip[EQ_WEAPON];
const item_def *iweap = weapon != -1? &you.inv[weapon] : NULL;
- if (iweap && iweap->base_type == OBJ_WEAPONS
- && is_range_weapon(*iweap))
+ if (iweap && iweap->base_type == OBJ_WEAPONS && is_range_weapon(*iweap))
return fsim_ranged_combat(out, wskill, mi, iweap, miss_slot);
else
return fsim_melee_combat(out, wskill, mi, iweap);
@@ -1644,19 +1644,24 @@ static std::string fsim_wskill()
static std::string fsim_weapon(int missile_slot)
{
std::string item_buf;
- if (you.equip[EQ_WEAPON] != -1)
+ if (you.equip[EQ_WEAPON] != -1 || missile_slot != -1)
{
- const item_def &weapon = you.inv[ you.equip[EQ_WEAPON] ];
- item_buf = weapon.name(DESC_PLAIN, true);
-
- if (is_range_weapon(weapon))
+ if (you.equip[EQ_WEAPON] != -1)
{
- const int missile =
- missile_slot == -1? get_fire_item_index() :
- missile_slot;
- if (missile < ENDOFPACK)
- return item_buf+" with "+you.inv[missile].name(DESC_PLAIN);
+ const item_def &weapon = you.inv[ you.equip[EQ_WEAPON] ];
+ item_buf = weapon.name(DESC_PLAIN, true);
+ if (is_range_weapon(weapon))
+ {
+ const int missile =
+ missile_slot == -1? get_fire_item_index() :
+ missile_slot;
+ if (missile < ENDOFPACK)
+ return item_buf + " with "
+ + you.inv[missile].name(DESC_PLAIN);
+ }
}
+ else
+ return you.inv[missile_slot].name(DESC_PLAIN);
}
else
{
@@ -1880,22 +1885,31 @@ int fsim_kit_equip(const std::string &kit)
trim_string(missile);
}
- for (int i = 0; i < ENDOFPACK; ++i)
+ if (!weapon.empty())
{
- if (!is_valid_item(you.inv[i]))
- continue;
-
- if (you.inv[i].name(DESC_PLAIN).find(weapon) != std::string::npos)
+ for (int i = 0; i < ENDOFPACK; ++i)
{
- if (i != you.equip[EQ_WEAPON])
+ if (!is_valid_item(you.inv[i]))
+ continue;
+
+ if (you.inv[i].name(DESC_PLAIN).find(weapon) != std::string::npos)
{
- wield_weapon(true, i, false);
if (i != you.equip[EQ_WEAPON])
- return -100;
+ {
+ wield_weapon(true, i, false);
+ if (i != you.equip[EQ_WEAPON])
+ return -100;
+ }
+ break;
}
- break;
}
}
+ else if (you.equip[EQ_WEAPON] != -1)
+ {
+ unwield_item(you.equip[EQ_WEAPON], false);
+ if (you.equip[EQ_WEAPON] != -1)
+ return (-100);
+ }
if (!missile.empty())
{
@@ -1904,7 +1918,7 @@ int fsim_kit_equip(const std::string &kit)
if (!is_valid_item(you.inv[i]))
continue;
- if (you.inv[i].name(DESC_PLAIN).find(missile)!=std::string::npos)
+ if (you.inv[i].name(DESC_PLAIN).find(missile) != std::string::npos)
{
missile_slot = i;
break;
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 5fe62d7f5c..b24bb605e5 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -88,7 +88,7 @@ static void print_description( const std::string &d )
std::string::size_type nextLine = std::string::npos;
unsigned int currentPos = 0;
- const unsigned int lineWidth = 70;
+ const unsigned int lineWidth = get_number_of_cols() - 1;
textcolor(LIGHTGREY);
@@ -1161,7 +1161,7 @@ static std::string describe_weapon( const item_def &item, bool verbose)
case SPWPN_VAMPIRICISM:
description += "It inflicts no extra harm, "
"but heals its wielder somewhat when "
- "he or she strikes a living foe. ";
+ "it strikes a living foe. ";
break;
case SPWPN_DISRUPTION:
description += "It is a weapon blessed by Zin, "
@@ -1345,13 +1345,14 @@ static std::string describe_ammo( const item_def &item )
switch (item.sub_type)
{
case MI_STONE:
- description += "A stone. It can be thrown by hand or fired with a sling. ";
+ description += "A stone. It can be thrown by hand "
+ "or fired with a sling. ";
break;
case MI_ARROW:
description += "An arrow, to be shot with a bow. ";
break;
case MI_NEEDLE:
- description += "A needle. It can be thrown by hand or fired with a blowgun. ";
+ description += "A needle. It can be fired with a blowgun. ";
break;
case MI_BOLT:
description += "A crossbow bolt. ";
@@ -1362,10 +1363,19 @@ static std::string describe_ammo( const item_def &item )
case MI_LARGE_ROCK:
description += "A rock, used by giants as a missile. ";
break;
+ case MI_SLING_BULLET:
+ description += "A small heavy projectile made of lead. "
+ "It can be fired with a sling, or thrown by hand.";
+ break;
+ case MI_JAVELIN:
+ description += "A long, light polearm that can be thrown by hand. ";
+ if (!is_throwable(item, you.body_size()))
+ description += "Unfortunately, it is too long and awkward "
+ "for you to use.";
+ break;
case MI_NONE: // was eggplant
description += "A purple vegetable. "
- "The presence of this object in the game "
- "indicates a bug (or some kind of cheating on your part). ";
+ "The presence of this object in the game indicates a bug. ";
break;
default:
DEBUGSTR("Unknown ammo type");
@@ -4729,60 +4739,6 @@ void describe_monsters(monsters& mons)
description << "It has come for your soul!";
break;
- case MONS_DEEP_ELF_SOLDIER:
- description << "This one is just a common soldier.";
- break;
-
- case MONS_DEEP_ELF_FIGHTER:
- description << "This soldier has learned some magic.";
- break;
-
- case MONS_DEEP_ELF_KNIGHT:
- description << "This one bears the scars of battles past.";
- break;
-
- case MONS_DEEP_ELF_MAGE:
- description << "Mana crackles between this one's long fingers.";
- break;
-
- case MONS_DEEP_ELF_SUMMONER:
- description << "This one is a mage specialized in the ancient "
- "art of summoning servants of destruction.";
- break;
-
- case MONS_DEEP_ELF_CONJURER:
- description << "This one is a mage specialized in the ancient "
- "art of hurling energies of destruction.";
- break;
-
- case MONS_DEEP_ELF_PRIEST:
- description << "This one is a servant of the deep elves' god.";
- break;
-
- case MONS_DEEP_ELF_HIGH_PRIEST:
- description <<
- "This one is an exalted servant of the deep elves' god.";
- break;
-
- case MONS_DEEP_ELF_DEMONOLOGIST:
- description <<
- "This mage specialized in demonology, and is marked heavily "
- "from long years in contact with unnatural demonic forces.";
- break;
-
- case MONS_DEEP_ELF_ANNIHILATOR:
- description << "This one likes destructive magics more than most, "
- "and is better at them.";
- break;
-
- case MONS_DEEP_ELF_SORCERER:
- description << "This mighty spellcaster draws power from Hell.";
- break;
-
- case MONS_DEEP_ELF_DEATH_MAGE:
- description << "A strong negative aura surrounds this one.";
- break;
-
case MONS_ELF:
// These are only possible from polymorphing or shapeshifting.
description << "This one is remarkably plain looking.";
diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc
index 1f4529fefc..ab8e845a92 100644
--- a/crawl-ref/source/direct.cc
+++ b/crawl-ref/source/direct.cc
@@ -1539,6 +1539,43 @@ static void describe_mons_enchantment(const monsters &mons,
mpr(msg.c_str());
}
+static void describe_monster_weapon(monsters *mons)
+{
+ std::string name1, name2;
+ const item_def *weap = mons->mslot_item(MSLOT_WEAPON),
+ *alt = mons->mslot_item(MSLOT_ALT_WEAPON);
+
+ if (weap)
+ name1 = weap->name(DESC_NOCAP_A);
+ if (alt && (!weap || mons_wields_two_weapons(mons)))
+ name2 = alt->name(DESC_NOCAP_A);
+
+ if (name1.empty() && !name2.empty())
+ name1.swap(name2);
+
+ if (name1 == name2 && weap)
+ {
+ item_def dup = *weap;
+ ++dup.quantity;
+ name1 = dup.name(DESC_NOCAP_A, false, false, true, true);
+ name2.clear();
+ }
+
+ if (name1.empty())
+ return;
+
+ std::ostringstream msg;
+ msg << mons_pronoun( mons->type, PRONOUN_CAP )
+ << " is wielding " << name1;
+
+ if (!name2.empty())
+ msg << " and " << name2;
+
+ msg << ".";
+
+ mpr(msg.str().c_str());
+}
+
static void describe_cell(int mx, int my)
{
bool mimic_item = false;
@@ -1563,28 +1600,11 @@ static void describe_cell(int mx, int my)
goto look_clouds;
#endif
- const int mon_wep = menv[i].inv[MSLOT_WEAPON];
const int mon_arm = menv[i].inv[MSLOT_ARMOUR];
mprf("%s.", str_monam(menv[i], DESC_CAP_A).c_str());
- if (menv[i].type != MONS_DANCING_WEAPON && mon_wep != NON_ITEM)
- {
- std::ostringstream msg;
- msg << mons_pronoun( menv[i].type, PRONOUN_CAP )
- << " is wielding "
- << mitm[mon_wep].name(DESC_NOCAP_A);
-
- // 2-headed ogres can wield 2 weapons
- if ((menv[i].type == MONS_TWO_HEADED_OGRE
- || menv[i].type == MONS_ETTIN)
- && menv[i].inv[MSLOT_ALT_WEAPON] != NON_ITEM)
- {
- msg << " and "
- << mitm[menv[i].inv[MSLOT_ALT_WEAPON]].name(DESC_NOCAP_A);
- }
- msg << ".";
- mpr(msg.str().c_str());
- }
+ if (menv[i].type != MONS_DANCING_WEAPON)
+ describe_monster_weapon(&menv[i]);
if (mon_arm != NON_ITEM)
mprf("%s is wearing %s.",
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index e45a663084..a63d6bb145 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -246,7 +246,7 @@ static void place_altars()
void builder(int level_number, int level_type)
{
// N tries to build the level, after which we bail with a capital B.
- int tries = 10;
+ int tries = 20;
while (tries-- > 0)
{
dgn_level_vetoed = false;
@@ -256,6 +256,7 @@ void builder(int level_number, int level_type)
return;
}
+ // Failed to build level, bail out.
save_game(true,
make_stringf("Unable to generate level for '%s'!",
level_id::current().describe().c_str()).c_str());
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index af60786a08..fe69a86ab6 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -1310,6 +1310,7 @@ enum fire_type
FIRE_DART,
FIRE_STONE,
FIRE_DAGGER,
+ FIRE_JAVELIN,
FIRE_SPEAR,
FIRE_HAND_AXE,
FIRE_CLUB,
@@ -1873,7 +1874,9 @@ enum missile_type
MI_BOLT,
MI_DART,
MI_NEEDLE,
- MI_LARGE_ROCK, //jmf: it'd be nice to move MI_LARGE_ROCK to DEBRIS_ROCK
+ MI_LARGE_ROCK,
+ MI_SLING_BULLET,
+ MI_JAVELIN,
NUM_MISSILES,
MI_NONE // was MI_EGGPLANT... used for launch type detection
};
@@ -1909,6 +1912,8 @@ enum mons_class_flags
M_UNIQUE = (1<<21), // monster is a unique
M_ACID_SPLASH = (1<<22), // Passive acid splash when hit.
+ M_ARCHER = (1<<23), // gets various archery boosts
+
M_SPECIAL_ABILITY = (1<<26), // XXX: eventually make these spells?
M_NO_SKELETON = (1<<29), // boneless corpses
@@ -2260,6 +2265,9 @@ enum monster_type // (int) menv[].type
MONS_MURRAY,
MONS_TIAMAT,
+ MONS_DEEP_ELF_BLADEMASTER,
+ MONS_DEEP_ELF_MASTER_ARCHER,
+
// The Lords of Hell:
MONS_GERYON = 340, // 340
MONS_DISPATER,
@@ -2372,7 +2380,9 @@ enum mon_attack_type
AT_ENGULF,
AT_CLAW,
AT_TAIL_SLAP,
- AT_BUTT
+ AT_BUTT,
+
+ AT_SHOOT // attack representing missile damage for M_ARCHER
};
enum mon_attack_flavour
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index a87304947d..4a5f2ada90 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -516,7 +516,8 @@ public:
std::string name(description_level_type descrip,
bool terse = false, bool ident = false,
- bool with_inscription = true ) const;
+ bool with_inscription = true,
+ bool quantity_in_words = false) const;
bool has_spells() const;
bool cursed() const;
int book_number() const;
@@ -1063,8 +1064,9 @@ public:
item_def *weapon(int which_attack = -1);
item_def *launcher();
item_def *missiles();
- int missile_count();
item_def *shield();
+
+ int missile_count();
void wield_melee_weapon(int near = -1);
void swap_weapons(int near = -1);
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index a8ba7d74a3..9c020368d4 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -3034,6 +3034,9 @@ void melee_attack::mons_perform_attack_rounds()
if (attk.type == AT_NONE)
break;
+ if (attk.type == AT_SHOOT)
+ continue;
+
damage_done = 0;
mons_set_weapon(attk);
to_hit = mons_to_hit();
diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc
index 0b4c918362..73b10fc005 100644
--- a/crawl-ref/source/initfile.cc
+++ b/crawl-ref/source/initfile.cc
@@ -716,8 +716,9 @@ void game_options::reset_options()
fire_order[i] = FIRE_NONE;
fire_order[0] = FIRE_LAUNCHER; // fire first from bow...
- fire_order[1] = FIRE_DART; // then only consider darts
- fire_order[2] = FIRE_STONE; // and then chuck stones
+ fire_order[1] = FIRE_JAVELIN;
+ fire_order[2] = FIRE_DART; // then only consider darts
+ fire_order[3] = FIRE_STONE; // and then chuck stones
item_stack_summary_minimum = 5;
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 2504377b1a..b2877bda6c 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -1134,7 +1134,7 @@ static int try_finding_throwing_weapon( int sub_type )
}
// Return index of first missile of sub_type or ENDOFPACK
-static int try_finding_missile( int sub_type )
+static int try_finding_missile( int sub_type, const item_def *launcher = NULL )
{
int i;
@@ -1144,15 +1144,20 @@ static int try_finding_missile( int sub_type )
if (!is_valid_item( you.inv[i] ))
continue;
+ const item_def &item = you.inv[i];
// In theory, we should do two passes, first trying
// to find a non-warning-inscribed item, then looping
// through the warning-inscribed ones. Seems unlikely
// to matter much.
+ if (launcher && item.launched_by(*launcher))
+ break;
+
// consider melee weapons that can also be thrown
- if (you.inv[i].base_type == OBJ_MISSILES &&
- you.inv[i].sub_type == sub_type &&
- check_warning_inscriptions(you.inv[i], OPER_FIRE))
+ if (item.base_type == OBJ_MISSILES
+ && (item.sub_type == sub_type
+ || (sub_type == MI_STONE && item.sub_type == MI_SLING_BULLET))
+ && check_warning_inscriptions(you.inv[i], OPER_FIRE))
{
break;
}
@@ -1180,8 +1185,7 @@ int get_fire_item_index( void )
&& you.inv[ weapon ].base_type == OBJ_WEAPONS
&& is_range_weapon( you.inv[ weapon ] ))
{
- int type_wanted = fires_ammo_type( you.inv[ weapon ] );
- item = try_finding_missile( type_wanted );
+ item = try_finding_missile( MI_NONE, &you.inv[weapon] );
}
break;
@@ -1193,6 +1197,10 @@ int get_fire_item_index( void )
item = try_finding_missile( MI_STONE );
break;
+ case FIRE_JAVELIN:
+ item = try_finding_missile( MI_JAVELIN );
+ break;
+
case FIRE_DAGGER:
item = try_finding_throwing_weapon( WPN_DAGGER );
break;
@@ -1352,6 +1360,33 @@ static bool determines_ammo_brand(int bow_brand, int ammo_brand)
return true;
}
+static int stat_adjust(int value, int stat, int statbase,
+ const int maxmult = 160, const int minmult = 40)
+{
+ int multiplier = (statbase + (stat - statbase) / 2) * 100 / statbase;
+ if (multiplier > maxmult)
+ multiplier = maxmult;
+ else if (multiplier < minmult)
+ multiplier = minmult;
+
+ if (multiplier > 100)
+ value = value * (100 + random2avg(multiplier - 100, 2)) / 100;
+ else if (multiplier < 100)
+ value = value * (100 - random2avg(100 - multiplier, 2)) / 100;
+
+ return (value);
+}
+
+static int str_adjust_thrown_damage(int dam)
+{
+ return stat_adjust(dam, you.strength, 15, 160, 90);
+}
+
+static int dex_adjust_thrown_tohit(int hit)
+{
+ return stat_adjust(hit, you.dex, 13, 160, 90);
+}
+
// throw_it - currently handles player throwing only. Monster
// throwing is handled in mstuff2:mons_throw()
// Note: If teleport is true, assume that pbolt is already set up,
@@ -1372,8 +1407,6 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
int exHitBonus = 0, exDamBonus = 0; // 'extra' bonus from skill/dex/str
int effSkill = 0; // effective launcher skill
int dice_mult = 100;
- bool launched = false; // item is launched
- bool thrown = false; // item is sensible thrown item
bool returning = false; // item will return to pack
int slayDam = 0;
@@ -1436,7 +1469,7 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
case OBJ_BOOKS: pbolt.type = SYM_OBJECT; break;
// this does not seem right, but value was 11 {dlb}
// notice how the .type does not match the class -- hmmm... {dlb}
- case OBJ_STAVES: pbolt.type = SYM_CHUNK; break;
+ case OBJ_STAVES: pbolt.type = SYM_STICK; break;
default: break;
}
@@ -1484,10 +1517,10 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
}
// figure out if we're thrown or launched
- throw_type(lnchClass, lnchType, wepClass, wepType, launched, thrown);
-
+ const launch_retval projected = is_launched(&you, you.weapon(), item);
+
// extract launcher bonuses due to magic
- if (launched)
+ if (projected == LRET_LAUNCHED)
{
lnchHitBonus = you.inv[you.equip[EQ_WEAPON]].plus;
lnchDamBonus = you.inv[you.equip[EQ_WEAPON]].plus2;
@@ -1498,7 +1531,7 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
ammoDamBonus = item.plus2;
// CALCULATIONS FOR LAUNCHED WEAPONS
- if (launched)
+ if (projected == LRET_LAUNCHED)
{
const item_def &launcher = you.inv[you.equip[EQ_WEAPON]];
const int bow_brand = get_weapon_brand( launcher );
@@ -1612,7 +1645,11 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
// Slings are really easy to learn because they're not
// really all that good, and its harder to get ammo anyways.
exercise(SK_SLINGS, 1 + random2avg(3, 2));
- baseHit += 0;
+
+ // Sling bullets are designed for slinging and easier to aim.
+ if (wepType == MI_SLING_BULLET)
+ baseHit += 4;
+
exHitBonus += (effSkill * 3) / 2;
// strength is good if you're using a nice sling.
@@ -1792,14 +1829,13 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
more();
you.wield_change = true;
}
-
}
// CALCULATIONS FOR THROWN WEAPONS
- if (thrown)
+ if (projected == LRET_THROWN)
{
returning = (get_weapon_brand(item) == SPWPN_RETURNING &&
- !one_chance_in(you.skills[SK_RANGED_COMBAT]+1));
+ !one_chance_in(1 + skill_bump(SK_RANGED_COMBAT)));
baseHit = 0;
// since darts/rocks are missiles, they only use inv_plus
@@ -1808,7 +1844,8 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
// all weapons that use 'throwing' go here..
if (wepClass == OBJ_WEAPONS
- || (wepClass == OBJ_MISSILES && wepType == MI_STONE))
+ || (wepClass == OBJ_MISSILES
+ && (wepType == MI_STONE || wepType == MI_SLING_BULLET)))
{
// elves with elven weapons
if (get_equip_race(item) == ISFLAG_ELVEN
@@ -1848,26 +1885,51 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
exDamBonus = (exDamBonus * (3 * baseDam + ammoDamBonus)) / 30;
}
- if (wepClass == OBJ_MISSILES && wepType == MI_DART)
+ if (wepClass == OBJ_MISSILES)
{
- // give an appropriate 'tohit' & damage
- baseHit = 2;
- baseDam = property( item, PWPN_DAMAGE );
-
- exHitBonus = you.skills[SK_DARTS] * 2;
- exHitBonus += (you.skills[SK_RANGED_COMBAT] * 2) / 3;
- exDamBonus = you.skills[SK_DARTS] / 3;
- exDamBonus += you.skills[SK_RANGED_COMBAT] / 5;
-
- // exercise skills
- exercise(SK_DARTS, 1 + random2avg(3, 2));
+ switch (wepType)
+ {
+ case MI_DART:
+ // give an appropriate 'tohit' & damage
+ baseHit = 2;
+ baseDam = property( item, PWPN_DAMAGE );
+
+ exHitBonus = you.skills[SK_DARTS] * 2;
+ exHitBonus += (you.skills[SK_RANGED_COMBAT] * 2) / 3;
+ exDamBonus = you.skills[SK_DARTS] / 3;
+ exDamBonus += you.skills[SK_RANGED_COMBAT] / 5;
+
+ // exercise skills
+ exercise(SK_DARTS, 1 + random2avg(3, 2));
+ break;
+ case MI_JAVELIN:
+ // Javelins use polearm and throwing skills.
+ baseHit = -1;
+ baseDam = property( item, PWPN_DAMAGE );
+ exHitBonus += (skill_bump(SK_RANGED_COMBAT) * 3
+ + skill_bump(SK_POLEARMS));
+ exDamBonus += you.skills[SK_RANGED_COMBAT] / 5;
+ exDamBonus += you.skills[SK_POLEARMS] * 2 / 5;
+
+ // Adjust for strength and dex.
+ exDamBonus = str_adjust_thrown_damage(exDamBonus);
+ exHitBonus = dex_adjust_thrown_tohit(exHitBonus);
+
+ // High dex helps damage a bit, too (aim for weak spots).
+ exDamBonus = stat_adjust(exDamBonus, you.dex, 20, 150, 100);
+
+ // exercise skills
+ exercise(SK_POLEARMS, 1 + random2avg(4, 2));
+ break;
+ }
}
// [dshaligram] The defined base damage applies only when used
// for launchers. Hand-thrown stones and darts do only half
// base damage. Yet another evil 4.0ism.
if (wepClass == OBJ_MISSILES
- && (wepType == MI_DART || wepType == MI_STONE))
+ && (wepType == MI_DART || wepType == MI_STONE
+ || wepType == MI_SLING_BULLET))
baseDam = div_rand_round(baseDam, 2);
// exercise skill
@@ -1886,7 +1948,7 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
}
// range, dexterity bonus, possible skill increase for silly throwing
- if (thrown || launched)
+ if (projected)
{
if (wepType == MI_LARGE_ROCK)
{
@@ -1904,7 +1966,7 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
exHitBonus += you.dex / 2;
// slaying bonuses
- if (!(launched && wepType == MI_NEEDLE))
+ if (projected != LRET_LAUNCHED || wepType != MI_NEEDLE)
{
slayDam = slaying_bonus(PWPN_DAMAGE);
slayDam = slayDam < 0? -random2(1 - slayDam)
@@ -1961,7 +2023,7 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
pbolt.damage.size += slayDam;
// only add bonuses if we're throwing something sensible
- if (thrown || launched || wepClass == OBJ_WEAPONS)
+ if (projected || wepClass == OBJ_WEAPONS)
{
pbolt.hit += ammoHitBonus + lnchHitBonus;
pbolt.damage.size += ammoDamBonus + lnchDamBonus;
@@ -1981,7 +2043,7 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
#endif
// create message
- mprf( "You %s %s.", launched ? "shoot" : "throw",
+ mprf( "You %s %s.", projected == LRET_LAUNCHED ? "shoot" : "throw",
item.name(DESC_NOCAP_A).c_str() );
// ensure we're firing a 'missile'-type beam
@@ -2014,7 +2076,7 @@ bool throw_it(struct bolt &pbolt, int throw_2, bool teleport, int acc_bonus)
dec_inv_item_quantity( throw_2, 1 );
// throwing and blowguns are silent
- if (launched && lnchType != WPN_BLOWGUN)
+ if (projected == LRET_LAUNCHED && lnchType != WPN_BLOWGUN)
noisy( 6, you.x_pos, you.y_pos );
// but any monster nearby can see that something has been thrown:
diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc
index ca7c19769d..2c4ede1ca4 100644
--- a/crawl-ref/source/itemname.cc
+++ b/crawl-ref/source/itemname.cc
@@ -85,7 +85,8 @@ std::string quant_name( const item_def &item, int quant,
// item buffer will be used.
std::string item_def::name(description_level_type descrip,
bool terse, bool ident,
- bool with_inscription) const
+ bool with_inscription,
+ bool quantity_words) const
{
std::ostringstream buff;
@@ -156,7 +157,12 @@ std::string item_def::name(description_level_type descrip,
}
if (descrip != DESC_BASENAME && descrip != DESC_QUALNAME)
- buff << this->quantity << " ";
+ {
+ if (quantity_words)
+ buff << number_in_words(this->quantity) << " ";
+ else
+ buff << this->quantity << " ";
+ }
}
else
{
@@ -1049,18 +1055,8 @@ std::string item_def::name_aux( description_level_type desc,
buff << ' ';
}
- buff << racial_description_string(*this, terse);
-
- switch (item_typ)
- {
- case MI_STONE: buff << "stone"; break;
- case MI_ARROW: buff << "arrow"; break;
- case MI_BOLT: buff << "bolt"; break;
- case MI_DART: buff << "dart"; break;
- case MI_NEEDLE: buff << "needle"; break;
- case MI_LARGE_ROCK: buff << "large rock" ; break;
- default: buff << "hysterical raisin"; break;
- }
+ buff << racial_description_string(*this, terse)
+ << ammo_name(static_cast<missile_type>(item_typ));
if (know_type && !basename)
{
diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc
index adf555ae1e..81a45b94b5 100644
--- a/crawl-ref/source/itemprop.cc
+++ b/crawl-ref/source/itemprop.cc
@@ -343,12 +343,14 @@ struct missile_def
static int Missile_index[NUM_MISSILES];
static missile_def Missile_prop[NUM_MISSILES] =
{
- { MI_NEEDLE, "needle", 0, 1, false },
- { MI_STONE, "stone", 4, 2, true },
- { MI_DART, "dart", 5, 3, true },
- { MI_ARROW, "arrow", 6, 5, false },
- { MI_BOLT, "bolt", 8, 5, false },
- { MI_LARGE_ROCK, "large rock", 20, 1000, true },
+ { MI_NEEDLE, "needle", 0, 1, false },
+ { MI_STONE, "stone", 4, 2, true },
+ { MI_DART, "dart", 5, 3, true },
+ { MI_ARROW, "arrow", 7, 5, false },
+ { MI_BOLT, "bolt", 9, 5, false },
+ { MI_LARGE_ROCK, "large rock", 20, 1000, true },
+ { MI_SLING_BULLET, "sling bullet", 6, 4, true },
+ { MI_JAVELIN, "javelin", 11, 40, true },
};
struct food_def
@@ -1694,6 +1696,21 @@ bool is_range_weapon( const item_def &item )
return (fires_ammo_type( item ) != MI_NONE);
}
+// decide if something is launched or thrown
+launch_retval is_launched(actor *actor, const item_def *launcher,
+ const item_def &missile)
+{
+ if (missile.base_type == OBJ_MISSILES
+ && launcher
+ && missile.launched_by(*launcher))
+ {
+ return (LRET_LAUNCHED);
+ }
+
+ return (is_throwable(missile, actor->body_size())?
+ LRET_THROWN : LRET_FUMBLED);
+}
+
bool is_range_weapon_type( weapon_type wtype )
{
item_def wpn;
@@ -1704,78 +1721,32 @@ bool is_range_weapon_type( weapon_type wtype )
return (is_range_weapon( wpn ));
}
+const char *ammo_name(missile_type ammo)
+{
+ return (ammo < 0 || ammo >= NUM_MISSILES? "eggplant"
+ : Missile_prop[ Missile_index[ammo] ].name);
+}
+
const char * ammo_name( const item_def &bow )
{
ASSERT( is_range_weapon( bow ) );
-
- const int ammo = fires_ammo_type( bow );
-
- return (Missile_prop[ Missile_index[ammo] ].name);
+ return ammo_name(fires_ammo_type( bow ));
}
// returns true if item can be reasonably thrown without a launcher
-bool is_throwable( const item_def &wpn )
+bool is_throwable( const item_def &wpn, size_type bodysize )
{
if (wpn.base_type == OBJ_WEAPONS)
return (Weapon_prop[ Weapon_index[wpn.sub_type] ].throwable);
else if (wpn.base_type == OBJ_MISSILES)
- return (Missile_prop[ Missile_index[wpn.sub_type] ].throwable);
-
- return (false);
-}
-
-// FIXME
-#if 0
-// decide if "being" is launching or throwing "ammo"
-launch_retval is_launched( int being_id, const item_def &ammo, bool msg )
-{
- ASSERT( being_id != MHITNOT );
-
- launch_retval ret = LRET_FUMBLED;
-
- const item_def * lnch = 0;
- int fit = 0;
-
- if (being_id == MHITYOU)
- {
- const int wpn = get_inv_wielded();
- lnch = (wpn == -1) ? 0 : &you.inv[wpn];
- fit = fit_item_throwable_size( ammo, player_size() );
- }
- else // monster case
{
- const int wpn = menv[being_id].inv[MSLOT_WEAPON];
- lnch = (wpn == NON_ITEM) ? 0 : &mitm[wpn];
- fit = fit_item_throwable_size( ammo, mons_size( menv[being_id].type ) );
- }
-
- if (lnch
- && lnch->base_type == OBJ_WEAPONS
- && is_range_weapon( *lnch )
- && ammo.base_type == OBJ_MISSILES
- && ammo.sub_type == fires_ammo_type( *lnch ))
- {
- ret = LRET_LAUNCHED;
- }
- else if (is_throwable( ammo ))
- {
- if (fit == 0)
- ret = LRET_THROWN;
- else
- {
- ret = LRET_FUMBLED;
-
- if (being_id == MHITYOU && msg)
- {
- mpr( MSGCH_WARN, "It's difficult to throw such a%s object.",
- (fit > 0) ? " large" : (fit < 0) ? " small" : "n awkward" );
- }
- }
+ if (bodysize < SIZE_MEDIUM && wpn.sub_type == MI_JAVELIN)
+ return (false);
+ return (Missile_prop[ Missile_index[wpn.sub_type] ].throwable);
}
- return (ret);
+ return (false);
}
-#endif
//
// Staff/rod functions:
diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h
index 48f3c53796..002cf22293 100644
--- a/crawl-ref/source/itemprop.h
+++ b/crawl-ref/source/itemprop.h
@@ -120,8 +120,11 @@ bool is_range_weapon_type( weapon_type wtype );
missile_type fires_ammo_type( const item_def &item );
missile_type fires_ammo_type( weapon_type wtype );
const char * ammo_name( const item_def &bow );
-bool is_throwable( const item_def &wpn );
-launch_retval is_launched( int being_id, const item_def &ammo, bool msg = false );
+const char * ammo_name( missile_type mtyp );
+bool is_throwable( const item_def &wpn,
+ size_type bodysize = SIZE_MEDIUM );
+launch_retval is_launched(actor *actor, const item_def *launcher,
+ const item_def &missile);
// staff/rod functions:
bool item_is_rod( const item_def &item );
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index 85c5e9b3c1..1cc99a4967 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -2943,7 +2943,8 @@ bool item_def::launched_by(const item_def &launcher) const
{
if (base_type != OBJ_MISSILES)
return (false);
- return (sub_type == fires_ammo_type(launcher));
+ const missile_type mt = fires_ammo_type(launcher);
+ return (sub_type == mt || (mt == MI_STONE && mt == MI_SLING_BULLET));
}
int item_def::index() const
diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc
index 042615ee64..14fe074a33 100644
--- a/crawl-ref/source/libutil.cc
+++ b/crawl-ref/source/libutil.cc
@@ -183,6 +183,72 @@ std::string pluralise(const std::string &name,
return name + "s";
}
+static std::string pow_in_words(int pow)
+{
+ switch (pow)
+ {
+ case 0:
+ return "";
+ case 3:
+ return " thousand";
+ case 6:
+ return " million";
+ case 9:
+ return " billion";
+ case 12:
+ default:
+ return " trillion";
+ }
+}
+
+static std::string tens_in_words(unsigned num)
+{
+ static const char *numbers[] = {
+ "", "one", "two", "three", "four", "five", "six", "seven",
+ "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
+ "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
+ };
+ static const char *tens[] = {
+ "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy",
+ "eighty", "ninety"
+ };
+
+ if (num < 20)
+ return numbers[num];
+
+ int ten = num / 10, digit = num % 10;
+ return std::string(tens[ten])
+ + (digit? std::string("-") + numbers[digit] : "");
+}
+
+static std::string join_strings(const std::string &a, const std::string &b)
+{
+ return !a.empty() && !b.empty()? a + " " + b :
+ a.empty()? b : a;
+}
+
+static std::string hundreds_in_words(unsigned num)
+{
+ unsigned dreds = num / 100, tens = num % 100;
+ std::string sdreds = dreds? tens_in_words(dreds) + " hundred" : "";
+ std::string stens = tens? tens_in_words(tens) : "";
+ return join_strings(sdreds, stens);
+}
+
+std::string number_in_words(unsigned num, int pow)
+{
+ if (pow == 12)
+ return number_in_words(num, 0) + pow_in_words(pow);
+
+ unsigned thousands = num % 1000, rest = num / 1000;
+ if (!rest and !thousands)
+ return ("zero");
+
+ return join_strings((rest? number_in_words(rest, pow + 3) : ""),
+ (thousands? hundreds_in_words(thousands) + pow_in_words(pow)
+ : ""));
+}
+
std::string replace_all(std::string s,
const std::string &find,
const std::string &repl)
diff --git a/crawl-ref/source/libutil.h b/crawl-ref/source/libutil.h
index e327ab6d74..137fe261e1 100644
--- a/crawl-ref/source/libutil.h
+++ b/crawl-ref/source/libutil.h
@@ -26,6 +26,8 @@ bool ends_with(const std::string &s, const std::string &suffix);
std::string pluralise(const std::string &name,
const char *no_of[] = NULL);
+std::string number_in_words(unsigned number, int pow = 0);
+
bool shell_safe(const char *file);
/**
diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc
index db3718a521..1244ed6144 100644
--- a/crawl-ref/source/makeitem.cc
+++ b/crawl-ref/source/makeitem.cc
@@ -171,6 +171,7 @@ static int newwave_missile_colour(const item_def &item)
switch (item.sub_type)
{
case MI_STONE:
+ case MI_SLING_BULLET:
case MI_LARGE_ROCK:
item_colour = BROWN;
break;
@@ -186,6 +187,9 @@ static int newwave_missile_colour(const item_def &item)
case MI_DART:
item_colour = CYAN;
break;
+ case MI_JAVELIN:
+ item_colour = RED;
+ break;
default:
// huh?
item_colour = LIGHTCYAN;
@@ -206,6 +210,9 @@ static int classic_missile_colour(const item_def &item)
case MI_ARROW:
item_colour = BROWN;
break;
+ case MI_SLING_BULLET:
+ item_colour = BLUE;
+ break;
case MI_NEEDLE:
item_colour = WHITE;
break;
@@ -1699,15 +1706,17 @@ int items( int allow_uniques, // not just true-false,
mitm[p].plus = 0;
mitm[p].special = SPMSL_NORMAL;
- temp_rand = random2(20);
- mitm[p].sub_type = (temp_rand < 6) ? MI_STONE : // 30 %
- (temp_rand < 10) ? MI_DART : // 20 %
- (temp_rand < 14) ? MI_ARROW : // 20 %
- (temp_rand < 18) ? MI_BOLT // 20 %
- : MI_NEEDLE; // 10 %
-
if (force_type != OBJ_RANDOM)
mitm[p].sub_type = force_type;
+ else
+ mitm[p].sub_type =
+ random_choose_weighted(30, MI_STONE,
+ 20, MI_DART,
+ 20, MI_ARROW,
+ 10, MI_NEEDLE,
+ 5, MI_SLING_BULLET,
+ 2, MI_JAVELIN,
+ 0);
// no fancy rocks -- break out before we get to racial/special stuff
if (mitm[p].sub_type == MI_LARGE_ROCK)
@@ -1803,8 +1812,11 @@ int items( int allow_uniques, // not just true-false,
set_item_ego_type( mitm[p], OBJ_MISSILES, SPMSL_POISONED );
// reduced quantity if special
- if (get_ammo_brand( mitm[p] ) == SPMSL_CURARE)
- quant = 1 + random2(9) + random2(9);
+ if (mitm[p].sub_type == MI_JAVELIN
+ || get_ammo_brand( mitm[p] ) == SPMSL_CURARE)
+ {
+ quant = random_range(2, 8);
+ }
else if (get_ammo_brand( mitm[p] ) != SPMSL_NORMAL )
quant = 1 + random2(9) + random2(12) + random2(12);
else
@@ -2927,7 +2939,7 @@ static void give_monster_item(
: mon->pickup_item(mthing, false, true)))
{
#ifdef DEBUG_DIAGNOSTICS
- mprf(MSGCH_WARN, "Destroying %s because %s doesn't want it!",
+ mprf(MSGCH_DIAGNOSTICS, "Destroying %s because %s doesn't want it!",
mthing.name(DESC_PLAIN).c_str(), mon->name(DESC_PLAIN).c_str());
#endif
destroy_item(thing);
@@ -3185,6 +3197,33 @@ static item_make_species_type give_weapon(monsters *mon, int level,
break;
}
+ case MONS_DEEP_ELF_BLADEMASTER:
+ {
+ item_race = MAKE_ITEM_ELVEN;
+ item.base_type = OBJ_WEAPONS;
+
+ // If the blademaster already has a weapon, give him the exact same
+ // sub_type to match.
+
+ const item_def *weap = mon->mslot_item(MSLOT_WEAPON);
+ if (weap && weap->base_type == OBJ_WEAPONS)
+ item.sub_type = weap->sub_type;
+ else
+ item.sub_type = random_choose_weighted(40, WPN_SABRE,
+ 10, WPN_SHORT_SWORD,
+ 2, WPN_QUICK_BLADE,
+ 0);
+ break;
+ }
+
+ case MONS_DEEP_ELF_MASTER_ARCHER:
+ {
+ item_race = MAKE_ITEM_ELVEN;
+ item.base_type = OBJ_WEAPONS;
+ item.sub_type = WPN_LONGBOW;
+ break;
+ }
+
case MONS_DEEP_ELF_ANNIHILATOR:
case MONS_DEEP_ELF_CONJURER:
case MONS_DEEP_ELF_DEATH_MAGE:
@@ -3613,6 +3652,7 @@ static item_make_species_type give_weapon(monsters *mon, int level,
}
give_monster_item(mon, thing_created, force_item);
+
if (give_aux_melee && (i.base_type != OBJ_WEAPONS || is_range_weapon(i)))
give_weapon(mon, level, true, false);
@@ -3643,12 +3683,18 @@ static void give_ammo(monsters *mon, int level,
: SPMSL_POISONED);
mitm[thing_created].flags = 0;
+
+ // Master archers get double ammo - archery is their only attack.
+ if (mon->type == MONS_DEEP_ELF_MASTER_ARCHER)
+ mitm[thing_created].quantity *= 2;
+
give_monster_item(mon, thing_created);
} // end if needs ammo
else
{
// Give some monsters throwing weapons.
- int weap_type = WPN_UNKNOWN;
+ int weap_type = -1;
+ object_class_type weap_class = OBJ_WEAPONS;
int qty = 0;
switch (mon->type)
{
@@ -3659,6 +3705,7 @@ static void give_ammo(monsters *mon, int level,
weap_type =
random_choose(WPN_HAND_AXE, WPN_SPEAR, -1);
qty = random_range(4, 8);
+ item_race = MAKE_ITEM_ORCISH;
}
break;
@@ -3668,15 +3715,23 @@ static void give_ammo(monsters *mon, int level,
weap_type =
random_choose(WPN_HAND_AXE, WPN_SPEAR, -1);
qty = random_range(2, 5);
+ item_race = MAKE_ITEM_ORCISH;
}
break;
+
+ case MONS_URUG:
+ weap_type = MI_JAVELIN;
+ weap_class = OBJ_MISSILES;
+ item_race = MAKE_ITEM_ORCISH;
+ qty = random_range(4, 7);
+ break;
}
- if (weap_type == WPN_UNKNOWN)
+ if (weap_type == -1)
return ;
const int thing_created =
- items( 0, OBJ_WEAPONS, weap_type, true, level, item_race );
+ items( 0, weap_class, weap_type, true, level, item_race );
if (thing_created != NON_ITEM)
{
mitm[thing_created].quantity = qty;
@@ -3701,6 +3756,13 @@ void give_armour(monsters *mon, int level)
switch (mon->type)
{
+ case MONS_DEEP_ELF_BLADEMASTER:
+ case MONS_DEEP_ELF_MASTER_ARCHER:
+ item_race = MAKE_ITEM_ELVEN;
+ mitm[bp].base_type = OBJ_ARMOUR;
+ mitm[bp].sub_type = ARM_LEATHER_ARMOUR;
+ break;
+
case MONS_DEEP_ELF_ANNIHILATOR:
case MONS_DEEP_ELF_CONJURER:
case MONS_DEEP_ELF_DEATH_MAGE:
diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index 0970b58672..6745380eff 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -2764,6 +2764,31 @@
,
{
+ MONS_DEEP_ELF_BLADEMASTER, 'e', LIGHTCYAN, "deep elf blademaster",
+ M_WARM_BLOOD | M_FIGHTER,
+ MR_NO_FLAGS,
+ 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -6,
+ { {AT_HIT, AF_PLAIN, 25}, {AT_HIT, AF_PLAIN, 25}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
+ { 16, 5, 3, 0 },
+ 0, 25, 15, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH,
+ MONUSE_WEAPONS_ARMOUR, SIZE_MEDIUM
+}
+,
+
+{
+ MONS_DEEP_ELF_MASTER_ARCHER, 'e', LIGHTMAGENTA, "deep elf master archer",
+ M_WARM_BLOOD | M_ARCHER,
+ MR_NO_FLAGS,
+ 450, 10, MONS_ELF, MONS_ELF, MH_NATURAL, -5,
+ // Attack damage gets rolled into their ranged attacks.
+ { {AT_SHOOT, AF_PLAIN, 25}, {AT_HIT, AF_PLAIN, 5}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
+ { 15, 4, 2, 0 },
+ 0, 15, 11, 7, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH,
+ MONUSE_WEAPONS_ARMOUR, SIZE_MEDIUM
+}
+,
+
+{
MONS_DEEP_ELF_MAGE, 'e', LIGHTRED, "deep elf mage",
M_SPELLCASTER | M_ACTUAL_SPELLS | M_WARM_BLOOD,
MR_RES_ELEC,
@@ -3358,7 +3383,7 @@
M_FIGHTER | M_SPELLCASTER | M_SPEAKS | M_UNIQUE,
MR_RES_ELEC | MR_VUL_FIRE | MR_RES_COLD,
0, 25, MONS_HILL_GIANT, MONS_ANTAEUS, MH_DEMONIC, -9,
- { {AT_HIT, AF_COLD, 55}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
+ { {AT_HIT, AF_COLD, 75}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
{ 22, 0, 0, 250 },
10, 4, 7, 7, MST_ANTAEUS, CE_CONTAMINATED, Z_NOZOMBIE, S_SHOUT, I_HIGH,
MONUSE_WEAPONS_ARMOUR, SIZE_GIANT,
diff --git a/crawl-ref/source/mon-pick.cc b/crawl-ref/source/mon-pick.cc
index 7e1343b71f..db5eb88d53 100644
--- a/crawl-ref/source/mon-pick.cc
+++ b/crawl-ref/source/mon-pick.cc
@@ -1731,6 +1731,11 @@ int mons_hallelf_level(int mcls)
mlev += 7;
break;
+ case MONS_DEEP_ELF_BLADEMASTER:
+ case MONS_DEEP_ELF_MASTER_ARCHER:
+ mlev += 10;
+ break;
+
default:
mlev += 99;
break;
@@ -1791,6 +1796,10 @@ int mons_hallelf_rare(int mcls)
case MONS_ORC_HIGH_PRIEST:
return 5;
+ case MONS_DEEP_ELF_BLADEMASTER:
+ case MONS_DEEP_ELF_MASTER_ARCHER:
+ return 1;
+
default:
return 0;
}
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index b9d5a62421..8d94b508f5 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -1601,9 +1601,15 @@ bool mons_aligned(int m1, int m2)
return (fr1 == fr2);
}
+bool mons_wields_two_weapons(monster_type m)
+{
+ return (m == MONS_TWO_HEADED_OGRE || m == MONS_ETTIN
+ || m == MONS_DEEP_ELF_BLADEMASTER);
+}
+
bool mons_wields_two_weapons(const monsters *m)
{
- return (m->type == MONS_TWO_HEADED_OGRE || m->type == MONS_ETTIN);
+ return (mons_wields_two_weapons(static_cast<monster_type>(m->type)));
}
bool mons_eats_corpses(const monsters *m)
@@ -1982,28 +1988,22 @@ bool mons_has_ranged_spell( const monsters *mon )
bool mons_has_ranged_attack( const monsters *mon )
{
- const int weapon = mon->inv[MSLOT_WEAPON];
- const int ammo = mon->inv[MSLOT_MISSILE];
-
- if ( weapon != NON_ITEM &&
- get_weapon_brand(mitm[weapon]) == SPWPN_RETURNING )
- return true;
-
- const int lnchClass = (weapon != NON_ITEM) ? mitm[weapon].base_type : -1;
- const int lnchType = (weapon != NON_ITEM) ? mitm[weapon].sub_type : 0;
-
- const int ammoClass = (ammo != NON_ITEM) ? mitm[ammo].base_type : -1;
- const int ammoType = (ammo != NON_ITEM) ? mitm[ammo].sub_type : 0;
-
- bool launched = false;
- bool thrown = false;
+ // Ugh.
+ monsters *mnc = const_cast<monsters*>(mon);
+ const item_def *weapon = mnc->launcher();
+ const item_def *primary = mnc->mslot_item(MSLOT_WEAPON);
+ const item_def *missile = mnc->missiles();
- throw_type( lnchClass, lnchType, ammoClass, ammoType, launched, thrown );
-
- if (launched || thrown)
+ if (!missile && weapon != primary
+ && get_weapon_brand(*primary) == SPWPN_RETURNING)
+ {
return (true);
+ }
- return (false);
+ if (!missile)
+ return (false);
+
+ return is_launched(mnc, weapon, *missile);
}
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index 82dfa1b68f..b4d58cbfb1 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -221,6 +221,7 @@ bool mons_is_demon( int mc );
bool mons_is_humanoid( int mc );
bool mons_wields_two_weapons(const monsters *m);
+bool mons_wields_two_weapons(monster_type m);
bool mons_is_summoned(const monsters *m);
// last updated 12may2000 {dlb}
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 0f61a1acee..487f2e246c 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -575,12 +575,9 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target,
else if (mons_itemuse(mon_type) >= MONUSE_STARTING_EQUIPMENT)
{
give_item( id, power );
-
// Give these monsters a second weapon -- bwr
- if (mon_type == MONS_TWO_HEADED_OGRE || mon_type == MONS_ETTIN)
- {
+ if (mons_wields_two_weapons(static_cast<monster_type>(mon_type)))
give_item( id, power );
- }
}
// give manticores 8 to 16 spike volleys.
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 0c3dc4d02b..455e61160a 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -3340,17 +3340,22 @@ static bool handle_throw(monsters *monster, bolt & beem)
if (mons_itemuse(monster->type) < MONUSE_OPEN_DOORS)
return (false);
- if (one_chance_in(5))
+ const bool archer = mons_class_flag(monster->type, M_ARCHER);
+ // Highly-specialised archers are more likely to shoot than talk.
+ if (one_chance_in(archer? 9 : 5))
return (false);
// don't allow offscreen throwing for now.
if (monster->foe == MHITYOU && !mons_near(monster))
return (false);
- // recent addition {GDL} - monsters won't throw if they can do melee.
- // wastes valuable ammo, and most monsters are better at melee anyway.
- if (adjacent( beem.target_x, beem.target_y, monster->x, monster->y ))
+ // Monsters won't shoot in melee range, largely for balance reasons.
+ // Specialist archers are an exception to this rule.
+ if (!archer
+ && adjacent( beem.target_x, beem.target_y, monster->x, monster->y ))
+ {
return (false);
+ }
item_def *launcher = NULL;
const item_def *weapon = NULL;
diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc
index b6aafd19ea..a2a9119591 100644
--- a/crawl-ref/source/mstuff2.cc
+++ b/crawl-ref/source/mstuff2.cc
@@ -969,41 +969,6 @@ void setup_generic_throw(struct monsters *monster, struct bolt &pbolt)
pbolt.is_beam = false;
}
-// decide if something is launched or thrown
-// pass -1 for launcher class & 0 for type if no weapon is wielded
-
-void throw_type( int lnchClass, int lnchType, int wepClass, int wepType,
- bool &launched, bool &thrown )
-{
- if (wepClass == OBJ_MISSILES
- && lnchClass == OBJ_WEAPONS
- && is_range_weapon_type(static_cast<weapon_type>(lnchType))
- && wepType == fires_ammo_type(static_cast<weapon_type>(lnchType)))
- {
- launched = true;
- }
-
- if (wepClass == OBJ_WEAPONS)
- {
- if (wepType == WPN_DAGGER || wepType == WPN_HAND_AXE || wepType == WPN_SPEAR)
- {
- thrown = true;
- }
- }
-
- if (wepClass == OBJ_MISSILES)
- {
- if (wepType == MI_DART || wepType == MI_STONE || wepType == MI_LARGE_ROCK)
- {
- thrown = true;
- }
- }
-
- // launched overrides thrown
- if (launched == true)
- thrown = false;
-}
-
bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
{
// XXX: ugly hack, but avoids adding dynamic allocation to this code
@@ -1021,16 +986,11 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
int damMult = 0;
int diceMult = 100;
- bool launched = false; // item is launched
- bool thrown = false; // item is sensible thrown item
-
// some initial convenience & initializations
int wepClass = mitm[hand_used].base_type;
int wepType = mitm[hand_used].sub_type;
int weapon = monster->inv[MSLOT_WEAPON];
-
- int lnchClass = (weapon != NON_ITEM) ? mitm[weapon].base_type : -1;
int lnchType = (weapon != NON_ITEM) ? mitm[weapon].sub_type : 0;
const bool skilled = mons_class_flag(monster->type, M_FIGHTER);
@@ -1047,16 +1007,12 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
pbolt.thrower = KILL_MON_MISSILE;
pbolt.aux_source.clear();
- // figure out if we're thrown or launched
- throw_type( lnchClass, lnchType, wepClass, wepType, launched, thrown );
- if (returning)
- {
- launched = false;
- thrown = true;
- }
+ const launch_retval projected =
+ is_launched(monster, monster->mslot_item(MSLOT_WEAPON),
+ mitm[hand_used]);
// extract launcher bonuses due to magic
- if (launched)
+ if (projected == LRET_LAUNCHED)
{
lnchHitBonus = mitm[ weapon ].plus;
lnchDamBonus = mitm[ weapon ].plus2;
@@ -1067,7 +1023,15 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
ammoHitBonus = item.plus;
ammoDamBonus = item.plus2;
- if (thrown)
+ // Archers get a boost from their melee attack.
+ if (mons_class_flag(monster->type, M_ARCHER))
+ {
+ const mon_attack_def attk = mons_attack_spec(monster, 0);
+ if (attk.type == AT_SHOOT)
+ ammoDamBonus += random2avg(attk.damage, 2);
+ }
+
+ if (projected == LRET_THROWN)
{
// Darts are easy.
if (wepClass == OBJ_MISSILES && wepType == MI_DART)
@@ -1090,9 +1054,14 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
// ammo damage needs adjusting here - OBJ_MISSILES
// don't get separate tohit/damage bonuses!
ammoDamBonus = ammoHitBonus;
+
// [dshaligram] Thrown stones/darts do only half the damage of
// launched stones/darts. This matches 4.0 behaviour.
- baseDam = div_rand_round(baseDam, 2);
+ if (wepType == MI_DART || wepType == MI_STONE
+ || wepType == MI_SLING_BULLET)
+ {
+ baseDam = div_rand_round(baseDam, 2);
+ }
}
// give monster "skill" bonuses based on HD
@@ -1100,7 +1069,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
exDamBonus = (damMult * monster->hit_dice) / 10 + 1;
}
- if (launched)
+ if (projected == LRET_LAUNCHED)
{
switch (lnchType)
{
@@ -1236,7 +1205,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
// now, if a monster is, for some reason, throwing something really
// stupid, it will have baseHit of 0 and damage of 0. Ah well.
std::string msg = str_monam(*monster, DESC_CAP_THE);
- msg += ((launched) ? " shoots " : " throws ");
+ msg += ((projected == LRET_LAUNCHED) ? " shoots " : " throws ");
if (!pbolt.name.empty())
{
@@ -1257,7 +1226,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
// [dshaligram] When changing bolt names here, you must edit
// hiscores.cc (scorefile_entry::terse_missile_cause()) to match.
- if (launched)
+ if (projected == LRET_LAUNCHED)
{
snprintf( throw_buff, sizeof(throw_buff), "Shot with a%s %s by %s",
(is_vowel(pbolt.name[0]) ? "n" : ""), pbolt.name.c_str(),
@@ -1277,7 +1246,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
pbolt.damage =
dice_def( 1, baseDam + random2avg(exDamBonus, 2) + ammoDamBonus );
- if (launched)
+ if (projected == LRET_LAUNCHED)
{
pbolt.damage.size += lnchDamBonus;
pbolt.hit += lnchHitBonus;
diff --git a/crawl-ref/source/mstuff2.h b/crawl-ref/source/mstuff2.h
index 9f55d933ee..c89f3ec272 100644
--- a/crawl-ref/source/mstuff2.h
+++ b/crawl-ref/source/mstuff2.h
@@ -91,15 +91,6 @@ void monster_teleport(struct monsters *monster, bool instan,
* *********************************************************************** */
void spore_goes_pop(struct monsters *monster);
-
-// last updated Jan14,2001 -- gdl
-/* ***********************************************************************
- * called from: monstuff
- * *********************************************************************** */
-void throw_type(int lnchClass, int lnchType, int wepClass, int wepType,
- bool &launched, bool &thrown);
-
-
bool orange_statue_effects(monsters *mons);
bool silver_statue_effects(monsters *mons);
bool moth_incite_monsters(const monsters *mon);
diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc
index 2f9d0c2bcf..3e1187d79f 100644
--- a/crawl-ref/source/stuff.cc
+++ b/crawl-ref/source/stuff.cc
@@ -215,6 +215,31 @@ int random_choose(int first, ...)
return (chosen);
}
+int random_choose_weighted(int weight, int first, ...)
+{
+ va_list args;
+ va_start(args, first);
+
+ int chosen = first, cweight = weight, nargs = 100;
+
+ while (nargs-- > 0)
+ {
+ const int nweight = va_arg(args, int);
+ if (!nweight)
+ break;
+
+ const int choice = va_arg(args, int);
+ if (random2(cweight += nweight) < nweight)
+ chosen = choice;
+ }
+
+ ASSERT(nargs > 0);
+
+ va_end(args);
+ return (chosen);
+}
+
+
int random2( int max )
{
if (max <= 1)
diff --git a/crawl-ref/source/stuff.h b/crawl-ref/source/stuff.h
index 23da3a4bd0..8d98e78b16 100644
--- a/crawl-ref/source/stuff.h
+++ b/crawl-ref/source/stuff.h
@@ -35,6 +35,7 @@ bool one_chance_in(int a_million);
int random2(int randmax);
int random_range(int low, int high);
int random_choose(int first, ...);
+int random_choose_weighted(int weight, int first, ...);
unsigned long random_int();
int random2avg( int max, int rolls );
int bestroll(int max, int rolls);