Crossfire Server, Trunk  R21024
food.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "global.h"
20 
21 #include <math.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "ob_methods.h"
26 #include "ob_types.h"
27 #include "sounds.h"
28 #include "sproto.h"
29 
30 static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags);
31 static void eat_special_food(object *who, object *food);
32 static int dragon_eat_flesh(object *op, object *meal);
33 
37 void init_type_food(void) {
41 }
42 
43 static void cursed_food_effects(object *who, object *food) {
44  /* check for hp, sp change */
45  if (food->stats.hp != 0 && !is_wraith_pl(who)) {
46  if (QUERY_FLAG(food, FLAG_CURSED)) {
47  safe_strncpy(who->contr->killer, food->name,
48  sizeof(who->contr->killer));
49  hit_player(who, food->stats.hp, food, AT_POISON, 1);
51  "Eck!...that was poisonous!");
52  } else {
53  if (food->stats.hp > 0)
55  "You begin to feel better.");
56  else
58  "Eck!...that was poisonous!");
59  who->stats.hp += food->stats.hp;
60  }
61  }
62  if (food->stats.sp != 0) {
63  if (QUERY_FLAG(food, FLAG_CURSED)) {
65  "You are drained of mana!");
66  who->stats.sp -= food->stats.sp;
67  if (who->stats.sp < 0)
68  who->stats.sp = 0;
69  } else {
71  "You feel a rush of magical energy!");
72  who->stats.sp += food->stats.sp;
73  /* place limit on max sp from food? */
74  }
75  }
76 }
77 
78 static void eat_common(object* applier, object* food) {
79  char buf[MAX_BUF];
80  int capacity_remaining = MAX_FOOD - applier->stats.food;
81  applier->stats.food += food->stats.food;
82  if (capacity_remaining < food->stats.food)
83  applier->stats.hp += capacity_remaining / 50;
84  else
85  applier->stats.hp += food->stats.food / 50;
86  if (applier->stats.hp > applier->stats.maxhp)
87  applier->stats.hp = applier->stats.maxhp;
88  if (applier->stats.food > MAX_FOOD)
89  applier->stats.food = MAX_FOOD;
90  const int wasted_food = food->stats.food - capacity_remaining;
91  if (wasted_food > 0) {
92  const int thresh = MAX_FOOD/4;
93  int speed_penalty;
94  if (wasted_food > thresh) {
95  speed_penalty = 10;
96  } else {
97  speed_penalty = 5;
98  }
99 
100  if (slow_living_by(applier, speed_penalty)) {
101  snprintf(buf, sizeof(buf),
102  "%s the %s makes you %s full. You feel yourself moving %s "
103  "slower.",
104  food->type == DRINK ? "Drinking" : "Eating", food->name,
105  wasted_food > thresh ? "very" : "rather",
106  wasted_food > thresh ? "much" : "somewhat");
109  }
110  }
111 }
112 
122 static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags) {
123  if (QUERY_FLAG(food, FLAG_NO_PICK)) {
125  MSG_TYPE_APPLY_FAILURE, "You can't %s that!",
126  food->type == DRINK ? "drink" : "eat");
127  return METHOD_OK;
128  }
129 
130  if (applier->type != PLAYER) {
131  applier->stats.hp = applier->stats.maxhp;
132  } else {
133  char buf[MAX_BUF];
134  if (food->type == FLESH && is_dragon_pl(applier)) {
135  /* check if this is a dragon (player), eating some flesh */
136  if (!QUERY_FLAG(food, FLAG_CURSED)) {
137  dragon_eat_flesh(applier, food);
138  eat_common(applier, food);
139  } else {
140  cursed_food_effects(applier, food);
141  }
142  } else if (is_old_wraith_pl(applier)) {
143  /* Check for old wraith player, give them the feeding skill */
144  object *skill = give_skill_by_name(applier, "wraith feed");
145  if (skill) {
147  link_player_skills(applier);
148 
149  snprintf(buf, sizeof(buf), "You have been dead for too long to taste %s, ", food->name);
151  buf);
153  "and seem to have obtained a taste for living flesh.");
154  } else
155  LOG(llevError, "wraith feed skill not found\n");
156  } else if (is_wraith_pl(applier)) {
157  /* Wraith player gets no food from eating. */
158  snprintf(buf, sizeof(buf), "You can no longer taste %s, and do not feel less hungry after %s it.", food->name, food->type == DRINK ? "drinking" : "eating");
160  buf);
161  } else {
162  /* usual case - not a wraith or a dragon eating non-flesh */
163  if (!QUERY_FLAG(food, FLAG_CURSED)) {
164  if (!is_dragon_pl(applier)) {
165  /* eating message for normal players*/
166  if (food->type == DRINK)
167  snprintf(buf, sizeof(buf), "Ahhh...that %s tasted good.", food->name);
168  else
169  snprintf(buf, sizeof(buf), "The %s tasted %s", food->name, food->type == FLESH ? "terrible!" : "good.");
170  } else {
171  /* eating message for dragon players*/
172  snprintf(buf, sizeof(buf), "The %s tasted terrible!", food->name);
173  }
175  eat_common(applier, food);
176  } else {
177  cursed_food_effects(applier, food);
178  }
179 
180  /* special food hack -b.t. */
181  if (food->title)
182  eat_special_food(applier, food);
183  }
184  }
185  apply_handle_yield(food);
187  return METHOD_OK;
188 }
189 
202 static void eat_special_food(object *who, object *food) {
203  object *force;
204  int i, did_one = 0;
205  int8_t k;
206 
207  force = create_archetype(FORCE_NAME);
208 
209  for (i = 0; i < NUM_STATS; i++) {
210  k = get_attr_value(&food->stats, i);
211  if (k) {
212  set_attr_value(&force->stats, i, k);
213  did_one = 1;
214  }
215  }
216 
217  /* check if we can protect the eater */
218  for (i = 0; i < NROFATTACKS; i++) {
219  if (food->resist[i] > 0) {
220  force->resist[i] = food->resist[i]/2;
221  did_one = 1;
222  }
223  }
224  if (did_one) {
225  force->speed = MOVE_PER_SECOND;
226  object_update_speed(force);
227  /* bigger morsel of food = longer effect time */
228  force->duration = food->stats.food/4;
229  SET_FLAG(force, FLAG_IS_USED_UP);
230  SET_FLAG(force, FLAG_APPLIED);
231  change_abil(who, force);
232  object_insert_in_ob(force, who);
233  } else {
235  }
236 
237  fix_object(who);
238 }
239 
253 static int dragon_eat_flesh(object *op, object *meal) {
254  object *skin = NULL; /* pointer to dragon skin force*/
255  object *abil = NULL; /* pointer to dragon ability force*/
256 
257  char buf[MAX_BUF]; /* tmp. string buffer */
258  double chance; /* improvement-chance of one resist type */
259  double totalchance = 1; /* total chance of gaining one resistance */
260  double bonus = 0; /* level bonus (improvement is easier at lowlevel) */
261  double mbonus = 0; /* monster bonus */
262  int atnr_winner[NROFATTACKS]; /* winning candidates for resistance improvement */
263  int winners = 0; /* number of winners */
264  int i; /* index */
265 
266  /* let's make sure and doublecheck the parameters */
267  if (meal->type != FLESH || !is_dragon_pl(op))
268  return 0;
269 
270  /* now grab the 'dragon_skin'- and 'dragon_ability'-forces
271  * from the player's inventory
272  */
273  skin = object_find_by_type_and_arch_name(op, FORCE, "dragon_skin_force");
274  abil = object_find_by_type_and_arch_name(op, FORCE, "dragon_ability_force");
275 
276  /* if either skin or ability are missing, this is an old player
277  * which is not to be considered a dragon -> bail out
278  */
279  if (skin == NULL || abil == NULL)
280  return 0;
281 
282  /*LOG(llevDebug, "-> player: %d, flesh: %d\n", op->level, meal->level);*/
283 
284  /* on to the interesting part: chances for adding resistance */
285  for (i = 0; i < NROFATTACKS; i++) {
286  if (meal->resist[i] > 0 && atnr_is_dragon_enabled(i)) {
287  /* got positive resistance, now calculate improvement chance (0-100) */
288 
289  /* this bonus makes resistance increase easier at lower levels */
290  bonus = (settings.max_level-op->level)*30./((double)settings.max_level);
291  if (i == abil->stats.exp)
292  bonus += 5; /* additional bonus for resistance of ability-focus */
293 
294  /* monster bonus increases with level, because high-level
295  * flesh is too rare
296  */
297  mbonus = op->level*20./((double)settings.max_level);
298 
299  chance = (((double)MIN(op->level+bonus, meal->level+bonus+mbonus))*100./((double)settings.max_level))-skin->resist[i];
300 
301  if (chance >= 0.)
302  chance += 1.;
303  else
304  chance = (chance < -12) ? 0. : 1./pow(2., -chance);
305 
306  /* chance is proportional to amount of resistance (max. 50) */
307  chance *= ((double)(MIN(meal->resist[i], 50)))/50.;
308 
309  /* doubled chance for resistance of ability-focus */
310  if (i == abil->stats.exp)
311  chance = MIN(100., chance*2.);
312 
313  /* now make the throw and save all winners (Don't insert luck bonus here!) */
314  if (RANDOM()%10000 < (unsigned int)(chance*100)) {
315  atnr_winner[winners] = i;
316  winners++;
317  }
318 
319  if (chance >= 0.01)
320  totalchance *= 1-chance/100;
321 
322  /*LOG(llevDebug, " %s: bonus %.1f, chance %.1f\n", attacks[i], bonus, chance);*/
323  }
324  }
325 
326  /* inverse totalchance as until now we have the failure-chance */
327  totalchance = 100-totalchance*100;
328  /* print message according to totalchance */
329  if (totalchance > 50.)
330  snprintf(buf, sizeof(buf), "Hmm! The %s tasted delicious!", meal->name);
331  else if (totalchance > 10.)
332  snprintf(buf, sizeof(buf), "The %s tasted very good.", meal->name);
333  else if (totalchance > 1.)
334  snprintf(buf, sizeof(buf), "The %s tasted good.", meal->name);
335  else if (totalchance > 0.1)
336  snprintf(buf, sizeof(buf), "The %s tasted bland.", meal->name);
337  else if (totalchance >= 0.01)
338  snprintf(buf, sizeof(buf), "The %s had a boring taste.", meal->name);
339  else if (meal->last_eat > 0 && atnr_is_dragon_enabled(meal->last_eat))
340  snprintf(buf, sizeof(buf), "The %s tasted strange.", meal->name);
341  else
342  snprintf(buf, sizeof(buf), "The %s had no taste.", meal->name);
344  buf);
345 
346  /* now choose a winner if we have any */
347  i = -1;
348  if (winners > 0)
349  i = atnr_winner[RANDOM()%winners];
350 
351  if (i >= 0 && i < NROFATTACKS && skin->resist[i] < 95) {
352  /* resistance increased! */
353  skin->resist[i]++;
354  fix_object(op);
355 
357  "Your skin is now more resistant to %s!",
358  change_resist_msg[i]);
359  }
360 
361  /* if this flesh contains a new ability focus, we mark it
362  * into the ability_force and it will take effect on next level
363  */
364  if (meal->last_eat > 0
366  && meal->last_eat != abil->last_eat) {
367  abil->last_eat = meal->last_eat; /* write:last_eat <new attnr focus> */
368 
369  if (meal->last_eat != abil->stats.exp) {
371  "Your metabolism prepares to focus on %s!",
372  change_resist_msg[meal->last_eat]);
374  "The change will happen at level %d",
375  abil->level+1);
376  } else {
378  "Your metabolism will continue to focus on %s.",
379  change_resist_msg[meal->last_eat]);
380  abil->last_eat = 0;
381  }
382  }
383  return 1;
384 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:315
int slow_living_by(object *op, const int speed_penalty)
Definition: attack.c:2191
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:218
void init_type_food(void)
Definition: food.c:37
#define SET_FLAG(xyz, p)
Definition: define.h:223
#define MSG_TYPE_APPLY_FAILURE
Definition: newclient.h:599
void apply_handle_yield(object *tmp)
Definition: apply.c:125
#define FLAG_IS_USED_UP
Definition: define.h:260
Definition: object.h:112
int16_t max_level
Definition: global.h:299
int16_t duration
Definition: object.h:403
int64_t exp
Definition: living.h:47
Definition: object.h:157
static const float MOVE_PER_SECOND
Definition: tod.h:50
int8_t get_attr_value(const living *stats, int attr)
Definition: living.c:313
int16_t sp
Definition: living.h:42
Definition: object.h:187
#define safe_strncpy
Definition: compat.h:23
static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags)
Definition: food.c:122
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:310
#define MSG_TYPE_APPLY
Definition: newclient.h:384
#define MIN(x, y)
Definition: compat.h:17
int16_t hp
Definition: living.h:40
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.c:1752
char method_ret
Definition: ob_methods.h:14
void object_free_drop_inventory(object *ob)
Definition: object.c:1389
int change_abil(object *op, object *tmp)
Definition: living.c:394
const char * title
Definition: object.h:317
#define NDI_RED
Definition: newclient.h:224
int16_t maxhp
Definition: living.h:41
object * create_archetype(const char *name)
Definition: arch.c:617
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2705
static int dragon_eat_flesh(object *op, object *meal)
Definition: food.c:253
int is_wraith_pl(object *op)
Definition: player.c:165
#define MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
Definition: newclient.h:542
#define FLAG_CAN_USE_SKILL
Definition: define.h:322
#define METHOD_OK
Definition: ob_methods.h:15
int is_dragon_pl(const object *op)
Definition: player.c:114
#define snprintf
Definition: win32.h:46
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:380
const char * name
Definition: object.h:311
static void eat_special_food(object *who, object *food)
Definition: food.c:202
static void eat_common(object *applier, object *food)
Definition: food.c:78
EXTERN const char *const change_resist_msg[NROFATTACKS]
Definition: attack.h:135
void register_apply(int ob_type, apply_func method)
Definition: ob_types.c:62
struct pl * contr
Definition: object.h:276
float speed
Definition: object.h:328
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
int is_old_wraith_pl(object *op)
Definition: player.c:177
#define MAX_BUF
Definition: define.h:35
int32_t last_eat
Definition: object.h:356
int16_t resist[NROFATTACKS]
Definition: object.h:341
#define FLAG_CURSED
Definition: define.h:317
Definition: object.h:107
char killer[BIG_NAME]
Definition: player.h:171
#define AT_POISON
Definition: attack.h:86
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:28
#define RANDOM()
Definition: define.h:681
signed char int8_t
Definition: win32.h:158
living stats
Definition: object.h:368
uint8_t type
Definition: object.h:338
struct Settings settings
Definition: init.c:40
static void cursed_food_effects(object *who, object *food)
Definition: food.c:43
#define FLAG_APPLIED
Definition: define.h:235
#define NROFATTACKS
Definition: attack.h:17
#define MSG_TYPE_APPLY_SUCCESS
Definition: newclient.h:598
#define FORCE_NAME
Definition: spells.h:169
void link_player_skills(object *op)
Definition: skill_util.c:112
#define MSG_TYPE_APPLY_CURSED
Definition: newclient.h:600
static const int32_t MAX_FOOD
Definition: define.h:477
#define NDI_UNIQUE
Definition: newclient.h:245
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
#define MSG_TYPE_ATTRIBUTE_RACE
Definition: newclient.h:557
#define FLAG_NO_PICK
Definition: define.h:239
object * object_find_by_type_and_arch_name(const object *who, int type, const char *name)
Definition: object.c:4186
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Definition: attack.c:1871
int16_t level
Definition: object.h:351
void fix_object(object *op)
Definition: living.c:1119
void object_update_speed(object *op)
Definition: object.c:1150
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
Definition: newclient.h:558
int atnr_is_dragon_enabled(int attacknr)
Definition: player.c:95
int32_t food
Definition: living.h:48
Definition: object.h:224