Crossfire Server, Trunk
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(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 check_heal_and_mana(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 
121 static method_ret food_type_apply(object *food, object *applier, int aflags) {
122  (void)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  }
140  check_heal_and_mana(applier, food);
141  } else if (is_old_wraith_pl(applier)) {
142  /* Check for old wraith player, give them the feeding skill */
143  object *skill = give_skill_by_name(applier, "wraith feed");
144  if (skill) {
146  link_player_skills(applier);
147 
148  snprintf(buf, sizeof(buf), "You have been dead for too long to taste %s, ", food->name);
150  buf);
152  "and seem to have obtained a taste for living flesh.");
153  } else
154  LOG(llevError, "wraith feed skill not found\n");
155  } else if (is_wraith_pl(applier)) {
156  /* Wraith player gets no food from eating. */
157  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");
159  buf);
160  } else {
161  /* usual case - not a wraith or a dragon eating non-flesh */
162  if (!QUERY_FLAG(food, FLAG_CURSED)) {
163  if (!is_dragon_pl(applier)) {
164  /* eating message for normal players*/
165  if (food->type == DRINK)
166  snprintf(buf, sizeof(buf), "Ahhh...that %s tasted good.", food->name);
167  else
168  snprintf(buf, sizeof(buf), "The %s tasted %s", food->name, food->type == FLESH ? "terrible!" : "good.");
169  } else {
170  /* eating message for dragon players*/
171  snprintf(buf, sizeof(buf), "The %s tasted terrible!", food->name);
172  }
174  eat_common(applier, food);
175  }
176  check_heal_and_mana(applier, food);
177 
178  /* special food hack -b.t. */
179  if (food->title)
180  eat_special_food(applier, food);
181  }
182  }
183  if (food->type == DRINK) {
184  play_sound_map(SOUND_TYPE_ITEM, applier, 0, "drink");
185  }
186  apply_handle_yield(food);
188  return METHOD_OK;
189 }
190 
203 static void eat_special_food(object *who, object *food) {
204  object *force;
205  int i, did_one = 0;
206  int8_t k;
207 
209 
210  for (i = 0; i < NUM_STATS; i++) {
211  k = get_attr_value(&food->stats, i);
212  if (k) {
213  set_attr_value(&force->stats, i, k);
214  did_one = 1;
215  }
216  }
217 
218  /* check if we can protect the eater */
219  for (i = 0; i < NROFATTACKS; i++) {
220  if (food->resist[i] > 0) {
221  force->resist[i] = food->resist[i]/2;
222  did_one = 1;
223  }
224  }
225  if (did_one) {
226  force->speed = MOVE_PER_SECOND;
228  /* bigger morsel of food = longer effect time */
229  force->duration = food->stats.food/4;
234  if (food->other_arch != NULL && who->map != NULL) {
236  }
237  } else {
239  }
240 
241  fix_object(who);
242 }
243 
257 static int dragon_eat_flesh(object *op, object *meal) {
258  object *skin = NULL; /* pointer to dragon skin force*/
259  object *abil = NULL; /* pointer to dragon ability force*/
260 
261  char buf[MAX_BUF]; /* tmp. string buffer */
262  double chance; /* improvement-chance of one resist type */
263  double totalchance = 1; /* total chance of gaining one resistance */
264  double bonus = 0; /* level bonus (improvement is easier at lowlevel) */
265  double mbonus = 0; /* monster bonus */
266  int atnr_winner[NROFATTACKS]; /* winning candidates for resistance improvement */
267  int winners = 0; /* number of winners */
268  int i; /* index */
269 
270  /* let's make sure and doublecheck the parameters */
271  if (meal->type != FLESH || !is_dragon_pl(op))
272  return 0;
273 
274  /* now grab the 'dragon_skin'- and 'dragon_ability'-forces
275  * from the player's inventory
276  */
277  skin = object_find_by_type_and_arch_name(op, FORCE, "dragon_skin_force");
278  abil = object_find_by_type_and_arch_name(op, FORCE, "dragon_ability_force");
279 
280  /* if either skin or ability are missing, this is an old player
281  * which is not to be considered a dragon -> bail out
282  */
283  if (skin == NULL || abil == NULL)
284  return 0;
285 
286  /*LOG(llevDebug, "-> player: %d, flesh: %d\n", op->level, meal->level);*/
287 
288  /* on to the interesting part: chances for adding resistance */
289  for (i = 0; i < NROFATTACKS; i++) {
290  if (meal->resist[i] > 0 && atnr_is_dragon_enabled(i)) {
291  /* got positive resistance, now calculate improvement chance (0-100) */
292 
293  /* this bonus makes resistance increase easier at lower levels */
294  bonus = (settings.max_level-op->level)*30./((double)settings.max_level);
295  if (i == abil->stats.exp)
296  bonus += 5; /* additional bonus for resistance of ability-focus */
297 
298  /* monster bonus increases with level, because high-level
299  * flesh is too rare
300  */
301  mbonus = op->level*20./((double)settings.max_level);
302 
303  chance = (((double)MIN(op->level+bonus, meal->level+bonus+mbonus))*100./((double)settings.max_level))-skin->resist[i];
304 
305  if (chance >= 0.)
306  chance += 1.;
307  else
308  chance = (chance < -12) ? 0. : 1./pow(2., -chance);
309 
310  /* chance is proportional to amount of resistance (max. 50) */
311  chance *= ((double)(MIN(meal->resist[i], 50)))/50.;
312 
313  /* doubled chance for resistance of ability-focus */
314  if (i == abil->stats.exp)
315  chance = MIN(100., chance*2.);
316 
317  /* now make the throw and save all winners (Don't insert luck bonus here!) */
318  if (RANDOM()%10000 < (unsigned int)(chance*100)) {
319  atnr_winner[winners] = i;
320  winners++;
321  }
322 
323  if (chance >= 0.01)
324  totalchance *= 1-chance/100;
325 
326  /*LOG(llevDebug, " %s: bonus %.1f, chance %.1f\n", attacks[i], bonus, chance);*/
327  }
328  }
329 
330  /* inverse totalchance as until now we have the failure-chance */
331  totalchance = 100-totalchance*100;
332  /* print message according to totalchance */
333  if (totalchance > 50.)
334  snprintf(buf, sizeof(buf), "Hmm! The %s tasted delicious!", meal->name);
335  else if (totalchance > 10.)
336  snprintf(buf, sizeof(buf), "The %s tasted very good.", meal->name);
337  else if (totalchance > 1.)
338  snprintf(buf, sizeof(buf), "The %s tasted good.", meal->name);
339  else if (totalchance > 0.1)
340  snprintf(buf, sizeof(buf), "The %s tasted bland.", meal->name);
341  else if (totalchance >= 0.01)
342  snprintf(buf, sizeof(buf), "The %s had a boring taste.", meal->name);
343  else if (meal->last_eat > 0 && atnr_is_dragon_enabled(meal->last_eat))
344  snprintf(buf, sizeof(buf), "The %s tasted strange.", meal->name);
345  else
346  snprintf(buf, sizeof(buf), "The %s had no taste.", meal->name);
348  buf);
349 
350  /* now choose a winner if we have any */
351  i = -1;
352  if (winners > 0)
353  i = atnr_winner[RANDOM()%winners];
354 
355  if (i >= 0 && i < NROFATTACKS && skin->resist[i] < 95) {
356  /* resistance increased! */
357  skin->resist[i]++;
358  fix_object(op);
359 
361  "Your skin is now more resistant to %s!",
362  change_resist_msg[i]);
363  }
364 
365  /* if this flesh contains a new ability focus, we mark it
366  * into the ability_force and it will take effect on next level
367  */
368  if (meal->last_eat > 0
370  && meal->last_eat != abil->last_eat) {
371  abil->last_eat = meal->last_eat; /* write:last_eat <new attnr focus> */
372 
373  if (meal->last_eat != abil->stats.exp) {
375  "Your metabolism prepares to focus on %s!",
376  change_resist_msg[meal->last_eat]);
378  "The change will happen at level %d",
379  abil->level+1);
380  } else {
382  "Your metabolism will continue to focus on %s.",
383  change_resist_msg[meal->last_eat]);
384  abil->last_eat = 0;
385  }
386  }
387  return 1;
388 }
init_type_food
void init_type_food(void)
Definition: food.c:37
PLAYER
@ PLAYER
Definition: object.h:107
global.h
SOUND_TYPE_ITEM
#define SOUND_TYPE_ITEM
Definition: newclient.h:335
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
check_heal_and_mana
static void check_heal_and_mana(object *who, object *food)
Definition: food.c:43
Settings::max_level
int16_t max_level
Definition: global.h:297
AT_POISON
#define AT_POISON
Definition: attack.h:86
llevError
@ llevError
Definition: logger.h:11
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
FLESH
@ FLESH
Definition: object.h:187
object_find_by_type_and_arch_name
object * object_find_by_type_and_arch_name(const object *who, int type, const char *name)
Definition: object.c:4248
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
liv::maxhp
int16_t maxhp
Definition: living.h:41
METHOD_OK
#define METHOD_OK
Definition: ob_methods.h:15
MSG_TYPE_APPLY_CURSED
#define MSG_TYPE_APPLY_CURSED
Definition: newclient.h:605
liv::hp
int16_t hp
Definition: living.h:40
MSG_TYPE_ATTRIBUTE
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:405
MIN
#define MIN(x, y)
Definition: compat.h:21
play_sound_map
void play_sound_map(int8_t sound_type, object *emitter, int dir, const char *action)
Definition: sounds.c:113
NDI_RED
#define NDI_RED
Definition: newclient.h:245
NROFATTACKS
#define NROFATTACKS
Definition: attack.h:17
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
FLAG_NO_PICK
#define FLAG_NO_PICK
Definition: define.h:239
settings
struct Settings settings
Definition: init.c:39
autojail.who
who
Definition: autojail.py:3
liv::exp
int64_t exp
Definition: living.h:47
is_old_wraith_pl
int is_old_wraith_pl(object *op)
Definition: player.c:185
slow_living_by
int slow_living_by(object *op, const int speed_penalty)
Definition: attack.c:2182
object_decrease_nrof_by_one
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:32
link_player_skills
void link_player_skills(object *op)
Definition: player.c:287
obj::name
sstring name
Definition: object.h:314
MSG_TYPE_ATTRIBUTE_RACE
#define MSG_TYPE_ATTRIBUTE_RACE
Definition: newclient.h:564
MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
Definition: newclient.h:565
MSG_TYPE_APPLY_SUCCESS
#define MSG_TYPE_APPLY_SUCCESS
Definition: newclient.h:603
MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
#define MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
Definition: newclient.h:555
eat_special_food
static void eat_special_food(object *who, object *food)
Definition: food.c:203
eat_common
static void eat_common(object *applier, object *food)
Definition: food.c:78
fix_object
void fix_object(object *op)
Definition: living.c:1126
obj::other_arch
struct archt * other_arch
Definition: object.h:418
sproto.h
liv::food
int32_t food
Definition: living.h:48
FLAG_CAN_USE_SKILL
#define FLAG_CAN_USE_SKILL
Definition: define.h:321
is_dragon_pl
int is_dragon_pl(const object *op)
Definition: player.c:122
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
MAX_BUF
#define MAX_BUF
Definition: define.h:35
INS_ON_TOP
#define INS_ON_TOP
Definition: object.h:569
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:281
RANDOM
#define RANDOM()
Definition: define.h:644
dragon_eat_flesh
static int dragon_eat_flesh(object *op, object *meal)
Definition: food.c:257
is_wraith_pl
int is_wraith_pl(object *op)
Definition: player.c:173
obj::title
sstring title
Definition: object.h:320
register_apply
void register_apply(int ob_type, apply_func method)
Definition: ob_types.c:62
method_ret
char method_ret
Definition: ob_methods.h:14
ob_types.h
sounds.h
obj::type
uint8_t type
Definition: object.h:343
change_resist_msg
const EXTERN char *const change_resist_msg[NROFATTACKS]
Definition: attack.h:135
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:262
apply_handle_yield
void apply_handle_yield(object *tmp)
Definition: apply.c:122
obj::stats
living stats
Definition: object.h:373
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
give.op
op
Definition: give.py:33
hit_player
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Definition: attack.c:1860
buf
StringBuffer * buf
Definition: readable.c:1610
set_attr_value
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:219
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2833
object_update_speed
void object_update_speed(object *op)
Definition: object.c:1330
obj::last_eat
int32_t last_eat
Definition: object.h:361
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:232
MSG_TYPE_APPLY_FAILURE
#define MSG_TYPE_APPLY_FAILURE
Definition: newclient.h:604
FLAG_IS_USED_UP
#define FLAG_IS_USED_UP
Definition: define.h:260
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:2080
draw_ext_info
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:309
get_attr_value
int8_t get_attr_value(const living *stats, int attr)
Definition: living.c:314
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.c:1546
FOOD
@ FOOD
Definition: object.h:112
DRINK
@ DRINK
Definition: object.h:157
ob_methods.h
atnr_is_dragon_enabled
int atnr_is_dragon_enabled(int attacknr)
Definition: player.c:103
food_type_apply
static method_ret food_type_apply(object *food, object *applier, int aflags)
Definition: food.c:121
give_skill_by_name
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.c:1779
change_abil
int change_abil(object *op, object *tmp)
Definition: living.c:395
liv::sp
int16_t sp
Definition: living.h:42
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:408
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
MOVE_PER_SECOND
static const float MOVE_PER_SECOND
Definition: tod.h:54
obj::resist
int16_t resist[NROFATTACKS]
Definition: object.h:346
NUM_STATS
@ NUM_STATS
Definition: living.h:18
MAX_FOOD
static const int32_t MAX_FOOD
Definition: define.h:461
FORCE
@ FORCE
Definition: object.h:224
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:319
obj::level
int16_t level
Definition: object.h:356
FORCE_NAME
#define FORCE_NAME
Definition: spells.h:169
dragon_attune.force
force
Definition: dragon_attune.py:45