Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_time_c = 00003 * "$Id: time.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 2006 Mark Wedel & Crossfire Development Team 00010 Copyright (C) 1992 Frank Tore Johansen 00011 00012 This program is free software; you can redistribute it and/or modify 00013 it under the terms of the GNU General Public License as published by 00014 the Free Software Foundation; either version 2 of the License, or 00015 (at your option) any later version. 00016 00017 This program is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 GNU General Public License for more details. 00021 00022 You should have received a copy of the GNU General Public License 00023 along with this program; if not, write to the Free Software 00024 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 00026 The authors can be reached via e-mail at crossfire-devel@real-time.com 00027 */ 00028 00035 #include <global.h> 00036 #include <spells.h> 00037 #ifndef __CEXTRACT__ 00038 #include <sproto.h> 00039 #endif 00040 00051 void remove_door(object *op) { 00052 int i; 00053 object *tmp; 00054 00055 for (i = 1; i < 9; i += 2) 00056 if ((tmp = present(DOOR, op->map, op->x+freearr_x[i], op->y+freearr_y[i])) != NULL) { 00057 tmp->speed = 0.1; 00058 update_ob_speed(tmp); 00059 tmp->speed_left = -0.2; 00060 } 00061 00062 if (op->other_arch) { 00063 tmp = arch_to_object(op->other_arch); 00064 tmp->x = op->x; 00065 tmp->y = op->y; 00066 tmp->map = op->map; 00067 tmp->level = op->level; 00068 insert_ob_in_map(tmp, op->map, op, 0); 00069 } 00070 remove_ob(op); 00071 free_object(op); 00072 } 00073 00080 void remove_locked_door(object *op) { 00081 int i; 00082 object *tmp; 00083 00084 for (i = 1; i < 9; i += 2) { 00085 tmp = present(LOCKED_DOOR, op->map, op->x+freearr_x[i], op->y+freearr_y[i]); 00086 if (tmp && tmp->slaying == op->slaying) {/* same key both doors */ 00087 tmp->speed = 0.1; 00088 update_ob_speed(tmp); 00089 tmp->speed_left = -0.2; 00090 } 00091 } 00092 if (op->other_arch) { 00093 tmp = arch_to_object(op->other_arch); 00094 tmp->x = op->x; 00095 tmp->y = op->y; 00096 tmp->map = op->map; 00097 tmp->level = op->level; 00098 insert_ob_in_map(tmp, op->map, op, 0); 00099 } 00100 remove_ob(op); 00101 free_object(op); 00102 } 00103 00114 static void generate_monster_inv(object *gen) { 00115 int i; 00116 int nx, ny; 00117 object *op, *head = NULL; 00118 const char *code; 00119 int qty = 0; 00120 00121 /* Code below assumes the generator is on a map, as it tries 00122 * to place the monster on the map. So if the generator 00123 * isn't on a map, complain and exit. 00124 */ 00125 if (gen->map == NULL) { 00126 LOG(llevError,"Generator (%s) not on a map?\n", gen->name); 00127 return; 00128 } 00129 00130 /*First count number of objects in inv*/ 00131 for (op = gen->inv; op; op = op->below) 00132 qty++; 00133 if (!qty) { 00134 LOG(llevError,"Generator (%s) has no inventory in generate_monster_inv?\n", gen->name); 00135 return;/*No inventory*/ 00136 } 00137 qty=rndm(0,qty-1); 00138 for (op=gen->inv;qty;qty--) 00139 op=op->below; 00140 i=find_multi_free_spot_within_radius(op, gen, &nx, &ny); 00141 if (i==-1) 00142 return; 00143 head=object_create_clone(op); 00144 CLEAR_FLAG(head, FLAG_IS_A_TEMPLATE); 00145 unflag_inv(head, FLAG_IS_A_TEMPLATE); 00146 if (rndm(0, 9)) 00147 generate_artifact(head, gen->map->difficulty); 00148 code = get_ob_key_value(gen, "generator_code"); 00149 if (code) { 00150 set_ob_key_value(head, "generator_code", code, 1); 00151 } 00152 insert_ob_in_map_at(head,gen->map,gen,0,nx,ny); 00153 if (QUERY_FLAG(head, FLAG_FREED)) return; 00154 fix_multipart_object(head); 00155 if (HAS_RANDOM_ITEMS(head)) 00156 create_treasure(head->randomitems, head, GT_APPLY, 00157 gen->map->difficulty, 0); 00158 } 00159 00168 static void generate_monster_arch(object *gen) { 00169 int i; 00170 int nx, ny; 00171 object *op, *head = NULL, *prev = NULL; 00172 archetype *at = gen->other_arch; 00173 const char *code; 00174 00175 if(gen->other_arch == NULL) { 00176 LOG(llevError, "Generator without other_arch: %s\n", gen->name); 00177 return; 00178 } 00179 /* Code below assumes the generator is on a map, as it tries 00180 * to place the monster on the map. So if the generator 00181 * isn't on a map, complain and exit. 00182 */ 00183 if (gen->map == NULL) { 00184 LOG(llevError,"Generator (%s) not on a map?\n", gen->name); 00185 return; 00186 } 00187 i = find_multi_free_spot_within_radius(&at->clone, gen, &nx, &ny); 00188 if (i == -1) return; 00189 while (at != NULL) { 00190 op = arch_to_object(at); 00191 op->x = nx + at->clone.x; 00192 op->y = ny + at->clone.y; 00193 00194 if (head != NULL) 00195 op->head = head, prev->more=op; 00196 00197 if (rndm(0, 9)) generate_artifact(op, gen->map->difficulty); 00198 00199 code = get_ob_key_value(gen, "generator_code"); 00200 if (code) { 00201 set_ob_key_value(head, "generator_code", code, 1); 00202 } 00203 00204 insert_ob_in_map(op, gen->map, gen,0); 00205 if (QUERY_FLAG(op, FLAG_FREED)) return; 00206 if(HAS_RANDOM_ITEMS(op)) 00207 create_treasure(op->randomitems, op, GT_APPLY, 00208 gen->map->difficulty, 0); 00209 if(head == NULL) 00210 head = op; 00211 prev = op; 00212 at = at->more; 00213 } 00214 } 00215 00222 static void generate_monster(object *gen) { 00223 sint8 children; 00224 sint8 max_children; 00225 sint8 x, y; 00226 object *tmp; 00227 const char *code, *value; 00228 00229 if (GENERATE_SPEED(gen)&&rndm(0, GENERATE_SPEED(gen)-1)) 00230 return; 00231 00232 value = get_ob_key_value(gen, "generator_max_map"); 00233 if (value) { 00234 max_children = (sint8)strtol(value, NULL, 10); 00235 if (max_children < 1) 00236 return; 00237 code = get_ob_key_value(gen, "generator_code"); 00238 if (code) { 00239 /* Generator has a limit and has created some, 00240 * so count how many already exist 00241 */ 00242 children = 0; 00243 for (x = 0; x < MAP_WIDTH(gen->map); x++) { 00244 for (y = 0; y < MAP_HEIGHT(gen->map); y++) { 00245 for (tmp = GET_MAP_OB(gen->map, x, y); tmp != NULL; tmp = tmp->above) { 00246 value = get_ob_key_value(tmp, "generator_code"); 00247 if (value && value == code) { 00248 children++; 00249 } 00250 } 00251 } 00252 } 00253 /* and return without generating if there are already enough */ 00254 if (children >= max_children+1) 00255 return; 00256 } else { 00257 /* Generator has a limit, but hasn't created anything yet, 00258 * so no need to count, just set code and go 00259 */ 00260 value = get_ob_key_value(gen, "generator_name"); 00261 if (value) { 00262 set_ob_key_value(gen, "generator_code", value, 1); 00263 } else if (gen->name) { 00264 set_ob_key_value(gen, "generator_code", gen->name, 1); 00265 } else { 00266 set_ob_key_value(gen, "generator_code", "generator", 1); 00267 } 00268 } 00269 } /* If this has a max map generator limit */ 00270 00271 if (QUERY_FLAG(gen, FLAG_CONTENT_ON_GEN)) 00272 generate_monster_inv(gen); 00273 else 00274 generate_monster_arch(gen); 00275 } 00276 00284 static void remove_force(object *op) { 00285 if (--op->duration > 0) { 00286 check_spell_expiry(op); 00287 return; 00288 } 00289 00290 switch (op->subtype) { 00291 case FORCE_CONFUSION: 00292 if (op->env != NULL) { 00293 CLEAR_FLAG(op->env, FLAG_CONFUSED); 00294 draw_ext_info(NDI_UNIQUE, 0, op->env, 00295 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END, 00296 "You regain your senses.", NULL); 00297 } 00298 break; 00299 00300 case FORCE_TRANSFORMED_ITEM: 00301 /* The force is into the item that was created */ 00302 if (op->env != NULL && op->inv != NULL) { 00303 object *inv = op->inv; 00304 object *pl = get_player_container(op); 00305 00306 remove_ob(inv); 00307 inv->weight = (inv->nrof ? (sint32)(op->env->weight/inv->nrof) : op->env->weight); 00308 if (op->env->env) { 00309 insert_ob_in_ob(inv, op->env->env); 00310 if (pl) { 00311 char name[HUGE_BUF]; 00312 00313 query_short_name(inv, name, HUGE_BUF); 00314 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_ITEM, MSG_TYPE_ITEM_CHANGE, 00315 "Your %s recovers its original form.", 00316 "Your %s recovers its original form.", 00317 name); 00318 } 00319 } else { 00320 /* Object on map */ 00321 inv->x = op->env->x; 00322 inv->y = op->env->y; 00323 insert_ob_in_map(inv, op->env->map, NULL, 0); 00324 } 00325 inv = op->env; 00326 remove_ob(op); 00327 free_object(op); 00328 remove_ob(inv); 00329 } 00330 return; 00331 00332 default: 00333 break; 00334 } 00335 00336 if (op->env != NULL) { 00337 CLEAR_FLAG(op, FLAG_APPLIED); 00338 change_abil(op->env, op); 00339 fix_object(op->env); 00340 } 00341 remove_ob(op); 00342 free_object(op); 00343 } 00344 00351 static void animate_trigger(object *op) { 00352 if ((unsigned char)++op->stats.wc >= NUM_ANIMATIONS(op)) { 00353 op->stats.wc = 0; 00354 check_trigger(op, NULL); 00355 } else { 00356 SET_ANIMATION(op, op->stats.wc); 00357 update_object(op, UP_OBJ_FACE); 00358 } 00359 } 00360 00367 static void move_hole(object *op) { /* 1 = opening, 0 = closing */ 00368 object *next, *tmp; 00369 00370 if (op->value) { /* We're opening */ 00371 if (--op->stats.wc <= 0) { /* Opened, let's stop */ 00372 op->stats.wc = 0; 00373 op->speed = 0; 00374 update_ob_speed(op); 00375 00376 /* Hard coding this makes sense for holes I suppose */ 00377 op->move_on = MOVE_WALK; 00378 for (tmp = op->above; tmp != NULL; tmp = next) { 00379 next = tmp->above; 00380 ob_move_on(op, tmp, tmp); 00381 } 00382 } 00383 00384 op->state = op->stats.wc; 00385 animate_object(op, 0); 00386 update_object(op, UP_OBJ_FACE); 00387 return; 00388 } 00389 /* We're closing */ 00390 op->move_on = 0; 00391 00392 op->stats.wc++; 00393 if ((int)op->stats.wc >= NUM_ANIMATIONS(op)) 00394 op->stats.wc = NUM_ANIMATIONS(op)-1; 00395 00396 op->state = op->stats.wc; 00397 animate_object(op, 0); 00398 update_object(op, UP_OBJ_FACE); 00399 if ((unsigned char)op->stats.wc == (NUM_ANIMATIONS(op)-1)) { 00400 op->speed = 0; 00401 update_ob_speed(op); /* closed, let's stop */ 00402 return; 00403 } 00404 } 00405 00426 object *stop_item(object *op) { 00427 if (free_no_drop(op)) 00428 return NULL; 00429 00430 if (op->map == NULL) 00431 return op; 00432 00433 switch (op->type) { 00434 case THROWN_OBJ: { 00435 object *payload = op->inv; 00436 00437 if (payload == NULL) 00438 return NULL; 00439 remove_ob(payload); 00440 remove_ob(op); 00441 free_object(op); 00442 return payload; 00443 } 00444 00445 case ARROW: 00446 if (op->speed >= MIN_ACTIVE_SPEED) 00447 op = fix_stopped_arrow(op); 00448 return op; 00449 00450 default: 00451 return op; 00452 } 00453 } 00454 00466 void fix_stopped_item(object *op, mapstruct *map, object *originator) { 00467 if (map == NULL) 00468 return; 00469 if (QUERY_FLAG(op, FLAG_REMOVED)) 00470 insert_ob_in_map(op, map, originator, 0); 00471 else if (op->type == ARROW) 00472 merge_ob(op, NULL); /* only some arrows actually need this */ 00473 } 00474 00483 object *fix_stopped_arrow(object *op) { 00484 if (free_no_drop(op)) 00485 return NULL; 00486 00487 if (rndm(0, 99) < op->stats.food) { 00488 /* Small chance of breaking */ 00489 remove_ob(op); 00490 free_object(op); 00491 return NULL; 00492 } 00493 00494 op->direction = 0; 00495 op->move_on = 0; 00496 op->move_type = 0; 00497 op->speed = 0; 00498 update_ob_speed(op); 00499 op->stats.wc = op->stats.sp; 00500 op->stats.dam = op->stats.hp; 00501 op->attacktype = op->stats.grace; 00502 if (op->slaying != NULL) 00503 FREE_AND_CLEAR_STR(op->slaying); 00504 00505 if (op->skill != NULL) 00506 FREE_AND_CLEAR_STR(op->skill); 00507 00508 if (op->spellarg != NULL) { 00509 op->slaying = add_string(op->spellarg); 00510 free(op->spellarg); 00511 op->spellarg = NULL; 00512 } else 00513 op->slaying = NULL; 00514 00515 /* Reset these to zero, so that can_merge will work properly */ 00516 op->spellarg = NULL; 00517 op->stats.sp = 0; 00518 op->stats.hp = 0; 00519 op->stats.grace = 0; 00520 op->level = 0; 00521 op->face = op->arch->clone.face; 00522 op->owner = NULL; /* So that stopped arrows will be saved */ 00523 update_object(op, UP_OBJ_FACE); 00524 return op; 00525 } 00526 00536 int free_no_drop(object *op) { 00537 if (!QUERY_FLAG(op, FLAG_NO_DROP)) { 00538 return 0; 00539 } 00540 00541 if (!QUERY_FLAG(op, FLAG_REMOVED)) { 00542 remove_ob(op); 00543 } 00544 00545 free_object2(op, 1); 00546 return 1; 00547 } 00548 00559 static void change_object(object *op) { /* Doesn`t handle linked objs yet */ 00560 object *tmp, *env; 00561 int i, j; 00562 00563 if (op->other_arch == NULL) { 00564 LOG(llevError, "Change object (%s) without other_arch error.\n", op->name); 00565 return; 00566 } 00567 00568 /* In non-living items only change when food value is 0 */ 00569 if (!QUERY_FLAG(op, FLAG_ALIVE)) { 00570 if (op->stats.food-- > 0) 00571 return; 00572 else 00573 op->stats.food = 1; /* so 1 other_arch is made */ 00574 } 00575 env = op->env; 00576 remove_ob(op); 00577 for (i = 0; i < NROFNEWOBJS(op); i++) { 00578 tmp = arch_to_object(op->other_arch); 00579 if (op->type == LAMP) 00580 tmp->stats.food = op->stats.food-1; 00581 tmp->stats.hp = op->stats.hp; /* The only variable it keeps. */ 00582 if (env) { 00583 tmp->x = env->x, 00584 tmp->y = env->y; 00585 tmp = insert_ob_in_ob(tmp, env); 00586 } else { 00587 j = find_first_free_spot(tmp, op->map, op->x, op->y); 00588 if (j == -1) /* No free spot */ 00589 free_object(tmp); 00590 else { 00591 tmp->x = op->x+freearr_x[j], 00592 tmp->y = op->y+freearr_y[j]; 00593 insert_ob_in_map(tmp, op->map, op, 0); 00594 } 00595 } 00596 } 00597 free_object(op); 00598 } 00599 00610 void move_firewall(object *op) { 00611 object *spell; 00612 00613 if (!op->map) 00614 return; /* dm has created a firewall in his inventory */ 00615 00616 spell = op->inv; 00617 if (!spell) { 00618 LOG(llevError, "move_firewall: no spell specified (%s, %s, %d, %d)\n", op->name, op->map->name, op->x, op->y); 00619 return; 00620 } 00621 00622 cast_spell(op, op, op->stats.sp ? op->stats.sp : rndm(1, 8), spell, NULL); 00623 } 00624 00625 00639 void move_player_mover(object *op) { 00640 object *victim, *nextmover; 00641 int dir = op->stats.sp; 00642 sint16 nx, ny; 00643 mapstruct *m; 00644 00645 /* Determine direction now for random movers so we do the right thing */ 00646 if (!dir) 00647 dir = rndm(1, 8); 00648 00649 for (victim = GET_MAP_OB(op->map, op->x, op->y); victim != NULL; victim = victim->above) { 00650 if (QUERY_FLAG(victim, FLAG_ALIVE) 00651 && !QUERY_FLAG(victim, FLAG_WIZPASS) 00652 && (victim->move_type&op->move_type || !victim->move_type)) { 00653 00654 if (victim->head) 00655 victim = victim->head; 00656 00657 if (QUERY_FLAG(op, FLAG_LIFESAVE)&&op->stats.hp-- < 0) { 00658 remove_ob(op); 00659 free_object(op); 00660 return; 00661 } 00662 nx = op->x+freearr_x[dir]; 00663 ny = op->y+freearr_y[dir]; 00664 m = op->map; 00665 if (get_map_flags(m, &m, nx, ny, &nx, &ny)&P_OUT_OF_MAP) { 00666 LOG(llevError, "move_player_mover: Trying to push player off the map! map=%s (%d, %d)\n", m->path, op->x, op->y); 00667 return; 00668 } 00669 00670 if (should_director_abort(op, victim)) 00671 return; 00672 00673 for (nextmover = GET_MAP_OB(m, nx, ny); nextmover != NULL; nextmover = nextmover->above) { 00674 if (nextmover->type == PLAYERMOVER) 00675 nextmover->speed_left = -.99; 00676 if (QUERY_FLAG(nextmover, FLAG_ALIVE)) { 00677 op->speed_left = -1.1; /* wait until the next thing gets out of the way */ 00678 } 00679 } 00680 00681 if (victim->type == PLAYER) { 00682 /* only level >= 1 movers move people */ 00683 if (op->level) { 00684 /* Following is a bit of hack. We need to make sure it 00685 * is cleared, otherwise the player will get stuck in 00686 * place. This can happen if the player used a spell to 00687 * get to this space. 00688 */ 00689 victim->contr->fire_on = 0; 00690 victim->speed_left = -FABS(victim->speed); 00691 move_player(victim, dir); 00692 } else 00693 return; 00694 } else 00695 move_object(victim, dir); 00696 00697 if (!op->stats.maxsp && op->attacktype) 00698 op->stats.maxsp = 2.0; 00699 00700 if (op->attacktype) { /* flag to paralyze the player */ 00701 victim->speed_left = -FABS(op->stats.maxsp*victim->speed/op->speed); 00702 /* Not sure why, but for some chars on metalforge, they 00703 * would sometimes get -inf speed_left, and from the 00704 * description, it could only happen here, so just put 00705 * a lower sanity limit. My only guess is that the 00706 * mover has 0 speed. 00707 */ 00708 if (victim->speed_left < -5.0) 00709 victim->speed_left = -5.0; 00710 } 00711 } 00712 } 00713 } 00714 00724 int process_object(object *op) { 00725 if (QUERY_FLAG(op, FLAG_IS_A_TEMPLATE)) 00726 return 0; 00727 00728 /* Lauwenmark: Handle for plugin time event */ 00729 if (execute_event(op, EVENT_TIME, NULL, NULL, NULL, SCRIPT_FIX_NOTHING) != 0) 00730 return 0; 00731 00732 if (QUERY_FLAG(op, FLAG_MONSTER)) 00733 if (move_monster(op) || QUERY_FLAG(op, FLAG_FREED)) 00734 return 1; 00735 00736 if ((QUERY_FLAG(op, FLAG_ANIMATE) && op->anim_speed == 0) 00737 || (op->temp_animation_id && op->temp_anim_speed == 0)) { 00738 op->state++; 00739 if (op->type == PLAYER) 00740 animate_object(op, op->facing); 00741 else 00742 animate_object(op, op->direction); 00743 00744 if (QUERY_FLAG(op, FLAG_SEE_ANYWHERE)) 00745 make_sure_seen(op); 00746 } 00747 if (QUERY_FLAG(op, FLAG_CHANGING) && !op->state) { 00748 change_object(op); 00749 return 1; 00750 } 00751 if (QUERY_FLAG(op, FLAG_GENERATOR) && !QUERY_FLAG(op, FLAG_FRIENDLY)) 00752 generate_monster(op); 00753 00754 if (QUERY_FLAG(op, FLAG_IS_USED_UP) && --op->stats.food <= 0) { 00755 if (QUERY_FLAG(op, FLAG_APPLIED)) 00756 remove_force(op); 00757 else { 00758 remove_ob(op); 00759 if (QUERY_FLAG(op, FLAG_SEE_ANYWHERE)) 00760 make_sure_not_seen(op); 00761 free_object(op); 00762 } 00763 return 1; 00764 } 00765 return (ob_process(op) == METHOD_OK ? 1 : 0); 00766 } 00767 00768 void legacy_remove_force(object *op) { 00769 remove_force(op); 00770 } 00771 00772 void legacy_animate_trigger(object *op) { 00773 animate_trigger(op); 00774 } 00775 00776 void legacy_move_hole(object *op) { 00777 move_hole(op); 00778 }