00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <cassert>
00023
00024 #include "game-server/state.hpp"
00025
00026 #include "defines.h"
00027 #include "point.h"
00028 #include "common/configuration.hpp"
00029 #include "game-server/accountconnection.hpp"
00030 #include "game-server/gamehandler.hpp"
00031 #include "game-server/inventory.hpp"
00032 #include "game-server/item.hpp"
00033 #include "game-server/itemmanager.hpp"
00034 #include "game-server/effect.hpp"
00035 #include "game-server/map.hpp"
00036 #include "game-server/mapcomposite.hpp"
00037 #include "game-server/mapmanager.hpp"
00038 #include "game-server/monster.hpp"
00039 #include "game-server/npc.hpp"
00040 #include "game-server/trade.hpp"
00041 #include "net/messageout.hpp"
00042 #include "scripting/script.hpp"
00043 #include "utils/logger.h"
00044
00045 enum
00046 {
00047 EVENT_REMOVE = 0,
00048 EVENT_INSERT,
00049 EVENT_WARP
00050 };
00051
00055 struct DelayedEvent
00056 {
00057 unsigned short type, x, y;
00058 MapComposite *map;
00059 };
00060
00061 typedef std::map< Actor *, DelayedEvent > DelayedEvents;
00062
00066 static DelayedEvents delayedEvents;
00067
00071 static void updateMap(MapComposite *map)
00072 {
00073
00074 const std::vector< Thing * > &things = map->getEverything();
00075 for (std::vector< Thing * >::const_iterator i = things.begin(),
00076 i_end = things.end(); i != i_end; ++i)
00077 {
00078 (*i)->update();
00079 }
00080
00081
00082 if (Script *s = map->getScript())
00083 {
00084 s->update();
00085 }
00086
00087
00088 for (BeingIterator i(map->getWholeMapIterator()); i; ++i)
00089 {
00090 (*i)->perform();
00091 }
00092
00093
00094 for (BeingIterator i(map->getWholeMapIterator()); i; ++i)
00095 {
00096 (*i)->move();
00097 }
00098 map->update();
00099 }
00100
00104 static void serializeLooks(Character *ch, MessageOut &msg, bool full)
00105 {
00106 const Possessions &poss = ch->getPossessions();
00107 static int const nb_slots = 4;
00108 static int const slots[nb_slots] =
00109 {
00110 EQUIP_FIGHT1_SLOT,
00111 EQUIP_HEAD_SLOT,
00112 EQUIP_TORSO_SLOT,
00113 EQUIP_LEGS_SLOT
00114 };
00115
00116
00117 int changed = (1 << nb_slots) - 1;
00118 if (!full)
00119 {
00120
00121 changed = (1 << nb_slots) - 1;
00122 }
00123
00124 int items[nb_slots];
00125
00126 int mask_full = 0, mask_diff = 0;
00127 int nb_full = 0, nb_diff = 0;
00128 for (int i = 0; i < nb_slots; ++i)
00129 {
00130 int id = poss.equipment[slots[i]];
00131 ItemClass *eq;
00132 items[i] = id && (eq = ItemManager::getItem(id)) ? eq->getSpriteID() : 0;
00133 if (changed & (1 << i))
00134 {
00135
00136 ++nb_diff;
00137 mask_diff |= 1 << i;
00138 }
00139 if (items[i])
00140 {
00141
00142
00143 ++nb_full;
00144 mask_full |= 1 << i;
00145 }
00146 }
00147
00148
00149 if (nb_full <= nb_diff) full = true;
00150
00151
00152
00153 int mask = full ? mask_full | (1 << 7) : mask_diff;
00154
00155 msg.writeByte(mask);
00156 for (int i = 0; i < nb_slots; ++i)
00157 {
00158 if (mask & (1 << i)) msg.writeShort(items[i]);
00159 }
00160 }
00161
00165 static void informPlayer(MapComposite *map, Character *p)
00166 {
00167 MessageOut moveMsg(GPMSG_BEINGS_MOVE);
00168 MessageOut damageMsg(GPMSG_BEINGS_DAMAGE);
00169 const Point &pold = p->getOldPosition(), ppos = p->getPosition();
00170 int pid = p->getPublicID(), pflags = p->getUpdateFlags();
00171 int visualRange = Configuration::getValue("visualRange", 320);
00172
00173
00174 for (BeingIterator i(map->getAroundBeingIterator(p, visualRange)); i; ++i)
00175 {
00176 Being *o = *i;
00177
00178 const Point &oold = o->getOldPosition(), opos = o->getPosition();
00179 int otype = o->getType();
00180 int oid = o->getPublicID(), oflags = o->getUpdateFlags();
00181 int flags = 0;
00182
00183
00184 bool wereInRange = pold.inRangeOf(oold, visualRange) &&
00185 !((pflags | oflags) & UPDATEFLAG_NEW_ON_MAP);
00186 bool willBeInRange = ppos.inRangeOf(opos, visualRange);
00187
00188 if (!wereInRange && !willBeInRange)
00189 {
00190
00191 continue;
00192 }
00193
00194
00195 if (wereInRange && willBeInRange)
00196 {
00197
00198 if ((oflags & UPDATEFLAG_ATTACK) && oid != pid)
00199 {
00200 MessageOut AttackMsg(GPMSG_BEING_ATTACK);
00201 AttackMsg.writeShort(oid);
00202 AttackMsg.writeByte(o->getDirection());
00203 AttackMsg.writeByte(static_cast< Being * >(o)->getAttackType());
00204 gameHandler->sendTo(p, AttackMsg);
00205 }
00206
00207
00208 if ((oflags & UPDATEFLAG_ACTIONCHANGE))
00209 {
00210 MessageOut ActionMsg(GPMSG_BEING_ACTION_CHANGE);
00211 ActionMsg.writeShort(oid);
00212 ActionMsg.writeByte(static_cast< Being * >(o)->getAction());
00213 gameHandler->sendTo(p, ActionMsg);
00214 }
00215
00216
00217 if (oflags & UPDATEFLAG_LOOKSCHANGE)
00218 {
00219 MessageOut LooksMsg(GPMSG_BEING_LOOKS_CHANGE);
00220 LooksMsg.writeShort(oid);
00221 Character * c = static_cast<Character * >(o);
00222 serializeLooks(c, LooksMsg, false);
00223 LooksMsg.writeShort(c->getHairStyle());
00224 LooksMsg.writeShort(c->getHairColor());
00225 LooksMsg.writeShort(c->getGender());
00226 gameHandler->sendTo(p, LooksMsg);
00227 }
00228
00229
00230 if (oflags & UPDATEFLAG_DIRCHANGE)
00231 {
00232 MessageOut DirMsg(GPMSG_BEING_DIR_CHANGE);
00233 DirMsg.writeShort(oid);
00234 DirMsg.writeByte(o->getDirection());
00235 gameHandler->sendTo(p, DirMsg);
00236 }
00237
00238
00239 if (o->canFight())
00240 {
00241 Being *victim = static_cast< Being * >(o);
00242 const Hits &hits = victim->getHitsTaken();
00243 for (Hits::const_iterator j = hits.begin(),
00244 j_end = hits.end(); j != j_end; ++j)
00245 {
00246 damageMsg.writeShort(oid);
00247 damageMsg.writeShort(*j);
00248 }
00249 }
00250
00251 if (oold == opos)
00252 {
00253
00254 continue;
00255 }
00256 }
00257
00258 if (!willBeInRange)
00259 {
00260
00261 MessageOut leaveMsg(GPMSG_BEING_LEAVE);
00262 leaveMsg.writeShort(oid);
00263 gameHandler->sendTo(p, leaveMsg);
00264 continue;
00265 }
00266
00267 if (!wereInRange)
00268 {
00269
00270 flags |= MOVING_POSITION;
00271 flags |= MOVING_DESTINATION;
00272
00273 MessageOut enterMsg(GPMSG_BEING_ENTER);
00274 enterMsg.writeByte(otype);
00275 enterMsg.writeShort(oid);
00276 enterMsg.writeByte(static_cast< Being *>(o)->getAction());
00277 enterMsg.writeShort(opos.x);
00278 enterMsg.writeShort(opos.y);
00279 switch (otype)
00280 {
00281 case OBJECT_CHARACTER:
00282 {
00283 Character *q = static_cast< Character * >(o);
00284 enterMsg.writeString(q->getName());
00285 enterMsg.writeByte(q->getHairStyle());
00286 enterMsg.writeByte(q->getHairColor());
00287 enterMsg.writeByte(q->getGender());
00288 serializeLooks(q, enterMsg, true);
00289 } break;
00290
00291 case OBJECT_MONSTER:
00292 {
00293 Monster *q = static_cast< Monster * >(o);
00294 enterMsg.writeShort(q->getSpecy()->getType());
00295 enterMsg.writeString(q->getName());
00296 } break;
00297
00298 case OBJECT_NPC:
00299 {
00300 NPC *q = static_cast< NPC * >(o);
00301 enterMsg.writeShort(q->getNPC());
00302 enterMsg.writeString(q->getName());
00303 } break;
00304
00305 default:
00306 assert(false);
00307 }
00308 gameHandler->sendTo(p, enterMsg);
00309 }
00310
00311
00312
00313
00314 Point odst = o->getDestination();
00315 if (opos != odst)
00316 {
00317 flags |= MOVING_POSITION;
00318 if (oflags & UPDATEFLAG_NEW_DESTINATION)
00319 {
00320 flags |= MOVING_DESTINATION;
00321 }
00322 }
00323 else
00324 {
00325
00326 flags |= MOVING_DESTINATION;
00327 }
00328
00329
00330 moveMsg.writeShort(oid);
00331 moveMsg.writeByte(flags);
00332 if (flags & MOVING_POSITION)
00333 {
00334 moveMsg.writeCoordinates(opos.x / 32, opos.y / 32);
00335 moveMsg.writeByte(o->getSpeed() / 10);
00336 }
00337 if (flags & MOVING_DESTINATION)
00338 {
00339 moveMsg.writeShort(odst.x);
00340 moveMsg.writeShort(odst.y);
00341 }
00342 }
00343
00344
00345 if (moveMsg.getLength() > 2)
00346 gameHandler->sendTo(p, moveMsg);
00347
00348 if (damageMsg.getLength() > 2)
00349 gameHandler->sendTo(p, damageMsg);
00350
00351
00352 p->sendStatus();
00353
00354
00355 for (CharacterIterator i(map->getWholeMapIterator()); i; ++i)
00356 {
00357 Character *c = *i;
00358
00359
00360 if (c == p)
00361 continue;
00362
00363
00364 if (c->getParty() == p->getParty())
00365 {
00366 int cflags = c->getUpdateFlags();
00367 if (cflags & UPDATEFLAG_HEALTHCHANGE)
00368 {
00369 MessageOut healthMsg(GPMSG_BEING_HEALTH_CHANGE);
00370 healthMsg.writeShort(c->getPublicID());
00371 healthMsg.writeShort(c->getHealth());
00372 gameHandler->sendTo(p, healthMsg);
00373 }
00374 }
00375 }
00376
00377
00378 MessageOut itemMsg(GPMSG_ITEMS);
00379 for (FixedActorIterator i(map->getAroundBeingIterator(p, visualRange)); i; ++i)
00380 {
00381 assert((*i)->getType() == OBJECT_ITEM ||
00382 (*i)->getType() == OBJECT_EFFECT);
00383
00384 Actor *o = *i;
00385 Point opos = o->getPosition();
00386 int oflags = o->getUpdateFlags();
00387 bool willBeInRange = ppos.inRangeOf(opos, visualRange);
00388 bool wereInRange = pold.inRangeOf(opos, visualRange) &&
00389 !((pflags | oflags) & UPDATEFLAG_NEW_ON_MAP);
00390
00391 if (willBeInRange ^ wereInRange)
00392 {
00393 switch (o->getType())
00394 {
00395 case OBJECT_ITEM:
00396 {
00397 Item *o = static_cast< Item * >(*i);
00398 if (oflags & UPDATEFLAG_NEW_ON_MAP)
00399 {
00400
00401
00402 MessageOut appearMsg(GPMSG_ITEM_APPEAR);
00403 appearMsg.writeShort(o->getItemClass()->getDatabaseID());
00404 appearMsg.writeShort(opos.x);
00405 appearMsg.writeShort(opos.y);
00406 gameHandler->sendTo(p, appearMsg);
00407 }
00408 else
00409 {
00410 itemMsg.writeShort(willBeInRange ? o->getItemClass()->getDatabaseID() : 0);
00411 itemMsg.writeShort(opos.x);
00412 itemMsg.writeShort(opos.y);
00413 }
00414 }
00415 break;
00416 case OBJECT_EFFECT:
00417 {
00418 Effect *o = static_cast< Effect * >(*i);
00419 o->show();
00420 if(!(oflags & UPDATEFLAG_NEW_ON_MAP))
00421 break;
00422 MessageOut effectMsg(GPMSG_CREATE_EFFECT);
00423 effectMsg.writeShort(o->getEffectId());
00424 effectMsg.writeShort(opos.x);
00425 effectMsg.writeShort(opos.y);
00426 gameHandler->sendTo(p, effectMsg);
00427 }
00428 break;
00429 default: break;
00430 }
00431 }
00432 }
00433
00434
00435 if (itemMsg.getLength() > 2)
00436 gameHandler->sendTo(p, itemMsg);
00437 }
00438
00439 #ifndef NDEBUG
00440 static bool dbgLockObjects;
00441 #endif
00442
00443 void GameState::update(int worldTime)
00444 {
00445 # ifndef NDEBUG
00446 dbgLockObjects = true;
00447 # endif
00448
00449
00450 const MapManager::Maps &maps = MapManager::getMaps();
00451 for (MapManager::Maps::const_iterator m = maps.begin(), m_end = maps.end(); m != m_end; ++m)
00452 {
00453 MapComposite *map = m->second;
00454 if (!map->isActive())
00455 {
00456 continue;
00457 }
00458
00459 updateMap(map);
00460
00461 for (CharacterIterator p(map->getWholeMapIterator()); p; ++p)
00462 {
00463 informPlayer(map, *p);
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474 }
00475
00476 for (ActorIterator i(map->getWholeMapIterator()); i; ++i)
00477 {
00478 Actor *a = *i;
00479 a->clearUpdateFlags();
00480 if (a->canFight())
00481 {
00482 static_cast< Being * >(a)->clearHitsTaken();
00483 }
00484 }
00485 }
00486
00487 # ifndef NDEBUG
00488 dbgLockObjects = false;
00489 # endif
00490
00491
00492 for (DelayedEvents::iterator i = delayedEvents.begin(),
00493 i_end = delayedEvents.end(); i != i_end; ++i)
00494 {
00495 const DelayedEvent &e = i->second;
00496 Actor *o = i->first;
00497 switch (e.type)
00498 {
00499 case EVENT_REMOVE:
00500 remove(o);
00501 if (o->getType() == OBJECT_CHARACTER)
00502 {
00503 Character *ch = static_cast< Character * >(o);
00504 ch->disconnected();
00505 gameHandler->kill(ch);
00506 }
00507 delete o;
00508 break;
00509
00510 case EVENT_INSERT:
00511 insertSafe(o);
00512 break;
00513
00514 case EVENT_WARP:
00515 assert(o->getType() == OBJECT_CHARACTER);
00516 warp(static_cast< Character * >(o), e.map, e.x, e.y);
00517 break;
00518 }
00519 }
00520 delayedEvents.clear();
00521 }
00522
00523 bool GameState::insert(Thing *ptr)
00524 {
00525 assert(!dbgLockObjects);
00526 MapComposite *map = ptr->getMap();
00527 assert(map && map->isActive());
00528
00529
00530
00531 if (!ptr->isVisible())
00532 {
00533 map->insert(ptr);
00534 ptr->inserted();
00535 return true;
00536 }
00537
00538
00539 Actor *obj = static_cast< Actor * >(ptr);
00540 Map *mp = map->getMap();
00541 Point pos = obj->getPosition();
00542 if (pos.x / 32 >= (unsigned)mp->getWidth() ||
00543 pos.y / 32 >= (unsigned)mp->getHeight())
00544 {
00545 LOG_ERROR("Tried to insert an actor at position " << pos.x << ','
00546 << pos.y << " outside map " << map->getID() << '.');
00547
00548 pos = Point(100, 100);
00549 obj->setPosition(pos);
00550 }
00551
00552 if (!map->insert(obj))
00553 {
00554
00555 LOG_ERROR("Too many actors on map " << map->getID() << '.');
00556 return false;
00557 }
00558
00559 obj->inserted();
00560
00561
00562 switch (obj->getType())
00563 {
00564 case OBJECT_ITEM:
00565 LOG_DEBUG("Item inserted: " << static_cast<Item*>(obj)->getItemClass()->getDatabaseID());
00566 break;
00567
00568 case OBJECT_NPC:
00569 LOG_DEBUG("NPC inserted: " << static_cast<NPC*>(obj)->getNPC());
00570 break;
00571
00572 case OBJECT_CHARACTER:
00573 LOG_DEBUG("Player inserted: " << static_cast<Being*>(obj)->getName());
00574 break;
00575
00576 case OBJECT_EFFECT:
00577 LOG_DEBUG("Effect inserted: " << static_cast<Effect*>(obj)->getEffectId());
00578 break;
00579
00580 case OBJECT_MONSTER:
00581 LOG_DEBUG("Monster inserted: " << static_cast<Monster*>(obj)->getSpecy()->getType());
00582 break;
00583
00584 case OBJECT_ACTOR:
00585 case OBJECT_OTHER:
00586 default:
00587 LOG_DEBUG("Thing inserted: " << obj->getType());
00588 }
00589
00590 obj->raiseUpdateFlags(UPDATEFLAG_NEW_ON_MAP);
00591 if (obj->getType() != OBJECT_CHARACTER)
00592 return true;
00593
00594
00595
00596
00597 MessageOut mapChangeMessage(GPMSG_PLAYER_MAP_CHANGE);
00598 mapChangeMessage.writeString(map->getName());
00599 mapChangeMessage.writeShort(pos.x);
00600 mapChangeMessage.writeShort(pos.y);
00601 gameHandler->sendTo(static_cast< Character * >(obj), mapChangeMessage);
00602
00603
00604 accountHandler->updateOnlineStatus(
00605 static_cast< Character * >(obj)->getDatabaseID(), true);
00606
00607 return true;
00608 }
00609
00610 bool GameState::insertSafe(Thing *ptr)
00611 {
00612 if (insert(ptr)) return true;
00613 delete ptr;
00614 return false;
00615 }
00616
00617 void GameState::remove(Thing *ptr)
00618 {
00619 assert(!dbgLockObjects);
00620 MapComposite *map = ptr->getMap();
00621 int visualRange = Configuration::getValue("visualRange", 320);
00622
00623 ptr->removed();
00624
00625
00626 switch (ptr->getType())
00627 {
00628 case OBJECT_ITEM:
00629 LOG_DEBUG("Item removed: " << static_cast<Item*>(ptr)->getItemClass()->getDatabaseID());
00630 break;
00631
00632 case OBJECT_NPC:
00633 LOG_DEBUG("NPC removed: " << static_cast<NPC*>(ptr)->getNPC());
00634 break;
00635
00636 case OBJECT_CHARACTER:
00637 LOG_DEBUG("Player removed: " << static_cast<Being*>(ptr)->getName());
00638 break;
00639
00640 case OBJECT_EFFECT:
00641 LOG_DEBUG("Effect removed: " << static_cast<Effect*>(ptr)->getEffectId());
00642 break;
00643
00644 case OBJECT_MONSTER:
00645 LOG_DEBUG("Monster removed: " << static_cast<Monster*>(ptr)->getSpecy()->getType());
00646 break;
00647
00648 case OBJECT_ACTOR:
00649 case OBJECT_OTHER:
00650 default:
00651 LOG_DEBUG("Thing removed: " << ptr->getType());
00652 }
00653
00654 if (ptr->canMove())
00655 {
00656 if (ptr->getType() == OBJECT_CHARACTER)
00657 {
00658 static_cast< Character * >(ptr)->cancelTransaction();
00659
00660
00661 accountHandler->updateOnlineStatus(
00662 static_cast< Character * >(ptr)->getDatabaseID(), false);
00663 }
00664
00665 Actor *obj = static_cast< Actor * >(ptr);
00666 MessageOut msg(GPMSG_BEING_LEAVE);
00667 msg.writeShort(obj->getPublicID());
00668 Point objectPos = obj->getPosition();
00669
00670 for (CharacterIterator p(map->getAroundActorIterator(obj, visualRange)); p; ++p)
00671 {
00672 if (*p != obj && objectPos.inRangeOf((*p)->getPosition(), visualRange))
00673 {
00674 gameHandler->sendTo(*p, msg);
00675 }
00676 }
00677 }
00678 else if (ptr->getType() == OBJECT_ITEM)
00679 {
00680 Item *obj = static_cast< Item * >(ptr);
00681 Point pos = obj->getPosition();
00682 MessageOut msg(GPMSG_ITEMS);
00683 msg.writeShort(0);
00684 msg.writeShort(pos.x);
00685 msg.writeShort(pos.y);
00686
00687 for (CharacterIterator p(map->getAroundActorIterator(obj, visualRange)); p; ++p)
00688 {
00689 if (pos.inRangeOf((*p)->getPosition(), visualRange))
00690 {
00691 gameHandler->sendTo(*p, msg);
00692 }
00693 }
00694 }
00695
00696 map->remove(ptr);
00697 }
00698
00699 void GameState::warp(Character *ptr, MapComposite *map, int x, int y)
00700 {
00701 remove(ptr);
00702 ptr->setMap(map);
00703 ptr->setPosition(Point(x, y));
00704 ptr->clearDestination();
00705
00706
00707
00708 accountHandler->sendCharacterData(ptr);
00709
00710 if (map->isActive())
00711 {
00712 if (!insert(ptr))
00713 {
00714 ptr->disconnected();
00715 gameHandler->kill(ptr);
00716 delete ptr;
00717 }
00718 }
00719 else
00720 {
00721 MessageOut msg(GAMSG_REDIRECT);
00722 msg.writeLong(ptr->getDatabaseID());
00723 accountHandler->send(msg);
00724 gameHandler->prepareServerChange(ptr);
00725 }
00726 }
00727
00731 static void enqueueEvent(Actor *ptr, const DelayedEvent &e)
00732 {
00733 std::pair< DelayedEvents::iterator, bool > p =
00734 delayedEvents.insert(std::make_pair(ptr, e));
00735
00736 if (!p.second && e.type == EVENT_REMOVE)
00737 {
00738 p.first->second.type = EVENT_REMOVE;
00739 }
00740 }
00741
00742 void GameState::enqueueInsert(Actor *ptr)
00743 {
00744 DelayedEvent e = { EVENT_INSERT, 0, 0, 0 };
00745 enqueueEvent(ptr, e);
00746 }
00747
00748 void GameState::enqueueRemove(Actor *ptr)
00749 {
00750 DelayedEvent e = { EVENT_REMOVE, 0, 0, 0 };
00751 enqueueEvent(ptr, e);
00752 }
00753
00754 void GameState::enqueueWarp(Character *ptr, MapComposite *m, int x, int y)
00755 {
00756 DelayedEvent e = { EVENT_WARP, x, y, m };
00757 enqueueEvent(ptr, e);
00758 }
00759
00760 void GameState::sayAround(Actor *obj, const std::string &text)
00761 {
00762 Point speakerPosition = obj->getPosition();
00763 int visualRange = Configuration::getValue("visualRange", 320);
00764
00765 for (CharacterIterator i(obj->getMap()->getAroundActorIterator(obj, visualRange)); i; ++i)
00766 {
00767 if (speakerPosition.inRangeOf((*i)->getPosition(), visualRange))
00768 {
00769 sayTo(*i, obj, text);
00770 }
00771 }
00772 }
00773
00774 void GameState::sayTo(Actor *destination, Actor *source, const std::string &text)
00775 {
00776 if (destination->getType() != OBJECT_CHARACTER)
00777 return;
00778
00779 MessageOut msg(GPMSG_SAY);
00780 if (source == NULL) {
00781 msg.writeShort(0);
00782 } else if (!source->canMove()) {
00783 msg.writeShort(65535);
00784 } else {
00785 msg.writeShort(static_cast< Actor * >(source)->getPublicID());
00786 }
00787 msg.writeString(text);
00788
00789 gameHandler->sendTo(static_cast< Character * >(destination), msg);
00790 }
00791
00792 void GameState::sayToAll(const std::string &text)
00793 {
00794 MessageOut msg(GPMSG_SAY);
00795
00796
00797 msg.writeShort(0);
00798 msg.writeString(text);
00799
00800
00801 gameHandler->sendToEveryone(msg);
00802 }