File: potion.c

package info (click to toggle)
crossfire 1.75.0-9
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 24,168 kB
  • sloc: ansic: 83,169; sh: 4,659; perl: 1,736; lex: 1,443; makefile: 1,199; python: 43
file content (225 lines) | stat: -rw-r--r-- 8,626 bytes parent folder | download | duplicates (4)
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
/*
 * Crossfire -- cooperative multi-player graphical RPG and adventure game
 *
 * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
 * Copyright (c) 1992 Frank Tore Johansen
 *
 * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
 * welcome to redistribute it under certain conditions. For details, please
 * see COPYING and LICENSE.
 *
 * The authors can be reached via e-mail at <crossfire@metalforge.org>.
 */

/**
 * @file
 * The implementation of the Potion class of objects.
 */

#include "global.h"

#include <string.h>

#include "ob_methods.h"
#include "ob_types.h"
#include "sounds.h"
#include "sproto.h"

static method_ret potion_type_apply(ob_methods *context, object *potion,
    object *applier, int aflags);

/**
 * Initializer for the potion object type.
 */
void init_type_potion(void) {
    register_apply(POTION, potion_type_apply);
}

/**
 * Handles applying a potion, dust, balm, or figurine.
 * @param context The method context
 * @param potion The potion to apply
 * @param applier The object attempting to apply the potion
 * @param aflags Special flags (always apply/unapply)
 * @return METHOD_OK unless failure for some reason.
 */
