Atrinik Server 2.5
types/weapon_improver.c
Go to the documentation of this file.
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 
00032 #include <global.h>
00033 
00040 #define IMPROVE_PREPARE 1
00041 
00042 #define IMPROVE_DAMAGE 2
00043 
00044 #define IMPROVE_WEIGHT 3
00045 
00046 #define IMPROVE_ENCHANT 4
00047 
00048 #define IMPROVE_STR 5
00049 
00050 #define IMPROVE_DEX 6
00051 
00052 #define IMPROVE_CON 7
00053 
00054 #define IMPROVE_WIS 8
00055 
00056 #define IMPROVE_CHA 9
00057 
00058 #define IMPROVE_INT 10
00059 
00060 #define IMPROVE_POW 11
00061 
00063 static int improve_weapon_stat(object *op, object *improver, object *weapon, signed char *stat, int sacrifice_count, char *statname);
00064 static int prepare_weapon(object *op, object *improver, object *weapon);
00065 static int improve_weapon(object *op, object *improver, object *weapon);
00066 static void eat_item(object *op, const char *item);
00067 static int check_item(object *op, const char *item);
00068 static int check_sacrifice(object *op, object *improver);
00069 
00074 void apply_weapon_improver(object *op, object *tmp)
00075 {
00076     object *otmp;
00077 
00078     if (op->type != PLAYER)
00079     {
00080         return;
00081     }
00082 
00083     if (blocks_magic(op->map, op->x, op->y))
00084     {
00085         new_draw_info(0, COLOR_WHITE, op, "Something blocks the magic of the scroll.");
00086         return;
00087     }
00088 
00089     otmp = find_marked_object(op);
00090 
00091     if (!otmp || otmp->type != WEAPON)
00092     {
00093         new_draw_info(0, COLOR_WHITE, op, "You need to mark a weapon object.");
00094         return;
00095     }
00096 
00097     new_draw_info(0, COLOR_WHITE, op, "Applied weapon builder.");
00098     improve_weapon(op, tmp, otmp);
00099     esrv_send_item(op, otmp);
00100 }
00101 
00112 int check_weapon_power(object *who, int improvs)
00113 {
00114     int level = who->level;
00115 
00116     /* The skill system hands out wc and dam bonuses to fighters
00117      * more generously than the old system (see fix_player). Thus
00118      * we need to curtail the power of player enchanted weapons.
00119      * I changed this to 1 improvement per "fighter" level/5 -b.t.
00120      * Note:  Nothing should break by allowing this ratio to be different or
00121      * using normal level - it is just a matter of play balance. */
00122     if (who->type == PLAYER)
00123     {
00124         object *wc_obj = NULL;
00125 
00126         for (wc_obj = who->inv; wc_obj; wc_obj = wc_obj->below)
00127         {
00128             if (wc_obj->type == EXPERIENCE && wc_obj->stats.Str)
00129             {
00130                 break;
00131             }
00132         }
00133 
00134         if (!wc_obj)
00135         {
00136             LOG(llevBug, "Player: %s lacks wc experience object.\n", who->name);
00137         }
00138         else
00139         {
00140             level = wc_obj->level;
00141         }
00142     }
00143 
00144     return (improvs <= ((level / 5) + 5));
00145 }
00146 
00156 static int improve_weapon_stat(object *op, object *improver, object *weapon, signed char *stat_id, int sacrifice_count, char *stat_name)
00157 {
00158     new_draw_info(0, COLOR_WHITE, op, "Your sacrifice was accepted.");
00159     *stat_id += sacrifice_count;
00160     weapon->last_eat++;
00161     new_draw_info_format(0, COLOR_WHITE, op, "Weapon's bonus to %s improved by %d.", stat_name, sacrifice_count);
00162     decrease_ob(improver);
00163 
00164     /* So it updates the players stats and the window */
00165     fix_player(op);
00166     return 1;
00167 }
00168 
00178 static int prepare_weapon(object *op, object *improver, object *weapon)
00179 {
00180     int sacrifice_count, i;
00181     char buf[MAX_BUF];
00182 
00183     if (weapon->level != 0)
00184     {
00185         new_draw_info(0, COLOR_WHITE, op, "Weapon already prepared.");
00186         return 0;
00187     }
00188 
00189     for (i = 0; i < NROFATTACKS; i++)
00190     {
00191         if (weapon->protection[i])
00192         {
00193             break;
00194         }
00195     }
00196 
00197     /* If we break out, i will be less than nrofattacks, preventing
00198      * improvement of items that already have protections. */
00199     if (i < NROFATTACKS || weapon->stats.hp || weapon->stats.sp || weapon->stats.exp || weapon->stats.ac)
00200     {
00201         new_draw_info(0, COLOR_WHITE, op, "Cannot prepare magic weapons.");
00202         return 0;
00203     }
00204 
00205     sacrifice_count = check_sacrifice(op, improver);
00206 
00207     if (sacrifice_count <= 0)
00208     {
00209         return 0;
00210     }
00211 
00212     sacrifice_count = isqrt(sacrifice_count);
00213     weapon->level = sacrifice_count;
00214     new_draw_info(0, COLOR_WHITE, op, "Your sacrifice was accepted.");
00215     eat_item(op, improver->slaying);
00216     new_draw_info_format(0, COLOR_WHITE, op, "Your *%s may be improved %d times.", weapon->name, sacrifice_count);
00217     snprintf(buf, sizeof(buf), "%s's %s", op->name, weapon->name);
00218     FREE_AND_COPY_HASH(weapon->name, buf);
00219 
00220     /* prevents preparing n weapons in the same slot at once! */
00221     weapon->nrof = 0;
00222     decrease_ob(improver);
00223     weapon->last_eat = 0;
00224     return 1;
00225 }
00226 
00241 static int improve_weapon(object *op, object *improver, object *weapon)
00242 {
00243     int sacrifice_count, sacrifice_needed = 0;
00244 
00245     if (improver->stats.sp == IMPROVE_PREPARE)
00246     {
00247         return prepare_weapon(op, improver, weapon);
00248     }
00249 
00250     if (weapon->level==0)
00251     {
00252         new_draw_info(0, COLOR_WHITE, op, "This weapon has not been prepared.");
00253         return 0;
00254     }
00255 
00256     if (weapon->level == weapon->last_eat)
00257     {
00258         new_draw_info(0, COLOR_WHITE, op, "This weapon cannot be improved any more.");
00259         return 0;
00260     }
00261 
00262     if (QUERY_FLAG(weapon, FLAG_APPLIED) && !check_weapon_power(op, weapon->last_eat + 1))
00263     {
00264         new_draw_info(0, COLOR_WHITE, op, "Improving the weapon will make it too powerful for you to use.\nUnready it if you really want to improve it.");
00265         return 0;
00266     }
00267 
00268     /* This just increases damage by 5 points, no matter what.  No sacrifice
00269      * is needed.  Since stats.dam is now a 16 bit value and not 8 bit,
00270      * don't put any maximum value on damage - the limit is how much the
00271      * weapon  can be improved. */
00272     if (improver->stats.sp == IMPROVE_DAMAGE)
00273     {
00274         weapon->stats.dam += 5;
00275         /* 5 KG's */
00276         weapon->weight += 5000;
00277         new_draw_info_format(0, COLOR_WHITE, op, "Damage has been increased by 5 to %d", weapon->stats.dam);
00278         weapon->last_eat++;
00279         decrease_ob(improver);
00280 
00281         return 1;
00282     }
00283 
00284     if (improver->stats.sp == IMPROVE_WEIGHT)
00285     {
00286         /* Reduce weight by 20% */
00287         weapon->weight = (weapon->weight * 8) / 10;
00288 
00289         if (weapon->weight < 1)
00290         {
00291             weapon->weight = 1;
00292         }
00293 
00294         new_draw_info_format(0, COLOR_WHITE, op, "Weapon weight reduced to %6.1f kg", (float)weapon->weight / 1000.0);
00295         weapon->last_eat++;
00296         decrease_ob(improver);
00297         return 1;
00298     }
00299 
00300     if (improver->stats.sp == IMPROVE_ENCHANT)
00301     {
00302         weapon->magic++;
00303         weapon->last_eat++;
00304         new_draw_info_format(0, COLOR_WHITE, op, "Weapon magic increased to %d", weapon->magic);
00305         decrease_ob(improver);
00306         return 1;
00307     }
00308 
00309     sacrifice_needed = weapon->stats.Str + weapon->stats.Int + weapon->stats.Dex + weapon->stats.Pow + weapon->stats.Con + weapon->stats.Cha + weapon->stats.Wis;
00310 
00311     if (sacrifice_needed < 1)
00312     {
00313         sacrifice_needed = 1;
00314     }
00315 
00316     sacrifice_needed *= 2;
00317 
00318     sacrifice_count = check_sacrifice(op, improver);
00319 
00320     if (sacrifice_count < sacrifice_needed)
00321     {
00322         new_draw_info_format(0, COLOR_WHITE, op, "You need at least %d %s", sacrifice_needed, improver->slaying);
00323         return 0;
00324     }
00325 
00326     eat_item(op, improver->slaying);
00327 
00328     switch (improver->stats.sp)
00329     {
00330         case IMPROVE_STR:
00331             return improve_weapon_stat(op, improver, weapon, (signed char *) &(weapon->stats.Str), 1, "strength");
00332 
00333         case IMPROVE_DEX:
00334             return improve_weapon_stat(op, improver, weapon, (signed char *) &(weapon->stats.Dex), 1, "dexterity");
00335 
00336         case IMPROVE_CON:
00337             return improve_weapon_stat(op, improver, weapon, (signed char *) &(weapon->stats.Con), 1, "constitution");
00338 
00339         case IMPROVE_WIS:
00340             return improve_weapon_stat(op, improver, weapon, (signed char *) &(weapon->stats.Wis), 1, "wisdom");
00341 
00342         case IMPROVE_CHA:
00343             return improve_weapon_stat(op, improver, weapon, (signed char *) &(weapon->stats.Cha), 1, "charisma");
00344 
00345         case IMPROVE_INT:
00346             return improve_weapon_stat(op, improver, weapon, (signed char *) &(weapon->stats.Int), 1, "intelligence");
00347 
00348         case IMPROVE_POW:
00349             return improve_weapon_stat(op, improver, weapon, (signed char *) &(weapon->stats.Pow), 1, "power");
00350 
00351         default:
00352             new_draw_info(0, COLOR_WHITE, op, "Unknown improvement type.");
00353     }
00354 
00355     LOG(llevBug, "improve_weapon(): Got to end of function!\n");
00356 
00357     return 0;
00358 }
00359 
00364 static void eat_item(object *op, const char *item)
00365 {
00366     object *prev = op;
00367 
00368     op = op->below;
00369 
00370     while (op != NULL)
00371     {
00372         if (strcmp(op->arch->name, item) == 0)
00373         {
00374             decrease_ob_nr(op, op->nrof);
00375             op = prev;
00376         }
00377 
00378         prev = op;
00379         op = op->below;
00380     }
00381 }
00382 
00390 static int check_item(object *op, const char *item)
00391 {
00392     int count = 0;
00393 
00394     if (item == NULL)
00395     {
00396         return 0;
00397     }
00398 
00399     op = op->below;
00400 
00401     while (op != NULL)
00402     {
00403         if (strcmp(op->arch->name, item) == 0)
00404         {
00405             if (!QUERY_FLAG(op, FLAG_CURSED) && !QUERY_FLAG(op, FLAG_DAMNED) && !QUERY_FLAG(op, FLAG_UNPAID))
00406             {
00407                 /* this is necessary for artifact sacrifices --FD-- */
00408                 if (op->nrof == 0)
00409                 {
00410                     count++;
00411                 }
00412                 else
00413                 {
00414                     count += op->nrof;
00415                 }
00416             }
00417         }
00418 
00419         op = op->below;
00420     }
00421 
00422     return count;
00423 }
00424 
00432 static int check_sacrifice(object *op, object *improver)
00433 {
00434     int count = 0;
00435 
00436     if (improver->slaying != NULL)
00437     {
00438         count = check_item(op, improver->slaying);
00439 
00440         if (count < 1)
00441         {
00442             new_draw_info_format(0, COLOR_WHITE, op, "The gods want more %ss.", improver->slaying);
00443             return 0;
00444         }
00445     }
00446     else
00447     {
00448         count = 1;
00449     }
00450 
00451     return count;
00452 }