Pol  Revision:4b29d2b
customhouses.cpp
Go to the documentation of this file.
1 
22 #include "customhouses.h"
23 
24 #include <cstddef>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <string>
28 
29 #include "../../bscript/bstruct.h"
30 #include "../../clib/cfgelem.h"
31 #include "../../clib/clib_endian.h"
32 #include "../../clib/logfacility.h"
33 #include "../../clib/passert.h"
34 #include "../../clib/stlutil.h"
35 #include "../../clib/streamsaver.h"
36 #include "../../plib/systemstate.h"
37 #include "../clidata.h"
38 #include "../core.h"
39 #include "../globals/uvars.h"
40 #include "../item/item.h"
41 #include "../item/itemdesc.h"
42 #include "../mkscrobj.h"
43 #include "../mobile/charactr.h"
44 #include "../network/cgdata.h"
45 #include "../network/client.h"
46 #include "../network/packethelper.h"
47 #include "../network/packets.h"
48 #include "../pktboth.h"
49 #include "../pktout.h"
50 #include "../pktoutid.h"
51 #include "../realms/realm.h"
52 #include "../scrdef.h"
53 #include "../scrsched.h"
54 #include "../syshook.h"
55 #include "../ufunc.h"
56 #include "../uoscrobj.h"
57 #include "../uworld.h"
58 #include "house.h"
59 #include "multidef.h"
60 
61 #ifdef USE_SYSTEM_ZLIB
62 #include <zlib.h>
63 #else
64 #include "../../../lib/zlib/zlib.h"
65 #endif
66 
67 
68 namespace Pol
69 {
70 namespace Multi
71 {
72 // bytes per tile - currently only mode 0 works, meaning we send u16 graphic, s8 x,y,z offsets
73 #define BYTES_PER_TILE 5
74 
75 // fixed z offsets for each floor
77  47, 67, 80};
78 
79 // translate z offset to floor number, use floor below passed-in z value, unless exact match
81 {
82  unsigned char i;
83  for ( i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
84  {
85  if ( z == custom_house_z_xlate_table[i] )
86  return i;
87  else if ( z < custom_house_z_xlate_table[i] )
88  return i - 1;
89  }
90  return -1;
91 }
92 
94 {
95  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
96  floor_sizes[i] = 0;
97 }
98 
99 // fixme: need a copy ctor?
100 
101 CustomHouseDesign::CustomHouseDesign( u32 _height, u32 _width, s32 xoffset, s32 yoffset )
102 {
103  InitDesign( _height, _width, xoffset, yoffset );
104 }
105 
107 
109 {
110  size_t size = sizeof( CustomHouseDesign );
111  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
112  size += Elements[i].estimatedSize();
113  return size;
114 }
115 
116 // init the geometry of the design. the design cannot exist outside the multi foundation boundary
117 // (exception: front steps)
118 void CustomHouseDesign::InitDesign( u32 _height, u32 _width, s32 xoffset, s32 yoffset )
119 {
120  height = _height;
121  width = _width;
122  xoff = xoffset;
123  yoff = yoffset;
124  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
125  {
126  floor_sizes[i] = 0;
127  Elements[i].SetWidth( _width );
128  Elements[i].SetHeight( _height );
129  Elements[i].xoff = xoffset;
130  Elements[i].yoff = yoffset;
131  }
132 }
133 
135 {
136  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
137  {
138  Elements[i] = design.Elements[i];
139  floor_sizes[i] = design.floor_sizes[i];
140  }
141  return *this;
142 }
143 
144 // Adds the element to the design
146 {
147  int floor_num = z_to_custom_house_table( elem.z );
148  if ( floor_num == -1 )
149  return;
150  Elements[floor_num].AddElement( elem );
151  floor_sizes[floor_num]++;
152 }
153 
154 // fixme: low walls not being replaced
155 // Replaces an existing object depending on the 2 tile heights
157 {
158  int floor_num = z_to_custom_house_table( elem.z );
159  if ( floor_num == -1 )
160  return;
161  char adding_height = Core::tileheight( elem.graphic );
162 
163  u32 xidx = elem.xoffset + xoff;
164  u32 yidx = elem.yoffset + yoff;
165  if ( !ValidLocation( xidx, yidx ) )
166  return;
167  HouseFloorZColumn* column = &Elements[floor_num].data.at( xidx ).at( yidx );
168  for ( HouseFloorZColumn::iterator itr = column->begin(), itrend = column->end(); itr != itrend;
169  ++itr )
170  {
171  char existing_height = Core::tileheight( itr->graphic );
172 
173  if ( ( ( existing_height == 0 ) && ( adding_height == 0 ) ) || // replace floor with floor
174  ( ( existing_height != 0 ) && ( adding_height != 0 ) ) ) // or nonfloor with nonfloor
175 
176  {
177  column->erase( itr );
178  floor_sizes[floor_num]--;
179  Add( elem );
180  return;
181  }
182  }
183 
184  // no replacement, just add
185  Add( elem );
186 }
187 
188 bool CustomHouseDesign::Erase( u32 xoffset, u32 yoffset, u8 z, int minheight )
189 {
190  int floor_num = z_to_custom_house_table( z );
191  if ( floor_num == -1 )
192  return false;
193 
194  u32 xidx = xoffset + xoff;
195  u32 yidx = yoffset + yoff;
196  if ( !ValidLocation( xidx, yidx ) )
197  return false;
198 
199  HouseFloorZColumn* column = &Elements[floor_num].data.at( xidx ).at( yidx );
200  for ( HouseFloorZColumn::iterator itr = column->begin(), itrend = column->end(); itr != itrend;
201  ++itr )
202  {
203  char t_height = Core::tileheight( itr->graphic );
204  if ( ( itr->z == z ) && ( t_height >= minheight ) )
205  {
206  column->erase( itr );
207  floor_sizes[floor_num]--;
208  return true;
209  }
210  }
211  return false;
212 }
213 
214 
215 bool CustomHouseDesign::EraseGraphicAt( u16 graphic, u32 xoffset, u32 yoffset, u8 z )
216 {
217  int floor_num = z_to_custom_house_table( z );
218  if ( floor_num == -1 )
219  return false;
220 
221  u32 xidx = xoffset + xoff;
222  u32 yidx = yoffset + yoff;
223  if ( !ValidLocation( xidx, yidx ) )
224  return false;
225  HouseFloorZColumn* column = &Elements[floor_num].data.at( xidx ).at( yidx );
226  for ( HouseFloorZColumn::iterator itr = column->begin(), itrend = column->end(); itr != itrend;
227  ++itr )
228  {
229  if ( itr->graphic == graphic )
230  {
231  column->erase( itr );
232  floor_sizes[floor_num]--;
233  return true;
234  }
235  }
236  return false;
237 }
238 
240 {
241  int floor_num = 1; // dirt always goes on floor 1 (z=7)
242 
243  if ( x + xoff == 0 || y + yoff == 0 ||
244  y + yoff ==
245  height ) // don't replace dirt at far-west and far-north sides, check height for y + 1
246  return;
247 
248  bool floor_exists = false;
249 
250  u32 xidx = x + xoff;
251  u32 yidx = y + yoff;
252  if ( !ValidLocation( xidx, yidx ) )
253  return;
254  HouseFloorZColumn* column = &Elements[floor_num].data.at( xidx ).at( yidx );
255  for ( HouseFloorZColumn::iterator itr = column->begin(), itrend = column->end(); itr != itrend;
256  ++itr )
257  {
258  if ( Core::tileheight( itr->graphic ) == 0 ) // a floor tile exists
259  {
260  floor_exists = true;
261  break;
262  }
263  }
264 
265  if ( floor_exists == false ) // add a dirt tile
266  {
268  elem.graphic = DIRTY_TILE;
269  elem.xoffset = x;
270  elem.yoffset = y;
271  elem.z = 7;
272 
273  Add( elem );
274  }
275 }
276 
278 {
279  // delete contents of all z column lists
280  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
281  {
282  for ( HouseFloor::iterator xitr = Elements[i].data.begin(), xitrend = Elements[i].data.end();
283  xitr != xitrend; ++xitr )
284  {
285  for ( HouseFloorRow::iterator yitr = xitr->begin(), yitrend = xitr->end(); yitr != yitrend;
286  ++yitr )
287  {
288  yitr->clear();
289  }
290  }
291  floor_sizes[i] = 0;
292  }
293 }
294 
295 // caller must delete, assume type 0
296 unsigned char* CustomHouseDesign::Compress( int floor, u32* uncompr_length, u32* compr_length )
297 {
298  int numtiles = floor_sizes[floor];
299  int nextindex = 0;
300  unsigned int ubuflen = numtiles * BYTES_PER_TILE;
301  unsigned long cbuflen =
302  ( ( (unsigned long)( ( (float)( ubuflen ) ) * 1.001f ) ) + 12 ); // as per zlib spec
303  unsigned char* uncompressed = new unsigned char[ubuflen];
304  memset( uncompressed, 0, ubuflen );
305  unsigned char* compressed = new unsigned char[cbuflen];
306  memset( compressed, 0, cbuflen );
307 
308  int i = 0;
309  for ( HouseFloor::const_iterator xitr = Elements[floor].data.begin(),
310  xitrend = Elements[floor].data.end();
311  xitr != xitrend; ++xitr )
312  {
313  i = 0;
314  for ( HouseFloorRow::const_iterator yitr = xitr->begin(), yitrend = xitr->end();
315  yitr != yitrend; ++yitr )
316  {
317  for ( HouseFloorZColumn::const_iterator zitr = yitr->begin(), zitrend = yitr->end();
318  zitr != zitrend; ++zitr, ++i )
319  {
320  // assume type 0, I don't know how to deal with stair pieces at odd Z values for mode 1,
321  // and mode 2 is just wacky. (position implied from list position, needs alot of null tiles
322  // to make that work (but they compress very well)
323  if ( i < numtiles )
324  {
325  uncompressed[nextindex++] = ( u8 )( ( zitr->graphic >> 8 ) & 0xFF );
326  uncompressed[nextindex++] = ( u8 )( zitr->graphic & 0xFF );
327 
328  uncompressed[nextindex++] = (u8)zitr->xoffset;
329  uncompressed[nextindex++] = (u8)zitr->yoffset;
330  uncompressed[nextindex++] = (u8)zitr->z;
331  }
332  }
333  }
334  }
335  *uncompr_length = nextindex;
336 
337  int ret = compress2( compressed, &cbuflen, uncompressed, nextindex, Z_DEFAULT_COMPRESSION );
338  if ( ret == Z_OK )
339  {
340  delete[] uncompressed;
341  *compr_length = cbuflen;
342  return compressed;
343  }
344  else
345  {
346  *uncompr_length = 0;
347  *compr_length = 0;
348  delete[] compressed;
349  if ( ubuflen > 0 )
350  delete[] uncompressed;
351  return nullptr;
352  }
353 }
354 
356 {
357  int total = 0;
358  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
359  total += floor_sizes[i];
360  return total == 0 ? true : false;
361 }
362 
363 unsigned int CustomHouseDesign::TotalSize() const
364 {
365  unsigned int size = 0;
366  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
367  {
368  size += floor_sizes[i];
369  }
370  return size;
371 }
372 
373 unsigned char CustomHouseDesign::NumUsedPlanes() const
374 {
375  unsigned char size = 0;
376  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
377  {
378  if ( floor_sizes[i] > 0 )
379  size++;
380  }
381  return size;
382 }
383 
384 // used to add the base foundation to initialize a design, and multi stairs. not tested with
385 // anything else.
386 // I guess you could add a boat or something inside a house, but deleting would be impossible.
387 // Deleting stairs is handled explicitly (CustomHouseDesign::DeleteStairs).
389 {
390  const MultiDef* multidef = MultiDefByMultiID( multiid );
391  if ( multidef == nullptr )
392  {
393  ERROR_PRINT << "Trying to add Multi to customhouse, multiid 0x" << fmt::hexu( multiid )
394  << " multi definition doesn't exist!\n";
395  return;
396  }
397 
398  for ( MultiDef::Components::const_iterator itr = multidef->components.begin(),
399  end = multidef->components.end();
400  itr != end; ++itr )
401  {
402  const MULTI_ELEM* m_elem = itr->second;
403  if ( ( ( m_elem->objtype ) & Plib::systemstate.config.max_tile_id ) ==
404  1 ) // don't add the invisible multi tile
405  continue;
406  // cout << "0x" << hex << m_elem->graphic
407  // << " 0x" << hex << m_elem->flags
408  // << ":" << dec << m_elem->x << "," << m_elem->y << "," << m_elem->z << endl;
409  CUSTOM_HOUSE_ELEMENT ch_elem;
410  ch_elem.graphic = m_elem->objtype;
411  ch_elem.xoffset = m_elem->x + x;
412  ch_elem.yoffset = m_elem->y + y;
413  ch_elem.z = static_cast<u8>( m_elem->z + z );
414  Add( ch_elem );
415  }
416 }
417 
418 void CustomHouseDesign::readProperties( Clib::ConfigElem& elem, const std::string& prefix )
419 {
420  std::string line;
421 
422  while ( elem.remove_prop( prefix.c_str(), &line ) )
423  {
424  ISTRINGSTREAM is( line );
425  u16 graphic;
426  s32 x, y;
427  u16 z;
428  is >> graphic;
429  is >> x;
430  is >> y;
431  is >> z;
432 
433  CUSTOM_HOUSE_ELEMENT _elem;
434  _elem.graphic = graphic;
435  _elem.xoffset = x;
436  _elem.yoffset = y;
437  _elem.z = (u8)z;
438  Add( _elem );
439  }
440 }
441 
442 void CustomHouseDesign::printProperties( Clib::StreamWriter& sw, const std::string& prefix ) const
443 {
444  if ( !IsEmpty() )
445  {
446  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
447  {
448  for ( HouseFloor::const_iterator xitr = Elements[i].data.begin(),
449  xitrend = Elements[i].data.end();
450  xitr != xitrend; ++xitr )
451  {
452  for ( HouseFloorRow::const_iterator yitr = xitr->begin(), yitrend = xitr->end();
453  yitr != yitrend; ++yitr )
454  {
455  for ( HouseFloorZColumn::const_iterator zitr = yitr->begin(), zitrend = yitr->end();
456  zitr != zitrend; ++zitr )
457  {
458  sw() << "\t" << prefix << "\t " << zitr->graphic << " " << zitr->xoffset << " "
459  << zitr->yoffset << " " << (u16)zitr->z << '\n';
460  }
461  }
462  }
463  }
464  }
465 }
466 
467 // for testing, prints each floor's x,y,z rows
468 void CustomHouseDesign::testprint( std::ostream& os ) const
469 {
470  if ( !IsEmpty() )
471  {
472  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
473  {
474  int x = 0, y = 0;
475  for ( HouseFloor::const_iterator xitr = Elements[i].data.begin(),
476  xitrend = Elements[i].data.end();
477  xitr != xitrend; ++xitr, x++ )
478  {
479  os << "X: " << x << std::endl;
480  for ( HouseFloorRow::const_iterator yitr = xitr->begin(), yitrend = xitr->end();
481  yitr != yitrend; ++yitr, y++ )
482  {
483  os << "\tY: " << y << std::endl;
484  for ( HouseFloorZColumn::const_iterator zitr = yitr->begin(), zitrend = yitr->end();
485  zitr != zitrend; ++zitr )
486  {
487  os << "\t\t" << zitr->graphic << " " << zitr->xoffset << " " << zitr->yoffset << " "
488  << (u16)zitr->z << std::endl;
489  }
490  }
491  }
492  }
493  }
494 }
495 
498 {
499  // Give scripters the chance to keep an item alive
500  if ( item->invisible() )
501  return false;
502 
503  // Some items could be part of the house, but not inside the house (e.g. an exterior lamp post)
504  // hide them to avoid an exception later, since this is not supported
505  if ( house->realm->find_supporting_multi( item->x, item->y, item->z ) != house )
506  return false;
507 
508  return true;
509 }
510 
512 {
513  UHouse::Components* comp = house->get_components();
514  UHouse::Components::iterator itr = comp->begin();
515  while ( itr != comp->end() )
516  {
517  Items::Item* item = ( *itr ).get();
518  if ( item != nullptr && !item->orphan() )
519  {
520  if ( isEditableItem( house, item ) )
521  {
522  itr = comp->erase( itr );
523  destroy_item( item );
524  continue;
525  }
526  }
527  ++itr;
528  }
529 }
530 
532 {
533  UHouse::Components* comp = house->get_components();
534  for ( UHouse::Components::const_iterator itr = comp->begin(), end = comp->end(); itr != end;
535  ++itr )
536  {
537  Items::Item* item = ( *itr ).get();
538  if ( item != nullptr && !item->orphan() )
539  {
540  if ( isEditableItem( house, item ) ) // give scripters the chance to keep an item alive
541  {
543  elem.graphic = item->graphic;
544  elem.xoffset = item->x - house->x;
545  elem.yoffset = item->y - house->y;
546  elem.z = item->z - house->z;
547  Add( elem );
548  }
549  }
550  }
551 }
552 void CustomHouseDesign::FillComponents( UHouse* house, bool add_as_component )
553 {
554  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
555  {
556  for ( HouseFloor::iterator xitr = Elements[i].data.begin(), xitrend = Elements[i].data.end();
557  xitr != xitrend; ++xitr )
558  {
559  for ( HouseFloorRow::iterator yitr = xitr->begin(), yitrend = xitr->end(); yitr != yitrend;
560  ++yitr )
561  {
562  HouseFloorZColumn::iterator zitr = yitr->begin();
563  while ( zitr != yitr->end() )
564  {
565  const Items::ItemDesc& id = Items::find_itemdesc( zitr->graphic );
566  if ( id.type == Items::ItemDesc::DOORDESC )
567  {
568  if ( add_as_component )
569  {
570  Items::Item* component = Items::Item::create( id.objtype );
571  if ( component != nullptr )
572  {
573  bool res = house->add_component( component, zitr->xoffset, zitr->yoffset, zitr->z );
574  passert_always_r( res,
575  "Couldn't add newly created door as house component. Please "
576  "report this bug on the forums." );
577  }
578  }
579  zitr = yitr->erase( zitr );
580  floor_sizes[i]--;
581  }
582  else if ( zitr->graphic >= TELEPORTER_START &&
583  zitr->graphic <= TELEPORTER_END ) // teleporters
584  {
585  if ( add_as_component )
586  {
587  Items::Item* component = Items::Item::create( zitr->graphic );
588  if ( component != nullptr )
589  {
590  bool res = house->add_component( component, zitr->xoffset, zitr->yoffset, zitr->z );
591  passert_always_r( res,
592  "Couldn't add newly created teleporter as house component. "
593  "Please report this bug on the forums." );
594  }
595  }
596  zitr = yitr->erase( zitr );
597  floor_sizes[i]--;
598  }
599  else
600  ++zitr;
601  }
602  }
603  }
604  }
605 }
606 
608 {
609  std::unique_ptr<Bscript::ObjArray> arr( new Bscript::ObjArray );
610  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
611  {
612  for ( HouseFloor::const_iterator xitr = Elements[i].data.begin(),
613  xitrend = Elements[i].data.end();
614  xitr != xitrend; ++xitr )
615  {
616  for ( HouseFloorRow::const_iterator yitr = xitr->begin(), yitrend = xitr->end();
617  yitr != yitrend; ++yitr )
618  {
619  for ( HouseFloorZColumn::const_iterator zitr = yitr->begin(), zitrend = yitr->end();
620  zitr != zitrend; ++zitr )
621  {
622  std::unique_ptr<Bscript::BStruct> itemstruct( new Bscript::BStruct );
623  itemstruct->addMember( "graphic", new Bscript::BLong( zitr->graphic ) );
624  itemstruct->addMember( "xoffset", new Bscript::BLong( zitr->xoffset ) );
625  itemstruct->addMember( "yoffset", new Bscript::BLong( zitr->yoffset ) );
626  itemstruct->addMember( "z", new Bscript::BLong( zitr->z ) );
627  arr->addElement( itemstruct.release() );
628  }
629  }
630  }
631  }
632  return arr.release();
633 }
634 
636 {
638  msg->WriteFlipped<u16>( 17u );
639  msg->offset += 2; // sub
640  msg->Write<u32>( house->serial_ext );
641  msg->Write<u8>( 0x5u ); // end
642  msg->offset += 2; // u16 unk2 FIXME what's the meaning
643  msg->Write<u32>( 0xFFFFFFFFu ); // fixme
644  msg->Write<u8>( 0xFFu ); // fixme
645  msg.Send( chr->client );
646 
647  const MultiDef& def = house->multidef();
648  move_character_to( chr, house->x + def.minrx, house->y + def.maxry + 1, house->z,
649  Core::MOVEITEM_FORCELOCATION, nullptr );
650  chr->client->gd->custom_house_serial = 0;
651  house->editing = false;
652  ItemList itemlist;
653  MobileList moblist;
654  UHouse::list_contents( house, itemlist, moblist );
655  while ( !itemlist.empty() )
656  {
657  Items::Item* item = itemlist.front();
658  send_item( chr->client, item );
659  itemlist.pop_front();
660  }
661 }
662 
664 {
665  u32 serial = cfBEu32( msg->serial );
666  UHouse* house = UHouse::FindWorkingHouse( serial );
667  if ( house == nullptr )
668  return;
669 
670  Core::CH_ADD add = msg->ch_add;
672  elem.graphic = cfBEu16( add.tileID );
673  elem.xoffset = cfBEu32( add.xoffset );
674  elem.yoffset = cfBEu32( add.yoffset );
675  elem.z = CustomHouseDesign::custom_house_z_xlate_table[house->editing_floor_num];
676 
677  // the south side of the house can have stairs at z=0
678  // int ysize = house->multidef().maxry - house->multidef().minry;
679 
680  if ( elem.yoffset == house->multidef().maxry + 1 )
681  elem.z = 0;
682 
683  house->WorkingDesign.AddOrReplace( elem );
684 
685  // invalidate stored packet
686  std::vector<u8> newvec;
687  house->WorkingCompressed.swap( newvec );
688 
689  house->revision++;
690 }
691 
693 {
694  u32 serial = cfBEu32( msg->serial );
695  UHouse* house = UHouse::FindWorkingHouse( serial );
696  if ( house == nullptr )
697  return;
698 
699  u16 itemID = cfBEu16( msg->ch_add_multi.multiID );
700  s8 x = static_cast<s8>( cfBEu32( msg->ch_add_multi.xoffset ) ),
701  y = static_cast<s8>( cfBEu32( msg->ch_add_multi.yoffset ) );
702  s8 z = CustomHouseDesign::custom_house_z_xlate_table[house->editing_floor_num];
703 
704  // only allow stairs IDs
705  if ( itemID < STAIR_MULTIID_MIN || itemID > STAIR_MULTIID_MAX )
706  {
707  Mobile::Character* chr = Core::find_character( serial );
708  if ( chr != nullptr && chr->client != nullptr )
710  return;
711  }
712 
713  house->WorkingDesign.AddMultiAtOffset( itemID, x, y, z );
714 
715  // invalidate stored packet
716  std::vector<u8> newvec;
717  house->WorkingCompressed.swap( newvec );
718 
719  house->revision++;
720 }
721 
723 {
724  u32 serial = cfBEu32( msg->serial );
725  UHouse* house = UHouse::FindWorkingHouse( serial );
726  if ( house == nullptr )
727  return;
728 
729  u32 x = cfBEu32( msg->ch_erase.xoffset ), y = cfBEu32( msg->ch_erase.yoffset );
730  u8 z = static_cast<u8>( cfBEu32( msg->ch_erase.z ) );
731  u16 graphic = cfBEu16( msg->ch_erase.tileID );
732 
733  u32 realx = x + house->WorkingDesign.xoff;
734  u32 realy = y + house->WorkingDesign.yoff;
735 
736  if ( z == 0 && realx < house->WorkingDesign.width && realy < ( house->WorkingDesign.height - 1 ) )
737  {
738  return;
739  }
740 
741  // check if not deleting a stairs piece (if we are, DeleteStairs will do it)
742  // check z == 0 to make sure all exterior stairs are deleted with EraseGraphicAt
743  if ( z == 0 || !house->WorkingDesign.DeleteStairs( graphic, x, y, z ) )
744  {
745  house->WorkingDesign.EraseGraphicAt( graphic, x, y, z );
746  // maybe replace empty ground floor with dirt tile
747  if ( z == CustomHouseDesign::custom_house_z_xlate_table[1] )
748  house->WorkingDesign.ReplaceDirtFloor( x, y );
749  }
750 
751  // invalidate stored packet
752  std::vector<u8> newvec;
753  house->WorkingCompressed.swap( newvec );
754 
755  Mobile::Character* chr = Core::find_character( serial );
756  if ( chr && chr->client )
758 
759  house->revision++;
760 }
761 
763 {
764  u32 serial = cfBEu32( msg->serial );
765  Mobile::Character* chr = Core::find_character( serial );
766  UHouse* house = UHouse::FindWorkingHouse( serial );
767  if ( house == nullptr )
768  return;
769 
770  house->WorkingDesign.Clear();
771 
772  // invalidate stored packet
773  std::vector<u8> newvec;
774  house->WorkingCompressed.swap( newvec );
775 
776  // add foundation back to design
777  house->WorkingDesign.AddMultiAtOffset( house->multiid, 0, 0, 0 );
778  if ( chr != nullptr && chr->client != nullptr )
780 
781  house->revision++;
782 }
783 
784 // if the client closed his tool
786 {
787  u32 serial = cfBEu32( msg->serial );
788  UHouse* house = UHouse::FindWorkingHouse( serial );
789  if ( house == nullptr )
790  return;
791  Mobile::Character* chr = Core::find_character( serial );
792  house->CustomHousesQuit( chr, false );
793 }
794 
796 {
797  u32 serial = cfBEu32( msg->serial );
798  UHouse* house = UHouse::FindWorkingHouse( serial );
799  Mobile::Character* chr = Core::find_character( serial );
800  if ( house == nullptr || chr == nullptr )
801  return;
802 
803  // remove dynamic bits (teleporters, doors)
804  house->WorkingDesign.FillComponents( house );
805 
806  // call a script to do post processing (calc cost, yes/no confirm, consume cost, link teleporters)
807  Core::ScriptDef sd;
808  if ( sd.config_nodie( "misc/customhousecommit.ecl", 0, 0 ) )
809  {
810  if ( sd.exists() )
811  {
812  house->waiting_for_accept = true;
813  if ( Core::start_script( sd, make_mobileref( chr ), new Module::EMultiRefObjImp( house ),
814  house->WorkingDesign.list_parts() ) != nullptr )
815  return;
816  }
817  }
818  house->AcceptHouseCommit( chr, true );
819 }
820 
822 {
823  u32 serial = cfBEu32( msg->serial );
824  UHouse* house = UHouse::FindWorkingHouse( serial );
825  Mobile::Character* chr = Core::find_character( serial );
826  if ( house == nullptr )
827  return;
828  u8 floor = msg->ch_select_floor.floornumber;
829 
830  if ( floor < 1 || floor > 4 )
831  floor = 1;
832 
833  house->editing_floor_num = floor;
834 
835  if ( chr )
836  {
837  move_character_to( chr, chr->x, chr->y,
838  house->z + CustomHouseDesign::custom_house_z_xlate_table[floor],
839  Core::MOVEITEM_FORCELOCATION, nullptr );
840  if ( chr->client )
842  }
843 }
844 
846 {
847  u32 serial = cfBEu32( msg->serial );
848  UHouse* house = UHouse::FindWorkingHouse( serial );
849  if ( house == nullptr )
850  return;
851 
852  house->BackupDesign = house->WorkingDesign;
853 }
854 
856 {
857  u32 serial = cfBEu32( msg->serial );
858  UHouse* house = UHouse::FindWorkingHouse( serial );
859  Mobile::Character* chr = Core::find_character( serial );
860  if ( house == nullptr )
861  return;
862 
863  house->WorkingDesign = house->BackupDesign;
864  std::vector<u8> newvec;
865  house->WorkingCompressed.swap( newvec );
866  if ( chr != nullptr && chr->client != nullptr )
868 }
869 
871 {
872  u32 serial = cfBEu32( msg->serial );
873  Mobile::Character* chr = Core::find_character( serial );
874  UHouse* house = UHouse::FindWorkingHouse( serial );
875  if ( chr != nullptr && chr->client != nullptr && house != nullptr )
877 }
878 
879 // replace working design with currently committed design
881 {
882  u32 serial = cfBEu32( msg->serial );
883  UHouse* house = UHouse::FindWorkingHouse( serial );
884 
885  if ( house == nullptr )
886  return;
887 
888  house->WorkingDesign = house->CurrentDesign;
889  std::vector<u8> newvec;
890  house->WorkingCompressed.swap( newvec );
891  Mobile::Character* chr = Core::find_character( serial );
892  if ( chr != nullptr && chr->client != nullptr )
894 }
895 
897 {
898  u32 serial = cfBEu32( msg->serial );
899  UHouse* house = UHouse::FindWorkingHouse( serial );
900  if ( house == nullptr )
901  return;
902 
905  elem.graphic = cfBEu16( add.tileID );
906  elem.xoffset = cfBEu32( add.xoffset );
907  elem.yoffset = cfBEu32( add.yoffset );
908  s8 z = static_cast<s8>( cfBEu32( add.zoffset ) );
909  if ( z < -3 || z > 12 || z % 3 != 0 )
910  z = -3;
911  elem.z = z + CustomHouseDesign::custom_house_z_xlate_table[house->editing_floor_num];
912 
913  house->WorkingDesign.AddOrReplace( elem );
914 
915  // invalidate stored packet
916  std::vector<u8> newvec;
917  house->WorkingCompressed.swap( newvec );
918 
919  house->revision++;
920 }
922 {
923  u32 serial = cfBEu32( msg->serial );
924  UHouse* house = UHouse::FindWorkingHouse( serial );
925  if ( house == nullptr )
926  return;
927 
928  Core::CH_DELETE_ROOF remove = msg->ch_delete_roof;
929  u32 x = cfBEu32( remove.xoffset ), y = cfBEu32( remove.yoffset );
930  u8 z = static_cast<u8>( cfBEu32( remove.zoffset ) );
931  u16 graphic = cfBEu16( remove.tileID );
932  if ( !house->WorkingDesign.EraseGraphicAt( graphic, x, y, z ) )
933  {
934  Mobile::Character* chr = Core::find_character( serial );
935  if ( chr != nullptr && chr->client != nullptr )
937  return;
938  }
939 
940  // invalidate stored packet
941  std::vector<u8> newvec;
942  house->WorkingCompressed.swap( newvec );
943 
944  house->revision++;
945 }
946 
947 void CustomHousesSendFull( UHouse* house, Network::Client* client, int design )
948 {
949  u32 clen;
950  u32 ulen;
951  unsigned char* data;
952  // unsigned char** stored_packet;
953  std::vector<u8>* stored_packet;
954 
955  u32 planeheader = 0;
956  u32 buffer_len = 0;
957  CustomHouseDesign* pdesign;
958  const unsigned int data_offset = 17;
959  const u32 mode = 0; // we only know how to do mode 0 at this point.
960 
961  // choose between sending working or current designs
962  switch ( design )
963  {
965  if ( house->CurrentCompressed.empty() ) // no design stored, create below
966  {
967  pdesign = &house->CurrentDesign;
968  stored_packet = &house->CurrentCompressed;
969  }
970  else // send stored packet
971  {
973  client, &house->CurrentCompressed[0],
974  ctBEu16( *( reinterpret_cast<u16*>( &house->CurrentCompressed[1] ) ) ) );
975  return;
976  }
977  break;
979  if ( house->WorkingCompressed.empty() ) // no design stored, create below
980  {
981  pdesign = &house->WorkingDesign;
982  stored_packet = &house->WorkingCompressed;
983  }
984  else // send stored packet
985  {
987  client, &house->WorkingCompressed[0],
988  ctBEu16( *( reinterpret_cast<u16*>( &house->WorkingCompressed[1] ) ) ) );
989  return;
990  }
991  break;
992  default:
993  return;
994  }
995 
996  // create compressed house message
997 
998  unsigned char planes = pdesign->NumUsedPlanes();
999  unsigned long sbuflen =
1000  ( (unsigned long)( ( (float)( pdesign->TotalSize() * BYTES_PER_TILE ) ) * 1.001f ) + 12 );
1001  sbuflen += planes * BYTES_PER_TILE; // for plane header dword
1002  sbuflen += 17; // for packet header
1003 
1004  std::vector<u8> packet( sbuflen );
1005 
1006 
1007  Core::PKTOUT_D8* msg = reinterpret_cast<Core::PKTOUT_D8*>( &packet[0] );
1008  msg->msgtype = Core::PKTOUT_D8_ID;
1009  msg->compressiontype = 0x3;
1010  msg->unk = 0;
1011  msg->serial = house->serial_ext;
1012  msg->revision = ctBEu32( house->revision );
1013  msg->numtiles = ctBEu16( static_cast<u16>( pdesign->TotalSize() ) );
1014 
1015  msg->buffer->planecount = planes;
1016  buffer_len = 1;
1017  for ( int i = 0; i < planes; i++ )
1018  {
1019  planeheader = 0;
1020  data = pdesign->Compress( i, &ulen, &clen );
1021  if ( data == nullptr ) // compression error
1022  {
1023  return;
1024  }
1025  if ( ulen == 0 )
1026  clen = 0;
1027  planeheader |= ( ( mode << 4 ) << 24 );
1028  planeheader |= ( ( i & 0xF ) << 24 );
1029  planeheader |= ( ( ulen & 0xFF ) << 16 );
1030  planeheader |= ( ( clen & 0xFF ) << 8 );
1031  planeheader |= ( ( ( ulen >> 4 ) & 0xF0 ) | ( ( clen >> 8 ) & 0xF ) );
1032  u32* p_planeheader = reinterpret_cast<u32*>( &( packet[buffer_len + data_offset] ) );
1033  *p_planeheader = ctBEu32( planeheader );
1034  buffer_len += 4;
1035  memcpy( &( packet[buffer_len + data_offset] ), data, clen );
1036  buffer_len += clen;
1037  delete[] data;
1038  }
1039  msg->msglen = ctBEu16( static_cast<u16>( buffer_len ) + data_offset );
1040  msg->planebuffer_len = ctBEu16( static_cast<u16>( buffer_len ) );
1041 
1042  Core::networkManager.clientTransmit->AddToQueue( client, &packet[0], cfBEu16( msg->msglen ) );
1043  stored_packet->swap( packet );
1044 }
1045 
1046 void CustomHousesSendFullToInRange( UHouse* house, int design, int range )
1047 {
1049  house->x, house->y, house->realm, range,
1050  [&]( Mobile::Character* chr ) { CustomHousesSendFull( house, chr->client, design ); } );
1051 }
1052 
1054 {
1056  msg->WriteFlipped<u16>( 13u );
1057  msg->offset += 2;
1058  msg->Write<u32>( house->serial_ext );
1059  msg->WriteFlipped<u32>( house->revision );
1060  msg.Send( client );
1061 }
1062 
1063 void UHouse::SetCustom( bool _custom )
1064 {
1065  if ( custom == false && _custom == true )
1066  CustomHouseSetInitialState();
1067 
1068  custom = _custom;
1069 }
1071 {
1072  int ysize, xsize, xbase, ybase;
1073  const MultiDef& def = multidef();
1074  ysize = def.maxry - def.minry + 1; //+1 to include offset 0 in -3..3
1075  xsize = def.maxrx - def.minrx + 1; //+1 to include offset 0 in -3..3
1076  xbase = abs( def.minrx );
1077  ybase = abs( def.minry );
1078  CurrentDesign.Clear();
1079  CurrentDesign.InitDesign( ysize + 1, xsize, xbase,
1080  ybase ); //+1 for front steps outside multidef footprint
1081  WorkingDesign.Clear();
1082  WorkingDesign.InitDesign( ysize + 1, xsize, xbase,
1083  ybase ); //+1 for front steps outside multidef footprint
1084  BackupDesign.Clear();
1085  BackupDesign.InitDesign( ysize + 1, xsize, xbase,
1086  ybase ); //+1 for front steps outside multidef footprint
1087 
1088  CurrentDesign.AddMultiAtOffset( multiid, 0, 0, 0 );
1089  WorkingDesign = CurrentDesign;
1090  std::vector<u8> newvec;
1091  WorkingCompressed.swap( newvec );
1092 
1093  std::vector<u8> newvec2;
1094  CurrentCompressed.swap( newvec2 );
1095 }
1096 
1097 void UHouse::CustomHousesQuit( Mobile::Character* chr, bool drop_changes )
1098 {
1099  if ( drop_changes )
1100  WorkingDesign = CurrentDesign;
1101  else
1102  {
1103  CurrentDesign.FillComponents( this );
1104  WorkingDesign.FillComponents( this, false ); // keep in sync
1105  }
1106  revision++;
1107  std::vector<u8> newvec;
1108  WorkingCompressed.swap( newvec );
1109 
1110  std::vector<u8> newvec2;
1111  CurrentCompressed.swap( newvec2 );
1112 
1113  if ( chr && chr->client )
1114  {
1115  CustomHouseStopEditing( chr, this );
1117  if ( Core::gamestate.system_hooks.close_customhouse_hook != nullptr )
1118  {
1120  make_mobileref( chr ), new Module::EMultiRefObjImp( this ) );
1121  }
1122  }
1123 }
1124 }
1125 }
unsigned char u8
Definition: rawtypes.h:25
#define DIRTY_TILE
Definition: customhouses.h:55
static Item * create(u32 objtype, u32 serial=0)
Definition: itemcr.cpp:53
CustomHouseDesign WorkingDesign
Definition: house.h:79
std::list< CUSTOM_HOUSE_ELEMENT > HouseFloorZColumn
Definition: customhouses.h:77
int floor_sizes[CUSTOM_HOUSE_NUM_PLANES]
Definition: customhouses.h:132
CustomHouseDesign & operator=(const CustomHouseDesign &design)
bool waiting_for_accept
Definition: house.h:91
static const char custom_house_z_xlate_table[CUSTOM_HOUSE_NUM_PLANES]
Definition: customhouses.h:145
void Add(CUSTOM_HOUSE_ELEMENT &elem)
CH_ADD_MULTI ch_add_multi
Definition: pktboth.h:1096
std::list< Items::Item * > ItemList
Definition: house.h:60
std::vector< Component > Components
Definition: house.h:71
static bool isEditableItem(UHouse *house, Items::Item *item)
Tells wether an item should be show in custom house design or not.
bool config_nodie(const std::string &name, const Plib::Package *pkg, const char *mainpfx)
Definition: scrdef.cpp:84
static void list_contents(const UHouse *house, ItemList &items_in, MobileList &chrs_in)
Definition: house.cpp:63
void CustomHousesErase(Core::PKTBI_D7 *msg)
SystemState systemstate
Definition: systemstate.cpp:12
bool call(Bscript::BObjectImp *p0)
Definition: syshook.cpp:44
Network::Client * client
Definition: charactr.h:871
CH_ERASE ch_erase
Definition: pktboth.h:1094
ExportedFunction * close_customhouse_hook
Definition: syshook.h:81
static UHouse * FindWorkingHouse(u32 chrserial)
Definition: house.cpp:650
Core::PolConfig config
Definition: systemstate.h:43
void SetCustom(bool custom)
bool ValidLocation(u32 xidx, u32 yidx)
Definition: customhouses.h:137
void CustomHousesSynch(Core::PKTBI_D7 *msg)
#define cfBEu32(x)
Definition: clib_endian.h:43
SystemHooks system_hooks
Definition: uvars.h:190
bool move_character_to(Mobile::Character *chr, unsigned short x, unsigned short y, short z, int flags, Realms::Realm *oldrealm)
Definition: core.cpp:50
Components * get_components()
Definition: house.h:134
#define ctBEu16(x)
Definition: clib_endian.h:46
Components components
Definition: multidef.h:87
std::list< Mobile::Character * > MobileList
Definition: house.h:61
CustomHouseDesign CurrentDesign
Definition: house.h:78
Bscript::ObjArray * list_parts() const
#define STAIR_MULTIID_MAX
Definition: customhouses.h:54
std::vector< u8 > CurrentCompressed
Definition: house.h:81
static void InRange(u16 x, u16 y, const Realms::Realm *realm, unsigned range, F &&f)
Definition: uworld.h:235
CustomHouseDesign BackupDesign
Definition: house.h:80
void CustomHouseStopEditing(Mobile::Character *chr, UHouse *house)
const ItemDesc & find_itemdesc(unsigned int objtype)
Definition: itemdesc.cpp:933
void CustomHousesCommit(Core::PKTBI_D7 *msg)
unsigned int max_tile_id
Definition: polcfg.h:61
void AddComponents(UHouse *house)
#define TELEPORTER_END
Definition: customhouses.h:57
CH_DELETE_ROOF ch_delete_roof
Definition: pktboth.h:1100
#define TELEPORTER_START
Definition: customhouses.h:56
bool EraseGraphicAt(u16 graphic, u32 xoffset, u32 yoffset, u8 z)
unsigned short u16
Definition: rawtypes.h:26
unsigned int u32
Definition: rawtypes.h:27
bool exists() const
Definition: scrdef.cpp:126
std::unique_ptr< Network::ClientTransmit > clientTransmit
Definition: network.h:97
void AddElement(CUSTOM_HOUSE_ELEMENT &elem)
void destroy_item(Item *item)
Definition: ufunc.cpp:1538
void InitDesign(u32 _height, u32 _width, s32 xoffset, s32 yoffset)
void CustomHousesQuit(Core::PKTBI_D7 *msg)
NetworkManager networkManager
Definition: network.cpp:28
static char z_to_custom_house_table(char z)
void printProperties(Clib::StreamWriter &sw, const std::string &prefix) const
void Send(Client *client, int len=-1) const
Definition: packethelper.h:69
void CustomHousesQuit(Mobile::Character *chr, bool drop_changes)
void CustomHousesRoofRemove(Core::PKTBI_D7 *msg)
void testprint(std::ostream &os) const
bool orphan() const
Definition: baseobject.h:119
char tileheight(unsigned short tilenum)
Definition: polfile2.cpp:34
void ReplaceDirtFloor(u32 x, u32 y)
void AcceptHouseCommit(Mobile::Character *chr, bool accept)
Definition: house.cpp:996
CH_SELECT_FLOOR ch_select_floor
Definition: pktboth.h:1097
signed int s32
Definition: rawtypes.h:31
bool DeleteStairs(u16 id, s32 x, s32 y, s8 z)
#define cfBEu16(x)
Definition: clib_endian.h:44
signed char s8
Definition: rawtypes.h:29
ClientGameData * gd
Definition: client.h:252
GameState gamestate
Definition: uvars.cpp:74
Mobile::Character * find_character(u32 serial)
Definition: fnsearch.cpp:60
#define passert_always_r(exp, reason)
Definition: passert.h:84
std::unordered_map< u64, ScriptDiffData > data
Definition: osmod.cpp:966
void FillComponents(UHouse *house, bool add_as_component=true)
#define ctBEu32(x)
Definition: clib_endian.h:45
void CustomHousesSendFullToInRange(UHouse *house, int design, int range)
CH_SELECT_ROOF ch_select_roof
Definition: pktboth.h:1099
void CustomHouseSetInitialState()
unsigned char * Compress(int floor, u32 *uncompr_length, u32 *compr_length)
const int MOVEITEM_FORCELOCATION
Definition: core.h:52
bool remove_prop(const char *propname, std::string *value)
Definition: cfgfile.cpp:128
void send_item(Client *client, const Item *item)
Definition: ufunc.cpp:676
int editing_floor_num
Definition: house.h:92
bool invisible() const
Definition: item.h:324
void CustomHousesBackup(Core::PKTBI_D7 *msg)
unsigned int TotalSize() const
#define ISTRINGSTREAM
Definition: stlutil.h:73
std::vector< u8 > WorkingCompressed
Definition: house.h:82
#define BYTES_PER_TILE
Realms::Realm * realm
Definition: baseobject.h:56
CustomHouseElements Elements[CUSTOM_HOUSE_NUM_PLANES]
Definition: customhouses.h:143
unsigned char NumUsedPlanes() const
void AddMultiAtOffset(u16 multiid, s8 x, s8 y, s8 z)
void CustomHousesClear(Core::PKTBI_D7 *msg)
#define ERROR_PRINT
Definition: logfacility.h:230
void AddOrReplace(CUSTOM_HOUSE_ELEMENT &elem)
void CustomHousesSelectFloor(Core::PKTBI_D7 *msg)
const MultiDef & multidef() const
Definition: multis.cpp:69
void CustomHousesSendFull(UHouse *house, Network::Client *client, int design)
void CustomHousesRestore(Core::PKTBI_D7 *msg)
bool Erase(u32 xoffset, u32 yoffset, u8 z, int minheight=0)
#define CUSTOM_HOUSE_NUM_PLANES
Definition: customhouses.h:52
Multi::UMulti * find_supporting_multi(unsigned short x, unsigned short y, short z) const
Definition: realmfunc.cpp:709
void start_script(const char *filename, Bscript::BObjectImp *param0, Bscript::BObjectImp *param1)
Definition: scrsched.cpp:150
Bscript::BObjectImp * make_mobileref(Mobile::Character *chr)
Definition: mkscrobj.cpp:14
void readProperties(Clib::ConfigElem &elem, const std::string &prefix)
void CustomHousesAdd(Core::PKTBI_D7 *msg)
Definition: berror.cpp:12
CUSTOM_HOUSE_PLANE_BUFFER buffer[1]
Definition: pktout.h:1225
void CustomHousesRoofSelect(Core::PKTBI_D7 *msg)
void CustomHousesRevert(Core::PKTBI_D7 *msg)
bool add_component(Items::Item *item, s32 xoff, s32 yoff, s16 zoff)
Definition: house.cpp:144
const MultiDef * MultiDefByMultiID(u16 multiid)
Definition: multidef.cpp:297
void CustomHousesAddMulti(Core::PKTBI_D7 *msg)
static void ClearComponents(UHouse *house)
unsigned short objtype
Definition: multidef.h:46
void CustomHousesSendShort(UHouse *house, Network::Client *client)