--- deliantra/server/server/move.C 2006/08/29 08:01:37 1.4 +++ deliantra/server/server/move.C 2009/11/06 12:27:06 1.33 @@ -1,39 +1,30 @@ /* - * static char *rcsid_move_c = - * "$Id: move.C,v 1.4 2006/08/29 08:01:37 root Exp $"; + * This file is part of Deliantra, the Roguelike Realtime MMORPG. + * + * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team + * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team + * Copyright (©) 1992,2007 Frank Tore Johansen + * + * Deliantra is free software: you can redistribute it and/or modify it under + * the terms of the Affero GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the Affero GNU General Public License + * and the GNU General Public License along with this program. If not, see + * . + * + * The authors can be reached via e-mail to */ -/* - CrossFire, A Multiplayer game for X-windows - - Copyright (C) 2002 Mark Wedel & Crossfire Development Team - Copyright (C) 1992 Frank Tore Johansen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - The author can be reached via e-mail to crossfire-devel@real-time.com -*/ - #include #ifndef __CEXTRACT__ -#include -#endif - -#ifdef COZY_SERVER -// use a ptotoype -extern int same_party (partylist *a, partylist *b); +# include #endif /* @@ -43,12 +34,12 @@ * This is an improvement from the previous move_ob(), which * removed and inserted objects even if they were unable to move. */ - -int move_object(object *op, int dir) { - return move_ob(op, dir, op); +int +move_object (object *op, int dir) +{ + return op->move (dir); } - /* object op is trying to move in direction dir. * originator is typically the same as op, but * can be different if originator is causing op to @@ -58,90 +49,75 @@ * move the object accordingly. This function is * very similiar to move_object. */ -int move_ob (object *op, int dir, object *originator) +int +move_ob (object *op, int dir, object *originator) { - sint16 newx = op->x+freearr_x[dir]; - sint16 newy = op->y+freearr_y[dir]; - object *tmp; - mapstruct *m; - int mflags; - - if(op==NULL) { - LOG(llevError,"Trying to move NULL.\n"); - return 0; - } + return op->move (dir, originator); +} - m = op->map; - mflags = get_map_flags(m, &m, newx, newy, &newx, &newy); +int +object::move (int dir, object *originator) +{ + sint16 newx = x + freearr_x[dir]; + sint16 newy = y + freearr_y[dir]; - /* If the space the player is trying to is out of the map, - * bail now - we know it can't work. - */ - if (mflags & P_OUT_OF_MAP) return 0; + mapxy pos (this); + pos.move (dir); + /* If the space the object is moving to is out of the map, + * bail now - we know it can't work. + */ + if (!pos.normalise ()) + return 0; - /* Is this space blocked? Players with wizpass are immune to - * this condition. - */ - if(blocked_link(op, m, newx, newy) && - !QUERY_FLAG(op, FLAG_WIZPASS)) - return 0; + /* Is this space blocked? Players with wizpass are immune to + * this condition. + */ + if (blocked_link (this, pos.m, pos.x, pos.y) && !flag [FLAG_WIZPASS]) + return 0; - /* 0.94.2 - we need to set the direction for the new animation code. - * it uses it to figure out face to use - I can't see it - * breaking anything, but it might. - */ - if(op->more != NULL && !move_ob(op->more, dir, op->more->head)) - return 0; + // check tail movability + if (more && !more->move (dir, more->head)) + return 0; - op->direction = dir; + /* we need to set the direction for the new animation code. + * it uses it to figure out face to use - I can't see it + * breaking anything, but it might. + */ + direction = dir; + + if (will_apply & 4) + check_earthwalls (this, pos.m, pos.x, pos.y); + + if (will_apply & 8) + check_doors (this, pos.m, pos.x, pos.y); + + /* If this is a tail portion, just want to tell caller that move is + * ok - the caller will deal with actual object removal/insertion + */ + if (head) + return 1; - if(op->will_apply&4) - check_earthwalls(op,m, newx,newy); - if(op->will_apply&8) - check_doors(op,m, newx,newy); - - /* 0.94.1 - I got a stack trace that showed it crash with remove_ob trying - * to remove a removed object, and this function was the culprit. A possible - * guess I have is that check_doors above ran into a trap, killing the - * monster. - * - * Unfortunately, it doesn't appear that the calling functions of move_object - * deal very well with op being killed, so all this might do is just - * migrate the problem someplace else. - */ - - if (QUERY_FLAG(op, FLAG_REMOVED)) { - LOG(llevDebug,"move_object: monster has been removed - will not process further\n"); - /* Was not successful, but don't want to try and move again */ - return 1; - } + if (pos.m != map && contr) + { + if (INVOKE_MAP (LEAVE, map, ARG_PLAYER (contr))) + return 0; - /* If this is a tail portion, just want to tell caller that move is - * ok - the caller will deal with actual object removal/insertion - */ - if(op->head) - return 1; + remove (); - remove_ob(op); + if (INVOKE_PLAYER (MAP_CHANGE, contr, ARG_MAP (pos.m), ARG_INT (pos.x), ARG_INT (pos.y))) + return 0; - /* we already have newx, newy, and m, so lets use them. - * In addition, this fixes potential crashes, because multipart object was - * on edge of map, +=x, +=y doesn't make correct coordinates. - */ - for(tmp = op; tmp != NULL; tmp = tmp->more) { - tmp->x += freearr_x[dir]; - tmp->y += freearr_y[dir]; - tmp->map = get_map_from_coord(tmp->map, &tmp->x, &tmp->y); + if (INVOKE_MAP (ENTER, pos.m, ARG_PLAYER (contr), ARG_INT (pos.x), ARG_INT (pos.y))) + return 0; } - /* insert_ob_in_map will deal with any tiling issues */ - insert_ob_in_map(op, m, originator,0); + /* insert_ob_in_map will deal with any tiling issues */ + pos.insert (this, originator); - return 1; /* this shouldn't be reached */ + return 1; } - /* * transfer_ob(): Move an object (even linked objects) to another spot * on the same map. @@ -154,29 +130,32 @@ * Return value: 1 if object was destroyed, 0 otherwise. */ -int transfer_ob (object *op, int x, int y, int randomly, object *originator) +int +transfer_ob (object *op, int x, int y, int randomly, object *originator) { - int i; - object *tmp; + int i; + object *tmp; + + if (randomly) + i = find_free_spot (op, op->map, x, y, 0, SIZEOFFREE); + else + i = find_first_free_spot (op, op->map, x, y); + + if (i == -1) + return 0; /* No free spot */ + + op = op->head_ (); + op->remove (); + + for (object *tmp = op; tmp; tmp = tmp->more) + { + tmp->x = x + freearr_x[i] + tmp->arch->x; + tmp->y = y + freearr_y[i] + tmp->arch->y; + } + + op = insert_ob_in_map (op, op->map, originator, 0); - if (randomly) - i = find_free_spot (op,op->map,x,y,0,SIZEOFFREE); - else - i = find_first_free_spot(op,op->map,x,y); - - if (i==-1) - return 0; /* No free spot */ - - if(op->head!=NULL) - op=op->head; - remove_ob(op); - for(tmp=op;tmp!=NULL;tmp=tmp->more) - tmp->x=x+freearr_x[i]+(tmp->arch==NULL?0:tmp->arch->clone.x), - tmp->y=y+freearr_y[i]+(tmp->arch==NULL?0:tmp->arch->clone.y); - - tmp = insert_ob_in_map(op,op->map,originator,0); - if (tmp) return 0; - else return 1; + return !op; } /* @@ -191,99 +170,82 @@ * be used close to each other and not have the player put to the * one of another type. */ -int teleport (object *teleporter, uint8 tele_type, object *user) +int +teleport (object *teleporter, uint8 tele_type, object *user) { - object *altern; - int i,j,k,nrofalt=0; - object *other_teleporter, *tmp; - mapstruct *m; - sint16 sx, sy; - - if(user==NULL) return 0; - if(user->head!=NULL) - user=user->head; - - /* Find all other teleporters within range. This range - * should really be setable by some object attribute instead of - * using hard coded values. - */ - for(i= -5;i<6;i++) - for(j= -5;j<6;j++) { - if(i==0&&j==0) - continue; - /* Perhaps this should be extended to support tiled maps */ - if(OUT_OF_REAL_MAP(teleporter->map,teleporter->x+i,teleporter->y+j)) - continue; - other_teleporter=get_map_ob(teleporter->map, - teleporter->x+i,teleporter->y+j); + if (!user) + return 0; - while (other_teleporter) { - if (other_teleporter->type == tele_type) break; - other_teleporter = other_teleporter->above; - } - if (other_teleporter && !(RANDOM() % ++nrofalt)) - altern = other_teleporter; - } + object *other_teleporter = 0; + int nrofalt = 0; - if(!nrofalt) { - LOG(llevError,"No alternative teleporters around!\n"); - return 0; + user = user->head_ (); + + /* Find all other teleporters within range. This range + * should really be setable by some object attribute instead of + * using hard coded values. + */ + unordered_mapwalk (teleporter, -5, -5, 5, 5) + { + mapspace &ms = m->at (nx, ny); + + for (object *tmp = ms.top; tmp; tmp = tmp->below) + if (tmp->type == tele_type) + { + if ((dx || dy) && !rndm (++nrofalt)) + other_teleporter = tmp; + + break; + } + } + + if (!nrofalt) + { + LOG (llevError, "%s: no alternative teleporters around (user %s).\n", + teleporter->debug_desc (), user->debug_desc ()); + return 0; } - other_teleporter=altern; - k=find_free_spot(user,other_teleporter->map, - other_teleporter->x,other_teleporter->y,1,9); - - /* if k==-1, unable to find a free spot. If this is shop - * mat that the player is using, find someplace to move - * the player - otherwise, player can get trapped in the shops - * that appear in random dungeons. We basically just make - * sure the space isn't no pass (eg wall), and don't care - * about is alive. - */ - if (k==-1) { - if (tele_type == SHOP_MAT && user->type == PLAYER) { - for (k=1; k<9; k++) { - if (get_map_flags(other_teleporter->map, &m, - other_teleporter->x + freearr_x[k], - other_teleporter->y + freearr_y[k], &sx,&sy) & - P_OUT_OF_MAP) continue; + int k = find_free_spot (user, other_teleporter->map, other_teleporter->x, other_teleporter->y, 1, 9); + + /* if k==-1, unable to find a free spot. If this is shop + * mat that the player is using, find someplace to move + * the player - otherwise, player can get trapped in the shops + * that appear in random dungeons. We basically just make + * sure the space isn't no pass (eg wall), and don't care + * about is alive. + */ + if (k == -1) + { + if (tele_type == SHOP_MAT && user->is_player ()) + { + for (k = 1; k < 9; k++) + { + maptile *m; + sint16 sx, sy; - if (!OB_TYPE_MOVE_BLOCK(user, GET_MAP_MOVE_BLOCK(m, sx, sy))) break; + if (get_map_flags (other_teleporter->map, &m, + other_teleporter->x + freearr_x[k], other_teleporter->y + freearr_y[k], &sx, &sy) & P_OUT_OF_MAP) + continue; + if (!OB_TYPE_MOVE_BLOCK (user, GET_MAP_MOVE_BLOCK (m, sx, sy))) + break; } - if (k==9) { - LOG(llevError,"Shop mat %s (%d, %d) is in solid rock?\n", - other_teleporter->name, other_teleporter->x, other_teleporter->y); - return 0; + + if (k == 9) + { + LOG (llevError, "Shop mat %s (%d, %d) is in solid rock?\n", + &other_teleporter->name, other_teleporter->x, other_teleporter->y); + return 0; } } - else return 0; - } - - remove_ob(user); - - /* Update location for the object */ - for(tmp=user;tmp!=NULL;tmp=tmp->more) { - tmp->x=other_teleporter->x+freearr_x[k]+ - (tmp->arch==NULL?0:tmp->arch->clone.x); - tmp->y=other_teleporter->y+freearr_y[k]+ - (tmp->arch==NULL?0:tmp->arch->clone.y); + else + return 0; } - tmp = insert_ob_in_map(user,other_teleporter->map,NULL,0); - return (tmp == NULL); -} -void recursive_roll(object *op,int dir,object *pusher) { - if(!roll_ob(op,dir,pusher)) { - new_draw_info_format(NDI_UNIQUE, 0, pusher, - "You fail to push the %s.",query_name(op)); - return; - } - (void) move_ob(pusher,dir,pusher); - new_draw_info_format(NDI_BLACK, 0, pusher, - "You move the %s.",query_name(op)); - return; + return !other_teleporter->map->insert ( + user, other_teleporter->x + freearr_x[k], other_teleporter->y + freearr_y[k] + ); } /* @@ -294,38 +256,41 @@ * This is currently only used for the boulder roll code. * Returns 1 if object does not fit, 0 if it does. */ - -int try_fit (object *op, mapstruct *m, int x, int y) +static int +try_fit (object *op, maptile *m, int x, int y) { - object *tmp, *more; - sint16 tx, ty; - int mflags; - mapstruct *m2; + object *tmp, *more; + sint16 tx, ty; + int mflags; + maptile *m2; - if (op->head) - op = op->head; + if (op->head) + op = op->head; - for (more = op; more ; more = more->more) { - tx = x + more->x - op->x; - ty = y + more->y - op->y; + for (more = op; more; more = more->more) + { + tx = x + more->x - op->x; + ty = y + more->y - op->y; - mflags = get_map_flags(m, &m2, tx, ty, &tx, &ty); + mflags = get_map_flags (m, &m2, tx, ty, &tx, &ty); - if (mflags & P_OUT_OF_MAP) - return 1; + if (mflags & P_OUT_OF_MAP) + return 1; - for (tmp = get_map_ob (m2, tx, ty); tmp; tmp=tmp->above) { - if (tmp->head == op || tmp == op) - continue; + for (tmp = GET_MAP_OB (m2, tx, ty); tmp; tmp = tmp->above) + { + if (tmp->head == op || tmp == op) + continue; - if ((QUERY_FLAG(tmp,FLAG_ALIVE) && tmp->type!=DOOR)) - return 1; + if ((QUERY_FLAG (tmp, FLAG_ALIVE) && tmp->type != DOOR)) + return 1; - if (OB_MOVE_BLOCK(op, tmp)) return 1; + if (OB_MOVE_BLOCK (op, tmp)) + return 1; } } - return 0; + return 0; } /* @@ -333,162 +298,167 @@ * it does not roll objects behind multipart objects properly. * Support for rolling multipart objects is questionable. */ +static int +roll_ob (object *op, int dir, object *pusher) +{ + sint16 x, y; + int flags; + maptile *m; + MoveType move_block; + + if (op->head) + op = op->head; + + x = op->x + freearr_x[dir]; + y = op->y + freearr_y[dir]; + + if (!QUERY_FLAG (op, FLAG_CAN_ROLL) + || (op->weight && random_roll (0, op->weight / 50000 - 1, pusher, PREFER_LOW) + > pusher->stats.Str)) + return 0; -int roll_ob(object *op,int dir, object *pusher) { - object *tmp; - sint16 x, y; - int flags; - mapstruct *m; - MoveType move_block; - - if (op->head) - op = op->head; - - x=op->x+freearr_x[dir]; - y=op->y+freearr_y[dir]; - - if(!QUERY_FLAG(op,FLAG_CAN_ROLL) || - (op->weight && - random_roll(0, op->weight/50000-1, pusher, PREFER_LOW) > pusher->stats.Str)) - return 0; - - m = op->map; - flags = get_map_flags(m, &m, x, y, &x, &y); + m = op->map; + flags = get_map_flags (m, &m, x, y, &x, &y); - if (flags & (P_OUT_OF_MAP | P_IS_ALIVE)) - return 0; + if (flags & (P_OUT_OF_MAP | P_IS_ALIVE)) + return 0; - move_block = GET_MAP_MOVE_BLOCK(m, x, y); + move_block = GET_MAP_MOVE_BLOCK (m, x, y); - /* If the target space is not blocked, no need to look at the objects on it */ - if ((op->move_type & move_block) == op->move_type) { - for (tmp=get_map_ob(m, x, y); tmp!=NULL; tmp=tmp->above) { - if (tmp->head == op) + /* If the target space is not blocked, no need to look at the objects on it */ + if ((op->move_type & move_block) == op->move_type) + { + for (object *tmp = GET_MAP_OB (m, x, y); tmp; tmp = tmp->above) + { + if (tmp->head == op) continue; - if (OB_MOVE_BLOCK(op, tmp) && !roll_ob(tmp,dir,pusher)) + + if (OB_MOVE_BLOCK (op, tmp) && !roll_ob (tmp, dir, pusher)) return 0; } } - if (try_fit (op, m, x, y)) - return 0; - remove_ob(op); - for(tmp=op; tmp!=NULL; tmp=tmp->more) - tmp->x+=freearr_x[dir],tmp->y+=freearr_y[dir]; - insert_ob_in_map(op,op->map,pusher,0); - return 1; -} + if (try_fit (op, m, x, y)) + return 0; -/* returns 1 if pushing invokes a attack, 0 when not */ -int push_ob(object *who, int dir, object *pusher) { - int str1, str2; - object *owner; - - if (who->head != NULL) - who = who->head; - owner = get_owner(who); - - /* Wake up sleeping monsters that may be pushed */ - CLEAR_FLAG(who,FLAG_SLEEP); - - /* player change place with his pets or summoned creature */ - /* TODO: allow multi arch pushing. Can't be very difficult */ - if (who->more == NULL -#ifdef COZY_SERVER - && - ( - (owner && owner->contr && pusher->contr - && same_party (owner->contr->party, pusher->contr->party)) - || owner == pusher - ) -#else - && owner == pusher -#endif - ) { - int temp; - mapstruct *m; - - remove_ob(who); - remove_ob(pusher); - temp = pusher->x; - pusher->x = who->x; - who->x = temp; - - temp = pusher->y; - pusher->y = who->y; - who->y = temp; - - m = pusher->map; - pusher->map = who->map; - who->map = m; + op->move (dir); - insert_ob_in_map (who,who->map,pusher,0); - insert_ob_in_map (pusher,pusher->map,pusher,0); - return 0; + return 1; +} + +void +recursive_roll (object *op, int dir, object *pusher) +{ + if (!roll_ob (op, dir, pusher)) + { + new_draw_info_format (NDI_UNIQUE, 0, pusher, "You fail to push the %s.", query_name (op)); + return; } + move_ob (pusher, dir, pusher); + new_draw_info_format (NDI_BLACK, 0, pusher, "You move the %s.", query_name (op)); + return; +} + +/* returns 1 if pushing invokes a attack, 0 when not */ +int +push_ob (object *who, int dir, object *pusher) +{ + int str1, str2; + object *owner; - /* We want ONLY become enemy of evil, unaggressive monster. We must RUN in them */ - /* In original we have here a unaggressive check only - that was the reason why */ - /* we so often become an enemy of friendly monsters... */ - /* funny: was they set to unaggressive 0 (= not so nice) they don't attack */ - - if(owner != pusher && pusher->type == PLAYER && who->type != PLAYER && - !QUERY_FLAG(who,FLAG_FRIENDLY)&& !QUERY_FLAG(who,FLAG_NEUTRAL)) { - if(pusher->contr->run_on) /* only when we run */ { - new_draw_info_format(NDI_UNIQUE, 0, pusher, - "You start to attack %s !!",who->name); - CLEAR_FLAG(who,FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */ - who->enemy = pusher; - return 1; - } - else + if (who->head != NULL) + who = who->head; + owner = who->owner; + + /* Wake up sleeping monsters that may be pushed */ + CLEAR_FLAG (who, FLAG_SLEEP); + + /* player change place with his pets or summoned creature */ + /* TODO: allow multi arch pushing. Can't be very difficult */ + if (who->more == NULL + && ((owner && owner->contr && pusher->contr && same_party (owner->contr->party, pusher->contr->party)) + || owner == pusher) + ) + { + int temp; + maptile *m; + + who->remove (); + pusher->remove (); + temp = pusher->x; + pusher->x = who->x; + who->x = temp; + + temp = pusher->y; + pusher->y = who->y; + who->y = temp; + + m = pusher->map; + pusher->map = who->map; + who->map = m; + + insert_ob_in_map (who, who->map, pusher, 0); + insert_ob_in_map (pusher, pusher->map, pusher, 0); + + return 0; + } + + /* We want ONLY become enemy of evil, unaggressive monster. We must RUN in them */ + /* In original we have here a unaggressive check only - that was the reason why */ + /* we so often become an enemy of friendly monsters... */ + /* funny: was they set to unaggressive 0 (= not so nice) they don't attack */ + if (owner != pusher && pusher->type == PLAYER && who->type != PLAYER && + !QUERY_FLAG (who, FLAG_FRIENDLY) && !QUERY_FLAG (who, FLAG_NEUTRAL)) + { + if (pusher->contr->run_on) /* only when we run */ { - new_draw_info_format(NDI_UNIQUE, 0, pusher, - "You avoid attacking %s .",who->name); + new_draw_info_format (NDI_UNIQUE, 0, pusher, "You start to attack %s!!", &who->name); + CLEAR_FLAG (who, FLAG_UNAGGRESSIVE); /* the sucker don't like you anymore */ + who->enemy = pusher; + return 1; } + else + new_draw_info_format (NDI_UNIQUE, 0, pusher, "You avoid attacking %s.", &who->name); } - /* now, lets test stand still. we NEVER can push stand_still monsters. */ - if(QUERY_FLAG(who,FLAG_STAND_STILL)) + /* now, lets test stand still. we NEVER can push stand_still monsters. */ + if (QUERY_FLAG (who, FLAG_STAND_STILL)) { - new_draw_info_format(NDI_UNIQUE, 0, pusher, - "You can't push %s.",who->name); - return 0; + new_draw_info_format (NDI_UNIQUE, 0, pusher, "You can't push %s.", &who->name); + return 0; } - - /* This block is basically if you are pushing friendly but - * non pet creaturs. - * It basically does a random strength comparision to - * determine if you can push someone around. Note that - * this pushes the other person away - its not a swap. - */ - - str1 = (who->stats.Str>0?who->stats.Str:who->level); - str2 = (pusher->stats.Str>0?pusher->stats.Str:pusher->level); - if(QUERY_FLAG(who,FLAG_WIZ) || - random_roll(str1, str1/2+str1*2, who, PREFER_HIGH) >= - random_roll(str2, str2/2+str2*2, pusher, PREFER_HIGH) || - !move_object(who,dir)) - { - if (who ->type == PLAYER) { - new_draw_info_format(NDI_UNIQUE, 0, who, - "%s tried to push you.",pusher->name); - } - return 0; + + /* This block is basically if you are pushing friendly but + * non pet creaturs. + * It basically does a random strength comparision to + * determine if you can push someone around. Note that + * this pushes the other person away - its not a swap. + */ + + str1 = (who->stats.Str > 0 ? who->stats.Str : who->level); + str2 = (pusher->stats.Str > 0 ? pusher->stats.Str : pusher->level); + if (QUERY_FLAG (who, FLAG_WIZ) || + random_roll (str1, str1 * 5 / 2, who, PREFER_HIGH) >= + random_roll (str2, str2 * 5 / 2, pusher, PREFER_HIGH) || !move_object (who, dir)) + { + if (who->type == PLAYER) + new_draw_info_format (NDI_UNIQUE, 0, who, "%s tried to push you.", &pusher->name); + + return 0; } - /* If we get here, the push succeeded. - * Let everyone know the status. - */ - if (who->type == PLAYER) { - new_draw_info_format(NDI_UNIQUE, 0, who, - "%s pushed you.",pusher->name); - } - if (pusher->type == PLAYER) { - new_draw_info_format(NDI_UNIQUE, 0, pusher, - "You pushed %s back.", who->name); + /* If we get here, the push succeeded. + * Let everyone know the status. + */ + if (who->type == PLAYER) + { + new_draw_info_format (NDI_UNIQUE, 0, who, "%s pushed you.", &pusher->name); } - - return 1; + if (pusher->type == PLAYER) + { + new_draw_info_format (NDI_UNIQUE, 0, pusher, "You pushed %s back.", &who->name); + } + + return 1; }