Pol  Revision:cb584c9
create.cpp
Go to the documentation of this file.
1 
13 #include <stdlib.h>
14 #include <string>
15 
16 #include <format/format.h>
17 #include "../clib/clib_endian.h"
18 #include "../clib/logfacility.h"
19 #include "../clib/rawtypes.h"
20 #include "../clib/refptr.h"
21 #include "../plib/systemstate.h"
22 #include "accounts/account.h"
23 #include "clidata.h"
24 #include "containr.h"
25 #include "gameclck.h"
26 #include "globals/network.h"
27 #include "globals/object_storage.h"
28 #include "globals/uvars.h"
29 #include "item/item.h"
30 #include "layers.h"
31 #include "mkscrobj.h"
32 #include "mobile/attribute.h"
33 #include "mobile/charactr.h"
34 #include "mobile/wornitems.h"
35 #include "module/osmod.h"
36 #include "module/uomod.h"
37 #include "network/client.h"
38 #include "objtype.h"
39 #include "pktdef.h"
40 #include "pktin.h"
41 #include "polcfg.h"
43 #include "scrsched.h"
44 #include "scrstore.h"
45 #include "skillid.h"
46 #include "startloc.h"
47 #include "uconst.h"
48 #include "ufunc.h"
49 #include "uoclient.h"
50 #include "uoexec.h"
51 #include "uoskills.h"
52 #include "uworld.h"
53 
54 
55 namespace Pol
56 {
57 namespace Core
58 {
59 void start_client_char( Network::Client* client );
60 void run_logon_script( Mobile::Character* chr );
61 
62 short validhaircolor( u16 /*color*/ )
63 {
64  return 1;
65 }
66 
67 /* Ah, I just realized what may be the true way to do this:
68  Read the tile file (given an object type, it gives data),
69  and make sure the resultant tile is on the right layer.
70  Only object types in the 0x2000's should be considered.
71  Also, make sure weight is 0 - some wall sconces are on
72  the beard layer. (!) (they aren't in the 0x2000's..)
73  */
74 
75 /* hair can be:
76  0x203B Short Hair // Human
77  0x203C Long Hair
78  0x203D PonyTail
79  0x2044 Mohawk
80  0x2045 Pageboy Hair
81  0x2046 Buns Hair
82  0x2047 Afro
83  0x2048 Receeding Hair
84  0x2049 Two Pig Tails
85  0x204A Krisna Hair
86 
87  0x2FBF Mid Long Hair // Elf (Mondain's Legacy)
88  0x2FC0 Long Feather Hair
89  0x2FC1 Short Elf Hair
90  0x2FC2 Mullet
91  0x2FCC Flower Hair
92  0x2FCD Long Elf Hair
93  0x2FCE Long Big Knob Hair
94  0x2FCF Long Big Braid Hair
95  0x2FD0 Long Big Bun Hair
96  0x2FD1 Spiked Hair
97  0x2FD2 Long Elf Two Hair
98 
99  0x4258 Horn Style 1 // Gargoyle Male (SA)
100  0x4259 Horn Style 2
101  0x425a Horn Style 3
102  0x425b Horn Style 4
103  0x425c Horn Style 5
104  0x425d Horn Style 6
105  0x425e Horn Style 7
106  0x425f Horn Style 8
107 
108  0x4261 Female Horn Style 1 // Gargoyle Female (SA)
109  0x4262 Female Horn Style 2
110  0x4273 Female Horn Style 3
111  0x4274 Female Horn Style 4
112  0x4275 Female Horn Style 5
113  0x42aa Female Horn Style 6
114  0x42ab Female Horn Style 7
115  0x42b1 Femaly Horn Style 8
116  */
117 bool validhair( u16 HairStyle )
118 {
119  if ( Plib::systemstate.config.max_tile_id < HairStyle )
120  {
121  return false;
122  }
123  else
124  {
125  if ( ( ( 0x203B <= HairStyle ) && ( HairStyle <= 0x203D ) ) ||
126  ( ( 0x2044 <= HairStyle ) && ( HairStyle <= 0x204A ) ) ||
127  ( ( 0x2FBF <= HairStyle ) && ( HairStyle <= 0x2FC2 ) ) ||
128  ( ( 0x2FCC <= HairStyle ) && ( HairStyle <= 0x2FD2 ) ) ||
129  ( ( 0x4258 <= HairStyle ) && ( HairStyle <= 0x425F ) ) ||
130  ( ( 0x4261 <= HairStyle ) && ( HairStyle <= 0x4262 ) ) ||
131  ( ( 0x4273 <= HairStyle ) && ( HairStyle <= 0x4275 ) ) ||
132  ( ( 0x42aa <= HairStyle ) && ( HairStyle <= 0x42ab ) ) || ( HairStyle == 0x42B1 ) )
133  return true;
134  else
135  return false;
136  }
137 }
138 
139 /* beard can be:
140  0x203E Long Beard // Human
141  0x203F Short Beard
142  0x2040 Goatee
143  0x2041 Mustache
144  0x204B Medium Short Beard
145  0x204C Medium Long Beard
146  0x204D Vandyke
147 
148  0x42ad facial horn style 1 // Gargoyle (SA)
149  0x42ae facial horn style 2
150  0x42af facial horn style 3
151  0x42b0 facial horn style 4
152  */
153 bool validbeard( u16 BeardStyle )
154 {
155  if ( ( ( 0x203E <= BeardStyle ) && ( BeardStyle <= 0x2041 ) ) ||
156  ( ( 0x204B <= BeardStyle ) && ( BeardStyle <= 0x204D ) ) ||
157  ( ( 0x42AD <= BeardStyle ) && ( BeardStyle <= 0x42B0 ) &&
158  ( Plib::systemstate.config.max_tile_id > BeardStyle ) ) )
159  return true;
160  else
161  return false;
162 }
163 
164 /* face can be:
165  0x3B44 face 1
166  0x3B45 face 2
167  0x3B46 face 3
168  0x3B47 face 4
169  0x3B48 face 5
170  0x3B49 face 6
171  0x3B4A face 7
172  0x3B4B face 8
173  0x3B4C face 9
174  0x3B4D face 10
175  0x3B4E anime //roleplay faces
176  0x3B4F hellian
177  0x3B50 juka
178  0x3B51 undead
179  0x3B52 meer
180  0x3B53 elder
181  0x3B54 orc
182  0x3B55 pirate
183  0x3B56 native papuan
184  0x3B57 vampire
185  */
186 bool validface( u16 FaceStyle )
187 {
189  {
190  if ( ( 0x3B44 <= FaceStyle ) && ( FaceStyle <= 0x3B4D ) )
191  return true;
193  {
194  if ( ( 0x3B4E <= FaceStyle ) && ( FaceStyle <= 0x3B57 ) )
195  return true;
196  }
197  }
198  return false;
199 }
200 
202 {
203  if ( client->acct == nullptr )
204  {
205  ERROR_PRINT << "Client from " << Network::AddressToString( &client->ipaddr )
206  << " tried to create a character without an account!\n";
207  client->forceDisconnect();
208  return;
209  }
210  else if ( Plib::systemstate.config.min_cmdlevel_to_login > client->acct->default_cmdlevel() )
211  {
212  // FIXME: Add send_login_error!
213  client->Disconnect();
214  return;
215  }
217  client->acct->get_character( msg->CharNumber ) != nullptr ||
218  msg->StartIndex >= gamestate.startlocations.size() )
219  {
220  ERROR_PRINT << "Create Character: Invalid parameters.\n";
222  client->Disconnect();
223  return;
224  }
225  else if ( !Plib::systemstate.config.allow_multi_clients_per_account &&
226  client->acct->has_active_characters() )
227  {
229  client->Disconnect();
230  return;
231  }
232 
233  unsigned short graphic;
234  URACE race;
235  UGENDER gender =
237  if ( client->ClientType & Network::CLIENTTYPE_7000 )
238  {
239  /*
240  0x00 / 0x01 = human male/female
241  0x02 / 0x03 = human male/female
242  0x04 / 0x05 = elf male/female
243  0x06 / 0x07 = gargoyle male/female
244  */
245  if ( ( msg->Sex & 0x6 ) == 0x6 )
246  {
247  race = RACE_GARGOYLE;
248  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_GARGOYLE_FEMALE : UOBJ_GARGOYLE_MALE;
249  }
250  else if ( ( msg->Sex & 0x4 ) == 0x4 )
251  {
252  race = RACE_ELF;
253  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_ELF_FEMALE : UOBJ_ELF_MALE;
254  }
255  else
256  {
257  race = RACE_HUMAN;
258  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_HUMAN_FEMALE : UOBJ_HUMAN_MALE;
259  }
260  }
261  else
262  {
263  /*
264  0x00 / 0x01 = human male/female
265  0x02 / 0x03 = elf male/female
266  */
267  if ( ( msg->Sex & Network::FLAG_RACE ) == Network::FLAG_RACE )
268  {
269  race = RACE_ELF;
270  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_ELF_FEMALE : UOBJ_ELF_MALE;
271  }
272  else
273  {
274  race = RACE_HUMAN;
275  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_HUMAN_FEMALE : UOBJ_HUMAN_MALE;
276  }
277  }
278 
279  Mobile::Character* chr = new Mobile::Character( graphic );
280 
281  chr->acct.set( client->acct );
282  chr->client = client;
283  chr->set_privs( client->acct->default_privlist() );
284  chr->cmdlevel( client->acct->default_cmdlevel(), false );
285 
286  client->UOExpansionFlagClient = ctBEu32( msg->clientflag );
287 
288  std::string tmpstr( msg->Name, sizeof msg->Name );
289  const char* tstr = tmpstr.c_str();
290  for ( unsigned int i = 0; i < strlen( tstr ); i++ )
291  {
292  char tmpchr = tstr[i];
293  if ( tmpchr >= ' ' && tmpchr <= '~' )
294  {
295  if ( tmpchr != '{' && tmpchr != '}' )
296  continue;
297  }
298 
299  ERROR_PRINT << "Create Character: Attempted to use invalid character '" << tmpchr << "' pos '"
300  << i << "' in name '" << tstr << "'. Client IP: " << client->ipaddrAsString()
301  << " Client Name: " << client->acct->name() << "\n";
302  client->forceDisconnect();
303  return;
304  }
305  chr->name_ = tstr;
306 
307  chr->serial = GetNextSerialNumber();
308  chr->serial_ext = ctBEu32( chr->serial );
309  chr->wornitems->serial = chr->serial;
310  chr->wornitems->serial_ext = chr->serial_ext;
311 
312  chr->graphic = graphic;
313  chr->race = race;
314  chr->gender = gender;
315 
316  chr->trueobjtype = chr->objtype_;
317  chr->color = cfBEu16( msg->SkinColor );
318  chr->truecolor = chr->color;
319 
320  Coordinate coord = gamestate.startlocations[msg->StartIndex]->select_coordinate();
321  Realms::Realm* realm = gamestate.startlocations[msg->StartIndex]->realm;
322 
323  chr->x = coord.x;
324  chr->y = coord.y;
325  chr->z = coord.z;
326  chr->facing = FACING_W;
327  chr->realm = realm;
328  chr->position_changed();
329 
330  bool valid_stats = false;
331  unsigned int stat_total = msg->Strength + msg->Intelligence + msg->Dexterity;
332  unsigned int stat_min, stat_max;
333  char* maxpos;
334  std::vector<std::string>::size_type sidx;
335  for ( sidx = 0; !valid_stats && sidx < settingsManager.ssopt.total_stats_at_creation.size();
336  ++sidx )
337  {
338  const char* statstr = settingsManager.ssopt.total_stats_at_creation[sidx].c_str();
339  stat_max = ( stat_min = strtoul( statstr, &maxpos, 0 ) );
340  if ( *( maxpos++ ) == '-' )
341  stat_max = strtoul( maxpos, 0, 0 );
342  if ( stat_total >= stat_min && stat_total <= stat_max )
343  valid_stats = true;
344  }
345  if ( !valid_stats )
346  {
347  fmt::Writer tmp;
348  tmp << "Create Character: Stats sum to " << stat_total << ".\n"
349  << "Valid values/ranges are: ";
350  for ( sidx = 0; sidx < settingsManager.ssopt.total_stats_at_creation.size(); ++sidx )
351  {
352  if ( sidx > 0 )
353  tmp << ",";
355  }
356  ERROR_PRINT << tmp.str() << "\n";
357  client->forceDisconnect();
358  return;
359  }
360  if ( msg->Strength < 10 || msg->Intelligence < 10 || msg->Dexterity < 10 )
361  {
362  ERROR_PRINT << "Create Character: A stat was too small."
363  << " Str=" << msg->Strength << " Int=" << msg->Intelligence
364  << " Dex=" << msg->Dexterity << "\n";
365 
366  client->forceDisconnect();
367  return;
368  }
369  if ( gamestate.pAttrStrength )
370  chr->attribute( gamestate.pAttrStrength->attrid ).base( msg->Strength * 10 );
374  chr->attribute( gamestate.pAttrDexterity->attrid ).base( msg->Dexterity * 10 );
375 
379  {
380  ERROR_PRINT << "Create Character: A skill number was out of range\n";
381  client->forceDisconnect();
382  return;
383  }
384  bool noskills =
385  ( msg->SkillValue1 + msg->SkillValue2 + msg->SkillValue3 == 0 ) && msg->profession;
386  if ( ( !noskills ) &&
387  ( ( msg->SkillValue1 + msg->SkillValue2 + msg->SkillValue3 != 100 ) ||
388  msg->SkillValue1 > 50 || msg->SkillValue2 > 50 || msg->SkillValue3 > 50 ) )
389  {
390  ERROR_PRINT << "Create Character: Starting skill values incorrect\n";
391  client->forceDisconnect();
392  return;
393  }
394 
396  // moved down here, after all error checking passes, else we get a half-created PC in the save.
399 
400  if ( !noskills )
401  {
402  const Mobile::Attribute* pAttr;
403  pAttr = GetUOSkill( msg->SkillNumber1 ).pAttr;
404  if ( pAttr )
405  chr->attribute( pAttr->attrid ).base( msg->SkillValue1 * 10 );
406  pAttr = GetUOSkill( msg->SkillNumber2 ).pAttr;
407  if ( pAttr )
408  chr->attribute( pAttr->attrid ).base( msg->SkillValue2 * 10 );
409  pAttr = GetUOSkill( msg->SkillNumber3 ).pAttr;
410  if ( pAttr )
411  chr->attribute( pAttr->attrid ).base( msg->SkillValue3 * 10 );
412  }
413 
414  chr->calc_vital_stuff();
415  chr->set_vitals_to_maximum();
416 
417 
418  chr->created_at = read_gameclock();
419 
420  Items::Item* tmpitem;
421  if ( validhair( cfBEu16( msg->HairStyle ) ) )
422  {
423  tmpitem = Items::Item::create( cfBEu16( msg->HairStyle ) );
424  tmpitem->layer = LAYER_HAIR;
425  tmpitem->color = cfBEu16( msg->HairColor );
426  tmpitem->realm = chr->realm;
427  if ( chr->equippable( tmpitem ) ) // check it or passert will trigger
428  chr->equip( tmpitem );
429  else
430  {
431  ERROR_PRINT.Format( "Create Character: Failed to equip hair 0x{:X}\n" ) << tmpitem->graphic;
432  tmpitem->destroy();
433  }
434  }
435 
436  if ( validbeard( cfBEu16( msg->BeardStyle ) ) )
437  {
438  tmpitem = Items::Item::create( cfBEu16( msg->BeardStyle ) );
439  tmpitem->layer = LAYER_BEARD;
440  tmpitem->color = cfBEu16( msg->BeardColor );
441  tmpitem->realm = chr->realm;
442  if ( chr->equippable( tmpitem ) ) // check it or passert will trigger
443  chr->equip( tmpitem );
444  else
445  {
446  ERROR_PRINT.Format( "Create Character: Failed to equip beard 0x{:X}\n" ) << tmpitem->graphic;
447  tmpitem->destroy();
448  }
449  }
450 
452  backpack->layer = LAYER_BACKPACK;
453  backpack->realm = chr->realm;
454  chr->equip( backpack );
455 
457  {
458  tmpitem = Items::Item::create( 0x0EED );
460  tmpitem->x = 46;
461  tmpitem->y = 91;
462  tmpitem->z = 0;
463  tmpitem->realm = chr->realm;
464  u8 newSlot = 1;
465  if ( !backpack->can_add_to_slot( newSlot ) || !tmpitem->slot_index( newSlot ) )
466  {
467  tmpitem->x = chr->x;
468  tmpitem->y = chr->y;
469  tmpitem->z = chr->z;
470  add_item_to_world( tmpitem );
472  move_item( tmpitem, tmpitem->x, tmpitem->y, tmpitem->z, nullptr );
473  }
474  else
475  backpack->add( tmpitem );
476  }
477 
478  if ( chr->race == RACE_HUMAN ||
479  chr->race == RACE_ELF ) // Gargoyles dont have shirts, pants, shoes and daggers.
480  {
481  tmpitem = Items::Item::create( 0x170F );
483  tmpitem->layer = LAYER_SHOES;
484  tmpitem->color = 0x021F;
485  tmpitem->realm = chr->realm;
486  chr->equip( tmpitem );
487 
488  tmpitem = Items::Item::create( 0xF51 );
490  tmpitem->layer = LAYER_HAND1;
491  tmpitem->realm = chr->realm;
492  chr->equip( tmpitem );
493 
494  unsigned short pantstype, shirttype;
495  if ( chr->gender == GENDER_FEMALE )
496  {
497  pantstype = 0x1516;
498  shirttype = 0x1517;
499  }
500  else
501  {
502  pantstype = 0x152e;
503  shirttype = 0x1517;
504  }
505 
506  tmpitem = Items::Item::create( pantstype );
508  tmpitem->layer = tilelayer( pantstype );
509  tmpitem->color = cfBEu16( msg->pantscolor ); // 0x0284;
510  tmpitem->realm = chr->realm;
511  chr->equip( tmpitem );
512 
513  tmpitem = Items::Item::create( shirttype );
515  tmpitem->layer = tilelayer( shirttype );
516  tmpitem->color = cfBEu16( msg->shirtcolor );
517  tmpitem->realm = chr->realm;
518  chr->equip( tmpitem );
519  }
520  else if ( chr->race == RACE_GARGOYLE ) // Gargoyles have Robes.
521  {
522  tmpitem = Items::Item::create( 0x1F03 );
524  tmpitem->layer = LAYER_ROBE_DRESS;
525  tmpitem->color = cfBEu16( msg->shirtcolor );
526  tmpitem->realm = chr->realm;
527  chr->equip( tmpitem );
528  }
529 
530  client->chr = chr;
531  client->acct->set_character( msg->CharNumber, client->chr );
532 
533  POLLOG.Format( "Account {} created character 0x{:X}\n" ) << client->acct->name() << chr->serial;
535  client->msgtype_filter = networkManager.game_filter.get();
536  start_client_char( client );
537 
538  // FIXME : Shouldn't this be triggered at the end of creation?
539  run_logon_script( chr );
540 
542  find_script( "misc/oncreate", true, Plib::systemstate.config.cache_interactive_scripts );
543  if ( prog.get() != nullptr )
544  {
545  std::unique_ptr<UOExecutor> ex( create_script_executor() );
546 
547  std::unique_ptr<Bscript::ObjArray> arr( new Bscript::ObjArray );
548  arr->addElement( new Bscript::BLong( msg->SkillNumber1 ) );
549  arr->addElement( new Bscript::BLong( msg->SkillNumber2 ) );
550  arr->addElement( new Bscript::BLong( msg->SkillNumber3 ) );
551 
552  ex->pushArg( new Bscript::BLong( msg->profession ) );
553  ex->pushArg( arr.release() );
554  ex->pushArg( make_mobileref( chr ) );
555 
556  ex->addModule( new Module::UOExecutorModule( *ex ) );
557  ex->os_module->critical = true;
558 
559  if ( ex->setProgram( prog.get() ) )
560  {
561  schedule_executor( ex.release() );
562  }
563  else
564  {
565  ERROR_PRINT << "script misc/oncreate: setProgram failed\n";
566  }
567  }
568 }
569 
570 void createchar2( Accounts::Account* acct, unsigned index )
571 {
573  chr->acct.set( acct );
574  acct->set_character( index, chr );
575  chr->setname( "new character" );
576 
577  chr->serial = GetNextSerialNumber();
578  chr->serial_ext = ctBEu32( chr->serial );
579  chr->realm = find_realm( std::string( "britannia" ) );
580  chr->x = chr->y = chr->z = chr->facing = 1;
581  chr->wornitems->serial = chr->serial;
582  chr->wornitems->serial_ext = chr->serial_ext;
583  chr->position_changed();
584  chr->graphic = UOBJ_HUMAN_MALE;
585  chr->gender = GENDER_MALE;
586  chr->trueobjtype = chr->objtype_;
587  chr->color = ctBEu16( 0 );
588  chr->truecolor = chr->color;
589  chr->created_at = read_gameclock();
590 
592 }
593 
594 
596 {
597  int charslot = ctBEu32( msg->char_slot );
598  if ( client->acct == nullptr )
599  {
600  ERROR_PRINT << "Client from " << Network::AddressToString( &client->ipaddr )
601  << " tried to create a character without an account!\n";
602  client->Disconnect();
603  return;
604  }
605  else if ( Plib::systemstate.config.min_cmdlevel_to_login > client->acct->default_cmdlevel() )
606  {
607  // FIXME: Add send_login_error ...
608  client->Disconnect();
609  return;
610  }
611  else if ( charslot >= Plib::systemstate.config.character_slots ||
612  client->acct->get_character( charslot ) != nullptr )
613  {
614  ERROR_PRINT << "Create Character: Invalid parameters.\n";
616  client->Disconnect();
617  return;
618  }
619  else if ( !Plib::systemstate.config.allow_multi_clients_per_account &&
620  client->acct->has_active_characters() )
621  {
623  client->Disconnect();
624  return;
625  }
626 
627  unsigned short graphic;
628  URACE race = ( URACE )( msg->race - 1 );
629  UGENDER gender = ( msg->gender & GENDER_FEMALE ) ? GENDER_FEMALE : GENDER_MALE;
630  if ( race == RACE_HUMAN )
631  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_HUMAN_FEMALE : UOBJ_HUMAN_MALE;
632  else if ( race == RACE_ELF )
633  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_ELF_FEMALE : UOBJ_ELF_MALE;
634  else
635  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_GARGOYLE_FEMALE : UOBJ_GARGOYLE_MALE;
636 
637 
638  Mobile::Character* chr = new Mobile::Character( graphic );
639 
640  chr->acct.set( client->acct );
641  chr->client = client;
642  chr->set_privs( client->acct->default_privlist() );
643  chr->cmdlevel( client->acct->default_cmdlevel(), false );
644 
645  client->UOExpansionFlagClient = msg->flags;
646 
647  std::string tmpstr( msg->name, sizeof msg->name );
648  const char* tstr = tmpstr.c_str();
649  for ( unsigned int i = 0; i < strlen( tstr ); i++ )
650  {
651  char tmpchr = tstr[i];
652  if ( tmpchr >= ' ' && tmpchr <= '~' )
653  {
654  if ( tmpchr != '{' && tmpchr != '}' )
655  continue;
656  }
657 
658  ERROR_PRINT << "Create Character: Attempted to use invalid character '" << tmpchr << "' pos '"
659  << i << "' in name '" << tstr << "'. Client IP: " << client->ipaddrAsString()
660  << " Client Name: " << client->acct->name() << "\n";
661  client->forceDisconnect();
662  return;
663  }
664  chr->name_ = tstr;
665 
666  chr->serial = GetNextSerialNumber();
667  chr->serial_ext = ctBEu32( chr->serial );
668  chr->wornitems->serial = chr->serial;
669  chr->wornitems->serial_ext = chr->serial_ext;
670 
671  chr->graphic = graphic;
672  chr->race = race;
673  chr->gender = gender;
674 
675  chr->trueobjtype = chr->objtype_;
676  chr->color = cfBEu16( msg->skin_color );
677  chr->truecolor = chr->color;
678 
679  Coordinate coord = gamestate.startlocations[0]->select_coordinate();
680  Realms::Realm* realm = gamestate.startlocations[0]->realm;
681 
682  chr->x = coord.x;
683  chr->y = coord.y;
684  chr->z = coord.z;
685  chr->realm = realm;
686  chr->position_changed();
687  chr->facing = FACING_W;
688 
689  bool valid_stats = false;
690  unsigned int stat_total = msg->strength + msg->intelligence + msg->dexterity;
691  unsigned int stat_min, stat_max;
692  char* maxpos;
693  std::vector<std::string>::size_type sidx;
694  for ( sidx = 0; !valid_stats && sidx < settingsManager.ssopt.total_stats_at_creation.size();
695  ++sidx )
696  {
697  const char* statstr = settingsManager.ssopt.total_stats_at_creation[sidx].c_str();
698  stat_max = ( stat_min = strtoul( statstr, &maxpos, 0 ) );
699  if ( *( maxpos++ ) == '-' )
700  stat_max = strtoul( maxpos, 0, 0 );
701  if ( stat_total >= stat_min && stat_total <= stat_max )
702  valid_stats = true;
703  }
704  if ( !valid_stats )
705  {
706  fmt::Writer tmp;
707  tmp << "Create Character: Stats sum to " << stat_total << ".\n"
708  << "Valid values/ranges are: ";
709  for ( sidx = 0; sidx < settingsManager.ssopt.total_stats_at_creation.size(); ++sidx )
710  {
711  if ( sidx > 0 )
712  tmp << ",";
714  }
715  ERROR_PRINT << tmp.str() << "\n";
716  client->forceDisconnect();
717  return;
718  }
719  if ( msg->strength < 10 || msg->intelligence < 10 || msg->dexterity < 10 )
720  {
721  ERROR_PRINT << "Create Character: A stat was too small."
722  << " Str=" << msg->strength << " Int=" << msg->intelligence
723  << " Dex=" << msg->dexterity << "\n";
724 
725  client->forceDisconnect();
726  return;
727  }
728  if ( gamestate.pAttrStrength )
729  chr->attribute( gamestate.pAttrStrength->attrid ).base( msg->strength * 10 );
733  chr->attribute( gamestate.pAttrDexterity->attrid ).base( msg->dexterity * 10 );
734 
735 
740  {
741  ERROR_PRINT << "Create Character: A skill number was out of range\n";
742  client->forceDisconnect();
743  return;
744  }
745 
746  bool noskills =
747  ( msg->skillvalue1 + msg->skillvalue2 + msg->skillvalue3 + msg->skillvalue4 == 0 ) &&
748  msg->profession;
749  if ( ( !noskills ) &&
750  ( ( msg->skillvalue1 + msg->skillvalue2 + msg->skillvalue3 + msg->skillvalue4 != 120 ) ||
751  msg->skillvalue1 > 50 || msg->skillvalue2 > 50 || msg->skillvalue3 > 50 ||
752  msg->skillvalue4 > 50 ) )
753  {
754  ERROR_PRINT << "Create Character: Starting skill values incorrect\n";
755  client->forceDisconnect();
756  return;
757  }
758 
760  // moved down here, after all error checking passes, else we get a half-created PC in the save.
763 
764  if ( !noskills )
765  {
766  const Mobile::Attribute* pAttr;
767  pAttr = GetUOSkill( msg->skillnumber1 ).pAttr;
768  if ( pAttr )
769  chr->attribute( pAttr->attrid ).base( msg->skillvalue1 * 10 );
770  pAttr = GetUOSkill( msg->skillnumber2 ).pAttr;
771  if ( pAttr )
772  chr->attribute( pAttr->attrid ).base( msg->skillvalue2 * 10 );
773  pAttr = GetUOSkill( msg->skillnumber3 ).pAttr;
774  if ( pAttr )
775  chr->attribute( pAttr->attrid ).base( msg->skillvalue3 * 10 );
776  pAttr = GetUOSkill( msg->skillnumber4 ).pAttr;
777  if ( pAttr )
778  chr->attribute( pAttr->attrid ).base( msg->skillvalue4 * 10 );
779  }
780 
781  chr->calc_vital_stuff();
782  chr->set_vitals_to_maximum();
783 
784 
785  chr->created_at = read_gameclock();
786 
787  Items::Item* tmpitem;
788  if ( validhair( cfBEu16( msg->hairstyle ) ) )
789  {
790  tmpitem = Items::Item::create( cfBEu16( msg->hairstyle ) );
791  tmpitem->layer = LAYER_HAIR;
792  tmpitem->color = cfBEu16( msg->haircolor );
793  tmpitem->realm = chr->realm;
794  if ( chr->equippable( tmpitem ) ) // check it or passert will trigger
795  chr->equip( tmpitem );
796  else
797  {
798  ERROR_PRINT.Format( "Create Character: Failed to equip hair 0x{:X}\n" ) << tmpitem->graphic;
799  tmpitem->destroy();
800  }
801  }
802 
803  if ( validbeard( cfBEu16( msg->beardstyle ) ) )
804  {
805  tmpitem = Items::Item::create( cfBEu16( msg->beardstyle ) );
806  tmpitem->layer = LAYER_BEARD;
807  tmpitem->color = cfBEu16( msg->beardcolor );
808  tmpitem->realm = chr->realm;
809  if ( chr->equippable( tmpitem ) ) // check it or passert will trigger
810  chr->equip( tmpitem );
811  else
812  {
813  ERROR_PRINT.Format( "Create Character: Failed to equip beard 0x{:X}\n" ) << tmpitem->graphic;
814  tmpitem->destroy();
815  }
816  }
817 
818  if ( validface( cfBEu16( msg->face_id ) ) )
819  {
820  tmpitem = Items::Item::create( cfBEu16( msg->face_id ) );
821  tmpitem->layer = LAYER_FACE;
822  tmpitem->color = cfBEu16( msg->face_color );
823  tmpitem->realm = chr->realm;
824  if ( chr->equippable( tmpitem ) ) // check it or passert will trigger
825  chr->equip( tmpitem );
826  else
827  {
828  ERROR_PRINT.Format( "Create Character: Failed to equip face 0x{:X}\n" ) << tmpitem->graphic;
829  tmpitem->destroy();
830  }
831  }
832 
834  backpack->layer = LAYER_BACKPACK;
835  backpack->realm = chr->realm;
836  chr->equip( backpack );
837 
839  {
840  tmpitem = Items::Item::create( 0x0EED );
842  tmpitem->x = 46;
843  tmpitem->y = 91;
844  tmpitem->z = 0;
845  tmpitem->realm = chr->realm;
846  u8 newSlot = 1;
847  if ( !backpack->can_add_to_slot( newSlot ) || !tmpitem->slot_index( newSlot ) )
848  {
849  tmpitem->x = chr->x;
850  tmpitem->y = chr->y;
851  tmpitem->z = chr->z;
852  add_item_to_world( tmpitem );
854  move_item( tmpitem, tmpitem->x, tmpitem->y, tmpitem->z, nullptr );
855  }
856  else
857  backpack->add( tmpitem );
858  }
859 
860  if ( chr->race == RACE_HUMAN ||
861  chr->race == RACE_ELF ) // Gargoyles dont have shirts, pants, shoes and daggers.
862  {
863  tmpitem = Items::Item::create( 0x170F );
865  tmpitem->layer = LAYER_SHOES;
866  tmpitem->color = 0x021F;
867  tmpitem->realm = chr->realm;
868  chr->equip( tmpitem );
869 
870  tmpitem = Items::Item::create( 0xF51 );
872  tmpitem->layer = LAYER_HAND1;
873  tmpitem->realm = chr->realm;
874  chr->equip( tmpitem );
875 
876  unsigned short pantstype, shirttype;
877  if ( chr->gender == GENDER_FEMALE )
878  {
879  pantstype = 0x1516;
880  shirttype = 0x1517;
881  }
882  else
883  {
884  pantstype = 0x152e;
885  shirttype = 0x1517;
886  }
887 
888  tmpitem = Items::Item::create( pantstype );
890  tmpitem->layer = tilelayer( pantstype );
891  tmpitem->color = cfBEu16( msg->pantscolor ); // 0x0284;
892  tmpitem->realm = chr->realm;
893  chr->equip( tmpitem );
894 
895  tmpitem = Items::Item::create( shirttype );
897  tmpitem->layer = tilelayer( shirttype );
898  tmpitem->color = cfBEu16( msg->shirtcolor );
899  tmpitem->realm = chr->realm;
900  chr->equip( tmpitem );
901  }
902  else if ( chr->race == RACE_GARGOYLE ) // Gargoyles have Robes.
903  {
904  tmpitem = Items::Item::create( 0x1F03 );
906  tmpitem->layer = LAYER_ROBE_DRESS;
907  tmpitem->color = cfBEu16( msg->shirtcolor );
908  tmpitem->realm = chr->realm;
909  chr->equip( tmpitem );
910  }
911 
912  client->chr = chr;
913  client->acct->set_character( charslot, client->chr );
914 
915  POLLOG.Format( "Account {} created character 0x{:X}\n" ) << client->acct->name() << chr->serial;
917  client->msgtype_filter = networkManager.game_filter.get();
918  start_client_char( client );
919 
920  // FIXME : Shouldn't this be triggered at the end of creation?
921  run_logon_script( chr );
922 
924  find_script( "misc/oncreate", true, Plib::systemstate.config.cache_interactive_scripts );
925  if ( prog.get() != nullptr )
926  {
927  std::unique_ptr<UOExecutor> ex( create_script_executor() );
928 
929  std::unique_ptr<Bscript::ObjArray> arr( new Bscript::ObjArray );
930  arr->addElement( new Bscript::BLong( msg->skillnumber1 ) );
931  arr->addElement( new Bscript::BLong( msg->skillnumber2 ) );
932  arr->addElement( new Bscript::BLong( msg->skillnumber3 ) );
933  arr->addElement( new Bscript::BLong( msg->skillnumber4 ) );
934 
935  ex->pushArg( new Bscript::BLong( msg->profession ) );
936  ex->pushArg( arr.release() );
937  ex->pushArg( make_mobileref( chr ) );
938 
939  ex->addModule( new Module::UOExecutorModule( *ex ) );
940  ex->os_module->critical = true;
941 
942  if ( ex->setProgram( prog.get() ) )
943  {
944  schedule_executor( ex.release() );
945  }
946  else
947  {
948  ERROR_PRINT << "script misc/oncreate: setProgram failed\n";
949  }
950  }
951 }
952 
954 {
955  if ( client->acct == nullptr )
956  {
957  ERROR_PRINT << "Client from " << Network::AddressToString( &client->ipaddr )
958  << " tried to create a character without an account!\n";
959  client->forceDisconnect();
960  return;
961  }
962  else if ( Plib::systemstate.config.min_cmdlevel_to_login > client->acct->default_cmdlevel() )
963  {
965  client->Disconnect();
966  return;
967  }
969  client->acct->get_character( msg->CharNumber ) != nullptr ||
970  msg->StartIndex >= gamestate.startlocations.size() )
971  {
972  ERROR_PRINT << "Create Character: Invalid parameters.\n";
974  client->Disconnect();
975  return;
976  }
977  else if ( !Plib::systemstate.config.allow_multi_clients_per_account &&
978  client->acct->has_active_characters() )
979  {
981  client->Disconnect();
982  return;
983  }
984 
985  unsigned short graphic;
986  URACE race;
987  UGENDER gender =
989  if ( client->ClientType & Network::CLIENTTYPE_7000 )
990  {
991  /*
992  0x00 / 0x01 = human male/female
993  0x02 / 0x03 = human male/female
994  0x04 / 0x05 = elf male/female
995  0x06 / 0x07 = gargoyle male/female
996  */
997  if ( ( msg->Sex & 0x6 ) == 0x6 )
998  {
999  race = RACE_GARGOYLE;
1000  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_GARGOYLE_FEMALE : UOBJ_GARGOYLE_MALE;
1001  }
1002  else if ( ( msg->Sex & 0x4 ) == 0x4 )
1003  {
1004  race = RACE_ELF;
1005  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_ELF_FEMALE : UOBJ_ELF_MALE;
1006  }
1007  else
1008  {
1009  race = RACE_HUMAN;
1010  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_HUMAN_FEMALE : UOBJ_HUMAN_MALE;
1011  }
1012  }
1013  else
1014  {
1015  /*
1016  0x00 / 0x01 = human male/female
1017  0x02 / 0x03 = elf male/female
1018  */
1019  if ( ( msg->Sex & Network::FLAG_RACE ) == Network::FLAG_RACE )
1020  {
1021  race = RACE_ELF;
1022  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_ELF_FEMALE : UOBJ_ELF_MALE;
1023  }
1024  else
1025  {
1026  race = RACE_HUMAN;
1027  graphic = ( gender == GENDER_FEMALE ) ? UOBJ_HUMAN_FEMALE : UOBJ_HUMAN_MALE;
1028  }
1029  }
1030 
1031  Mobile::Character* chr = new Mobile::Character( graphic );
1032 
1033  chr->acct.set( client->acct );
1034  chr->client = client;
1035  chr->set_privs( client->acct->default_privlist() );
1036  chr->cmdlevel( client->acct->default_cmdlevel(), false );
1037 
1038  client->UOExpansionFlagClient = ctBEu32( msg->clientflag );
1039 
1040  std::string tmpstr( msg->Name, sizeof msg->Name );
1041  const char* tstr = tmpstr.c_str();
1042  for ( unsigned int i = 0; i < strlen( tstr ); i++ )
1043  {
1044  char tmpchr = tstr[i];
1045  if ( tmpchr >= ' ' && tmpchr <= '~' )
1046  {
1047  if ( tmpchr != '{' && tmpchr != '}' )
1048  continue;
1049  }
1050 
1051  ERROR_PRINT << "Create Character: Attempted to use invalid character '" << tmpchr << "' pos '"
1052  << i << "' in name '" << tstr << "'. Client IP: " << client->ipaddrAsString()
1053  << " Client Name: " << client->acct->name() << "\n";
1054  client->Disconnect();
1055  return;
1056  }
1057  chr->name_ = tstr;
1058 
1059  chr->serial = GetNextSerialNumber();
1060  chr->serial_ext = ctBEu32( chr->serial );
1061  chr->wornitems->serial = chr->serial;
1062  chr->wornitems->serial_ext = chr->serial_ext;
1063 
1064  chr->graphic = graphic;
1065  chr->race = race;
1066  chr->gender = gender;
1067 
1068  chr->trueobjtype = chr->objtype_;
1069  chr->color = cfBEu16( msg->SkinColor );
1070  chr->truecolor = chr->color;
1071 
1072  Coordinate coord = gamestate.startlocations[msg->StartIndex]->select_coordinate();
1073  Realms::Realm* realm = gamestate.startlocations[msg->StartIndex]->realm;
1074 
1075  chr->x = coord.x;
1076  chr->y = coord.y;
1077  chr->z = coord.z;
1078  chr->realm = realm;
1079  chr->position_changed();
1080  chr->facing = FACING_W;
1081 
1082  bool valid_stats = false;
1083  unsigned int stat_total = msg->Strength + msg->Intelligence + msg->Dexterity;
1084  unsigned int stat_min, stat_max;
1085  char* maxpos;
1086  std::vector<std::string>::size_type sidx;
1087  for ( sidx = 0; !valid_stats && sidx < settingsManager.ssopt.total_stats_at_creation.size();
1088  ++sidx )
1089  {
1090  const char* statstr = settingsManager.ssopt.total_stats_at_creation[sidx].c_str();
1091  stat_max = ( stat_min = strtoul( statstr, &maxpos, 0 ) );
1092  if ( *( maxpos++ ) == '-' )
1093  stat_max = strtoul( maxpos, 0, 0 );
1094  if ( stat_total >= stat_min && stat_total <= stat_max )
1095  valid_stats = true;
1096  }
1097  if ( !valid_stats )
1098  {
1099  fmt::Writer tmp;
1100  tmp << "Create Character: Stats sum to " << stat_total << ".\n"
1101  << "Valid values/ranges are: ";
1102  for ( sidx = 0; sidx < settingsManager.ssopt.total_stats_at_creation.size(); ++sidx )
1103  {
1104  if ( sidx > 0 )
1105  tmp << ",";
1107  }
1108  ERROR_PRINT << tmp.str() << "\n";
1109  client->forceDisconnect();
1110  return;
1111  }
1112  if ( msg->Strength < 10 || msg->Intelligence < 10 || msg->Dexterity < 10 )
1113  {
1114  ERROR_PRINT << "Create Character: A stat was too small."
1115  << " Str=" << msg->Strength << " Int=" << msg->Intelligence
1116  << " Dex=" << msg->Dexterity << "\n";
1117 
1118  client->forceDisconnect();
1119  return;
1120  }
1121  if ( gamestate.pAttrStrength )
1122  chr->attribute( gamestate.pAttrStrength->attrid ).base( msg->Strength * 10 );
1125  if ( gamestate.pAttrDexterity )
1126  chr->attribute( gamestate.pAttrDexterity->attrid ).base( msg->Dexterity * 10 );
1127 
1128  // With latest clients EA broke the prof.txt, added Evaluating Intelligence and Spirit Speak which
1129  // returns SkillNumber 0xFF
1130  // Check for it here to not crash the client during char creation
1131  bool broken_prof = ( msg->SkillNumber1 == 0xFF || msg->SkillNumber2 == 0xFF ||
1132  msg->SkillNumber3 == 0xFF || msg->SkillNumber4 == 0xFF ) &&
1133  msg->profession;
1134 
1135  if ( broken_prof )
1136  {
1137  unsigned char temp_skillid = 0;
1138 
1139  if ( msg->profession == 2 ) // Mage profession
1140  temp_skillid = SKILLID_EVALUATINGINTEL;
1141  if ( msg->profession == 4 ) // Necromancy profession
1142  temp_skillid = SKILLID_SPIRITSPEAK;
1143 
1144 
1145  if ( msg->SkillNumber1 == 0xFF )
1146  {
1147  msg->SkillNumber1 = temp_skillid;
1148  msg->SkillValue1 = 30;
1149  }
1150  else if ( msg->SkillNumber2 == 0xFF )
1151  {
1152  msg->SkillNumber2 = temp_skillid;
1153  msg->SkillValue2 = 30;
1154  }
1155  else if ( msg->SkillNumber3 == 0xFF )
1156  {
1157  msg->SkillNumber3 = temp_skillid;
1158  msg->SkillValue3 = 30;
1159  }
1160  else if ( msg->SkillNumber4 == 0xFF )
1161  {
1162  msg->SkillNumber4 = temp_skillid;
1163  msg->SkillValue4 = 30;
1164  }
1165  }
1166 
1167 
1172  {
1173  ERROR_PRINT << "Create Character: A skill number was out of range\n";
1174  client->forceDisconnect();
1175  return;
1176  }
1177 
1178  bool noskills =
1179  ( msg->SkillValue1 + msg->SkillValue2 + msg->SkillValue3 + msg->SkillValue4 == 0 ) &&
1180  msg->profession;
1181 
1182  if ( ( !noskills ) &&
1183  ( ( msg->SkillValue1 + msg->SkillValue2 + msg->SkillValue3 + msg->SkillValue4 != 120 ) ||
1184  msg->SkillValue1 > 50 || msg->SkillValue2 > 50 || msg->SkillValue3 > 50 ||
1185  msg->SkillValue4 > 50 ) )
1186  {
1187  ERROR_PRINT << "Create Character: Starting skill values incorrect\n";
1188  client->forceDisconnect();
1189  return;
1190  }
1191 
1193  // moved down here, after all error checking passes, else we get a half-created PC in the save.
1196 
1197  if ( !noskills )
1198  {
1199  const Mobile::Attribute* pAttr;
1200  pAttr = GetUOSkill( msg->SkillNumber1 ).pAttr;
1201  if ( pAttr )
1202  chr->attribute( pAttr->attrid ).base( msg->SkillValue1 * 10 );
1203  pAttr = GetUOSkill( msg->SkillNumber2 ).pAttr;
1204  if ( pAttr )
1205  chr->attribute( pAttr->attrid ).base( msg->SkillValue2 * 10 );
1206  pAttr = GetUOSkill( msg->SkillNumber3 ).pAttr;
1207  if ( pAttr )
1208  chr->attribute( pAttr->attrid ).base( msg->SkillValue3 * 10 );
1209  pAttr = GetUOSkill( msg->SkillNumber4 ).pAttr;
1210  if ( pAttr )
1211  chr->attribute( pAttr->attrid ).base( msg->SkillValue4 * 10 );
1212  }
1213 
1214  chr->calc_vital_stuff();
1215  chr->set_vitals_to_maximum();
1216 
1217 
1218  chr->created_at = read_gameclock();
1219 
1220  Items::Item* tmpitem;
1221  if ( validhair( cfBEu16( msg->HairStyle ) ) )
1222  {
1223  tmpitem = Items::Item::create( cfBEu16( msg->HairStyle ) );
1224  tmpitem->layer = LAYER_HAIR;
1225  tmpitem->color = cfBEu16( msg->HairColor );
1226  tmpitem->realm = chr->realm;
1227  if ( chr->equippable( tmpitem ) ) // check it or passert will trigger
1228  chr->equip( tmpitem );
1229  else
1230  {
1231  ERROR_PRINT.Format( "Create Character: Failed to equip hair 0x{:X}\n" ) << tmpitem->graphic;
1232  tmpitem->destroy();
1233  }
1234  }
1235 
1236  if ( validbeard( cfBEu16( msg->BeardStyle ) ) )
1237  {
1238  tmpitem = Items::Item::create( cfBEu16( msg->BeardStyle ) );
1239  tmpitem->layer = LAYER_BEARD;
1240  tmpitem->color = cfBEu16( msg->BeardColor );
1241  tmpitem->realm = chr->realm;
1242  if ( chr->equippable( tmpitem ) ) // check it or passert will trigger
1243  chr->equip( tmpitem );
1244  else
1245  {
1246  ERROR_PRINT.Format( "Create Character: Failed to equip beard 0x{:X}\n" ) << tmpitem->graphic;
1247  tmpitem->destroy();
1248  }
1249  }
1250 
1252  backpack->layer = LAYER_BACKPACK;
1253  backpack->realm = chr->realm;
1254  chr->equip( backpack );
1255 
1256  if ( settingsManager.ssopt.starting_gold != 0 )
1257  {
1258  tmpitem = Items::Item::create( 0x0EED );
1260  tmpitem->x = 46;
1261  tmpitem->y = 91;
1262  tmpitem->z = 0;
1263  tmpitem->realm = chr->realm;
1264  u8 newSlot = 1;
1265  if ( !backpack->can_add_to_slot( newSlot ) || !tmpitem->slot_index( newSlot ) )
1266  {
1267  tmpitem->x = chr->x;
1268  tmpitem->y = chr->y;
1269  tmpitem->z = chr->z;
1270  add_item_to_world( tmpitem );
1271  register_with_supporting_multi( tmpitem );
1272  move_item( tmpitem, tmpitem->x, tmpitem->y, tmpitem->z, nullptr );
1273  }
1274  else
1275  backpack->add( tmpitem );
1276  }
1277 
1278  if ( chr->race == RACE_HUMAN ||
1279  chr->race == RACE_ELF ) // Gargoyles dont have shirts, pants, shoes and daggers.
1280  {
1281  tmpitem = Items::Item::create( 0x170F );
1283  tmpitem->layer = LAYER_SHOES;
1284  tmpitem->color = 0x021F;
1285  tmpitem->realm = chr->realm;
1286  chr->equip( tmpitem );
1287 
1288  tmpitem = Items::Item::create( 0xF51 );
1290  tmpitem->layer = LAYER_HAND1;
1291  tmpitem->realm = chr->realm;
1292  chr->equip( tmpitem );
1293 
1294  unsigned short pantstype, shirttype;
1295  if ( chr->gender == GENDER_FEMALE )
1296  {
1297  pantstype = 0x1516;
1298  shirttype = 0x1517;
1299  }
1300  else
1301  {
1302  pantstype = 0x152e;
1303  shirttype = 0x1517;
1304  }
1305 
1306  tmpitem = Items::Item::create( pantstype );
1308  tmpitem->layer = tilelayer( pantstype );
1309  tmpitem->color = cfBEu16( msg->pantscolor ); // 0x0284;
1310  tmpitem->realm = chr->realm;
1311  chr->equip( tmpitem );
1312 
1313  tmpitem = Items::Item::create( shirttype );
1315  tmpitem->layer = tilelayer( shirttype );
1316  tmpitem->color = cfBEu16( msg->shirtcolor );
1317  tmpitem->realm = chr->realm;
1318  chr->equip( tmpitem );
1319  }
1320  else if ( chr->race == RACE_GARGOYLE ) // Gargoyles have Robes.
1321  {
1322  tmpitem = Items::Item::create( 0x1F03 );
1324  tmpitem->layer = LAYER_ROBE_DRESS;
1325  tmpitem->color = cfBEu16( msg->shirtcolor );
1326  tmpitem->realm = chr->realm;
1327  chr->equip( tmpitem );
1328  }
1329 
1330  client->chr = chr;
1331  client->acct->set_character( msg->CharNumber, client->chr );
1332 
1333  POLLOG.Format( "Account {} created character 0x{:X}\n" ) << client->acct->name() << chr->serial;
1335  client->msgtype_filter = networkManager.game_filter.get();
1336  start_client_char( client );
1337 
1338  // FIXME : Shouldn't this be triggered at the end of creation?
1339  run_logon_script( chr );
1340 
1342  find_script( "misc/oncreate", true, Plib::systemstate.config.cache_interactive_scripts );
1343  if ( prog.get() != nullptr )
1344  {
1345  std::unique_ptr<UOExecutor> ex( create_script_executor() );
1346 
1347  std::unique_ptr<Bscript::ObjArray> arr( new Bscript::ObjArray );
1348  arr->addElement( new Bscript::BLong( msg->SkillNumber1 ) );
1349  arr->addElement( new Bscript::BLong( msg->SkillNumber2 ) );
1350  arr->addElement( new Bscript::BLong( msg->SkillNumber3 ) );
1351  arr->addElement( new Bscript::BLong( msg->SkillNumber4 ) );
1352 
1353  ex->pushArg( new Bscript::BLong( msg->profession ) );
1354  ex->pushArg( arr.release() );
1355  ex->pushArg( make_mobileref( chr ) );
1356 
1357  ex->addModule( new Module::UOExecutorModule( *ex ) );
1358  ex->os_module->critical = true;
1359 
1360  if ( ex->setProgram( prog.get() ) )
1361  {
1362  schedule_executor( ex.release() );
1363  }
1364  else
1365  {
1366  ERROR_PRINT << "script misc/oncreate: setProgram failed\n";
1367  }
1368  }
1369 }
1370 }
1371 }
unsigned char u8
Definition: rawtypes.h:25
bool newbie() const
Definition: item.h:338
#define UOBJ_GARGOYLE_FEMALE
Definition: objtype.h:136
void ClientCreateChar(Network::Client *client, PKTIN_00 *msg)
Definition: create.cpp:201
void register_with_supporting_multi(Item *item)
Definition: ufunc.cpp:1875
static Item * create(u32 objtype, u32 serial=0)
Definition: itemcr.cpp:53
std::vector< std::string > total_stats_at_creation
Definition: ssopt.h:92
#define UOBJ_GARGOYLE_MALE
Definition: objtype.h:135
bool can_add_to_slot(u8 &slotIndex)
Definition: containr.cpp:178
Core::gameclock_t created_at
Definition: charactr.h:906
void set_character(int index, Mobile::Character *chr)
Definition: account.cpp:174
void forceDisconnect()
Definition: client.h:288
const u8 FLAG_GENDER
Definition: client.h:88
void add_item_to_world(Items::Item *item)
Definition: uworld.cpp:31
const Mobile::Attribute * pAttr
Definition: uoskills.h:38
SystemState systemstate
Definition: systemstate.cpp:12
const u8 FLAG_RACE
Definition: client.h:89
sockaddr ipaddr
Definition: client.h:213
Network::Client * client
Definition: charactr.h:871
Core::PolConfig config
Definition: systemstate.h:43
bool newbie_starting_equipment
Definition: ssopt.h:81
Accounts::Account * acct
Definition: client.h:181
void SetCharacterWorldPosition(Mobile::Character *chr, Realms::WorldChangeReason reason)
Definition: uworld.cpp:128
unsigned char tilelayer(unsigned short tilenum)
Definition: polfile2.cpp:22
void addModule(ExecutorModule *module)
Definition: executor.cpp:3032
char Name[30]
Definition: pktin.h:530
u32 GetNextSerialNumber(void)
Definition: ufunc.cpp:119
#define UOBJ_ELF_FEMALE
Definition: objtype.h:131
T * get() const
Definition: refptr.h:176
ref_ptr< Core::WornItemsContainer > wornitems
Definition: charactr.h:787
#define ctBEu16(x)
Definition: clib_endian.h:46
void calc_vital_stuff(bool i_mod=true, bool v_mod=true)
Definition: charactr.cpp:1581
Mobile::Character * get_character(int index)
Definition: account.cpp:169
unsigned char cmdlevel() const
Definition: charactr.h:993
void start_client_char(Network::Client *client)
Definition: pol.cpp:238
char name[30]
Definition: pktin.h:284
bool validface(u16 FaceStyle)
Definition: create.cpp:186
bool has_active_characters()
Returns true if at least one character from this account is already logged in.
Definition: account.cpp:185
virtual void add(Items::Item *item)
Definition: containr.cpp:194
Mobile::Character * chr
Definition: client.h:182
Core::UGENDER gender
Definition: charactr.h:918
unsigned short starting_gold
Definition: ssopt.h:58
unsigned short u16
Definition: rawtypes.h:26
#define UOBJ_HUMAN_MALE
Definition: objtype.h:125
void setamount(u16 amount)
Definition: item.cpp:575
unsigned short maxskills
Definition: uoclient.h:55
void pushArg(BObjectImp *arg)
Definition: executor.cpp:3021
virtual void destroy()
Definition: uobject.cpp:122
void send_login_error(Network::Client *client, unsigned char reason)
Definition: login.cpp:60
const UOSkill & GetUOSkill(unsigned skillid)
Definition: uoskills.cpp:21
UOExecutor * create_script_executor()
Definition: scrsched.cpp:644
const Mobile::Attribute * pAttrStrength
Definition: uvars.h:179
NetworkManager networkManager
Definition: network.cpp:28
const AttributeValue & attribute(unsigned attrid) const
Definition: charactr.h:1051
const char * name() const
Definition: account.cpp:193
bool setProgram(EScriptProgram *prog)
Definition: executor.cpp:731
void createchar2(Accounts::Account *acct, unsigned index)
Definition: create.cpp:570
const char * AddressToString(struct sockaddr *addr)
Definition: sockio.cpp:212
#define cfBEu16(x)
Definition: clib_endian.h:44
#define POLLOG
Definition: logfacility.h:219
bool validhair(u16 HairStyle)
Definition: create.cpp:117
#define LOGIN_ERROR_MISC
Definition: pktdef.h:69
#define UOBJ_BACKPACK
Definition: objtype.h:202
gameclock_t read_gameclock()
Reads the current value of the game clock.
Definition: gameclck.cpp:57
const u32 objtype_
Definition: uobject.h:249
Core::URACE race
Definition: charactr.h:919
void setname(const std::string &)
Definition: uobject.cpp:206
GameState gamestate
Definition: uvars.cpp:74
void equip(Items::Item *item)
Definition: charactr.cpp:1433
void move_item(Item *item, UFACING facing)
Definition: ufunc.cpp:1601
#define ctBEu32(x)
Definition: clib_endian.h:45
SettingsManager settingsManager
Definition: settings.cpp:14
bool Insert(UObject *obj)
Definition: objecthash.cpp:30
short validhaircolor(u16)
Definition: create.cpp:62
#define UOBJ_HUMAN_FEMALE
Definition: objtype.h:126
Core::AccountRef acct
Definition: charactr.h:914
const Core::MessageTypeFilter * msgtype_filter
Definition: client.h:219
const Mobile::Attribute * pAttrIntelligence
Definition: uvars.h:180
unsigned char support_faces
Definition: ssopt.h:80
u8 slot_index() const
Definition: item.h:358
void position_changed(void)
Definition: charactr.cpp:3590
Realms::Realm * realm
Definition: baseobject.h:56
std::string ipaddrAsString() const
Definition: clientio.cpp:37
ObjectStorageManager objStorageManager
void run_logon_script(Mobile::Character *chr)
Definition: pol.cpp:355
std::string default_privlist() const
Definition: account.cpp:277
u32 UOExpansionFlagClient
Definition: client.h:259
Realms::Realm * find_realm(const std::string &name)
Definition: realms.cpp:64
#define UOBJ_ELF_MALE
Definition: objtype.h:130
std::unique_ptr< MessageTypeFilter > game_filter
Definition: network.h:74
#define ERROR_PRINT
Definition: logfacility.h:230
void schedule_executor(UOExecutor *ex)
Definition: scrsched.cpp:662
unsigned short x
Definition: startloc.h:24
const Mobile::Attribute * pAttrDexterity
Definition: uvars.h:181
StartingLocations startlocations
Definition: uvars.h:145
void set_privs(const std::string &privlist)
Definition: charactr.cpp:1178
void set(T *ptr)
Definition: refptr.h:276
ref_ptr< Bscript::EScriptProgram > find_script(const std::string &name, bool complain_if_not_found, bool cache_script)
Definition: scrstore.cpp:38
unsigned short y
Definition: startloc.h:25
bool equippable(const Items::Item *item) const
Definition: charactr.cpp:1356
unsigned short character_slots
Definition: polcfg.h:65
void ClientCreateCharKR(Network::Client *client, PKTIN_8D *msg)
Definition: create.cpp:595
Bscript::BObjectImp * make_mobileref(Mobile::Character *chr)
Definition: mkscrobj.cpp:14
unsigned char default_cmdlevel() const
Definition: account.cpp:282
Definition: berror.cpp:12
#define LOGIN_ERROR_OTHER_CHAR_INUSE
Definition: pktdef.h:64
boost_utils::object_name_flystring name_
Definition: uobject.h:272
char Name[30]
Definition: pktin.h:38
Module::OSExecutorModule * os_module
Definition: uoexec.h:37
void ClientCreateChar70160(Network::Client *client, PKTIN_F8 *msg)
Definition: create.cpp:953
bool validbeard(u16 BeardStyle)
Definition: create.cpp:153
UoClientGeneral uoclient_general
Definition: network.h:67