Crossfire Server, Branches 1.12  R18729
food.c
Go to the documentation of this file.
1 /*
2  CrossFire, A Multiplayer game for X-windows
3 
4  Copyright (C) 2007 Mark Wedel & Crossfire Development Team
5  Copyright (C) 1992 Frank Tore Johansen
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21  The authors can be reached via e-mail at crossfire-devel@real-time.com
22 */
26 #include <global.h>
27 #include <ob_methods.h>
28 #include <ob_types.h>
29 #include <sounds.h>
30 #include <sproto.h>
31 #include <math.h>
32 
33 static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags);
34 static void eat_special_food(object *who, object *food);
35 static int dragon_eat_flesh(object *op, object *meal);
36 
40 void init_type_food(void) {
44 }
45 
55 static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags) {
56  int capacity_remaining;
57 
58  if (QUERY_FLAG(food, FLAG_NO_PICK)) {
59  draw_ext_info_format(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, "You can't %s that!", NULL, food->type == DRINK ? "drink" : "eat");
60  return METHOD_OK;
61  }
62 
63  if (applier->type != PLAYER)
64  applier->stats.hp = applier->stats.maxhp;
65  else {
66  /* check if this is a dragon (player), eating some flesh */
67  if (food->type == FLESH && is_dragon_pl(applier))
68  dragon_eat_flesh(applier, food);
69 
70  /* Check for old wraith player, give them the feeding skill */
71  else if (is_old_wraith_pl(applier)) {
72  object *skill = give_skill_by_name(applier, "wraith feed");
73 
74  if (skill) {
75  char buf[MAX_BUF];
76 
78  link_player_skills(applier);
79 
80  snprintf(buf, sizeof(buf), "You have been dead for too long to taste %s, ", food->name);
82  buf, NULL);
84  "and seem to have obtained a taste for living flesh.", NULL);
85  } else
86  LOG(llevError, "wraith feed skill not found\n");
87  /* Wraith player gets no food from eating. */
88  } else if (is_wraith_pl(applier)) {
89  char buf[MAX_BUF];
90 
91  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");
93  buf, NULL);
94  /* usual case - not a wraith or a dgaron: */
95  } else {
96  if (applier->stats.food+food->stats.food > 999) {
97  if (food->type == FOOD || food->type == FLESH)
99  "You feel full, but what a waste of food!", NULL);
100  else
102  "Most of the drink goes down your face not your throat!", NULL);
103  }
104 
105  if (!QUERY_FLAG(food, FLAG_CURSED)) {
106  char buf[MAX_BUF];
107 
108  if (!is_dragon_pl(applier)) {
109  /* eating message for normal players*/
110  if (food->type == DRINK)
111  snprintf(buf, sizeof(buf), "Ahhh...that %s tasted good.", food->name);
112  else
113  snprintf(buf, sizeof(buf), "The %s tasted %s", food->name, food->type == FLESH ? "terrible!" : "good.");
114  } else {
115  /* eating message for dragon players*/
116  snprintf(buf, sizeof(buf), "The %s tasted terrible!", food->name);
117  }
118 
120  buf, NULL);
121  capacity_remaining = 999-applier->stats.food;
122  applier->stats.food += food->stats.food;
123  if (capacity_remaining < food->stats.food)
124  applier->stats.hp += capacity_remaining/50;
125  else
126  applier->stats.hp += food->stats.food/50;
127  if (applier->stats.hp > applier->stats.maxhp)
128  applier->stats.hp = applier->stats.maxhp;
129  if (applier->stats.food > 999)
130  applier->stats.food = 999;
131  }
132 
133  /* special food hack -b.t. */
134  if (food->title || QUERY_FLAG(food, FLAG_CURSED))
135  eat_special_food(applier, food);
136  }
137  }
138  handle_apply_yield(food);
139  decrease_ob(food);
140  return METHOD_OK;
141 }
142 
155 static void eat_special_food(object *who, object *food) {
156  object *force;
157  int i, did_one = 0;
158  sint8 k;
159 
160  force = create_archetype(FORCE_NAME);
161 
162  for (i = 0; i < NUM_STATS; i++) {
163  k = get_attr_value(&food->stats, i);
164  if (k) {
165  set_attr_value(&force->stats, i, k);
166  did_one = 1;
167  }
168  }
169 
170  /* check if we can protect the eater */
171  for (i = 0; i < NROFATTACKS; i++) {
172  if (food->resist[i] > 0) {
173  force->resist[i] = food->resist[i]/2;
174  did_one = 1;
175  }
176  }
177  if (did_one) {
178  force->speed = 0.1;
179  update_ob_speed(force);
180  /* bigger morsel of food = longer effect time */
181  force->stats.food = food->stats.food/5;
182  SET_FLAG(force, FLAG_IS_USED_UP);
183  SET_FLAG(force, FLAG_APPLIED);
184  change_abil(who, force);
185  insert_ob_in_ob(force, who);
186  } else {
187  free_object(force);
188  }
189 
190  /* check for hp, sp change */
191  if (food->stats.hp != 0 && !is_wraith_pl(who)) {
192  if (QUERY_FLAG(food, FLAG_CURSED)) {
193  strcpy(who->contr->killer, food->name);
194  hit_player(who, food->stats.hp, food, AT_POISON, 1);
196  "Eck!...that was poisonous!", NULL);
197  } else {
198  if (food->stats.hp > 0)
200  "You begin to feel better.", NULL);
201  else
203  "Eck!...that was poisonous!", NULL);
204  who->stats.hp += food->stats.hp;
205  }
206  }
207  if (food->stats.sp != 0) {
208  if (QUERY_FLAG(food, FLAG_CURSED)) {
210  "You are drained of mana!", NULL);
211  who->stats.sp -= food->stats.sp;
212  if (who->stats.sp < 0)
213  who->stats.sp = 0;
214  } else {
216  "You feel a rush of magical energy!", NULL);
217  who->stats.sp += food->stats.sp;
218  /* place limit on max sp from food? */
219  }
220  }
221  fix_object(who);
222 }
223 
237 static int dragon_eat_flesh(object *op, object *meal) {
238  object *skin = NULL; /* pointer to dragon skin force*/
239  object *abil = NULL; /* pointer to dragon ability force*/
240  object *tmp = NULL; /* tmp. object */
241 
242  char buf[MAX_BUF]; /* tmp. string buffer */
243  double chance; /* improvement-chance of one resist type */
244  double totalchance = 1; /* total chance of gaining one resistance */
245  double bonus = 0; /* level bonus (improvement is easier at lowlevel) */
246  double mbonus = 0; /* monster bonus */
247  int atnr_winner[NROFATTACKS]; /* winning candidates for resistance improvement */
248  int winners = 0; /* number of winners */
249  int i; /* index */
250 
251  /* let's make sure and doublecheck the parameters */
252  if (meal->type != FLESH || !is_dragon_pl(op))
253  return 0;
254 
255  /* now grab the 'dragon_skin'- and 'dragon_ability'-forces
256  * from the player's inventory
257  */
258  for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
259  if (tmp->type == FORCE) {
260  if (strcmp(tmp->arch->name, "dragon_skin_force") == 0)
261  skin = tmp;
262  else if (strcmp(tmp->arch->name, "dragon_ability_force") == 0)
263  abil = tmp;
264  }
265  }
266 
267  /* if either skin or ability are missing, this is an old player
268  * which is not to be considered a dragon -> bail out
269  */
270  if (skin == NULL || abil == NULL)
271  return 0;
272 
273  /* now start by filling stomache and health, according to food-value */
274  if ((999-op->stats.food) < meal->stats.food)
275  op->stats.hp += (999-op->stats.food)/50;
276  else
277  op->stats.hp += meal->stats.food/50;
278  if (op->stats.hp > op->stats.maxhp)
279  op->stats.hp = op->stats.maxhp;
280 
281  op->stats.food = MIN(999, op->stats.food+meal->stats.food);
282 
283  /*LOG(llevDebug, "-> player: %d, flesh: %d\n", op->level, meal->level);*/
284 
285  /* on to the interesting part: chances for adding resistance */
286  for (i = 0; i < NROFATTACKS; i++) {
287  if (meal->resist[i] > 0 && atnr_is_dragon_enabled(i)) {
288  /* got positive resistance, now calculate improvement chance (0-100) */
289 
290  /* this bonus makes resistance increase easier at lower levels */
291  bonus = (settings.max_level-op->level)*30./((double)settings.max_level);
292  if (i == abil->stats.exp)
293  bonus += 5; /* additional bonus for resistance of ability-focus */
294 
295  /* monster bonus increases with level, because high-level
296  * flesh is too rare
297  */
298  mbonus = op->level*20./((double)settings.max_level);
299 
300  chance = (((double)MIN(op->level+bonus, meal->level+bonus+mbonus))*100./((double)settings.max_level))-skin->resist[i];
301 
302  if (chance >= 0.)
303  chance += 1.;
304  else
305  chance = (chance < -12) ? 0. : 1./pow(2., -chance);
306 
307  /* chance is proportional to amount of resistance (max. 50) */
308  chance *= ((double)(MIN(meal->resist[i], 50)))/50.;
309 
310  /* doubled chance for resistance of ability-focus */
311  if (i == abil->stats.exp)
312  chance = MIN(100., chance*2.);
313 
314  /* now make the throw and save all winners (Don't insert luck bonus here!) */
315  if (RANDOM()%10000 < (int)(chance*100)) {
316  atnr_winner[winners] = i;
317  winners++;
318  }
319 
320  if (chance >= 0.01)
321  totalchance *= 1-chance/100;
322 
323  /*LOG(llevDebug, " %s: bonus %.1f, chance %.1f\n", attacks[i], bonus, chance);*/
324  }
325  }
326 
327  /* inverse totalchance as until now we have the failure-chance */
328  totalchance = 100-totalchance*100;
329  /* print message according to totalchance */
330  if (totalchance > 50.)
331  snprintf(buf, sizeof(buf), "Hmm! The %s tasted delicious!", meal->name);
332  else if (totalchance > 10.)
333  snprintf(buf, sizeof(buf), "The %s tasted very good.", meal->name);
334  else if (totalchance > 1.)
335  snprintf(buf, sizeof(buf), "The %s tasted good.", meal->name);
336  else if (totalchance > 0.1)
337  snprintf(buf, sizeof(buf), "The %s tasted bland.", meal->name);
338  else if (totalchance >= 0.01)
339  snprintf(buf, sizeof(buf), "The %s had a boring taste.", meal->name);
340  else if (meal->last_eat > 0 && atnr_is_dragon_enabled(meal->last_eat))
341  snprintf(buf, sizeof(buf), "The %s tasted strange.", meal->name);
342  else
343  snprintf(buf, sizeof(buf), "The %s had no taste.", meal->name);
345  buf, NULL);
346 
347  /* now choose a winner if we have any */
348  i = -1;
349  if (winners > 0)
350  i = atnr_winner[RANDOM()%winners];
351 
352  if (i >= 0 && i < NROFATTACKS && skin->resist[i] < 95) {
353  /* resistance increased! */
354  skin->resist[i]++;
355  fix_object(op);
356 
358  "Your skin is now more resistant to %s!",
359  "Your skin is now more resistant to %s!",
360  change_resist_msg[i]);
361  }
362 
363  /* if this flesh contains a new ability focus, we mark it
364  * into the ability_force and it will take effect on next level
365  */
366  if (meal->last_eat > 0
368  && meal->last_eat != abil->last_eat) {
369  abil->last_eat = meal->last_eat; /* write:last_eat <new attnr focus> */
370 
371  if (meal->last_eat != abil->stats.exp) {
373  "Your metabolism prepares to focus on %s!",
374  "Your metabolism prepares to focus on %s!",
375  change_resist_msg[meal->last_eat]);
377  "The change will happen at level %d",
378  "The change will happen at level %d",
379  abil->level+1);
380  } else {
382  "Your metabolism will continue to focus on %s.",
383  "Your metabolism will continue to focus on %s.",
384  change_resist_msg[meal->last_eat]);
385  abil->last_eat = 0;
386  }
387  }
388  return 1;
389 }
#define FOOD
Definition: define.h:118
signed char sint8
Definition: global.h:80
void init_type_food(void)
Definition: food.c:40
#define MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
Definition: newclient.h:471
#define SET_FLAG(xyz, p)
Definition: define.h:510
#define MSG_TYPE_APPLY_FAILURE
Definition: newclient.h:519
#define FLAG_IS_USED_UP
Definition: define.h:556
sint8 get_attr_value(const living *stats, int attr)
Definition: living.c:377
sint16 max_level
Definition: global.h:391
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
Definition: standalone.c:171
sint64 exp
Definition: living.h:88
sint16 sp
Definition: living.h:83
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: standalone.c:175
static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags)
Definition: food.c:55
#define PLAYER
Definition: define.h:113
sint16 hp
Definition: living.h:81
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.c:1689
char method_ret
Definition: ob_methods.h:41
int change_abil(object *op, object *tmp)
Definition: living.c:443
const char * title
Definition: object.h:170
#define NDI_RED
Definition: newclient.h:198
sint16 maxhp
Definition: living.h:82
void handle_apply_yield(object *tmp)
Definition: apply.c:131
object * create_archetype(const char *name)
Definition: arch.c:625
static int dragon_eat_flesh(object *op, object *meal)
Definition: food.c:237
int is_wraith_pl(object *op)
Definition: player.c:173
#define FLAG_CAN_USE_SKILL
Definition: define.h:618
#define METHOD_OK
Definition: ob_methods.h:42
int is_dragon_pl(const object *op)
Definition: player.c:125
void set_attr_value(living *stats, int attr, sint8 value)
Definition: living.c:296
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:327
const char * name
Definition: object.h:167
#define FLESH
Definition: define.h:234
struct obj * below
Definition: object.h:145
static void eat_special_food(object *who, object *food)
Definition: food.c:155
EXTERN const char *const change_resist_msg[NROFATTACKS]
Definition: attack.h:163
void register_apply(int ob_type, apply_func method)
Definition: ob_types.c:79
struct pl * contr
Definition: object.h:134
float speed
Definition: object.h:181
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
#define MSG_TYPE_APPLY
Definition: newclient.h:330
int is_old_wraith_pl(object *op)
Definition: player.c:192
object * insert_ob_in_ob(object *op, object *where)
Definition: object.c:2510
#define MAX_BUF
Definition: define.h:81
sint32 last_eat
Definition: object.h:207
#define MIN(x, y)
Definition: define.h:67
sint16 resist[NROFATTACKS]
Definition: object.h:192
#define MSG_TYPE_APPLY_CURSED
Definition: newclient.h:520
#define FLAG_CURSED
Definition: define.h:613
#define MSG_TYPE_ATTRIBUTE_RACE
Definition: newclient.h:479
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
#define decrease_ob(xyz)
Definition: global.h:276
char killer[BIG_NAME]
Definition: player.h:222
#define AT_POISON
Definition: attack.h:114
#define FORCE
Definition: define.h:296
#define MSG_TYPE_APPLY_SUCCESS
Definition: newclient.h:518
#define NUM_STATS
Definition: living.h:48
living stats
Definition: object.h:219
struct archt * arch
Definition: object.h:263
struct Settings settings
Definition: init.c:48
#define FLAG_APPLIED
Definition: define.h:531
#define NROFATTACKS
Definition: attack.h:45
#define FORCE_NAME
Definition: spells.h:196
void update_ob_speed(object *op)
Definition: object.c:1008
void link_player_skills(object *op)
Definition: skill_util.c:112
#define DRINK
Definition: define.h:187
struct obj * inv
Definition: object.h:148
#define NDI_UNIQUE
Definition: newclient.h:219
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
void free_object(object *ob)
Definition: object.c:1238
#define FLAG_NO_PICK
Definition: define.h:535
sint16 level
Definition: object.h:202
void fix_object(object *op)
Definition: living.c:900
const char * name
Definition: object.h:322
int hit_player(object *op, int dam, object *hitter, uint32 type, int full_hit)
Definition: attack.c:1868
uint8 type
Definition: object.h:189
int atnr_is_dragon_enabled(int attacknr)
Definition: player.c:106
sint32 food
Definition: living.h:89