Area.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. bool LoadEditHeightmap(C Str &name, Heightmap &hm, C Str &game_path)
  5. {
  6. ReadLock rl(WorldAreaSync);
  7. File f; if(f.readTry(name))for(ChunkReader cr(f); File *f=cr(); )
  8. {
  9. if(EqualPath(cr.name(), "Heightmap"))switch(cr.ver())
  10. {
  11. case 0: return hm.load(*f, game_path);
  12. }
  13. }
  14. return false;
  15. }
  16. void SaveEditObject(Chunks &chunks, Memc<ObjData> &objs, C Str &edit_path)
  17. {
  18. if(!objs.elms())chunks.delChunk("Object");else
  19. {
  20. File f; f.writeMem(); objs.save(f, edit_path); f.pos(0); chunks.setChunk("Object", 0, f);
  21. }
  22. }
  23. void SaveGameObject(Chunks &chunks, Memc<ObjData> &objs, C Project &proj, WorldVer &world_ver)
  24. {
  25. REPA(objs)if(!objs[i].removed && !world_ver.embedded(objs[i])) // check if there's at least one existing object
  26. {
  27. File f; f.writeMem();
  28. Memt<Game::Area::Data::AreaObj> area_objs; FREPA(objs)if(!objs[i].removed && !world_ver.embedded(objs[i]))objs[i].copyTo(area_objs.New(), proj);
  29. area_objs.sort(CompareObj); FREPAO(area_objs).save(f, proj.game_path);
  30. f.pos(0); chunks.setChunk("Object", 1, f);
  31. return;
  32. }
  33. chunks.delChunk("Object");
  34. }
  35. bool LoadEditObject(int ver, File &f, Memc<ObjData> &objs, C Str &edit_path)
  36. {
  37. switch(ver)
  38. {
  39. case 0: return objs.load(f, edit_path);
  40. }
  41. return false;
  42. }
  43. bool LoadEditObject(C Str &name, Memc<ObjData> &objs, C Str &edit_path)
  44. {
  45. ReadLock rl(WorldAreaSync);
  46. File f; if(f.readTry(name))for(ChunkReader cr(f); File *f=cr(); )
  47. {
  48. if(EqualPath(cr.name(), "Object"))return LoadEditObject(cr.ver(), *f, objs, edit_path);
  49. }
  50. return false;
  51. }
  52. /******************************************************************************/
  53. bool LoadEdit(C Str &name, Heightmap *hm, Memc<ObjData> *objs, C Str &game_path, C Str &edit_path)
  54. {
  55. ReadLock rl(WorldAreaSync);
  56. File f; if(f.readTry(name))
  57. {
  58. for(ChunkReader cr(f); File *f=cr(); )
  59. {
  60. if(hm && EqualPath(cr.name(), "Heightmap"))switch(cr.ver())
  61. {
  62. case 0: hm->load(*f, game_path); break;
  63. }else
  64. if(objs && EqualPath(cr.name(), "Object"))LoadEditObject(cr.ver(), *f, *objs, edit_path);
  65. }
  66. return true;
  67. }
  68. return false;
  69. }
  70. /******************************************************************************/
  71. bool LoadGame(C Str &name, Mesh *hm_mesh, PhysPart *hm_phys, Memc<Game::Area::Data::AreaObj> *objs, MeshGroup *obj_mesh, PhysBody *obj_phys, Memc<WaterMesh> *waters, C Str &game_path)
  72. {
  73. ReadLock rl(WorldAreaSync);
  74. File f; if(f.readTry(name))
  75. {
  76. for(ChunkReader cr(f); File *f=cr(); )
  77. {
  78. if(hm_mesh && EqualPath(cr.name(), "HeightmapMesh"))switch(cr.ver())
  79. {
  80. case 0: hm_mesh->load(*f, game_path); break;
  81. }else
  82. if(hm_phys && EqualPath(cr.name(), "HeightmapPhys"))switch(cr.ver())
  83. {
  84. case 0: hm_phys->load(*f); break;
  85. }else
  86. if(obj_mesh && EqualPath(cr.name(), "ObjectMesh"))switch(cr.ver())
  87. {
  88. case 0: obj_mesh->load(*f, game_path); break;
  89. }else
  90. if(obj_phys && EqualPath(cr.name(), "ObjectPhys"))switch(cr.ver())
  91. {
  92. case 0: obj_phys->load(*f, game_path); break;
  93. }else
  94. if(objs && EqualPath(cr.name(), "Object"))switch(cr.ver())
  95. {
  96. case 1: for(; !f->end(); )if(!objs->New().load(*f, game_path)){objs->removeLast(); break;} break;
  97. }else
  98. if(waters && EqualPath(cr.name(), "Water"))switch(cr.ver())
  99. {
  100. case 0: Load(*f, *waters, game_path); break;
  101. }
  102. }
  103. return true;
  104. }
  105. return false;
  106. }
  107. /******************************************************************************/
  108. /******************************************************************************/
  109. bool AreaVer::HasHm(C TimeStamp &removed_time, C TimeStamp &height_time) {return height_time>removed_time;}
  110. bool AreaVer::hasHm()C {return HasHm(hm_removed_time, hm_height_time);}
  111. bool AreaVer::newerHm(C AreaVer &ver)C
  112. {
  113. return hm_removed_time>ver.hm_removed_time || hm_height_time>ver.hm_height_time || hm_mtrl_time>ver.hm_mtrl_time || hm_color_time>ver.hm_color_time;
  114. }
  115. bool AreaVer::oldHm(C TimeStamp &now)C
  116. {
  117. return hm_removed_time.old(now) && hm_height_time.old(now) && hm_mtrl_time.old(now) && hm_color_time.old(now);
  118. }
  119. bool AreaVer::old(C TimeStamp &now)C
  120. {
  121. return oldHm(now) && rebuild_time.old(now);
  122. }
  123. uint AreaVer::compare(C AreaVer *dest)C // return which elements from 'this' should be sent to 'dest'
  124. {
  125. uint out=0;
  126. if(dest)
  127. {
  128. if(obj_ver!=dest->obj_ver)out|=AREA_SYNC_OBJ;
  129. if(hasHm() && dest->hasHm()) // both have heightmaps
  130. {
  131. out|=((hm_height_time>dest->hm_height_time) ? AREA_SYNC_HEIGHT : 0)
  132. |(( hm_mtrl_time>dest-> hm_mtrl_time) ? AREA_SYNC_MTRL : 0)
  133. |(( hm_color_time>dest-> hm_color_time) ? AREA_SYNC_COLOR : 0);
  134. }else
  135. if(hasHm() && !dest->hasHm() && HasHm(dest->hm_removed_time, hm_height_time)) // this has heightmap, dest doesn't have, this will change dest state
  136. {
  137. out|=AREA_SYNC_HEIGHT|AREA_SYNC_MTRL|AREA_SYNC_COLOR;
  138. }else
  139. if(!hasHm() && dest->hasHm() && !HasHm(hm_removed_time, dest->hm_height_time)) // this has no heightmap, dest has heightmap, this will change dest state
  140. {
  141. out|=AREA_SYNC_REMOVED;
  142. }// else both_are_removed;
  143. }else
  144. {
  145. if(obj_ver)out|=AREA_SYNC_OBJ; // send only if have objects
  146. if(hasHm()) // send only if have heightmap
  147. {
  148. out|=AREA_SYNC_HEIGHT|AREA_SYNC_MTRL|AREA_SYNC_COLOR;
  149. }
  150. }
  151. return out;
  152. }
  153. uint AreaVer::setHm(bool on) // make sure that 'hasHm' will return 'on'
  154. {
  155. if(on){if(!hasHm()){hm_height_time=hm_removed_time+1; return AREA_SYNC_HEIGHT ;}}
  156. else {if( hasHm()){hm_removed_time=hm_height_time+1; return AREA_SYNC_REMOVED;}}
  157. return 0;
  158. }
  159. bool AreaVer::sync(C AreaVer &src)
  160. {
  161. bool changed=false;
  162. changed|=Sync(hm_removed_time, src.hm_removed_time);
  163. changed|=Sync(hm_height_time , src.hm_height_time );
  164. changed|=Sync(hm_mtrl_time , src.hm_mtrl_time );
  165. changed|=Sync(hm_color_time , src.hm_color_time );
  166. return changed;
  167. }
  168. uint AreaVer::sync(C AreaVer &src, Heightmap &hm, C Heightmap &src_hm, uint mask)
  169. {
  170. uint changed=0;
  171. int res=src_hm.resolution(); // 'src' has newer data so use its resolution
  172. bool all=false;
  173. if(hm.is())hm.resize(res);else
  174. {
  175. hm.create(res, 0, null, false, null, null, null, null, null, null, null, null);
  176. if(src_hm.is())all=true; // if we're recreating heightmap completely then we need to set all values
  177. }
  178. if((mask&AREA_SYNC_HEIGHT) && (Sync(hm_height_time, src.hm_height_time) || all))
  179. {
  180. changed|=AREA_SYNC_HEIGHT;
  181. REPD(y, res)
  182. REPD(x, res)hm.height(x, y, src_hm.height(x, y));
  183. }
  184. if((mask&AREA_SYNC_MTRL) && (Sync(hm_mtrl_time, src.hm_mtrl_time) || all))
  185. {
  186. changed|=AREA_SYNC_MTRL;
  187. REPD(y, res)
  188. REPD(x, res)
  189. {
  190. MaterialPtr m[4]; VecB4 i;
  191. src_hm.getMaterial(x, y, m[0], m[1], m[2], m[3], i);
  192. hm.setMaterial(x, y, m[0], m[1], m[2], m[3], i);
  193. }
  194. hm.cleanMaterials();
  195. }
  196. if((mask&AREA_SYNC_COLOR) && (Sync(hm_color_time, src.hm_color_time) || all))
  197. {
  198. changed|=AREA_SYNC_COLOR;
  199. REPD(y, res)
  200. REPD(x, res)hm.color(x, y, src_hm.color(x, y));
  201. hm.cleanColor();
  202. }
  203. return changed;
  204. }
  205. uint AreaVer::undo(C AreaVer &src, Heightmap &hm, C Heightmap &src_hm)
  206. {
  207. uint changed=0;
  208. int res=src_hm.resolution(); // 'src' has newer data so use its resolution
  209. bool all=false;
  210. if(hm.is())hm.resize(res);else
  211. {
  212. hm.create(res, 0, null, false, null, null, null, null, null, null, null, null);
  213. if(src_hm.is())all=true; // if we're recreating heightmap completely then we need to set all values
  214. }
  215. if(Undo(hm_height_time, src.hm_height_time) || all)
  216. {
  217. changed|=AREA_SYNC_HEIGHT;
  218. REPD(y, res)
  219. REPD(x, res)hm.height(x, y, src_hm.height(x, y));
  220. }
  221. if(Undo(hm_mtrl_time, src.hm_mtrl_time) || all)
  222. {
  223. changed|=AREA_SYNC_MTRL;
  224. REPD(y, res)
  225. REPD(x, res)
  226. {
  227. MaterialPtr m[4]; VecB4 i;
  228. src_hm.getMaterial(x, y, m[0], m[1], m[2], m[3], i);
  229. hm.setMaterial(x, y, m[0], m[1], m[2], m[3], i);
  230. }
  231. hm.cleanMaterials();
  232. }
  233. if(Undo(hm_color_time, src.hm_color_time) || all)
  234. {
  235. changed|=AREA_SYNC_COLOR;
  236. REPD(y, res)
  237. REPD(x, res)hm.color(x, y, src_hm.color(x, y));
  238. }
  239. return changed;
  240. }
  241. bool AreaVer::save(File &f)C
  242. {
  243. f.cmpUIntV(0);
  244. f<<hm_removed_time<<hm_height_time<<hm_mtrl_time<<hm_color_time<<obj_ver;
  245. return f.ok();
  246. }
  247. bool AreaVer::load(File &f)
  248. {
  249. switch(f.decUIntV())
  250. {
  251. case 0:
  252. {
  253. f>>hm_removed_time>>hm_height_time>>hm_mtrl_time>>hm_color_time>>obj_ver;
  254. if(f.ok())return true;
  255. }break;
  256. }
  257. return false;
  258. }
  259. bool ObjVer::removed( )C {return FlagTest(flag, REMOVED);}
  260. bool ObjVer::ovrPath( )C {return FlagTest(flag, OVR_PATH);}
  261. bool ObjVer::meshVarOvr( )C {return FlagTest(flag, OVR_MESH_VARIATION);}
  262. bool ObjVer::terrain(Project &proj)C {if(flag&OVR_ACCESS)return FlagTest(flag, TERRAIN); return proj.getObjTerrain(elm_obj_id);}
  263. OBJ_PATH ObjVer::path(Project &proj)C {if(ovrPath() )return pathSelf(); return proj.getObjPath (elm_obj_id);}
  264. OBJ_PATH ObjVer::pathSelf( )C {return OBJ_PATH((flag>>PATH_SHIFT)&PATH_MASK);}
  265. bool ObjVer::set(C ObjData &obj, C VecI2 &area_xy) // return true if any member was changed
  266. {
  267. bool changed=false;
  268. uint flag =0;
  269. FlagSet(flag, REMOVED , obj.removed);
  270. FlagSet(flag, OVR_ACCESS , FlagTest(obj.params.flag, EditObject::OVR_ACCESS )); FlagSet(flag, TERRAIN, obj.params.access==OBJ_ACCESS_TERRAIN);
  271. FlagSet(flag, OVR_PATH , FlagTest(obj.params.flag, EditObject::OVR_PATH )); flag|=((obj.params.path&PATH_MASK)<<PATH_SHIFT);
  272. FlagSet(flag, OVR_MESH_VARIATION, FlagTest(obj.params.flag, EditObject::OVR_MESH_VARIATION));
  273. if(T.flag !=flag ){T.flag =flag ; changed=true;}
  274. if(T.area_xy !=area_xy ){T.area_xy =area_xy ; changed=true;}
  275. if(T.mesh_variation_id!=obj.params.mesh_variation_id){T.mesh_variation_id=obj.params.mesh_variation_id; changed=true;}
  276. UID elm_obj_id=obj.params.base.id(); if(T.elm_obj_id !=elm_obj_id ){T.elm_obj_id =elm_obj_id ; changed=true;}
  277. SmallMatrix matrix =obj.matrix; if(T.matrix !=matrix ){T.matrix =matrix ; changed=true;}
  278. return changed;
  279. }
  280. bool ObjVer::save(File &f)C
  281. {
  282. f.cmpUIntV(2);
  283. f.cmpIntV(area_xy.x).cmpIntV(area_xy.y)<<flag<<elm_obj_id<<matrix<<mesh_variation_id;
  284. return f.ok();
  285. }
  286. bool ObjVer::load(File &f)
  287. {
  288. switch(f.decUIntV())
  289. {
  290. case 2:
  291. {
  292. f.decIntV(area_xy.x).decIntV(area_xy.y)>>flag>>elm_obj_id>>matrix>>mesh_variation_id;
  293. if(f.ok())return true;
  294. }break;
  295. case 1:
  296. {
  297. area_xy.x=DecIntV(f); area_xy.y=DecIntV(f); f>>flag>>elm_obj_id>>matrix>>mesh_variation_id;
  298. if(f.ok())return true;
  299. }break;
  300. case 0:
  301. {
  302. area_xy.x=DecIntV(f); area_xy.y=DecIntV(f); f>>flag>>elm_obj_id>>matrix; mesh_variation_id=0;
  303. if(f.ok())return true;
  304. }break;
  305. }
  306. return false;
  307. }
  308. bool WaterVer::removed()C {return !areas.valid();}
  309. bool WaterVer::save(File &f)C
  310. {
  311. f.cmpUIntV(0);
  312. f<<ver<<areas;
  313. return f.ok();
  314. }
  315. bool WaterVer::load(File &f)
  316. {
  317. switch(f.decUIntV())
  318. {
  319. case 0:
  320. {
  321. f>>ver>>areas;
  322. if(f.ok())return true;
  323. }break;
  324. }
  325. return false;
  326. }
  327. bool WorldVer::CreateRebuild(byte &area_rebuild_flag, C VecI2 &area_xy, ptr) {area_rebuild_flag=0; return true;}
  328. bool WorldVer::CreateObjEmbed(RectI &obj_area , C UID &id , ptr) {obj_area.set(0, -1); return true;}
  329. WorldVer::~WorldVer() {flush();}
  330. WorldVer& WorldVer::setChanged() {changed=true; return T;}
  331. void WorldVer::operator=(C WorldVer &src)
  332. {
  333. setChanged();
  334. // don't set path, to avoid accidental resaving from a temporary variable
  335. // don't set world_id
  336. areas=src.areas;
  337. rebuild=src.rebuild;
  338. obj=src.obj;
  339. obj_embed=src.obj_embed;
  340. waypoints=src.waypoints;
  341. lakes=src.lakes;
  342. rivers=src.rivers;
  343. }
  344. bool WorldVer::embedded(C ObjData &obj) {return obj_embed.find(obj.id)!=null;}
  345. bool WorldVer::hasHm(C VecI2 &area_xy) {if(AreaVer *area_ver=areas.find(area_xy))return area_ver->hasHm(); return false;}
  346. RectI WorldVer::getTerrainAreas()
  347. {
  348. RectI rect(0, -1); MapLock ml(areas); REPA(areas)if(areas.lockedData(i).hasHm())Include(rect, areas.lockedKey (i));
  349. return rect;
  350. }
  351. RectI WorldVer::getObjAreas()
  352. {
  353. RectI rect(0, -1); MapLock ml(obj); REPA(obj)if(!obj.lockedData(i).removed())Include(rect, obj.lockedData(i).area_xy);
  354. return rect;
  355. }
  356. RectI WorldVer::getObjEmbedAreas()
  357. {
  358. RectI rect(0, -1); MapLock ml(obj_embed); REPA(obj_embed)Include(rect, obj_embed.lockedData(i));
  359. return rect;
  360. }
  361. RectI WorldVer::getLakeAreas()
  362. {
  363. RectI rect(0, -1); MapLock ml(lakes); REPA(lakes)if(!lakes.lockedData(i).removed())Include(rect, lakes.lockedData(i).areas);
  364. return rect;
  365. }
  366. RectI WorldVer::getRiverAreas()
  367. {
  368. RectI rect(0, -1); MapLock ml(rivers); REPA(rivers)if(!rivers.lockedData(i).removed())Include(rect, rivers.lockedData(i).areas);
  369. return rect;
  370. }
  371. void WorldVer::rebuildAreaNeighbor(C VecI2 &area_xy, uint flag, uint neighbor_flag_if_exists) // !! 'neighbor_flag_if_exists' is processed only if that neighbor area already exists !!
  372. {
  373. if(!IsServer) // 'rebuild' doesn't need to be stored on the server
  374. if(flag || neighbor_flag_if_exists)
  375. {
  376. MapLock ml(rebuild); // to be multi-thread safe
  377. if(flag)
  378. if(AreaVer *area_ver=areas.get(area_xy))
  379. if(byte *f=rebuild.get(area_xy)){area_ver->rebuild_time=CurTime; if((*f&flag)!=flag){*f|=flag; setChanged();}}
  380. if(neighbor_flag_if_exists)
  381. for(int y=-1; y<=1; y++)
  382. for(int x=-1; x<=1; x++)if(x || y) // skip 'area_xy'
  383. {
  384. VecI2 xy=area_xy+VecI2(x, y);
  385. if(AreaVer *area_ver=areas.find(xy))
  386. {
  387. uint nf=neighbor_flag_if_exists; if(!area_ver->hasHm())FlagDisable(nf, AREA_REBUILD_HM|AREA_REBUILD_HM_MESH|AREA_REBUILD_HM_MESH_SIMPLIFY|AREA_REBUILD_HM_PHYS); // if neighbor doesn't have heightmap then don't rebuild these elements
  388. if( nf)if(byte *f=rebuild.get(xy)){area_ver->rebuild_time=CurTime; if((*f&nf)!=nf){*f|=nf; setChanged();}}
  389. }
  390. }
  391. }
  392. }
  393. void WorldVer::rebuildArea(C VecI2 &area_xy, uint sync_flag, bool skip_mesh)
  394. {
  395. uint rebuild_flag=0, rebuild_neighbor_flag=0;
  396. if(sync_flag& AREA_SYNC_COLOR )rebuild_flag |= AREA_REBUILD_HM_MESH|AREA_REBUILD_HM_MESH_SIMPLIFY;
  397. if(sync_flag&(AREA_SYNC_HEIGHT|AREA_SYNC_MTRL|AREA_SYNC_REMOVED))rebuild_flag |=AREA_REBUILD_HM|AREA_REBUILD_HM_MESH|AREA_REBUILD_HM_MESH_SIMPLIFY|AREA_REBUILD_HM_PHYS|AREA_REBUILD_PATH; // check 'AREA_SYNC_MTRL' too because it can create holes
  398. if(sync_flag&(AREA_SYNC_HEIGHT| AREA_SYNC_REMOVED))rebuild_neighbor_flag|= AREA_REBUILD_HM_MESH|AREA_REBUILD_HM_MESH_SIMPLIFY |AREA_REBUILD_PATH; // check for 'AREA_SYNC_HEIGHT' and 'AREA_SYNC_REMOVED' because terrain shape affects ambient occlusion and vtx normals of neighbor areas, height can affect neighbor pathmesh too
  399. if(skip_mesh)FlagDisable(rebuild_flag, AREA_REBUILD_HM_MESH); // do not adjust 'rebuild_neighbor_flag' too, because the optimizations that use 'skip_mesh' will rebuild HM_MESH only for that area, but neighbor rebuild is not guaranteed
  400. rebuildAreaNeighbor(area_xy, rebuild_flag, rebuild_neighbor_flag);
  401. }
  402. void WorldVer::rebuildPaths(C RectI &area)
  403. {
  404. RectI a=area; a.extend(1); // extra 1 border is needed for paths, even if we're rebuilding a single area due to object change, because its path can span across multiple areas (if the object is bigger than area, then it's set as embedded and 'area' rect will cover those areas, so we don't need to extend it more)
  405. for(int x=a.min.x; x<=a.max.x; x++)
  406. for(int y=a.min.y; y<=a.max.y; y++)rebuildAreaNeighbor(VecI2(x, y), AREA_REBUILD_PATH);
  407. }
  408. void WorldVer::rebuildPaths(C UID &obj_id, C VecI2 &obj_area_xy)
  409. {
  410. if(C RectI *embed_rect=obj_embed.find(obj_id))rebuildPaths(*embed_rect); // if is embedded then rebuild all covered areas
  411. else rebuildPaths(obj_area_xy); // rebuild paths at object area
  412. }
  413. void WorldVer::rebuildEmbedObj(C RectI &area)
  414. {
  415. for(int x=area.min.x; x<=area.max.x; x++)
  416. for(int y=area.min.y; y<=area.max.y; y++)rebuildAreaNeighbor(VecI2(x, y), AREA_REBUILD_EMBED_OBJ);
  417. // we must also rebuild paths for that areas
  418. rebuildPaths(area);
  419. }
  420. void WorldVer::rebuildGameAreaObjs(C VecI2 &area_xy) {rebuildAreaNeighbor(area_xy, AREA_REBUILD_GAME_AREA_OBJS);}
  421. void WorldVer::rebuildWater(C RectI &area)
  422. {
  423. for(int x=area.min.x; x<=area.max.x; x++)
  424. for(int y=area.min.y; y<=area.max.y; y++)rebuildAreaNeighbor(VecI2(x, y), AREA_REBUILD_WATER);
  425. // we must also rebuild paths for that areas
  426. rebuildPaths(area);
  427. }
  428. void WorldVer::changedObj(C ObjData &obj, C VecI2 &area_xy)
  429. {
  430. MapLock ml(T.obj); // lock before modifying
  431. if(ObjVer *obj_ver=T.obj.get(obj.id))if(obj_ver->set(obj, area_xy))setChanged();
  432. }
  433. void WorldVer::changedWaypoint(C UID &waypoint_id) {waypoints(waypoint_id)-> randomize(); setChanged();}
  434. void WorldVer::changedLake(C UID & lake_id) {lakes ( lake_id)->ver.randomize(); setChanged();}
  435. void WorldVer::changedRiver(C UID & river_id) {rivers ( river_id)->ver.randomize(); setChanged();}
  436. bool WorldVer::save(File &f, bool network)
  437. {
  438. f.cmpUIntV(0);
  439. f.cmpUIntV(areas .elms()); FREPA(areas ){f<<areas .lockedKey(i); areas .lockedData(i).save(f);}
  440. if(!network){f.cmpUIntV(rebuild .elms()); FREPA(rebuild ){f<<rebuild .lockedKey(i); f<<rebuild .lockedData(i);}} // every client maintains the rebuild list locally so no need to send it over
  441. if(!network){f.cmpUIntV(obj .elms()); FREPA(obj ){f<<obj .lockedKey(i); obj .lockedData(i).save(f);}} // every client maintains the object quick list according to their local data, so no need to send it over
  442. if(!network){f.cmpUIntV(obj_embed.elms()); FREPA(obj_embed){f<<obj_embed.lockedKey(i); f<<obj_embed.lockedData(i) ;}} // every client maintains the object quick list according to their local data, so no need to send it over
  443. f.cmpUIntV(waypoints.elms()); FREPA(waypoints){f<<waypoints.lockedKey(i); f<<waypoints.lockedData(i);}
  444. f.cmpUIntV(lakes .elms()); FREPA(lakes ){f<<lakes .lockedKey(i); lakes .lockedData(i).save(f);}
  445. f.cmpUIntV(rivers .elms()); FREPA(rivers ){f<<rivers .lockedKey(i); rivers .lockedData(i).save(f);}
  446. return f.ok();
  447. }
  448. bool WorldVer::load(File &f, bool network)
  449. {
  450. changed=false;
  451. areas .del();
  452. rebuild .del();
  453. obj .del();
  454. obj_embed.del();
  455. waypoints.del();
  456. lakes .del();
  457. rivers .del();
  458. switch(f.decUIntV())
  459. {
  460. case 0:
  461. {
  462. REP(f.decUIntV()){VecI2 xy; f>>xy; if(!areas (xy)->load(f))goto error;}
  463. if(!network)REP(f.decUIntV()){VecI2 xy; f>>xy; f>>*rebuild (xy);}
  464. if(!network)REP(f.decUIntV()){UID id; f>>id; if(!obj (id)->load(f))goto error;}
  465. if(!network)REP(f.decUIntV()){UID id; f>>id; f>>*obj_embed(id);}
  466. REP(f.decUIntV()){UID id; f>>id; f>>*waypoints(id);}
  467. REP(f.decUIntV()){UID id; f>>id; if(!lakes (id)->load(f))goto error;}
  468. REP(f.decUIntV()){UID id; f>>id; if(!rivers (id)->load(f))goto error;}
  469. if(f.ok())return true;
  470. }break;
  471. }
  472. error:
  473. areas.del(); rebuild.del(); obj.del(); obj_embed.del(); waypoints.del(); lakes.del(); rivers.del();
  474. return false;
  475. }
  476. bool WorldVer::save(C Str &name)
  477. {
  478. File f; save(f.writeMem()); f.pos(0); return SafeOverwrite(f, name);
  479. }
  480. bool WorldVer::load(C Str &name) // name is "<WorldID>\\Data"
  481. {
  482. T.path=name;
  483. T.world_id=FileNameID(GetPath(name)); // ignore "Data" and grab the WorldID
  484. File f; if(f.readTry(name))return load(f);
  485. return false;
  486. }
  487. void WorldVer::flush()
  488. {
  489. if(changed)
  490. {
  491. changed=false;
  492. if(path.is())save(path);
  493. }
  494. }
  495. MiniMapVer::~MiniMapVer() {flush();}
  496. MiniMapVer& MiniMapVer::setChanged() {changed=true; return T;}
  497. void MiniMapVer::operator=(C MiniMapVer &src)
  498. {
  499. setChanged();
  500. // don't set path, to avoid accidental resaving from a temporary variable
  501. // don't set mini_map_id
  502. time=src.time;
  503. settings=src.settings;
  504. images=src.images;
  505. }
  506. bool MiniMapVer::save(File &f, bool network)C
  507. {
  508. f.cmpUIntV(1);
  509. f<<time;
  510. settings.save(f);
  511. f.cmpUIntV(images.elms()); FREPA(images)f.cmpIntV(images[i].x).cmpIntV(images[i].y);
  512. return f.ok();
  513. }
  514. bool MiniMapVer::load(File &f, bool network)
  515. {
  516. changed=false;
  517. switch(f.decUIntV())
  518. {
  519. case 1:
  520. {
  521. f>>time;
  522. if(!settings.load(f))break;
  523. images.setNum(f.decUIntV()); FREPA(images)f.decIntV(images[i].x).decIntV(images[i].y);
  524. if(f.ok())return true;
  525. }break;
  526. case 0:
  527. {
  528. f>>time;
  529. if(!settings.load(f))break;
  530. images.setNum(f.decUIntV()); FREPA(images){images[i].x=DecIntV(f); images[i].y=DecIntV(f);}
  531. if(f.ok())return true;
  532. }break;
  533. }
  534. time .zero();
  535. settings.del();
  536. images .del();
  537. return false;
  538. }
  539. bool MiniMapVer::save(C Str &name)C
  540. {
  541. File f; save(f.writeMem()); f.pos(0); return SafeOverwrite(f, name);
  542. }
  543. bool MiniMapVer::load(C Str &name)
  544. {
  545. T.path=name;
  546. T.mini_map_id=FileNameID(name);
  547. File f; if(f.readTry(name))return load(f);
  548. return false;
  549. }
  550. void MiniMapVer::flush()
  551. {
  552. if(changed)
  553. {
  554. changed=false;
  555. if(path.is())save(path);
  556. }
  557. }
  558. ObjVer::ObjVer() : area_xy(0 ), matrix(MatrixIdentity), elm_obj_id(UIDZero ), mesh_variation_id(0 ), flag(0 ) {}
  559. WaterVer::WaterVer() : areas(0, -1) {}
  560. WorldVer::WorldVer() : changed(false), world_id(UIDZero), areas(Compare ), rebuild(Compare, CreateRebuild ), obj(Compare ), obj_embed(Compare, CreateObjEmbed), waypoints(Compare ), lakes(Compare ), rivers(Compare ) {}
  561. MiniMapVer::MiniMapVer() : changed(false), mini_map_id(UIDZero) {}
  562. /******************************************************************************/