Crossfire Server, Branch 1.12
R12190
|
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 }