Crossfire Server, Branch 1.12  R12190
time.c
Go to the documentation of this file.
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 }