|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00030 #include <global.h> 00031 00037 #define POTION_IMPROVE_STATS 3 00038 00039 #define POTION_IMPROVE_HP 0 00040 00041 #define POTION_IMPROVE_SP 1 00042 00043 #define POTION_IMPROVE_GRACE 2 00044 00049 static const size_t potion_improve_stat_offsets[POTION_IMPROVE_STATS] = 00050 { 00051 offsetof(player, levhp), offsetof(player, levsp), offsetof(player, levgrace) 00052 }; 00053 00058 static const char *const potion_improve_stat_names[POTION_IMPROVE_STATS] = 00059 { 00060 "health", "mana", "grace" 00061 }; 00062 00068 static int potion_improve_apply(object *op, object *tmp) 00069 { 00070 int indices[POTION_IMPROVE_STATS] = {POTION_IMPROVE_HP, POTION_IMPROVE_SP, POTION_IMPROVE_GRACE}; 00071 int stat_id, i, level, max, cursed; 00072 char *levarr, oldlev; 00073 00074 /* Shuffle the array so we check all the stats for possible 00075 * improvement in random order. */ 00076 permute(indices, 0, POTION_IMPROVE_STATS); 00077 /* Determine whether the potion is cursed or not. */ 00078 cursed = QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED); 00079 00080 /* A potion will be used up even if we don't actually increase any 00081 * stats. */ 00082 decrease_ob(tmp); 00083 00084 /* Check all the stats. As 'indices' has been shuffled, that is used 00085 * to determine which stat we're working on, instead of directly 00086 * using 'stat'. */ 00087 for (stat_id = 0; stat_id < POTION_IMPROVE_STATS; stat_id++) 00088 { 00089 /* Determine level related to this stat, and the maximum possible 00090 * value it can be improved to. */ 00091 if (indices[stat_id] == POTION_IMPROVE_HP) 00092 { 00093 level = op->level; 00094 max = op->arch->clone.stats.maxhp; 00095 } 00096 else if (indices[stat_id] == POTION_IMPROVE_SP) 00097 { 00098 level = CONTR(op)->exp_ptr[EXP_MAGICAL]->level; 00099 max = op->arch->clone.stats.maxsp; 00100 } 00101 else 00102 { 00103 level = CONTR(op)->exp_ptr[EXP_WISDOM]->level; 00104 max = op->arch->clone.stats.maxgrace; 00105 } 00106 00107 /* Check to see if we can increase (or decrease, if the potion is 00108 * cursed) any stats. If the potion is cursed, we start at level 00109 * 2, because level 1 has the improvement fixed to the maximum 00110 * value (since it's the first level, obviously). */ 00111 for (i = cursed ? 2 : 1; i <= level; i++) 00112 { 00113 /* Get pointer to the array that stores the level 00114 * improvements for this stat. */ 00115 levarr = (void *) ((char *) CONTR(op) + potion_improve_stat_offsets[indices[stat_id]]); 00116 00117 if (cursed) 00118 { 00119 /* The potion is cursed and there is a stat improvement 00120 * for this level, so go ahead and remove the 00121 * improvement. */ 00122 if (levarr[i] != 1) 00123 { 00124 oldlev = levarr[i]; 00125 levarr[i] = 1; 00126 fix_player(op); 00127 insert_spell_effect("meffect_purple", op->map, op->x, op->y); 00128 play_sound_map(op->map, CMD_SOUND_EFFECT, "poison.ogg", op->x, op->y, 0, 0); 00129 new_draw_info_format(0, COLOR_WHITE, op, "The foul potion burns like fire inside you, and your %s decreases by %d!", potion_improve_stat_names[indices[stat_id]], oldlev - levarr[i]); 00130 return 1; 00131 } 00132 } 00133 else 00134 { 00135 /* The stat improvement for this level has not been maxed 00136 * yet, so go ahead and increase it. */ 00137 if (levarr[i] != max) 00138 { 00139 oldlev = levarr[i]; 00140 levarr[i] = max; 00141 fix_player(op); 00142 insert_spell_effect("meffect_yellow", op->map, op->x, op->y); 00143 play_sound_map(op->map, CMD_SOUND_EFFECT, "magic_default.ogg", op->x, op->y, 0, 0); 00144 new_draw_info_format(0, COLOR_WHITE, op, "You feel a little more perfect, and your %s increases by %d!", potion_improve_stat_names[indices[stat_id]], levarr[i] - oldlev); 00145 return 1; 00146 } 00147 } 00148 } 00149 } 00150 00151 /* No effect (because there is nothing to increase [or decrease, in 00152 * case of cursed potions]); inform the player. */ 00153 if (cursed) 00154 { 00155 play_sound_map(op->map, CMD_SOUND_EFFECT, "poison.ogg", op->x, op->y, 0, 0); 00156 new_draw_info(0, COLOR_WHITE, op, "The potion was foul but had no effect on your tortured body."); 00157 } 00158 else 00159 { 00160 play_sound_map(op->map, CMD_SOUND_EFFECT, "magic_default.ogg", op->x, op->y, 0, 0); 00161 new_draw_info(0, COLOR_WHITE, op, "The potion had no effect - you are already perfect."); 00162 } 00163 00164 return 1; 00165 } 00166 00173 static int potion_special_apply(object *op, object *tmp) 00174 { 00175 object *force; 00176 int i, val; 00177 00178 /* Create a force and copy the effects in. */ 00179 force = get_archetype("force"); 00180 00181 if (!force) 00182 { 00183 LOG(llevBug, "potion_special_apply(): Can't create force object.\n"); 00184 return 0; 00185 } 00186 00187 force->type = POTION_EFFECT; 00188 /* Copy the amount of time the effect should last. */ 00189 force->stats.food = tmp->stats.food; 00190 SET_FLAG(force, FLAG_IS_USED_UP); 00191 00192 /* Make sure the effect lasts for at least a little while. */ 00193 if (force->stats.food <= 0) 00194 { 00195 force->stats.food = 1; 00196 } 00197 00198 if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) 00199 { 00200 int protection; 00201 00202 /* The potion is cursed/damned, so the negative effects stay 00203 * longer. */ 00204 force->stats.food *= 3; 00205 00206 /* Copy over protection values to the force from the potion, but 00207 * reverse them first. Attacks are ignored, as it's not actually 00208 * possible to store negative attack values in objects. */ 00209 for (i = 0; i < NROFATTACKS; i++) 00210 { 00211 protection = tmp->protection[i] > 0 ? -tmp->protection[i] : tmp->protection[i]; 00212 00213 /* If the potion is damned, the effects worsen... */ 00214 if (QUERY_FLAG(tmp, FLAG_DAMNED)) 00215 { 00216 protection *= 2; 00217 } 00218 00219 /* Actually set the protection value, but make sure it's in a 00220 * valid range. */ 00221 force->protection[i] = MIN(100, MAX(-100, protection)); 00222 } 00223 00224 insert_spell_effect("meffect_purple", op->map, op->x, op->y); 00225 play_sound_map(op->map, CMD_SOUND_EFFECT, "poison.ogg", op->x, op->y, 0, 0); 00226 } 00227 else 00228 { 00229 memcpy(force->protection, tmp->protection, sizeof(tmp->protection)); 00230 memcpy(force->attack, tmp->attack, sizeof(tmp->attack)); 00231 00232 insert_spell_effect("meffect_green", op->map, op->x, op->y); 00233 play_sound_map(op->map, CMD_SOUND_EFFECT, "magic_default.ogg", op->x, op->y, 0, 0); 00234 } 00235 00236 /* Copy over stat values. */ 00237 for (i = 0; i < NUM_STATS; i++) 00238 { 00239 /* Get the stat value from the potion. */ 00240 val = get_attr_value(&tmp->stats, i); 00241 00242 /* No value, nothing to do. */ 00243 if (!val) 00244 { 00245 continue; 00246 } 00247 00248 /* If the potion is cursed/damned and the stat increase is 00249 * positive, reverse it. */ 00250 if ((QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) && val > 0) 00251 { 00252 val = -val; 00253 } 00254 00255 /* The potion is damned, so the negative effect is worse. */ 00256 if (QUERY_FLAG(tmp, FLAG_DAMNED)) 00257 { 00258 val *= 2; 00259 } 00260 00261 /* Now set the stat value of the force to the one calculcated 00262 * above, but make sure it doesn't overflow sint8. */ 00263 change_attr_value(&force->stats, i, MIN(SINT8_MAX, MAX(SINT8_MIN, val))); 00264 } 00265 00266 /* Insert the force into the player and apply it. */ 00267 force->speed_left = -1; 00268 force = insert_ob_in_ob(force, op); 00269 SET_FLAG(force, FLAG_APPLIED); 00270 00271 if (!change_abil(op, force)) 00272 { 00273 new_draw_info(0, COLOR_WHITE, op, "Nothing happened."); 00274 } 00275 00276 decrease_ob(tmp); 00277 return 1; 00278 } 00279 00286 static int potion_restoration_apply(object *op, object *tmp) 00287 { 00288 object *depletion; 00289 archetype *at; 00290 int i; 00291 00292 /* Cursed potion of minor restoration; reverse effects (stats are 00293 * depleted). */ 00294 if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) 00295 { 00296 /* Drain 2 stats if the potion is cursed, 4 if it's damned. */ 00297 for (i = QUERY_FLAG(tmp, FLAG_DAMNED) ? 0 : 2; i < 4; i++) 00298 { 00299 drain_stat(op); 00300 } 00301 00302 insert_spell_effect("meffect_purple", op->map, op->x, op->y); 00303 play_sound_map(op->map, CMD_SOUND_EFFECT, "poison.ogg", op->x, op->y, 0, 0); 00304 decrease_ob(tmp); 00305 return 1; 00306 } 00307 00308 at = find_archetype("depletion"); 00309 00310 if (!at) 00311 { 00312 LOG(llevBug, "potion_restoration_apply(): Could not find archetype 'depletion'.\n"); 00313 return 0; 00314 } 00315 00316 depletion = present_arch_in_ob(at, op); 00317 00318 if (depletion) 00319 { 00320 for (i = 0; i < NUM_STATS; i++) 00321 { 00322 if (get_attr_value(&depletion->stats, i)) 00323 { 00324 new_draw_info(0, COLOR_WHITE, op, restore_msg[i]); 00325 } 00326 } 00327 00328 remove_ob(depletion); 00329 fix_player(op); 00330 } 00331 else 00332 { 00333 new_draw_info(0, COLOR_WHITE, op, "You are not depleted."); 00334 } 00335 00336 insert_spell_effect("meffect_green", op->map, op->x, op->y); 00337 play_sound_map(op->map, CMD_SOUND_EFFECT, "magic_default.ogg", op->x, op->y, 0, 0); 00338 decrease_ob(tmp); 00339 return 1; 00340 } 00341 00347 int apply_potion(object *op, object *tmp) 00348 { 00349 if (op->type != PLAYER) 00350 { 00351 return 0; 00352 } 00353 00354 if (!CONTR(op) || !CONTR(op)->exp_ptr[EXP_MAGICAL] || !CONTR(op)->exp_ptr[EXP_WISDOM]) 00355 { 00356 LOG(llevBug, "apply_potion(): Called with invalid player object (no controller or no magic/wisdom exp object).\n"); 00357 return 0; 00358 } 00359 00360 /* Use magic devices skill for using potions. */ 00361 if (!change_skill(op, SK_USE_MAGIC_ITEM)) 00362 { 00363 return 0; 00364 } 00365 00366 /* We are using it, so we now know what it does. */ 00367 if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED)) 00368 { 00369 identify(tmp); 00370 } 00371 00372 /* Potions with temporary effects. */ 00373 if (tmp->last_eat == -1) 00374 { 00375 CONTR(op)->stat_potions_used++; 00376 return potion_special_apply(op, tmp); 00377 } 00378 /* Potion of minor restoration (removes depletion). */ 00379 else if (tmp->last_eat == 1) 00380 { 00381 CONTR(op)->stat_potions_used++; 00382 return potion_restoration_apply(op, tmp); 00383 } 00384 /* Improvement potion. */ 00385 else if (tmp->last_eat == 2) 00386 { 00387 CONTR(op)->stat_potions_used++; 00388 return potion_improve_apply(op, tmp); 00389 } 00390 00391 if (tmp->stats.sp == SP_NO_SPELL) 00392 { 00393 new_draw_info(0, COLOR_WHITE, op, "Nothing happens as you apply it."); 00394 decrease_ob(tmp); 00395 return 0; 00396 } 00397 00398 CONTR(op)->stat_potions_used++; 00399 00400 /* Fire in the player's facing direction, unless the spell is 00401 * something like healing or cure disease. */ 00402 cast_spell(op, tmp, spells[tmp->stats.sp].flags & SPELL_DESC_SELF ? 0 : op->facing, tmp->stats.sp, 1, CAST_POTION, NULL); 00403 decrease_ob(tmp); 00404 00405 if (!QUERY_FLAG(op, FLAG_REMOVED)) 00406 { 00407 fix_player(op); 00408 } 00409 00410 return 1; 00411 }
1.7.4