Crossfire Server, Trunk  R213250
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  if (food->type == DRINK) {
186  play_sound_map(SOUND_TYPE_ITEM, applier, 0, "drink");
187  }
188  apply_handle_yield(food);
190  return METHOD_OK;
191 }
192 
205 static void eat_special_food(object *who, object *food) {
206  object *force;
207  int i, did_one = 0;
208  int8_t k;
209 
210  force = create_archetype(FORCE_NAME);
211 
212  for (i = 0; i < NUM_STATS; i++) {
213  k = get_attr_value(&food->stats, i);
214  if (k) {
215  set_attr_value(&force->stats, i, k);
216  did_one = 1;
217  }
218  }
219 
220  /* check if we can protect the eater */
221  for (i = 0; i < NROFATTACKS; i++) {
222  if (food->resist[i] > 0) {
223  force->resist[i] = food->resist[i]/2;
224  did_one = 1;
225  }
226  }
227  if (did_one) {
228  force->speed = MOVE_PER_SECOND;
229  object_update_speed(force);
230  /* bigger morsel of food = longer effect time */
231  force->duration = food->stats.food/4;
232  SET_FLAG(force, FLAG_IS_USED_UP);
233  SET_FLAG(force, FLAG_APPLIED);
234  change_abil(who, force);
235  object_insert_in_ob(force, who);
236  } else {
238  }
239 
240  fix_object(who);
241 }
242 
256 static int dragon_eat_flesh(object *op, object *meal) {
257  object *skin = NULL; /* pointer to dragon skin force*/
258  object *abil = NULL; /* pointer to dragon ability force*/
259 
260  char buf[MAX_BUF]; /* tmp. string buffer */
261  double chance; /* improvement-chance of one resist type */
262  double totalchance = 1; /* total chance of gaining one resistance */
263  double bonus = 0; /* level bonus (improvement is easier at lowlevel) */
264  double mbonus = 0; /* monster bonus */
265  int atnr_winner[NROFATTACKS]; /* winning candidates for resistance improvement */
266  int winners = 0; /* number of winners */
267  int i; /* index */
268 
269  /* let's make sure and doublecheck the parameters */
270  if (meal->type != FLESH || !is_dragon_pl(op))
271  return 0;
272 
273  /* now grab the 'dragon_skin'- and 'dragon_ability'-forces
274  * from the player's inventory
275  */
276  skin = object_find_by_type_and_arch_name(op, FORCE, "dragon_skin_force");
277  abil = object_find_by_type_and_arch_name(op, FORCE, "dragon_ability_force");
278 
279  /* if either skin or ability are missing, this is an old player
280  * which is not to be considered a dragon -> bail out
281  */
282  if (skin == NULL || abil == NULL)
283  return 0;
284 
285  /*LOG(llevDebug, "-> player: %d, flesh: %d\n", op->level, meal->level);*/
286 
287  /* on to the interesting part: chances for adding resistance */
288  for (i = 0; i < NROFATTACKS; i++) {
289  if (meal->resist[i] > 0 && atnr_is_dragon_enabled(i)) {
290  /* got positive resistance, now calculate improvement chance (0-100) */
291 
292  /* this bonus makes resistance increase easier at lower levels */
293  bonus = (settings.max_level-op->level)*30./((double)settings.max_level);
294  if (i == abil->stats.exp)
295  bonus += 5; /* additional bonus for resistance of ability-focus */
296 
297  /* monster bonus increases with level, because high-level
298  * flesh is too rare
299  */
300  mbonus = op->level*20./((double)settings.max_level);
301 
302  chance = (((double)MIN(op->level+bonus, meal->level+bonus+mbonus))*100./((double)settings.max_level))-skin->resist[i];
303 
304  if (chance >= 0.)
305  chance += 1.;
306  else
307  chance = (chance < -12) ? 0. : 1./pow(2., -chance);
308 
309  /* chance is proportional to amount of resistance (max. 50) */
310  chance *= ((double)(MIN(meal->resist[i], 50)))/50.;
311 
312  /* doubled chance for resistance of ability-focus */
313  if (i == abil->stats.exp)
314  chance = MIN(100., chance*2.);
315 
316  /* now make the throw and save all winners (Don't insert luck bonus here!) */
317  if (RANDOM()%10000 < (unsigned int)(chance*100)) {
318  atnr_winner[winners] = i;
319  winners++;
320  }
321 
322  if (chance >= 0.01)
323  totalchance *= 1-chance/100;
324 
325  /*LOG(llevDebug, " %s: bonus %.1f, chance %.1f\n", attacks[i], bonus, chance);*/
326  }
327  }
328 
329  /* inverse totalchance as until now we have the failure-chance */
330  totalchance = 100-totalchance*100;
331  /* print message according to totalchance */
332  if (totalchance > 50.)
333  snprintf(buf, sizeof(buf), "Hmm! The %s tasted delicious!", meal->name);
334  else if (totalchance > 10.)
335  snprintf(buf, sizeof(buf), "The %s tasted very good.", meal->name);
336  else if (totalchance > 1.)
337  snprintf(buf, sizeof(buf), "The %s tasted good.", meal->name);
338  else if (totalchance > 0.1)
339  snprintf(buf, sizeof(buf), "The %s tasted bland.", meal->name);
340  else if (totalchance >= 0.01)
341  snprintf(buf, sizeof(buf), "The %s had a boring taste.", meal->name);
342  else if (meal->last_eat > 0 && atnr_is_dragon_enabled(meal->last_eat))
343  snprintf(buf, sizeof(buf), "The %s tasted strange.", meal->name);
344  else
345  snprintf(buf, sizeof(buf), "The %s had no taste.", meal->name);
347  buf);
348 
349  /* now choose a winner if we have any */
350  i = -1;
351  if (winners > 0)
352  i = atnr_winner[RANDOM()%winners];
353 
354  if (i >= 0 && i < NROFATTACKS && skin->resist[i] < 95) {
355  /* resistance increased! */
356  skin->resist[i]++;
357  fix_object(op);
358 
360  "Your skin is now more resistant to %s!",
361  change_resist_msg[i]);
362  }
363 
364  /* if this flesh contains a new ability focus, we mark it
365  * into the ability_force and it will take effect on next level
366  */
367  if (meal->last_eat > 0
369  && meal->last_eat != abil->last_eat) {
370  abil->last_eat = meal->last_eat; /* write:last_eat <new attnr focus> */
371 
372  if (meal->last_eat != abil->stats.exp) {
374  "Your metabolism prepares to focus on %s!",
375  change_resist_msg[meal->last_eat]);
377  "The change will happen at level %d",
378  abil->level+1);
379  } else {
381  "Your metabolism will continue to focus on %s.",
382  change_resist_msg[meal->last_eat]);
383  abil->last_eat = 0;
384  }
385  }
386  return 1;
387 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:316
int slow_living_by(object *op, const int speed_penalty)
Definition: attack.c:2185
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:219
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 link_player_skills(object *op)
Definition: player.c:279
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:305
int16_t duration
Definition: object.h:405
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:314
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:311
#define MSG_TYPE_APPLY
Definition: newclient.h:384
#define MIN(x, y)
Definition: compat.h:17
int16_t hp
Definition: living.h:40
#define SOUND_TYPE_ITEM
Definition: newclient.h:310
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.c:1773
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:395
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:620
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2707
static int dragon_eat_flesh(object *op, object *meal)
Definition: food.c:256
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:205
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:357
int16_t resist[NROFATTACKS]
Definition: object.h:342
#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:369
uint8_t type
Definition: object.h:339
struct Settings settings
Definition: init.c:39
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
#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
void play_sound_map(int8_t sound_type, object *emitter, int dir, const char *action)
Definition: sounds.c:107
#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:4187
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Definition: attack.c:1862
int16_t level
Definition: object.h:352
void fix_object(object *op)
Definition: living.c:1120
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