|
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 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 }
1.7.4