Crossfire Server, Trunk
food.cpp
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 }
living::exp
int64_t exp
Definition: living.h:47
PLAYER
@ PLAYER
Definition: object.h:112
global.h
settings
struct Settings settings
Definition: init.cpp:139
SOUND_TYPE_ITEM
#define SOUND_TYPE_ITEM
Definition: newclient.h:324
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
living::maxhp
int16_t maxhp
Definition: living.h:41
AT_POISON
#define AT_POISON
Definition: attack.h:86
init_type_food
void init_type_food(void)
Definition: food.cpp:37
llevError
@ llevError
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
FLESH
@ FLESH
Definition: object.h:192
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
dragon_eat_flesh
static int dragon_eat_flesh(object *op, object *meal)
Definition: food.cpp:257
register_apply
void register_apply(int ob_type, apply_func method)
Definition: ob_types.cpp:62
METHOD_OK
#define METHOD_OK
Definition: ob_methods.h:15
give_skill_by_name
object * give_skill_by_name(object *op, const char *skill_name)
Definition: living.cpp:1777
MSG_TYPE_APPLY_CURSED
#define MSG_TYPE_APPLY_CURSED
Definition: newclient.h:594
MSG_TYPE_ATTRIBUTE
#define MSG_TYPE_ATTRIBUTE
Definition: newclient.h:394
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,...) PRINTF_ARGS(6
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.cpp:113
fix_object
void fix_object(object *op)
Definition: living.cpp:1125
NDI_RED
#define NDI_RED
Definition: newclient.h:234
NROFATTACKS
#define NROFATTACKS
Definition: attack.h:17
object::title
sstring title
Definition: object.h:325
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
object::level
int16_t level
Definition: object.h:361
buf
StringBuffer * buf
Definition: readable.cpp:1552
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2848
eat_special_food
static void eat_special_food(object *who, object *food)
Definition: food.cpp:203
object::resist
int16_t resist[NROFATTACKS]
Definition: object.h:351
FLAG_NO_PICK
#define FLAG_NO_PICK
Definition: define.h:239
draw_ext_info
vs only yadda is in because all tags get reset on the next draw_ext_info In the second since it is all in one draw_ext_info
Definition: media-tags.txt:61
autojail.who
who
Definition: autojail.py:3
food_type_apply
static method_ret food_type_apply(object *food, object *applier, int aflags)
Definition: food.cpp:121
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1555
slow_living_by
int slow_living_by(object *op, const int speed_penalty)
Definition: attack.cpp:2226
object_decrease_nrof_by_one
#define object_decrease_nrof_by_one(xyz)
Definition: compat.h:32
MSG_TYPE_ATTRIBUTE_RACE
#define MSG_TYPE_ATTRIBUTE_RACE
Definition: newclient.h:553
MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
#define MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START
Definition: newclient.h:554
MSG_TYPE_APPLY_SUCCESS
#define MSG_TYPE_APPLY_SUCCESS
Definition: newclient.h:592
MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
#define MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN
Definition: newclient.h:544
set_attr_value
void set_attr_value(living *stats, int attr, int8_t value)
Definition: living.cpp:218
change_resist_msg
const char *const change_resist_msg[NROFATTACKS]
Definition: init.cpp:70
object::last_eat
int32_t last_eat
Definition: object.h:366
object_update_speed
void object_update_speed(object *op)
Definition: object.cpp:1344
object::type
uint8_t type
Definition: object.h:348
living::food
int32_t food
Definition: living.h:48
eat_common
static void eat_common(object *applier, object *food)
Definition: food.cpp:78
sproto.h
living::sp
int16_t sp
Definition: living.h:42
FLAG_CAN_USE_SKILL
#define FLAG_CAN_USE_SKILL
Definition: define.h:321
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
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.cpp:2095
object::other_arch
struct archetype * other_arch
Definition: object.h:423
MAX_BUF
#define MAX_BUF
Definition: define.h:35
INS_ON_TOP
#define INS_ON_TOP
Definition: object.h:574
is_wraith_pl
int is_wraith_pl(object *op)
Definition: player.cpp:173
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
RANDOM
#define RANDOM()
Definition: define.h:644
change_abil
int change_abil(object *op, object *tmp)
Definition: living.cpp:394
method_ret
char method_ret
Definition: ob_methods.h:14
ob_types.h
sounds.h
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
apply_handle_yield
void apply_handle_yield(object *tmp)
Definition: apply.cpp:122
object::name
sstring name
Definition: object.h:319
Settings::max_level
int16_t max_level
Definition: global.h:302
is_dragon_pl
int is_dragon_pl(const object *op)
Definition: player.cpp:122
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.cpp:1902
atnr_is_dragon_enabled
int atnr_is_dragon_enabled(int attacknr)
Definition: player.cpp:103
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.cpp:4268
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:229
stats
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various stats
Definition: stats.txt:2
MSG_TYPE_APPLY_FAILURE
#define MSG_TYPE_APPLY_FAILURE
Definition: newclient.h:593
FLAG_IS_USED_UP
#define FLAG_IS_USED_UP
Definition: define.h:260
FOOD
@ FOOD
Definition: object.h:117
check_heal_and_mana
static void check_heal_and_mana(object *who, object *food)
Definition: food.cpp:43
skill
skill
Definition: arch-handbook.txt:585
DRINK
@ DRINK
Definition: object.h:162
ob_methods.h
is_old_wraith_pl
int is_old_wraith_pl(object *op)
Definition: player.cpp:185
object::stats
living stats
Definition: object.h:378
get_attr_value
int8_t get_attr_value(const living *stats, int attr)
Definition: living.cpp:313
bonus
Player Stats effect how well a character can survie and interact inside the crossfire world This section discusses the various what they and how they effect the player s actions Also in this section are the stat modifiers that specific classes professions bring Player and sps the current and maximum the Current and Maximum The Current Sp can go somewhat negative When Sp is negative not all spells can be and a more negative Sp makes spell casting less likey to succeed can affect Damage and how the characters as well as how often the character can attack this affects the prices when buying and selling items if this drops the player will start losing hit points wd Cleric or Dwarf sm Elf wd Fireborn ft Human ra Mage C Monk se Ninja hi Priest C Quetzalcoatl mw Swashbuckler si Thief st Viking ba Warrior or Wizard C Wraith C Class Prof Str Dex Con Wis Cha Int Pow Net Skills Enclosed are codes used for the skills above The ones in and fighting should all be pretty self explanatory For the other a brief description is for a more detailed look at the skills doc file Skill remove use magic items phys no fire cold Fireborns are supposed to be fire spirits They re closely in tune with magic and are powerful and learn magic easily Being fire they are immune to fire and and vulnerable to cold They are vulnerable to ghosthit and drain because being mostly non anything which strikes directly at the spirit hits them harder race attacktype restrictions immunities prot vuln Quetzalcoatl physical no armour fire cold Quetzalcoatl s are now born knowing the spell of burning but because of their negative wisdom bonus
Definition: stats.txt:176
link_player_skills
void link_player_skills(object *op)
Definition: player.cpp:287
MSG_TYPE_APPLY
#define MSG_TYPE_APPLY
Definition: newclient.h:397
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
MOVE_PER_SECOND
static const float MOVE_PER_SECOND
Definition: tod.h:54
NUM_STATS
@ NUM_STATS
Definition: living.h:18
MAX_FOOD
static const int32_t MAX_FOOD
Definition: define.h:461
living::hp
int16_t hp
Definition: living.h:40
FORCE
@ FORCE
Definition: object.h:229
FORCE_NAME
#define FORCE_NAME
Definition: spells.h:169
dragon_attune.force
force
Definition: dragon_attune.py:45