Atrinik Server  4.0
food.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 
31 #include <global.h>
32 #include <arch.h>
33 #include <player.h>
34 #include <object.h>
35 #include <object_methods.h>
36 #include <artifact.h>
37 
39 
41 #define FOOD_MAX 999
42 
46 #define FOOD_CHANCE_ARTIFACT 4
47 
51 #define FOOD_CHANCE_CURSED 25
52 
56 #define FOOD_CHANCE_CURSE_ATTR 4
57 
69 static void
70 food_create_force (object *who, object *food, object *force)
71 {
72  HARD_ASSERT(who != NULL);
73  HARD_ASSERT(food != NULL);
74  HARD_ASSERT(force != NULL);
75 
76  for (int i = 0; i < NUM_STATS; i++) {
77  set_attr_value(&force->stats, i, get_attr_value(&food->stats, i));
78  }
79 
80  memcpy(force->protection, food->protection, sizeof(force->protection));
81 
82  /* Negate all stats for cursed food */
83  if (QUERY_FLAG(food, FLAG_CURSED) || QUERY_FLAG(food, FLAG_DAMNED)) {
84  int mult = QUERY_FLAG(food, FLAG_CURSED) ? 2 : 3;
85 
86  for (int i = 0; i < NUM_STATS; i++) {
87  int val = get_attr_value(&force->stats, i);
88  if (val > 0) {
89  val = -val;
90  }
91 
92  val *= mult;
93  set_attr_value(&force->stats, i, val);
94  }
95 
96  for (int i = 0; i < NROFATTACKS; i++) {
97  if (force->protection[i] > 0) {
98  force->protection[i] = -force->protection[i];
99  }
100 
101  force->protection[i] *= mult;
102  }
103  }
104 
105  if (!DBL_EQUAL(food->speed_left, 0.0)) {
106  force->speed = food->speed_left;
107  }
108 
109  SET_FLAG(force, FLAG_APPLIED);
110  SET_FLAG(force, FLAG_IS_USED_UP);
111 
112  force = object_insert_into(force, who, 0);
113  SOFT_ASSERT(force != NULL,
114  "Failed to insert force from food %s into %s.",
115  object_get_str(food),
116  object_get_str(who));
117 }
118 
142 static void
143 food_eat_special (object *who, object *food)
144 {
145  HARD_ASSERT(who != NULL);
146  HARD_ASSERT(food != NULL);
147 
148  /* Check if we need to create a special force to hold stat
149  * modifications. */
150  bool create_force = false;
151  for (int i = 0; i < NUM_STATS && !create_force; i++) {
152  if (get_attr_value(&food->stats, i) != 0) {
153  create_force = true;
154  }
155  }
156 
157  for (int i = 0; i < NROFATTACKS && !create_force; i++) {
158  if (food->protection[i] != 0) {
159  create_force = true;
160  }
161  }
162 
163  if (create_force) {
164  food_create_force(who, food, arch_get("force"));
165  }
166 
167  /* Check for HP change */
168  if (food->stats.hp) {
169  if (QUERY_FLAG(food, FLAG_CURSED) || QUERY_FLAG(food, FLAG_DAMNED)) {
170  int tmp = food->stats.hp;
171  if (tmp > 0) {
172  tmp = -tmp;
173  }
174 
175  player_set_killer(CONTR(who), food->name);
176 
177  if (QUERY_FLAG(food, FLAG_CURSED)) {
178  who->stats.hp += tmp * 2;
179  } else {
180  who->stats.hp += tmp * 3;
181  }
182 
183  draw_info(COLOR_WHITE, who, "Eck!... that was rotten food!");
184  } else {
185  draw_info(COLOR_WHITE, who, "You begin to feel better.");
186  who->stats.hp += food->stats.hp;
187  if (who->stats.hp > who->stats.maxhp) {
188  who->stats.hp = who->stats.maxhp;
189  }
190  }
191  }
192 
193  /* Check for SP change */
194  if (food->stats.sp) {
195  if (QUERY_FLAG(food, FLAG_CURSED) || QUERY_FLAG(food, FLAG_DAMNED)) {
196  int tmp = food->stats.sp;
197  if (tmp > 0) {
198  tmp = -tmp;
199  }
200 
201  draw_info(COLOR_WHITE, who, "Your mana is drained!");
202 
203  if (QUERY_FLAG(food, FLAG_CURSED)) {
204  who->stats.sp += tmp * 2;
205  } else {
206  who->stats.sp += tmp * 3;
207  }
208 
209  if (who->stats.sp < 0) {
210  who->stats.sp = 0;
211  }
212  } else {
213  draw_info(COLOR_WHITE, who, "You feel a rush of magical energy!");
214  who->stats.sp += food->stats.sp;
215  if (who->stats.sp > who->stats.maxsp) {
216  who->stats.sp = who->stats.maxsp;
217  }
218  }
219  }
220 }
221 
223 static int
224 apply_func (object *op, object *applier, int aflags)
225 {
226  HARD_ASSERT(op != NULL);
227  HARD_ASSERT(applier != NULL);
228 
229  if (applier->type != PLAYER) {
231  }
232 
233  if (applier->stats.food + op->stats.food > FOOD_MAX) {
234  if ((applier->stats.food + op->stats.food) - FOOD_MAX >
235  op->stats.food / 5) {
236  draw_info_format(COLOR_WHITE, applier,
237  "You are too full to %s this right now!",
238  op->type == DRINK ? "drink" : "eat");
239  return OBJECT_METHOD_OK;
240  }
241 
242  if (op->type == FOOD || op->type == FLESH) {
243  draw_info(COLOR_WHITE, applier,
244  "You feel full, but what a waste of food!");
245  } else {
246  draw_info(COLOR_WHITE, applier,
247  "Most of the drink goes down your face not your throat!");
248  }
249  }
250 
251  if (!QUERY_FLAG(op, FLAG_CURSED) && !QUERY_FLAG(op, FLAG_DAMNED)) {
252  int capacity_remaining = FOOD_MAX - applier->stats.food;
253  if (op->type == DRINK) {
254  draw_info_format(COLOR_WHITE, applier,
255  "Ahhh... that %s tasted good.",
256  op->name);
257  } else {
258  draw_info_format(COLOR_WHITE, applier,
259  "The %s tasted %s",
260  op->name,
261  op->type == FLESH ? "terrible!" : "good.");
262  }
263 
264  applier->stats.food =
265  MAX(0, MIN(FOOD_MAX, applier->stats.food + ABS(op->stats.food)));
266  CONTR(applier)->stat_food_consumed += op->stats.food;
267 
268  /* Heal for a bit */
269  applier->stats.hp += MIN(capacity_remaining, op->stats.food) / 50;
270  if (applier->stats.hp > applier->stats.maxhp) {
271  applier->stats.hp = applier->stats.maxhp;
272  }
273  } else {
274  draw_info_format(COLOR_WHITE, applier,
275  "The %s tasted terrible!",
276  op->name);
277  applier->stats.food =
278  MAX(0, MIN(FOOD_MAX, applier->stats.food - ABS(op->stats.food)));
279  }
280 
281  CONTR(applier)->stat_food_num_consumed++;
282 
283  if (op->title != NULL ||
284  QUERY_FLAG(op, FLAG_CURSED) ||
285  QUERY_FLAG(op, FLAG_DAMNED)) {
286  food_eat_special(applier, op);
287  }
288 
289  decrease_ob(op);
290  return OBJECT_METHOD_OK;
291 }
292 
294 static int
296  object **ret,
297  int difficulty,
298  treasure_affinity_t *affinity,
299  int flags)
300 {
301  HARD_ASSERT(op != NULL);
302  HARD_ASSERT(difficulty > 0);
303 
304  /* Avoid processing if the item is already special. */
305  if (process_treasure_is_special(op)) {
307  }
308 
309  if (rndm_chance(FOOD_CHANCE_ARTIFACT)) {
310  artifact_generate(op, difficulty, affinity);
311  }
312 
313  /* Chance for the food to become cursed. */
314  if (!(flags & GT_ONLY_GOOD) && rndm_chance(FOOD_CHANCE_CURSED)) {
315  SET_FLAG(op, FLAG_CURSED);
317 
318  int curses = 0;
319 
320  if (rndm_chance(FOOD_CHANCE_CURSE_ATTR)) {
321  op->stats.food = -((rndm(50, FOOD_MAX) / 10 + 0.5) * 10);
322  curses++;
323  } else {
324  op->stats.food = -op->stats.food;
325  }
326 
327  if (rndm_chance(FOOD_CHANCE_CURSE_ATTR)) {
328  op->stats.hp = -(rndm(1, 5) * difficulty);
329  curses++;
330  }
331 
332  if (rndm_chance(FOOD_CHANCE_CURSE_ATTR)) {
333  op->stats.sp = -(rndm(1, 5) * difficulty);
334  curses++;
335  }
336 
337  for (int i = 0; i < NUM_STATS; i++) {
338  if (!rndm_chance(FOOD_CHANCE_CURSE_ATTR)) {
339  break;
340  }
341 
342  set_attr_value(&op->stats, i, -rndm(1, 5));
343  curses++;
344  }
345 
346  for (int i = 0; i < NROFATTACKS; i++) {
347  if (!rndm_chance(FOOD_CHANCE_CURSE_ATTR)) {
348  break;
349  }
350 
351  op->protection[i] = -rndm(5, 30);
352  curses++;
353  }
354 
355  if (op->title == NULL) {
356  if (curses >= 10) {
357  op->title = add_refcount(shstr_cons.of_vile_poison);
358  } else if (curses >= 5) {
359  op->title = add_refcount(shstr_cons.of_hideous_poison);
360  } else {
361  op->title = add_refcount(shstr_cons.of_poison);
362  }
363  }
364  }
365 
366  return OBJECT_METHOD_OK;
367 }
368 
373 {
374  OBJECT_METHODS(FOOD)->apply_func = apply_func;
375  OBJECT_METHODS(FOOD)->process_treasure_func = process_treasure_func;
376  OBJECT_METHODS(FOOD)->override_treasure_processing = true;
377 }
#define GT_ONLY_GOOD
Definition: treasure.h:63
#define FOOD_MAX
Definition: food.c:41
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:239
#define FLAG_CURSED
Definition: define.h:1154
double speed_left
Definition: object.h:472
#define FLAG_PERM_CURSED
Definition: define.h:1306
object * arch_get(const char *name)
Definition: arch.c:430
#define PLAYER
Definition: define.h:122
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
#define FOOD_CHANCE_CURSED
Definition: food.c:51
static int apply_func(object *op, object *applier, int aflags)
Definition: food.c:224
int16_t maxsp
Definition: living.h:81
int32_t hp
Definition: living.h:72
#define FLESH
Definition: define.h:328
#define FOOD_CHANCE_CURSE_ATTR
Definition: food.c:56
#define decrease_ob(xyz)
Definition: object.h:624
const char * title
Definition: object.h:171
#define FLAG_DAMNED
Definition: define.h:1158
int32_t maxhp
Definition: living.h:75
const char * object_get_str(const object *op)
Definition: object.c:3151
object * object_insert_into(object *op, object *where, int flag)
Definition: object.c:2158
const char * name
Definition: object.h:168
#define SET_FLAG(xyz, p)
Definition: define.h:741
OBJECT_TYPE_INIT_DEFINE(food)
Definition: food.c:372
void player_set_killer(player *pl, const char *killer)
Definition: player.c:2696
#define FLAG_IS_USED_UP
Definition: define.h:976
double speed
Definition: object.h:469
#define OBJECT_METHOD_UNHANDLED
static void food_create_force(object *who, object *food, object *force)
Definition: food.c:70
#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
shstr_constants shstr_cons
Definition: init.c:58
living stats
Definition: object.h:481
uint8_t type
Definition: object.h:360
#define OBJECT_METHODS(type)
#define FOOD
Definition: define.h:142
bool artifact_generate(object *op, int difficulty, treasure_affinity_t *affinity)
Definition: artifact.c:541
#define OBJECT_METHOD_OK
#define DRINK
Definition: define.h:279
static void food_eat_special(object *who, object *food)
Definition: food.c:143
static int process_treasure_func(object *op, object **ret, int difficulty, treasure_affinity_t *affinity, int flags)
Definition: food.c:295
#define FOOD_CHANCE_ARTIFACT
Definition: food.c:46
int16_t food
Definition: living.h:84