summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/delay.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/delay.cc')
-rw-r--r--crawl-ref/source/delay.cc1129
1 files changed, 787 insertions, 342 deletions
diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc
index 0894207395..6998a3df50 100644
--- a/crawl-ref/source/delay.cc
+++ b/crawl-ref/source/delay.cc
@@ -2,6 +2,8 @@
* File: delay.cc
* Summary: Functions for handling multi-turn actions.
*
+ * Modified for Crawl Reference by $Author$ on $Date$
+ *
* Change History (most recent first):
*
* <1> Sept 09, 2001 BWR Created
@@ -13,40 +15,111 @@
#include <stdio.h>
#include <string.h>
+#include "clua.h"
+#include "command.h"
#include "delay.h"
#include "enum.h"
#include "food.h"
+#include "invent.h"
#include "items.h"
#include "itemname.h"
+#include "itemprop.h"
#include "item_use.h"
#include "it_use2.h"
#include "message.h"
#include "misc.h"
#include "monstuff.h"
+#include "mstuff2.h"
#include "ouch.h"
#include "output.h"
#include "player.h"
#include "randart.h"
#include "spl-util.h"
#include "stuff.h"
+#include "travel.h"
+
+extern std::vector<SelItem> items_for_multidrop;
+
+static void armour_wear_effects(const int item_inv_slot);
+static void handle_run_delays(const delay_queue_item &delay);
+static void handle_macro_delay();
+
+// Returns true if this delay can act as a parent to other delays, i.e. if
+// other delays can be spawned while this delay is running. If is_parent_delay
+// returns true, new delays will be pushed immediately to the front of the
+// delay in question, rather than at the end of the queue.
+static bool is_parent_delay(int delay)
+{
+ // Interlevel travel can do upstairs/downstairs delays.
+ // Lua macros can in theory perform any of the other delays,
+ // including travel; in practise travel still assumes there can be
+ // no parent delay.
+ return (delay == DELAY_TRAVEL || delay == DELAY_MACRO);
+}
+
+static void push_delay(const delay_queue_item &delay)
+{
+ for (delay_queue_type::iterator i = you.delay_queue.begin();
+ i != you.delay_queue.end();
+ ++i)
+ {
+ if (is_parent_delay( i->type ))
+ {
+ you.delay_queue.insert(i, delay);
+ return;
+ }
+ }
+ you.delay_queue.push_back( delay );
+}
+
+static void pop_delay()
+{
+ if (!you.delay_queue.empty())
+ you.delay_queue.erase( you.delay_queue.begin() );
+}
+
+static void clear_pending_delays()
+{
+ while (you.delay_queue.size() > 1)
+ you.delay_queue.pop_back();
+}
void start_delay( int type, int turns, int parm1, int parm2 )
/***********************************************************/
{
- delay_queue_item delay;
+ delay_queue_item delay;
delay.type = type;
delay.duration = turns;
delay.parm1 = parm1;
delay.parm2 = parm2;
- you.delay_queue.push( delay );
+ switch ( delay.type ) {
+ case DELAY_ARMOUR_ON:
+ mpr("You start putting on your armour.", MSGCH_MULTITURN_ACTION);
+ break;
+ case DELAY_ARMOUR_OFF:
+ mpr("You start removing your armour.", MSGCH_MULTITURN_ACTION);
+ break;
+ case DELAY_MEMORISE:
+ mpr("You start memorising the spell.", MSGCH_MULTITURN_ACTION);
+ break;
+ case DELAY_PASSWALL:
+ mpr("You begin to meditate on the wall.", MSGCH_MULTITURN_ACTION);
+ break;
+ default:
+ break;
+ }
+ push_delay( delay );
}
void stop_delay( void )
/*********************/
{
- delay_queue_item delay = you.delay_queue.front();
+ if ( you.delay_queue.empty() )
+ return;
+
+ delay_queue_item delay = you.delay_queue.front();
// At the very least we can remove any queued delays, right
// now there is no problem with doing this... note that
@@ -54,42 +127,60 @@ void stop_delay( void )
// as the effect of a delay doesn't normally allow interaction
// until it is done... it merely chains up individual actions
// into a single action. -- bwr
- if (you.delay_queue.size() > 1)
- {
- while (you.delay_queue.size())
- you.delay_queue.pop();
-
- you.delay_queue.push( delay );
- }
+ clear_pending_delays();
switch (delay.type)
{
case DELAY_BUTCHER:
// Corpse keeps track of work in plus2 field, see handle_delay() -- bwr
mpr( "You stop butchering the corpse." );
- you.delay_queue.pop();
+ pop_delay();
break;
- case DELAY_MEMORIZE:
+ case DELAY_MEMORISE:
// Losing work here is okay... having to start from
// scratch is a reasonable behaviour. -- bwr
mpr( "Your memorization is interrupted." );
- you.delay_queue.pop();
+ pop_delay();
break;
case DELAY_PASSWALL:
// The lost work here is okay since this spell requires
- // the player to "attune to the rock". If changed, the
+ // the player to "attune to the rock". If changed, then
// the delay should be increased to reduce the power of
// this spell. -- bwr
mpr( "Your meditation is interrupted." );
- you.delay_queue.pop();
+ pop_delay();
+ break;
+
+ case DELAY_MULTIDROP:
+ // No work lost
+ mpr( "You stop dropping stuff." );
+ pop_delay();
+ break;
+
+ case DELAY_RUN:
+ case DELAY_REST:
+ case DELAY_TRAVEL:
+ case DELAY_MACRO:
+ // Always interruptible.
+ pop_delay();
+
+ // Keep things consistent, otherwise disturbing phenomena can occur.
+ // Note that runrest::stop() will turn around and call stop_delay()
+ // again, but that's okay because the delay is already popped off
+ // the queue.
+ if (is_run_delay(delay.type) && you.running)
+ stop_running();
+
+ // There's no special action needed for macros - if we don't call out
+ // to the Lua function, it can't do damage.
break;
- case DELAY_INTERUPTABLE:
- // always stopable by definition...
+ case DELAY_INTERRUPTIBLE:
+ // always stoppable by definition...
// try using a more specific type anyways. -- bwr
- you.delay_queue.pop();
+ pop_delay();
break;
case DELAY_EAT:
@@ -101,7 +192,7 @@ void stop_delay( void )
case DELAY_ARMOUR_ON:
case DELAY_ARMOUR_OFF:
- // These two have the default action of not being interuptable,
+ // These two have the default action of not being interruptible,
// although they will often be chained (remove cloak, remove
// armour, wear new armour, replace cloak), all of which can
// be stopped when complete. This is a fairly reasonable
@@ -117,7 +208,8 @@ void stop_delay( void )
case DELAY_DROP_ITEM: // one turn... only used for easy armour drops
case DELAY_ASCENDING_STAIRS: // short... and probably what people want
case DELAY_DESCENDING_STAIRS: // short... and probably what people want
- case DELAY_UNINTERUPTABLE: // never stopable
+ case DELAY_UNINTERRUPTIBLE: // never stoppable
+ case DELAY_JEWELLERY_ON: // one turn
default:
break;
}
@@ -136,414 +228,767 @@ int current_delay_action( void )
: DELAY_NOT_DELAYED);
}
+bool is_run_delay(int delay)
+{
+ return (delay == DELAY_RUN || delay == DELAY_REST
+ || delay == DELAY_TRAVEL);
+}
+
void handle_delay( void )
/***********************/
{
- int ego;
char str_pass[ ITEMNAME_SIZE ];
- if (you_are_delayed())
+ if (!you_are_delayed())
+ return;
+
+ delay_queue_item &delay = you.delay_queue.front();
+
+ // Run delays and Lua delays don't have a specific end time.
+ if (is_run_delay(delay.type))
{
- delay_queue_item &delay = you.delay_queue.front();
+ handle_run_delays(delay);
+ return;
+ }
- // First check cases where delay may no longer be valid:
- // XXX: need to handle passwall when monster digs -- bwr
- if (delay.type == DELAY_BUTCHER)
+ if (delay.type == DELAY_MACRO)
+ {
+ handle_macro_delay();
+ return;
+ }
+
+ // First check cases where delay may no longer be valid:
+ // XXX: need to handle passwall when monster digs -- bwr
+ if (delay.type == DELAY_BUTCHER)
+ {
+ // A monster may have raised the corpse you're chopping up! -- bwr
+ // Note that a monster could have raised the corpse and another
+ // monster could die and create a corpse with the same ID number...
+ // However, it would not be at the player's square like the
+ // original and that's why we do it this way. Note that
+ // we ignore the conversion to skeleton possibility just to
+ // be nice. -- bwr
+ if (is_valid_item( mitm[ delay.parm1 ] )
+ && mitm[ delay.parm1 ].base_type == OBJ_CORPSES
+ && mitm[ delay.parm1 ].x == you.x_pos
+ && mitm[ delay.parm1 ].y == you.y_pos )
{
- // A monster may have raised the corpse you're chopping up! -- bwr
- // Note that a monster could have raised the corpse and another
- // monster could die and create a corpse with the same ID number...
- // However, it would not be at the player's square like the
- // original and that's why we do it this way. Note that
- // we ignore the conversion to skeleton possiblity just to
- // be nice. -- bwr
- if (is_valid_item( mitm[ delay.parm1 ] )
- && mitm[ delay.parm1 ].base_type == OBJ_CORPSES
- && mitm[ delay.parm1 ].x == you.x_pos
- && mitm[ delay.parm1 ].y == you.y_pos)
- {
- // mark work done on the corpse in case we stop -- bwr
- mitm[ delay.parm1 ].plus2++;
- }
- else
- {
- // corpse is no longer valid!
- stop_delay();
- return;
+ // special < 100 is the rottenness check
+ if ( (mitm[delay.parm1].special < 100) &&
+ (delay.parm2 >= 100) ) {
+ mpr("The corpse rots.", MSGCH_ROTTEN_MEAT);
+ delay.parm2 = 99; // don't give the message twice
}
+
+ // mark work done on the corpse in case we stop -- bwr
+ mitm[ delay.parm1 ].plus2++;
+ }
+ else
+ {
+ // corpse is no longer valid!
+ stop_delay();
+ return;
}
+ }
+ if ( delay.type == DELAY_MULTIDROP )
+ {
+
+ // Throw away invalid items; items usually go invalid because
+ // of chunks rotting away.
+ while (!items_for_multidrop.empty()
+ // Don't look for gold in inventory
+ && items_for_multidrop[0].slot != PROMPT_GOT_SPECIAL
+ && !is_valid_item(you.inv[ items_for_multidrop[0].slot ]))
+ items_for_multidrop.erase( items_for_multidrop.begin() );
- // Handle delay:
- if (delay.duration > 0)
+ if ( items_for_multidrop.empty() )
{
+ // ran out of things to drop
+ pop_delay();
+ return;
+ }
+ }
+
+ // Handle delay:
+ if (delay.duration > 0)
+ {
#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "Delay type: %d duration: %d",
- delay.type, delay.duration );
+ snprintf( info, INFO_SIZE, "Delay type: %d (%s), duration: %d",
+ delay.type, delay_name(delay.type), delay.duration );
- mpr( info, MSGCH_DIAGNOSTICS );
+ mpr( info, MSGCH_DIAGNOSTICS );
#endif
- delay.duration--;
+ switch ( delay.type )
+ {
+ case DELAY_ARMOUR_ON:
+ in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass );
+ snprintf( info, INFO_SIZE,
+ "You continue putting on %s.", str_pass );
+ mpr(info, MSGCH_MULTITURN_ACTION);
+ break;
+ case DELAY_ARMOUR_OFF:
+ in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass );
+ snprintf( info, INFO_SIZE,
+ "You continue taking off %s.", str_pass );
+ mpr(info, MSGCH_MULTITURN_ACTION);
+ break;
+ case DELAY_BUTCHER:
+ mpr("You continue butchering the corpse.",
+ MSGCH_MULTITURN_ACTION);
+ break;
+ case DELAY_MEMORISE:
+ mpr("You continue memorising.", MSGCH_MULTITURN_ACTION);
+ break;
+ case DELAY_PASSWALL:
+ mpr("You continue meditating on the rock.",
+ MSGCH_MULTITURN_ACTION);
+ break;
+ case DELAY_MULTIDROP:
+ drop_item( items_for_multidrop[0].slot,
+ items_for_multidrop[0].quantity );
+ items_for_multidrop.erase( items_for_multidrop.begin() );
+ break;
+ default:
+ break;
}
- else
+ delay.duration--;
+ }
+ else
+ {
+ switch (delay.type)
{
- switch (delay.type)
- {
- case DELAY_AUTOPICKUP:
- break;
+ case DELAY_AUTOPICKUP:
+ break;
- case DELAY_WEAPON_SWAP:
- weapon_switch( delay.parm1 );
- break;
+ case DELAY_WEAPON_SWAP:
+ weapon_switch( delay.parm1 );
+ break;
- case DELAY_ARMOUR_ON:
- set_ident_flags( you.inv[ delay.parm1 ], ISFLAG_EQ_ARMOUR_MASK );
+ case DELAY_JEWELLERY_ON:
+ puton_ring( delay.parm1, false );
+ break;
- in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass );
- snprintf( info, INFO_SIZE, "You finish putting on %s.", str_pass );
- mpr(info);
+ case DELAY_ARMOUR_ON:
+ armour_wear_effects( delay.parm1 );
+ break;
+
+ case DELAY_ARMOUR_OFF:
+ {
+ in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass );
+ snprintf( info, INFO_SIZE, "You finish taking off %s.", str_pass );
+ mpr(info);
- if (you.inv[ delay.parm1 ].sub_type < ARM_SHIELD
- || you.inv[ delay.parm1 ].sub_type > ARM_LARGE_SHIELD)
+ const equipment_type slot =
+ get_armour_slot( you.inv[delay.parm1] );
+
+ if (slot == EQ_BODY_ARMOUR)
+ {
+ you.equip[EQ_BODY_ARMOUR] = -1;
+ }
+ else
+ {
+ switch (slot)
{
- you.equip[EQ_BODY_ARMOUR] = delay.parm1;
+ case EQ_SHIELD:
+ if (delay.parm1 == you.equip[EQ_SHIELD])
+ you.equip[EQ_SHIELD] = -1;
+ break;
- if (you.duration[DUR_ICY_ARMOUR] != 0)
- {
- mpr( "Your icy armour melts away.", MSGCH_DURATION );
- you.redraw_armour_class = 1;
- you.duration[DUR_ICY_ARMOUR] = 0;
- }
+ case EQ_CLOAK:
+ if (delay.parm1 == you.equip[EQ_CLOAK])
+ you.equip[EQ_CLOAK] = -1;
+ break;
+
+ case EQ_HELMET:
+ if (delay.parm1 == you.equip[EQ_HELMET])
+ you.equip[EQ_HELMET] = -1;
+ break;
+
+
+ case EQ_GLOVES:
+ if (delay.parm1 == you.equip[EQ_GLOVES])
+ you.equip[EQ_GLOVES] = -1;
+ break;
+
+ case EQ_BOOTS:
+ if (delay.parm1 == you.equip[EQ_BOOTS])
+ you.equip[EQ_BOOTS] = -1;
+ break;
+
+ default:
+ break;
}
- else
+ }
+
+ unwear_armour( delay.parm1 );
+
+ you.redraw_armour_class = 1;
+ you.redraw_evasion = 1;
+ break;
+ }
+ case DELAY_EAT:
+ mpr( "You finish eating." );
+ // For chunks, warn the player if they're not getting much
+ // nutrition.
+ if (delay.parm1)
+ chunk_nutrition_message(delay.parm1);
+ break;
+
+ case DELAY_MEMORISE:
+ mpr( "You finish memorising." );
+ add_spell_to_memory( delay.parm1 );
+ break;
+
+ case DELAY_PASSWALL:
+ {
+ mpr( "You finish merging with the rock." );
+ more(); // or the above message won't be seen
+
+ const int pass_x = delay.parm1;
+ const int pass_y = delay.parm2;
+
+ if (pass_x != 0 && pass_y != 0)
{
- switch (you.inv[ delay.parm1 ].sub_type)
+
+ switch (grd[ pass_x ][ pass_y ])
{
- case ARM_BUCKLER:
- case ARM_LARGE_SHIELD:
- case ARM_SHIELD:
- if (you.duration[DUR_CONDENSATION_SHIELD])
- {
- mpr( "Your icy shield evaporates.", MSGCH_DURATION );
- you.duration[DUR_CONDENSATION_SHIELD] = 0;
- }
- you.equip[EQ_SHIELD] = delay.parm1;
- break;
- case ARM_CLOAK:
- you.equip[EQ_CLOAK] = delay.parm1;
- break;
- case ARM_HELMET:
- you.equip[EQ_HELMET] = delay.parm1;
+ case DNGN_ROCK_WALL:
+ case DNGN_STONE_WALL:
+ case DNGN_METAL_WALL:
+ case DNGN_GREEN_CRYSTAL_WALL:
+ case DNGN_WAX_WALL:
+ case DNGN_SILVER_STATUE:
+ case DNGN_ORANGE_CRYSTAL_STATUE:
+ ouch(1 + you.hp, 0, KILLED_BY_PETRIFICATION);
break;
- case ARM_GLOVES:
- you.equip[EQ_GLOVES] = delay.parm1;
+
+ case DNGN_SECRET_DOOR: // oughtn't happen
+ case DNGN_CLOSED_DOOR: // open the door
+ grd[ pass_x ][ pass_y ] = DNGN_OPEN_DOOR;
break;
- case ARM_BOOTS:
- you.equip[EQ_BOOTS] = delay.parm1;
+
+ default:
break;
}
- }
- ego = get_armour_ego_type( you.inv[ delay.parm1 ] );
- if (ego != SPARM_NORMAL)
- {
- switch (ego)
+ // move any monsters out of the way:
+ int mon = mgrd[ pass_x ][ pass_y ];
+ if (mon != NON_MONSTER)
{
- case SPARM_RUNNING:
- strcpy(info, "You feel quick");
- strcat(info, (you.species == SP_NAGA
- || you.species == SP_CENTAUR) ? "." : " on your feet.");
- mpr(info);
- break;
+ // one square, a few squares, anywhere...
+ if (!shift_monster(&menv[mon])
+ && !monster_blink(&menv[mon]))
+ {
+ monster_teleport( &menv[mon], true, true );
+ }
+ }
- case SPARM_FIRE_RESISTANCE:
- mpr("You feel resistant to fire.");
- break;
+ move_player_to_grid(pass_x, pass_y, false, true, true);
+ redraw_screen();
+ }
+ }
+ break;
- case SPARM_COLD_RESISTANCE:
- mpr("You feel resistant to cold.");
- break;
+ case DELAY_BUTCHER:
+ strcpy( info, "You finish " );
+ strcat( info, (you.species == SP_TROLL
+ || you.species == SP_GHOUL) ? "ripping"
+ : "chopping" );
- case SPARM_POISON_RESISTANCE:
- mpr("You feel healthy.");
- break;
+ strcat( info, " the corpse into pieces." );
+ mpr( info );
- case SPARM_SEE_INVISIBLE:
- mpr("You feel perceptive.");
- break;
+ turn_corpse_into_chunks( mitm[ delay.parm1 ] );
- case SPARM_DARKNESS:
- if (!you.invis)
- mpr("You become transparent for a moment.");
- break;
+ if (you.berserker && you.berserk_penalty != NO_BERSERK_PENALTY)
+ {
+ mpr("You enjoyed that.");
+ you.berserk_penalty = 0;
+ }
+ break;
- case SPARM_STRENGTH:
- modify_stat(STAT_STRENGTH, 3, false);
- break;
+ case DELAY_DROP_ITEM:
+ // Note: checking if item is droppable is assumed to
+ // be done before setting up this delay... this includes
+ // quantity (delay.parm2). -- bwr
- case SPARM_DEXTERITY:
- modify_stat(STAT_DEXTERITY, 3, false);
- break;
+ // Make sure item still exists.
+ if (!is_valid_item( you.inv[ delay.parm1 ] ))
+ break;
- case SPARM_INTELLIGENCE:
- modify_stat(STAT_INTELLIGENCE, 3, false);
- break;
+ // Must handle unwield_item before we attempt to copy
+ // so that temporary brands and such are cleared. -- bwr
+ if (delay.parm1 == you.equip[EQ_WEAPON])
+ {
+ unwield_item( delay.parm1 );
+ you.equip[EQ_WEAPON] = -1;
+ canned_msg( MSG_EMPTY_HANDED );
+ }
- case SPARM_PONDEROUSNESS:
- mpr("You feel rather ponderous.");
- // you.speed += 2;
- you.redraw_evasion = 1;
- break;
+ if (!copy_item_to_grid( you.inv[ delay.parm1 ],
+ you.x_pos, you.y_pos, delay.parm2,
+ true ))
+ {
+ mpr("Too many items on this level, not dropping the item.");
+ }
+ else
+ {
+ quant_name( you.inv[ delay.parm1 ], delay.parm2,
+ DESC_NOCAP_A, str_pass );
- case SPARM_LEVITATION:
- mpr("You feel rather light.");
- break;
+ snprintf( info, INFO_SIZE, "You drop %s.", str_pass );
+ mpr(info);
- case SPARM_MAGIC_RESISTANCE:
- mpr("You feel resistant to magic.");
- break;
+ dec_inv_item_quantity( delay.parm1, delay.parm2 );
+ }
+ break;
+
+ case DELAY_ASCENDING_STAIRS:
+ up_stairs();
+ untag_followers();
+ break;
+
+ case DELAY_DESCENDING_STAIRS:
+ down_stairs( false, delay.parm1 );
+ untag_followers();
+ break;
+
+ case DELAY_INTERRUPTIBLE:
+ case DELAY_UNINTERRUPTIBLE:
+ // these are simple delays that have no effect when complete
+ break;
+
+ default:
+ mpr( "You finish doing something." );
+ break;
+ }
- case SPARM_PROTECTION:
- mpr("You feel protected.");
- break;
+ you.wield_change = true;
+ print_stats(); // force redraw of the stats
+ you.turn_is_over = true;
+ pop_delay();
+ }
+}
- case SPARM_STEALTH:
- mpr("You feel stealthy.");
- break;
+static void armour_wear_effects(const int item_slot)
+{
+ item_def &arm = you.inv[item_slot];
- case SPARM_RESISTANCE:
- mpr("You feel resistant to extremes of temperature.");
- break;
+ set_ident_flags(arm, ISFLAG_EQ_ARMOUR_MASK );
+ mprf("You finish putting on %s.", item_name(arm, DESC_NOCAP_YOUR));
- case SPARM_POSITIVE_ENERGY:
- mpr("Your life-force is being protected.");
- break;
+ const equipment_type eq_slot = get_armour_slot(arm);
- case SPARM_ARCHMAGI:
- if (!you.skills[SK_SPELLCASTING])
- mpr("You feel strangely numb.");
- else
- mpr("You feel extremely powerful.");
- break;
- }
- }
+ if (eq_slot == EQ_BODY_ARMOUR)
+ {
+ you.equip[EQ_BODY_ARMOUR] = item_slot;
- if (is_random_artefact( you.inv[ delay.parm1 ] ))
- use_randart( delay.parm1 );
+ if (you.duration[DUR_ICY_ARMOUR] != 0)
+ {
+ mpr( "Your icy armour melts away.", MSGCH_DURATION );
+ you.redraw_armour_class = 1;
+ you.duration[DUR_ICY_ARMOUR] = 0;
+ }
+ }
+ else
+ {
+ switch (eq_slot)
+ {
+ case EQ_SHIELD:
+ if (you.duration[DUR_CONDENSATION_SHIELD])
+ {
+ mpr( "Your icy shield evaporates.", MSGCH_DURATION );
+ you.duration[DUR_CONDENSATION_SHIELD] = 0;
+ }
+ you.equip[EQ_SHIELD] = item_slot;
+ break;
+ case EQ_CLOAK:
+ you.equip[EQ_CLOAK] = item_slot;
+ break;
+ case EQ_HELMET:
+ you.equip[EQ_HELMET] = item_slot;
+ break;
+ case EQ_GLOVES:
+ you.equip[EQ_GLOVES] = item_slot;
+ break;
+ case EQ_BOOTS:
+ you.equip[EQ_BOOTS] = item_slot;
+ break;
+ default:
+ break;
+ }
+ }
- if (item_cursed( you.inv[ delay.parm1 ] ))
- mpr( "Oops, that feels deathly cold." );
+ int ego = get_armour_ego_type( arm );
+ if (ego != SPARM_NORMAL)
+ {
+ switch (ego)
+ {
+ case SPARM_RUNNING:
+ mprf("You feel quick%s.",
+ (you.species == SP_NAGA || you.species == SP_CENTAUR)
+ ? "" : " on your feet");
+ break;
+
+ case SPARM_FIRE_RESISTANCE:
+ mpr("You feel resistant to fire.");
+ break;
+
+ case SPARM_COLD_RESISTANCE:
+ mpr("You feel resistant to cold.");
+ break;
+
+ case SPARM_POISON_RESISTANCE:
+ mpr("You feel healthy.");
+ break;
+
+ case SPARM_SEE_INVISIBLE:
+ mpr("You feel perceptive.");
+ break;
+
+ case SPARM_DARKNESS:
+ if (!you.invis)
+ mpr("You become transparent for a moment.");
+ break;
+
+ case SPARM_STRENGTH:
+ modify_stat(STAT_STRENGTH, 3, false);
+ break;
+
+ case SPARM_DEXTERITY:
+ modify_stat(STAT_DEXTERITY, 3, false);
+ break;
+
+ case SPARM_INTELLIGENCE:
+ modify_stat(STAT_INTELLIGENCE, 3, false);
+ break;
+
+ case SPARM_PONDEROUSNESS:
+ mpr("You feel rather ponderous.");
+ // you.speed += 2;
+ you.redraw_evasion = 1;
+ break;
+
+ case SPARM_LEVITATION:
+ mpr("You feel rather light.");
+ break;
+
+ case SPARM_MAGIC_RESISTANCE:
+ mpr("You feel resistant to magic.");
+ break;
+
+ case SPARM_PROTECTION:
+ mpr("You feel protected.");
+ break;
+
+ case SPARM_STEALTH:
+ mpr("You feel stealthy.");
+ break;
+
+ case SPARM_RESISTANCE:
+ mpr("You feel resistant to extremes of temperature.");
+ break;
+
+ case SPARM_POSITIVE_ENERGY:
+ mpr("Your life-force is being protected.");
+ break;
+
+ case SPARM_ARCHMAGI:
+ if (!you.skills[SK_SPELLCASTING])
+ mpr("You feel strangely numb.");
+ else
+ mpr("You feel extremely powerful.");
+ break;
+ }
+ }
- you.redraw_armour_class = 1;
- you.redraw_evasion = 1;
- break;
+ if (is_random_artefact( arm ))
+ use_randart( item_slot );
- case DELAY_ARMOUR_OFF:
- in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass );
- snprintf( info, INFO_SIZE, "You finish taking off %s.", str_pass );
- mpr(info);
+ if (item_cursed( arm ))
+ mpr( "Oops, that feels deathly cold." );
- if (you.inv[ delay.parm1 ].sub_type < ARM_SHIELD
- || you.inv[ delay.parm1 ].sub_type > ARM_LARGE_SHIELD)
- {
- you.equip[EQ_BODY_ARMOUR] = -1;
- }
- else
- {
- switch (you.inv[ delay.parm1 ].sub_type)
- {
- case ARM_BUCKLER:
- case ARM_LARGE_SHIELD:
- case ARM_SHIELD:
- if (delay.parm1 == you.equip[EQ_SHIELD])
- you.equip[EQ_SHIELD] = -1;
- break;
+ warn_shield_penalties();
- case ARM_CLOAK:
- if (delay.parm1 == you.equip[EQ_CLOAK])
- you.equip[EQ_CLOAK] = -1;
- break;
+ you.redraw_armour_class = 1;
+ you.redraw_evasion = 1;
+}
- case ARM_HELMET:
- if (delay.parm1 == you.equip[EQ_HELMET])
- you.equip[EQ_HELMET] = -1;
- break;
+static command_type get_running_command() {
+ if ( kbhit() ) {
+ stop_running();
+ return CMD_NO_CMD;
+ }
+ if ( is_resting() ) {
+ you.running.rest();
+ return CMD_MOVE_NOWHERE;
+ }
+ return direction_to_command( you.running.x, you.running.y );
+}
+static void handle_run_delays(const delay_queue_item &delay)
+{
+ // Handle inconsistencies between the delay queue and you.running.
+ // We don't want to send the game into a deadlock.
+ if (!you.running)
+ {
+ pop_delay();
+ return;
+ }
- case ARM_GLOVES:
- if (delay.parm1 == you.equip[EQ_GLOVES])
- you.equip[EQ_GLOVES] = -1;
- break;
+ ASSERT( !you.turn_is_over );
- case ARM_BOOTS:
- if (delay.parm1 == you.equip[EQ_BOOTS])
- you.equip[EQ_BOOTS] = -1;
- break;
- }
- }
+ command_type cmd = CMD_NO_CMD;
+ switch (delay.type)
+ {
+ case DELAY_REST:
+ case DELAY_RUN:
+ cmd = get_running_command();
+ break;
+ case DELAY_TRAVEL:
+ cmd = travel();
+ break;
+ }
- unwear_armour( delay.parm1 );
+ if (cmd != CMD_NO_CMD)
+ {
+ mesclr();
+ process_command(cmd);
+ }
- you.redraw_armour_class = 1;
- you.redraw_evasion = 1;
- break;
+ // If you.running has gone to zero, and the run delay was not
+ // removed, remove it now. This is needed to clean up after
+ // find_travel_pos() function in travel.cc.
+ if (!you.running && is_run_delay(current_delay_action()))
+ pop_delay();
+}
- case DELAY_EAT:
- mpr( "You finish eating." );
- break;
+static void handle_macro_delay()
+{
+ run_macro();
+}
- case DELAY_MEMORIZE:
- mpr( "You finish memorising." );
- add_spell_to_memory( delay.parm1 );
- break;
+void run_macro(const char *macroname)
+{
+ const int currdelay = current_delay_action();
+ if (currdelay != DELAY_NOT_DELAYED && currdelay != DELAY_MACRO)
+ return;
- case DELAY_PASSWALL:
- {
- mpr( "You finish merging with the rock." );
- more(); // or the above message won't be seen
+#ifdef CLUA_BINDINGS
+ if (!clua)
+ {
+ mpr("Lua not initialized", MSGCH_DIAGNOSTICS);
+ stop_delay();
+ return;
+ }
- const int pass_x = delay.parm1;
- const int pass_y = delay.parm2;
+ if (!clua.callbooleanfn(false, "c_macro", "s", macroname))
+ {
+ if (clua.error.length())
+ mpr(clua.error.c_str());
- if (pass_x != 0 && pass_y != 0)
- {
+ stop_delay();
+ }
+ else
+ {
+ start_delay(DELAY_MACRO, 1);
+ }
+#else
+ stop_delay();
+#endif
+}
- switch (grd[ pass_x ][ pass_y ])
- {
- case DNGN_ROCK_WALL:
- case DNGN_STONE_WALL:
- case DNGN_METAL_WALL:
- case DNGN_GREEN_CRYSTAL_WALL:
- case DNGN_WAX_WALL:
- case DNGN_SILVER_STATUE:
- case DNGN_ORANGE_CRYSTAL_STATUE:
- ouch(1 + you.hp, 0, KILLED_BY_PETRIFICATION);
- break;
-
- case DNGN_SECRET_DOOR: // oughtn't happen
- case DNGN_CLOSED_DOOR: // open the door
- grd[ pass_x ][ pass_y ] = DNGN_OPEN_DOOR;
- break;
-
- default:
- break;
- }
+// Returns true if the delay should be interrupted, false if the user function
+// had no opinion on the matter, -1 if the delay should not be interrupted.
+static int userdef_interrupt_activity( const delay_queue_item &idelay,
+ activity_interrupt_type ai,
+ const activity_interrupt_data &at )
+{
+#ifdef CLUA_BINDINGS
+ const int delay = idelay.type;
+ lua_State *ls = clua.state();
+ if (!ls || ai == AI_FORCE_INTERRUPT)
+ return (true);
+
+ // Kludge: We have to continue to support ch_stop_run. :-(
+ if (is_run_delay(delay) && you.running && ai == AI_SEE_MONSTER)
+ {
+ bool stop_run = false;
+ if (clua.callfn("ch_stop_run", "M>b",
+ (const monsters *) at.data, &stop_run))
+ {
+ if (stop_run)
+ return (true);
- //jmf: hmm, what to do. kill the monster? (seems too powerful)
- // displace the monster? randomly teleport the monster?
- // This seems fair: try to move the monster, but if not
- // able to, then kill it.
- int mon = mgrd[ pass_x ][ pass_y ];
- if (mon != NON_MONSTER)
- {
- monster_blink( &menv[ mon ] );
+ // No further processing.
+ return (-1);
+ }
- // recheck square for monster
- mon = mgrd[ pass_x ][ pass_y ];
- if (mon != NON_MONSTER)
- monster_die( &menv[ mon ], KILL_YOU, 0 );
- }
+ // If we get here, ch_stop_run wasn't defined, fall through to the
+ // other handlers.
+ }
+
+ const char *interrupt_name = activity_interrupt_name(ai);
+ const char *act_name = delay_name(delay);
- you.x_pos = pass_x;
- you.y_pos = pass_y;
- redraw_screen();
+ bool ran = clua.callfn("c_interrupt_activity", "1:ssA",
+ act_name, interrupt_name, &at);
+ if (ran)
+ {
+ // If the function returned nil, we want to cease processing.
+ if (lua_isnil(ls, -1))
+ {
+ lua_pop(ls, 1);
+ return (-1);
+ }
- const unsigned char grid = grd[ you.x_pos ][ you.y_pos ];
- if ((grid == DNGN_LAVA || grid == DNGN_DEEP_WATER)
- && !player_is_levitating())
- {
- if (you.species == SP_MERFOLK && grid == DNGN_DEEP_WATER)
- {
- mpr("You fall into the water and return "
- "to your normal form.");
- merfolk_start_swimming();
- }
- else
- {
- fall_into_a_pool( true, grid );
- redraw_screen();
- }
- }
- }
- }
- break;
+ bool stopact = lua_toboolean(ls, -1);
+ lua_pop(ls, 1);
+ if (stopact)
+ return (true);
+ }
- case DELAY_BUTCHER:
- strcpy( info, "You finish " );
- strcat( info, (you.species == SP_TROLL
- || you.species == SP_GHOUL) ? "ripping"
- : "chopping" );
+ if (delay == DELAY_MACRO &&
+ clua.callbooleanfn(true, "c_interrupt_macro",
+ "sA", interrupt_name, &at))
+ return (true);
- strcat( info, " the corpse into pieces." );
- mpr( info );
+#endif
+ return (false);
+}
- turn_corpse_into_chunks( mitm[ delay.parm1 ] );
+// Returns true if the activity should be interrupted, false otherwise.
+static bool should_stop_activity(const delay_queue_item &item,
+ activity_interrupt_type ai,
+ const activity_interrupt_data &at)
+{
+ int userd = userdef_interrupt_activity(item, ai, at);
- if (you.berserker && you.berserk_penalty != NO_BERSERK_PENALTY)
- {
- mpr("You enjoyed that.");
- you.berserk_penalty = 0;
- }
- break;
+ // If the user script wanted to stop the activity or cease processing,
+ // do so.
+ if (userd)
+ return (userd == 1);
- case DELAY_DROP_ITEM:
- // Note: checking if item is dropable is assumed to
- // be done before setting up this delay... this includes
- // quantity (delay.parm2). -- bwr
+ return (ai == AI_FORCE_INTERRUPT ||
+ (Options.activity_interrupts[item.type][ai]));
+}
- // Make sure item still exists.
- if (!is_valid_item( you.inv[ delay.parm1 ] ))
- break;
+void interrupt_activity( activity_interrupt_type ai,
+ const activity_interrupt_data &at )
+{
+ const int delay = current_delay_action();
+ if (delay == DELAY_NOT_DELAYED)
+ return;
- // Must handle unwield_item before we attempt to copy
- // so that temporary brands and such are cleared. -- bwr
- if (delay.parm1 == you.equip[EQ_WEAPON])
- {
- unwield_item( delay.parm1 );
- you.equip[EQ_WEAPON] = -1;
- canned_msg( MSG_EMPTY_HANDED );
- }
+ // First try to stop the current delay.
+ const delay_queue_item &item = you.delay_queue.front();
+
+ if (should_stop_activity(item, ai, at))
+ {
+ stop_delay();
+ return;
+ }
- if (!copy_item_to_grid( you.inv[ delay.parm1 ],
- you.x_pos, you.y_pos, delay.parm2 ))
+ // Check the other queued delays; the first delay that is interruptible
+ // will kill itself and all subsequent delays. This is so that a travel
+ // delay stacked behind a delay such as stair/autopickup will be killed
+ // correctly by interrupts that the simple stair/autopickup delay ignores.
+ for (int i = 1, size = you.delay_queue.size(); i < size; ++i)
+ {
+ const delay_queue_item &it = you.delay_queue[i];
+ if (should_stop_activity(it, ai, at))
+ {
+ // Do we have a queued run delay? If we do, flush the delay queue
+ // so that stop running Lua notifications happen.
+ for (int j = i; j < size; ++j)
+ {
+ if (is_run_delay( you.delay_queue[j].type ))
{
- mpr("Too many items on this level, not dropping the item.");
+ stop_delay();
+ return;
}
- else
- {
- quant_name( you.inv[ delay.parm1 ], delay.parm2,
- DESC_NOCAP_A, str_pass );
+ }
- snprintf( info, INFO_SIZE, "You drop %s.", str_pass );
- mpr(info);
+ // Non-run queued delays can be discarded without any processing.
+ you.delay_queue.erase( you.delay_queue.begin() + i,
+ you.delay_queue.end() );
+ break;
+ }
+ }
+}
- dec_inv_item_quantity( delay.parm1, delay.parm2 );
- }
- break;
+static const char *activity_interrupt_names[] =
+{
+ "force", "keypress", "full_hp", "full_mp", "statue",
+ "hungry", "message", "hp_loss", "burden", "stat",
+ "monster", "monster_attack", "teleport"
+};
- case DELAY_ASCENDING_STAIRS:
- up_stairs();
- untag_followers();
- break;
+const char *activity_interrupt_name(activity_interrupt_type ai)
+{
+ ASSERT( sizeof(activity_interrupt_names)
+ / sizeof(*activity_interrupt_names) == NUM_AINTERRUPTS );
- case DELAY_DESCENDING_STAIRS:
- down_stairs( false, delay.parm1 );
- untag_followers();
- break;
+ if (ai == NUM_AINTERRUPTS)
+ return ("");
- case DELAY_INTERUPTABLE:
- case DELAY_UNINTERUPTABLE:
- // these are simple delays that have no effect when complete
- break;
+ return activity_interrupt_names[ai];
+}
- default:
- mpr( "You finish doing something." );
- break;
- }
+activity_interrupt_type get_activity_interrupt(const std::string &name)
+{
+ ASSERT( sizeof(activity_interrupt_names)
+ / sizeof(*activity_interrupt_names) == NUM_AINTERRUPTS );
- you.wield_change = true;
- print_stats(); // force redraw of the stats
- you.turn_is_over = 1;
- you.delay_queue.pop();
- }
+ for (int i = 0; i < NUM_AINTERRUPTS; ++i)
+ if (name == activity_interrupt_names[i])
+ return activity_interrupt_type(i);
+
+ return (NUM_AINTERRUPTS);
+}
+
+static const char *delay_names[] =
+{
+ "not_delayed", "eat", "armour_on", "armour_off", "jewellery_on",
+ "memorise", "butcher", "autopickup", "weapon_swap", "passwall",
+ "drop_item", "multidrop", "ascending_stairs", "descending_stairs", "run",
+ "rest", "travel", "macro", "interruptible", "uninterruptible",
+};
+
+// Gets a delay given its name.
+// name must be lowercased already!
+delay_type get_delay(const std::string &name)
+{
+ ASSERT( sizeof(delay_names) / sizeof(*delay_names) == NUM_DELAYS );
+
+ for (int i = 0; i < NUM_DELAYS; ++i)
+ {
+ if (name == delay_names[i])
+ return delay_type(i);
}
+
+ // Also check American spellings:
+ if (name == "armor_on")
+ return (DELAY_ARMOUR_ON);
+
+ if (name == "armor_off")
+ return (DELAY_ARMOUR_OFF);
+
+ if (name == "memorize")
+ return (DELAY_MEMORISE);
+
+ if (name == "jewelry_on")
+ return (DELAY_JEWELLERY_ON);
+
+ return (NUM_DELAYS);
+}
+
+const char *delay_name(int delay)
+{
+ ASSERT( sizeof(delay_names) / sizeof(*delay_names) == NUM_DELAYS );
+
+ if (delay < 0 || delay >= NUM_DELAYS)
+ return ("");
+
+ return delay_names[delay];
}