Crossfire Server, Branch 1.12  R12190
food.c
Go to the documentation of this file.
00001 /*
00002     CrossFire, A Multiplayer game for X-windows
00003 
00004     Copyright (C) 2007 Mark Wedel & Crossfire Development Team
00005     Copyright (C) 1992 Frank Tore Johansen
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020 
00021     The authors can be reached via e-mail at crossfire-devel@real-time.com
00022 */
00026 #include <global.h>
00027 #include <ob_methods.h>
00028 #include <ob_types.h>
00029 #include <sounds.h>
00030 #include <sproto.h>
00031 #include <math.h>
00032 
00033 static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags);
00034 static void eat_special_food(object *who, object *food);
00035 static int dragon_eat_flesh(object *op, object *meal);
00036 
00040 void init_type_food(void) {
00041     register_apply(FOOD, food_type_apply);
00042     register_apply(DRINK, food_type_apply);
00043     register_apply(FLESH, food_type_apply);
00044 }
00045 
00055 static method_ret food_type_apply(ob_methods *context, object *food, object *applier, int aflags) {
00056     int capacity_remaining;
00057 
00058     if (QUERY_FLAG(food, FLAG_NO_PICK)) {
00059         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");
00060         return METHOD_OK;
00061     }
00062 
00063     if (applier->type != PLAYER)
00064         applier->stats.hp = applier->stats.maxhp;
00065     else {
00066             /* check if this is a dragon (player), eating some flesh */
00067         if (food->type == FLESH && is_dragon_pl(applier))
00068             dragon_eat_flesh(applier, food);
00069 
00070             /* Check for old wraith player, give them the feeding skill */
00071         else if (is_old_wraith_pl(applier)) {
00072             object *skill = give_skill_by_name(applier, "wraith feed");
00073 
00074             if (skill) {
00075                 char buf[MAX_BUF];
00076 
00077                 SET_FLAG(skill, FLAG_CAN_USE_SKILL);
00078                 link_player_skills(applier);
00079 
00080                 snprintf(buf, sizeof(buf), "You have been dead for too long to taste %s, ", food->name);
00081                 draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE,
00082                     buf, NULL);
00083                 draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE,
00084                     "and seem to have obtained a taste for living flesh.", NULL);
00085             } else
00086                 LOG(llevError, "wraith feed skill not found\n");
00087             /* Wraith player gets no food from eating. */
00088         } else if (is_wraith_pl(applier)) {
00089             char buf[MAX_BUF];
00090 
00091             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");
00092             draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE,
00093                 buf, NULL);
00094             /* usual case - not a wraith or a dgaron: */
00095         } else {
00096             if (applier->stats.food+food->stats.food > 999) {
00097                 if (food->type == FOOD || food->type == FLESH)
00098                     draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE,
00099                         "You feel full, but what a waste of food!", NULL);
00100                 else
00101                     draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE,
00102                         "Most of the drink goes down your face not your throat!", NULL);
00103             }
00104 
00105             if (!QUERY_FLAG(food, FLAG_CURSED)) {
00106                 char buf[MAX_BUF];
00107 
00108                 if (!is_dragon_pl(applier)) {
00109                         /* eating message for normal players*/
00110                     if (food->type == DRINK)
00111                         snprintf(buf, sizeof(buf), "Ahhh...that %s tasted good.", food->name);
00112                     else
00113                         snprintf(buf, sizeof(buf), "The %s tasted %s", food->name, food->type == FLESH ? "terrible!" : "good.");
00114                 } else {
00115                         /* eating message for dragon players*/
00116                     snprintf(buf, sizeof(buf), "The %s tasted terrible!", food->name);
00117                 }
00118 
00119                 draw_ext_info(NDI_UNIQUE, 0, applier, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00120                               buf, NULL);
00121                 capacity_remaining = 999-applier->stats.food;
00122                 applier->stats.food += food->stats.food;
00123                 if (capacity_remaining < food->stats.food)
00124                     applier->stats.hp += capacity_remaining/50;
00125                 else
00126                     applier->stats.hp += food->stats.food/50;
00127                 if (applier->stats.hp > applier->stats.maxhp)
00128                     applier->stats.hp = applier->stats.maxhp;
00129                 if (applier->stats.food > 999)
00130                     applier->stats.food = 999;
00131             }
00132 
00133                 /* special food hack -b.t. */
00134             if (food->title || QUERY_FLAG(food, FLAG_CURSED))
00135                 eat_special_food(applier, food);
00136         }
00137     }
00138     handle_apply_yield(food);
00139     decrease_ob(food);
00140     return METHOD_OK;
00141 }
00142 
00155 static void eat_special_food(object *who, object *food) {
00156     object *force;
00157     int i, did_one = 0;
00158     sint8 k;
00159 
00160     force = create_archetype(FORCE_NAME);
00161 
00162     for (i = 0; i < NUM_STATS; i++) {
00163         k = get_attr_value(&food->stats, i);
00164         if (k) {
00165             set_attr_value(&force->stats, i, k);
00166             did_one = 1;
00167         }
00168     }
00169 
00170     /* check if we can protect the eater */
00171     for (i = 0; i < NROFATTACKS; i++) {
00172         if (food->resist[i] > 0) {
00173             force->resist[i] = food->resist[i]/2;
00174             did_one = 1;
00175         }
00176     }
00177     if (did_one) {
00178         force->speed = 0.1;
00179         update_ob_speed(force);
00180         /* bigger morsel of food = longer effect time */
00181         force->stats.food = food->stats.food/5;
00182         SET_FLAG(force, FLAG_IS_USED_UP);
00183         SET_FLAG(force, FLAG_APPLIED);
00184         change_abil(who, force);
00185         insert_ob_in_ob(force, who);
00186     } else {
00187         free_object(force);
00188     }
00189 
00190     /* check for hp, sp change */
00191     if (food->stats.hp != 0 && !is_wraith_pl(who)) {
00192         if (QUERY_FLAG(food, FLAG_CURSED)) {
00193             strcpy(who->contr->killer, food->name);
00194             hit_player(who, food->stats.hp, food, AT_POISON, 1);
00195             draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED,
00196                 "Eck!...that was poisonous!", NULL);
00197         } else {
00198             if (food->stats.hp > 0)
00199                 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00200                     "You begin to feel better.", NULL);
00201             else
00202                 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED,
00203                     "Eck!...that was poisonous!", NULL);
00204             who->stats.hp += food->stats.hp;
00205         }
00206     }
00207     if (food->stats.sp != 0) {
00208         if (QUERY_FLAG(food, FLAG_CURSED)) {
00209             draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED,
00210                 "You are drained of mana!", NULL);
00211             who->stats.sp -= food->stats.sp;
00212             if (who->stats.sp < 0)
00213                 who->stats.sp = 0;
00214         } else {
00215             draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00216                 "You feel a rush of magical energy!", NULL);
00217             who->stats.sp += food->stats.sp;
00218             /* place limit on max sp from food? */
00219         }
00220     }
00221     fix_object(who);
00222 }
00223 
00237 static int dragon_eat_flesh(object *op, object *meal) {
00238     object *skin = NULL;    /* pointer to dragon skin force*/
00239     object *abil = NULL;    /* pointer to dragon ability force*/
00240     object *tmp = NULL;     /* tmp. object */
00241 
00242     char buf[MAX_BUF];            /* tmp. string buffer */
00243     double chance;                /* improvement-chance of one resist type */
00244     double totalchance = 1;       /* total chance of gaining one resistance */
00245     double bonus = 0;             /* level bonus (improvement is easier at lowlevel) */
00246     double mbonus = 0;            /* monster bonus */
00247     int atnr_winner[NROFATTACKS]; /* winning candidates for resistance improvement */
00248     int winners = 0;              /* number of winners */
00249     int i;                        /* index */
00250 
00251     /* let's make sure and doublecheck the parameters */
00252     if (meal->type != FLESH || !is_dragon_pl(op))
00253         return 0;
00254 
00255     /* now grab the 'dragon_skin'- and 'dragon_ability'-forces
00256      * from the player's inventory
00257      */
00258     for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
00259         if (tmp->type == FORCE) {
00260             if (strcmp(tmp->arch->name, "dragon_skin_force") == 0)
00261                 skin = tmp;
00262             else if (strcmp(tmp->arch->name, "dragon_ability_force") == 0)
00263                 abil = tmp;
00264         }
00265     }
00266 
00267     /* if either skin or ability are missing, this is an old player
00268      * which is not to be considered a dragon -> bail out
00269      */
00270     if (skin == NULL || abil == NULL)
00271         return 0;
00272 
00273         /* now start by filling stomache and health, according to food-value */
00274     if ((999-op->stats.food) < meal->stats.food)
00275         op->stats.hp += (999-op->stats.food)/50;
00276     else
00277         op->stats.hp += meal->stats.food/50;
00278     if (op->stats.hp > op->stats.maxhp)
00279         op->stats.hp = op->stats.maxhp;
00280 
00281     op->stats.food = MIN(999, op->stats.food+meal->stats.food);
00282 
00283     /*LOG(llevDebug, "-> player: %d, flesh: %d\n", op->level, meal->level);*/
00284 
00285     /* on to the interesting part: chances for adding resistance */
00286     for (i = 0; i < NROFATTACKS; i++) {
00287         if (meal->resist[i] > 0 && atnr_is_dragon_enabled(i)) {
00288             /* got positive resistance, now calculate improvement chance (0-100) */
00289 
00290             /* this bonus makes resistance increase easier at lower levels */
00291             bonus = (settings.max_level-op->level)*30./((double)settings.max_level);
00292             if (i == abil->stats.exp)
00293                 bonus += 5;  /* additional bonus for resistance of ability-focus */
00294 
00295             /* monster bonus increases with level, because high-level
00296              * flesh is too rare
00297              */
00298             mbonus = op->level*20./((double)settings.max_level);
00299 
00300             chance = (((double)MIN(op->level+bonus, meal->level+bonus+mbonus))*100./((double)settings.max_level))-skin->resist[i];
00301 
00302             if (chance >= 0.)
00303                 chance += 1.;
00304             else
00305                 chance = (chance < -12) ? 0. : 1./pow(2., -chance);
00306 
00307             /* chance is proportional to amount of resistance (max. 50) */
00308             chance *= ((double)(MIN(meal->resist[i], 50)))/50.;
00309 
00310             /* doubled chance for resistance of ability-focus */
00311             if (i == abil->stats.exp)
00312                 chance = MIN(100., chance*2.);
00313 
00314             /* now make the throw and save all winners (Don't insert luck bonus here!) */
00315             if (RANDOM()%10000 < (int)(chance*100)) {
00316                 atnr_winner[winners] = i;
00317                 winners++;
00318             }
00319 
00320             if (chance >= 0.01)
00321                 totalchance *= 1-chance/100;
00322 
00323             /*LOG(llevDebug, "   %s: bonus %.1f, chance %.1f\n", attacks[i], bonus, chance);*/
00324         }
00325     }
00326 
00327     /* inverse totalchance as until now we have the failure-chance   */
00328     totalchance = 100-totalchance*100;
00329     /* print message according to totalchance */
00330     if (totalchance > 50.)
00331         snprintf(buf, sizeof(buf), "Hmm! The %s tasted delicious!", meal->name);
00332     else if (totalchance > 10.)
00333         snprintf(buf, sizeof(buf), "The %s tasted very good.", meal->name);
00334     else if (totalchance > 1.)
00335         snprintf(buf, sizeof(buf), "The %s tasted good.", meal->name);
00336     else if (totalchance > 0.1)
00337         snprintf(buf, sizeof(buf), "The %s tasted bland.", meal->name);
00338     else if (totalchance >= 0.01)
00339         snprintf(buf, sizeof(buf), "The %s had a boring taste.", meal->name);
00340     else if (meal->last_eat > 0 && atnr_is_dragon_enabled(meal->last_eat))
00341         snprintf(buf, sizeof(buf), "The %s tasted strange.", meal->name);
00342     else
00343         snprintf(buf, sizeof(buf), "The %s had no taste.", meal->name);
00344     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
00345         buf, NULL);
00346 
00347         /* now choose a winner if we have any */
00348     i = -1;
00349     if (winners > 0)
00350         i = atnr_winner[RANDOM()%winners];
00351 
00352     if (i >= 0 && i < NROFATTACKS && skin->resist[i] < 95) {
00353         /* resistance increased! */
00354         skin->resist[i]++;
00355         fix_object(op);
00356 
00357         draw_ext_info_format(NDI_UNIQUE|NDI_RED, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_PROTECTION_GAIN,
00358             "Your skin is now more resistant to %s!",
00359             "Your skin is now more resistant to %s!",
00360             change_resist_msg[i]);
00361     }
00362 
00363     /* if this flesh contains a new ability focus, we mark it
00364      * into the ability_force and it will take effect on next level
00365      */
00366     if (meal->last_eat > 0
00367     && atnr_is_dragon_enabled(meal->last_eat)
00368     && meal->last_eat != abil->last_eat) {
00369         abil->last_eat = meal->last_eat; /* write:last_eat <new attnr focus> */
00370 
00371         if (meal->last_eat != abil->stats.exp) {
00372             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
00373                 "Your metabolism prepares to focus on %s!",
00374                 "Your metabolism prepares to focus on %s!",
00375                 change_resist_msg[meal->last_eat]);
00376             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
00377                 "The change will happen at level %d",
00378                 "The change will happen at level %d",
00379                 abil->level+1);
00380         } else {
00381             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
00382                 "Your metabolism will continue to focus on %s.",
00383                 "Your metabolism will continue to focus on %s.",
00384                 change_resist_msg[meal->last_eat]);
00385             abil->last_eat = 0;
00386         }
00387     }
00388     return 1;
00389 }