Atrinik Server 2.5
types/arrow.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 
00037 sint32 bow_get_ws(object *bow, object *arrow)
00038 {
00039     return (((float) bow->stats.sp / (1000000 / MAX_TIME)) + ((float) arrow->last_grace / (1000000 / MAX_TIME))) * 1000;
00040 }
00041 
00048 sint16 arrow_get_wc(object *op, object *bow, object *arrow)
00049 {
00050     object *skill = CONTR(op)->skill_ptr[bow_get_skill(bow)];
00051 
00052     if (!skill)
00053     {
00054         return 0;
00055     }
00056 
00057     return arrow->stats.wc + bow->magic + arrow->magic + skill->level + thaco_bonus[op->stats.Dex] + bow->stats.wc;
00058 }
00059 
00066 sint16 arrow_get_damage(object *op, object *bow, object *arrow)
00067 {
00068     sint16 dam;
00069     object *skill = CONTR(op)->skill_ptr[bow_get_skill(bow)];
00070 
00071     if (!skill)
00072     {
00073         return 0;
00074     }
00075 
00076     dam = arrow->stats.dam + arrow->magic;
00077     dam = FABS((int) ((float) (dam * LEVEL_DAMAGE(skill->level))));
00078     dam += dam * (dam_bonus[op->stats.Str] / 2 + bow->stats.dam + bow->magic) / 10;
00079 
00080     if (bow->item_condition > arrow->item_condition)
00081     {
00082         dam = (sint16) (((float) dam / 100.0f) * (float) bow->item_condition);
00083     }
00084     else
00085     {
00086         dam = (sint16) (((float) dam / 100.0f) * (float) arrow->item_condition);
00087     }
00088 
00089     return dam;
00090 }
00091 
00096 int bow_get_skill(object *bow)
00097 {
00098     if (bow->sub_type == RANGE_WEAP_BOW)
00099     {
00100         return SK_MISSILE_WEAPON;
00101     }
00102     else if (bow->sub_type == RANGE_WEAP_XBOWS)
00103     {
00104         return SK_XBOW_WEAP;
00105     }
00106     else
00107     {
00108         return SK_SLING_WEAP;
00109     }
00110 }
00111 
00121 object *arrow_find(object *op, shstr *type, int tag)
00122 {
00123 if (op->type != PLAYER || CONTR(op)->socket.socket_version < 1048)
00124 {
00125     object *tmp = NULL;
00126 
00127     if (tag == -2)
00128     {
00129         for (op = op->inv; op; op = op->below)
00130         {
00131             if (!tmp && op->type == CONTAINER && op->race == type && QUERY_FLAG(op, FLAG_APPLIED))
00132             {
00133                 tmp = arrow_find(op, type, -2);
00134             }
00135             else if (op->type == ARROW && op->race == type)
00136             {
00137                 return op;
00138             }
00139         }
00140 
00141         return tmp;
00142     }
00143     else
00144     {
00145         if (tag == -1)
00146         {
00147             return tmp;
00148         }
00149 
00150         for (op = op->inv; op; op = op->below)
00151         {
00152             if (op->count == (tag_t) tag)
00153             {
00154                 /* Simple task: we have an arrow marked */
00155                 if (op->race == type && op->type == ARROW)
00156                 {
00157                     return op;
00158                 }
00159 
00160                 /* we have container marked as missile source. Skip
00161                  * search when there is nothing in. Use the standard
00162                  * search now. */
00163                 if (op->race == type && op->type == CONTAINER)
00164                 {
00165                     tmp = arrow_find(op, type, -2);
00166                     return tmp;
00167                 }
00168             }
00169         }
00170 
00171         return tmp;
00172     }
00173 }
00174 else
00175 {
00176     object *tmp = CONTR(op)->ready_object[READY_OBJ_ARROW];
00177 
00178     /* Nothing readied. */
00179     if (!tmp)
00180     {
00181         return NULL;
00182     }
00183 
00184     if (CONTR(op)->socket.socket_version >= 1048 && !OBJECT_VALID(tmp, CONTR(op)->ready_object_tag[READY_OBJ_ARROW]))
00185     {
00186         return NULL;
00187     }
00188 
00189     /* The type does not match the arrow/quiver. */
00190     if (tmp->race != type)
00191     {
00192         return NULL;
00193     }
00194 
00195     /* The readied item is an arrow, so simply return it. */
00196     if (tmp->type == ARROW)
00197     {
00198         return tmp;
00199     }
00200     /* A quiver, search through it for arrows. */
00201     else if (tmp->type == CONTAINER)
00202     {
00203         for (tmp = tmp->inv; tmp; tmp = tmp->below)
00204         {
00205             if (tmp->race == type && tmp->type == ARROW)
00206             {
00207                 return tmp;
00208             }
00209         }
00210     }
00211 
00212     return NULL;
00213 }
00214 }
00215 
00220 void bow_fire(object *op, int dir)
00221 {
00222     object *left_cont, *bow, *arrow = NULL, *left, *tmp_op;
00223     tag_t left_tag;
00224 
00225     /* If no dir is specified, attempt to find get the direction from
00226      * player's target. */
00227     if (!dir && op->type == PLAYER && OBJECT_VALID(CONTR(op)->target_object, CONTR(op)->target_object_count))
00228     {
00229         rv_vector range_vector;
00230         dir = get_dir_to_target(op, CONTR(op)->target_object, &range_vector);
00231     }
00232 
00233     if (!dir)
00234     {
00235         new_draw_info(0, COLOR_WHITE, op, "You can't shoot yourself!");
00236         return;
00237     }
00238 
00239     bow = CONTR(op)->equipment[PLAYER_EQUIP_BOW];
00240 
00241     if (!bow)
00242     {
00243         LOG(llevBug, "bow_fire(): bow without activated bow (%s - %d).\n", op->name, dir);
00244     }
00245 
00246     if (!bow->race)
00247     {
00248         new_draw_info_format(0, COLOR_WHITE, op, "Your %s is broken.", bow->name);
00249         return;
00250     }
00251 
00252     if ((arrow = arrow_find(op, bow->race, CONTR(op)->firemode_tag2)) == NULL)
00253     {
00254         new_draw_info_format(0, COLOR_WHITE, op, "You have no %s left.", bow->race);
00255         return;
00256     }
00257 
00258     if (wall(op->map, op->x + freearr_x[dir], op->y + freearr_y[dir]))
00259     {
00260         new_draw_info(0, COLOR_WHITE, op, "Something is in the way.");
00261         return;
00262     }
00263 
00264     /* This should not happen, but sometimes does */
00265     if (arrow->nrof == 0)
00266     {
00267         LOG(llevDebug, "arrow->nrof == 0 in bow_fire() (%s)\n", query_name(arrow, NULL));
00268         remove_ob(arrow);
00269         return;
00270     }
00271 
00272     CONTR(op)->stat_arrows_fired++;
00273 
00274     /* These are arrows left to the player */
00275     left = arrow;
00276     left_tag = left->count;
00277     left_cont = left->env;
00278     arrow = get_split_ob(arrow, 1, NULL, 0);
00279     set_owner(arrow, op);
00280     arrow->direction = dir;
00281     arrow->x = op->x;
00282     arrow->y = op->y;
00283     arrow->speed = 1;
00284 
00285     /* Now the trick: we transfer the shooting speed in the used
00286      * skill - that will allow us to use "set_skill_speed() as global
00287      * function. */
00288     op->chosen_skill->stats.maxsp = bow->stats.sp + arrow->last_grace;
00289     update_ob_speed(arrow);
00290     arrow->speed_left = 0;
00291     SET_ANIMATION(arrow, (NUM_ANIMATIONS(arrow) / NUM_FACINGS(arrow)) * dir);
00292     /* Save original wc and dam */
00293     arrow->last_heal = arrow->stats.wc;
00294     /* Will be put back in fix_arrow() */
00295     arrow->stats.hp = arrow->stats.dam;
00296     /* Determine how many tiles the arrow will fly. */
00297     arrow->last_sp = bow->last_sp + arrow->last_sp;
00298     /* Get the used skill. */
00299     tmp_op = SK_skill(op);
00300 
00301     /* Now we do this: arrow wc = wc base from skill + (wc arrow + magic) + (wc range weapon bonus + magic) */
00302     if (tmp_op)
00303     {
00304         /* wc is in last heal */
00305         arrow->stats.wc += tmp_op->last_heal;
00306         /* Add tiles range from the skill object. */
00307         arrow->last_sp += tmp_op->last_sp;
00308     }
00309     else
00310     {
00311         arrow->stats.wc += 10;
00312     }
00313 
00314     /* Add in all our wc bonus */
00315     arrow->stats.wc = arrow_get_wc(op, bow, arrow);
00316     arrow->stats.wc_range = bow->stats.wc_range;
00317     arrow->stats.dam = arrow_get_damage(op, bow, arrow);
00318     arrow->level = SK_level(op);
00319     arrow->map = op->map;
00320     SET_MULTI_FLAG(arrow, FLAG_FLYING);
00321     SET_FLAG(arrow, FLAG_IS_MISSILE);
00322     SET_FLAG(arrow, FLAG_FLY_ON);
00323     SET_FLAG(arrow, FLAG_WALK_ON);
00324     /* Temporary buffer for "tiles to fly" */
00325     arrow->stats.grace = arrow->last_sp;
00326     /* Reflection timer */
00327     arrow->stats.maxgrace = 60 + (RANDOM() % 12);
00328     play_sound_map(op->map, CMD_SOUND_EFFECT, "bow1.ogg", op->x, op->y, 0, 0);
00329 
00330     if (insert_ob_in_map(arrow, op->map, op, 0))
00331     {
00332         move_arrow(arrow);
00333     }
00334 
00335     if (was_destroyed(left, left_tag))
00336     {
00337         esrv_del_item(CONTR(op), left_tag, left_cont);
00338     }
00339     else
00340     {
00341         esrv_send_item(op, left);
00342     }
00343 }
00344 
00349 object *fix_stopped_arrow(object *op)
00350 {
00351     object *owner;
00352 
00353     /* Small chance of breaking */
00354     if (op->last_eat && rndm_chance(op->last_eat))
00355     {
00356         remove_ob(op);
00357         return NULL;
00358     }
00359 
00360     /* Used as temp. vars to control reflection/move speed */
00361     op->stats.grace = 0;
00362     op->stats.maxgrace = 0;
00363 
00364     op->direction = 0;
00365     CLEAR_FLAG(op, FLAG_WALK_ON);
00366     CLEAR_FLAG(op, FLAG_FLY_ON);
00367     CLEAR_MULTI_FLAG(op, FLAG_FLYING);
00368     owner = get_owner(op);
00369 
00370     /* Food is a self destruct marker - that long the item will need to be destruct! */
00371     if ((!owner || owner->type != PLAYER) && op->stats.food && op->type == ARROW)
00372     {
00373         SET_FLAG(op, FLAG_IS_USED_UP);
00374         SET_FLAG(op, FLAG_NO_PICK);
00375 
00376         /* Important to neutralize the arrow! */
00377         op->type = MISC_OBJECT;
00378         op->speed = 0.1f;
00379         op->speed_left = 0.0f;
00380     }
00381     else
00382     {
00383         op->stats.wc_range = op->arch->clone.stats.wc_range;
00384         op->last_sp = op->arch->clone.last_sp;
00385         op->level = op->arch->clone.level;
00386         op->stats.food = op->arch->clone.stats.food;
00387         op->speed = 0;
00388     }
00389 
00390     update_ob_speed(op);
00391     op->stats.wc = op->last_heal;
00392     op->stats.dam = op->stats.hp;
00393 
00394     /* Reset these to zero, so that CAN_MERGE will work properly */
00395     op->last_heal = 0;
00396     op->stats.hp = 0;
00397     op->face = op->arch->clone.face;
00398 
00399     /* So that stopped arrows will be saved */
00400     clear_owner(op);
00401     update_object(op, UP_OBJ_FACE);
00402 
00403     if (owner && owner->type == PLAYER && QUERY_FLAG(op, FLAG_STAND_STILL))
00404     {
00405         pick_up(owner, op, 0);
00406         return NULL;
00407     }
00408 
00409     return op;
00410 }
00411 
00415 void move_arrow(object *op)
00416 {
00417     object *tmp = NULL, *hitter;
00418     int x, y;
00419     int flags;
00420     int was_reflected;
00421     mapstruct *m = op->map;
00422 
00423     if (op->map == NULL)
00424     {
00425         LOG(llevBug, "move_arrow(): Arrow %s had no map.\n", query_name(op, NULL));
00426         remove_ob(op);
00427         check_walk_off(op, NULL, MOVE_APPLY_VANISHED);
00428         return;
00429     }
00430 
00431     if (op->last_sp-- < 0)
00432     {
00433         stop_arrow(op);
00434         return;
00435     }
00436 
00437     x = op->x + DIRX(op);
00438     y = op->y + DIRY(op);
00439     was_reflected = 0;
00440 
00441     if (!(m = get_map_from_coord(op->map, &x, &y)))
00442     {
00443         stop_arrow(op);
00444         return;
00445     }
00446 
00447     if (!(hitter = get_owner(op)))
00448     {
00449         hitter = op;
00450     }
00451 
00452     if ((flags = GET_MAP_FLAGS(m, x, y)) & (P_IS_ALIVE | P_IS_PLAYER))
00453     {
00454         /* Search for a vulnerable object. */
00455         for (tmp = GET_MAP_OB_LAYER(m, x, y, LAYER_LIVING - 1); tmp && tmp->layer == LAYER_LIVING; tmp = tmp->above)
00456         {
00457             tmp = HEAD(tmp);
00458 
00459             /* Now, let friends fire through friends */
00460             if (!IS_LIVE(tmp) || is_friend_of(hitter, tmp) || tmp == hitter)
00461             {
00462                 continue;
00463             }
00464 
00465             if (QUERY_FLAG(tmp, FLAG_REFL_MISSILE) && rndm(0, 99) < 90 - op->level / 10)
00466             {
00467                 op->direction = absdir(op->direction + 4);
00468                 op->state = 0;
00469 
00470                 if (GET_ANIM_ID(op))
00471                 {
00472                     SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction);
00473                 }
00474 
00475                 if (wall(m, x, y))
00476                 {
00477                     /* Target is standing on a wall. Let arrow turn around before
00478                      * the wall. */
00479                     x = op->x;
00480                     y = op->y;
00481                 }
00482 
00483                 /* Skip normal movement calculations. */
00484                 was_reflected = 1;
00485                 break;
00486             }
00487             else
00488             {
00489                 /* Attack the object. */
00490                 op = hit_with_arrow(op, tmp);
00491 
00492                 if (op == NULL)
00493                 {
00494                     return;
00495                 }
00496             }
00497         }
00498     }
00499 
00500     if (!was_reflected && wall(m, x, y))
00501     {
00502         /* If the object doesn't reflect, stop the arrow from moving. */
00503         if (!QUERY_FLAG(op, FLAG_REFLECTING) || !(rndm(0, 19)))
00504         {
00505             stop_arrow(op);
00506             return;
00507         }
00508         else
00509         {
00510             /* If one of the major directions (n, s, e, w), just reverse it */
00511             if (op->direction & 1)
00512             {
00513                 op->direction = absdir(op->direction + 4);
00514             }
00515             else
00516             {
00517                 int left, right;
00518 
00519                 left = wall(op->map, op->x + freearr_x[absdir(op->direction - 1)], op->y + freearr_y[absdir(op->direction - 1)]);
00520                 right = wall(op->map, op->x + freearr_x[absdir(op->direction + 1)], op->y + freearr_y[absdir(op->direction + 1)]);
00521 
00522                 if (left == right)
00523                 {
00524                     op->direction = absdir(op->direction + 4);
00525                 }
00526                 else if (left)
00527                 {
00528                     op->direction = absdir(op->direction + 2);
00529                 }
00530                 else if (right)
00531                 {
00532                     op->direction = absdir(op->direction - 2);
00533                 }
00534             }
00535 
00536             x = op->x + DIRX(op);
00537             y = op->y + DIRY(op);
00538 
00539             /* Couldn't find a direction to move the arrow to - just stop
00540              * it from moving. */
00541             if (!(m = get_map_from_coord(op->map, &x, &y)) || wall(m, x, y))
00542             {
00543                 stop_arrow(op);
00544                 return;
00545             }
00546 
00547             /* Update object image for new facing. */
00548             if (GET_ANIM_ID(op))
00549             {
00550                 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction);
00551             }
00552         }
00553     }
00554 
00555     /* Move the arrow. */
00556     remove_ob(op);
00557 
00558     if (check_walk_off(op, NULL, MOVE_APPLY_VANISHED) == CHECK_WALK_OK)
00559     {
00560         op->x = x;
00561         op->y = y;
00562         insert_ob_in_map(op, m, op, 0);
00563     }
00564 }
00565 
00569 void stop_arrow(object *op)
00570 {
00571     play_sound_map(op->map, CMD_SOUND_EFFECT, "drop.ogg", op->x, op->y, 0, 0);
00572     CLEAR_FLAG(op, FLAG_IS_MISSILE);
00573     op = fix_stopped_arrow(op);
00574 
00575     if (op)
00576     {
00577         merge_ob(op, NULL);
00578     }
00579 }