|
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 00032 static void animate_turning(object *op); 00033 static void trigger_move(object *op, int state); 00034 static objectlink *get_button_links(object *button); 00035 00040 void push_button(object *op) 00041 { 00042 object *tmp; 00043 objectlink *ol; 00044 00045 /*LOG(llevDebug, "push_button: %s (%d)\n", op->name, op->count);*/ 00046 for (ol = get_button_links(op); ol; ol = ol->next) 00047 { 00048 tmp = ol->objlink.ob; 00049 00050 if (!tmp || tmp->count != ol->id) 00051 { 00052 LOG(llevBug, "Internal error in push_button (%s).\n", op->name); 00053 continue; 00054 } 00055 00056 /* a button link object can become freed when the map is saving. As 00057 * a map is saved, objects are removed and freed, and if an object is 00058 * on top of a button, this function is eventually called. If a map 00059 * is getting moved out of memory, the status of buttons and levers 00060 * probably isn't important - it will get sorted out when the map is 00061 * re-loaded. As such, just exit this function if that is the case. */ 00062 if (!OBJECT_ACTIVE(tmp)) 00063 { 00064 LOG(llevDebug, "push_button: button link with invalid object! (%x - %x)", QUERY_FLAG(tmp, FLAG_REMOVED), tmp->count); 00065 return; 00066 } 00067 00068 /* If the criteria isn't appropriate, don't do anything. */ 00069 if (op->value && QUERY_FLAG(tmp, FLAG_CONNECT_NO_PUSH)) 00070 { 00071 continue; 00072 } 00073 00074 if (!op->value && QUERY_FLAG(tmp, FLAG_CONNECT_NO_RELEASE)) 00075 { 00076 continue; 00077 } 00078 00079 if (HAS_EVENT(tmp, EVENT_TRIGGER) && trigger_event(EVENT_TRIGGER, tmp, op, NULL, NULL, 0, 0, 0, SCRIPT_FIX_NOTHING)) 00080 { 00081 continue; 00082 } 00083 00084 switch (tmp->type) 00085 { 00086 case GATE: 00087 case PIT: 00088 tmp->value = tmp->stats.maxsp ? !op->value : op->value; 00089 tmp->speed = 0.5; 00090 update_ob_speed(tmp); 00091 break; 00092 00093 case HANDLE: 00094 SET_ANIMATION(tmp, ((NUM_ANIMATIONS(tmp) / NUM_FACINGS(tmp)) * tmp->direction) + (tmp->value = tmp->stats.maxsp ? !op->value : op->value)); 00095 update_object(tmp, UP_OBJ_FACE); 00096 break; 00097 00098 case SIGN: 00099 if (!tmp->stats.food || tmp->last_eat < tmp->stats.food) 00100 { 00101 if (tmp->title) 00102 { 00103 play_sound_map(tmp->map, CMD_SOUND_EFFECT, tmp->title, tmp->x, tmp->y, 0, 0); 00104 } 00105 00106 if (tmp->msg) 00107 { 00108 new_info_map(0, COLOR_NAVY, tmp->map, tmp->x, tmp->y, MAP_INFO_NORMAL, tmp->msg); 00109 } 00110 00111 if (tmp->stats.food) 00112 { 00113 tmp->last_eat++; 00114 } 00115 } 00116 00117 break; 00118 00119 case ALTAR: 00120 tmp->value = 1; 00121 SET_ANIMATION(tmp, ((NUM_ANIMATIONS(tmp) / NUM_FACINGS(tmp)) * tmp->direction) + tmp->value); 00122 update_object(tmp, UP_OBJ_FACE); 00123 break; 00124 00125 case BUTTON: 00126 case PEDESTAL: 00127 tmp->value = op->value; 00128 SET_ANIMATION(tmp, ((NUM_ANIMATIONS(tmp) / NUM_FACINGS(tmp)) * tmp->direction) + tmp->value); 00129 update_object(tmp, UP_OBJ_FACE); 00130 break; 00131 00132 case TIMED_GATE: 00133 tmp->speed = tmp->arch->clone.speed; 00134 /* original values */ 00135 update_ob_speed(tmp); 00136 tmp->value = 1; 00137 tmp->stats.sp = 1; 00138 tmp->stats.hp = tmp->stats.maxhp; 00139 break; 00140 00141 case FIREWALL: 00142 /* connection flag1 = on/off */ 00143 if (op->last_eat) 00144 { 00145 tmp->last_eat != 0 ? (tmp->last_eat = 0) : (tmp->last_eat = 1); 00146 } 00147 /* "normal" connection - turn wall */ 00148 else 00149 { 00150 /* next direction */ 00151 if (tmp->stats.maxsp) 00152 { 00153 tmp->direction = absdir(tmp->direction + tmp->stats.maxsp); 00154 animate_turning(tmp); 00155 } 00156 } 00157 00158 break; 00159 00160 case DIRECTOR: 00161 /* next direction */ 00162 if (tmp->stats.maxsp) 00163 { 00164 tmp->direction = absdir(tmp->direction + tmp->stats.maxsp); 00165 animate_turning(tmp); 00166 } 00167 00168 break; 00169 00170 case TELEPORTER: 00171 move_teleporter(tmp); 00172 break; 00173 00174 case CREATOR: 00175 move_creator(tmp); 00176 break; 00177 00178 case SPAWN_POINT: 00179 spawn_point(tmp); 00180 break; 00181 00182 case DUPLICATOR: 00183 move_duplicator(tmp); 00184 break; 00185 } 00186 } 00187 } 00188 00195 void update_button(object *op) 00196 { 00197 object *ab, *tmp, *head; 00198 int move, fly, tot, any_down = 0, old_value = op->value; 00199 objectlink *ol; 00200 00201 for (ol = get_button_links(op); ol; ol = ol->next) 00202 { 00203 tmp = ol->objlink.ob; 00204 00205 if (!tmp || tmp->count != ol->id) 00206 { 00207 LOG(llevDebug, "Internal error in update_button (%s).\n", op->name); 00208 continue; 00209 } 00210 00211 if (tmp->type == BUTTON) 00212 { 00213 fly = QUERY_FLAG(tmp, FLAG_FLY_ON); 00214 move = QUERY_FLAG(tmp, FLAG_WALK_ON); 00215 00216 for (ab = GET_BOTTOM_MAP_OB(tmp), tot = 0; ab != NULL; ab = ab->above) 00217 { 00218 if (ab != tmp && (fly ? (int) QUERY_FLAG(ab, FLAG_FLYING) : move)) 00219 { 00220 tot += ab->weight * (ab->nrof ? ab->nrof : 1) + ab->carrying; 00221 } 00222 } 00223 00224 tmp->value = (tot >= tmp->weight) ? 1 : 0; 00225 00226 if (tmp->value) 00227 { 00228 any_down = 1; 00229 00230 if (QUERY_FLAG(tmp, FLAG_CONNECT_RESET)) 00231 { 00232 tmp->value = 0; 00233 } 00234 } 00235 } 00236 else if (tmp->type == PEDESTAL) 00237 { 00238 tmp->value = 0; 00239 fly = QUERY_FLAG(tmp, FLAG_FLY_ON); 00240 move = QUERY_FLAG(tmp, FLAG_WALK_ON); 00241 00242 for (ab = GET_BOTTOM_MAP_OB(tmp); ab != NULL; ab = ab->above) 00243 { 00244 head = ab->head ? ab->head : ab; 00245 00246 if (ab != tmp && (fly ? (int) QUERY_FLAG(ab, FLAG_FLYING) : move) && (head->race == tmp->slaying || (!strcmp(tmp->slaying, "player") && head->type == PLAYER))) 00247 { 00248 tmp->value = 1; 00249 } 00250 } 00251 00252 if (tmp->value) 00253 { 00254 any_down = 1; 00255 00256 if (QUERY_FLAG(tmp, FLAG_CONNECT_RESET)) 00257 { 00258 tmp->value = 0; 00259 } 00260 } 00261 } 00262 } 00263 00264 /* If any other buttons were down, force this to remain down */ 00265 if (any_down) 00266 { 00267 op->value = 1; 00268 } 00269 00270 /* If this button hasn't changed, don't do anything */ 00271 if (op->value != old_value) 00272 { 00273 SET_ANIMATION(op, ((NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction) + op->value); 00274 update_object(op, UP_OBJ_FACE); 00275 push_button(op); 00276 } 00277 00278 if (op->value && QUERY_FLAG(op, FLAG_CONNECT_RESET)) 00279 { 00280 op->value = 0; 00281 SET_ANIMATION(op, ((NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction) + op->value); 00282 update_object(op, UP_OBJ_FACE); 00283 } 00284 } 00285 00290 void update_buttons(mapstruct *m) 00291 { 00292 objectlink *ol, *obp; 00293 object *ab, *tmp; 00294 int fly, move; 00295 00296 for (obp = m->buttons; obp; obp = obp->next) 00297 { 00298 for (ol = obp->objlink.link; ol; ol = ol->next) 00299 { 00300 if (!ol->objlink.ob || ol->objlink.ob->count != ol->id) 00301 { 00302 LOG(llevBug, "Internal error in update_button (%s (%dx%d):%d, connected %ld ).\n", ol->objlink.ob ? ol->objlink.ob->name : "null", ol->objlink.ob ? ol->objlink.ob->x:-1, ol->objlink.ob ? ol->objlink.ob->y:-1, ol->id, obp->value); 00303 continue; 00304 } 00305 00306 if (ol->objlink.ob->type == BUTTON || ol->objlink.ob->type == PEDESTAL) 00307 { 00308 update_button(ol->objlink.ob); 00309 } 00310 else if (ol->objlink.ob->type == CHECK_INV) 00311 { 00312 tmp = ol->objlink.ob; 00313 fly = QUERY_FLAG(tmp, FLAG_FLY_ON); 00314 move = QUERY_FLAG(tmp, FLAG_WALK_ON); 00315 00316 for (ab = GET_BOTTOM_MAP_OB(tmp); ab != NULL; ab = ab->above) 00317 { 00318 if (ab != tmp && (fly ? (int) QUERY_FLAG(ab, FLAG_FLYING) : move)) 00319 { 00320 check_inv(ab, tmp); 00321 } 00322 } 00323 } 00324 else if (ol->objlink.ob->type == TRIGGER_BUTTON || ol->objlink.ob->type == TRIGGER_PEDESTAL || ol->objlink.ob->type == TRIGGER_ALTAR) 00325 { 00326 /* check_trigger will itself sort out the numbers of 00327 * items above the trigger */ 00328 check_trigger(ol->objlink.ob, ol->objlink.ob); 00329 } 00330 } 00331 } 00332 } 00333 00337 void use_trigger(object *op) 00338 { 00339 /* Toggle value */ 00340 op->value = !op->value; 00341 push_button(op); 00342 } 00343 00347 static void animate_turning(object *op) 00348 { 00349 SET_ANIMATION(op, ((NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction) + op->state); 00350 update_object(op, UP_OBJ_FACE); 00351 } 00352 00353 /* 1 down and 0 up */ 00354 static void trigger_move(object *op, int state) 00355 { 00356 op->stats.wc = state; 00357 00358 if (state) 00359 { 00360 use_trigger(op); 00361 00362 if (op->stats.exp) 00363 { 00364 op->speed = 1.0f / op->stats.exp; 00365 update_ob_speed(op); 00366 op->speed_left = -1; 00367 } 00368 } 00369 else 00370 { 00371 use_trigger(op); 00372 op->speed = 0; 00373 update_ob_speed(op); 00374 } 00375 } 00376 00377 /* cause != NULL: something has moved on top of op 00378 * 00379 * cause == NULL: nothing has moved, we have been called from 00380 * animate_trigger(). 00381 * 00382 * TRIGGER_ALTAR: Returns 1 if 'cause' was destroyed, 0 if not. 00383 * 00384 * TRIGGER: Returns 1 if handle could be moved, 0 if not. 00385 * 00386 * TRIGGER_BUTTON, TRIGGER_PEDESTAL: Returns 0. */ 00387 int check_trigger(object *op, object *cause) 00388 { 00389 object *tmp; 00390 int push = 0, tot = 0; 00391 int in_movement = op->stats.wc || op->speed; 00392 00393 switch (op->type) 00394 { 00395 case TRIGGER_BUTTON: 00396 if (op->weight > 0) 00397 { 00398 if (cause) 00399 { 00400 for (tmp = GET_BOTTOM_MAP_OB(op); tmp; tmp = tmp->above) 00401 { 00402 if (!QUERY_FLAG(tmp, FLAG_FLYING)) 00403 { 00404 tot += tmp->weight * (tmp->nrof ? tmp->nrof : 1) + tmp->carrying; 00405 } 00406 } 00407 00408 if (tot >= op->weight) 00409 { 00410 push = 1; 00411 } 00412 00413 if (op->stats.ac == push) 00414 { 00415 return 0; 00416 } 00417 00418 op->stats.ac = push; 00419 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + push); 00420 update_object(op, UP_OBJ_FACE); 00421 00422 if (in_movement || !push) 00423 { 00424 return 0; 00425 } 00426 } 00427 00428 trigger_move(op, push); 00429 } 00430 00431 return 0; 00432 00433 case TRIGGER_PEDESTAL: 00434 if (cause) 00435 { 00436 for (tmp = GET_BOTTOM_MAP_OB(op); tmp; tmp = tmp->above) 00437 { 00438 object *head = tmp->head ? tmp->head : tmp; 00439 00440 if ((!QUERY_FLAG(head, FLAG_FLYING) || QUERY_FLAG(op, FLAG_FLY_ON)) && (head->race == op->slaying || (!strcmp(op->slaying, "player") && head->type == PLAYER))) 00441 { 00442 push = 1; 00443 break; 00444 } 00445 } 00446 00447 if (op->stats.ac == push) 00448 { 00449 return 0; 00450 } 00451 00452 op->stats.ac = push; 00453 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + push); 00454 update_object(op, UP_OBJ_FACE); 00455 00456 if (in_movement || !push) 00457 { 00458 return 0; 00459 } 00460 } 00461 00462 trigger_move(op, push); 00463 return 0; 00464 00465 case TRIGGER_ALTAR: 00466 if (cause) 00467 { 00468 if (in_movement) 00469 { 00470 return 0; 00471 } 00472 00473 if (operate_altar(op, &cause)) 00474 { 00475 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + 1); 00476 update_object(op, UP_OBJ_FACE); 00477 00478 if (op->last_sp >= 0) 00479 { 00480 trigger_move (op, 1); 00481 00482 if (op->last_sp > 0) 00483 { 00484 op->last_sp = -op->last_sp; 00485 } 00486 } 00487 else 00488 { 00489 /* for trigger altar with last_sp, the ON/OFF 00490 * status (-> +/- value) is "simulated": */ 00491 op->value = !op->value; 00492 trigger_move(op, 1); 00493 op->last_sp = -op->last_sp; 00494 op->value = !op->value; 00495 } 00496 00497 return cause == NULL; 00498 } 00499 else 00500 { 00501 return 0; 00502 } 00503 } 00504 else 00505 { 00506 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction); 00507 update_object(op, UP_OBJ_FACE); 00508 00509 /* If trigger_altar has "last_sp > 0" set on the map, 00510 * it will push the connected value only once per sacrifice. 00511 * Otherwise (default), the connected value will be 00512 * pushed twice: First by sacrifice, second by reset! -AV */ 00513 if (!op->last_sp) 00514 { 00515 trigger_move(op, 0); 00516 } 00517 else 00518 { 00519 op->stats.wc = 0; 00520 op->value = !op->value; 00521 op->speed = 0; 00522 update_ob_speed(op); 00523 } 00524 } 00525 00526 return 0; 00527 00528 case TRIGGER: 00529 if (cause) 00530 { 00531 if (in_movement) 00532 { 00533 return 0; 00534 } 00535 00536 push = 1; 00537 } 00538 00539 SET_ANIMATION(op, (NUM_ANIMATIONS(op) / NUM_FACINGS(op)) * op->direction + push); 00540 update_object(op, UP_OBJ_FACE); 00541 trigger_move(op, push); 00542 return 1; 00543 00544 default: 00545 LOG(llevDebug, "Unknown trigger type: %s (%d)\n", op->name, op->type); 00546 return 0; 00547 } 00548 } 00549 00555 void add_button_link(object *button, mapstruct *map, int connected) 00556 { 00557 objectlink *obp, *ol = get_objectlink(); 00558 00559 if (!map) 00560 { 00561 LOG(llevBug, "Tried to add button-link without map.\n"); 00562 return; 00563 } 00564 00565 button->path_attuned = connected; 00566 00567 SET_FLAG(button, FLAG_IS_LINKED); 00568 00569 ol->objlink.ob = button; 00570 ol->id = button->count; 00571 00572 for (obp = map->buttons; obp && obp->value != connected; obp = obp->next); 00573 { 00574 if (obp) 00575 { 00576 ol->next = obp->objlink.link; 00577 obp->objlink.link = ol; 00578 } 00579 else 00580 { 00581 obp = get_objectlink(); 00582 obp->value = connected; 00583 00584 obp->next = map->buttons; 00585 map->buttons = obp; 00586 obp->objlink.link = ol; 00587 } 00588 } 00589 } 00590 00594 void remove_button_link(object *op) 00595 { 00596 objectlink *obp, **olp, *ol; 00597 00598 if (op->map == NULL) 00599 { 00600 LOG(llevBug, "remove_button_link(): Object without map.\n"); 00601 return; 00602 } 00603 00604 if (!QUERY_FLAG(op, FLAG_IS_LINKED)) 00605 { 00606 LOG(llevBug, "remove_button_link(): Unlinked object.\n"); 00607 return; 00608 } 00609 00610 for (obp = op->map->buttons; obp; obp = obp->next) 00611 { 00612 for (olp = &obp->objlink.link; (ol = *olp); olp = &ol->next) 00613 { 00614 if (ol->objlink.ob == op) 00615 { 00616 *olp = ol->next; 00617 free_objectlink_simple(ol); 00618 return; 00619 } 00620 } 00621 } 00622 00623 LOG(llevBug, "remove_button_link(): Couldn't find object.\n"); 00624 CLEAR_FLAG(op, FLAG_IS_LINKED); 00625 } 00626 00631 static objectlink *get_button_links(object *button) 00632 { 00633 objectlink *obp, *ol; 00634 00635 if (!button->map) 00636 { 00637 return NULL; 00638 } 00639 00640 for (obp = button->map->buttons; obp; obp = obp->next) 00641 { 00642 for (ol = obp->objlink.link; ol; ol = ol->next) 00643 { 00644 if (ol->objlink.ob == button && ol->id == button->count) 00645 { 00646 return obp->objlink.link; 00647 } 00648 } 00649 } 00650 00651 return NULL; 00652 } 00653 00659 int get_button_value(object *button) 00660 { 00661 objectlink *obp, *ol; 00662 00663 if (!button->map) 00664 { 00665 return 0; 00666 } 00667 00668 for (obp = button->map->buttons; obp; obp = obp->next) 00669 { 00670 for (ol = obp->objlink.link; ol; ol = ol->next) 00671 { 00672 if (ol->objlink.ob == button && ol->id == button->count) 00673 { 00674 return obp->value; 00675 } 00676 } 00677 } 00678 00679 return 0; 00680 } 00681 00692 void do_mood_floor(object *op) 00693 { 00694 object *tmp; 00695 00696 for (tmp = op->above; tmp; tmp = tmp->above) 00697 { 00698 if (QUERY_FLAG(tmp, FLAG_MONSTER)) 00699 { 00700 break; 00701 } 00702 } 00703 00704 /* Doesn't affect players, and if there is a player on this space, won't also 00705 * be a monster here. */ 00706 if (!tmp || tmp->type == PLAYER) 00707 { 00708 return; 00709 } 00710 00711 switch (op->last_sp) 00712 { 00713 /* Furious -- makes all monsters mad */ 00714 case 0: 00715 if (QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE)) 00716 { 00717 CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE); 00718 } 00719 00720 if (QUERY_FLAG(tmp, FLAG_FRIENDLY)) 00721 { 00722 CLEAR_FLAG(tmp, FLAG_FRIENDLY); 00723 tmp->move_type = 0; 00724 tmp->owner = NULL; 00725 } 00726 00727 break; 00728 00729 /* Angry -- get neutral monsters mad */ 00730 case 1: 00731 if (QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE) && !QUERY_FLAG(tmp, FLAG_FRIENDLY)) 00732 { 00733 CLEAR_FLAG(tmp, FLAG_UNAGGRESSIVE); 00734 } 00735 00736 break; 00737 00738 /* Calm -- pacify unfriendly monsters */ 00739 case 2: 00740 if (!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE)) 00741 { 00742 SET_FLAG(tmp, FLAG_UNAGGRESSIVE); 00743 } 00744 00745 break; 00746 00747 /* Make all monsters fall asleep */ 00748 case 3: 00749 if (!QUERY_FLAG(tmp, FLAG_SLEEP)) 00750 { 00751 SET_FLAG(tmp, FLAG_SLEEP); 00752 } 00753 00754 break; 00755 00756 default: 00757 break; 00758 } 00759 } 00760 00772 object *check_inv_recursive(object *op, const object *trig) 00773 { 00774 object *tmp, *ret = NULL; 00775 00776 for (tmp = op->inv; tmp; tmp = tmp->below) 00777 { 00778 if (tmp->inv && !IS_SYS_INVISIBLE(tmp)) 00779 { 00780 ret = check_inv_recursive(tmp, trig); 00781 00782 if (ret) 00783 { 00784 return ret; 00785 } 00786 } 00787 else if ((trig->stats.hp && tmp->type == trig->stats.hp) || (trig->slaying && trig->stats.sp ? (tmp->slaying && trig->slaying == tmp->slaying) : (tmp->name && trig->slaying == tmp->name)) || (trig->race && trig->race == tmp->arch->name)) 00788 { 00789 return tmp; 00790 } 00791 } 00792 00793 return NULL; 00794 } 00795 00805 void check_inv(object *op, object *trig) 00806 { 00807 object *match; 00808 00809 if (op->type != PLAYER) 00810 { 00811 return; 00812 } 00813 00814 match = check_inv_recursive(op, trig); 00815 00816 if (match && trig->last_sp) 00817 { 00818 if (trig->last_heal) 00819 { 00820 decrease_ob(match); 00821 } 00822 00823 use_trigger(trig); 00824 } 00825 else if (!match && !trig->last_sp) 00826 { 00827 use_trigger(trig); 00828 } 00829 }
1.7.4