Advertisement
Guest User

Untitled

a guest
Aug 14th, 2016
163
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 63.16 KB | None | 0 0
  1. // Author: cvet
  2. #include "_macros.fos"
  3. #include "_msgstr.fos"
  4. #include "_teams.fos"
  5. #include "_npc_pids.fos"
  6. #include "_animation.fos"
  7. #include "teams_table.fos"
  8.  
  9. // Imports
  10. import void InitializeGame() from "config";
  11. import bool OnUseExplode( Critter& cr, Item& explode, Critter@ targetCr, Item@ targetItem, Scenery@ targetScen, uint timer ) from "explode";
  12. import bool UseItemOnCar( Critter& cr, Item& car, Item& item ) from "car";
  13. import bool UseSkillOnCar( Critter& cr, Item& car, int skill ) from "car";
  14. import void WorldmapInit() from "worldmap";
  15. import void SetStartLocation( Critter& cr ) from "replication";
  16. import void SetReplicationTime( Critter& cr ) from "replication";
  17. import void ReplicateCritter( Critter& cr ) from "replication";
  18. import void CombatAttack( Critter& cr, Critter& target, ProtoItem& weapon, uint8 weaponMode, ProtoItem@ ammo ) from "combat";
  19. import bool TryRepairItem( Critter& cr, Item& item ) from "repair";
  20. import bool WantedSignSet( Item& wantedSign, string& name, uint cost ) from "wanted";
  21. import bool IsReadableBook( uint16 pid ) from "books";
  22. import void TryReadBook( Critter& cr, Item& book ) from "books";
  23. import void UseDrug( Critter& cr, Item& drug ) from "drugs";
  24. import void UseDrugOn( Critter& cr, Critter& onCr, Item& drug ) from "drugs";
  25. import bool UseGeiger( Critter& cr, Item& geiger ) from "geiger";
  26. import bool UseItemOnGeiger( Critter& cr, Item& geiger, Item& item ) from "geiger";
  27. import bool UseSkillOnGeiger( Critter& cr, Item& geiger, int skill ) from "geiger";
  28. import void CallTownSupply( Critter& victim, Critter& hostile ) from "town_supply";
  29. import bool AddAttackPlane( Critter& npc, uint priority, Critter& target ) from "npc_planes";
  30. import bool AddAttackPlane( Critter& npc, uint priority, Critter& target, int minHp ) from "npc_planes";
  31. import bool UseSkillOnLocker( Critter& cr, Item& locker, int skill ) from "lockers";
  32. import bool PerkCheck( Critter& cr, uint perk ) from "perks";
  33. import void CritterGenerate( Critter& cr ) from "parameters";
  34. import void CaravansInit() from "caravan";
  35. import int GetDeteriorationProcent( Item& item ) from "repair";
  36. import void SetDeterioration( Item& item, int deteriorationProcent ) from "repair";
  37. import void NpcProcessLevel( Critter& npc ) from "parameters";
  38. import bool IsPermanentDeath( uint id ) from "replication";
  39. import bool SetPlayerStoryLineLocation( Critter& player ) from "q_main_intro";
  40. import void EditRadioSettings( Critter& player, Item& radio ) from "radio";
  41. import void SetNoPvp() from "nopvp_maps";
  42. import void OnCritterMapOut( Critter& cr, Map& map ) from "nopvp_maps";
  43. import void OnCitterMapIn( Critter& cr, Map& map ) from "nopvp_maps";
  44. import void DeclareEvents() from "game_event";
  45. import void CheckAllGameEvents() from "game_event";
  46. import void PlaceLoot2Container( Map& map, Critter& cr ) from "game_event_caches";
  47. import void StartMessenger() from "bulletin_board";
  48. import void SaveMessengerData() from "bulletin_board";
  49. import void CountEmpRocketTests( Critter& player, Critter& target ) from "navarro";
  50. import void OnCritterKilled( Critter& cr, Critter@ killer ) from "q_hunter";
  51. import bool BarterAllowed( int crId, Item@[]& buyItems, Item@[]& saleItems ) from "limited_barter";
  52. import uint CheckPlayerName( const string& name ) from "parameters";
  53.  
  54. // //////////////////////////////////////////////////////////////////////////////////////////////////
  55. // Called before world generation.
  56. void init()
  57. {
  58. InitializeGame();
  59. }
  60.  
  61. // //////////////////////////////////////////////////////////////////////////////////////////////////
  62. // Call on start server.
  63. bool start()
  64. {
  65. // Send info about others critters
  66. // Remember:
  67. // - all this info can be hacked in client;
  68. // - more parameters - more traffic.
  69. SetSendParameter( ST_GENDER, true );
  70. SetSendParameter( ST_AGE, true );
  71. SetSendParameter( ST_FOLLOW_CRIT, true );
  72. SetSendParameter( ST_PLAYER_KARMA, true );
  73. // Armor class, uses Agility
  74. SetSendParameter( ST_ARMOR_CLASS, true );
  75. SetSendParameter( ST_TURN_BASED_AC, true );
  76. // Agility
  77. SetSendParameter( ST_AGILITY, true );
  78. // Hit points, uses Strenght and Endurance
  79. SetSendParameter( ST_MAX_LIFE, true );
  80. SetSendParameter( ST_CURRENT_HP, true );
  81. // Strenght, uses battle timeout
  82. SetSendParameter( ST_STRENGTH, true );
  83. SetSendParameter( PE_ADRENALINE_RUSH, true );
  84. // Battle timeout
  85. SetSendParameter( TO_BATTLE, true );
  86. // Endurance
  87. SetSendParameter( ST_ENDURANCE, true );
  88. // Injures
  89. SetSendParameter( DAMAGE_EYE, true );
  90. SetSendParameter( DAMAGE_RIGHT_ARM, true );
  91. SetSendParameter( DAMAGE_LEFT_ARM, true );
  92. SetSendParameter( DAMAGE_RIGHT_LEG, true );
  93. SetSendParameter( DAMAGE_LEFT_LEG, true );
  94. // Item slots, passed with -
  95. SetSendParameter( -SLOT_HAND1, true, "fonline_tla.dll@allowSlot_Hand1" );
  96. SetSendParameter( -SLOT_ARMOR, true );
  97. // Some flags for correct client working
  98. SetSendParameter( MODE_NO_BARTER, true );
  99. SetSendParameter( MODE_NO_STEAL, true );
  100. SetSendParameter( MODE_NO_LOOT, true );
  101. SetSendParameter( MODE_NO_FLATTEN, true );
  102. SetSendParameter( MODE_NO_TALK, true );
  103. // 3d animation layers
  104. #ifdef PLAYERS_3D
  105. // Enable sending 3d layers, from Skin to Backpack
  106. uint fromLayer = ST_ANIM3D_LAYERS + ANIM3D_LAYER_SKIN;
  107. uint toLayer = ST_ANIM3D_LAYERS + ANIM3D_LAYER_BACKPACK;
  108. for( uint i = fromLayer; i <= toLayer; i++ )
  109. SetSendParameter( i, true );
  110. #endif
  111. // Npc talk distance
  112. SetSendParameter( ST_TALK_DISTANCE, true );
  113. // Dialog id
  114. SetSendParameter( ST_DIALOG_ID, true );
  115. // To see pid of unarmed attack
  116. SetSendParameter( ST_HANDS_ITEM_AND_MODE, true );
  117. // Scale factor
  118. SetSendParameter( ST_SCALE_FACTOR, true );
  119. // Walk / Run speed
  120. SetSendParameter( ST_WALK_TIME, true );
  121. SetSendParameter( ST_RUN_TIME, true );
  122.  
  123. // Send item data masks
  124. // Look fields in fonline.h 'struct Item::ItemData'
  125. // SortValue Info Indicator PicMapHash PicInvHash AnimWaitBase AStay[2] AShow[2] AHide[2] Flags Rate LightDist Inten Flags LightColor ScriptId TrapValue Count Cost ScriptValues[10] Other 36 bytes
  126. // ITEM_DATA_MASK_CHOSEN ITEM_DATA_MASK_CHOSEN ITEM_DATA_MASK_CHOSEN
  127. int8[] mask0 = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0 };
  128. // ITEM_DATA_MASK_CRITTER ITEM_DATA_MASK_CRITTER ITEM_DATA_MASK_CRITTER
  129. int8[] mask1 = { 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  130. // ITEM_DATA_MASK_CRITTER_EXT ITEM_DATA_MASK_CRITTER_EXT ITEM_DATA_MASK_CRITTER_EXT
  131. int8[] mask2 = { 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0 };
  132. // ITEM_DATA_MASK_CONTAINER ITEM_DATA_MASK_CONTAINER ITEM_DATA_MASK_CONTAINER
  133. int8[] mask3 = { -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0 };
  134. // ITEM_DATA_MASK_MAP ITEM_DATA_MASK_MAP ITEM_DATA_MASK_MAP
  135. int8[] mask4 = { -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0 };
  136. SetItemDataMask( ITEM_DATA_MASK_CHOSEN, mask0 );
  137. SetItemDataMask( ITEM_DATA_MASK_CRITTER, mask1 );
  138. SetItemDataMask( ITEM_DATA_MASK_CRITTER_EXT, mask2 );
  139. SetItemDataMask( ITEM_DATA_MASK_CONTAINER, mask3 );
  140. SetItemDataMask( ITEM_DATA_MASK_MAP, mask4 );
  141.  
  142. // Global map initialization
  143. WorldmapInit();
  144.  
  145. // Caravans initialization
  146. CaravansInit();
  147.  
  148. // NoPvP initialization
  149. SetNoPvp();
  150.  
  151. // Game Events initialization
  152. DeclareEvents();
  153. CheckAllGameEvents();
  154.  
  155. // Bulletin boards
  156. StartMessenger();
  157. return true;
  158. }
  159.  
  160. // //////////////////////////////////////////////////////////////////////////////////////////////////
  161. // Call on world initialization.
  162. // Parameter Min Max
  163. // multiplier 1 50000
  164. // year 1700 30000
  165. // month 1 12
  166. // day 1 31
  167. // hour 0 23
  168. // minute 0 59
  169. void get_start_time( uint16& multiplier, uint16& year, uint16& month, uint16& day, uint16& hour, uint16& minute )
  170. {
  171. multiplier = 20;
  172. year = 2246;
  173. month = 10;
  174. day = 30;
  175. hour = 1;
  176. minute = 0;
  177. }
  178.  
  179. // //////////////////////////////////////////////////////////////////////////////////////////////////
  180. // Call on finish server.
  181. void finish()
  182. {}
  183.  
  184. // //////////////////////////////////////////////////////////////////////////////////////////////////
  185. // Call every returned value, in milliseconds.
  186. // Return next call in milliseconds or zero to disable loop.
  187. uint loop()
  188. {
  189. return 0; // Disable
  190. }
  191.  
  192. // //////////////////////////////////////////////////////////////////////////////////////////////////
  193. // Call on something critter attack another.
  194. void critter_attack( Critter& cr, Critter& target, ProtoItem& weapon, uint8 weaponMode, ProtoItem@ ammo )
  195. {
  196.  
  197. if( cr.IsPlayer() && valid( ammo ) && ammo.ProtoId == PID_EMP_ROCKET && target.Stat[ ST_BODY_TYPE ] == BT_ROBOT )
  198. CountEmpRocketTests( cr, target );
  199. cr.TimeoutBase[ TO_AGGRESSOR ] = __FullSecond + REAL_MINUTE( 1 );
  200. CombatAttack( cr, target, weapon, weaponMode, ammo );
  201. }
  202.  
  203. // //////////////////////////////////////////////////////////////////////////////////////////////////
  204. // Call on something critter attack another.
  205. void critter_attacked( Critter& cr, Critter& attacker )
  206. {
  207. if( cr.IsPlayer() )
  208. return; // Diable player helping
  209. else
  210. AddAttackPlane( cr, 0, attacker ); // Answer on attack
  211. // if(cr.Timeout[TO_AGGRESSOR]>0) return;
  212.  
  213. uint helpers = 0;
  214. uint maxHelpers = 10 - attacker.Stat[ ST_CHARISMA ];
  215. maxHelpers = CLAMP( maxHelpers, 2, 8 );
  216.  
  217. Critter@[] critters;
  218. cr.GetCritters( true, FIND_LIFE_AND_KO | FIND_ONLY_NPC, critters );
  219. for( uint i = 0, j = critters.length(); i < j; i++ )
  220. {
  221. NpcPlane@ plane = critters[ i ].GetCurPlane();
  222. if( valid( plane ) && plane.Type == AI_PLANE_ATTACK && plane.Attack_TargId == attacker.Id )
  223. {
  224. helpers++;
  225. if( helpers >= maxHelpers )
  226. return; // No help needed
  227. @critters[ i ] = null; // Exclude this critter
  228. }
  229. }
  230.  
  231. int crHpProc = cr.Stat[ ST_CURRENT_HP ] * 100 / cr.Stat[ ST_MAX_LIFE ];
  232. uint teamId = cr.Stat[ ST_TEAM_ID ];
  233. uint attackerTeamId = attacker.Stat[ ST_TEAM_ID ];
  234. for( uint i = 0, j = critters.length(); i < j; i++ )
  235. {
  236. Critter@ someCr = critters[ i ];
  237. if( not valid( someCr ) )
  238. continue; // Skip excluded
  239.  
  240. uint someCrTeamId = someCr.Stat[ ST_TEAM_ID ];
  241. if( attackerTeamId == someCrTeamId )
  242. continue; // No attack temmate
  243.  
  244. int teamParity = TEAM_PARITY( someCrTeamId, teamId ); // From -> To
  245. switch( teamParity )
  246. {
  247. case Ignore:
  248. continue;
  249. case Anyway:
  250. break;
  251. case NotBusy:
  252. if( someCr.IsCurPlane( AI_PLANE_ATTACK ) )
  253. continue;
  254. break;
  255. case HpLess10:
  256. if( crHpProc >= 10 )
  257. continue;
  258. break;
  259. case HpLess30:
  260. if( crHpProc >= 30 )
  261. continue;
  262. break;
  263. case HpLess50:
  264. if( crHpProc >= 50 )
  265. continue;
  266. break;
  267. case IfDead:
  268. if( not cr.IsDead() )
  269. continue;
  270. break;
  271. case GoodPerson:
  272. if( attacker.IsNpc() || cr.Stat[ ST_CHARISMA ] < 5 || cr.Stat[ ST_KARMA ] < 0 )
  273. continue;
  274. break;
  275. default:
  276. continue;
  277. }
  278.  
  279. AddAttackPlane( someCr, 0, attacker );
  280. helpers++;
  281. if( helpers >= maxHelpers )
  282. break; // Enough
  283. }
  284. }
  285.  
  286. // //////////////////////////////////////////////////////////////////////////////////////////////////
  287. // Call on something critter steal another.
  288. bool critter_stealing( Critter& cr, Critter& thief, Item& item, uint count )
  289. {
  290. thief.TimeoutBase[ TO_AGGRESSOR ] = __FullSecond + REAL_MINUTE( 1 );
  291.  
  292. if( cr.IsDead() || cr.Timeout[ TO_BATTLE ] > 0 || thief.Timeout[ TO_BATTLE ] > 0 )
  293. {
  294. thief.StatBase[ ST_LAST_STEAL_CR_ID ] = 0;
  295. thief.StatBase[ ST_STEAL_COUNT ] = 0;
  296. return false;
  297. }
  298.  
  299. if( cr.Mode[ MODE_NO_PVP ] != 0 || thief.Mode[ MODE_NO_PVP ] != 0 )
  300. {
  301. thief.Say( SAY_NETMSG, "No PvP." );
  302. return false;
  303. }
  304.  
  305. int dir1 = cr.Dir;
  306. int dir2 = thief.Dir;
  307. int kDir = MAX( dir1, dir2 ) - MIN( dir1, dir2 );
  308. if( kDir > 3 )
  309. kDir = 6 - kDir;
  310.  
  311. int steal = thief.Skill[ SK_STEAL ];
  312. if( steal <= 0 )
  313. steal = 1;
  314. int size = item.Proto.Volume;
  315. if( size <= 0 )
  316. size = 1;
  317.  
  318. // Perk pickpocket, ignore size and facing
  319. if( thief.Perk[ PE_PICKPOCKET ] != 0 )
  320. {
  321. kDir = 0;
  322. size = 1;
  323. }
  324.  
  325. // Count modifier
  326. int kCount = count / steal;
  327. if( kCount <= 0 )
  328. kCount = 1;
  329.  
  330. // Check time of stealing
  331. uint lastStealCrId = thief.Stat[ ST_LAST_STEAL_CR_ID ];
  332. uint stealCount = thief.Stat[ ST_STEAL_COUNT ];
  333. if( lastStealCrId == cr.Id && thief.Timeout[ TO_STEALING ] > 0 )
  334. steal -= steal * stealCount * 9 / 100;
  335.  
  336. // Calc
  337. int k = ( steal - kDir * 10 ) / ( size * kCount );
  338. k = CLAMP( k, 5, 95 );
  339. bool success = !( Random( 1, 100 ) > k );
  340.  
  341. if( success )
  342. {
  343. // Add experience 10,30,60,100,150,210,280,360,450,550,660,780
  344. const int[] stealExp = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120 };
  345.  
  346. if( lastStealCrId == cr.Id && thief.Timeout[ TO_STEALING ] > 0 )
  347. {
  348. stealCount++;
  349. if( stealCount > 11 )
  350. stealCount = 11;
  351. thief.StatBase[ ST_STEAL_COUNT ] = stealCount;
  352. }
  353. else
  354. {
  355. thief.StatBase[ ST_LAST_STEAL_CR_ID ] = cr.Id;
  356. thief.StatBase[ ST_STEAL_COUNT ] = 0;
  357. }
  358.  
  359. thief.TimeoutBase[ TO_STEALING ] = STEAL_TIMEOUT( thief );
  360. if( cr.IsNpc() )
  361. {
  362. GameVar@ stealExpCount = ::GetUnicumVar( UVAR_steal_exp_count, cr.Id, thief.Id );
  363. if( stealExpCount < 12 )
  364. {
  365. thief.StatBase[ ST_EXPERIENCE ] += stealExp[ stealCount ];
  366. thief.AddScore( SCORE_THIEF, 1 );
  367. }
  368. stealExpCount = stealExpCount + 1;
  369. }
  370. }
  371. else
  372. {
  373. thief.StatBase[ ST_LAST_STEAL_CR_ID ] = 0;
  374. thief.StatBase[ ST_STEAL_COUNT ] = 0;
  375.  
  376. if( cr.IsNpc() )
  377. {
  378. int thiefHp = thief.Stat[ ST_CURRENT_HP ];
  379. AddAttackPlane( cr, 0, thief, thiefHp< 10 || Random( 1, 10 ) > cr.Stat[ ST_LUCK ] + 4 || cr.Stat[ ST_CHARISMA ] < 3 ? __DeadHitPoints : Random( thiefHp / 4, thiefHp / 2 ) );
  380. }
  381. }
  382.  
  383. return success;
  384. }
  385.  
  386. // //////////////////////////////////////////////////////////////////////////////////////////////////
  387. // Call on something critter use item.
  388. bool critter_use_item( Critter& cr, Item& item, Critter@ targetCr, Item@ targetItem, Scenery@ targetScen, uint param )
  389. {
  390. bool isPlayer = cr.IsPlayer();
  391. uint16 pid = item.GetProtoId();
  392. bool useOnSelf = ( not valid( targetCr ) && not valid( targetItem ) && not valid( targetScen ) );
  393.  
  394. // Radio
  395. if( FLAG( item.Flags, ITEM_RADIO ) && useOnSelf )
  396. {
  397. if( isPlayer )
  398. EditRadioSettings( cr, item );
  399. return true;
  400. }
  401.  
  402. // Book reading
  403. if( useOnSelf && IsReadableBook( pid ) )
  404. {
  405. TryReadBook( cr, item );
  406. return true;
  407. }
  408.  
  409. // Explosion
  410. if( OnUseExplode( cr, item, targetCr, targetItem, targetScen, param ) )
  411. return true;
  412.  
  413. // Cars
  414. if( valid( targetItem ) && targetItem.GetType() == ITEM_TYPE_CAR && UseItemOnCar( cr, targetItem, item ) )
  415. return true;
  416.  
  417. // Drugs
  418. if( item.GetType() == ITEM_TYPE_DRUG )
  419. {
  420. if( useOnSelf )
  421. UseDrug( cr, item );
  422. else if( valid( targetCr ) )
  423. UseDrugOn( cr, targetCr, item );
  424. else
  425. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  426. return true;
  427. }
  428.  
  429. // Play dice
  430. if( pid == PID_DICE )
  431. {
  432. cr.SayMsg( SAY_EMOTE_ON_HEAD, TEXTMSG_TEXT, STR_DICE_THROW, "$result" + Random( 1, 6 ) );
  433. return true;
  434. }
  435. if( pid == PID_LOADED_DICE )
  436. {
  437. cr.SayMsg( SAY_EMOTE_ON_HEAD, TEXTMSG_TEXT, STR_DICE_THROW, "$result" + uint( ( item.Id % 6 ) + 1 ) );
  438. return true;
  439. }
  440.  
  441. // Magic ball
  442. if( pid == PID_MAGIC_8_BALL )
  443. {
  444. // cr.SayMsg(SAY_EMOTE_ON_HEAD,TEXTMSG_TEXT,Random(1,2)==1?STR_MAGIC_BALL_YES:STR_MAGIC_BALL_NO);
  445. return true;
  446. }
  447.  
  448. // Cosmetic
  449. if( pid == PID_COSMETIC_CASE && cr.Stat[ ST_GENDER ] == GENDER_FEMALE )
  450. {
  451. cr.SayMsg( SAY_EMOTE_ON_HEAD, TEXTMSG_TEXT, STR_COSMETIC_USE );
  452. return true;
  453. }
  454.  
  455. // Cigarettes smoking
  456. if( pid == PID_CIGARETTES && _CritCountItem( cr, PID_LIGHTER ) > 0 )
  457. {
  458. cr.SayMsg( SAY_EMOTE_ON_HEAD, TEXTMSG_TEXT, STR_CIGARETTES_SMOKE );
  459. return true;
  460. }
  461.  
  462. // Geiger counter
  463. if( pid == PID_GEIGER_COUNTER && useOnSelf && UseGeiger( cr, item ) )
  464. return true;
  465. if( valid( targetItem ) && targetItem.GetProtoId() == PID_GEIGER_COUNTER && UseItemOnGeiger( cr, targetItem, item ) )
  466. return true;
  467.  
  468. // Take process to engine
  469. return false;
  470. }
  471.  
  472. // //////////////////////////////////////////////////////////////////////////////////////////////////
  473. // Call on something critter use skill.
  474. bool critter_use_skill( Critter& cr, int skill, Critter@ targetCr, Item@ targetItem, Scenery@ targetScen )
  475. {
  476. bool isPlayer = cr.IsPlayer();
  477.  
  478. // Cars
  479. if( valid( targetItem ) && targetItem.GetType() == ITEM_TYPE_CAR && UseSkillOnCar( cr, targetItem, skill ) )
  480. return true;
  481.  
  482. // Geiger counter
  483. if( valid( targetItem ) && targetItem.GetProtoId() == PID_GEIGER_COUNTER && UseSkillOnGeiger( cr, targetItem, skill ) )
  484. return true;
  485.  
  486. // Doors or containers
  487. if( valid( targetItem ) && ( targetItem.GetType() == ITEM_TYPE_DOOR || targetItem.GetType() == ITEM_TYPE_CONTAINER ) && UseSkillOnLocker( cr, targetItem, skill ) )
  488. return true;
  489.  
  490. switch( skill )
  491. {
  492. case SKILL_PICK_ON_GROUND: // Pick item or scenery on ground
  493. {
  494. // Scenery
  495. if( valid( targetScen ) )
  496. {
  497. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  498. return true;
  499. }
  500.  
  501. // Wanted
  502. if( valid( targetItem ) && targetItem.GetProtoId() == PID_WANTED_SIGN && WantedSignSet( targetItem, cr.Name, Random( 1000, 2000 ) ) )
  503. return true;
  504. // Explosion
  505. if( valid( targetItem ) && targetItem.GetProtoId() == PID_ACTIVE_MINE && OnUseExplode( cr, targetItem, null, null, null, 0 ) )
  506. return true;
  507.  
  508. // Pick some item
  509. if( valid( targetItem ) )
  510. {
  511. Item@ item = targetItem;
  512. if( not FLAG( item.Flags, ITEM_CAN_PICKUP ) )
  513. {
  514. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  515. break;
  516. }
  517.  
  518. int freeWeight = cr.Stat[ ST_CARRY_WEIGHT ] - cr.ItemsWeight();
  519. if( freeWeight >= int( item.Proto.Weight * item.GetCount() ) )
  520. {
  521. // Pick full
  522. MoveItem( item, 0, cr );
  523. }
  524. else
  525. {
  526. // Pick half
  527. if( item.IsStackable() && freeWeight >= int(item.Proto.Weight) )
  528. MoveItem( item, freeWeight / item.Proto.Weight, cr );
  529. // Overweight
  530. else
  531. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_OVERWEIGHT );
  532. }
  533. }
  534. }
  535. break;
  536. case SKILL_PUT_CONT: // Put item in container, only targetItem is valid
  537. case SKILL_TAKE_CONT: // Take item from container, only targetItem is valid
  538. case SKILL_TAKE_ALL_CONT: // Take all items from critter or item container
  539. return false; // Allow transactions
  540. case SKILL_LOOT_CRITTER: // Loot critter, only targetCr is valid
  541. cr.Action( ACTION_PICK_CRITTER, 0, null );
  542. cr.ShowContainer( targetCr, null, TRANSFER_CRIT_LOOT );
  543. return true;
  544. case SKILL_PUSH_CRITTER: // Push critter, only targetCr is valid
  545. cr.Action( ACTION_PICK_CRITTER, 2, null );
  546. if( ( cr.Timeout[ TO_BATTLE ] == 0 && targetCr.Timeout[ TO_BATTLE ] == 0 ) &&
  547. ( targetCr.IsPlayer() || ( targetCr.IsNoPlanes() && targetCr.GetTalkedPlayers( null ) == 0 ) ) )
  548. targetCr.MoveRandom();
  549. return true;
  550. case SK_SCIENCE:
  551. {
  552. // Radio
  553. if( valid( targetItem ) && FLAG( targetItem.Flags, ITEM_RADIO ) && targetItem.Accessory == ACCESSORY_CRITTER && targetItem.CritId == cr.Id )
  554. {
  555. if( isPlayer )
  556. EditRadioSettings( cr, targetItem );
  557. return true;
  558. }
  559.  
  560. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING ); // Todo: "You fail to learn anything."
  561. }
  562. break;
  563. case SK_REPAIR:
  564. {
  565. // Generic repair
  566. if( valid( targetItem ) && targetItem.Accessory == ACCESSORY_CRITTER && targetItem.IsDeteriorable() && TryRepairItem( cr, targetItem ) )
  567. return true;
  568.  
  569. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  570. }
  571. break;
  572. case SK_SNEAK:
  573. {
  574. if( cr.Mode[ MODE_HIDE ] != 0 )
  575. cr.ModeBase[ MODE_HIDE ] = 0;
  576. else if( not isPlayer )
  577. cr.ModeBase[ MODE_HIDE ] = 1;
  578. else
  579. {
  580. if( cr.Timeout[ TO_SNEAK ] > 0 )
  581. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_TIMEOUT_SNEAK_WAIT );
  582. else if( IS_TURN_BASED_TIMEOUT( cr ) )
  583. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_TIMEOUT_BATTLE_WAIT );
  584. // else if(cr.GetCritters(true,FIND_LIFE,null)>0)
  585. // {
  586. // cr.TimeoutBase[TO_SNEAK]=SNEAK_TIMEOUT(cr);
  587. // cr.SayMsg(SAY_NETMSG,TEXTMSG_GAME,STR_SKILL_SNEAK_VISIBLE);
  588. // }
  589. else
  590. cr.ModeBase[ MODE_HIDE ] = 1;
  591. }
  592. }
  593. break;
  594. case SK_STEAL:
  595. {
  596. if( valid( targetItem ) )
  597. {
  598. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  599. }
  600. else if( valid( targetCr ) )
  601. {
  602. // Loot
  603. if( targetCr.IsDead() )
  604. {
  605. cr.Action( ACTION_PICK_CRITTER, 0, null );
  606. cr.ShowContainer( targetCr, null, TRANSFER_CRIT_LOOT );
  607. }
  608. // Steal
  609. else
  610. {
  611. if( isPlayer && cr.Timeout[ TO_SK_STEAL ] > 0 )
  612. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_WEARINESS );
  613. else
  614. {
  615. cr.Action( ACTION_PICK_CRITTER, 1, null );
  616. cr.ShowContainer( targetCr, null, TRANSFER_CRIT_STEAL );
  617. cr.TimeoutBase[ TO_SK_STEAL ] = STEAL_TIMEOUT( cr );
  618. cr.StatBase[ ST_LAST_STEAL_CR_ID ] = 0;
  619. cr.StatBase[ ST_STEAL_COUNT ] = 0;
  620. }
  621. }
  622. }
  623. else
  624. {
  625. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  626. }
  627. }
  628. break;
  629. case SK_FIRST_AID:
  630. {
  631. if( valid( targetItem ) || valid( targetScen ) )
  632. {
  633. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  634. break;
  635. }
  636.  
  637. if( not valid( targetCr ) )
  638. @targetCr = cr;
  639. bool is_self = ( targetCr.Id == cr.Id );
  640.  
  641. if( targetCr.IsDead() )
  642. {
  643. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_NORESSURECT );
  644. break;
  645. }
  646.  
  647. if( targetCr.Stat[ ST_CURRENT_HP ] >= targetCr.Stat[ ST_MAX_LIFE ] )
  648. {
  649. if( _CritIsInjured( targetCr ) )
  650. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_NOFIRSTAID_NEEDDOCTOR );
  651. else if( is_self )
  652. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_NONEED_FIRSTAID );
  653. break;
  654. }
  655.  
  656. if( isPlayer && cr.Timeout[ TO_SK_FIRST_AID ] > 0 )
  657. {
  658. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_WEARINESS );
  659. break;
  660. }
  661.  
  662. int sk = cr.Skill[ SK_FIRST_AID ];
  663. uint8 mode = 0;
  664. uint16 activePid = cr.GetSlotProto( SLOT_HAND1, mode ).ProtoId;
  665. if( activePid == PID_FIRST_AID_KIT )
  666. {
  667. sk += 25;
  668. if( Random( 0, 30 ) == 0 )
  669. cr.DeleteItem( PID_FIRST_AID_KIT, 1 );
  670. }
  671. else if( activePid == PID_FIELD_MEDIC_KIT )
  672. {
  673. sk += 50;
  674. if( Random( 0, 30 ) == 0 )
  675. cr.DeleteItem( PID_FIELD_MEDIC_KIT, 1 );
  676. }
  677. int mod = 11 - cr.Stat[ ST_LUCK ];
  678. mod = CLAMP( mod, 1, 10 );
  679. int heal = Random( sk / mod, sk );
  680. if( heal < 1 )
  681. heal = 1;
  682. if( cr.Perk[ PE_VAMPIRE_REGENERATION ] > 0 )
  683. heal += 5;
  684. int curHp = targetCr.Stat[ ST_CURRENT_HP ];
  685. int maxHp = targetCr.Stat[ ST_MAX_LIFE ];
  686. if( curHp + heal > maxHp )
  687. heal = maxHp - curHp;
  688. targetCr.StatBase[ ST_CURRENT_HP ] += heal;
  689.  
  690. cr.Say( SAY_NETMSG, "+" + heal );
  691. if( not is_self )
  692. targetCr.Say( SAY_NETMSG, "+" + heal );
  693.  
  694. if( isPlayer )
  695. {
  696. GameVar@ firstAidCount = ::GetUnicumVar( UVAR_first_aid_count, cr.Id, targetCr.Id );
  697. if( firstAidCount < 10 )
  698. cr.StatBase[ ST_EXPERIENCE ] += heal * 3;
  699. firstAidCount += 1;
  700.  
  701. cr.TimeoutBase[ TO_SK_FIRST_AID ] = FIRST_AID_TIMEOUT( cr );
  702. cr.AddScore( SCORE_DOCTOR, 1 );
  703. }
  704. }
  705. break;
  706. case SK_DOCTOR:
  707. {
  708. if( valid( targetItem ) || valid( targetScen ) )
  709. {
  710. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  711. break;
  712. }
  713.  
  714. if( not valid( targetCr ) )
  715. @targetCr = cr;
  716. bool is_self = ( targetCr.Id == cr.Id );
  717.  
  718. if( targetCr.IsDead() )
  719. {
  720. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_NORESSURECT );
  721. break;
  722. }
  723.  
  724. if( not _CritIsInjured( targetCr ) )
  725. {
  726. if( targetCr.Stat[ ST_CURRENT_HP ] < targetCr.Stat[ ST_MAX_LIFE ] )
  727. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_NODOCTOR_NEEDFIRSTAID );
  728. else if( is_self )
  729. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_NONEED_DOCTOR );
  730. break;
  731. }
  732.  
  733. if( isPlayer && cr.Timeout[ TO_SK_DOCTOR ] > 0 )
  734. {
  735. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_WEARINESS );
  736. break;
  737. }
  738.  
  739. int uninjured = 0;
  740. int sk = cr.Skill[ SK_DOCTOR ];
  741. uint8 mode = 0;
  742. uint16 activePid = cr.GetSlotProto( SLOT_HAND1, mode ).ProtoId;
  743. if( activePid == PID_DOCTORS_BAG )
  744. {
  745. sk += 25;
  746. if( Random( 0, 30 ) == 0 )
  747. cr.DeleteItem( PID_DOCTORS_BAG, 1 );
  748. }
  749. else if( activePid == PID_PARAMEDICS_BAG )
  750. {
  751. sk += 50;
  752. if( Random( 0, 30 ) == 0 )
  753. cr.DeleteItem( PID_PARAMEDICS_BAG, 1 );
  754. }
  755.  
  756. for( int i = DAMAGE_EYE; i <= DAMAGE_LEFT_LEG; ++i )
  757. {
  758. if( targetCr.Damage[ i ] != 0 )
  759. {
  760. if( sk > Random( 5, 95 ) )
  761. {
  762. targetCr.DamageBase[ i ] = 0;
  763. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_HEAL_DMG( i - DAMAGE_POISONED ) );
  764. uninjured++;
  765. }
  766. else
  767. {
  768. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_NOHEAL_DMG( i - DAMAGE_POISONED ) );
  769. }
  770. sk /= 2;
  771. }
  772. }
  773.  
  774. if( isPlayer )
  775. {
  776. if( uninjured > 0 )
  777. cr.StatBase[ ST_EXPERIENCE ] += uninjured * 50;
  778. cr.TimeoutBase[ TO_SK_DOCTOR ] = DOCTOR_TIMEOUT( cr );
  779. cr.AddScore( SCORE_DOCTOR, uninjured );
  780. }
  781. }
  782. break;
  783. case SK_LOCKPICK:
  784. {
  785. // Lockers processed in lockers.fos
  786. }
  787. break;
  788. case SK_TRAPS:
  789. {
  790. // Explosion
  791. if( valid( targetItem ) )
  792. {
  793. uint16 pid = targetItem.GetProtoId();
  794. if( ( pid == PID_ACTIVE_DYNAMITE || pid == PID_ACTIVE_PLASTIC_EXPLOSIVE || pid == PID_ACTIVE_MINE ) &&
  795. OnUseExplode( cr, targetItem, null, null, null, 0 ) )
  796. return true;
  797. }
  798.  
  799. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  800. }
  801. break;
  802. default:
  803. {
  804. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING );
  805. }
  806. break;
  807. }
  808.  
  809. return true;
  810. }
  811.  
  812. // //////////////////////////////////////////////////////////////////////////////////////////////////
  813. // Call on something critter reload weapon.
  814. // If ammo is not valid than only unload.
  815. void critter_reload_weapon( Critter& cr, Item& weapon, Item@ ammo )
  816. {
  817. // Special weapons
  818. if( weapon.Proto.Weapon_Caliber == 0 )
  819. {
  820. if( weapon.GetProtoId() == PID_SOLAR_SCORCHER )
  821. {
  822. if( IS_NIGHT( __Hour ) )
  823. cr.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_SOLAR_SCORCHER_NO_LIGHT );
  824. else
  825. {
  826. weapon.AmmoCount = weapon.Proto.Weapon_MaxAmmoCount;
  827. weapon.Update();
  828. }
  829. }
  830.  
  831. return;
  832. }
  833.  
  834. // Other weapons
  835. // Unload
  836. if( not valid( ammo ) || ( weapon.AmmoCount > 0 && weapon.AmmoPid != ammo.GetProtoId() ) )
  837. {
  838. if( weapon.AmmoPid != 0 )
  839. {
  840. Item@ existAmmo = cr.GetItem( weapon.AmmoPid, -1 );
  841. if( not valid( existAmmo ) )
  842. cr.AddItem( weapon.AmmoPid, weapon.AmmoCount );
  843. else
  844. _IncItem( existAmmo, weapon.AmmoCount );
  845. }
  846. weapon.AmmoCount = 0;
  847. }
  848.  
  849. // Load
  850. if( valid( ammo ) )
  851. {
  852. uint count = MIN( ammo.GetCount(), weapon.Proto.Weapon_MaxAmmoCount - weapon.AmmoCount );
  853. weapon.AmmoCount += count;
  854. weapon.AmmoPid = ammo.GetProtoId();
  855. _SubItem( ammo, count );
  856. }
  857.  
  858. weapon.Update();
  859. }
  860.  
  861. // //////////////////////////////////////////////////////////////////////////////////////////////////
  862. // Call on player register/login in game or npc created/loaded.
  863. // Default start position for players is center of global map.
  864. void critter_init( Critter& cr, bool firstTime )
  865. {
  866. if( firstTime )
  867. {
  868. if( cr.IsPlayer() )
  869. {
  870. // Input: 7 special, 3 tag skills, 2 traits, age, gender
  871. uint traits = 0;
  872. for( uint i = TRAIT_BEGIN; i <= TRAIT_END; i++ )
  873. {
  874. if( cr.TraitBase[ i ] != 0 && traits < 2 )
  875. {
  876. cr.TraitBase[ i ] = 1;
  877. traits++;
  878. }
  879. else
  880. cr.TraitBase[ i ] = 0;
  881. }
  882.  
  883. if( cr.StatBase[ ST_GENDER ] < 0 || cr.StatBase[ ST_GENDER ] > 1 )
  884. cr.StatBase[ ST_GENDER ] = 0;
  885. if( cr.StatBase[ ST_AGE ] < 14 || cr.StatBase[ ST_AGE ] > 80 )
  886. cr.StatBase[ ST_AGE ] = 25;
  887. for( uint i = ST_STRENGTH; i <= ST_LUCK; i++ )
  888. cr.StatBase[ i ] = CLAMP( cr.StatBase[ i ], 1, 10 );
  889.  
  890. if( ( cr.StatBase[ ST_STRENGTH ] + cr.StatBase[ ST_PERCEPTION ] + cr.StatBase[ ST_ENDURANCE ] +
  891. cr.StatBase[ ST_CHARISMA ] + cr.StatBase[ ST_INTELLECT ] + cr.StatBase[ ST_AGILITY ] + cr.StatBase[ ST_LUCK ] ) != __StartSpecialPoints )
  892. {
  893. for( uint i = ST_STRENGTH; i <= ST_LUCK; i++ )
  894. cr.StatBase[ i ] = 5;
  895. }
  896.  
  897. cr.StatBase[ ST_EMP_RESIST ] = 500;
  898. cr.AddHolodiskInfo( 42 ); // Journalist's research
  899.  
  900. // Default skin
  901. #ifdef PLAYERS_3D
  902. if( cr.StatBase[ ST_GENDER ] == GENDER_MALE )
  903. {
  904. cr.StatBase[ ST_BASE_CRTYPE ] = CLAMP( cr.StatBase[ ST_BASE_CRTYPE ], CRTYPE_3D_MALE_NORMAL, CRTYPE_3D_MALE_FAT );
  905. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_HAIR ] = CLAMP( cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_HAIR ], 0, ATTRIBUTE_Hair_Male_Shoulder + ATTRIBUTE_COLOR_RedGrey );
  906. if( cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_MUSTACHE ] != ATTRIBUTE_Mustache_MadMax )
  907. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_MUSTACHE ] = CLAMP( cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_MUSTACHE ], 0, ATTRIBUTE_Mustache_Male_Stubble + ATTRIBUTE_COLOR_RedGrey );
  908. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_BEARD ] = CLAMP( cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_BEARD ], 0, ATTRIBUTE_Beard_Male_Stubble + ATTRIBUTE_COLOR_RedGrey );
  909. }
  910. else
  911. {
  912. cr.StatBase[ ST_BASE_CRTYPE ] = CLAMP( cr.StatBase[ ST_BASE_CRTYPE ], CRTYPE_3D_FEMALE_NORMAL, CRTYPE_3D_FEMALE_FAT );
  913. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_HAIR ] = CLAMP( cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_HAIR ], 0, ATTRIBUTE_Hair_Female_Short + ATTRIBUTE_COLOR_RedGrey );
  914. if( cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_MUSTACHE ] != ATTRIBUTE_Mustache_MadMax )
  915. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_MUSTACHE ] = 0;
  916. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_BEARD ] = 0;
  917. }
  918.  
  919. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_SKIN ] = CLAMP( cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_SKIN ], ATTRIBUTE_Skin_Human_White01, ATTRIBUTE_Skin_Human_Yellow03 );
  920. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_PONYTAIL ] = CLAMP( cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_PONYTAIL ], 0, ATTRIBUTE_Ponytail_Ponytail2 + ATTRIBUTE_COLOR_RedGrey );
  921. cr.StatBase[ ST_ANIM3D_LAYERS + ANIM3D_LAYER_ARMLET ] = ATTRIBUTE_Armlet_PipBoyClosed;
  922.  
  923. cr.ChangeCrType( cr.StatBase[ ST_BASE_CRTYPE ] );
  924. #endif
  925. #ifndef PLAYERS_3D
  926. cr.ChangeCrType( cr.Stat[ ST_GENDER ] == GENDER_MALE ? CRTYPE_DEFAULT_M : CRTYPE_DEFAULT_F );
  927. #endif
  928. }
  929.  
  930. if( cr.TagSkill[ TAG_SKILL1 ] < int(SKILL_BEGIN) || cr.TagSkill[ TAG_SKILL1 ] > int(SKILL_END) )
  931. cr.TagSkillBase[ TAG_SKILL1 ] = 0;
  932. if( cr.TagSkill[ TAG_SKILL2 ] < int(SKILL_BEGIN) || cr.TagSkill[ TAG_SKILL2 ] > int(SKILL_END) )
  933. cr.TagSkillBase[ TAG_SKILL2 ] = 0;
  934. if( cr.TagSkill[ TAG_SKILL3 ] < int(SKILL_BEGIN) || cr.TagSkill[ TAG_SKILL3 ] > int(SKILL_END) )
  935. cr.TagSkillBase[ TAG_SKILL3 ] = 0;
  936. if( cr.TagSkill[ TAG_SKILL1 ] == cr.TagSkill[ TAG_SKILL2 ] )
  937. cr.TagSkillBase[ TAG_SKILL1 ] = 0;
  938. if( cr.TagSkill[ TAG_SKILL2 ] == cr.TagSkill[ TAG_SKILL3 ] )
  939. cr.TagSkillBase[ TAG_SKILL2 ] = 0;
  940. if( cr.TagSkill[ TAG_SKILL3 ] == cr.TagSkill[ TAG_SKILL1 ] )
  941. cr.TagSkillBase[ TAG_SKILL3 ] = 0;
  942.  
  943. CritterGenerate( cr );
  944. cr.StatBase[ ST_CURRENT_HP ] = cr.Stat[ ST_MAX_LIFE ];
  945. cr.StatBase[ ST_CURRENT_AP ] = cr.Stat[ ST_ACTION_POINTS ] * 100;
  946.  
  947. // for(int i=REPUTATION_BEGIN;i<=599;i++) cr.ParamBase[i]=int(0x80000000); // 599 is last number processed in client
  948.  
  949. if( cr.IsPlayer() )
  950. {
  951. for( uint i = ST_STRENGTH; i <= ST_LUCK; i++ )
  952. cr.StatBase[ i ] = CLAMP( cr.StatBase[ i ], 1, 10 );
  953.  
  954. cr.StatBase[ ST_REPLICATION_COST ] = 100;
  955. cr.StatBase[ ST_REPLICATION_MONEY ] = 0;
  956. cr.StatBase[ ST_REPLICATION_COUNT ] = 0;
  957. cr.StatBase[ ST_TEAM_ID ] = 1;
  958. cr.StatBase[ ST_DAMAGE_TYPE ] = DAMAGE_NORMAL;
  959.  
  960. SetStartLocation( cr );
  961.  
  962. // Main quest
  963. if( __MainStoryLineActive )
  964. SetPlayerStoryLineLocation( cr );
  965. }
  966. else
  967. {
  968. cr.ChangeCrType( cr.StatBase[ ST_BASE_CRTYPE ] );
  969. if( cr.Stat[ ST_LEVEL ] != 0 )
  970. NpcProcessLevel( cr );
  971. }
  972. }
  973. else
  974. {
  975. // Main quest
  976. if( cr.IsPlayer() && __MainStoryLineActive )
  977. SetPlayerStoryLineLocation( cr );
  978.  
  979. // Current skin validation
  980. Item@ armor = cr.GetItem( 0, SLOT_ARMOR );
  981. if( not valid( armor ) )
  982. {
  983. uint crType = cr.Stat[ ST_BASE_CRTYPE ];
  984. if( crType == 0 )
  985. crType = ( cr.Stat[ ST_GENDER ] == GENDER_MALE ? CRTYPE_DEFAULT_M : CRTYPE_DEFAULT_F );
  986. if( cr.CrType != crType )
  987. cr.ChangeCrType( crType );
  988. }
  989.  
  990. // Armor perk validation
  991. if( not valid( armor ) && cr.Stat[ ST_CURRENT_ARMOR_PERK ] != 0 )
  992. {
  993. switch( cr.Stat[ ST_CURRENT_ARMOR_PERK ] )
  994. {
  995. case ARMOR_PERK_POWERED:
  996. cr.StatBase[ ST_STRENGTH_EXT ] -= 3;
  997. cr.StatBase[ ST_RADIATION_RESISTANCE_EXT ] -= 30;
  998. break;
  999. case ARMOR_PERK_COMBAT:
  1000. cr.StatBase[ ST_RADIATION_RESISTANCE_EXT ] -= 20;
  1001. break;
  1002. case ARMOR_PERK_ADVANCED_I:
  1003. cr.StatBase[ ST_STRENGTH_EXT ] -= 4;
  1004. cr.StatBase[ ST_RADIATION_RESISTANCE_EXT ] -= 60;
  1005. break;
  1006. case ARMOR_PERK_ADVANCED_II:
  1007. cr.StatBase[ ST_STRENGTH_EXT ] -= 4;
  1008. cr.StatBase[ ST_RADIATION_RESISTANCE_EXT ] -= 75;
  1009. break;
  1010. case ARMOR_PERK_CHARISMA:
  1011. cr.StatBase[ ST_CHARISMA_EXT ] -= 1;
  1012. break;
  1013. default:
  1014. break;
  1015. }
  1016. cr.StatBase[ ST_CURRENT_ARMOR_PERK ] = 0;
  1017. }
  1018.  
  1019. // Clear timeouts if too long (happens when saves got removed)
  1020. for( uint i = TIMEOUT_BEGIN; i <= TIMEOUT_END; i++ )
  1021. if( i != TO_BATTLE && cr.Timeout[ i ] > int(MAXIMUM_TIMEOUT) )
  1022. cr.TimeoutBase[ i ] = __FullSecond;
  1023.  
  1024. // Erase zero time events
  1025. cr.EraseTimeEvents( 0 );
  1026.  
  1027. // TRAIT_FAST_SHOT migration, delete in future
  1028. if( cr.Trait[ TRAIT_FAST_SHOT ] != 0 )
  1029. cr.ModeBase[ MODE_NO_AIM ] = 1;
  1030. }
  1031. }
  1032.  
  1033. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1034. // Call on critter exit from game.
  1035. void critter_finish( Critter& cr, bool toDelete )
  1036. {
  1037. if( toDelete && cr.Stat[ ST_DEAD_BLOCKER_ID ] != 0 )
  1038. {
  1039. Item@ block = ::GetItem( cr.Stat[ ST_DEAD_BLOCKER_ID ] );
  1040. if( valid( block ) )
  1041. DeleteItem( block );
  1042. cr.StatBase[ ST_DEAD_BLOCKER_ID ] = 0;
  1043. }
  1044. }
  1045.  
  1046. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1047. // Call every __CritterIdleTick time.
  1048. void critter_idle( Critter& cr )
  1049. {
  1050. if( cr.IsDead() && cr.Stat[ ST_REPLICATION_TIME ] >= 0 && cr.Timeout[ TO_REPLICATION ] == 0 )
  1051. ReplicateCritter( cr );
  1052.  
  1053. // Healing
  1054. if( cr.Timeout[ TO_HEALING ] == 0 )
  1055. {
  1056. if( !cr.IsDead() && cr.Mode[ MODE_NO_HEAL ] == 0 && cr.Timeout[ TO_BATTLE ] == 0 && cr.StatBase[ ST_CURRENT_HP ] < cr.Stat[ ST_MAX_LIFE ] )
  1057. {
  1058. cr.StatBase[ ST_CURRENT_HP ] += cr.Stat[ ST_HEALING_RATE ] * ( cr.Perk[ PE_VAMPIRE_REGENERATION ] > 0 ? 2 : 1 );
  1059. if( cr.StatBase[ ST_CURRENT_HP ] > cr.Stat[ ST_MAX_LIFE ] )
  1060. cr.StatBase[ ST_CURRENT_HP ] = cr.Stat[ ST_MAX_LIFE ];
  1061. }
  1062.  
  1063. cr.TimeoutBase[ TO_HEALING ] = HEALING_TIMEOUT( cr );
  1064. }
  1065. }
  1066.  
  1067. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1068. // Call on something critter dies.
  1069. // Killer can be null.
  1070. void critter_dead( Critter& cr, Critter@ killer )
  1071. {
  1072. Map@ map = cr.GetMap();
  1073. // Move inventory items to ground
  1074. if( ( cr.Anim2Dead == ANIM2_DEAD_PULSE_DUST || cr.Anim2Dead == ANIM2_DEAD_EXPLODE ) && _CritCanDropItemsOnDead( cr ) )
  1075. {
  1076. // Drop all, exclude armor
  1077. Item@[] items;
  1078. cr.GetItems( SLOT_INV, items );
  1079. cr.GetItems( SLOT_HAND1, items );
  1080. cr.GetItems( SLOT_HAND2, items );
  1081. if( valid( map ) )
  1082. {
  1083. // Disable drop of hidden items
  1084. for( uint i = 0, j = items.length(); i < j; i++ )
  1085. {
  1086. if( FLAG( items[ i ].Flags, ITEM_GAG ) )
  1087. {
  1088. Item@ item = items[ i ];
  1089. if( valid( item ) )
  1090. DeleteItem( item );
  1091. @items[ i ] = null;
  1092. }
  1093. else if( FLAG( items[ i ].Flags, ITEM_HIDDEN ) )
  1094. @items[ i ] = null;
  1095. }
  1096. MoveItems( items, map, cr.HexX, cr.HexY );
  1097. }
  1098. else
  1099. DeleteItems( items );
  1100. }
  1101.  
  1102. // Mob drops
  1103. uint16 npcPid = cr.GetProtoId();
  1104. uint16 dropPid = 0;
  1105. switch( npcPid )
  1106. {
  1107. case NPC_PID_GoldenGecko:
  1108. case NPC_PID_ToughGoldenGecko:
  1109. if( valid( killer ) && killer.Perk[ PE_GECKO_SKINNING ] != 0 )
  1110. dropPid = PID_GOLDEN_GECKO_PELT;
  1111. break;
  1112. case NPC_PID_SmallSilverGecko:
  1113. case NPC_PID_ToughSilverGecko:
  1114. if( valid( killer ) && killer.Perk[ PE_GECKO_SKINNING ] != 0 )
  1115. dropPid = PID_GECKO_PELT;
  1116. break;
  1117. case NPC_PID_SmallRadscorpion:
  1118. case NPC_PID_SmallRadscorpion2:
  1119. case NPC_PID_LargeRadscorpion:
  1120. case NPC_PID_LargeRadscorpion2:
  1121. case NPC_PID_LargeRadscorpion3:
  1122. dropPid = PID_SCORPION_TAIL;
  1123. break;
  1124. case NPC_PID_Brahmin:
  1125. case NPC_PID_Brahmin2:
  1126. case NPC_PID_Brahmin3:
  1127. case NPC_PID_WeakBrahmin:
  1128. case NPC_PID_WildBrahmin:
  1129. if( valid( killer ) && killer.Perk[ PE_GECKO_SKINNING ] != 0 )
  1130. dropPid = PID_BRAHMIN_SKIN;
  1131. break;
  1132. case NPC_PID_GiantBeetle:
  1133. case NPC_PID_GiantBeetle_2:
  1134. if( Random( 1, 3 ) == 3 )
  1135. dropPid = PID_RADSCORPION_PARTS;
  1136. break;
  1137. }
  1138. if( dropPid != 0 && _CritCountItem( cr, dropPid ) == 0 )
  1139. _CritAddItem( cr, dropPid, 1 );
  1140.  
  1141. // Karma, temporary
  1142. if( valid( killer ) && killer.IsPlayer() )
  1143. {
  1144. int karmaBefore = killer.StatBase[ ST_KARMA ];
  1145. uint team = cr.Stat[ ST_TEAM_ID ];
  1146. // if(team==TEAM_Player) killer.StatBase[ST_KARMA]-=10;
  1147. if( team == TEAM_Poorman )
  1148. killer.StatBase[ ST_KARMA ] -= 10;
  1149. else if( team == TEAM_Narcoman )
  1150. killer.StatBase[ ST_KARMA ] += 10;
  1151. else if( team == TEAM_Citizen )
  1152. killer.StatBase[ ST_KARMA ] -= 10;
  1153. else if( team == TEAM_Guard )
  1154. killer.StatBase[ ST_KARMA ] -= 10;
  1155. else if( team == TEAM_PrivateGuard )
  1156. killer.StatBase[ ST_KARMA ] -= 10;
  1157. else if( team == TEAM_Trader )
  1158. killer.StatBase[ ST_KARMA ] -= 10;
  1159. else if( team == TEAM_Bandit )
  1160. killer.StatBase[ ST_KARMA ] += 10;
  1161. else if( team == TEAM_CasinoPlayer )
  1162. killer.StatBase[ ST_KARMA ] -= 10;
  1163. else if( team == TEAM_CityElite )
  1164. killer.StatBase[ ST_KARMA ] -= 30;
  1165. else if( team == TEAM_QuestNpc )
  1166. killer.StatBase[ ST_KARMA ] -= 30;
  1167. else if( team == TEAM_Police )
  1168. killer.StatBase[ ST_KARMA ] -= 10;
  1169. else if( team == TEAM_Slaver )
  1170. killer.StatBase[ ST_KARMA ] += 10;
  1171. else if( team == TEAM_Slave )
  1172. killer.StatBase[ ST_KARMA ] -= 10;
  1173. else if( team == TEAM_Trapper )
  1174. killer.StatBase[ ST_KARMA ] -= 10;
  1175.  
  1176. if( cr.Stat[ ST_BODY_TYPE ] == BT_CHILDREN )
  1177. killer.KarmaBase[ KARMA_CHILDKILLER ]++;
  1178.  
  1179. CallTownSupply( cr, killer ); // Town supply call
  1180. }
  1181.  
  1182. SetReplicationTime( cr );
  1183. if( cr.IsNpc() )
  1184. cr.DropPlanes(); // Delete all planes
  1185. if( valid( map ) && cr.Mode[ MODE_NO_FLATTEN ] != 0 )
  1186. {
  1187. Item@ blocker = map.AddItem( cr.HexX, cr.HexY, PID_UNVISIBLE_BLOCK, 1 );
  1188. if( valid( blocker ) )
  1189. cr.StatBase[ ST_DEAD_BLOCKER_ID ] = blocker.Id;
  1190. }
  1191. OnCritterKilled( cr, killer );
  1192. }
  1193.  
  1194. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1195. // Call on something critter reswapned.
  1196. void critter_respawn( Critter& cr )
  1197. {
  1198. if( cr.Stat[ ST_DEAD_BLOCKER_ID ] != 0 )
  1199. {
  1200. Item@ block = ::GetItem( cr.Stat[ ST_DEAD_BLOCKER_ID ] );
  1201. if( valid( block ) )
  1202. DeleteItem( block );
  1203. cr.StatBase[ ST_DEAD_BLOCKER_ID ] = 0;
  1204. }
  1205. }
  1206.  
  1207. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1208. // Call on something critter in to map.
  1209. void map_critter_in( Map& map, Critter& cr )
  1210. {
  1211. OnCitterMapIn( cr, map );
  1212. if( cr.IsPlayer() )
  1213. {
  1214. uint16 locPid = map.GetLocation().GetProtoId();
  1215. if( LOCATION_IS_CITY( locPid ) )
  1216. {
  1217. GameVar@ lastCityVar = GetLocalVar( LVAR_last_city, cr.Id );
  1218. if( lastCityVar is null )
  1219. return;
  1220. lastCityVar = locPid;
  1221. }
  1222.  
  1223. PlaceLoot2Container( map, cr );
  1224. }
  1225. }
  1226.  
  1227. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1228. // Call on something critter out from map.
  1229. void map_critter_out( Map& map, Critter& cr )
  1230. {
  1231. OnCritterMapOut( cr, map );
  1232. }
  1233.  
  1234. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1235. // Call on something player votes for another.
  1236. // Already checked valid positions and timeout.
  1237. void karma_voting( Critter& crFrom, Critter& crTo, bool valUp )
  1238. {
  1239. // Players karma system (not used)
  1240. crTo.StatBase[ ST_PLAYER_KARMA ] += ( valUp ? int(5) : -10 );
  1241. crFrom.TimeoutBase[ TO_KARMA_VOTING ] = __FullSecond + REAL_HOUR( 4 ); // 4 hours
  1242.  
  1243. /* // Good / Evil system (not used)
  1244. crFrom.TimeoutBase[TO_KARMA_VOTING]=__FullSecond+60; // Some small time to prevent bruteforce
  1245. int crId=int(crTo.Id);
  1246. // Find alredy added
  1247. for(uint i=GOOD_EVIL_LIST_BEGIN;i<=GOOD_EVIL_LIST_END;i++)
  1248. {
  1249. int id=crFrom.GoodEvilList[i];
  1250. if(id!=0)
  1251. {
  1252. bool isEvil=FLAG(id,0x80000000);
  1253. if(isEvil) id^=0x80000000;
  1254.  
  1255. if(id==crId)
  1256. {
  1257. if((valUp && not isEvil) || (not valUp && isEvil)) return; // Already added
  1258. crFrom.GoodEvilListBase[i]=0; // Erase from list
  1259. return;
  1260. }
  1261. }
  1262. }
  1263. // Add new record
  1264. if(not valUp) crId|=0x80000000;
  1265. for(uint i=GOOD_EVIL_LIST_BEGIN;i<=GOOD_EVIL_LIST_END;i++)
  1266. {
  1267. int id=crFrom.GoodEvilList[i];
  1268. if(id==0)
  1269. {
  1270. crFrom.GoodEvilListBase[i]=crId;
  1271. return;
  1272. }
  1273. }
  1274. // All places busy, erase first 10
  1275. for(uint i=GOOD_EVIL_LIST_BEGIN;i<=GOOD_EVIL_LIST_END-10;i++) crFrom.GoodEvilListBase[i]=crFrom.GoodEvilListBase[i+10];
  1276. for(uint i=GOOD_EVIL_LIST_END-9;i<=GOOD_EVIL_LIST_END;i++) crFrom.GoodEvilListBase[i]=0;
  1277. crFrom.GoodEvilListBase[GOOD_EVIL_LIST_END-10]=crId;*/
  1278. }
  1279.  
  1280. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1281. // Call to determine visibility of critters.
  1282. // To start working set flag LOOK_CHECK_SCRIPT to __LookChecks global var.
  1283. // Return true to allow see one critter another, false to disallow.
  1284. // Note: CRITTER_EVENT_SHOW_CRITTER_X, CRITTER_EVENT_HIDE_CRITTER_X is not processed.
  1285. bool check_look( Map& map, Critter& cr, Critter& opponent )
  1286. {
  1287. return true;
  1288. }
  1289.  
  1290. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1291. // Call to determine visibility of items with ITEM_TRAP flag.
  1292. // To start working set flag LOOK_CHECK_SCRIPT to __LookChecks global var.
  1293. bool check_trap_look( Map& map, Critter& cr, Item& trap )
  1294. {
  1295. return true;
  1296. }
  1297.  
  1298. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1299. // Call to determine cost of single item.
  1300. // To allow function set __CustomItemCost to true.
  1301. // Don't forgot specify this function in client script.
  1302. uint item_cost( Item& item, Critter& cr, Critter& npc, bool sell )
  1303. {
  1304. return sell ? 1 : 2;
  1305. }
  1306.  
  1307. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1308. // Call on barter transaction.
  1309. // Return false to cancel transaction.
  1310. bool items_barter( Item@[]& saleItems, uint[]& saleItemsCount, Item@[]& buyItems, uint[]& buyItemsCount, Critter& player, Critter& npc )
  1311. {
  1312. if( npc.Mode[ MODE_BARTER_ONLY_CASH ] > 0 )
  1313. {
  1314. for( uint i = 0, j = saleItems.length(); i < j; i++ )
  1315. {
  1316. Item@ item = saleItems[ i ];
  1317. if( valid( item ) && item.GetProtoId() != PID_BOTTLE_CAPS )
  1318. {
  1319. npc.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_BARTER_ONLY_CASH );
  1320. npc.SayMsg( SAY_DIALOG, TEXTMSG_GAME, STR_BARTER_ONLY_CASH );
  1321. return false;
  1322. }
  1323. }
  1324. }
  1325.  
  1326. if( !BarterAllowed( npc.Id, buyItems, saleItems ) )
  1327. {
  1328. npc.SayMsg( SAY_NETMSG, TEXTMSG_GAME, STR_BARTER_ITEM_NOT_ALLOWED );
  1329. return false;
  1330. }
  1331.  
  1332. return true;
  1333. }
  1334.  
  1335. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1336. // Call on something player craft some items.
  1337. // Resources have None accessory and will be deleted after this function if no will be added to some place.
  1338. void items_crafted( Item@[]& items, uint[]& itemsCount, Item@[]& resources, Critter& crafter )
  1339. {
  1340. // Find max deterioration value
  1341. int maxDeterioration = 0;
  1342. for( uint i = 0, j = resources.length(); i < j; i++ )
  1343. {
  1344. Item@ item = resources[ i ];
  1345. int deterioration = GetDeteriorationProcent( item );
  1346. if( deterioration > maxDeterioration )
  1347. maxDeterioration = deterioration;
  1348. }
  1349.  
  1350. for( uint i = 0, j = items.length(); i < j; i++ )
  1351. {
  1352. // Unload weapons
  1353. Item@ item = items[ i ];
  1354. if( item.GetType() == ITEM_TYPE_WEAPON && item.Proto.Weapon_MaxAmmoCount > 0 )
  1355. {
  1356. item.AmmoCount = 0;
  1357. item.Update();
  1358. }
  1359.  
  1360. // Set max deterioration value
  1361. SetDeterioration( item, maxDeterioration );
  1362. }
  1363. }
  1364.  
  1365. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1366. // Levelup callback.
  1367. void player_levelup( Critter& player, uint skillIndex, uint skillUp, uint perkIndex )
  1368. {
  1369. if( skillIndex >= SKILL_BEGIN && skillIndex <= SKILL_END )
  1370. {
  1371. for( ; skillUp != 0; skillUp-- )
  1372. {
  1373. int skillVal = player.SkillBase[ skillIndex ];
  1374. if( skillVal >= MAX_SKILL_VAL )
  1375. break;
  1376.  
  1377. int needPoints = 1;
  1378. if( skillVal > __SkillModAdd6 )
  1379. needPoints = 6;
  1380. else if( skillVal > __SkillModAdd5 )
  1381. needPoints = 5;
  1382. else if( skillVal > __SkillModAdd4 )
  1383. needPoints = 4;
  1384. else if( skillVal > __SkillModAdd3 )
  1385. needPoints = 3;
  1386. else if( skillVal > __SkillModAdd2 )
  1387. needPoints = 2;
  1388.  
  1389. if( player.StatBase[ ST_UNSPENT_SKILL_POINTS ] < needPoints )
  1390. break;
  1391.  
  1392. skillVal++;
  1393. if( _CritIsTagSkill( player, skillIndex ) && skillVal < MAX_SKILL_VAL )
  1394. skillVal++;
  1395. player.SkillBase[ skillIndex ] = skillVal;
  1396. player.StatBase[ ST_UNSPENT_SKILL_POINTS ] -= needPoints;
  1397. }
  1398. }
  1399. else if( perkIndex >= PERK_BEGIN && perkIndex <= PERK_END )
  1400. {
  1401. if( PerkCheck( player, perkIndex ) )
  1402. {
  1403. player.PerkBase[ perkIndex ]++;
  1404. player.StatBase[ ST_UNSPENT_PERKS ]--;
  1405. }
  1406. }
  1407.  
  1408. player.StatBase[ ST_REPLICATION_COST ] = player.Stat[ ST_LEVEL ] * 100;
  1409. }
  1410.  
  1411. // //////////////////////////////////////////////////////////////////////////////////////////////////
  1412. // Turn based callbacks.
  1413. // Called on every round begin, return false to disable turn-based
  1414. void turn_based_begin( Map& map )
  1415. {
  1416. // Try end battle
  1417. if( map.TurnBasedRound > 0 )
  1418. {
  1419. uint[] crittersIds;
  1420. map.GetTurnBasedSequence( crittersIds );
  1421.  
  1422. bool continueBattle = false;
  1423. if( crittersIds.length() >= 2 )
  1424. {
  1425. for( uint i = 0, j = crittersIds.length(); i < j; i++ )
  1426. {
  1427. Critter@ cr = ::GetCritter( crittersIds[ i ] );
  1428. if( !( not valid( cr ) || cr.IsDead() ||
  1429. ( cr.IsNpc() && cr.GetPlanes( AI_PLANE_ATTACK, null ) == 0 ) ||
  1430. ( cr.IsPlayer() && ( cr.Mode[ MODE_END_COMBAT ] != 0 || cr.Stat[ ST_CURRENT_HP ] < 1 ) ) ) )
  1431. {
  1432. continueBattle = true;
  1433. break;
  1434. }
  1435. }
  1436. }
  1437.  
  1438. if( not continueBattle )
  1439. map.EndTurnBased();
  1440. }
  1441. }
  1442.  
  1443. // Call on end turn-based battle
  1444. void turn_based_end( Map& map )
  1445. {
  1446. //
  1447. }
  1448.  
  1449. // Call on every begin and end turn
  1450. void turn_based_process( Map& map, Critter& cr, bool beginTurn )
  1451. {
  1452. if( beginTurn )
  1453. {
  1454. cr.StatBase[ ST_MOVE_AP ] = cr.Stat[ ST_MAX_MOVE_AP ];
  1455. cr.StatBase[ ST_TURN_BASED_AC ] = 0;
  1456. }
  1457. else
  1458. {
  1459. bool hthEvade = false;
  1460. if( cr.Perk[ PE_HTH_EVADE ] != 0 )
  1461. {
  1462. // In hands only HtH weapons
  1463. Item@ hand1 = cr.GetItem( 0, SLOT_HAND1 );
  1464. Item@ hand2 = cr.GetItem( 0, SLOT_HAND2 );
  1465. if( ( not valid( hand1 ) || hand1.GetType() != ITEM_TYPE_WEAPON || not hand1.Weapon_IsHtHAttack( 0 ) ) &&
  1466. ( not valid( hand1 ) || hand1.GetType() != ITEM_TYPE_WEAPON || not hand1.Weapon_IsHtHAttack( 0 ) ) )
  1467. {
  1468. hthEvade = true;
  1469. }
  1470. }
  1471.  
  1472. cr.StatBase[ ST_TURN_BASED_AC ] = cr.Stat[ ST_CURRENT_AP ] * ( hthEvade ? 2 : 1 );
  1473. if( cr.Stat[ ST_TURN_BASED_AC ] < 0 )
  1474. cr.StatBase[ ST_TURN_BASED_AC ] = 0;
  1475. if( hthEvade && cr.Skill[ SK_UNARMED ] > 0 )
  1476. cr.StatBase[ ST_TURN_BASED_AC ] += cr.Skill[ SK_UNARMED ] / 12;
  1477. cr.StatBase[ ST_MOVE_AP ] = 0;
  1478. }
  1479. }
  1480.  
  1481. // Call when need generate turns sequence
  1482. void turn_based_sequence( Map& map, Critter@[]& critters, Critter@ firstTurnCrit )
  1483. {
  1484. // Check first turn critter
  1485. if( valid( firstTurnCrit ) && ( firstTurnCrit.IsDead() || firstTurnCrit.Stat[ ST_CURRENT_AP ] <= 0 ) )
  1486. @firstTurnCrit = null;
  1487.  
  1488. // Collect critters
  1489. SequenceCritter[] sequenceCritters;
  1490. for( uint i = 0, j = critters.length(); i < j; i++ )
  1491. {
  1492. Critter@ cr = critters[ i ];
  1493. if( valid( firstTurnCrit ) && firstTurnCrit.Id == cr.Id )
  1494. continue;
  1495. if( cr.IsDead() )
  1496. continue;
  1497. sequenceCritters.resize( sequenceCritters.length() + 1 );
  1498. @sequenceCritters.last().critter = cr;
  1499. }
  1500.  
  1501. // Sort sequence, see SequenceCritter::opCmp below
  1502. SequenceCritterRandom = Random( 0, 1 );
  1503. sequenceCritters.sortDesc();
  1504.  
  1505. // Fill result
  1506. critters.resize( 0 );
  1507. if( valid( firstTurnCrit ) )
  1508. critters.insertLast( firstTurnCrit );
  1509. for( uint i = 0, j = sequenceCritters.length(); i < j; i++ )
  1510. critters.insertLast( sequenceCritters[ i ].critter );
  1511. }
  1512.  
  1513. // Sequence sorter for turn_based_sequence
  1514. int SequenceCritterRandom = 0;
  1515. class SequenceCritter
  1516. {
  1517. Critter@ critter;
  1518. int opCmp( SequenceCritter& in other )
  1519. {
  1520. bool result;
  1521. Critter@ cr1 = critter;
  1522. Critter@ cr2 = other.critter;
  1523. int seq1 = cr1.Stat[ ST_SEQUENCE ];
  1524. int seq2 = cr2.Stat[ ST_SEQUENCE ];
  1525. if( seq1 == seq2 )
  1526. {
  1527. int ag1 = cr1.Stat[ ST_AGILITY ];
  1528. int ag2 = cr2.Stat[ ST_AGILITY ];
  1529. if( ag1 == ag2 )
  1530. {
  1531. int lk1 = cr1.Stat[ ST_LUCK ];
  1532. int lk2 = cr2.Stat[ ST_LUCK ];
  1533. if( lk1 == lk2 )
  1534. {
  1535. if( SequenceCritterRandom == 0 )
  1536. result = cr1.Id > cr2.Id;
  1537. else
  1538. result = cr1.Id < cr2.Id;
  1539. }
  1540. else
  1541. result = lk1 > lk2;
  1542. }
  1543. else
  1544. result = ag1 > ag2;
  1545. }
  1546. else
  1547. result = seq1 > seq2;
  1548. return result ? int(1) : int(-1);
  1549. }
  1550. }
  1551.  
  1552. // Call on world saving
  1553. // Range of currentIndex: 1..9999
  1554. void world_save( uint currentIndex, uint[]& deleteIndexes )
  1555. {
  1556. // Keep only current and four last saves
  1557. if( currentIndex == 1 )
  1558. {
  1559. deleteIndexes.resize( 5 );
  1560. for( uint i = 0; i < 5; i++ )
  1561. deleteIndexes[ i ] = 9999 - i;
  1562. }
  1563. else if( currentIndex > 4 )
  1564. {
  1565. deleteIndexes.resize( 1 );
  1566. deleteIndexes[ 0 ] = currentIndex - 5;
  1567. }
  1568. // bboards
  1569. SaveMessengerData();
  1570. }
  1571.  
  1572. // Call on player try register
  1573. // Return true to allow, false to disallow
  1574. bool player_registration( uint ip, string& name, uint& textMsg, uint& strNum )
  1575. {
  1576. uint nameError = CheckPlayerName( name );
  1577. if( nameError != 0 )
  1578. {
  1579. textMsg = TEXTMSG_GAME;
  1580. strNum = nameError;
  1581. return false;
  1582. }
  1583. return true;
  1584. }
  1585.  
  1586. // Call on player try login
  1587. // Return true to allow, false to disallow
  1588. bool player_login( uint ip, string& name, uint id, uint& textMsg, uint& strNum )
  1589. {
  1590. if( __PermanentDeath > 0 && IsPermanentDeath( id ) )
  1591. {
  1592. textMsg = TEXTMSG_GAME;
  1593. strNum = STR_NET_PERMANENT_DEATH;
  1594. return false;
  1595. }
  1596. return true;
  1597. }
  1598.  
  1599. // Call on player try change access
  1600. // Return true to allow, false to disallow
  1601. bool player_getaccess( Critter& player, int access, string& password )
  1602. {
  1603. Log( "Access changed for player " + player.Name + ", from " + player.GetAccess() + " to " + access + "." );
  1604. return true;
  1605. }
  1606.  
  1607. bool player_allowcommand( Critter@ player, string@ adminPanel, uint8 command )
  1608. {
  1609. if( valid( adminPanel ) )
  1610. return true;
  1611.  
  1612. switch( command )
  1613. {
  1614. // ACCESS_CLIENT
  1615. case COMMAND_CHANGE_PASSWORD:
  1616. case COMMAND_DELETE_ACCOUNT:
  1617. case COMMAND_EXIT:
  1618. case COMMAND_GETACCESS:
  1619. case COMMAND_MYINFO:
  1620. return true;
  1621. // ACCESS_TESTER
  1622. case COMMAND_DROP_UID:
  1623. case COMMAND_PARAM:
  1624. case COMMAND_TOGLOBAL:
  1625. if( player.GetAccess() >= ACCESS_TESTER )
  1626. return true;
  1627. break;
  1628. // ACCESS_MODER
  1629. case COMMAND_ADDITEM:
  1630. case COMMAND_ADDITEM_SELF:
  1631. case COMMAND_ADDLOCATION:
  1632. case COMMAND_ADDNPC:
  1633. case COMMAND_BAN:
  1634. case COMMAND_CHECKVAR:
  1635. case COMMAND_CRITID:
  1636. case COMMAND_DISCONCRIT:
  1637. case COMMAND_GAMEINFO:
  1638. case COMMAND_KILLCRIT:
  1639. case COMMAND_MOVECRIT:
  1640. case COMMAND_RESPAWN:
  1641. case COMMAND_SETVAR:
  1642. if( player.GetAccess() >= ACCESS_MODER )
  1643. return true;
  1644. break;
  1645. // ACCESS_ADMIN
  1646. case COMMAND_LOADDIALOG:
  1647. case COMMAND_LOADLOCATION:
  1648. case COMMAND_LOADMAP:
  1649. case COMMAND_LOADSCRIPT:
  1650. case COMMAND_LOG:
  1651. case COMMAND_RELOAD_CLIENT_SCRIPTS:
  1652. case COMMAND_RELOADAI:
  1653. case COMMAND_RELOADDIALOGS:
  1654. case COMMAND_RELOADLOCATIONS:
  1655. case COMMAND_RELOADMAPS:
  1656. case COMMAND_RELOADSCRIPTS:
  1657. case COMMAND_RELOADTEXTS:
  1658. case COMMAND_REGENMAP:
  1659. case COMMAND_RUNSCRIPT:
  1660. case COMMAND_SETTIME:
  1661. if( player.GetAccess() == ACCESS_ADMIN )
  1662. return true;
  1663. break;
  1664. // Unknown command
  1665. default:
  1666. player.Say( SAY_NETMSG, "Unknown command." );
  1667. return false;
  1668. }
  1669.  
  1670. player.Say( SAY_NETMSG, "Access denied." );
  1671. return false;
  1672. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement