Pol  Revision:cb584c9
house.cpp
Go to the documentation of this file.
1 
15 #include "house.h"
16 
17 #include <boost/numeric/conversion/cast.hpp>
18 #include <stdlib.h>
19 
20 #include <format/format.h>
21 #include "../../bscript/berror.h"
22 #include "../../bscript/executor.h"
23 #include "../../bscript/objmembers.h"
24 #include "../../bscript/objmethods.h"
25 #include "../../clib/cfgelem.h"
26 #include "../../clib/clib_endian.h"
27 #include "../../clib/logfacility.h"
28 #include "../../clib/passert.h"
29 #include "../../clib/refptr.h"
30 #include "../../clib/streamsaver.h"
31 #include "../../plib/mapcell.h"
32 #include "../../plib/mapshape.h"
33 #include "../../plib/systemstate.h"
34 #include "../core.h"
35 #include "../fnsearch.h"
36 #include "../globals/object_storage.h"
37 #include "../item/itemdesc.h"
38 #include "../mobile/charactr.h"
39 #include "../module/osmod.h"
40 #include "../module/uomod.h"
41 #include "../network/cgdata.h"
42 #include "../network/client.h"
43 #include "../realms/realm.h"
44 #include "../scrdef.h"
45 #include "../scrsched.h"
46 #include "../scrstore.h"
47 #include "../uconst.h"
48 #include "../ufunc.h"
49 #include "../uobject.h"
50 #include "../uoexec.h"
51 #include "../uoexhelp.h"
52 #include "../uoscrobj.h"
53 #include "../uworld.h"
54 #include "customhouses.h"
55 #include "multi.h"
56 #include "multidef.h"
57 
58 
59 namespace Pol
60 {
61 namespace Multi
62 {
63 void UHouse::list_contents( const UHouse* house, ItemList& items_in, MobileList& chrs_in )
64 {
65  const MultiDef& md = house->multidef();
66  short x1 = house->x + md.minrx, y1 = house->y + md.minry;
67  short x2 = house->x + md.maxrx, y2 = house->y + md.maxry;
68 
70  x1, y1, x2, y2, house->realm, [&]( Mobile::Character* chr ) {
71  UMulti* multi = house->realm->find_supporting_multi( chr->x, chr->y, chr->z );
72  if ( const_cast<const UMulti*>( multi ) == house )
73  chrs_in.push_back( chr );
74  } );
76  x1, y1, x2, y2, house->realm, [&]( Items::Item* item ) {
77  UMulti* multi = house->realm->find_supporting_multi( item->x, item->y, item->z );
78  if ( const_cast<const UMulti*>( multi ) == house )
79  {
80  if ( Core::tile_flags( item->graphic ) & Plib::FLAG::WALKBLOCK )
81  items_in.push_front( item );
82  else
83  items_in.push_back( item );
84  }
85  } );
86 }
87 
88 UHouse::UHouse( const Items::ItemDesc& itemdesc )
89  : UMulti( itemdesc ),
90  editing( false ),
91  waiting_for_accept( false ),
92  editing_floor_num( 1 ),
93  revision( 0 ),
94  custom( false )
95 {
96 }
97 
98 size_t UHouse::estimatedSize() const
99 {
100  size_t size = base::estimatedSize() + CurrentDesign.estimatedSize() +
101  WorkingDesign.estimatedSize() + BackupDesign.estimatedSize() + 3 * sizeof( u8* ) +
102  CurrentCompressed.capacity() * sizeof( u8 ) + 3 * sizeof( u8* ) +
103  WorkingCompressed.capacity() * sizeof( u8 ) + sizeof( bool ) /*editing*/
104  + sizeof( bool ) /*waiting_for_accept*/
105  + sizeof( int ) /*editing_floor_num*/
106  + sizeof( u32 ) /*revision*/
107  + sizeof( bool ) /*custom*/
108  // no estimateSize here element is in objhash
109  + 3 * sizeof( Squatter* ) + squatters_.capacity() * sizeof( Squatter ) +
110  3 * sizeof( Component* ) + components_.capacity() * sizeof( Component );
111  return size;
112 }
113 
118 {
119  const MultiDef& md = multidef();
120  for ( unsigned i = 0; i < md.elems.size(); ++i )
121  {
122  const MULTI_ELEM& elem = md.elems[i];
123  if ( !elem.is_static )
124  {
125  Items::Item* item = Items::Item::create( elem.objtype );
126  bool res = add_component( item, elem.x, elem.y, elem.z );
127  passert_always_r( res,
128  "Couldn't add newly created item as house component. Please report this "
129  "bug on the forums." );
130  }
131  }
132 }
133 
144 bool UHouse::add_component( Items::Item* item, s32 xoff, s32 yoff, s16 zoff )
145 {
146  if ( !can_add_component( item ) )
147  return false;
148 
149  u16 newx, newy;
150  s8 newz;
151  try
152  {
153  // These casts should be safe, but better check them - 2015-01-25 Bodom
154  newx = boost::numeric_cast<u16>( x + xoff );
155  newy = boost::numeric_cast<u16>( y + yoff );
156  newz = boost::numeric_cast<s8>( z + zoff );
157  }
158  catch ( boost::bad_numeric_cast& )
159  {
160  // Printing an error because this is supposed to not happen,
161  // so it's probably a bug.
162  POLLOG_ERROR << "Out-of-range coordinates while trying to add Item "
163  << fmt::hexu( item->serial ) << " to House " << fmt::hexu( serial ) << '\n';
164  return false;
165  }
166  item->x = newx;
167  item->y = newy;
168  item->z = newz;
169  item->disable_decay();
170  item->movable( false );
171  item->realm = realm;
172  update_item_to_inrange( item );
173  add_item_to_world( item );
175  return true;
176 }
177 
186 {
187  if ( !can_add_component( item.get() ) )
188  return false;
189 
190  add_component_no_check( item );
191  return true;
192 }
193 
200 {
201  std::unique_ptr<Bscript::ObjArray> arr( new Bscript::ObjArray );
202  for ( Components::const_iterator itr = components_.begin(), end = components_.end(); itr != end;
203  ++itr )
204  {
205  Items::Item* item = ( *itr ).get();
206  if ( item != nullptr && !item->orphan() )
207  {
208  arr->addElement( new Module::EItemRefObjImp( item ) );
209  }
210  }
211  return arr.release();
212 }
213 
215 {
216  ItemList itemlist;
217  MobileList moblist;
218  list_contents( this, itemlist, moblist );
219  std::unique_ptr<Bscript::ObjArray> arr( new Bscript::ObjArray );
220  for ( auto& item : itemlist )
221  {
222  if ( std::find( components_.cbegin(), components_.cend(), Component( item ) ) ==
223  components_.cend() )
224  {
225  arr->addElement( new Module::EItemRefObjImp( item ) );
226  }
227  }
228  return arr.release();
229 }
230 
232 {
233  ItemList itemlist;
234  MobileList moblist;
235  list_contents( this, itemlist, moblist );
236  std::unique_ptr<Bscript::ObjArray> arr( new Bscript::ObjArray );
237  for ( auto& chr : moblist )
238  {
239  arr->addElement( new Module::ECharacterRefObjImp( chr ) );
240  }
241  return arr.release();
242 }
243 
245 {
246  return this;
247 }
248 
250 {
251  using namespace Bscript;
253  if ( imp )
254  return imp;
255 
256  switch ( id )
257  {
258  case MBR_COMPONENTS:
259  return component_list();
260  break;
261  case MBR_ITEMS:
262  return items_list();
263  break;
264  case MBR_MOBILES:
265  return mobiles_list();
266  break;
267  case MBR_CUSTOM:
268  return new BLong( IsCustom() );
269  break;
270  case MBR_EDITING:
271  return new BLong( IsEditing() );
272  break;
273  case MBR_MULTIID:
274  return new BLong( multiid );
275  break;
276  case MBR_HOUSEPARTS:
277  if ( !IsCustom() )
278  return new BError( "House is not custom" );
279  else if ( IsEditing() )
280  return new BError( "House is currently been edited" );
281  else
282  return CurrentDesign.list_parts();
283  break;
284  default:
285  return nullptr;
286  }
287 }
288 
289 Bscript::BObjectImp* UHouse::get_script_member( const char* membername ) const
290 {
291  Bscript::ObjMember* objmember = Bscript::getKnownObjMember( membername );
292  if ( objmember != nullptr )
293  return this->get_script_member_id( objmember->id );
294  else
295  return nullptr;
296 }
297 
299 {
300  using namespace Bscript;
301  BObjectImp* imp = base::script_method_id( id, ex );
302  if ( imp != nullptr )
303  return imp;
304 
305  switch ( id )
306  {
307  case MTH_SETCUSTOM:
308  {
309  int _custom;
310  if ( ex.getParam( 0, _custom ) )
311  {
312  SetCustom( _custom ? true : false );
313  return new BLong( 1 );
314  }
315  break;
316  }
317  case MTH_ADD_COMPONENT:
318  {
319  BApplicObjBase* aob;
320  if ( ex.hasParams( 0 ) )
321  {
323  if ( aob )
324  {
325  Module::EItemRefObjImp* ir = static_cast<Module::EItemRefObjImp*>( aob );
326  Core::ItemRef iref = ir->value();
327 
328  if ( add_component( iref ) )
329  return new BLong( 1 );
330 
331  if ( iref->house() )
332  return new BError( "Item is already an house component" );
333  else
334  return new BError( "Couldn't add component" );
335  }
336  }
337  break;
338  }
339  case MTH_ERASE_COMPONENT:
340  {
341  BApplicObjBase* aob;
342  if ( ex.hasParams( 0 ) )
343  {
345  if ( aob )
346  {
347  Module::EItemRefObjImp* ir = static_cast<Module::EItemRefObjImp*>( aob );
348  Core::ItemRef iref = ir->value();
349  Components::iterator pos;
350  pos = find( components_.begin(), components_.end(), iref );
351  if ( pos != components_.end() )
352  {
353  iref->house( nullptr );
354  components_.erase( pos );
355  }
356  else
357  return new BError( "Component not found" );
358  return new BLong( 1 );
359  }
360  }
361  break;
362  }
363  case MTH_ADD_HOUSE_PART:
364  {
365  if ( !IsCustom() )
366  return new BError( "House is not custom" );
367  else if ( IsEditing() )
368  return new BError( "House is currently been edited" );
369  else if ( !ex.hasParams( 4 ) )
370  return new BError( "Not enough parameters" );
371  u16 item_graphic;
372  int xoff, yoff, item_z;
373  if ( ex.getParam( 0, item_graphic ) && ex.getParam( 1, xoff ) && ex.getParam( 2, yoff ) &&
374  ex.getParam( 3, item_z ) )
375  {
377  elem.graphic = item_graphic;
378  elem.xoffset = xoff;
379  elem.yoffset = yoff;
380  elem.z = static_cast<u8>( item_z );
381  CurrentDesign.Add( elem );
382  // invalidate
383  // invalidate
385  std::vector<u8> newvec;
386  WorkingCompressed.swap( newvec );
387  std::vector<u8> newvec2;
388  CurrentCompressed.swap( newvec2 );
389  revision++;
391  return new BLong( 1 );
392  }
393  break;
394  }
396  {
397  if ( !IsCustom() )
398  return new BError( "House is not custom" );
399  else if ( IsEditing() )
400  return new BError( "House is currently been edited" );
401  else if ( !ex.hasParams( 4 ) )
402  return new BError( "Not enough parameters" );
403  int item_graphic, xoff, yoff, item_z;
404  if ( ex.getParam( 0, item_graphic ) && ex.getParam( 1, xoff ) && ex.getParam( 2, yoff ) &&
405  ex.getParam( 3, item_z ) )
406  {
407  bool ret =
408  CurrentDesign.EraseGraphicAt( static_cast<u16>( item_graphic ), static_cast<u32>( xoff ),
409  static_cast<u32>( yoff ), static_cast<u8>( item_z ) );
410  if ( ret )
411  {
412  // invalidate
414  std::vector<u8> newvec;
415  WorkingCompressed.swap( newvec );
416  std::vector<u8> newvec2;
417  CurrentCompressed.swap( newvec2 );
419  }
420  return new BLong( ret ? 1 : 0 );
421  }
422  break;
423  }
424  case MTH_ACCEPT_COMMIT:
425  {
426  if ( !IsCustom() )
427  return new BError( "House is not custom" );
428  // else if (!IsEditing())
429  // return new BError( "House is currently not been edited" );
430  else if ( !IsWaitingForAccept() )
431  return new BError( "House is currently not waiting for a commit" );
432  else if ( !ex.hasParams( 2 ) )
433  return new BError( "Not enough parameters" );
434  int accept;
435  Mobile::Character* chr;
436  if ( ex.getParam( 1, accept ) && getCharacterParam( ex, 0, chr ) )
437  {
438  AcceptHouseCommit( chr, accept ? true : false );
439  return new BLong( 1 );
440  }
441  break;
442  }
443  case MTH_CANCEL_EDITING:
444  {
445  if ( !IsCustom() )
446  return new BError( "House is not custom" );
447  else if ( !IsEditing() )
448  return new BError( "House is currently not been edited" );
449  else if ( !ex.hasParams( 2 ) )
450  return new BError( "Not enough parameters" );
451  Mobile::Character* chr;
452  int drop_changes;
453  if ( getCharacterParam( ex, 0, chr ) && ex.getParam( 1, drop_changes ) )
454  {
455  if ( chr->client->gd->custom_house_serial == serial )
456  CustomHousesQuit( chr, drop_changes ? true : false );
457  else
458  return new BError( "Character is not editing this house" );
459  return new BLong( 1 );
460  }
461  break;
462  }
463 
464  default:
465  return nullptr;
466  }
467  return new BError( "Invalid parameter type" );
468 }
469 
471 {
472  Bscript::ObjMethod* objmethod = Bscript::getKnownObjMethod( methodname );
473  if ( objmethod != nullptr )
474  return this->script_method_id( objmethod->id, ex );
475  else
476  return nullptr;
477 }
478 
480 {
481  base::readProperties( elem );
482  u32 tmp_serial;
483  multiid = elem.remove_ushort( "MultiID", this->multidef().multiid );
484 
485  while ( elem.remove_prop( "Component", &tmp_serial ) )
486  {
487  Items::Item* item = Core::find_toplevel_item( tmp_serial );
488  if ( item != nullptr )
489  {
490  if ( !add_component( Component( item ) ) )
491  {
492  fmt::Writer os;
493  os << "Couldn't add component " << fmt::hexu( item->serial ) << " to house "
494  << fmt::hexu( serial ) << ".\n";
495  UHouse* contHouse = item->house();
496  if ( contHouse == nullptr )
497  {
498  os << "This is probably a core bug. Please report it on the forums.";
499  }
500  else
501  {
502  os << "This item is already part of house " << contHouse->serial << ".\n";
503  os << "Allowing an item to be a component in two different houses was a bug,\n";
504  os << "please also fix your save data.";
505  }
506  throw std::runtime_error( os.str() );
507  }
508  }
509  }
510  custom = elem.remove_bool( "Custom", false );
511  if ( custom )
512  {
513  short ysize, xsize, xbase, ybase;
514  const MultiDef& def = multidef();
515  ysize = def.maxry - def.minry + 1; //+1 to include offset 0 in -3..3
516  xsize = def.maxrx - def.minrx + 1; //+1 to include offset 0 in -3..3
517  xbase = (short)abs( def.minrx );
518  ybase = (short)abs( def.minry );
519  CurrentDesign.InitDesign( ysize + 1, xsize, xbase,
520  ybase ); //+1 for front steps outside multidef footprint
521  WorkingDesign.InitDesign( ysize + 1, xsize, xbase,
522  ybase ); //+1 for front steps outside multidef footprint
523  BackupDesign.InitDesign( ysize + 1, xsize, xbase,
524  ybase ); //+1 for front steps outside multidef footprint
525  CurrentDesign.readProperties( elem, "Current" );
526  WorkingDesign.readProperties( elem, "Working" );
527  BackupDesign.readProperties( elem, "Backup" );
528 
529  elem.remove_prop( "DesignRevision", &revision );
530  }
531 }
532 
534 {
535  base::printProperties( sw );
536 
537  sw() << "\tMultiID\t" << multiid << pf_endl;
538 
539  for ( Components::const_iterator itr = components_.begin(), end = components_.end(); itr != end;
540  ++itr )
541  {
542  Items::Item* item = ( *itr ).get();
543  if ( item != nullptr && !item->orphan() )
544  {
545  sw() << "\tComponent\t0x" << fmt::hex( item->serial ) << pf_endl;
546  }
547  }
548  sw() << "\tCustom\t" << custom << pf_endl;
549  if ( custom )
550  {
551  CurrentDesign.printProperties( sw, "Current" );
552  WorkingDesign.printProperties( sw, "Working" );
553  BackupDesign.printProperties( sw, "Backup" );
554  sw() << "\tDesignRevision\t" << revision << pf_endl;
555  }
556 }
557 
559 {
560  while ( !components_.empty() )
561  {
562  Items::Item* item = components_.back().get();
563  if ( Plib::systemstate.config.loglevel >= 5 )
564  POLLOG.Format( "Destroying component 0x{:X}, serial=0x{:X}\n" )
565  << item->objtype_ << item->serial;
566  if ( !item->orphan() )
567  Core::destroy_item( item );
568  if ( Plib::systemstate.config.loglevel >= 5 )
569  POLLOG << "Component destroyed\n";
570  components_.pop_back();
571  }
572 }
573 
574 bool UHouse::readshapes( Plib::MapShapeList& vec, short shape_x, short shape_y, short zbase )
575 {
576  if ( !custom )
577  return false;
578 
579  bool result = false;
580  HouseFloorZColumn* elems;
581  HouseFloorZColumn::iterator itr;
582  CustomHouseDesign* design;
583  design =
584  editing
585  ? &WorkingDesign
586  : &CurrentDesign; // consider having a list of players that should use the working set
587 
588  if ( shape_x + design->xoff < 0 || shape_x + design->xoff >= static_cast<s32>( design->width ) ||
589  shape_y + design->yoff < 0 || shape_y + design->yoff >= static_cast<s32>( design->height ) )
590  return false;
591  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
592  {
593  elems = design->Elements[i].GetElementsAt( shape_x, shape_y );
594  for ( itr = elems->begin(); itr != elems->end(); ++itr )
595  {
596  Plib::MapShape shape;
597  shape.z = itr->z + zbase;
598  shape.height = Core::tileheight( itr->graphic );
599  shape.flags = Core::tile_flags( itr->graphic );
600  if ( !shape.height )
601  {
602  ++shape.height;
603  --shape.z;
604  }
605  vec.push_back( shape );
606  result = true;
607  }
608  }
609  return result;
610 }
611 
612 bool UHouse::readobjects( Core::StaticList& vec, short obj_x, short obj_y, short zbase )
613 {
614  if ( !custom )
615  return false;
616 
617  bool result = false;
618  HouseFloorZColumn* elems;
619  HouseFloorZColumn::iterator itr;
620  CustomHouseDesign* design;
621  design =
622  editing
623  ? &WorkingDesign
624  : &CurrentDesign; // consider having a list of players that should use the working set
625 
626  if ( obj_x + design->xoff < 0 || obj_x + design->xoff >= static_cast<s32>( design->width ) ||
627  obj_y + design->yoff < 0 || obj_y + design->yoff >= static_cast<s32>( design->height ) )
628  return false;
629  for ( int i = 0; i < CUSTOM_HOUSE_NUM_PLANES; i++ )
630  {
631  elems = design->Elements[i].GetElementsAt( obj_x, obj_y );
632  for ( itr = elems->begin(); itr != elems->end(); ++itr )
633  {
634  Core::StaticRec rec( itr->graphic, static_cast<signed char>( itr->z + zbase ),
635  Core::tile_flags( itr->graphic ), Core::tileheight( itr->graphic ) );
636  if ( !rec.height )
637  {
638  ++rec.height;
639  --rec.z;
640  }
641  vec.push_back( rec );
642  result = true;
643  }
644  }
645  return result;
646 }
647 
648 // consider: storing editing char serial list on the house, to validate who should see the working
649 // set
651 {
652  Mobile::Character* chr = Core::find_character( chrserial );
653  if ( chr == nullptr )
654  return nullptr;
655  if ( chr->client == nullptr )
656  return nullptr;
657 
658  u32 house_serial = chr->client->gd->custom_house_serial;
659 
660  UMulti* multi = Core::system_find_multi( house_serial );
661  if ( multi == nullptr )
662  return nullptr;
663 
664  UHouse* house = multi->as_house();
665  if ( house == nullptr )
666  return nullptr;
667 
668  return house;
669 }
670 
671 // fixme realm
672 bool multis_exist_in( unsigned short mywest, unsigned short mynorth, unsigned short myeast,
673  unsigned short mysouth, Realms::Realm* realm )
674 {
675  unsigned short wxL, wyL, wxH, wyH;
676 
677  Core::zone_convert_clip( mywest - 100, mynorth - 100, realm, &wxL, &wyL );
678  Core::zone_convert_clip( myeast + 100, mysouth + 100, realm, &wxH, &wyH );
679  for ( unsigned short wx = wxL; wx <= wxH; ++wx )
680  {
681  for ( unsigned short wy = wyL; wy <= wyH; ++wy )
682  {
683  for ( const auto& multi : realm->zone[wx][wy].multis )
684  {
685  const MultiDef& edef = multi->multidef();
686  // find out if any of our walls would fall within its footprint.
687  unsigned short itswest, itseast, itsnorth, itssouth;
688 
689  itswest = static_cast<unsigned short>( multi->x + edef.minrx );
690  itseast = static_cast<unsigned short>( multi->x + edef.maxrx );
691  itsnorth = static_cast<unsigned short>( multi->y + edef.minry );
692  itssouth = static_cast<unsigned short>( multi->y + edef.maxry );
693 
694  if ( mynorth >= itsnorth && mynorth <= itssouth ) // North
695  {
696  if ( ( mywest >= itswest && mywest <= itseast ) || // NW
697  ( myeast >= itswest && myeast <= itseast ) ) // NE
698  {
699  return true;
700  }
701  }
702  if ( mysouth >= itsnorth && mysouth <= itssouth ) // South
703  {
704  if ( ( mywest >= itswest && mywest <= itseast ) || // SW
705  ( myeast >= itswest && myeast <= itseast ) ) // SE
706  {
707  return true;
708  }
709  }
710 
711  if ( itsnorth >= mynorth && itsnorth <= mysouth ) // North
712  {
713  if ( ( itswest >= mywest && itswest <= myeast ) || // NW
714  ( itseast >= mywest && itseast <= myeast ) ) // NE
715  {
716  return true;
717  }
718  }
719  if ( itssouth >= mynorth && itssouth <= mysouth ) // South
720  {
721  if ( ( itswest >= mywest && itswest <= myeast ) || // SW
722  ( itseast >= mywest && itseast <= myeast ) ) // SE
723  {
724  return true;
725  }
726  }
727  }
728  }
729  }
730  return false;
731 }
732 
733 bool objects_exist_in( unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2,
735 {
736  unsigned short wxL, wyL, wxH, wyH;
737  Core::zone_convert_clip( x1, y1, realm, &wxL, &wyL );
738  Core::zone_convert_clip( x2, y2, realm, &wxH, &wyH );
739  auto includes = [&]( const Core::UObject* obj ) {
740  if ( obj->x >= x1 && obj->x <= x2 && obj->y >= y1 && obj->y <= y2 )
741  {
742  return true;
743  }
744  return false;
745  };
746  for ( unsigned short wx = wxL; wx <= wxH; ++wx )
747  {
748  for ( unsigned short wy = wyL; wy <= wyH; ++wy )
749  {
750  for ( const auto& chr : realm->zone[wx][wy].characters )
751  {
752  if ( includes( chr ) )
753  return true;
754  }
755  for ( const auto& chr : realm->zone[wx][wy].npcs )
756  {
757  if ( includes( chr ) )
758  return true;
759  }
760  for ( const auto& item : realm->zone[wx][wy].items )
761  {
762  if ( includes( item ) )
763  return true;
764  }
765  }
766  }
767  return false;
768 }
769 
770 bool statics_cause_problems( unsigned short x1, unsigned short y1, unsigned short x2,
771  unsigned short y2, s8 z, int /*flags*/, Realms::Realm* realm )
772 {
773  for ( unsigned short x = x1; x <= x2; ++x )
774  {
775  for ( unsigned short y = y1; y <= y2; ++y )
776  {
777  short newz;
778  UMulti* multi;
779  Items::Item* item;
780  if ( !realm->walkheight( x, y, z, &newz, &multi, &item, true, Core::MOVEMODE_LAND ) )
781  {
782  POLLOG.Format( "Refusing to place house at {},{},{}: can't stand there\n" ) << x << y << z;
783  return true;
784  }
785  if ( labs( z - newz ) > 2 )
786  {
787  POLLOG.Format( "Refusing to place house at {},{},{}: result Z ({}) is too far afield\n" )
788  << x << y << z << newz;
789  return true;
790  }
791  }
792  }
793  return false;
794 }
795 
797  Realms::Realm* realm, int flags )
798 {
799  const MultiDef* md = MultiDefByMultiID( descriptor.multiid );
800  if ( md == nullptr )
801  {
802  return new Bscript::BError(
803  "Multi definition not found for House, objtype=" + Clib::hexint( descriptor.objtype ) +
804  ", multiid=" + Clib::hexint( descriptor.multiid ) );
805  }
806 
807  if ( ( !realm->valid( x + md->minrx, y + md->minry, z + md->minrz ) ) ||
808  ( !realm->valid( x + md->maxrx, y + md->maxry, z + md->maxrz ) ) )
809  return new Bscript::BError( "That location is out of bounds" );
810 
811  if ( ~flags & CRMULTI_IGNORE_MULTIS )
812  {
813  if ( multis_exist_in( x + md->minrx - 1, y + md->minry - 5, x + md->maxrx + 1,
814  y + md->maxry + 5, realm ) )
815  {
816  return new Bscript::BError( "Location intersects with another structure" );
817  }
818  }
819 
820  if ( ~flags & CRMULTI_IGNORE_OBJECTS )
821  {
822  if ( objects_exist_in( x + md->minrx, y + md->minry, x + md->maxrx, y + md->maxry, realm ) )
823  {
824  return new Bscript::BError( "Something is blocking that location" );
825  }
826  }
827  if ( ~flags & CRMULTI_IGNORE_FLATNESS )
828  {
829  if ( statics_cause_problems( x + md->minrx - 1, y + md->minry - 1, x + md->maxrx + 1,
830  y + md->maxry + 1, z, flags, realm ) )
831  {
832  return new Bscript::BError( "That location is not suitable" );
833  }
834  }
835 
836  UHouse* house = new UHouse( descriptor );
838  house->serial_ext = ctBEu32( house->serial );
839  house->x = x;
840  house->y = y;
841  house->z = z;
842  house->realm = realm;
843  send_multi_to_inrange( house );
844  // update_item_to_inrange( house );
845  add_multi_to_world( house );
846  house->create_components();
847 
851 
852  return house->make_ref();
853 }
854 
855 
857 {
858  item->set_dirty();
859  item->movable( true );
860  item->set_decay_after( 60 ); // just a dummy in case decay=0
861  item->restart_decay_timer();
862  for ( unsigned short xd = 0; xd < 5; ++xd )
863  {
864  for ( unsigned short yd = 0; yd < 5; ++yd )
865  {
866  Items::Item* walkon;
867  UMulti* multi;
868  short newz;
869  unsigned short sx = item->x;
870  unsigned short sy = item->y;
871  item->x = 0; // move 'self' a bit so it doesn't interfere with itself
872  item->y = 0;
873  bool res = item->realm->walkheight( sx + xd, sy + yd, item->z, &newz, &multi, &walkon, true,
875  item->x = sx;
876  item->y = sy;
877  if ( res )
878  {
879  move_item( item, item->x + xd, item->y + yd, static_cast<signed char>( newz ), nullptr );
880  return;
881  }
882  }
883  }
884  short newz;
885  if ( item->realm->groundheight( item->x, item->y, &newz ) )
886  {
887  move_item( item, item->x, item->y, static_cast<signed char>( newz ), nullptr );
888  return;
889  }
890 }
891 
892 
894 {
895  move_character_to( chr, chr->x, chr->y, chr->z, Core::MOVEITEM_FORCELOCATION, nullptr );
896 }
897 
898 // void send_remove_object_if_inrange( Client *client, const UObject *item );
899 
901 {
902  if ( house->IsEditing() )
903  return new Bscript::BError( "House currently being customized." );
904 
905  house->destroy_components();
906 
907  ItemList item_contents;
908  MobileList chr_contents;
909  UHouse::list_contents( house, item_contents, chr_contents );
910 
912  remove_multi_from_world( house );
913 
914  while ( !item_contents.empty() )
915  {
916  Items::Item* item = item_contents.front();
917  move_to_ground( item );
918 
919  item_contents.pop_front();
920  }
921 
922  while ( !chr_contents.empty() )
923  {
924  Mobile::Character* chr = chr_contents.back();
925  move_to_ground( chr );
926  chr->registered_house = 0;
927  chr_contents.pop_back();
928  }
929 
930  house->ClearSquatters();
931 
932  house->destroy();
933 
934  return new Bscript::BLong( 1 );
935 }
936 
938 {
939  if ( !obj->ismobile() )
940  return;
941  if ( find( squatters_.begin(), squatters_.end(), obj ) == squatters_.end() )
942  {
943  set_dirty();
944  squatters_.push_back( Squatter( obj ) );
945  }
946 }
947 
949 {
950  Squatters::iterator this_squatter = find( squatters_.begin(), squatters_.end(), obj );
951 
952  if ( this_squatter != squatters_.end() )
953  {
954  set_dirty();
955  squatters_.erase( this_squatter );
956  }
957 }
958 
960 {
961  squatters_.clear();
962 }
963 
965 {
967  if ( !itemdesc.walk_on_script.empty() )
968  {
970  prog = find_script2( itemdesc.walk_on_script,
971  true, // complain if not found
973  if ( prog.get() != nullptr )
974  {
975  std::unique_ptr<Core::UOExecutor> ex( Core::create_script_executor() );
976  ex->addModule( new Module::UOExecutorModule( *ex ) );
977  if ( prog->haveProgram )
978  {
979  ex->pushArg( new Bscript::BLong( chr->lastz ) );
980  ex->pushArg( new Bscript::BLong( chr->lasty ) );
981  ex->pushArg( new Bscript::BLong( chr->lastx ) );
982  ex->pushArg( new Module::EItemRefObjImp( this ) );
983  ex->pushArg( new Module::ECharacterRefObjImp( chr ) );
984  }
985 
986  ex->os_module->priority = 100;
987 
988  if ( ex->setProgram( prog.get() ) )
989  {
990  schedule_executor( ex.release() );
991  }
992  }
993  }
994 }
995 
997 {
998  waiting_for_accept = false;
999  if ( accept )
1000  {
1001  revision++;
1002 
1003  // commit working design to current design
1005 
1006  // invalidate old packet
1007  std::vector<u8> newvec;
1008  CurrentCompressed.swap( newvec );
1009 
1010  CustomHouseStopEditing( chr, this );
1011 
1012  // send full house
1014  }
1015  else
1016  {
1017  WorkingDesign.AddComponents( this );
1019  if ( chr && chr->client )
1021  }
1022 }
1023 }
1024 }
bool empty() const
Definition: scrdef.h:41
unsigned char u8
Definition: rawtypes.h:25
Core::UObjectRef Squatter
Definition: house.h:138
u16 wyH
Definition: uworld.h:181
static Item * create(u32 objtype, u32 serial=0)
Definition: itemcr.cpp:53
CustomHouseDesign WorkingDesign
Definition: house.h:79
virtual void register_object(Core::UObject *obj) POL_OVERRIDE
Definition: house.cpp:937
virtual void walk_on(Mobile::Character *chr) POL_OVERRIDE
Definition: house.cpp:964
unsigned int tile_flags(unsigned short tilenum)
Definition: polfile2.cpp:49
std::list< CUSTOM_HOUSE_ELEMENT > HouseFloorZColumn
Definition: customhouses.h:77
bool waiting_for_accept
Definition: house.h:91
void add_component_no_check(Component item)
Definition: house.h:152
ref_ptr< Bscript::EScriptProgram > find_script2(const ScriptDef &script, bool complain_if_not_found, bool cache_script)
Definition: scrstore.cpp:83
void Add(CUSTOM_HOUSE_ELEMENT &elem)
const int CRMULTI_IGNORE_OBJECTS
Definition: multi.h:46
std::list< Items::Item * > ItemList
Definition: house.h:60
bool IsEditing() const
Definition: house.h:88
virtual Bscript::BObjectImp * script_method_id(const int id, Bscript::Executor &ex) POL_OVERRIDE
Definition: house.cpp:298
virtual Bscript::BObjectImp * script_method_id(const int id, Bscript::Executor &ex) POL_OVERRIDE
Definition: uoscrobj.cpp:1325
Components components_
Definition: house.h:165
void add_item_to_world(Items::Item *item)
Definition: uworld.cpp:31
unsigned int flags
Definition: mapshape.h:20
std::vector< StaticRec > StaticList
Definition: udatfile.h:38
Bscript::ObjArray * items_list() const
Definition: house.cpp:214
BApplicObjType eitemrefobjimp_type
Definition: uoscrobj.cpp:125
virtual Bscript::BObjectImp * script_method(const char *membername, Bscript::Executor &ex) POL_OVERRIDE
Definition: house.cpp:470
static void list_contents(const UHouse *house, ItemList &items_in, MobileList &chrs_in)
Definition: house.cpp:63
void create_components()
Definition: house.cpp:117
SystemState systemstate
Definition: systemstate.cpp:12
Network::Client * client
Definition: charactr.h:871
static UHouse * FindWorkingHouse(u32 chrserial)
Definition: house.cpp:650
Core::PolConfig config
Definition: systemstate.h:43
void SetCustom(bool custom)
#define RANGE_VISUAL_LARGE_BUILDINGS
Definition: uconst.h:73
u16 wyL
Definition: uworld.h:179
virtual void unregister_object(Core::UObject *obj) POL_OVERRIDE
Definition: house.cpp:948
virtual Bscript::BObjectImp * get_script_member(const char *membername) const POL_OVERRIDE
Definition: house.cpp:289
bool walkheight(unsigned short x, unsigned short y, short oldz, short *newz, Multi::UMulti **pmulti, Items::Item **pwalkon, bool doors_block, Core::MOVEMODE movemode, short *gradual_boost=nullptr)
Definition: realmfunc.cpp:340
Squatters squatters_
Definition: house.h:140
const int CRMULTI_IGNORE_MULTIS
Definition: multi.h:45
Items::Item * find_toplevel_item(u32 serial)
Definition: fnsearch.cpp:69
UObject(u32 objtype, UOBJ_CLASS uobj_class)
Definition: uobject.cpp:72
T * get() const
Definition: refptr.h:176
ObjMember * getKnownObjMember(const char *token)
Definition: parser.cpp:483
bool move_character_to(Mobile::Character *chr, unsigned short x, unsigned short y, short z, int flags, Realms::Realm *oldrealm)
Definition: core.cpp:50
virtual void printProperties(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: item.cpp:337
std::list< Mobile::Character * > MobileList
Definition: house.h:61
Bscript::ObjArray * component_list() const
Definition: house.cpp:199
void disable_decay()
Definition: item.cpp:987
Core::ItemRef Component
Definition: house.h:70
CustomHouseDesign CurrentDesign
Definition: house.h:78
void send_multi_to_inrange(const Multi::UMulti *multi)
Definition: ufunc.cpp:1659
Bscript::ObjArray * list_parts() const
HouseFloorZColumn * GetElementsAt(s32 xoffset, s32 yoffset)
#define POLLOG_ERROR
Definition: logfacility.h:207
std::vector< u8 > CurrentCompressed
Definition: house.h:81
bool multis_exist_in(unsigned short mywest, unsigned short mynorth, unsigned short myeast, unsigned short mysouth, Realms::Realm *realm)
Definition: house.cpp:672
CustomHouseDesign BackupDesign
Definition: house.h:80
ObjMethod * getKnownObjMethod(const char *token)
Definition: parser.cpp:666
void CustomHouseStopEditing(Mobile::Character *chr, UHouse *house)
const ItemDesc & find_itemdesc(unsigned int objtype)
Definition: itemdesc.cpp:933
void AddComponents(UHouse *house)
unsigned short multiid
Definition: itemdesc.h:128
void add_multi_to_world(Multi::UMulti *multi)
Definition: uworld.cpp:69
static Bscript::BObjectImp * scripted_create(const Items::ItemDesc &descriptor, u16 x, u16 y, s8 z, Realms::Realm *realm, int flags)
Definition: house.cpp:796
virtual bool readshapes(Plib::MapShapeList &vec, short shape_x, short shape_y, short zbase) POL_OVERRIDE
Definition: house.cpp:574
ZoneCharacters characters
Definition: uworld.h:70
void set_dirty()
Definition: uobject.h:291
std::string hexint(unsigned short v)
Definition: strutil.cpp:23
virtual void readProperties(Clib::ConfigElem &elem) POL_OVERRIDE
Definition: item.cpp:434
bool EraseGraphicAt(u16 graphic, u32 xoffset, u32 yoffset, u8 z)
unsigned short u16
Definition: rawtypes.h:26
virtual size_t estimatedSize() const POL_OVERRIDE
Definition: house.cpp:98
unsigned int u32
Definition: rawtypes.h:27
void zone_convert_clip(int x, int y, const Realms::Realm *realm, unsigned short *wx, unsigned short *wy)
Definition: uworld.h:86
bool getCharacterParam(Bscript::Executor &exec, unsigned param, Mobile::Character *&chrptr)
Definition: uoexhelp.cpp:140
virtual bool readobjects(Core::StaticList &vec, short obj_x, short obj_y, short zbase) POL_OVERRIDE
Definition: house.cpp:612
bool objects_exist_in(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2, Realms::Realm *realm)
Definition: house.cpp:733
Multi::UMulti * system_find_multi(u32 serial)
Definition: fnsearch.cpp:50
virtual Bscript::BObjectImp * get_script_member_id(const int id) const POL_OVERRIDE
id test
Definition: house.cpp:249
Bscript::ObjArray * mobiles_list() const
Definition: house.cpp:231
virtual void destroy()
Definition: uobject.cpp:122
virtual class UHouse * as_house()
Definition: multis.cpp:55
signed short s16
Definition: rawtypes.h:30
void destroy_item(Item *item)
Definition: ufunc.cpp:1538
Bscript::BObjectImp * destroy_house(UHouse *house)
Definition: house.cpp:900
UOExecutor * create_script_executor()
Definition: scrsched.cpp:644
u16 wxH
Definition: uworld.h:180
Definition: refptr.h:65
void InitDesign(u32 _height, u32 _width, s32 xoffset, s32 yoffset)
u32 GetNewItemSerialNumber(void)
Definition: ufunc.cpp:142
Core::ScriptDef walk_on_script
Definition: itemdesc.h:96
void printProperties(Clib::StreamWriter &sw, const std::string &prefix) const
const int CRMULTI_IGNORE_FLATNESS
Definition: multi.h:47
void set_decay_after(unsigned int seconds)
Definition: item.cpp:954
void CustomHousesQuit(Mobile::Character *chr, bool drop_changes)
bool statics_cause_problems(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2, s8 z, int, Realms::Realm *realm)
Definition: house.cpp:770
bool orphan() const
Definition: baseobject.h:119
std::vector< MULTI_ELEM > elems
Definition: multidef.h:68
char tileheight(unsigned short tilenum)
Definition: polfile2.cpp:34
void AcceptHouseCommit(Mobile::Character *chr, bool accept)
Definition: house.cpp:996
void ClearSquatters()
Definition: house.cpp:959
bool IsCustom() const
Definition: house.h:84
UHouse(const Items::ItemDesc &itemdesc)
Definition: house.cpp:88
signed int s32
Definition: rawtypes.h:31
#define POLLOG
Definition: logfacility.h:219
virtual void readProperties(Clib::ConfigElem &elem) POL_OVERRIDE
Definition: house.cpp:479
signed char s8
Definition: rawtypes.h:29
ClientGameData * gd
Definition: client.h:252
const u32 objtype_
Definition: uobject.h:249
void send_remove_object_to_inrange(const UObject *centerObject)
Definition: ufunc.cpp:428
Mobile::Character * find_character(u32 serial)
Definition: fnsearch.cpp:60
void move_item(Item *item, UFACING facing)
Definition: ufunc.cpp:1601
#define passert_always_r(exp, reason)
Definition: passert.h:84
bool cache_interactive_scripts
Definition: polcfg.h:51
#define ctBEu32(x)
Definition: clib_endian.h:45
void CustomHousesSendFullToInRange(UHouse *house, int design, int range)
bool movable() const
Definition: item.h:300
void destroy_components()
Definition: house.cpp:558
bool Insert(UObject *obj)
Definition: objecthash.cpp:30
virtual Bscript::BObjectImp * get_script_member_id(const int id) const POL_OVERRIDE
id test
Definition: multis.cpp:87
const int MOVEITEM_FORCELOCATION
Definition: core.h:52
bool remove_prop(const char *propname, std::string *value)
Definition: cfgfile.cpp:128
ZoneCharacters npcs
Definition: uworld.h:71
ZoneItems items
Definition: uworld.h:72
signed char z
Definition: udatfile.h:22
void restart_decay_timer()
Definition: item.cpp:974
BApplicObjBase * getApplicObjParam(unsigned param, const BApplicObjType *object_type)
Definition: executor.cpp:488
std::vector< u8 > WorkingCompressed
Definition: house.h:82
virtual size_t estimatedSize() const POL_OVERRIDE
Definition: multis.cpp:112
Realms::Realm * realm
Definition: baseobject.h:56
ObjectStorageManager objStorageManager
bool groundheight(unsigned short x, unsigned short y, short *z) const
Definition: realmfunc.cpp:760
virtual void printProperties(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: house.cpp:533
CustomHouseElements Elements[CUSTOM_HOUSE_NUM_PLANES]
Definition: customhouses.h:143
unsigned short remove_ushort(const char *propname)
Definition: cfgfile.cpp:318
virtual class UHouse * as_house() POL_OVERRIDE
Definition: house.cpp:244
Core::Zone ** zone
Definition: realm.h:133
bool hasParams(unsigned howmany) const
Definition: executor.h:144
bool can_add_component(const Items::Item *item)
Definition: house.h:148
void move_to_ground(Items::Item *item)
Definition: house.cpp:856
ZoneMultis multis
Definition: uworld.h:73
virtual Bscript::BObjectImp * make_ref() POL_OVERRIDE
Definition: uoscrobj.cpp:3521
void schedule_executor(UOExecutor *ex)
Definition: scrsched.cpp:662
const MultiDef & multidef() const
Definition: multis.cpp:69
void CustomHousesSendFull(UHouse *house, Network::Client *client, int design)
bool valid(unsigned short x, unsigned short y, short z) const
Definition: realm.cpp:119
const ItemDesc & itemdesc() const
Definition: item.cpp:127
u16 wxL
Definition: uworld.h:178
void remove_multi_from_world(Multi::UMulti *multi)
Definition: uworld.cpp:76
#define CUSTOM_HOUSE_NUM_PLANES
Definition: customhouses.h:52
bool IsWaitingForAccept() const
Definition: house.h:89
#define pf_endl
Definition: proplist.cpp:25
void readProperties(Clib::ConfigElem &elem, const std::string &prefix)
Definition: berror.cpp:12
bool remove_bool(const char *propname)
Definition: cfgfile.cpp:426
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
static void ClearComponents(UHouse *house)
unsigned short objtype
Definition: multidef.h:46
void update_item_to_inrange(const Item *item)
Definition: ufunc.cpp:737
bool getParam(unsigned param, int &value)
Definition: executor.cpp:363
static void InBox(u16 x1, u16 y1, u16 x2, u16 y2, const Realms::Realm *realm, F &&f)
Definition: uworld.h:252