Atrinik Server 2.5
server/skills.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 
00030 #include <global.h>
00031 #include <book.h>
00032 
00039 sint64 find_traps(object *pl, int level)
00040 {
00041     object *tmp, *tmp2;
00042     mapstruct *m;
00043     int xt, yt, i, suc = 0;
00044 
00045     /* First we search all around us for runes and traps, which are
00046      * all type RUNE */
00047     for (i = 0; i < 9; i++)
00048     {
00049         /* Check everything in the square for trapness */
00050         xt = pl->x + freearr_x[i];
00051         yt = pl->y + freearr_y[i];
00052 
00053         if (!(m = get_map_from_coord(pl->map, &xt, &yt)))
00054         {
00055             continue;
00056         }
00057 
00058         for (tmp = get_map_ob(m, xt, yt); tmp != NULL; tmp = tmp->above)
00059         {
00060             /* And now we'd better do an inventory traversal of each
00061              * of these objects' inventory */
00062             if (pl != tmp && (tmp->type == PLAYER || tmp->type == MONSTER))
00063             {
00064                 continue;
00065             }
00066 
00067             for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
00068             {
00069                 if (tmp2->type == RUNE)
00070                 {
00071                     if (trap_see(pl, tmp2, level))
00072                     {
00073                         trap_show(tmp2, tmp);
00074 
00075                         if (!suc)
00076                         {
00077                             suc = 1;
00078                         }
00079                     }
00080                     else
00081                     {
00082                         /* Give out a "we have found signs of traps"
00083                          * if the traps level is not 1.8 times higher. */
00084                         if (tmp2->level <= (level * 1.8f))
00085                         {
00086                             suc = 2;
00087                         }
00088                     }
00089                 }
00090             }
00091 
00092             if (tmp->type == RUNE)
00093             {
00094                 if (trap_see(pl, tmp, level))
00095                 {
00096                     trap_show(tmp, tmp);
00097 
00098                     if (!suc)
00099                     {
00100                         suc = 1;
00101                     }
00102                 }
00103                 else
00104                 {
00105                     /* Give out a "we have found signs of traps"
00106                      * if the traps level is not 1.8 times higher. */
00107                     if (tmp->level <= (level * 1.8f))
00108                     {
00109                         suc = 2;
00110                     }
00111                 }
00112             }
00113         }
00114     }
00115 
00116     if (!suc)
00117     {
00118         new_draw_info(0, COLOR_WHITE, pl, "You can't detect any trap here.");
00119     }
00120     else if (suc == 2)
00121     {
00122         new_draw_info(0, COLOR_WHITE, pl, "You detect trap signs!");
00123     }
00124 
00125     return 0;
00126 }
00127 
00132 sint64 remove_trap(object *op)
00133 {
00134     object *tmp, *tmp2;
00135     mapstruct *m;
00136     int i, x, y;
00137 
00138     for (i = 0; i < 9; i++)
00139     {
00140         x = op->x + freearr_x[i];
00141         y = op->y + freearr_y[i];
00142 
00143         if (!(m = get_map_from_coord(op->map, &x, &y)))
00144         {
00145             continue;
00146         }
00147 
00148         /* Check everything in the square for trapness */
00149         for (tmp = get_map_ob(m, x, y); tmp != NULL; tmp = tmp->above)
00150         {
00151             /* And now we'd better do an inventory traversal of each
00152              * of these objects' inventory */
00153             for (tmp2 = tmp->inv; tmp2; tmp2 = tmp2->below)
00154             {
00155                 if (tmp2->type == RUNE && tmp2->stats.Cha <= 1)
00156                 {
00157                     if (QUERY_FLAG(tmp2, FLAG_SYS_OBJECT) || QUERY_FLAG(tmp2, FLAG_IS_INVISIBLE))
00158                     {
00159                         trap_show(tmp2, tmp);
00160                     }
00161 
00162                     trap_disarm(op, tmp2);
00163                     return 0;
00164                 }
00165             }
00166 
00167             if (tmp->type == RUNE && tmp->stats.Cha <= 1)
00168             {
00169                 if (QUERY_FLAG(tmp, FLAG_SYS_OBJECT) || QUERY_FLAG(tmp, FLAG_IS_INVISIBLE))
00170                 {
00171                     trap_show(tmp, tmp);
00172                 }
00173 
00174                 trap_disarm(op, tmp);
00175                 return 0;
00176             }
00177         }
00178     }
00179 
00180     new_draw_info(0, COLOR_WHITE, op, "There is no trap to remove nearby.");
00181     return 0;
00182 }
00183 
00195 object *find_throw_tag(object *op, tag_t tag)
00196 {
00197     object *tmp;
00198 
00199 if (CONTR(op)->socket.socket_version < 1048)
00200 {
00201     /* Look through the inventory. */
00202     for (tmp = op->inv; tmp; tmp = tmp->below)
00203     {
00204         /* Can't toss invisible or inv-locked items */
00205         if (IS_SYS_INVISIBLE(tmp) || QUERY_FLAG(tmp, FLAG_INV_LOCKED))
00206         {
00207             continue;
00208         }
00209 
00210         if (tmp->count == tag)
00211         {
00212             break;
00213         }
00214     }
00215 }
00216 else
00217 {
00218     tmp = CONTR(op)->ready_object[READY_OBJ_THROW];
00219 }
00220 
00221     if (!tmp)
00222     {
00223         return NULL;
00224     }
00225 
00226     if (CONTR(op)->socket.socket_version >= 1048 && !OBJECT_VALID(tmp, CONTR(op)->ready_object_tag[READY_OBJ_THROW]))
00227     {
00228         return NULL;
00229     }
00230 
00231     if (QUERY_FLAG(tmp, FLAG_APPLIED))
00232     {
00233         /* We can't apply throwing stuff like darts, so this must be a
00234          * weapon. Skip if not OR when it can't be thrown OR when it is
00235          * startequip which can't be dropped. */
00236         if (tmp->type != WEAPON || !QUERY_FLAG(tmp, FLAG_IS_THROWN))
00237         {
00238             new_draw_info_format(0, COLOR_WHITE, op, "You can't throw %s.", query_base_name(tmp, NULL));
00239             return NULL;
00240         }
00241         else if (QUERY_FLAG(tmp, FLAG_STARTEQUIP))
00242         {
00243             new_draw_info(0, COLOR_WHITE, op, "You can't throw god-given item!");
00244             return NULL;
00245         }
00246         /* If cursed or damned, we can't unapply it - no throwing. */
00247         else if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))
00248         {
00249             new_draw_info_format(0, COLOR_WHITE, op, "The %s sticks to your hand!", query_base_name(tmp, NULL));
00250             return NULL;
00251         }
00252         /* It's a throw hybrid weapon - unapply it. Then we will fire it
00253          * after this function returns. */
00254         else
00255         {
00256             if (apply_special(op, tmp, AP_UNAPPLY | AP_NO_MERGE))
00257             {
00258                 LOG(llevBug, "find_throw_ob(): couldn't unapply throwing item %s from %s\n", query_name(tmp, NULL), query_name(op, NULL));
00259                 return NULL;
00260             }
00261         }
00262     }
00263     else
00264     {
00265         /* Not weapon nor throwable - no throwing. */
00266         if ((tmp->type != WEAPON && tmp->type != POTION) && !QUERY_FLAG(tmp, FLAG_IS_THROWN))
00267         {
00268             new_draw_info_format(0, COLOR_WHITE, op, "You can't throw %s.", query_base_name(tmp, NULL));
00269             return NULL;
00270         }
00271         /* Special message for throw hybrid weapons. */
00272         else if (tmp->type == WEAPON)
00273         {
00274             new_draw_info_format(0, COLOR_WHITE, op, "You must apply the %s first.", query_base_name(tmp, NULL));
00275             return NULL;
00276         }
00277     }
00278 
00279     return tmp;
00280 }
00281 
00287 void do_throw(object *op, object *toss_item, int dir)
00288 {
00289     object *left_cont, *throw_ob = toss_item, *left = NULL, *tmp_op;
00290     tag_t left_tag;
00291     rv_vector range_vector;
00292 
00293     if (!throw_ob)
00294     {
00295         if (op->type == PLAYER)
00296         {
00297             new_draw_info(0, COLOR_WHITE, op, "You have nothing to throw.");
00298         }
00299 
00300         return;
00301     }
00302 
00303     if (QUERY_FLAG(throw_ob, FLAG_STARTEQUIP))
00304     {
00305         if (op->type == PLAYER)
00306         {
00307             new_draw_info(0, COLOR_WHITE, op, "The gods won't let you throw that.");
00308         }
00309 
00310         return;
00311     }
00312 
00313     if (throw_ob->weight <= 0)
00314     {
00315         new_draw_info_format(0, COLOR_WHITE, op, "You can't throw %s.\n", query_base_name(throw_ob, NULL));
00316         return;
00317     }
00318 
00319     /* These are throwing objects left to the player */
00320     left = throw_ob;
00321     left_cont = left->env;
00322     left_tag = left->count;
00323 
00324     /* Sometimes get_split_ob can't split an object (because op->nrof==0?)
00325      * and returns NULL. We must use 'left' then */
00326     if ((throw_ob = get_split_ob(throw_ob, 1, NULL, 0)) == NULL)
00327     {
00328         throw_ob = left;
00329         remove_ob(left);
00330         check_walk_off(left, NULL, MOVE_APPLY_VANISHED);
00331 
00332         if (op->type == PLAYER)
00333         {
00334             esrv_del_item(CONTR(op), left->count, left->env);
00335         }
00336     }
00337     else if (op->type == PLAYER)
00338     {
00339         if (was_destroyed(left, left_tag))
00340         {
00341             esrv_del_item(CONTR(op), left_tag, left_cont);
00342         }
00343         else
00344         {
00345             esrv_update_item(UPD_NROF, op, left);
00346         }
00347     }
00348 
00349     /* Special case: throwing powdery substances like dust, dirt */
00350     if (QUERY_FLAG(throw_ob, FLAG_DUST))
00351     {
00352         cast_dust(op, throw_ob, dir);
00353 
00354         /* update the shooting speed for the player action timer.
00355          * We init the used skill with it - its not calculated here.
00356          * cast_dust() can change the used skill... */
00357         if (op->type == PLAYER)
00358         {
00359             op->chosen_skill->stats.maxsp = throw_ob->last_grace;
00360         }
00361 
00362         return;
00363     }
00364 
00365     /* Targetting throwing */
00366     if (!dir && op->type == PLAYER && OBJECT_VALID(CONTR(op)->target_object, CONTR(op)->target_object_count))
00367     {
00368         dir = get_dir_to_target(op, CONTR(op)->target_object, &range_vector);
00369     }
00370 
00371     /* Three things here prevent a throw, you aimed at your feet, you
00372      * have no effective throwing strength, or you threw at a wall */
00373     if (!dir || wall(op->map, op->x + freearr_x[dir], op->y + freearr_y[dir]))
00374     {
00375         /* Bounces off 'wall', and drops to feet */
00376         if (!QUERY_FLAG(throw_ob, FLAG_REMOVED))
00377         {
00378             remove_ob(throw_ob);
00379 
00380             if (check_walk_off(throw_ob, NULL, MOVE_APPLY_MOVE) != CHECK_WALK_OK)
00381             {
00382                 return;
00383             }
00384         }
00385 
00386         if (op->type == PLAYER)
00387         {
00388             if (!dir)
00389             {
00390                 new_draw_info_format(0, COLOR_WHITE, op, "You drop %s at the ground.", query_name(throw_ob, NULL));
00391             }
00392             else
00393             {
00394                 new_draw_info(0, COLOR_WHITE, op, "Something is in the way.");
00395             }
00396         }
00397 
00398         throw_ob->x = op->x;
00399         throw_ob->y = op->y;
00400 
00401         if (!insert_ob_in_map(throw_ob, op->map, op, 0))
00402         {
00403             return;
00404         }
00405 
00406         return;
00407     }
00408 
00409     CONTR(op)->stat_missiles_thrown++;
00410 
00411     set_owner(throw_ob, op);
00412     set_owner(throw_ob->inv, op);
00413     throw_ob->direction = dir;
00414     throw_ob->x = op->x;
00415     throw_ob->y = op->y;
00416 
00417     /* Save original wc and dam */
00418     throw_ob->last_heal = throw_ob->stats.wc;
00419     throw_ob->stats.hp = throw_ob->stats.dam;
00420 
00421     /* Speed */
00422     throw_ob->speed = MIN(1.0f, (speed_bonus[op->stats.Str] + 1.0f) / 1.5f);
00423 
00424     /* Now we get the wc from the used skill. */
00425     if ((tmp_op = SK_skill(op)))
00426     {
00427         throw_ob->stats.wc += tmp_op->last_heal;
00428     }
00429     /* Monsters */
00430     else
00431     {
00432         throw_ob->stats.wc += 10;
00433     }
00434 
00435     throw_ob->stats.wc_range = op->stats.wc_range;
00436 
00437     if (QUERY_FLAG(throw_ob, FLAG_IS_THROWN))
00438     {
00439         throw_ob->stats.dam += throw_ob->magic;
00440         throw_ob->stats.wc += throw_ob->magic;
00441 
00442         /* Adjust for players */
00443         if (op->type == PLAYER)
00444         {
00445             op->chosen_skill->stats.maxsp = throw_ob->last_grace;
00446             throw_ob->stats.dam = FABS((int) ((float) (throw_ob->stats.dam + dam_bonus[op->stats.Str] / 2) * LEVEL_DAMAGE(SK_level(op))));
00447             throw_ob->stats.wc += thaco_bonus[op->stats.Dex] + SK_level(op);
00448         }
00449         else
00450         {
00451             throw_ob->stats.dam = FABS((int) ((float) (throw_ob->stats.dam) * LEVEL_DAMAGE(op->level)));
00452             throw_ob->stats.wc += 10 + op->level;
00453         }
00454 
00455         throw_ob->stats.grace = throw_ob->last_sp;
00456         throw_ob->stats.maxgrace = 60 + (RANDOM() % 12);
00457 
00458         /* Only throw objects get directional faces */
00459         if (GET_ANIM_ID(throw_ob) && NUM_ANIMATIONS(throw_ob))
00460         {
00461             SET_ANIMATION(throw_ob, (NUM_ANIMATIONS(throw_ob) / NUM_FACINGS(throw_ob)) * dir);
00462         }
00463 
00464         /* Adjust damage with item condition */
00465         throw_ob->stats.dam = (sint16) (((float) throw_ob->stats.dam / 100.0f) * (float) throw_ob->item_condition);
00466     }
00467 
00468     if (throw_ob->stats.dam < 0)
00469     {
00470         throw_ob->stats.dam = 0;
00471     }
00472 
00473     update_ob_speed(throw_ob);
00474     throw_ob->speed_left = 0;
00475 
00476     SET_MULTI_FLAG(throw_ob, FLAG_FLYING);
00477     SET_FLAG(throw_ob, FLAG_FLY_ON);
00478     SET_FLAG(throw_ob, FLAG_WALK_ON);
00479 
00480     play_sound_map(op->map, CMD_SOUND_EFFECT, "throw.ogg", op->x, op->y, 0, 0);
00481 
00482     /* Trigger the THROW event */
00483     trigger_event(EVENT_THROW, op, throw_ob, NULL, NULL, 0, 0, 0, SCRIPT_FIX_ACTIVATOR);
00484 
00485     if (insert_ob_in_map(throw_ob, op->map, op, 0))
00486     {
00487         move_arrow(throw_ob);
00488     }
00489 }