Pol  Revision:cb584c9
boat.cpp
Go to the documentation of this file.
1 
19 #include "boat.h"
20 
21 #include <exception>
22 #include <string>
23 
24 #include "../../bscript/berror.h"
25 #include "../../clib/cfgelem.h"
26 #include "../../clib/cfgfile.h"
27 #include "../../clib/clib_endian.h"
28 #include "../../clib/logfacility.h"
29 #include "../../clib/passert.h"
30 #include "../../clib/stlutil.h"
31 #include "../../clib/streamsaver.h"
32 #include "../../plib/systemstate.h"
33 #include "../containr.h"
34 #include "../extobj.h"
35 #include "../fnsearch.h"
36 #include "../globals/object_storage.h"
37 #include "../globals/settings.h"
38 #include "../globals/uvars.h"
39 #include "../item/item.h"
40 #include "../item/itemdesc.h"
41 #include "../mdelta.h"
42 #include "../mkscrobj.h"
43 #include "../mobile/charactr.h"
44 #include "../network/client.h"
45 #include "../network/packethelper.h"
46 #include "../network/packets.h"
47 #include "../pktdef.h"
48 #include "../polvar.h"
49 #include "../realms/realm.h"
50 #include "../scrsched.h"
51 #include "../tiles.h"
52 #include "../uconst.h"
53 #include "../ufunc.h"
54 #include "../uobject.h"
55 #include "../uworld.h"
56 #include "boatcomp.h"
57 #include "multi.h"
58 #include "multidef.h"
59 
60 
61 namespace Pol
62 {
63 namespace Multi
64 {
65 //#define DEBUG_BOATS
66 
67 std::vector<Network::Client*> boat_sent_to;
68 
69 BoatShape::ComponentShape::ComponentShape( const std::string& str, unsigned char type )
70 {
71  altgraphic = 0;
73  ISTRINGSTREAM is( str );
74  std::string tmp;
75  if ( is >> tmp )
76  {
77  graphic = static_cast<unsigned short>( strtoul( tmp.c_str(), nullptr, 0 ) );
78  if ( graphic )
79  {
80  unsigned short xd, yd;
81  if ( is >> xd >> yd )
82  {
83  xdelta = xd;
84  ydelta = yd;
85  zdelta = 0;
86  signed short zd;
87  if ( is >> zd )
88  zdelta = zd;
89  return;
90  }
91  }
92  }
93 
94  ERROR_PRINT << "Boat component definition '" << str << "' is poorly formed.\n";
95  throw std::runtime_error( "Poorly formed boat.cfg component definition" );
96 }
97 
98 BoatShape::ComponentShape::ComponentShape( const std::string& str, const std::string& altstr,
99  unsigned char type )
100 {
101  altgraphic = 0;
102  bool ok = false;
103  objtype = get_component_objtype( type );
104  ISTRINGSTREAM is( str );
105  std::string tmp;
106  if ( is >> tmp )
107  {
108  graphic = static_cast<unsigned short>( strtoul( tmp.c_str(), nullptr, 0 ) );
109  if ( graphic )
110  {
111  unsigned short xd, yd;
112  if ( is >> xd >> yd )
113  {
114  xdelta = xd;
115  ydelta = yd;
116  zdelta = 0;
117  signed short zd;
118  if ( is >> zd )
119  zdelta = zd;
120  ok = true;
121  }
122  }
123  }
124 
125  ISTRINGSTREAM altis( altstr );
126  std::string alttmp;
127  if ( ok && altis >> alttmp )
128  {
129  altgraphic = static_cast<unsigned short>( strtoul( alttmp.c_str(), nullptr, 0 ) );
130  return;
131  }
132  else
133  ok = false;
134 
135  if ( !ok )
136  {
137  ERROR_PRINT << "Boat component definition '" << str << "' is poorly formed.\n";
138  throw std::runtime_error( "Poorly formed boat.cfg component definition" );
139  }
140 }
141 
142 
145 {
146  std::string tmp_str;
147 
148  while ( elem.remove_prop( "Tillerman", &tmp_str ) )
149  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_TILLERMAN ) );
150  while ( elem.remove_prop( "PortGangplankRetracted", &tmp_str ) )
151  Componentshapes.push_back( ComponentShape(
152  tmp_str, elem.remove_string( "PortGangplankExtended" ), COMPONENT_PORT_PLANK ) );
153  while ( elem.remove_prop( "StarboardGangplankRetracted", &tmp_str ) )
154  Componentshapes.push_back( ComponentShape(
155  tmp_str, elem.remove_string( "StarboardGangplankExtended" ), COMPONENT_STARBOARD_PLANK ) );
156  while ( elem.remove_prop( "Hold", &tmp_str ) )
157  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_HOLD ) );
158  while ( elem.remove_prop( "Rope", &tmp_str ) )
159  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_ROPE ) );
160  while ( elem.remove_prop( "Wheel", &tmp_str ) )
161  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_WHEEL ) );
162  while ( elem.remove_prop( "Hull", &tmp_str ) )
163  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_HULL ) );
164  while ( elem.remove_prop( "Tiller", &tmp_str ) )
165  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_TILLER ) );
166  while ( elem.remove_prop( "Rudder", &tmp_str ) )
167  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_RUDDER ) );
168  while ( elem.remove_prop( "Sails", &tmp_str ) )
169  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_SAILS ) );
170  while ( elem.remove_prop( "Storage", &tmp_str ) )
171  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_STORAGE ) );
172  while ( elem.remove_prop( "Weaponslot", &tmp_str ) )
173  Componentshapes.push_back( ComponentShape( tmp_str, COMPONENT_WEAPONSLOT ) );
174 }
175 
177 {
178  if ( objtype == Core::settingsManager.extobj.tillerman )
179  return true;
180  else if ( objtype == Core::settingsManager.extobj.port_plank )
181  return true;
182  else if ( objtype == Core::settingsManager.extobj.starboard_plank )
183  return true;
184  else if ( objtype == Core::settingsManager.extobj.hold )
185  return true;
186  else if ( objtype == Core::settingsManager.extobj.rope )
187  return true;
188  else if ( objtype == Core::settingsManager.extobj.wheel )
189  return true;
190  else if ( objtype == Core::settingsManager.extobj.hull )
191  return true;
192  else if ( objtype == Core::settingsManager.extobj.tiller )
193  return true;
194  else if ( objtype == Core::settingsManager.extobj.rudder )
195  return true;
196  else if ( objtype == Core::settingsManager.extobj.sails )
197  return true;
198  else if ( objtype == Core::settingsManager.extobj.storage )
199  return true;
200  else if ( objtype == Core::settingsManager.extobj.weaponslot )
201  return true;
202  else
203  return false;
204 }
205 
207 {
208  return 3 * sizeof( ComponentShape* ) + Componentshapes.capacity() * sizeof( ComponentShape );
209 }
210 
211 unsigned int get_component_objtype( unsigned char type )
212 {
213  switch ( type )
214  {
215  case COMPONENT_TILLERMAN:
221  case COMPONENT_HOLD:
223  case COMPONENT_ROPE:
225  case COMPONENT_WHEEL:
227  case COMPONENT_HULL:
229  case COMPONENT_TILLER:
231  case COMPONENT_RUDDER:
233  case COMPONENT_SAILS:
235  case COMPONENT_STORAGE:
239  default:
240  return 0;
241  }
242 }
243 
244 void read_boat_cfg( void )
245 {
246  Clib::ConfigFile cf( "config/boats.cfg", "Boat" );
247  Clib::ConfigElem elem;
248  while ( cf.read( elem ) )
249  {
250  unsigned short multiid = elem.remove_ushort( "MultiID" );
251  try
252  {
253  Core::gamestate.boatshapes[multiid] = new BoatShape( elem );
254  }
255  catch ( std::exception& )
256  {
257  ERROR_PRINT << "Error occurred reading definition for boat 0x" << fmt::hexu( multiid )
258  << "\n";
259  throw;
260  }
261  }
262 }
263 
265 {
266  Core::BoatShapes::iterator iter = Core::gamestate.boatshapes.begin();
267  for ( ; iter != Core::gamestate.boatshapes.end(); ++iter )
268  {
269  if ( iter->second != nullptr )
270  delete iter->second;
271  iter->second = nullptr;
272  }
273  Core::gamestate.boatshapes.clear();
274 }
275 
276 bool BoatShapeExists( u16 multiid )
277 {
278  return Core::gamestate.boatshapes.count( multiid ) != 0;
279 }
280 
281 void UBoat::send_smooth_move( Network::Client* client, Core::UFACING move_dir, u8 speed, u16 newx,
282  u16 newy, bool relative )
283 {
285 
286  u16 xmod = newx - x;
287  u16 ymod = newy - y;
288  Core::UFACING b_facing = boat_facing();
289 
290  if ( relative == false )
291  move_dir = static_cast<Core::UFACING>( ( b_facing + move_dir ) * 7 );
292 
293  msg->offset += 2; // Length
294  msg->Write<u32>( serial_ext );
295  msg->Write<u8>( speed );
296 
297  msg->Write<u8>( move_dir );
298  msg->Write<u8>( b_facing );
299 
300  msg->WriteFlipped<u16>( newx );
301  msg->WriteFlipped<u16>( newy );
302  msg->WriteFlipped<u16>( ( z < 0 ) ? static_cast<u16>( 0x10000 + z ) : static_cast<u16>( z ) );
303 
304  u16 object_count = static_cast<u16>( travellers_.size() + Components.size() );
305 
306  msg->WriteFlipped<u16>( object_count );
307 
308  for ( auto& travellerRef : travellers_ )
309  {
310  UObject* obj = travellerRef.get();
311 
312  if ( !obj->orphan() )
313  {
314  msg->Write<u32>( obj->serial_ext );
315  msg->WriteFlipped<u16>( static_cast<u16>( obj->x + xmod ) );
316  msg->WriteFlipped<u16>( static_cast<u16>( obj->y + ymod ) );
317  msg->WriteFlipped<u16>(
318  static_cast<u16>( ( obj->z < 0 ) ? ( 0x10000 + obj->z ) : ( obj->z ) ) );
319  }
320  }
321 
322  for ( auto& component : Components )
323  {
324  if ( component != nullptr && !component->orphan() )
325  {
326  msg->Write<u32>( component->serial_ext );
327  msg->WriteFlipped<u16>( static_cast<u16>( component->x + xmod ) );
328  msg->WriteFlipped<u16>( static_cast<u16>( component->y + ymod ) );
329  msg->WriteFlipped<u16>( static_cast<u16>( ( component->z < 0 ) ? ( 0x10000 + component->z )
330  : ( component->z ) ) );
331  }
332  }
333 
334  u16 len = msg->offset;
335 
336  msg->offset = 1;
337  msg->WriteFlipped<u16>( len );
338 
339  msg.Send( client, len );
340 }
341 
342 void UBoat::send_smooth_move_to_inrange( Core::UFACING move_dir, u8 speed, u16 newx, u16 newy,
343  bool relative )
344 {
346  newx, newy, realm, RANGE_VISUAL_LARGE_BUILDINGS, [&]( Mobile::Character* zonechr ) {
347  Network::Client* client = zonechr->client;
348 
349  if ( inrange( client->chr, this ) &&
350  client->ClientType & Network::CLIENTTYPE_7090 ) // send this only to those who see the
351  // old location aswell
352  send_smooth_move( client, move_dir, speed, newx, newy, relative );
353  } );
354 }
355 
357 {
359 
360  msg->offset += 2; // Length
361 
362  u16 inner_packet_count =
363  static_cast<u16>( travellers_.size() + Components.size() + 1 ); // Add 1 for the boat aswell
364 
365  msg->WriteFlipped<u16>( inner_packet_count );
366 
367  // Build boat part
368 
369  msg->Write<u8>( 0xF3u );
370  msg->WriteFlipped<u16>( 0x1u );
371  msg->Write<u8>( 0x2u ); // MultiData flag
372  msg->Write<u32>( this->serial_ext );
373  msg->WriteFlipped<u16>( this->multidef().multiid );
374  msg->offset++; // ID offset, TODO CHECK IF NEED THESE
375  msg->WriteFlipped<u16>( 0x1u ); // Amount
376  msg->WriteFlipped<u16>( 0x1u ); // Amount
377  msg->WriteFlipped<u16>( this->x );
378  msg->WriteFlipped<u16>( this->y );
379  msg->Write<s8>( this->z );
380  msg->offset++; // facing 0 for multis
381  msg->WriteFlipped<u16>( this->color );
382  msg->offset++; // flags 0 for multis
383  msg->offset += 2; // HSA access flags, TODO find out what these are for and implement it
384 
385  u8 flags = 0;
386 
387  for ( auto& travellerRef : travellers_ )
388  {
389  UObject* obj = travellerRef.get();
390 
391  if ( !obj->orphan() )
392  {
393  msg->Write<u8>( 0xF3u );
394  msg->WriteFlipped<u16>( 0x1u );
395 
396  if ( obj->ismobile() )
397  msg->Write<u8>( 0x1u ); // CharData flag
398  else
399  msg->Write<u8>( 0x0u ); // ItemData flag
400 
401  msg->Write<u32>( obj->serial_ext );
402  msg->WriteFlipped<u16>( obj->graphic );
403  msg->offset++; // ID offset, TODO CHECK IF NEED THESE
404 
405  if ( obj->ismobile() )
406  {
407  flags = 0;
408 
409  msg->WriteFlipped<u16>( 0x1u ); // Amount
410  msg->WriteFlipped<u16>( 0x1u ); // Amount
411  }
412  else
413  {
414  Items::Item* item = static_cast<Items::Item*>( obj );
415 
416  if ( item->invisible() && !client->chr->can_seeinvisitems() )
417  {
418  send_remove_object( client, item );
419  continue;
420  }
421 
422  if ( client->chr->can_move( item ) )
423  flags |= ITEM_FLAG_FORCE_MOVABLE;
424 
425  msg->WriteFlipped<u16>( item->get_senditem_amount() ); // Amount
426  msg->WriteFlipped<u16>( item->get_senditem_amount() ); // Amount
427  }
428 
429  msg->WriteFlipped<u16>( obj->x );
430  msg->WriteFlipped<u16>( obj->y );
431  msg->Write<s8>( obj->z );
432  msg->Write<u8>( obj->facing );
433  msg->WriteFlipped<u16>( obj->color );
434 
435  msg->Write<u8>( flags ); // FLAGS
436  msg->offset += 2; // HSA access flags, TODO find out what these are for and implement it
437  }
438  }
439 
440  for ( auto& component : Components )
441  {
442  if ( component != nullptr && !component->orphan() )
443  {
444  msg->Write<u8>( 0xF3u );
445  msg->WriteFlipped<u16>( 0x1u );
446  msg->Write<u8>( 0x0u ); // ItemData flag
447  msg->Write<u32>( component->serial_ext );
448  msg->WriteFlipped<u16>( component->graphic );
449  msg->offset++; // ID offset, TODO CHECK IF NEED THESE
450  msg->WriteFlipped<u16>( component->get_senditem_amount() ); // Amount
451  msg->WriteFlipped<u16>( component->get_senditem_amount() ); // Amount
452  msg->WriteFlipped<u16>( component->x );
453  msg->WriteFlipped<u16>( component->y );
454  msg->Write<s8>( component->z );
455  msg->Write<u8>( component->facing );
456  msg->WriteFlipped<u16>( component->color );
457  msg->offset++; // FLAGS, no flags for components
458  msg->offset += 2; // HSA access flags, TODO find out what these are for and implement it
459  }
460  }
461 
462  u16 len = msg->offset;
463 
464  msg->offset = 1;
465  msg->WriteFlipped<u16>( len );
466 
467  msg.Send( client, len );
468 }
469 
471 {
473  msg->WriteFlipped<u16>( 0x1u );
474  msg->Write<u8>( 0x02u );
475  msg->Write<u32>( serial_ext );
476  msg->WriteFlipped<u16>( multidef().multiid );
477  msg->offset++; // 0;
478  msg->WriteFlipped<u16>( 0x1u ); // amount
479  msg->WriteFlipped<u16>( 0x1u ); // amount2
480  msg->WriteFlipped<u16>( x );
481  msg->WriteFlipped<u16>( y );
482  msg->Write<s8>( z );
483  msg->offset++; // u8 facing
484  msg->WriteFlipped<u16>( color ); // u16 color
485  msg->offset += 3; // u8 flags + u16 HSA access flags
486 
487  msg.Send( client );
488 
489  for ( auto& travellerRef : travellers_ )
490  {
491  UObject* obj = travellerRef.get();
492 
493  if ( !obj->orphan() )
494  {
495  if ( obj->ismobile() )
496  {
497  Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
498  send_owncreate( client, chr );
499  }
500  else
501  {
502  Items::Item* item = static_cast<Items::Item*>( obj );
503  send_item( client, item );
504  }
505  }
506  }
507 
508  for ( auto& component : Components )
509  {
510  if ( component != nullptr && !component->orphan() )
511  send_item( client, component.get() );
512  }
513 }
514 
516 {
518  x, y, realm, RANGE_VISUAL_LARGE_BUILDINGS, [&]( Mobile::Character* zonechr ) {
519  Network::Client* client = zonechr->client;
520 
521  if ( client->ClientType & Network::CLIENTTYPE_7090 )
522  send_display_boat( client );
523  else if ( client->ClientType & Network::CLIENTTYPE_7000 )
524  send_boat( client );
525  else
526  send_boat_old( client );
527  } );
528 
530  oldx, oldy, this->realm, RANGE_VISUAL_LARGE_BUILDINGS, [&]( Mobile::Character* zonechr ) {
531  Network::Client* client = zonechr->client;
532 
533  if ( !inrange( client->chr, this ) ) // send remove to chrs only seeing the old loc
534  send_remove_boat( client );
535  } );
536 }
537 
539 {
540  // Client >= 7.0.0.0 ( SA )
542  msg2->WriteFlipped<u16>( 0x1u );
543  msg2->Write<u8>( 0x02u );
544  msg2->Write<u32>( this->serial_ext );
545  msg2->WriteFlipped<u16>( this->multidef().multiid );
546  msg2->offset++; // 0;
547  msg2->WriteFlipped<u16>( 0x1u ); // amount
548  msg2->WriteFlipped<u16>( 0x1u ); // amount2
549  msg2->WriteFlipped<u16>( this->x );
550  msg2->WriteFlipped<u16>( this->y );
551  msg2->Write<s8>( this->z );
552  msg2->offset++; // u8 facing
553  msg2->WriteFlipped<u16>( this->color ); // u16 color
554  msg2->offset++; // u8 flags
555 
556  msg2.Send( client );
557 }
558 
560 {
562  msg->offset += 2;
563  u16 b_graphic = this->multidef().multiid | 0x4000;
564  msg->Write<u32>( this->serial_ext );
565  msg->WriteFlipped<u16>( b_graphic );
566  msg->WriteFlipped<u16>( this->x );
567  msg->WriteFlipped<u16>( this->y );
568  msg->Write<s8>( this->z );
569  u16 len1A = msg->offset;
570  msg->offset = 1;
571  msg->WriteFlipped<u16>( len1A );
572 
573  client->pause();
574  msg.Send( client, len1A );
575  boat_sent_to.push_back( client );
576 }
577 
579 {
580  send_remove_object( client, this );
581 }
582 
584 {
585  for ( Network::Client* client : boat_sent_to )
586  {
587  client->restart();
588  }
589  boat_sent_to.clear();
590 }
591 
592 
593 UBoat::UBoat( const Items::ItemDesc& descriptor ) : UMulti( descriptor )
594 {
595  passert( Core::gamestate.boatshapes.count( multiid ) != 0 );
596  tillerman = nullptr;
597  hold = nullptr;
598  portplank = nullptr;
599  starboardplank = nullptr;
600 }
601 
603 {
604  return this;
605 }
606 
607 /* boats are a bit strange - they can move from side to side, forwards and
608  backwards, without turning.
609  */
611 {
612  const MultiDef& md = multidef();
613  for ( MultiDef::HullList::const_iterator itr = md.hull.begin(), end = md.hull.end(); itr != end;
614  ++itr )
615  {
616  unsigned short ax = x + ( *itr )->x;
617  unsigned short ay = y + ( *itr )->y;
618 
619  unsigned int gh = realm->encode_global_hull( ax, ay );
620  realm->global_hulls.insert( gh );
621  }
622 }
623 
625 {
626  const MultiDef& md = multidef();
627  for ( MultiDef::HullList::const_iterator itr = md.hull.begin(), end = md.hull.end(); itr != end;
628  ++itr )
629  {
630  unsigned short ax = x + ( *itr )->x;
631  unsigned short ay = y + ( *itr )->y;
632 
633  unsigned int gh = realm->encode_global_hull( ax, ay );
634  realm->global_hulls.erase( gh );
635  }
636 }
637 
638 // navigable: Can the ship sit here? ie is every point on the hull on water,and not blocked?
639 bool UBoat::navigable( const MultiDef& md, unsigned short x, unsigned short y, short z,
641 {
642  if ( int( x + md.minrx ) < 0 || int( x + md.maxrx ) > int( realm->width() ) ||
643  int( y + md.minry ) < 0 || int( y + md.maxry ) > int( realm->height() ) )
644  {
645 #ifdef DEBUG_BOATS
646  INFO_PRINT << "Location " << x << "," << y << " impassable, location is off the map\n";
647 #endif
648  return false;
649  }
650 
651  /* Test the external hull to make sure it's on water */
652 
653  for ( MultiDef::HullList::const_iterator itr = md.hull.begin(), end = md.hull.end(); itr != end;
654  ++itr )
655  {
656  unsigned short ax = x + ( *itr )->x;
657  unsigned short ay = y + ( *itr )->y;
658  short az = z + ( *itr )->z;
659 #ifdef DEBUG_BOATS
660  INFO_PRINT << "[" << ax << "," << ay << "]";
661 #endif
662  /*
663  * See if any other ship hulls occupy this space
664  */
665  unsigned int gh = realm->encode_global_hull( ax, ay );
666  if ( realm->global_hulls.count( gh ) ) // already a boat there
667  {
668 #ifdef DEBUG_BOATS
669  INFO_PRINT << "Location " << realm->name() << " " << ax << "," << ay
670  << " already has a ship hull present\n";
671 #endif
672  return false;
673  }
674 
675  if ( !realm->navigable( ax, ay, az, Plib::systemstate.tile[( *itr )->objtype].height ) )
676  return false;
677  }
678 
679  return true;
680 }
681 
682 // ugh, we've moved the ship already - but we're comparing using the character's coords
683 // which used to be on the ship.
684 // let's hope it's always illegal to stand on the edges. !!
685 bool UBoat::on_ship( const BoatContext& bc, const UObject* obj )
686 {
687  if ( Core::IsItem( obj->serial ) )
688  {
689  const Item* item = static_cast<const Item*>( obj );
690  if ( item->container != nullptr )
691  return false;
692  }
693  short rx = obj->x - bc.x;
694  short ry = obj->y - bc.y;
695 
696  return bc.mdef.body_contains( rx, ry );
697 }
698 
699 void UBoat::move_travellers( Core::UFACING move_dir, const BoatContext& oldlocation,
700  unsigned short newx, unsigned short newy, Realms::Realm* oldrealm )
701 {
702  bool any_orphans = false;
703 
704  for ( auto& travellerRef : travellers_ )
705  {
706  UObject* obj = travellerRef.get();
707 
708  // consider: occasional sweeps of all boats to reap orphans
709  if ( obj->orphan() || !on_ship( oldlocation, obj ) )
710  {
711  any_orphans = true;
712  travellerRef.clear();
713  ;
714  continue;
715  }
716 
717  obj->set_dirty();
718  if ( obj->ismobile() )
719  {
720  Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
721 
722  if ( chr->logged_in() )
723  {
724  chr->lastx = chr->x;
725  chr->lasty = chr->y;
726 
727  if ( newx != USHRT_MAX &&
728  newy != USHRT_MAX ) // dave added 3/27/3, if move_xy was used, dont use facing
729  {
730  s16 dx, dy;
731  dx = chr->x - oldlocation.x; // keeps relative distance from boat mast
732  dy = chr->y - oldlocation.y;
733  chr->x = newx + dx;
734  chr->y = newy + dy;
735  }
736  else
737  {
738  chr->x += Core::move_delta[move_dir].xmove;
739  chr->y += Core::move_delta[move_dir].ymove;
740  }
741 
742  MoveCharacterWorldPosition( chr->lastx, chr->lasty, chr->x, chr->y, chr, oldrealm );
743  chr->position_changed();
744  if ( chr->client != nullptr )
745  {
746  if ( oldrealm != chr->realm )
747  {
749  Core::send_owncreate( chr->client, chr );
750  }
751 
753  {
754  Core::send_objects_newly_inrange_on_boat( chr->client, this->serial );
755 
756  if ( chr->poisoned() ) // if poisoned send 0x17 for newer clients
757  send_poisonhealthbar( chr->client, chr );
758 
759  if ( chr->invul() ) // if invul send 0x17 for newer clients
760  send_invulhealthbar( chr->client, chr );
761  }
762  else
763  {
764  Core::send_goxyz( chr->client, chr );
765  // lastx and lasty are set above so these two calls will work right.
766  // FIXME these are also called, in this order, in MOVEMENT.CPP.
767  // should be consolidated.
768  Core::send_objects_newly_inrange_on_boat( chr->client, this->serial );
769  }
770  }
772  }
773  else
774  {
775  // characters that are logged out move with the boat
776  // they aren't in the worldzones so this is real easy.
777  chr->lastx = chr->x; // I think in this case setting last? isn't
778  chr->lasty = chr->y; // necessary, but I'll do it anyway.
779 
780  if ( newx != USHRT_MAX &&
781  newy != USHRT_MAX ) // dave added 3/27/3, if move_xy was used, dont use facing
782  {
783  s16 dx, dy;
784  dx = chr->x - oldlocation.x; // keeps relative distance from boat mast
785  dy = chr->y - oldlocation.y;
786  chr->x = newx + dx;
787  chr->y = newy + dy;
788  }
789  else
790  {
791  chr->x += Core::move_delta[move_dir].xmove;
792  chr->y += Core::move_delta[move_dir].ymove;
793  }
794  }
795  }
796  else
797  {
798  Items::Item* item = static_cast<Items::Item*>( obj );
799 
800  u16 oldx, oldy;
801 
802  if ( newx != USHRT_MAX &&
803  newy != USHRT_MAX ) // dave added 4/9/3, if move_xy was used, dont use facing
804  {
805  s16 dx, dy;
806  dx = item->x - oldlocation.x; // keeps relative distance from boat mast
807  dy = item->y - oldlocation.y;
808  // Core::move_item( item, newx + dx, newy + dy, item->z, nullptr );
809 
810  item->set_dirty();
811 
812  oldx = item->x;
813  oldy = item->y;
814 
815  item->x = newx + dx;
816  item->y = newy + dy;
817 
818  item->restart_decay_timer();
819  MoveItemWorldPosition( oldx, oldy, item, oldrealm );
820  }
821  else
822  {
823  item->set_dirty();
824 
825  oldx = item->x;
826  oldy = item->y;
827 
828  item->x += Core::move_delta[move_dir].xmove;
829  item->y += Core::move_delta[move_dir].ymove;
830 
831  item->restart_decay_timer();
832  MoveItemWorldPosition( oldx, oldy, item, nullptr );
833  }
834 
836  item->x, item->y, realm, RANGE_VISUAL, [&]( Mobile::Character* zonechr ) {
837  Network::Client* client = zonechr->client;
838 
839  if ( !( client->ClientType & Network::CLIENTTYPE_7090 ) )
840  send_item( client, item );
841  } );
842 
844  oldx, oldy, oldrealm, RANGE_VISUAL, [&]( Mobile::Character* zonechr ) {
845  Network::Client* client = zonechr->client;
846 
847  if ( !inrange( client->chr,
848  item ) ) // not in range. If old loc was in range, send a delete.
849  send_remove_object( client, item );
850  } );
851  }
852  }
853 
854  if ( any_orphans )
855  remove_orphans();
856 }
857 
859 {
860  chr->lastx = chr->x;
861  chr->lasty = chr->y;
862 
863  s16 xd = chr->x - x;
864  s16 yd = chr->y - y;
865 
866  switch ( dir )
867  {
868  case LEFT:
869  chr->x = x + yd;
870  chr->y = y - xd;
871  chr->facing = static_cast<Core::UFACING>( ( chr->facing + 6 ) & 7 );
872  break;
873  case AROUND:
874  chr->x = x - xd;
875  chr->y = y - yd;
876  chr->facing = static_cast<Core::UFACING>( ( chr->facing + 4 ) & 7 );
877  break;
878  case RIGHT:
879  chr->x = x - yd;
880  chr->y = y + xd;
881  chr->facing = static_cast<Core::UFACING>( ( chr->facing + 2 ) & 7 );
882  break;
883  case NO_TURN:
884  break;
885  }
886 }
887 
888 void UBoat::turn_travellers( RELATIVE_DIR dir, const BoatContext& oldlocation )
889 {
890  bool any_orphans = false;
891 
892  for ( auto& travellerRef : travellers_ )
893  {
894  UObject* obj = travellerRef.get();
895 
896  // consider: occasional sweeps of all boats to reap orphans
897  if ( obj->orphan() || !on_ship( oldlocation, obj ) )
898  {
899  any_orphans = true;
900  travellerRef.clear();
901  continue;
902  }
903 
904  obj->set_dirty();
905  if ( obj->ismobile() )
906  {
907  Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
908  if ( chr->logged_in() )
909  {
910  // send_remove_character_to_nearby( chr );
911 
912 
913  turn_traveller_coords( chr, dir );
914 
915 
916  Core::MoveCharacterWorldPosition( chr->lastx, chr->lasty, chr->x, chr->y, chr, nullptr );
917  chr->position_changed();
918  if ( chr->client != nullptr )
919  {
921  {
922  if ( chr->poisoned() ) // if poisoned send 0x17 for newer clients
923  send_poisonhealthbar( chr->client, chr );
924 
925  if ( chr->invul() ) // if invul send 0x17 for newer clients
926  send_invulhealthbar( chr->client, chr );
927 
928  Core::send_objects_newly_inrange_on_boat( chr->client, this->serial );
929  }
930  else
931  {
932  Core::send_goxyz( chr->client, chr );
933  // lastx and lasty are set above so these two calls will work right.
934  // FIXME these are also called, in this order, in MOVEMENT.CPP.
935  // should be consolidated.
936  Core::send_objects_newly_inrange_on_boat( chr->client, this->serial );
937  }
938  }
940  // chr->lastx = ~ (unsigned short) 0; // force tellmove() to send "owncreate" and not send
941  // deletes.
942  // chr->lasty = ~ (unsigned short) 0;
943  }
944  else
945  {
946  turn_traveller_coords( chr, dir );
947  }
948  }
949  else
950  {
951  Items::Item* item = static_cast<Items::Item*>( obj );
952  s16 xd = item->x - x;
953  s16 yd = item->y - y;
954  u16 newx( 0 );
955  u16 newy( 0 );
956  switch ( dir )
957  {
958  case NO_TURN:
959  newx = item->x;
960  newy = item->y;
961  break;
962  case LEFT:
963  newx = x + yd;
964  newy = y - xd;
965  break;
966  case AROUND:
967  newx = x - xd;
968  newy = y - yd;
969  break;
970  case RIGHT:
971  newx = x - yd;
972  newy = y + xd;
973  break;
974  }
975  item->set_dirty();
976 
977  u16 oldx = item->x;
978  u16 oldy = item->y;
979 
980  item->x = newx;
981  item->y = newy;
982 
983  item->restart_decay_timer();
984 
985  MoveItemWorldPosition( oldx, oldy, item, nullptr );
986 
988  item->x, item->y, realm, RANGE_VISUAL, [&]( Mobile::Character* zonechr ) {
989  Network::Client* client = zonechr->client;
990 
991  if ( !( client->ClientType & Network::CLIENTTYPE_7090 ) )
992  send_item( client, item );
993  } );
994 
996  oldx, oldy, realm, RANGE_VISUAL, [&]( Mobile::Character* zonechr ) {
997  Network::Client* client = zonechr->client;
998 
999  if ( !inrange( client->chr,
1000  item ) ) // not in range. If old loc was in range, send a delete.
1001  send_remove_object( client, item );
1002  } );
1003  }
1004  }
1005 
1006  if ( any_orphans )
1007  remove_orphans();
1008 }
1009 
1011 {
1012  bool any_orphan_travellers;
1013  do
1014  {
1015  any_orphan_travellers = false;
1016 
1017  for ( Travellers::iterator itr = travellers_.begin(), end = travellers_.end(); itr != end;
1018  ++itr )
1019  {
1020  UObject* obj = ( *itr ).get();
1021  if ( obj == nullptr )
1022  {
1023  set_dirty();
1024  travellers_.erase( itr );
1025  any_orphan_travellers = true;
1026  break;
1027  }
1028  }
1029  } while ( any_orphan_travellers );
1030 }
1031 
1033 {
1034  BoatContext bc( *this );
1035 
1036  for ( auto& travellerRef : travellers_ )
1037  {
1038  UObject* obj = travellerRef.get();
1039 
1040  if ( obj->orphan() || !on_ship( bc, obj ) )
1041  {
1042  set_dirty();
1043  travellerRef.clear();
1044  }
1045  }
1046  remove_orphans();
1047 }
1048 
1050 {
1051  BoatContext bc( *this );
1052 
1053  for ( const auto& travellerRef : travellers_ )
1054  {
1055  UObject* obj = travellerRef.get();
1056 
1057  if ( !obj->orphan() && on_ship( bc, obj ) && obj->ismobile() )
1058  {
1059  Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
1060 
1061  if ( !chr->logged_in() )
1062  {
1063  return true;
1064  }
1065  }
1066  }
1067  return false;
1068 }
1069 
1071  Realms::Realm* new_realm )
1072 {
1073  BoatContext bc( *this );
1074 
1075  for ( auto& travellerRef : travellers_ )
1076  {
1077  UObject* obj = travellerRef.get();
1078 
1079  if ( !obj->orphan() && on_ship( bc, obj ) && obj->ismobile() )
1080  {
1081  Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
1082 
1083  if ( !chr->logged_in() )
1084  {
1085  chr->set_dirty();
1086  chr->x = new_x;
1087  chr->y = new_y;
1088  chr->z = static_cast<signed char>( new_z );
1089  chr->realm = new_realm;
1090  chr->realm_changed(); // not sure if neccessary...
1091  travellerRef.clear();
1092  }
1093  }
1094  }
1095  remove_orphans();
1096 }
1097 
1099 {
1100  for ( auto& travellerRef : travellers_ )
1101  {
1102  UObject* obj = travellerRef.get();
1103  obj->z += delta_z;
1104  }
1105  for ( auto& component : Components )
1106  {
1107  component->z += delta_z;
1108  }
1109 }
1110 
1112 {
1114 }
1115 
1117 {
1118  BoatContext bc( *this );
1119 
1120  for ( auto& travellerRef : travellers_ )
1121  {
1122  UObject* obj = travellerRef.get();
1123 
1124  if ( !obj->orphan() && on_ship( bc, obj ) && obj->ismobile() )
1125  {
1126  Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
1128  chr->realm = realm;
1129  chr->realm_changed();
1130  }
1131  if ( !obj->orphan() && on_ship( bc, obj ) && Core::IsItem( obj->serial ) )
1132  {
1133  Items::Item* item = static_cast<Items::Item*>( obj );
1134  item->realm = realm;
1135  if ( item->isa( Core::UOBJ_CLASS::CLASS_CONTAINER ) )
1136  {
1137  Core::UContainer* cont = static_cast<Core::UContainer*>( item );
1138  cont->for_each_item( Core::setrealm, (void*)realm );
1139  }
1140  }
1141  }
1142  for ( auto& component : Components )
1143  {
1144  component->realm = realm;
1145  }
1146 }
1147 
1148 bool UBoat::deck_empty() const
1149 {
1150  return travellers_.empty();
1151 }
1152 
1153 bool UBoat::hold_empty() const
1154 {
1155  Items::Item* it = this->hold;
1156  if ( it == nullptr )
1157  {
1158  return true;
1159  }
1160  Core::UContainer* cont = static_cast<Core::UContainer*>( it );
1161  return ( cont->count() == 0 );
1162 }
1163 
1165 {
1166  // we only do tellmove here because tellmove also checks attacks.
1167  // this way, we move everyone, then check for attacks.
1168 
1169  for ( auto& travellerRef : travellers_ )
1170  {
1171  UObject* obj = travellerRef.get();
1172 
1173  if ( obj != nullptr ) // sometimes we've destroyed objects because of control scripts
1174  {
1175  if ( obj->ismobile() )
1176  {
1177  Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
1178  if ( chr->isa( Core::UOBJ_CLASS::CLASS_NPC ) ||
1179  chr->has_active_client() ) // dave 3/27/3, dont tell moves of offline PCs
1180  chr->tellmove();
1181  }
1182  }
1183  }
1184 }
1185 
1186 // dave 3/26/3 added
1187 bool UBoat::move_xy( unsigned short newx, unsigned short newy, int flags, Realms::Realm* oldrealm )
1188 {
1189  bool result;
1190  BoatMoveGuard registerguard( this );
1191 
1192  if ( ( flags & Core::MOVEITEM_FORCELOCATION ) || navigable( multidef(), newx, newy, z, realm ) )
1193  {
1194  BoatContext bc( *this );
1195 
1196  set_dirty();
1197  move_multi_in_world( x, y, newx, newy, this, oldrealm );
1198 
1199  u16 oldx = x;
1200  u16 oldy = y;
1201  x = newx;
1202  y = newy;
1203 
1204  move_travellers( Core::FACING_N, bc, newx, newy,
1205  oldrealm ); // facing is ignored if params 3 & 4 are not USHRT_MAX
1206  move_components( oldrealm );
1207  // NOTE, send_boat_to_inrange pauses those it sends to.
1208  send_display_boat_to_inrange( oldx, oldy );
1209  // send_boat_to_inrange( this, oldx, oldy );
1210  do_tellmoves();
1211  unpause_paused();
1212 
1213  result = true;
1214  }
1215  else
1216  {
1217  result = false;
1218  }
1219 
1220  return result;
1221 }
1222 
1223 bool UBoat::move( Core::UFACING dir, u8 speed, bool relative )
1224 {
1225  bool result;
1226 
1227  BoatMoveGuard registerguard( this );
1228 
1229  Core::UFACING move_dir;
1230 
1231  if ( relative == false )
1232  move_dir = dir;
1233  else
1234  move_dir = static_cast<Core::UFACING>( ( dir + boat_facing() ) & 7 );
1235 
1236  unsigned short newx, newy;
1237  newx = x + Core::move_delta[move_dir].xmove;
1238  newy = y + Core::move_delta[move_dir].ymove;
1239 
1240  if ( navigable( multidef(), newx, newy, z, realm ) )
1241  {
1242  BoatContext bc( *this );
1243 
1244  send_smooth_move_to_inrange( move_dir, speed, newx, newy, relative );
1245 
1246  set_dirty();
1247 
1248  move_multi_in_world( x, y, newx, newy, this, realm );
1249 
1250  u16 oldx = x;
1251  u16 oldy = y;
1252  x = newx;
1253  y = newy;
1254 
1255  // NOTE, send_boat_to_inrange pauses those it sends to.
1256  // send_boat_to_inrange( this, oldx, oldy );
1257  move_travellers( move_dir, bc, x, y, realm );
1259 
1262  Network::Client* client = zonechr->client;
1263 
1264  if ( client->ClientType & Network::CLIENTTYPE_7090 )
1265  {
1266  if ( Core::inrange( client->chr->x, client->chr->y, oldx, oldy ) )
1267  return;
1268  else
1269  send_boat_newly_inrange( client ); // send HSA packet only for newly inrange
1270  }
1271  else
1272  {
1273  if ( client->ClientType & Network::CLIENTTYPE_7000 )
1274  send_boat( client ); // Send
1275  else
1276  send_boat_old( client );
1277  }
1278  } );
1279 
1281  oldx, oldy, realm, RANGE_VISUAL_LARGE_BUILDINGS, [&]( Mobile::Character* zonechr ) {
1282  Network::Client* client = zonechr->client;
1283 
1284  if ( !inrange( client->chr, this ) ) // send remove to chrs only seeing the old loc
1285  send_remove_boat( client );
1286  } );
1287 
1288  do_tellmoves();
1289  unpause_paused();
1290  result = true;
1291  }
1292  else
1293  {
1294  result = false;
1295  }
1296  return result;
1297 }
1298 
1299 inline unsigned short UBoat::multiid_ifturn( RELATIVE_DIR dir )
1300 {
1301  return ( multiid & ~3u ) | ( ( multiid + dir ) & 3 );
1302 }
1303 
1305 {
1306  unsigned short multiid_dir = multiid_ifturn( dir );
1307  passert( MultiDefByMultiIDExists( multiid_dir ) );
1308  return *MultiDefByMultiID( multiid_dir );
1309 }
1310 
1312 {
1313  return static_cast<Core::UFACING>( ( multiid & 3 ) * 2 );
1314 }
1315 
1317 {
1318  passert( Core::gamestate.boatshapes.count( multiid ) != 0 );
1320 }
1321 
1322 
1323 void UBoat::transform_components( const BoatShape& old_boatshape, Realms::Realm* oldrealm )
1324 {
1325  const BoatShape& bshape = boatshape();
1326  auto end = Components.end();
1327  auto end2 = bshape.Componentshapes.end();
1328  auto old_end = old_boatshape.Componentshapes.end();
1329 
1330  auto itr = Components.begin();
1331  auto itr2 = bshape.Componentshapes.begin();
1332  auto old_itr = old_boatshape.Componentshapes.begin();
1333 
1334  for ( ; itr != end && itr2 != end2 && old_itr != old_end; ++itr, ++itr2, ++old_itr )
1335  {
1336  Items::Item* item = itr->get();
1337  if ( item != nullptr )
1338  {
1339  if ( item->orphan() )
1340  continue;
1341 
1342  // This should be rare enough for a simple log to be the solution. We don't want POL to crash
1343  // in MoveItemWorldPosition() because the item was not in the world to start with, so we skip
1344  // it.
1345  if ( item->container != nullptr || item->has_gotten_by() )
1346  {
1347  u32 containerSerial = ( item->container != nullptr ) ? item->container->serial : 0;
1348  POLLOG_ERROR.Format(
1349  "Boat component is gotten or in a container and couldn't be moved together with the "
1350  "boat: serial 0x{:X}\n, graphic: 0x{:X}, container: 0x{:X}." )
1351  << item->serial << item->graphic << containerSerial;
1352  continue;
1353  }
1354 
1355  item->set_dirty();
1357  item->graphic == old_itr->altgraphic )
1358  item->graphic = itr2->altgraphic;
1360  item->graphic == old_itr->altgraphic )
1361  item->graphic = itr2->altgraphic;
1362  else
1363  item->graphic = itr2->graphic;
1364 
1365  u16 oldx = item->x;
1366  u16 oldy = item->y;
1367 
1368  item->x = x + itr2->xdelta;
1369  item->y = y + itr2->ydelta;
1370  item->z = z + static_cast<s8>( itr2->zdelta );
1371 
1372  MoveItemWorldPosition( oldx, oldy, item, oldrealm );
1373 
1375  item->x, item->y, realm, RANGE_VISUAL, [&]( Mobile::Character* zonechr ) {
1376  Network::Client* client = zonechr->client;
1377 
1378  if ( !( client->ClientType & Network::CLIENTTYPE_7090 ) )
1379  send_item( client, item );
1380  } );
1381 
1383  oldx, oldy, oldrealm, RANGE_VISUAL, [&]( Mobile::Character* zonechr ) {
1384  Network::Client* client = zonechr->client;
1385 
1386  if ( !inrange( client->chr,
1387  item ) ) // not in range. If old loc was in range, send a delete.
1388  send_remove_object( client, item );
1389  } );
1390  }
1391  }
1392 }
1393 
1395 {
1396  const BoatShape& bshape = boatshape();
1397  auto itr = Components.begin();
1398  auto end = Components.end();
1399  auto itr2 = bshape.Componentshapes.begin();
1400  auto end2 = bshape.Componentshapes.end();
1401  for ( ; itr != end && itr2 != end2; ++itr, ++itr2 )
1402  {
1403  Items::Item* item = itr->get();
1404  if ( item != nullptr )
1405  {
1406  if ( item->orphan() )
1407  {
1408  continue;
1409  }
1410 
1411  // This should be rare enough for a simple log to be the solution. We don't want POL to crash
1412  // in MoveItemWorldPosition() because the item was not in the world to start with, so we skip
1413  // it.
1414  if ( item->container != nullptr || item->has_gotten_by() )
1415  {
1416  u32 containerSerial = ( item->container != nullptr ) ? item->container->serial : 0;
1417  POLLOG_INFO.Format(
1418  "Boat component is gotten or in a container and couldn't be moved together with the "
1419  "boat: serial 0x{:X}\n, graphic: 0x{:X}, container: 0x{:X}." )
1420  << item->serial << item->graphic << containerSerial;
1421  continue;
1422  }
1423 
1424  item->set_dirty();
1425 
1426  u16 oldx = item->x;
1427  u16 oldy = item->y;
1428 
1429  item->x = x + itr2->xdelta;
1430  item->y = y + itr2->ydelta;
1431  item->z = z + static_cast<s8>( itr2->zdelta );
1432 
1433  MoveItemWorldPosition( oldx, oldy, item, oldrealm );
1434 
1436  item->x, item->y, realm, RANGE_VISUAL, [&]( Mobile::Character* zonechr ) {
1437  Network::Client* client = zonechr->client;
1438 
1439  if ( !( client->ClientType & Network::CLIENTTYPE_7090 ) )
1440  send_item( client, item );
1441  } );
1442 
1444  oldx, oldy, oldrealm, RANGE_VISUAL, [&]( Mobile::Character* zonechr ) {
1445  Network::Client* client = zonechr->client;
1446 
1447  if ( !inrange( client->chr,
1448  item ) ) // not in range. If old loc was in range, send a delete.
1449  send_remove_object( client, item );
1450  } );
1451  }
1452  }
1453 }
1454 
1456 {
1457  bool result;
1458  BoatMoveGuard registerguard( this );
1459 
1460  const MultiDef& newmd = multi_ifturn( dir );
1461 
1462  if ( navigable( newmd, x, y, z, realm ) )
1463  {
1464  BoatContext bc( *this );
1465  const BoatShape& old_boatshape = boatshape();
1466 
1467  set_dirty();
1468  multiid = multiid_ifturn( dir );
1469 
1470  // send_boat_to_inrange( this, x ,y, false ); // pauses those it sends to
1471  turn_travellers( dir, bc );
1472  transform_components( old_boatshape, nullptr );
1474  do_tellmoves();
1475  unpause_paused();
1476  facing = ( ( dir * 2 ) + facing ) & 7;
1477  result = true;
1478  }
1479  else
1480  {
1481  result = false;
1482  }
1483  return result;
1484 }
1485 
1487 {
1488  if ( find( travellers_.begin(), travellers_.end(), obj ) == travellers_.end() )
1489  {
1490  set_dirty();
1491  travellers_.push_back( Traveller( obj ) );
1492  }
1493 }
1494 
1496 {
1497  Travellers::iterator this_traveller = find( travellers_.begin(), travellers_.end(), obj );
1498 
1499  if ( this_traveller != travellers_.end() )
1500  {
1501  set_dirty();
1502  travellers_.erase( this_traveller );
1503  }
1504 }
1505 
1507 {
1508  UPlank* plank;
1509 
1510  if ( portplank != nullptr && !portplank->orphan() )
1511  {
1512  plank = static_cast<UPlank*>( portplank );
1513  plank->setboat( this );
1514  }
1515 
1516  if ( starboardplank != nullptr && !starboardplank->orphan() )
1517  {
1518  plank = static_cast<UPlank*>( starboardplank );
1519  plank->setboat( this );
1520  }
1521 }
1522 
1524 {
1525  for ( auto& component : Components )
1526  {
1527  if ( component == nullptr )
1528  continue;
1529  // check boat members here
1530  if ( component->objtype_ == Core::settingsManager.extobj.tillerman && tillerman == nullptr )
1531  tillerman = component.get();
1532  if ( component->objtype_ == Core::settingsManager.extobj.port_plank && portplank == nullptr )
1533  portplank = component.get();
1534  if ( component->objtype_ == Core::settingsManager.extobj.starboard_plank &&
1535  starboardplank == nullptr )
1536  starboardplank = component.get();
1537  if ( component->objtype_ == Core::settingsManager.extobj.hold && hold == nullptr )
1538  hold = component.get();
1539  }
1540 }
1541 
1545 {
1546  if ( Core::settingsManager.polvar.DataWrittenBy < 99 )
1547  {
1548  passert_always_r( graphic >= 0x4000, "Unexpected boat graphic < 0x4000 in POL < 099 data" );
1549  multiid = graphic - 0x4000;
1550  }
1552 }
1553 
1555 {
1556  base::readProperties( elem );
1557 
1558  // POL098 and earlier was not saving a MultiID in its data files,
1559  // but it was using 0x4000 + id as graphic instead. Not respecting
1560  // this would rotate most of the boats during POL098 -> POL99 migration
1561  if ( Core::settingsManager.polvar.DataWrittenBy >= 99 )
1562  multiid = elem.remove_ushort( "MultiID", this->multidef().multiid );
1563 
1564  BoatContext bc( *this );
1565  u32 tmp_serial;
1566  while ( elem.remove_prop( "Traveller", &tmp_serial ) )
1567  {
1568  if ( Core::IsItem( tmp_serial ) )
1569  {
1570  Items::Item* item = Core::find_toplevel_item( tmp_serial );
1571  if ( item != nullptr )
1572  {
1574  {
1575  Components.push_back( Component( item ) );
1576  }
1577  else if ( on_ship( bc, item ) )
1578  {
1579  travellers_.push_back( Traveller( item ) );
1580  }
1581  }
1582  }
1583  else
1584  {
1585  Mobile::Character* chr = Core::system_find_mobile( tmp_serial );
1586 
1587  if ( chr != nullptr )
1588  {
1589  if ( on_ship( bc, chr ) )
1590  travellers_.push_back( Traveller( chr ) );
1591  }
1592  }
1593  }
1594  while ( elem.remove_prop( "Component", &tmp_serial ) )
1595  {
1596  Items::Item* item = Core::system_find_item( tmp_serial );
1597  if ( item != nullptr )
1598  {
1600  {
1601  Components.push_back( Component( item ) );
1602  }
1603  }
1604  }
1607 
1608  regself(); // do this after our x,y are known.
1609  // consider throwing if starting position isn't passable.
1610 
1611  Core::start_script( "misc/boat", make_boatref( this ) );
1612 }
1613 
1615 {
1616  base::printProperties( sw );
1617 
1618  sw() << "\tMultiID\t" << multiid << pf_endl;
1619 
1620  BoatContext bc( *this );
1621 
1622  for ( auto& travellerRef : travellers_ )
1623  {
1624  UObject* obj = travellerRef.get();
1625  if ( !obj->orphan() && on_ship( bc, obj ) )
1626  {
1627  sw() << "\tTraveller\t0x" << fmt::hex( obj->serial ) << pf_endl;
1628  }
1629  }
1630  for ( auto& component : Components )
1631  {
1632  if ( component != nullptr && !component->orphan() )
1633  {
1634  sw() << "\tComponent\t0x" << fmt::hex( component->serial ) << pf_endl;
1635  }
1636  }
1637 }
1638 
1640  Realms::Realm* realm, int flags )
1641 {
1642  unsigned short multiid = descriptor.multiid;
1643  unsigned short multiid_offset =
1644  static_cast<unsigned short>( ( flags & CRMULTI_FACING_MASK ) >> CRMULTI_FACING_SHIFT );
1645  unsigned char facing = static_cast<unsigned char>( multiid_offset * 2 );
1646  multiid += multiid_offset;
1647 
1648  const MultiDef* md = MultiDefByMultiID( multiid );
1649  if ( md == nullptr )
1650  {
1651  return new Bscript::BError(
1652  "Multi definition not found for Boat, objtype=" + Clib::hexint( descriptor.objtype ) +
1653  ", multiid=" + Clib::hexint( multiid ) );
1654  }
1655  if ( !Core::gamestate.boatshapes.count( descriptor.multiid ) )
1656  {
1657  return new Bscript::BError(
1658  "No boatshape for Boat in boats.cfg, objtype=" + Clib::hexint( descriptor.objtype ) +
1659  ", multiid=" + Clib::hexint( multiid ) );
1660  }
1661 
1662  if ( !navigable( *md, x, y, z, realm ) )
1663  {
1664  return new Bscript::BError( "Position indicated is impassable" );
1665  }
1666 
1667  UBoat* boat = new UBoat( descriptor );
1668  boat->multiid = multiid;
1670  boat->serial_ext = ctBEu32( boat->serial );
1671  boat->x = x;
1672  boat->y = y;
1673  boat->z = z;
1674  boat->realm = realm;
1675  boat->facing = facing;
1676  add_multi_to_world( boat );
1677  boat->send_display_boat_to_inrange( x, y );
1678  // send_boat_to_inrange( boat, x, y, false );
1679  boat->create_components();
1680  boat->rescan_components();
1681  unpause_paused();
1682  boat->regself();
1683 
1687 
1688  Core::start_script( "misc/boat", make_boatref( boat ) );
1689  return make_boatref( boat );
1690 }
1691 
1693 {
1694  const BoatShape& bshape = boatshape();
1695  for ( std::vector<BoatShape::ComponentShape>::const_iterator itr = bshape.Componentshapes.begin(),
1696  end = bshape.Componentshapes.end();
1697  itr != end; ++itr )
1698  {
1699  Items::Item* component = Items::Item::create( itr->objtype );
1700  if ( component == nullptr )
1701  continue;
1702  // check boat members here
1703  if ( component->objtype_ == Core::settingsManager.extobj.tillerman && tillerman == nullptr )
1704  tillerman = component;
1705  if ( component->objtype_ == Core::settingsManager.extobj.port_plank && portplank == nullptr )
1706  portplank = component;
1708  starboardplank == nullptr )
1709  starboardplank = component;
1710  if ( component->objtype_ == Core::settingsManager.extobj.hold && hold == nullptr )
1711  hold = component;
1712 
1713  component->graphic = itr->graphic;
1714  // component itemdesc entries generally have graphic=1, so they don't get their height set.
1715  component->height = Core::tileheight( component->graphic );
1716  component->x = x + itr->xdelta;
1717  component->y = y + itr->ydelta;
1718  component->z = z + static_cast<s8>( itr->zdelta );
1719  component->disable_decay();
1720  component->movable( false );
1721  component->realm = realm;
1722  add_item_to_world( component );
1723  update_item_to_inrange( component );
1724  Components.push_back( Component( component ) );
1725  }
1726 }
1727 
1729 {
1730  BoatContext bc( *this );
1732 
1733  for ( const auto& travellerRef : travellers_ )
1734  {
1735  UObject* obj = travellerRef.get();
1736  if ( !obj->orphan() && on_ship( bc, obj ) && Core::IsItem( obj->serial ) )
1737  {
1738  Item* item = static_cast<Item*>( obj );
1739  arr->addElement( make_itemref( item ) );
1740  }
1741  }
1742  return arr;
1743 }
1744 
1746 {
1747  BoatContext bc( *this );
1749  for ( const auto& travellerRef : travellers_ )
1750  {
1751  UObject* obj = travellerRef.get();
1752  if ( !obj->orphan() && on_ship( bc, obj ) && obj->ismobile() )
1753  {
1754  Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
1755  if ( chr->logged_in() )
1756  arr->addElement( make_mobileref( chr ) );
1757  }
1758  }
1759  return arr;
1760 }
1761 
1762 Bscript::BObjectImp* UBoat::component_list( unsigned char type ) const
1763 {
1765  for ( const auto& component : Components )
1766  {
1767  if ( component != nullptr && !component->orphan() )
1768  {
1769  if ( type == COMPONENT_ALL )
1770  {
1771  arr->addElement( component->make_ref() );
1772  }
1773  else
1774  {
1775  if ( component->objtype_ == get_component_objtype( type ) )
1776  arr->addElement( component->make_ref() );
1777  }
1778  }
1779  }
1780  return arr;
1781 }
1782 
1784 {
1785  for ( auto& component : Components )
1786  {
1787  if ( component != nullptr && !component->orphan() )
1788  {
1789  Core::destroy_item( component.get() );
1790  }
1791  }
1792  Components.clear();
1793 }
1794 
1795 size_t UBoat::estimatedSize() const
1796 {
1797  size_t size = base::estimatedSize() + sizeof( Items::Item* ) /*tillerman*/
1798  + sizeof( Items::Item* ) /*portplank*/
1799  + sizeof( Items::Item* ) /*starboardplank*/
1800  + sizeof( Items::Item* ) /*hold*/
1801  // no estimateSize here element is in objhash
1802  + 3 * sizeof( Traveller* ) + travellers_.capacity() * sizeof( Traveller ) +
1803  3 * sizeof( Items::Item** ) + Components.capacity() * sizeof( Items::Item* );
1804  return size;
1805 }
1806 
1808 {
1809  boat->cleanup_deck();
1810 
1811  if ( !boat->hold_empty() )
1812  return new Bscript::BError( "There is cargo in the ship's hold" );
1813  if ( boat->has_offline_mobiles() )
1814  return new Bscript::BError( "There are logged-out characters on the deck" );
1815  if ( !boat->deck_empty() )
1816  return new Bscript::BError( "The deck is not empty" );
1817 
1818  boat->destroy_components();
1819  boat->unregself();
1820 
1822  boat,
1823  [&]( Mobile::Character* zonechr ) { Core::send_remove_object( zonechr->client, boat ); } );
1824  remove_multi_from_world( boat );
1825  boat->destroy();
1826  return new Bscript::BLong( 1 );
1827 }
1828 }
1829 }
unsigned char u8
Definition: rawtypes.h:25
unsigned int rudder
Definition: extobj.h:26
bool poisoned() const
Definition: charactr.h:979
void move_offline_mobiles(Core::xcoord new_x, Core::ycoord new_y, Core::zcoord new_z, Realms::Realm *new_realm)
Definition: boat.cpp:1070
void turn_traveller_coords(Mobile::Character *chr, RELATIVE_DIR dir)
Definition: boat.cpp:858
static Item * create(u32 objtype, u32 serial=0)
Definition: itemcr.cpp:53
bool move(Core::UFACING dir, u8 speed, bool relative)
Definition: boat.cpp:1223
void send_invulhealthbar(Client *client, const Character *chr)
Definition: ufunc.cpp:196
#define ITEM_FLAG_FORCE_MOVABLE
Definition: pktdef.h:25
std::string remove_string(const char *propname)
Definition: cfgfile.cpp:381
void send_smooth_move_to_inrange(Core::UFACING move_dir, u8 speed, u16 newx, u16 newy, bool relative)
Definition: boat.cpp:342
unsigned int weaponslot
Definition: extobj.h:29
bool hold_empty() const
Definition: boat.cpp:1153
void send_remove_character_to_nearby(const Character *chr)
Definition: ufunc.cpp:379
void do_tellmoves()
Definition: boat.cpp:1164
void add_item_to_world(Items::Item *item)
Definition: uworld.cpp:31
short zcoord
Definition: poltype.h:21
void setboat(UBoat *boat)
Definition: boatcomp.cpp:23
void create_components()
Definition: boat.cpp:1692
ExternalObject extobj
Definition: settings.h:30
virtual void readProperties(Clib::ConfigElem &elem) POL_OVERRIDE
Definition: boat.cpp:1554
SystemState systemstate
Definition: systemstate.cpp:12
Network::Client * client
Definition: charactr.h:871
#define RANGE_VISUAL_LARGE_BUILDINGS
Definition: uconst.h:73
bool deck_empty() const
Definition: boat.cpp:1148
unsigned int port_plank
Definition: extobj.h:19
Bscript::BObjectImp * items_list() const
Definition: boat.cpp:1728
const int CRMULTI_FACING_MASK
Definition: multi.h:52
unsigned short ycoord
Definition: poltype.h:20
void destroy_components()
Definition: boat.cpp:1783
Items::Item * find_toplevel_item(u32 serial)
Definition: fnsearch.cpp:69
bool BoatShapeExists(u16 multiid)
Definition: boat.cpp:276
UObject(u32 objtype, UOBJ_CLASS uobj_class)
Definition: uobject.cpp:72
ComponentShape(const std::string &str, const std::string &altstr, unsigned char type)
Definition: boat.cpp:98
virtual void on_color_changed() POL_OVERRIDE
Definition: boat.cpp:1111
Items::Item * portplank
Definition: boat.h:181
void move_components(Realms::Realm *oldrealm)
Definition: boat.cpp:1394
std::vector< ComponentShape > Componentshapes
Definition: boat.h:87
#define POLLOG_INFO
Definition: logfacility.h:213
virtual void printProperties(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: boat.cpp:1614
Core::UFACING boat_facing() const
Definition: boat.cpp:1311
virtual void printProperties(Clib::StreamWriter &sw) const POL_OVERRIDE
Definition: item.cpp:337
bool has_active_client() const
Definition: charactr.cpp:448
bool inrange(const UObject *c1, unsigned short x, unsigned short y)
Definition: ufunc.cpp:454
Core::UObjectRef Traveller
Definition: boat.h:226
virtual void register_object(Core::UObject *obj) POL_OVERRIDE
Definition: boat.cpp:1486
std::vector< Network::Client * > boat_sent_to
Definition: boat.cpp:67
void send_new_subserver(Client *client)
Definition: ufunc.cpp:2109
void disable_decay()
Definition: item.cpp:987
virtual void fixInvalidGraphic()
Fixes invalid graphic, moving here to allow it to be overridden in subclass (see Multi) ...
Definition: uobject.cpp:281
unsigned int tillerman
Definition: extobj.h:18
bool logged_in() const
Definition: charactr.cpp:428
void unregself()
Definition: boat.cpp:624
#define POLLOG_ERROR
Definition: logfacility.h:207
static void InRange(u16 x, u16 y, const Realms::Realm *realm, unsigned range, F &&f)
Definition: uworld.h:235
void clean_boatshapes()
Definition: boat.cpp:264
void regself()
Definition: boat.cpp:610
unsigned int sails
Definition: extobj.h:27
void realm_changed()
Definition: boat.cpp:1116
const MultiDef & multi_ifturn(RELATIVE_DIR dir)
Definition: boat.cpp:1304
Travellers travellers_
Definition: boat.h:228
unsigned short multiid
Definition: itemdesc.h:128
MoveDelta move_delta[8]
Definition: ufacing.cpp:16
const std::string name() const
Definition: realm.cpp:124
void send_remove_boat(Network::Client *client)
Definition: boat.cpp:578
bool can_seeinvisitems() const
Definition: charactr.h:1032
unsigned int tiller
Definition: extobj.h:25
static unsigned int encode_global_hull(unsigned short ax, unsigned short ay)
Definition: realm.h:236
const MultiDef & mdef
Definition: boat.h:102
Mobile::Character * chr
Definition: client.h:182
virtual u16 get_senditem_amount() const
Definition: item.cpp:552
void add_multi_to_world(Multi::UMulti *multi)
Definition: uworld.cpp:69
void send_boat(Network::Client *client)
Definition: boat.cpp:538
void send_boat_newly_inrange(Network::Client *client)
Definition: boat.cpp:470
void remove_orphans()
Definition: boat.cpp:1010
void set_dirty()
Definition: uobject.h:291
std::string hexint(unsigned short v)
Definition: strutil.cpp:23
UBoat(const Items::ItemDesc &descriptor)
Definition: boat.cpp:593
virtual void readProperties(Clib::ConfigElem &elem) POL_OVERRIDE
Definition: item.cpp:434
unsigned short u16
Definition: rawtypes.h:26
unsigned int u32
Definition: rawtypes.h:27
#define RANGE_VISUAL
Definition: uconst.h:72
bool IsItem(u32 serial)
Definition: uobject.h:316
void send_poisonhealthbar(Client *client, const Character *chr)
Definition: ufunc.cpp:186
Bscript::BObjectImp * mobiles_list() const
Definition: boat.cpp:1745
unsigned int get_component_objtype(unsigned char type)
Definition: boat.cpp:211
void reread_components()
Definition: boat.cpp:1523
bool turn(RELATIVE_DIR dir)
Definition: boat.cpp:1455
virtual void destroy()
Definition: uobject.cpp:122
void send_smooth_move(Network::Client *client, Core::UFACING move_dir, u8 speed, u16 newx, u16 newy, bool relative)
Definition: boat.cpp:281
signed short s16
Definition: rawtypes.h:30
void destroy_item(Item *item)
Definition: ufunc.cpp:1538
static bool navigable(const MultiDef &, unsigned short x, unsigned short y, short z, Realms::Realm *realm)
Definition: boat.cpp:639
void send_goxyz(Client *client, const Character *chr)
Definition: ufunc.cpp:149
unsigned int starboard_plank
Definition: extobj.h:20
Definition: refptr.h:65
virtual void unregister_object(Core::UObject *obj) POL_OVERRIDE
Definition: boat.cpp:1495
u32 GetNewItemSerialNumber(void)
Definition: ufunc.cpp:142
unsigned short height() const
Definition: realm.h:245
void send_remove_object(Client *client, const UObject *object)
Definition: ufunc.cpp:420
void Send(Client *client, int len=-1) const
Definition: packethelper.h:69
bool orphan() const
Definition: baseobject.h:119
void send_boat_old(Network::Client *client)
Definition: boat.cpp:559
char tileheight(unsigned short tilenum)
Definition: polfile2.cpp:34
#define passert(exp)
Definition: passert.h:62
void setrealm(Item *item, void *arg)
Definition: ufunc.cpp:1571
void send_display_boat_to_inrange(u16 oldx=USHRT_MAX, u16 oldy=USHRT_MAX)
Definition: boat.cpp:515
signed char s8
Definition: rawtypes.h:29
void send_display_boat(Network::Client *client)
Definition: boat.cpp:356
Core::UContainer * container
Definition: item.h:256
const u32 objtype_
Definition: uobject.h:249
GameState gamestate
Definition: uvars.cpp:74
Core::ItemRef Component
Definition: boat.h:232
virtual size_t estimatedSize() const POL_OVERRIDE
Definition: boat.cpp:1795
Items::Item * starboardplank
Definition: boat.h:182
const int CRMULTI_FACING_SHIFT
Definition: multi.h:53
unsigned int rope
Definition: extobj.h:22
#define passert_always_r(exp, reason)
Definition: passert.h:84
size_t estimateSize() const
Definition: boat.cpp:206
#define ctBEu32(x)
Definition: clib_endian.h:45
void send_objects_newly_inrange_on_boat(Network::Client *client, u32 serial)
Definition: movement.cpp:77
SettingsManager settingsManager
Definition: settings.cpp:14
unsigned int hull
Definition: extobj.h:24
static void InVisualRange(const UObject *obj, F &&f)
Definition: uworld.h:245
bool movable() const
Definition: item.h:300
void MoveCharacterWorldPosition(unsigned short oldx, unsigned short oldy, unsigned short newx, unsigned short newy, Mobile::Character *chr, Realms::Realm *oldrealm)
Definition: uworld.cpp:171
Mobile::Character * system_find_mobile(u32 serial)
Definition: fnsearch.cpp:32
void adjust_traveller_z(s8 delta_z)
Definition: boat.cpp:1098
bool Insert(UObject *obj)
Definition: objecthash.cpp:30
const int MOVEITEM_FORCELOCATION
Definition: core.h:52
bool remove_prop(const char *propname, std::string *value)
Definition: cfgfile.cpp:128
bool MultiDefByMultiIDExists(u16 multiid)
Definition: multidef.cpp:293
void send_item(Client *client, const Item *item)
Definition: ufunc.cpp:676
Items::Item * tillerman
Definition: boat.h:180
Bscript::BObjectImp * make_itemref(Items::Item *item)
Definition: mkscrobj.cpp:19
std::vector< Component > Components
Definition: boat.h:233
Bscript::BObjectImp * component_list(unsigned char type) const
Definition: boat.cpp:1762
void cleanup_deck()
Definition: boat.cpp:1032
void addElement(BObjectImp *imp)
Definition: object.cpp:1399
unsigned int hold
Definition: extobj.h:21
Items::Item * system_find_item(u32 serial)
Definition: fnsearch.cpp:41
void transform_components(const BoatShape &old_boatshape, Realms::Realm *oldrealm)
Definition: boat.cpp:1323
static bool objtype_is_component(unsigned int objtype)
Definition: boat.cpp:176
bool invisible() const
Definition: item.h:324
void restart_decay_timer()
Definition: item.cpp:974
#define ISTRINGSTREAM
Definition: stlutil.h:73
virtual UBoat * as_boat() POL_OVERRIDE
Definition: boat.cpp:602
static bool on_ship(const BoatContext &bc, const Core::UObject *obj)
Definition: boat.cpp:685
void turn_travellers(RELATIVE_DIR dir, const BoatContext &oldlocation)
Definition: boat.cpp:888
void position_changed(void)
Definition: charactr.cpp:3590
unsigned count() const
Definition: containr.h:267
bool navigable(unsigned short x, unsigned short y, short z, short height) const
Definition: realmfunc.cpp:677
virtual size_t estimatedSize() const POL_OVERRIDE
Definition: multis.cpp:112
Realms::Realm * realm
Definition: baseobject.h:56
std::set< unsigned int > global_hulls
Definition: realm.h:134
ObjectStorageManager objStorageManager
unsigned short width() const
Definition: realm.h:241
unsigned short remove_ushort(const char *propname)
Definition: cfgfile.cpp:318
void read_boat_cfg()
Definition: boat.cpp:244
unsigned int wheel
Definition: extobj.h:23
static Bscript::BObjectImp * scripted_create(const Items::ItemDesc &descriptor, u16 x, u16 y, s8 z, Realms::Realm *realm, int flags)
Definition: boat.cpp:1639
void move_multi_in_world(unsigned short oldx, unsigned short oldy, unsigned short newx, unsigned short newy, Multi::UMulti *multi, Realms::Realm *oldrealm)
Definition: uworld.cpp:87
bool isa(UOBJ_CLASS uobj_class) const
Definition: baseobject.h:99
#define ERROR_PRINT
Definition: logfacility.h:230
Bscript::BObjectImp * make_boatref(Multi::UBoat *boat)
Definition: mkscrobj.cpp:24
bool can_move(const Items::Item *item) const
Definition: charactr.cpp:1218
Items::Item * hold
Definition: boat.h:183
unsigned int storage
Definition: extobj.h:28
const MultiDef & multidef() const
Definition: multis.cpp:69
BoatShapes boatshapes
Definition: uvars.h:215
void move_travellers(enum Core::UFACING move_dir, const BoatContext &oldlocation, unsigned short x=USHRT_MAX, unsigned short y=USHRT_MAX, Realms::Realm *oldrealm=nullptr)
Definition: boat.cpp:699
bool read(ConfigElem &elem)
Definition: cfgfile.cpp:1015
void remove_multi_from_world(Multi::UMulti *multi)
Definition: uworld.cpp:76
#define INFO_PRINT
Definition: logfacility.h:223
void start_script(const char *filename, Bscript::BObjectImp *param0, Bscript::BObjectImp *param1)
Definition: scrsched.cpp:150
bool has_offline_mobiles() const
Definition: boat.cpp:1049
#define pf_endl
Definition: proplist.cpp:25
void send_owncreate(Client *client, const Character *chr)
Definition: ufunc.cpp:206
Bscript::BObjectImp * make_mobileref(Mobile::Character *chr)
Definition: mkscrobj.cpp:14
const BoatShape & boatshape() const
Definition: boat.cpp:1316
void MoveItemWorldPosition(unsigned short oldx, unsigned short oldy, Items::Item *item, Realms::Realm *oldrealm)
Definition: uworld.cpp:214
void rescan_components()
Definition: boat.cpp:1506
void unpause_paused()
Definition: boat.cpp:583
virtual void fixInvalidGraphic() POL_OVERRIDE
POL098 and earlier were using graphic to store MultiID, this should not be lost to avoid screwing up ...
Definition: boat.cpp:1544
bool move_xy(unsigned short x, unsigned short y, int flags, Realms::Realm *oldrealm)
Definition: boat.cpp:1187
Definition: berror.cpp:12
enum Pol::Mobile::Character::MOVEREASON move_reason
Bscript::BObjectImp * destroy_boat(UBoat *boat)
Definition: boat.cpp:1807
const MultiDef * MultiDefByMultiID(u16 multiid)
Definition: multidef.cpp:297
unsigned short xcoord
Definition: poltype.h:19
void update_item_to_inrange(const Item *item)
Definition: ufunc.cpp:737
bool body_contains(short rx, short ry) const
Definition: multidef.cpp:114
unsigned short multiid_ifturn(RELATIVE_DIR dir)
Definition: boat.cpp:1299
virtual void for_each_item(void(*f)(Item *item, void *a), void *arg)
Definition: containr.cpp:659
bool invul() const
Definition: charactr.cpp:4080