static method_ret potion_type_apply(ob_methods *context, object *potion,
    object *applier, int aflags) {
    int got_one = 0, i;
    object *force;

    if (applier->type == PLAYER) {
        if (!QUERY_FLAG(potion, FLAG_IDENTIFIED))
            potion = identify(potion);
    }

    play_sound_map(SOUND_TYPE_ITEM, applier, 0, "drink");
    apply_handle_yield(potion);

    /* Potion of restoration - only for players */
    if (applier->type == PLAYER && (potion->attacktype&AT_DEPLETE)) {
        if (QUERY_FLAG(potion, FLAG_CURSED) || QUERY_FLAG(potion, FLAG_DAMNED)) {
            drain_stat(applier);
            fix_object(applier);
            object_decrease_nrof_by_one(potion);
            return METHOD_OK;
        }

        if (remove_depletion(applier, potion->level) == 0)
            draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE,
                "Your potion had no effect.");

        object_decrease_nrof_by_one(potion);
        return METHOD_OK;
    }

    /* improvement potion - only for players */
    if (applier->type == PLAYER && potion->attacktype&AT_GODPOWER) {
        for (i = 1; i < MIN(11, applier->level); i++) {
            if (QUERY_FLAG(potion, FLAG_CURSED) || QUERY_FLAG(potion, FLAG_DAMNED)) {
                if (applier->contr->levhp[i] != 1) {
                    applier->contr->levhp[i] = 1;
                    break;
                }
                if (applier->contr->levsp[i] != 1) {
                    applier->contr->levsp[i] = 1;
                    break;
                }
                if (applier->contr->levgrace[i] != 1) {
                    applier->contr->levgrace[i] = 1;
                    break;
                }
            } else {
                if (applier->contr->levhp[i] < 9) {
                    applier->contr->levhp[i] = 9;
                    break;
                }
                if (applier->contr->levsp[i] < 6) {
                    applier->contr->levsp[i] = 6;
                    break;
                }
                if (applier->contr->levgrace[i] < 3) {
                    applier->contr->levgrace[i] = 3;
                    break;
                }
            }
        }
            /* Just makes checking easier */
        if (i < MIN(11, applier->level))
            got_one = 1;
        if (!QUERY_FLAG(potion, FLAG_CURSED) && !QUERY_FLAG(potion, FLAG_DAMNED)) {
            if (got_one) {
                fix_object(applier);
                draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
                              "The Gods smile upon you and remake you a little more in their image. "
                              "You feel a little more perfect.");
            } else
                draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE,
                              "The potion had no effect - you are already perfect");
        } else {  /* cursed potion */
            if (got_one) {
                fix_object(applier);
                draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED,
                              "The Gods are angry and punish you.");
            } else
                draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED,
                              "You are fortunate that you are so pathetic.");
        }
        object_decrease_nrof_by_one(potion);
        return METHOD_OK;
    }

    /* A potion that casts a spell.  Healing, restore spellpoint
     * (power potion) and heroism all fit into this category.
     * Given the spell object code, there is no limit to the number
     * of spells that potions can be cast, but direction is
     * problematic to try and imbue fireball potions for example.
     */
    if (potion->inv) {
        if (QUERY_FLAG(potion, FLAG_CURSED) || QUERY_FLAG(potion, FLAG_DAMNED)) {
            object *fball;

            draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED,
                "Yech!  Your lungs are on fire!");
            /* Explodes a fireball centered at player */
            fball = create_archetype(EXPLODING_FIREBALL);
            fball->dam_modifier = random_roll(1, applier->level, applier, PREFER_LOW)/5+1;
            fball->stats.maxhp = random_roll(1, applier->level, applier, PREFER_LOW)/10+2;
            object_insert_in_map_at(fball, applier->map, NULL, 0, applier->x, applier->y);
        } else
            cast_spell(applier, potion, applier->facing, potion->inv, NULL);

        object_decrease_nrof_by_one(potion);
        /* if youre dead, no point in doing this... */
        if (!QUERY_FLAG(applier, FLAG_REMOVED))
            fix_object(applier);
        return METHOD_OK;
    }

    /* Deal with protection potions */
    force = NULL;
    for (i = 0; i < NROFATTACKS; i++) {
        if (potion->resist[i]) {
            if (!force)
                force = create_archetype(FORCE_NAME);
            memcpy(force->resist, potion->resist, sizeof(potion->resist));
            force->type = POTION_RESIST_EFFECT;
            force->speed = MOVE_PER_SECOND;
            force->duration = 60;
            break;  /* Only need to find one protection since we cappliery entire batch */
        }
    }
    /* This is a protection potion */
    if (force) {
        char name[MAX_BUF];
        int resist = i;

        /* cursed items last longer */
        if (QUERY_FLAG(potion, FLAG_CURSED) || QUERY_FLAG(potion, FLAG_DAMNED)) {
            draw_ext_info_format(NDI_RED|NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED, "The %s was cursed!", potion->name);
            force->duration *= 10;
            for (i = 0; i < NROFATTACKS; i++)
                if (force->resist[i] > 0)
                    force->resist[i] = -force->resist[i];  /* prot => vuln */
        }
        force->speed_left = -1;
        /* set name for expiry messages */
        snprintf(name, MAX_BUF, "resistance to %s", change_resist_msg[resist]);
        free_string(force->name);
        force->name = add_string(name);
        store_spell_expiry(force);
        force = object_insert_in_ob(force, applier);
        CLEAR_FLAG(potion, FLAG_APPLIED);
        SET_FLAG(force, FLAG_APPLIED);
        change_abil(applier, force);
        object_decrease_nrof_by_one(potion);

        if (potion->other_arch != NULL && applier->map != NULL) {
            object_insert_in_map_at(arch_to_object(potion->other_arch), applier->map, NULL, INS_ON_TOP, applier->x, applier->y);
        }

        return METHOD_OK;
    }

    /* Only thing left are the stat potions */
    if (applier->type == PLAYER) { /* only for players */
        if ((QUERY_FLAG(potion, FLAG_CURSED) || QUERY_FLAG(potion, FLAG_DAMNED))
        && potion->value != 0)
            CLEAR_FLAG(potion, FLAG_APPLIED);
        else
            SET_FLAG(potion, FLAG_APPLIED);
        if (!change_abil(applier, potion))
            draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE,
                "Nothing happened.");
    }

    /* CLEAR_FLAG is so that if the character has other potions
     * that were grouped with the one consumed, his
     * stat will not be raised by them.  fix_object just clears
     * up all the stats.
     */
    CLEAR_FLAG(potion, FLAG_APPLIED);
    fix_object(applier);
    object_decrease_nrof_by_one(potion);
    return METHOD_OK;
}