Crossfire Server, Trunk  R20513
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 
52 static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags) {
53  int capacity_remaining;
54 
55  if (QUERY_FLAG(food, FLAG_NO_PICK)) {
56  draw_ext_info_format(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "You can't %s that!", food->type == DRINK ? "drink" : "eat");
57  return METHOD_OK;
58  }
59 
60  if (applier->type != PLAYER)
61  applier->stats.hp = applier->stats.maxhp;
62  else {
63  /* check if this is a dragon (player), eating some flesh */
64  if (food->type == FLESH && is_dragon_pl(applier))
65  dragon_eat_flesh(applier, food);
66 
67  /* Check for old wraith player, give them the feeding skill */
68  else if (is_old_wraith_pl(applier)) {
69  object *skill = give_skill_by_name(applier, "wraith feed");
70 
71  if (skill) {
72  char buf[MAX_BUF];
73 
75  link_player_skills(applier);
76 
77  snprintf(buf, sizeof(buf), "You have been dead for too long to taste %s, ", food->name);
79  buf);
81  "and seem to have obtained a taste for living flesh.");
82  } else
83  LOG(llevError, "wraith feed skill not found\n");
84  /* Wraith player gets no food from eating. */
85  } else if (is_wraith_pl(applier)) {
86  char buf[MAX_BUF];
87 
88  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");
90  buf);
91  /* usual case - not a wraith or a dragon: */
92  } else {
93  if (applier->stats.food + food->stats.food > 999) {
94  if (food->type == FOOD || food->type == FLESH)
96  "You feel full, but what a waste of food!");
97  else
99  "Most of the drink goes down your face not your throat!");
100  }
101 
102  if (!QUERY_FLAG(food, FLAG_CURSED)) {
103  char buf[MAX_BUF];
104 
105  if (!is_dragon_pl(applier)) {
106  /* eating message for normal players*/
107  if (food->type == DRINK)
108  snprintf(buf, sizeof(buf), "Ahhh...that %s tasted good.", food->name);
109  else
110  snprintf(buf, sizeof(buf), "The %s tasted %s", food->name, food->type == FLESH ? "terrible!" : "good.");
111  } else {
112  /* eating message for dragon players*/
113  snprintf(buf, sizeof(buf), "The %s tasted terrible!", food->name);
114  }
115 
117  buf);
118  capacity_remaining = 999-applier->stats.food;
119  applier->stats.food += food->stats.food;
120  if (capacity_remaining < food->stats.food)
121  applier->stats.hp += capacity_remaining/50;
122  else
123  applier->stats.hp += food->stats.food/50;
124  if (applier->stats.hp > applier->stats.maxhp)
125  applier->stats.hp = applier->stats.maxhp;
126  if (applier->stats.food > 999)
127  applier->stats.food = 999;
128  }
129 
130  /* special food hack -b.t. */
131  if (food->title || QUERY_FLAG(food, FLAG_CURSED))
132  eat_special_food(applier, food);
133  }
134  }
135  apply_handle_yield(food);
137  return METHOD_OK;
138 }
139 
152 static void eat_special_food(object *who, object *food) {
153  object *force;
154  int i, did_one = 0;
155  int8_t k;
156 
157  force = create_archetype(FORCE_NAME);
158 
159  for (i = 0; i < NUM_STATS; i++) {
160  k = get_attr_value(&food->stats, i);
161  if (k) {
162  set_attr_value(&force->stats, i, k);
163  did_one = 1;
164  }
165  }
166 
167  /* check if we can protect the eater */
168  for (i = 0; i < NROFATTACKS; i++) {
169  if (food->resist[i] > 0) {
170  force->resist[i] = food->resist[i]/2;
171  did_one = 1;
172  }
173  }
174  if (did_one) {
175  force->speed = 0.1;
176  object_update_speed(force);
177  /* bigger morsel of food = longer effect time */
178  force->duration = food->stats.food/5;
179  SET_FLAG(force, FLAG_IS_USED_UP);
180  SET_FLAG(force, FLAG_APPLIED);
181  change_abil(who, force);
182  object_insert_in_ob(force, who);
183  } else {
185  }
186 
187  /* check for hp, sp change */
188  if (food->stats.hp != 0 && !is_wraith_pl(who)) {
189  if (QUERY_FLAG(food, FLAG_CURSED)) {
190  safe_strncpy(who->contr->killer, food->name,
191  sizeof(who->contr->killer));
192  hit_player(who, food->stats.hp, food, AT_POISON, 1);
194  "Eck!...that was poisonous!");
195  } else {
196  if (food->stats.hp > 0)
198  "You begin to feel better.");
199  else
201  "Eck!...that was poisonous!");
202  who->stats.hp += food->stats.hp;
203  }
204  }
205  if (food->stats.sp != 0) {
206  if (QUERY_FLAG(food, FLAG_CURSED)) {
208  "You are drained of mana!");
209  who->stats.sp -= food->stats.sp;
210  if (who->stats.sp < 0)
211  who->stats.sp = 0;
212  } else {
214  "You feel a rush of magical energy!");
215  who->stats.sp += food->stats.sp;
216  /* place limit on max sp from food? */
217  }
218  }
219  fix_object(who);
220 }
221 
235 static int dragon_eat_flesh(object *op, object *meal) {
236  object *skin = NULL; /* pointer to dragon skin force*/
237  object *abil = NULL; /* pointer to dragon ability force*/
238 
239  char buf[MAX_BUF]; /* tmp. string buffer */
240  double chance; /* improvement-chance of one resist type */
241  double totalchance = 1; /* total chance of gaining one resistance */
242  double bonus = 0; /* level bonus (improvement is easier at lowlevel) */
243  double mbonus = 0; /* monster bonus */
244  int atnr_winner[NROFATTACKS]; /* winning candidates for resistance improvement */
245  int winners = 0; /* number of winners */
246  int i; /* index */
247 
248  /* let's make sure and doublecheck the parameters */
249  if (meal->type != FLESH || !is_dragon_pl(op))
250  return 0;
251 
252  /* now grab the 'dragon_skin'- and 'dragon_ability'-forces
253  * from the player's inventory
254  */
255  skin = object_find_by_type_and_arch_name(op, FORCE, "dragon_skin_force");
256  abil = object_find_by_type_and_arch_name(op, FORCE, "dragon_ability_force");
257 
258  /* if either skin or ability are missing, this is an old player
259  * which is not to be considered a dragon -> bail out
260  */
261  if (skin == NULL || abil == NULL)
262  return 0;
263 
264  /* now start by filling stomache and health, according to food-value */
265  if ((999-op->stats.food) < meal->stats.food)
266  op->stats.hp += (999-op->stats.food)/50;
267  else
268  op->stats.hp += meal->stats.food/50;
269  if (op->stats.hp > op->stats.maxhp)
270  op->stats.hp = op->stats.maxhp;
271 
272  op->stats.food = MIN(999, op->stats.food+meal->stats.food);
273 
274  /*LOG(llevDebug, "-> player: %d, flesh: %d\n", op->level, meal->level);*/
275 
276  /* on to the interesting part: chances for adding resistance */
277  for (i = 0; i < NROFATTACKS; i++) {
278  if (meal->resist[i] > 0 && atnr_is_dragon_enabled(i)) {
279  /* got positive resistance, now calculate improvement chance (0-100) */
280 
281  /* this bonus makes resistance increase easier at lower levels */
282  bonus = (settings.max_level-op->level)*30./((double)settings.max_level);
283  if (i == abil->stats.exp)
284  bonus += 5; /* additional bonus for resistance of ability-focus */
285 
286  /* monster bonus increases with level, because high-level
287  * flesh is too rare
288  */
289  mbonus = op->level*20./((double)settings.max_level);
290 
291  chance = (((double)MIN(op->level+bonus, meal->level+bonus+mbonus))*100./((double)settings.max_level))-skin->resist[i];
292 
293  if (chance >= 0.)
294  chance += 1.;
295  else
296  chance = (chance < -12) ? 0. : 1./pow(2., -chance);
297 
298  /* chance is proportional to amount of resistance (max. 50) */
299  chance *= ((double)(MIN(meal->resist[i], 50)))/50.;
300 
301  /* doubled chance for resistance of ability-focus */
302  if (i == abil->stats.exp)
303  chance = MIN(100., chance*2.);
304 
305  /* now make the throw and save all winners (Don't insert luck bonus here!) */
306  if (RANDOM()%10000 < (unsigned int)(chance*100)) {
307  atnr_winner[winners] = i;
308  winners++;
309  }
310 
311  if (chance >= 0.01)
312  totalchance *= 1-chance/100;
313 
314  /*LOG(llevDebug, " %s: bonus %.1f, chance %.1f\n", attacks[i], bonus, chance);*/
315  }
316  }
317 
318  /* inverse totalchance as until now we have the failure-chance */
319  totalchance = 100-totalchance*100;
320  /* print message according to totalchance */
321  if (totalchance > 50.)
322  snprintf(buf, sizeof(buf), "Hmm! The %s tasted delicious!", meal->name);
323  else if (totalchance > 10.)
324  snprintf(buf, sizeof(buf), "The %s tasted very good.", meal->name);
325  else if (totalchance > 1.)
326  snprintf(buf, sizeof(buf), "The %s tasted good.", meal->name);
327  else if (totalchance > 0.1)
328  snprintf(buf, sizeof(buf), "The %s tasted bland.", meal->name);
329  else if (totalchance >= 0.01)
330  snprintf(buf, sizeof(buf), "The %s had a boring taste.", meal->name);
331  else if (meal->last_eat > 0 && atnr_is_dragon_enabled(meal->last_eat))
332  snprintf(buf, sizeof(buf), "The %s tasted strange.", meal->name);
333  else
334  snprintf(buf, sizeof(buf), "The %s had no taste.", meal->name);
336  buf);
337 
338  /* now choose a winner if we have any */
339  i = -1;
340  if (winners > 0)
341  i = atnr_winner[RANDOM()%winners];
342 
343  if (i >= 0 && i < NROFATTACKS && skin->resist[i] < 95) {
344  /* resistance increased! */
345  skin->resist[i]++;
346  fix_object(op);
347 
349  "Your skin is now more resistant to %s!",
350  change_resist_msg[i]);
351  }
352 
353  /* if this flesh contains a new ability focus, we mark it
354  * into the ability_force and it will take effect on next level
355  */
356  if (meal->last_eat > 0
358  && meal->last_eat != abil->last_eat) {
359  abil->last_eat = meal->last_eat; /* write:last_eat <new attnr focus> */
360 
361  if (meal->last_eat != abil->stats.exp) {
363  "Your metabolism prepares to focus on %s!",
364  change_resist_msg[meal->last_eat]);
366  "The change will happen at level %d",
367  abil->level+1);
368  } else {
370  "Your metabolism will continue to focus on %s.",
371  change_resist_msg[meal->last_eat]);
372  abil->last_eat = 0;
373  }
374  }
375  return 1;
376 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Sends message to player(s).
Definition: main.c:315
Error, serious thing.
Definition: logger.h:11
void set_attr_value(living *stats, int attr, int8_t value)
Sets Str/Dex/con/Wis/Cha/Int/Pow in stats to value, depending on what attr is (STR to POW)...
Definition: living.c:218
Sound-related defines.
void init_type_food(void)
Initializer for the food object type.
Definition: food.c:37
#define SET_FLAG(xyz, p)
Definition: define.h:223
Typedefs for ob_methods.
Definition: ob_methods.h:45
#define MSG_TYPE_APPLY_FAILURE
Apply OK, but no/bad result.
Definition: newclient.h:599
void apply_handle_yield(object *tmp)
This checks whether the object has a "on_use_yield" field, and if so generated and drops matching ite...
Definition: apply.c:125
#define FLAG_IS_USED_UP
When (–food<0) the object will exit.
Definition: define.h:260
See Food.
Definition: object.h:112
int16_t max_level
This is read out of exp_table.
Definition: global.h:300
int16_t duration
How long the spell lasts.
Definition: object.h:403
int64_t exp
Experience.
Definition: living.h:46
See Drink.
Definition: object.h:157
int8_t get_attr_value(const living *stats, int attr)
Gets the value of a stat.
Definition: living.c:313
int16_t sp
Spell points.
Definition: living.h:41
animal &#39;body parts&#39; -b.t.
Definition: object.h:187
Global type definitions and header inclusions.
#define safe_strncpy
Definition: compat.h:23
static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags)
Handles applying food.
Definition: food.c:52
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Sends message to player(s).
Definition: main.c:310
#define MSG_TYPE_APPLY
Applying objects.
Definition: newclient.h:384
#define MIN(x, y)
Definition: compat.h:17
int16_t hp
Hit Points.
Definition: living.h:39
object * give_skill_by_name(object *op, const char *skill_name)
Given the skill name skill_name, we find the skill archetype/object, set appropriate values...
Definition: living.c:1727
char method_ret
Define some standard return values for callbacks which don&#39;t need to return any other results...
Definition: ob_methods.h:14
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.c:1368
int change_abil(object *op, object *tmp)
Permanently alters an object&#39;s stats/flags based on another object.
Definition: living.c:394
const char * title
Of foo, etc.
Definition: object.h:317
#define NDI_RED
Definition: newclient.h:224
int16_t maxhp
Max hit points.
Definition: living.h:40
object * create_archetype(const char *name)
Finds which archetype matches the given name, and returns a new object containing a copy of the arche...
Definition: arch.c:620
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.c:2690
static int dragon_eat_flesh(object *op, object *meal)
A dragon is eating some flesh.
Definition: food.c:235
int is_wraith_pl(object *op)
Tests if a player is a wraith.
Definition: player.c:165
#define MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
Protections in this.
Definition: newclient.h:542
#define FLAG_CAN_USE_SKILL
The monster can use skills.
Definition: define.h:322
#define METHOD_OK
Definition: ob_methods.h:15
int is_dragon_pl(const object *op)
Checks if player is a dragon.
Definition: player.c:114
#define snprintf
Definition: win32.h:46
#define MSG_TYPE_ATTRIBUTE
Changes to attributes (stats, resistances, etc)
Definition: newclient.h:380
const char * name
The name of the object, obviously...
Definition: object.h:311
static void eat_special_food(object *who, object *food)
Handles player eating food that temporarily changes status (resistances, stats).
Definition: food.c:152
EXTERN const char *const change_resist_msg[NROFATTACKS]
Definition: attack.h:135
Number of statistics.
Definition: living.h:18
void register_apply(int ob_type, apply_func method)
Registers the apply method for the given type.
Definition: ob_types.c:62
struct pl * contr
Pointer to the player which control this object.
Definition: object.h:276
float speed
The overall speed of this object.
Definition: object.h:328
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
int is_old_wraith_pl(object *op)
Checks if player is a wraith without the &#39;wraith feed&#39; skill.
Definition: player.c:177
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
int32_t last_eat
How long since we last ate.
Definition: object.h:356
Object type variables.
int16_t resist[NROFATTACKS]
Resistance adjustments for attacks.
Definition: object.h:341
#define FLAG_CURSED
The object is cursed.
Definition: define.h:317
See Player.
Definition: object.h:107
char killer[BIG_NAME]
Who killed this player.
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:679
signed char int8_t
Type definitions for fixed-size integer types.
Definition: win32.h:158
living stats
Str, Con, Dex, etc.
Definition: object.h:368
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
struct Settings settings
Server settings.
Definition: init.c:40
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:235
#define NROFATTACKS
Definition: attack.h:17
#define MSG_TYPE_APPLY_SUCCESS
Was able to apply object.
Definition: newclient.h:598
#define FORCE_NAME
Definition: spells.h:169
void link_player_skills(object *op)
This function goes through the player inventory and sets up the last_skills[] array in the player obj...
Definition: skill_util.c:112
#define MSG_TYPE_APPLY_CURSED
Applied a cursed object (BAD)
Definition: newclient.h:600
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
#define MSG_TYPE_ATTRIBUTE_RACE
Race-related changes.
Definition: newclient.h:557
#define FLAG_NO_PICK
Object can&#39;t be picked up.
Definition: define.h:239
object * object_find_by_type_and_arch_name(const object *who, int type, const char *name)
Find object in inventory by type and archetype name.
Definition: object.c:4168
int hit_player(object *op, int dam, object *hitter, uint32_t type, int full_hit)
Object is attacked by something.
Definition: attack.c:1861
Object type functions and variables.
int16_t level
Level of creature or object.
Definition: object.h:351
void fix_object(object *op)
Updates all abilities given by applied objects in the inventory of the given object.
Definition: living.c:1120
void object_update_speed(object *op)
Updates the speed of an object.
Definition: object.c:1129
int atnr_is_dragon_enabled(int attacknr)
Determine if the attacktype represented by the specified attack-number is enabled for dragon players...
Definition: player.c:95
int32_t food
How much food in stomach.
Definition: living.h:47
Definition: object.h:224