Atrinik Server  4.0
potion.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
32 #include <global.h>
33 #include <arch.h>
34 #include <player.h>
35 #include <object.h>
36 #include <object_methods.h>
37 #include <artifact.h>
38 
40 
44 #define POTION_CHANCE_CURSED 3
45 
48 #define POTION_CHANCE_DAMNED 2
49 
58 static void
59 potion_apply_spell (object *op, object *applier)
60 {
61  HARD_ASSERT(op != NULL);
62  HARD_ASSERT(applier != NULL);
63 
64  SOFT_ASSERT(op->stats.sp >= 0 && op->stats.sp < NROFREALSPELLS,
65  "Potion with invalid spell ID %d: %s, applier: %s",
66  op->stats.sp,
67  object_get_str(op),
68  object_get_str(applier));
69 
70  /* If it's a cursed potion of remove depletion, drain stats instead. */
71  if (op->stats.sp == SP_REMOVE_DEPLETION &&
73  /* Drain 2 stats if the potion is cursed, 4 if it's damned. */
74  for (int i = QUERY_FLAG(op, FLAG_DAMNED) ? 0 : 2; i < 4; i++) {
75  drain_stat(applier);
76  }
77 
78  insert_spell_effect("meffect_purple",
79  applier->map,
80  applier->x,
81  applier->y);
82  play_sound_map(applier->map,
83  CMD_SOUND_EFFECT,
84  "poison.ogg",
85  applier->x,
86  applier->y,
87  0,
88  0);
89  return;
90  }
91 
92  /* Fire in the player's facing direction, unless the spell is
93  * something like healing or cure disease. */
94  int direction = 0;
95  if (!(spells[op->stats.sp].flags & SPELL_DESC_SELF)) {
96  direction = applier->direction;
97  }
98 
99  cast_spell(applier,
100  op,
101  direction,
102  op->stats.sp,
103  1,
104  CAST_POTION,
105  NULL);
106 }
107 
116 static void
117 potion_apply_effects (object *op, object *applier)
118 {
119  HARD_ASSERT(op != NULL);
120  HARD_ASSERT(applier != NULL);
121 
122  /* Create a force and copy the effects in. */
123  object *force = arch_get("force");
124  force->type = POTION_EFFECT;
125  /* Copy the amount of time the effect should last. */
126  force->stats.food = op->stats.food;
127  SET_FLAG(force, FLAG_IS_USED_UP);
128 
129  /* Make sure the effect lasts for at least a little while. */
130  if (force->stats.food <= 0) {
131  force->stats.food = 1;
132  }
133 
134  if (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED)) {
135  int protection;
136 
137  /* The potion is cursed/damned, so the negative effects stay
138  * around longer. */
139  force->stats.food *= 3;
140 
141  /* Copy over protection values to the force from the potion, but
142  * negate them first. Attacks are ignored, as it's not actually
143  * possible to store negative attack values in objects. */
144  for (int i = 0; i < NROFATTACKS; i++) {
145  protection = -ABS(op->protection[i]);
146 
147  /* If the potion is damned, the effects worsen... */
148  if (QUERY_FLAG(op, FLAG_DAMNED)) {
149  protection *= 2;
150  }
151 
152  /* Actually set the protection value, but make sure it's in a
153  * valid range. */
154  force->protection[i] = MIN(100, MAX(-100, protection));
155  }
156 
157  insert_spell_effect("meffect_purple",
158  applier->map,
159  applier->x,
160  applier->y);
161  play_sound_map(applier->map,
162  CMD_SOUND_EFFECT,
163  "poison.ogg",
164  applier->x,
165  applier->y,
166  0,
167  0);
168  } else {
169  memcpy(force->protection, op->protection, sizeof(op->protection));
170  memcpy(force->attack, op->attack, sizeof(op->attack));
171 
172  insert_spell_effect("meffect_green",
173  applier->map,
174  applier->x,
175  applier->y);
176  play_sound_map(applier->map,
177  CMD_SOUND_EFFECT,
178  "magic_default.ogg",
179  applier->x,
180  applier->y,
181  0,
182  0);
183  }
184 
185  /* Copy over stat values. */
186  for (int i = 0; i < NUM_STATS; i++) {
187  /* Get the stat value from the potion. */
188  int val = get_attr_value(&op->stats, i);
189  /* No value, nothing to do. */
190  if (val == 0) {
191  continue;
192  }
193 
194  /* If the potion is cursed/damned and the stat increase is
195  * positive, reverse it. */
196  if (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED)) {
197  val = -ABS(val);
198 
199  /* The potion is damned, so the negative effect is worse. */
200  if (QUERY_FLAG(op, FLAG_DAMNED)) {
201  val *= 2;
202  }
203  }
204 
205  /* Now set the stat value of the force to the one calculate
206  * above, but make sure it doesn't overflow an int8_t. */
207  change_attr_value(&force->stats,
208  i,
209  MIN(INT8_MAX, MAX(INT8_MIN, val)));
210  }
211 
212  /* Insert the force into the player and apply it. */
213  force->speed_left = -1;
214  force = object_insert_into(force, applier, 0);
215  SOFT_ASSERT(force != NULL,
216  "Failed to insert potion effect force into %s",
217  object_get_str(applier));
218  SET_FLAG(force, FLAG_APPLIED);
219 
220  if (!living_update(applier)) {
221  draw_info(COLOR_WHITE, applier, "Nothing happened.");
222  }
223 }
224 
226 static int
227 apply_func (object *op, object *applier, int aflags)
228 {
229  HARD_ASSERT(op != NULL);
230  HARD_ASSERT(applier != NULL);
231 
232  if (applier->type != PLAYER) {
234  }
235 
236  /* Use magic devices skill for using potions. */
237  if (!change_skill(applier, SK_MAGIC_DEVICES)) {
238  return OBJECT_METHOD_ERROR;
239  }
240 
241  /* We are using it, so we now know what it does. */
242  if (!QUERY_FLAG(op, FLAG_IDENTIFIED)) {
243  identify(op);
244  }
245 
246  CONTR(applier)->stat_potions_used++;
247 
248  /* REMOVEME:
249  * Old potions of remove depletion used a custom flag instead of using
250  * the remove depletion spell, so update them and remove the flag. */
251  if (op->last_eat == 1) {
252  op->stats.sp = SP_REMOVE_DEPLETION;
253  op->last_eat = 0;
254  }
255 
256  if (op->stats.sp != SP_NO_SPELL) {
257  potion_apply_spell(op, applier);
258  } else {
259  potion_apply_effects(op, applier);
260  }
261 
262  decrease_ob(op);
263  return OBJECT_METHOD_OK;
264 }
265 
267 static int
269  object **ret,
270  int difficulty,
271  treasure_affinity_t *affinity,
272  int flags)
273 {
274  HARD_ASSERT(op != NULL);
275  HARD_ASSERT(difficulty > 0);
276 
277  /* Avoid processing if the item is already special. */
278  if (process_treasure_is_special(op)) {
280  }
281 
282  bool generated_art = false;
283  switch (op->sub_type) {
284  case POTION_NORMAL:
285  generated_art = artifact_generate(op, difficulty, affinity);
286  break;
287 
288  case POTION_BALM:
289  op->stats.sp = spell_get_random(difficulty, SPELL_USE_BALM);
290  break;
291 
292  case POTION_DUST:
293  op->stats.sp = spell_get_random(difficulty, SPELL_USE_DUST);
294  break;
295 
296  default:
297  break;
298  }
299 
300  if (op->stats.sp == SP_NO_SPELL && !generated_art) {
301  log_error("Failed to generate a spell/artifact for potion: %s",
302  object_get_str(op));
303  object_remove(op, 0);
304  object_destroy(op);
305  return OBJECT_METHOD_ERROR;
306  }
307 
309 
310  /* Calculate the level. Essentially -20 below the difficulty at worst, or
311  * +15 above the difficulty at best. */
312  int level = difficulty - 20 + rndm(0, 35);
313  level = MIN(MAX(level, 1), MAXLEVEL);
314  op->level = level;
315 
316  /* Chance to make special potions damned/cursed, unless we're only
317  * generating good treasures. */
318  if (op->stats.sp == SP_NO_SPELL &&
319  !(flags & GT_ONLY_GOOD) &&
320  rndm_chance(POTION_CHANCE_CURSED)) {
321  if (rndm_chance(POTION_CHANCE_DAMNED)) {
322  SET_FLAG(op, FLAG_DAMNED);
323  } else {
324  SET_FLAG(op, FLAG_CURSED);
325  }
326  }
327 
328  return OBJECT_METHOD_OK;
329 }
330 
335 {
336  OBJECT_METHODS(POTION)->apply_func = apply_func;
337  OBJECT_METHODS(POTION)->process_treasure_func = process_treasure_func;
338  OBJECT_METHODS(POTION)->override_treasure_processing = true;
339 }
#define GT_ONLY_GOOD
Definition: treasure.h:63
void object_destroy(object *ob)
Definition: object.c:1441
void drain_stat(object *op)
Definition: living.c:353
#define FLAG_CURSED
Definition: define.h:1154
void identify(object *op)
Definition: item.c:1364
#define NROFREALSPELLS
Definition: define.h:664
double speed_left
Definition: object.h:472
#define OBJECT_METHOD_ERROR
object * arch_get(const char *name)
Definition: arch.c:430
#define POTION_NORMAL
Definition: define.h:636
#define PLAYER
Definition: define.h:122
#define SPELL_DESC_SELF
Definition: spells.h:120
int8_t get_attr_value(const living *stats, int attr)
Definition: living.c:305
int16_t sp
Definition: living.h:78
#define QUERY_FLAG(xyz, p)
Definition: define.h:761
static void potion_apply_spell(object *op, object *applier)
Definition: potion.c:59
void object_remove(object *op, int flags)
Definition: object.c:1623
uint8_t attack[NROFATTACKS]
Definition: object.h:436
int cast_spell(object *op, object *caster, int dir, int type, int ability, int item, const char *stringarg)
Definition: spell_util.c:192
#define decrease_ob(xyz)
Definition: object.h:624
#define FLAG_DAMNED
Definition: define.h:1158
int16_t y
Definition: object.h:276
const char * object_get_str(const object *op)
Definition: object.c:3151
int8_t direction
Definition: object.h:350
#define POTION_DUST
Definition: define.h:640
object * object_insert_into(object *op, object *where, int flag)
Definition: object.c:2158
struct mapdef * map
Definition: object.h:139
#define SPELL_USE_BALM
Definition: spells.h:89
#define SP_NO_SPELL
Definition: spells.h:202
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:277
#define SET_FLAG(xyz, p)
Definition: define.h:741
uint32_t flags
Definition: spells.h:189
int insert_spell_effect(const char *archname, mapstruct *m, int x, int y)
Definition: spell_util.c:118
#define FLAG_IS_USED_UP
Definition: define.h:976
int change_skill(object *who, int sk_index)
Definition: skill_util.c:340
#define OBJECT_METHOD_UNHANDLED
#define POTION
Definition: define.h:138
int16_t x
Definition: object.h:273
#define POTION_EFFECT
Definition: define.h:469
int16_t last_eat
Definition: object.h:319
spell_struct spells[NROFREALSPELLS]
Definition: spellist.h:34
void play_sound_map(mapstruct *map, int type, const char *filename, int x, int y, int loop, int volume)
Definition: sounds.c:159
#define MAXLEVEL
Definition: global.h:221
#define FLAG_APPLIED
Definition: define.h:1182
int8_t protection[NROFATTACKS]
Definition: object.h:439
bool process_treasure_is_special(object *op)
#define NUM_STATS
Definition: living.h:49
#define SPELL_USE_DUST
Definition: spells.h:91
living stats
Definition: object.h:481
uint8_t type
Definition: object.h:360
#define OBJECT_METHODS(type)
#define POTION_BALM
Definition: define.h:638
bool artifact_generate(object *op, int difficulty, treasure_affinity_t *affinity)
Definition: artifact.c:541
int spell_get_random(int level, int flags)
Definition: spell_util.c:1230
static int process_treasure_func(object *op, object **ret, int difficulty, treasure_affinity_t *affinity, int flags)
Definition: potion.c:268
#define POTION_CHANCE_CURSED
Definition: potion.c:44
int living_update(object *op)
Definition: living.c:1661
#define OBJECT_METHOD_OK
#define FLAG_IS_MAGICAL
Definition: define.h:1129
static int apply_func(object *op, object *applier, int aflags)
Definition: potion.c:227
OBJECT_TYPE_INIT_DEFINE(potion)
Definition: potion.c:334
#define CAST_POTION
Definition: spells.h:283
#define POTION_CHANCE_DAMNED
Definition: potion.c:48
int8_t level
Definition: object.h:347
#define FLAG_IDENTIFIED
Definition: define.h:980
int16_t food
Definition: living.h:84
uint8_t sub_type
Definition: object.h:363
static void potion_apply_effects(object *op, object *applier)
Definition: potion.c:117