Based on feedback here and some more thoughts of my own, I've updated my generic mover. I actually have it coded, though I haven't tested it. The major changes since I last discussed it are that I added the ability to move based on the type of an object, and I made it so that you can use it like a teleporter, moving to a non-adjacent square. Personally, I would like to have it be able to move to another map (loading and adjusting the timeout value of that map as appropriate), but I haven't looked into that. Since there currently is no way to move anything between maps besides players, I suspect that there would be some complications. As to my code, I have several questions: What do I need to do to make sure it doesn't move things like other movers, the floor, directors, and such? Should I have a list of object types that are by default excluded? In using these, how much do I have to worry about CPU time? If no object arrives on the same square as the mover, will the mover still have to scan through the objects on the square? Anyway, here is my code as it stands now. There are a few minor changes elsewhere (like adding the new GENERIC_MOVER type), but nothing too exciting. --PC /* move_generic_mover: this function takes a "generic mover" as an * argument, and performs the function of a mover, which is: * * a player mover finds any players that are sitting on it. It * moves them in the op->stats.ac direction. speed is how often it'll move. * If attacktype is nonzero it will paralyze the player. If lifesave is set, * it'll dissapear after grace+1 moves. If maxsp is set and attacktype is set, * it'll paralyze the victim for maxsp*his speed/op->speed * * ac: direction in which to move (0 for random or location) * hp,sp: x,y location to move to (or 0/0 if random) * speed: how often to move * lifesave: disappear after hp+1 moves if set * grace: number of moves (plus one) before disappearing, if lifesave is set. * attacktype: paralyze when moving if non-zero * maxsp: how long to paralyze (relative to victim->speed/speed) [default to 2 if zero] * level: bitmap to determine which objects move; add the values together * Selection options: (consider any matching object) * 1 -- move monsters * 2 -- move players * 4 -- move non-living objects * exp: bitmap to determine which objects to exclude; add the values together * Exclusion options: (ignore excluded objects) * 1 -- exclude flying objects * 2 -- exclude non-flying objects * 4 -- exclude identified objects * 8 -- exclude non-identified objects * 16 -- exclude unpaid objects * 32 -- exclude non-unpaid objects * slaying: only move objects that match the text string (match vs. query_name()) * maxhp: control how slaying matches are performed: * 0 -- ignore slaying field * 1 -- exact match * 2 -- fuzzy match (strncasecmp) * (negative values indicate only move non-matching objects) * value: only move if object type matches (negative to only move non-matching) * * For clarification: There are a number of ways here of controling which * objects are moved. In order for an object to be moved, it must pass the * tests for each of the selection methods. For example, if the level is * zero, nothing will be moved, even if it matches the slaying field. * */ void move_generic_mover(object *op) { object *victim, *nextmover; int direction; int newx,newy; /* * Consider every object on this square */ for(victim=get_map_ob(op->map,op->x,op->y); victim !=NULL; victim=victim->above) { /* * Is this an object that this mover acts on? */ if( /* Check type of object */ ( ( !QUERY_FLAG(victim, FLAG_ALIVE)&&(op->level&1) ) || ( QUERY_FLAG(victim, FLAG_ALIVE)&&(victim->type!=PLAYER)&&(op->level&2) ) || ( QUERY_FLAG(victim, FLAG_ALIVE)&&(victim->type==PLAYER)&&(op->level&4) ) ) && /* Check flying status */ !( (QUERY_FLAG(victim,FLAG_FLYING))&&(op->stats.exp&1) ) && !( !(QUERY_FLAG(victim,FLAG_FLYING))&&(op->stats.exp&2) ) && !( (QUERY_FLAG(victim,FLAG_IDENTIFIED))&&(op->stats.exp&4) ) && !( !(QUERY_FLAG(victim,FLAG_IDENTIFIED))&&(op->stats.exp&8) ) && !( (QUERY_FLAG(victim,FLAG_UNPAID))&&(op->stats.exp&16) ) && !( !(QUERY_FLAG(victim,FLAG_UNPAID))&&(op->stats.exp&32) ) ) { /* * Is this a type-checking mover? */ if (op->value && ( ( op->value>0 && victim->type!=op->value ) || ( op->value<0 && victim->type==-op->value ))) { continue; } /* * Is this a matching mover? */ if ( !op->slaying ) op->stats.maxhp=0; if (op->stats.maxhp) { char *vname; vname=query_name(victim); switch (op->stats.maxhp) { case 1: /* Exact match or next object */ if (strcmp(op->slaying,vname)) continue; break; case 2: /* Rough match or next object */ if (strncasecmp(op->slaying,vname,strlen(op->slaying))) continue; break; case -1: if (!strcmp(op->slaying,vname)) continue; break; case -2: if (!strncasecmp(op->slaying,vname,strlen(op->slaying))) continue; break; default: /* This is an error in the mover object definition */ /* Most likely it means the server needs to be upgraded */ break; } } /* * Have we exceeded our maximum number of moves? */ if(QUERY_FLAG(op,FLAG_LIFESAVE)&&op->stats.grace--<0) { remove_ob(op); free_object(op); return; } /* * Are we moving on top of another mover? */ if (op->stats.ac) { newx=op->x+freearr_x[op->stats.ac]; newy=op->y+freearr_y[op->stats.ac]; } else if (op->stats.hp || op->stats.sp) { newx=op->stats.hp; newy=op->stats.sp; } else { direction = op->stats.ac?op->stats.ac:(RANDOM()%8)+1; newx=op->x+freearr_x[direction]; newy=op->y+freearr_y[direction]; } if (out_of_map(op->map, newx, newy)) { LOG(llevError, "Removed illegal generic mover.\n"); remove_ob(op); free_object(op); return; } for(nextmover=get_map_ob(op->map,newx,newy); nextmover !=NULL; nextmover=nextmover->above) { if(nextmover->type == GENERICMOVER) { nextmover->speed_left=-.99;} if(QUERY_FLAG(nextmover,FLAG_ALIVE)) { op->speed_left=-1.1; /* wait until the next thing gets out of the way */ } } /* * Do the move */ if(victim->type==PLAYER) { /* Following is a bit of hack. We need to make sure it * is cleared, otherwise the player will get stuck in * place. This can happen if the player used a spell to * get to this space. */ victim->contr->fire_on=0; victim->speed_left=-FABS(victim->speed); } remove_ob(op); for(tmp=op;tmp!=NULL;tmp=tmp->more) { /* Is this for multi-square objects? */ tmp->x=newx+(tmp->arch==NULL?0:tmp->arch->clone.x); tmp->y=newy+(tmp->arch==NULL?0:tmp->arch->clone.y); } insert_ob_in_map(op,op->map,originator); /* * Paralyze */ if(!op->stats.maxsp&&op->attacktype) op->stats.maxsp=2.0; if(op->attacktype) /* flag to paralyze the player */ victim->speed_left= -FABS(op->stats.maxsp*victim->speed/op->speed); } } }