summaryrefslogtreecommitdiffstats
path: root/stone_soup/crawl-ref/source/delay.cc
blob: b288d1bb85cdc490580de0b4ea3cabd2c0865a91 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
/*
 *  File:     delay.cc
 *  Summary:  Functions for handling multi-turn actions.    
 *
 *  Change History (most recent first):
 *
 * <1> Sept 09, 2001     BWR             Created
 */

#include "AppHdr.h"
#include "externs.h"

#include <stdio.h>
#include <string.h>

#include "delay.h"
#include "enum.h"
#include "food.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 "ouch.h"
#include "output.h"
#include "player.h"
#include "randart.h"
#include "spl-util.h"
#include "stuff.h"

void start_delay( int type, int turns, int parm1, int parm2 )
/***********************************************************/
{
    delay_queue_item  delay;
    
    delay.type = type;
    delay.duration = turns;
    delay.parm1 = parm1;
    delay.parm2 = parm2;

    you.delay_queue.push( delay ); 
}

void stop_delay( void )
/*********************/
{
    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
    // any queuing here can only happen from a single command,
    // 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 );
    }

    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();
        break;

    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();
        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 delay should be increased to reduce the power of
        // this spell. -- bwr
        mpr( "Your meditation is interrupted." );
        you.delay_queue.pop();
        break;

    case DELAY_INTERUPTABLE:  
        // always stopable by definition... 
        // try using a more specific type anyways. -- bwr
        you.delay_queue.pop();
        break;

    case DELAY_EAT:
        // XXX: Large problems with object destruction here... food can
        // be from in the inventory or on the ground and these are
        // still handled quite differently.  Eventually we would like 
        // this to be stoppable, with partial food items implimented. -- bwr
        break; 

    case DELAY_ARMOUR_ON:
    case DELAY_ARMOUR_OFF:
        // These two have the default action of not being interuptable,
        // 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 
        // behaviour, although perhaps the character should have 
        // option of reversing the current action if it would take 
        // less time to get out of the plate mail that's half on
        // than it would take to continue.  Probably too much trouble,
        // and would have to have a prompt... this works just fine. -- bwr
        break;

    case DELAY_AUTOPICKUP:        // one turn... too much trouble
    case DELAY_WEAPON_SWAP:       // one turn... too much trouble 
    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 
    default:
        break;
    }
}

bool you_are_delayed( void )
/**************************/
{
    return (!you.delay_queue.empty());
}

int current_delay_action( void )
/******************************/
{
    return (you_are_delayed() ? you.delay_queue.front().type 
                              : DELAY_NOT_DELAYED);
}

void handle_delay( void )
/***********************/
{
    int   ego;
    char  str_pass[ ITEMNAME_SIZE ];

    if (you_are_delayed()) 
    {
        delay_queue_item &delay = you.delay_queue.front();

        // 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 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;
            }
        }

        // Handle delay:
        if (delay.duration > 0)
        {
#if DEBUG_DIAGNOSTICS
            snprintf( info, INFO_SIZE, "Delay type: %d   duration: %d", 
                      delay.type, delay.duration ); 

            mpr( info, MSGCH_DIAGNOSTICS );
#endif
            delay.duration--;
        }
        else 
        {
            switch (delay.type)
            {
            case DELAY_AUTOPICKUP:
                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 );

                in_name( delay.parm1, DESC_NOCAP_YOUR, str_pass ); 
                snprintf( info, INFO_SIZE, 
                        "You finish putting on %s.", str_pass );
                mpr(info);

                const equipment_type slot =
                        get_armour_slot( you.inv[delay.parm1] );

                if (slot == EQ_BODY_ARMOUR)
                {
                    you.equip[EQ_BODY_ARMOUR] = 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 (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] = delay.parm1;
                        break;
                    case EQ_CLOAK:
                        you.equip[EQ_CLOAK] = delay.parm1;
                        break;
                    case EQ_HELMET:
                        you.equip[EQ_HELMET] = delay.parm1;
                        break;
                    case EQ_GLOVES:
                        you.equip[EQ_GLOVES] = delay.parm1;
                        break;
                    case EQ_BOOTS:
                        you.equip[EQ_BOOTS] = delay.parm1;
                        break;
                    default:
                        break;
                    }
                }

                ego = get_armour_ego_type( you.inv[ delay.parm1 ] );
                if (ego != SPARM_NORMAL)
                {   
                    switch (ego)
                    {
                    case SPARM_RUNNING:
                        strcpy(info, "You feel quick");
                        strcat(info, (you.species == SP_NAGA
                                || you.species == SP_CENTAUR) ? "." : " on your feet.");
                        mpr(info);
                        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;
                    }
                }

                if (is_random_artefact( you.inv[ delay.parm1 ] ))
                    use_randart( delay.parm1 );

                if (item_cursed( you.inv[ delay.parm1 ] ))
                    mpr( "Oops, that feels deathly cold." );

                you.redraw_armour_class = 1;
                you.redraw_evasion = 1;
                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);

                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)
                    {
                    case EQ_SHIELD:
                        if (delay.parm1 == you.equip[EQ_SHIELD])
                            you.equip[EQ_SHIELD] = -1;
                        break;

                    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;
                    }
                }

                unwear_armour( delay.parm1 );

                you.redraw_armour_class = 1;
                you.redraw_evasion = 1;
                break;
            }
            case DELAY_EAT:
                mpr( "You finish eating." );
                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 (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;
                        }

                //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 ] );

                            // recheck square for monster
                            mon = mgrd[ pass_x ][ pass_y ];
                            if (mon != NON_MONSTER)
                                monster_die( &menv[ mon ], KILL_YOU, 0 );
                        }

                        you.x_pos = pass_x;
                        you.y_pos = pass_y;
                        redraw_screen();

                        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; 

            case DELAY_BUTCHER:
                strcpy( info, "You finish " );
                strcat( info, (you.species == SP_TROLL
                                || you.species == SP_GHOUL) ? "ripping"
                                                            : "chopping" );

                strcat( info, " the corpse into pieces." );
                mpr( info );

                turn_corpse_into_chunks( mitm[ delay.parm1 ] );

                if (you.berserker && you.berserk_penalty != NO_BERSERK_PENALTY)
                {
                    mpr("You enjoyed that.");
                    you.berserk_penalty = 0;
                }
                break;

            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

                // Make sure item still exists.
                if (!is_valid_item( you.inv[ delay.parm1 ] ))
                    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 );
                }

                if (!copy_item_to_grid( you.inv[ delay.parm1 ], 
                                        you.x_pos, you.y_pos, delay.parm2 ))
                {
                    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 );

                    snprintf( info, INFO_SIZE, "You drop %s.", str_pass );
                    mpr(info);

                    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_INTERUPTABLE:
            case DELAY_UNINTERUPTABLE:
                // these are simple delays that have no effect when complete
                break;

            default:
                mpr( "You finish doing something." );
                break; 
            }

            you.wield_change = true;
            print_stats();  // force redraw of the stats
            you.turn_is_over = 1;
            you.delay_queue.pop();
        }
    }
}