/******************************************************************************/ #include "stdafx.h" namespace EE{ namespace Net{ /******************************************************************************/ #define CC4_NOBJ CC4('N','O','B','J') #define NEIGHBOR_UPDATE_DISTANCE 3.0 // travel distance after which neighbors are updated /******************************************************************************/ static Bool AreNeighbors(C Vec &a, C Vec &b, World *world) { return Dist2(a.xz(), b.xz())<=world->_neighbor_dist2; } /******************************************************************************/ Obj::Obj() { _pos.zero(); _old_pos.zero(); _area=null; _world=null; } /******************************************************************************/ // GET / SET /******************************************************************************/ Obj& Obj::pos(C Vec &pos) { T._pos=pos; if(inGame()) { VecI2 xy=_world->worldToArea(pos); if(_area->xy()!=xy)putToArea(*_world->grid.get(xy).data()); if(Dist2(_old_pos.xz(), pos.xz())>=Sqr(NEIGHBOR_UPDATE_DISTANCE)) // after moving NEIGHBOR_UPDATE_DISTANCE meters { _old_pos=pos; updateNeighbors(); } }else { _old_pos=pos; } return T; } /******************************************************************************/ void Obj::setWorld(World *world, C Vec &pos) { leaveGame(); T._world=world; T.pos(pos); } /******************************************************************************/ // OPERATIONS /******************************************************************************/ void Obj::enterGame() { if(!inGame() && _world) { putToArea(*_world->grid.get(_world->worldToArea(pos())).data()); updateNeighbors(); } } void Obj::leaveGame() { removeFromArea (); removeNeighbors(); } /******************************************************************************/ void Obj::removeFromArea() { if(_area) { _area->_objs.exclude(this); _area=null; } } void Obj::putToArea(Area &area) { removeFromArea(); T._area=&area; T._area->_objs.include(this); } /******************************************************************************/ static void Connect(Obj &a, Obj &b) { if(&a!=&b) { REP(a.neighbors())if(&a.neighbor(i).obj()==&b)return; // already are neighbors // first add to containers a._neighbors.New()._obj=&b; b._neighbors.New()._obj=&a; // then run callbacks a.connected(b); b.connected(a); } } static void Disconnect(Obj &a, Obj &b) { if(&a!=&b)REP(a.neighbors())if(&a.neighbor(i).obj()==&b) { // first remove from containers a._neighbors.remove(i); REP(b.neighbors())if(&b.neighbor(i).obj()==&a)b._neighbors.remove(i); // then run callbacks a.disconnected(b); b.disconnected(a); break; } } static void DetectNeighbors(Cell &cell, Obj &obj) { Area &area=cell(); REP( area.objs())if(InRange(i, area.objs())) // InRange in case 'connect' will somehow modify the 'area.objs' { Obj &test=area.obj(i); if(AreNeighbors(test.pos(), obj.pos(), obj.world()))Connect(obj, test); } } void Obj::updateNeighbors() { // update existing neighbors REP(neighbors()) if(InRange(i, neighbors())) // InRange in case 'disconnect' will somehow modify the 'neighbors' container if(!AreNeighbors(pos(), neighbor(i).obj().pos(), world())) Disconnect(T, neighbor(i).obj()); // add new neighbors if(world())world()->grid.func(world()->worldToArea(Rect_C(pos().xz(), world()->neighborDist())), DetectNeighbors, T); } void Obj::removeNeighbors() { REP(neighbors()) if(InRange(i, neighbors())) // InRange in case 'disconnect' will somehow modify the 'neighbors' container Disconnect(T, neighbor(i).obj()); } /******************************************************************************/ // NETWORK /******************************************************************************/ void Obj::compress(File &f, C StrLibrary *worlds) { if(worlds)worlds->putStr(f, Worlds.name(world()));else f.putStr(Worlds.name(world())); f<getStr(f));else _world=Worlds(f.getStr()); f>>_pos; } /******************************************************************************/ // IO /******************************************************************************/ Bool Obj::save(File &f) { f.cmpUIntV(0); // version f.putAsset(Worlds.name(world())); // operate on Str instead of ID in case user uses a custom world name f<>_pos; if(f.ok())return true; }break; } return false; } /******************************************************************************/ }} /******************************************************************************/