Advertisement
Guest User

Untitled

a guest
Aug 14th, 2016
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 135.82 KB | None | 0 0
  1. //
  2. // FOnline: 2238
  3. // Rotators
  4. //
  5. // main.fos
  6. //
  7.  
  8. #include "_animation.fos"
  9. #include "_basetypes.fos"
  10. #include "_colors.fos"
  11. #include "_macros.fos"
  12. #include "_town.fos"
  13. #include "_vars.fos"
  14. #include "_npc_pids.fos"
  15. #include "_vals.fos"
  16.  
  17. #include "backend_h.fos"
  18. #include "broadcast_h.fos"
  19. #include "caravans_h.fos"
  20. #include "config_h.fos"
  21. #include "config_file_h.fos"
  22. #include "critter_age_h.fos"
  23. #include "debug_h.fos"
  24. #include "economy_h.fos"
  25. #include "factions_h.fos"
  26. #include "follower_capturing.fos"
  27. #include "follower_common_h.fos"
  28. #include "follower_h.fos"
  29. #include "groups_h.fos"
  30. #include "item_dogtags_h.fos"
  31. #include "item_perks_h.fos"
  32. #include "logging_h.fos"
  33. #include "lexems_h.fos"
  34. #include "map_tent_h.fos"
  35. #include "mapdata_h.fos"
  36. #include "messages_h.fos"
  37. #include "minigames_h.fos"
  38. #include "mob_wave_h.fos"
  39. #include "MsgStr.h"
  40. #include "npc_common_h.fos"
  41. #include "npc_planes_h.fos"
  42. #include "npc_schedule_h.fos"
  43. #include "online_stats_h.fos"
  44. #include "recycler_h.fos"
  45. #include "reinforcements_h.fos"
  46. #include "reputations_h.fos"
  47. #include "town_h.fos"
  48. #include "utils_h.fos"
  49. #include "weather_h.fos"
  50. #include "world_common_h.fos"
  51. #include "worldmap_h.fos"
  52. #include "xfire_h.fos"
  53.  
  54. // Imports
  55. import void DropDrugEffects(Critter& cr) from "drugs";
  56. import bool FindBaseEncounter(array<Critter@>& group, Item@ car, uint x, uint y, uint& encounterDescriptor, bool& waitForAnswer) from "factions_player";
  57. import bool IsArenaCombatant(Critter& cr) from "arena";
  58. import bool IsInsideArena(Critter& cr) from "arena";
  59. import bool IsCarTrunk(Item@ item) from "car";
  60. import bool OnUseExplode(Critter& cr, Item& explode, Critter@ targetCr, Item@ targetItem, Scenery@ targetScen, uint timer) from "explode";
  61. import bool PerkCheck(Critter& cr, uint perk, bool always) from "perks";
  62. import bool ReversableItem(Item& item) from "fix_boy";
  63. import bool ReverseItem(Critter& cr, Item& item) from "fix_boy";
  64. import uint GetItemRecipe(uint16 itemPid, array<uint16>& pids, array<uint>& cnt) from "fix_boy";
  65. import bool TryRepairItem(Critter& cr, Item& item) from "repair";
  66. import bool TryRechargeItem(Critter& cr, Item& item) from "teslaRecharge";
  67. import bool UseItemOnCar(Critter& cr, Item& car, Item& item) from "car";
  68. import bool UseProspectMap(Critter& cr, Item& map) from "prospects";
  69. import bool UseSkillOnCar(Critter& cr, Item& car, int skill) from "car";
  70. import bool UseSkillOnLocker(Critter& cr, Item& locker, int skill) from "lockers";
  71. import bool WantedSignSet(Item& wantedSign, string& name, uint cost) from "wanted";
  72.  
  73. import int GetCarTrunkComplexity(Item@ item) from "car";
  74. import uint GetRandomDeathAnimation() from "cheats";
  75. import void AddCrittersKilled() from "cavelog";
  76. import void AddPlayerDeaths() from "cavelog";
  77. import void ApplyTimeout(array<Item@>& items, array<uint>& itemsCount, array<Item@>& resources, Critter& crafter) from "fix_boy";
  78. import void CheckBountyHunters(Critter& cr, Critter@ killer) from "bounties";
  79. import void CombatAttack(Critter& cr, Critter& target, ProtoItem& weapon, uint8 weaponMode, ProtoItem@ ammo) from "combat";
  80. import void CritterGenerate(Critter& cr) from "parameters";
  81. import void CritterDescription_Set(Critter& player, int& description1, int& description2) from "critter_description";
  82. import void EditRadioSettings(Critter& player, Item& radio) from "radio";
  83. import void FlushInfluenceBuffer(Critter& cr, ITown@ town) from "town";
  84. import uint GMTrack(uint targetId, string@ message) from "cheats";
  85. import void GreetPlayer(Critter@ player, Map@ map) from "map_greet";
  86. import void InitCheats(bool fromGame) from "cheats";
  87. import void InitBrahminPens() from "brahmin_pens";
  88. import void InitBrahminTraders() from "brahmin_traders";
  89. import void InitCars() from "car_seller";
  90. import void InitCaveLog() from "cavelog";
  91. import void InitCaveRandomization() from "cave";
  92. import void InitCompanions() from "companion";
  93. import void InitMercs() from "mercs";
  94. import void InitReverseItem() from "fix_boy";
  95. import void InitRoutes() from "patrolroutes";
  96. import void InitSlaveruns() from "slaverun";
  97. import void InitTowns() from "towns";
  98. import void InitTrains() from "trains";
  99. import void InitBlueprints() from "blueprints";
  100. import void InitPerks() from "perks";
  101. import void InitProduction() from "production";
  102. import void InitShufflingSpawns() from "shuffling_spawner";
  103. import bool IsArenaItem(Item@ item) from "arena";
  104. import void LogAction(Critter& cr, string& s) from "logging_critter";
  105. import void NpcProcessLevel(Critter& cr) from "parameters";
  106. import void PlaySound(Critter& cr, string& soundName) from "media";
  107. import string PrepareSound_Pick(Item& item) from "media";
  108. import void ProcessCrippling() from "crippling";
  109. import void RemoveAuthed(uint id) from "cheats";
  110. import void RemoveWorkbenches(Critter& player) from "workbench";
  111. import void ReplicateCritter(Critter& cr) from "replication";
  112. import void RemoveArenaItems(Critter& cr) from "arena";
  113. import void RemoveOnlinePlayer(Critter& cr) from "utils";
  114. import void SaveBrahminPenData() from "brahmin_pens";
  115. import void SaveEventSpawns() from "cheats";
  116. import void SaveCarPriceData() from "car_seller";
  117. import void SaveCompanionData() from "companion";
  118. import void SetReplicationTime(Critter& cr) from "replication";
  119. import void SetSpectator(Critter& cr, bool on) from "utils";
  120. import void SetStartLocation(Critter& cr) from "replication";
  121. import void SetTimeoutForAll(Map& map, int timeout, int time) from "cheats";
  122. import void UseDrug(Critter& cr, Item& drug) from "drugs";
  123. import void UseDrugOn(Critter& cr, Critter& onCr, Item& drug) from "drugs";
  124. import void WearItem(Critter& cr, Item& item, int wearCount) from "repair";
  125. import void WorldmapInit() from "worldmap";
  126. import void WorldmapUpdatePlayer(Critter@ player) from "worldmap_players";
  127. import void WorldmapRemovePlayer(Critter@ player) from "worldmap_players";
  128. import void TrackLocation(Critter& player) from "globalmap_group";
  129. import bool UseFirstAidOnCritter(Critter& cr, Critter& targetCr, Item@ item) from "skills";
  130. import bool UseDoctorOnCritter(Critter& cr, Critter& targetCr, Item@ item) from "skills";
  131. import array<Critter@> WorldmapGetPlayers(uint zx, uint zy) from "worldmap_players";
  132.  
  133. import void FixDrugs(Critter& cr) from "utils";
  134.  
  135. import bool GuardNotLegit(Critter@ cr, Item@ item, bool deleteItem) from "cheats";
  136. import void RemoveNotLegit(Critter@ cr) from "cheats";
  137. import bool IsReadableBook(uint16 pid) from "books";
  138. import void TryReadBook(Critter& cr, Item& book) from "books";
  139. import void AddBonuses(Item@ item, string@ crafter) from "item_bonus";
  140. import uint BonusNumber(Item@ item) from "item_bonus";
  141.  
  142. import void UpdateDrugs(Critter& cr) from "drugs";
  143.  
  144. import string IntToIp(int d) from "utils";
  145. import string GetTimeString(uint fullsecond) from "time";
  146.  
  147. dictionary ItemPids;
  148. dictionary VarIds;
  149. dictionary BaseTypes;
  150. dictionary MapData;
  151. array<string> ItemNames;
  152. array<string> VarNames;
  153.  
  154. //// Get the id of the item using identifiers
  155. // stored in ITEMPID.H file
  156. bool GetItemPid(const string& in identifier, int& out pid)
  157. {
  158. return ItemPids.get(identifier, pid);
  159. }
  160. bool GetVarId(const string& in identifier, int& out id)
  161. {
  162. return VarIds.get(identifier, id);
  163. }
  164. // void GetVarNames(const string& filter
  165. bool GetBaseType(const string& in identifier, int& out id)
  166. {
  167. return BaseTypes.get(identifier, id);
  168. }
  169.  
  170. bool GetMapData(const string& in identifier, int& out id)
  171. {
  172. return MapData.get(identifier, id);
  173. }
  174.  
  175. ////////////////////////////////////////////////////////////////////////////////////////////////////
  176. // Called before world generation.
  177. void init()
  178. {
  179. InitLogs(); // Should be first
  180. InitDebug();
  181. InitializeGame();
  182. InitPerks();
  183. InitBlueprints();
  184. FLog(LOG_WMLOCATIONS, "START");
  185. }
  186.  
  187. ////////////////////////////////////////////////////////////////////////////////////////////////////
  188. // Call on start server.
  189. bool start()
  190. {
  191. // Send info about others critters
  192. // Remember:
  193. // - all this info can be hacked in client;
  194. // - more parameters - more traffic.
  195. SetSendParameter(ST_GENDER, true);
  196. SetSendParameter(ST_AGE, true);
  197. SetSendParameter(ST_FOLLOW_CRIT, true);
  198. SetSendParameter(ST_PLAYER_KARMA, true);
  199. // Armor class, uses Agility and Perks for runtime calc
  200. SetSendParameter(ST_ARMOR_CLASS, true);
  201. SetSendParameter(PE_LIVEWIRE, true);
  202. SetSendParameter(PE_HTH_EVADE, true);
  203. SetSendParameter(PE_HTH_EVADE_II, true);
  204. // SetSendParameter(ST_TURN_BASED_AC,true);
  205. // Agility
  206. SetSendParameter(ST_AGILITY, true);
  207. // Hit points, uses Strenght and Endurance
  208. SetSendParameter(ST_MAX_LIFE, true, "Utils.dll@_AllowParameterIfAwareness");
  209. SetSendParameter(ST_MAX_LIFE_EXT, true, "Utils.dll@_AllowParameterIfAwareness");
  210. SetSendParameter(ST_CURRENT_HP, true, "Utils.dll@_AllowParameterIfAwareness");
  211. SetSendParameter(ST_HEALTH_LEVEL, true);
  212. SetSendParameter(ST_TURN_BASED_AC, true);
  213. // Strenght, uses battle timeout
  214. SetSendParameter(ST_STRENGTH, true, "Utils.dll@_AllowParameterIfAwareness");
  215. // Battle timeout
  216. SetSendParameter(TO_BATTLE, true);
  217. // Endurance
  218. SetSendParameter(ST_ENDURANCE, true, "Utils.dll@_AllowParameterIfAwareness");
  219. // Injures
  220. SetSendParameter(DAMAGE_EYE, true);
  221. SetSendParameter(DAMAGE_RIGHT_ARM, true);
  222. SetSendParameter(DAMAGE_LEFT_ARM, true);
  223. SetSendParameter(DAMAGE_RIGHT_LEG, true);
  224. SetSendParameter(DAMAGE_LEFT_LEG, true);
  225. // Item slots, passed with -
  226. SetSendParameter(-SLOT_HAND1, true, "Utils.dll@_AllowSlotHand1");
  227. SetSendParameter(-SLOT_HAND2, false);
  228. SetSendParameter(-SLOT_ARMOR, true, "Utils.dll@_AllowSlotHand1");
  229. SetSendParameter(-SLOT_HEAD, true);
  230.  
  231. // Some flags for correct client working
  232. SetSendParameter(MODE_HIDE, true, "cheats@AllowParameterIfModer");
  233. SetSendParameter(MODE_NO_BARTER, true);
  234. SetSendParameter(MODE_NO_LOOT, true);
  235. SetSendParameter(MODE_NO_STEAL, true);
  236. SetSendParameter(MODE_NO_FLATTEN, true);
  237. SetSendParameter(MODE_NO_TALK, true);
  238. SetSendParameter(ST_BODY_TYPE, true, "Utils.dll@_AllowParameterIfAwareness");
  239. // 3d animation layers, from Skin to Backpack
  240. #ifdef PLAYERS_3D
  241. for(uint i = ST_ANIM3D_LAYERS + ANIM3D_LAYER_SKIN;
  242. i <= ST_ANIM3D_LAYERS + ANIM3D_LAYER_BACKPACK; i++)
  243. SetSendParameter(i, true);
  244. #endif
  245. // Npc talk distance
  246. SetSendParameter(ST_TALK_DISTANCE, true);
  247. // Dialog id
  248. SetSendParameter(ST_DIALOG_ID, true);
  249. // To see pid of unarmed attack
  250. SetSendParameter(ST_HANDS_ITEM_AND_MODE, true);
  251. // Scale factor
  252. SetSendParameter(ST_SCALE_FACTOR, true);
  253. SetSendParameter(ST_WALK_TIME, true);
  254. SetSendParameter(ST_RUN_TIME, true);
  255.  
  256. // faction id, only for team mates
  257. SetSendParameter(ST_TEAM_ID, true);
  258. SetSendParameter(ST_FACTION_RANK, true);
  259.  
  260. // Critter description
  261. SetSendParameter(ST_DESCRIPTION1, true, "Utils.dll@_AllowParameterIfAwareness");
  262. SetSendParameter(ST_DESCRIPTION2, true, "Utils.dll@_AllowParameterIfAwareness");
  263. SetSendParameter(KARMA_SLAVER, true);
  264.  
  265. // Minigames data (team, id, custom data)
  266. SetSendParameter(ST_MINIGAME_DATA, true);
  267. SetSendParameter(ST_VAR9,true);
  268.  
  269. // Look fields in fonline.h 'struct Item::ItemData'
  270. // 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
  271. // ITEM_DATA_MASK_CHOSEN ITEM_DATA_MASK_CHOSEN ITEM_DATA_MASK_CHOSEN
  272. 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, -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, -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, -1, -1, -1, -1, -1, -1, 0, 0 };
  273. // ITEM_DATA_MASK_CRITTER ITEM_DATA_MASK_CRITTER ITEM_DATA_MASK_CRITTER
  274. 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, -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, -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 };
  275. // ITEM_DATA_MASK_CRITTER_EXT ITEM_DATA_MASK_CRITTER_EXT ITEM_DATA_MASK_CRITTER_EXT
  276. 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, -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, -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, -1, -1, -1, -1, -1, -1, 0, 0 };
  277. // ITEM_DATA_MASK_CONTAINER ITEM_DATA_MASK_CONTAINER ITEM_DATA_MASK_CONTAINER
  278. 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, -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, -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, -1, -1, -1, -1, -1, -1, 0, 0 };
  279. // ITEM_DATA_MASK_MAP ITEM_DATA_MASK_MAP ITEM_DATA_MASK_MAP
  280. 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, -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, -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, -1, -1, -1, -1, -1, -1, 0, 0 };
  281. SetItemDataMask(ITEM_DATA_MASK_CHOSEN, mask0);
  282. SetItemDataMask(ITEM_DATA_MASK_CRITTER, mask1);
  283. SetItemDataMask(ITEM_DATA_MASK_CRITTER_EXT, mask2);
  284. SetItemDataMask(ITEM_DATA_MASK_CONTAINER, mask3);
  285. SetItemDataMask(ITEM_DATA_MASK_MAP, mask4);
  286.  
  287. InitDBLogs();
  288.  
  289. WorldmapInit();
  290. ReadDefines("scripts/ITEMPID.H", ItemPids, ItemNames);
  291. ReadDefines("scripts/_vars.fos", VarIds, VarNames);
  292. ReadDefines("scripts/_basetypes.fos", BaseTypes, null);
  293. ReadDefines("scripts/mapdata_h.fos", MapData, null);
  294. // cheats init
  295. InitCheats(true);
  296.  
  297. // factions!
  298. InitFactions();
  299. // register big factions
  300. RegisterFaction(FACTION_BOS, "Brotherhood Of Steel", false);
  301. RegisterFaction(FACTION_ENCLAVE, "Enclave", false);
  302. RegisterFaction(FACTION_UNITY, "The Unity", false);
  303. RegisterFaction(FACTION_NCR, "New California Republic", false);
  304. RegisterFaction(FACTION_VAULT_CITY, "Vault City", false);
  305. RegisterFaction(FACTION_RAIDERS, "Raiders", false);
  306. RegisterFaction(FACTION_MORDINOS, "Mordinos", false);
  307. // RegisterFaction(FACTION_GUNRUNNERS, "Gun Runners", false); <- screw it
  308.  
  309. // and some fixed playerdriven factions
  310. RegisterFaction(FACTION_VAGRANTS, "The Tanker Vagrants", true);
  311. RegisterFaction(FACTION_REDDING_GUTTERSNIPES, "The Redding Guttersnipes", true);
  312.  
  313. __TimeoutBattle = REAL_SECOND(10); // do not move this to InitializeGame() - __TimeMultiplier is still 0 there!
  314.  
  315. InitTents();
  316. InitCars();
  317.  
  318. // Initialize the script for improved economy
  319. InitEconomy();
  320. // radiation tables
  321. // InitRadiationEffects();
  322.  
  323. // Mercenaries
  324. InitMercs();
  325. //
  326. InitNpcSchedules();
  327. // Patrol routes
  328. InitRoutes();
  329. // Brahmin pens
  330. InitBrahminPens();
  331. // Bramin traders
  332. InitBrahminTraders();
  333. // Towns
  334. InitTowns();
  335.  
  336. InitCaveRandomization();
  337. InitSlaveruns();
  338. // InitAmmoLog();
  339. InitCaveLog();
  340. InitCompanions();
  341. // Caravans wohoo
  342. InitCaravans(); // Uncomment if you want to have one caravan running Hub-Adytum-Lost Hills
  343. // reputations, alliances
  344. InitGroups();
  345. // scenariooos
  346. // InitScenarios();
  347. InitTrains();
  348. InitAlertMaps();
  349. InitWeather();
  350. InitReverseItem();
  351. InitProduction();
  352. InitShufflingSpawns();
  353. ProcessCrippling();
  354.  
  355. array<Map@> allMaps;
  356. uint allMapsCount = GetAllMaps(0, allMaps);
  357. uint noGridsMaps = 0;
  358. for(uint m = 0; m < allMapsCount; m++)
  359. {
  360. if(!valid(allMaps[m]) || allMaps[m].IsNotValid) // JIC
  361. continue;
  362.  
  363. if(_MapHasMode(allMaps[m], MAP_MODE_NO_GRIDS))
  364. {
  365. // in case of changes, remember about editing cheats@ExecMapGrids
  366. allMaps[m].SetEvent(MAP_EVENT_LOOP_3, "cheats@_LockMap");
  367. allMaps[m].SetLoopTime(3, 500);
  368. SetTimeoutForAll(allMaps[m], TO_TRANSFER, REAL_SECOND(2238));
  369. noGridsMaps++;
  370. }
  371. // if()
  372. }
  373.  
  374. if(noGridsMaps > 0)
  375. Log("Exit grids disabled on " + noGridsMaps + " map" + (noGridsMaps > 1 ? "s" : ""));
  376.  
  377. StartPersonalDataCleanup();
  378.  
  379. SetGvar(GVAR_is_first_time, 0);
  380. return true;
  381. }
  382. array<uint> Tents;
  383. void GotoTent(Critter& cr, int p0, int p1, int p2)
  384. {
  385. cr.TransitToMap(Tents[p0], 0);
  386. }
  387.  
  388. ////////////////////////////////////////////////////////////////////////////////////////////////////
  389. // Call on world initialization.
  390. // Parameter Min Max
  391. // multiplier 1 99
  392. // year 1700 30000
  393. // month 1 12
  394. // day 1 31
  395. // hour 0 23
  396. // minute 0 59
  397. void get_start_time(uint16& multiplier, uint16& year, uint16& month, uint16& day, uint16& hour, uint16& minute)
  398. {
  399. multiplier = 20;
  400. year = 2238;
  401. month = 1;
  402. day = 9;
  403. hour = 6;
  404. minute = 3;
  405. }
  406.  
  407. ////////////////////////////////////////////////////////////////////////////////////////////////////
  408. // Call on finish server.
  409. void finish()
  410. {
  411. FLog(LOG_WMLOCATIONS, "FINISH");
  412. CloseLogs();
  413. }
  414.  
  415. ////////////////////////////////////////////////////////////////////////////////////////////////////
  416. // Call every returned value, in milliseconds.
  417. // Return next call in milliseconds or zero to disable loop.
  418. uint lastsecond = 0;
  419. uint last_os_save = 0;
  420. uint last_os_mod = 0;
  421.  
  422. uint loop()
  423. {
  424. // updates worldmap
  425. // WorldmapUpdate(10000);
  426.  
  427. uint16 second = 0;
  428. uint16 minute = 0;
  429. uint16 hour = 0;
  430. uint16 a, b, c, d, g;
  431. GetTime(a, b, c, d, hour, minute, second, g);
  432.  
  433. if((minute == 0 || minute == 30) && second < 20) // a time buffer to make sure that long cycles won't prevent updating
  434. {
  435. for(uint i = 1; i < TOWN_COUNT + 1; i++)
  436. {
  437. ITown@ town = GetTown(i);
  438. town.Update(hour, minute);
  439. }
  440. }
  441.  
  442. for(uint i = 1; i < TOWN_COUNT + 1; i++)
  443. {
  444. ITown@ town = GetTown(i);
  445. town.UpdateTick();
  446. if(((second % 10) == 0) && lastsecond != second)
  447. {
  448. if(town.GetVersion() == TOWN_VERSION_INFLUENCE)
  449. town.TownCheck();
  450. else
  451. town.AreaCheck();
  452. }
  453. }
  454.  
  455. UpdateBroadcast();
  456.  
  457. if(last_os_save == 0)
  458. last_os_save = ELAPSED_TIME;
  459. else if(ELAPSED_TIME > last_os_save + OS_CYCLE_TIME)
  460. {
  461. array<Critter@> players;
  462. uint n = GetAllOnlinePlayers(players);
  463. for(uint i = 0; i < n; i++)
  464. if(players[i].Id % OS_MAX_MOD == last_os_mod)
  465. OnlineStats_TrySave(players[i]);
  466. last_os_save += OS_CYCLE_TIME;
  467. last_os_mod++;
  468. if(last_os_mod == OS_MAX_MOD)
  469. last_os_mod = 0;
  470. }
  471.  
  472. lastsecond = second;
  473. return 500;
  474. }
  475.  
  476. ////////////////////////////////////////////////////////////////////////////////////////////////////
  477. // Call when critter attack another.
  478. void critter_attack(Critter& cr, Critter& target, ProtoItem& weapon, uint8 weaponMode, ProtoItem@ ammo)
  479. {
  480. CombatAttack(cr, target, weapon, weaponMode, ammo);
  481. }
  482.  
  483. ////////////////////////////////////////////////////////////////////////////////////////////////////
  484. // Called when a critter is attacked by another.
  485. #define _CanHelp # (npc, who)(_GroupMode(npc) == FACTION_MODE_NPC_ONLY ? _IsTrueNpc(who) : (_GroupMode(npc) == FACTION_MODE_PLAYER_ONLY ? !_IsTrueNpc(who) : true))
  486. #define _AnyGuard # (npc) (_GroupMode(npc) != 0 && _GroupMode(npc) != 4)
  487. // These macros are a nice example of code that can't be inlined function nor can be put in do{}while(false) (because of continue) :)
  488. #define _GenGuardTryHelpCr \
  489. # (npc) if(crHelpers == 0 && !override) \
  490. continue; if(!_CanHelp(npc, cr)) \
  491. continue; crHelpers--; AddAttackPlane(npc, 0, attacker); continue
  492. #define _GenGuardTryHelpAttacker \
  493. # (npc) if(attackerHelpers == 0 && !override) \
  494. continue; if(!_CanHelp(npc, attacker)) \
  495. continue; attackerHelpers--; AddAttackPlane(npc, 0, cr); continue
  496. #define _TryHelpCr \
  497. # (npc) if(!_CanHelp(npc, cr)) \
  498. continue; if(!_IsTrueNpc(cr)) { if(crHelpers == 0 && !override) \
  499. continue; crHelpers--; } AddAttackPlane(npc, 0, attacker); continue
  500. #define _TryHelpAttacker \
  501. # (npc) if(!_CanHelp(npc, attacker)) \
  502. continue; if(!_IsTrueNpc(attacker)) { if(attackerHelpers == 0 && !override) \
  503. continue; attackerHelpers--; } AddAttackPlane(npc, 0, cr); continue
  504.  
  505. void critter_attacked(Critter& cr, Critter& attacker)
  506. {
  507. if(attacker.Id == cr.Id)
  508. return;
  509.  
  510. Map@ map = cr.GetMap();
  511. if(IsDueling(attacker) || IsArenaCombatant(attacker))
  512. return;
  513. uint crGroup = _GroupIndex(cr);
  514. uint attackerGroup = _GroupIndex(attacker);
  515.  
  516. DPlayerLog(attacker, "" + attackerGroup + "vs" + crGroup);
  517.  
  518. if(crGroup == attackerGroup && _IsTrueNpc(cr) && _IsTrueNpc(attacker))
  519. return;
  520.  
  521. if(attacker.IsPlayer() && !_GroupHasNoAffiliation(crGroup) && !IsFlaggedAsIllegal(cr))
  522. {
  523. if(IsTown(map))
  524. SubReputation(attacker, crGroup, REP_ATK_PENALTY * MAX(1, _GroupRank(cr)));
  525. else
  526. TryReportOffense(cr, attacker, REP_ATK_PENALTY * MAX(1, _GroupRank(cr)));
  527. }
  528. if(!cr.IsPlayer() && (!_IsTrueNpc(attacker) || (crGroup != attackerGroup)))
  529. AddAttackPlane(cr, 0, attacker);
  530.  
  531. // In similar fashion to vanilla, but only for general purpose guards (normal guards will react no matter what, unless the helped is player/follower)
  532. uint crHelpers = 10 - attacker.Stat[ST_CHARISMA];
  533. crHelpers = CLAMP(crHelpers, 2, 8);
  534. if(_AnyGuard(cr))
  535. crHelpers += 4;
  536. uint attackerHelpers = 10 - cr.Stat[ST_CHARISMA];
  537. attackerHelpers = CLAMP(attackerHelpers, 2, 4);
  538. if(_AnyGuard(attacker))
  539. attackerHelpers += 4;
  540.  
  541. // Reinforcements
  542. bool override = GetAlertLevel(map) >= __AlertLevelReact; // map is on high alert, don't use helpers number, everyone fights
  543.  
  544. // We say again: *Helpers are potential general purpose guards or faction-guards helping player/follower :)
  545. // Put all critters that can see either the target or the attacker in critters
  546. array<Critter@> critters;
  547. uint k = 0;
  548.  
  549. if(override)
  550. k = map.GetCritters(0, FIND_LIFE_AND_KO | FIND_ONLY_NPC, critters);
  551. else
  552. {
  553. uint n = cr.GetCritters(true, FIND_LIFE_AND_KO | FIND_ONLY_NPC, critters);
  554. uint m = attacker.GetCritters(true, FIND_LIFE_AND_KO | FIND_ONLY_NPC, critters);
  555. m += n;
  556. if(m == 0)
  557. return;
  558.  
  559. k = m;
  560. if(n != 0 && m != n) // union-find if necessary (usually it is)
  561. {
  562. k = n;
  563. // Remove the doubled ones
  564. m -= 1;
  565. while(k != m)
  566. {
  567. if(critters[k].IsSee(cr))
  568. {
  569. // Swap this guy with the last on the "probably ok" list
  570. Critter@ temp = critters[k];
  571. @critters[k] = critters[m];
  572. @critters[m] = temp;
  573. m--;
  574. }
  575. else
  576. k++;
  577. }
  578. if(!critters[k].IsSee(cr))
  579. k++;
  580. }
  581. }
  582.  
  583. // Exclude those that are already busy helping, check if a general purpose guard is there and if the attacker should be flagged
  584. // Flagging unflagged attacker when the target is unflagged and there's no extreme reputations combination
  585.  
  586. for(uint i = 0; i < k; i++)
  587. {
  588. if(!_IsTrueNpc(critters[i]) || critters[i].Id == cr.Id || critters[i].Id == attacker.Id)
  589. {
  590. @critters[i] = null;
  591. continue;
  592. }
  593. if(_CritHasExtMode(critters[i], MODE_EXT_GUARD))
  594. {
  595. uint crRepIndex = _GroupIndex(critters[i]);
  596. if(!_IsTrueNpc(attacker) &&
  597. !IsFlaggedAsIllegal(attacker) &&
  598. !IsFlaggedAsIllegal(cr) &&
  599. (crRepIndex >= REPUTATION_COUNT || (attacker.Reputation[crRepIndex] < __ReputationLoved) ||
  600. (cr.Reputation[crRepIndex] >= __ReputationHated))
  601. )
  602. SetCritterIllegalFlag(attacker, ILLEGAL_FLAG_ATTACKING);
  603. }
  604.  
  605.  
  606. NpcPlane@ plane = critters[i].GetCurPlane();
  607. if(valid(plane) && plane.Type == AI_PLANE_ATTACK && plane.Attack_TargId == attacker.Id)
  608. {
  609. if(crHelpers > 0)
  610. crHelpers--;
  611. @critters[i] = null; // Exclude this critter
  612. }
  613. if(valid(plane) && plane.Type == AI_PLANE_ATTACK && plane.Attack_TargId == cr.Id)
  614. {
  615. if(attackerHelpers > 0)
  616. attackerHelpers--;
  617. @critters[i] = null; // Exclude this critter
  618. }
  619. }
  620.  
  621. int crRep = 0;
  622. int attackerRep = 0;
  623. uint npcGroup = 0;
  624. int crStatus = FACTION_NEUTRAL;
  625. int attackerStatus = FACTION_NEUTRAL;
  626.  
  627. // Global rule: always help a friend against a non-friend; always attack the enemy if against non-enemy
  628. for(uint i = 0; i < k; i++)
  629. {
  630. if(!valid(critters[i]))
  631. continue; // Skip excluded
  632. npcGroup = _GroupIndex(critters[i]);
  633.  
  634. if(_CritHasExtMode(critters[i], MODE_EXT_GUARD)) // General purpose guard
  635. {
  636. if(attackerHelpers == 0 && crHelpers == 0 && !override)
  637. continue;
  638. // Logic behind an all-purpose guard:
  639. // Always ignore the attack if critter is flagged as illegal
  640. // Help all npc members if they are attacking
  641. // Help all npc members if they are attacked
  642. // If neither of the above, check if the attacked is liked very much, and the target is not. help the attacker if this is the case.
  643. // Otherwise, check if reputation difference is big enough, ignore the attack then
  644. // If not, then proceed with the default action (help the target)
  645.  
  646. // Faction members npcs are ok, help them
  647. if((npcGroup == attackerGroup) && _IsTrueNpc(attacker))
  648. {
  649. _GenGuardTryHelpAttacker(critters[i]);
  650. }
  651.  
  652. // Always help a friend against a non-friend; always attack the enemy if against non-enemy
  653. crStatus = GetGroupsStatus(cr, critters[i]);
  654. attackerStatus = GetGroupsStatus(attacker, critters[i]);
  655.  
  656. if(crStatus == FACTION_ALLY)
  657. {
  658. if(attackerStatus != FACTION_ALLY)
  659. {
  660. _GenGuardTryHelpCr(critters[i]);
  661. }
  662. else
  663. continue;
  664. }
  665. else if(crStatus == FACTION_NEUTRAL)
  666. {
  667. if(attackerStatus == FACTION_ALLY)
  668. {
  669. _GenGuardTryHelpAttacker(critters[i]);
  670. }
  671. }
  672. else if(crStatus == FACTION_ENEMY)
  673. {
  674. if(attackerStatus != FACTION_ENEMY)
  675. {
  676. _GenGuardTryHelpAttacker(critters[i]);
  677. }
  678. }
  679.  
  680. // Is the critter illegal? then the attacker is ok
  681. if(IsFlaggedAsIllegal(cr))
  682. continue;
  683.  
  684. // Help the friendly npc
  685. if((crGroup == npcGroup) && _IsTrueNpc(cr))
  686. {
  687. _GenGuardTryHelpCr(critters[i]);
  688. }
  689.  
  690. // If the attacker is liked, and the target is not, actually help the attacker
  691. crRep = npcGroup < REPUTATION_COUNT ? cr.Reputation[npcGroup] : 0;
  692. attackerRep = npcGroup < REPUTATION_COUNT ? attacker.Reputation[npcGroup] : 0;
  693. if((attackerRep >= __ReputationLoved) && (crRep < __ReputationHated))
  694. {
  695. _GenGuardTryHelpAttacker(critters[i]);
  696. }
  697.  
  698. // Be neutral on medium difference
  699. if(attackerRep - crRep > REP_DIFF)
  700. continue;
  701.  
  702. // Default behaviour
  703. _GenGuardTryHelpCr(critters[i]);
  704. } // General purpose guard
  705. else if(_GroupIndex(critters[i]) > 1 && _GroupMode(critters[i]) > 0 && _GroupMode(critters[i]) < 4) // non-general guard
  706. {
  707. // General rules: target is in our team, npc attacker is in our team: do nothing
  708. // target is in our team, player attacker is in our team: kill the player
  709. // target is in our team, attacker not is in our team: kill the attacker
  710. // target is not in our team, attacker is in our team: kill the critter
  711. // target is not in our team, attacker is not in our team: help the side that has v. high reputation,
  712. // providing that the other side has v. low reputation
  713.  
  714.  
  715. // Always help a friend against a non-friend; always attack the enemy if against non-enemy
  716. crStatus = GetGroupsStatus(cr, critters[i]);
  717. attackerStatus = GetGroupsStatus(attacker, critters[i]);
  718.  
  719. if(crStatus == FACTION_ALLY)
  720. {
  721. if(attackerStatus != FACTION_ALLY)
  722. {
  723. _GenGuardTryHelpCr(critters[i]); // Act like a generic guard in this case
  724. }
  725. }
  726. else if(crStatus == FACTION_NEUTRAL)
  727. {
  728. if(attackerStatus == FACTION_ALLY)
  729. {
  730. _GenGuardTryHelpAttacker(critters[i]); // Act like a generic guard in this case
  731. }
  732. }
  733. else if(crStatus == FACTION_ENEMY)
  734. {
  735. if(attackerStatus != FACTION_ENEMY)
  736. {
  737. _GenGuardTryHelpAttacker(critters[i]); // Act like a generic guard in this case
  738. }
  739. }
  740.  
  741. if(npcGroup == crGroup)
  742. {
  743. if(npcGroup == attackerGroup)
  744. {
  745. if(_IsTrueNpc(attacker))
  746. continue;
  747. else
  748. {
  749. _TryHelpCr(critters[i]);
  750. }
  751. }
  752. else
  753. {
  754. if(_IsTrueNpc(attacker))
  755. continue;
  756. else
  757. _TryHelpCr(critters[i]);
  758. }
  759. }
  760. else
  761. {
  762. if(npcGroup == attackerGroup)
  763. {
  764. _TryHelpAttacker(critters[i]);
  765. }
  766. else
  767. {
  768. crRep = npcGroup < REPUTATION_COUNT ? cr.Reputation[npcGroup] : 0;
  769. attackerRep = npcGroup < REPUTATION_COUNT ? attacker.Reputation[npcGroup] : 0;
  770. if((attackerRep >= __ReputationLoved) && (crRep < __ReputationHated))
  771. {
  772. _TryHelpAttacker(critters[i]);
  773. }
  774. else if((crRep >= __ReputationLoved) && (attackerRep < __ReputationHated))
  775. {
  776. _TryHelpCr(critters[i]);
  777. }
  778. }
  779. }
  780. } // Non-general purpose guard
  781. else if(_GroupHasNoAffiliation(npcGroup)) // In encounters
  782. {
  783. if(npcGroup == crGroup)
  784. AddAttackPlane(critters[i], 0, attacker);
  785. else if(npcGroup == attackerGroup)
  786. AddAttackPlane(critters[i], 0, cr);
  787. }
  788. } // Main loop
  789. }
  790.  
  791. ////////////////////////////////////////////////////////////////////////////////////////////////////
  792. // Called after the critter gets any damage.
  793. void PostDamage(Critter& cr)
  794. {
  795. if(valid(cr.GetMap()) && cr.GetMap().IsTurnBased())
  796. return;
  797. if(cr.IsPlayer() || !IsHumanoid(cr) || cr.Cond == COND_DEAD)
  798. return;
  799. if(cr.IsInjured())
  800. {
  801. if(cr.Timeout[TO_SK_DOCTOR] <= 0)
  802. {
  803. if(cr.Stat[ST_INTELLECT] >= Random(1, 10))
  804. {
  805. array<NpcPlane@> planes;
  806. cr.GetPlanes(PLANE_DOCTOR_CRITTER, planes);
  807. if(planes.length() > 0)
  808. planes[0].IdentifierExt = cr.Id;
  809. else
  810. AddDoctorCritterPlane(cr, valid(cr.GetCurPlane()) ? cr.GetCurPlane().Priority + 5 : 0, cr, false); // self
  811. }
  812. }
  813. }
  814. if(cr.Stat[ST_CURRENT_HP] != cr.Stat[ST_MAX_LIFE])
  815. {
  816. if(cr.Timeout[TO_SK_FIRST_AID] <= 0 && cr.Timeout[TO_WEAKENED] <= 0)
  817. {
  818. if(cr.Stat[ST_INTELLECT] >= Random(1, 10))
  819. {
  820. if(cr.Stat[ST_CURRENT_HP] <= cr.Stat[ST_MAX_LIFE] / 2 || (cr.Stat[ST_MAX_LIFE] - cr.Stat[ST_CURRENT_HP] > cr.Skill[SK_FIRST_AID]))
  821. {
  822. array<NpcPlane@> planes;
  823. cr.GetPlanes(PLANE_HEAL_CRITTER, planes);
  824. if(planes.length() > 0)
  825. planes[0].IdentifierExt = cr.Id;
  826. else
  827. AddHealCritterPlane(cr, valid(cr.GetCurPlane()) ? cr.GetCurPlane().Priority + 5 : 0, cr, false); // self
  828. }
  829. }
  830. }
  831. }
  832.  
  833. }
  834.  
  835. ////////////////////////////////////////////////////////////////////////////////////////////////////
  836. // Call when a critter steals from another.
  837. bool critter_stealing(Critter& cr, Critter& thief, Item& item, uint count)
  838. {
  839. if(cr.IsDead() || cr.Timeout[TO_BATTLE] > 0 || thief.Timeout[TO_BATTLE] > 0)
  840. {
  841. thief.StatBase[ST_LAST_STEAL_CR_ID] = 0;
  842. thief.StatBase[ST_STEAL_COUNT] = 0;
  843. return false;
  844. }
  845.  
  846. switch(item.GetProtoId())
  847. {
  848. case PID_WORKBENCH:
  849. case PID_WORKBENCH_PRIMITIVE:
  850. case PID_WORKBENCH_RAIDERS:
  851. case PID_MFC_MACHINE:
  852. case PID_MED_MACHINE:
  853. case PID_AMMO_FACILITY:
  854. case PID_CAMPFIRE:
  855. case PID_ADVANCED_BENCH:
  856. case PID_TOBACCO_BENCH:
  857. //DeleteItem(item);
  858. return false;
  859. }
  860.  
  861. int dir1 = cr.Dir;
  862. int dir2 = thief.Dir;
  863. int kDir = MAX(dir1, dir2) - MIN(dir1, dir2);
  864. if(kDir > 3)
  865. kDir = 6 - kDir;
  866.  
  867. int steal = thief.Skill[SK_STEAL];
  868. if(steal <= 0)
  869. steal = 1;
  870. int size = item.Proto.Volume;
  871. if(size <= 0)
  872. size = 1;
  873.  
  874. // Perk pickpocket, ignore size and facing --> now only halve
  875. bool pickpocket = thief.Perk[PE_PICKPOCKET] != 0;
  876.  
  877. // Count modifier
  878. int kCount = count / steal;
  879. if(kCount <= 0)
  880. kCount = 1;
  881.  
  882. // Check time of stealing
  883. uint lastStealCrId = thief.Stat[ST_LAST_STEAL_CR_ID];
  884. uint stealCount = thief.Stat[ST_STEAL_COUNT];
  885. if(lastStealCrId == cr.Id && thief.Timeout[TO_SK_STEAL] > 0)
  886. steal -= steal * stealCount * 10 / 100;
  887.  
  888. // Calc
  889. int k = (steal - kDir * (pickpocket ? 5 : 10)) / MAX(1, (size * kCount) / (pickpocket ? 2 : 1));
  890. k = CLAMP(k, 5, 95);
  891.  
  892. GameVar@ var = GetUnicumVar(UVAR_proximity_check, cr.Id, thief.Id);
  893. if(var.GetValue() == 1)
  894. k /= 2;
  895.  
  896. bool success = !(Random(1, 100) > k);
  897.  
  898.  
  899. Map@ map = cr.GetMap();
  900. if(map.GetLocation().GetProtoId()==LOCATION_BarterGround || map.GetLocation().GetProtoId()==LOCATION_Necropolis || map.GetLocation().GetProtoId()==LOCATION_NewRenoStables || map.GetLocation().GetProtoId()==LOCATION_Hub || map.GetProtoId()==LOCATION_Hinkley)
  901. {
  902. thief.Say(SAY_NETMSG, "|4291317840 " + "You cannot steal here.");
  903. success = false;
  904. }
  905.  
  906. // flag critter if guards spot it
  907. /*if (IsTown(thief.GetMap()) && !IsFlaggedAsIllegal(thief))
  908. thief.SendMessage(MSG_IM_STEALING, cr.Id, MESSAGE_TO_VISIBLE_ME);*/
  909.  
  910. if(success || cr.Perk[PE_THIEF] != 0 || Random(1, 50) <= cr.Stat[ST_LUCK])
  911. _SetTimeout(thief, TO_SK_STEAL, STEAL_TIMEOUT(thief));
  912.  
  913. if(success)
  914. {
  915. // Add experience
  916. // const int stealExp[12]={10,30,60,100,150,210,280,360,450,550,660,780};
  917. // const int stealExp[12]={10,20,30,40,50,60,70,80,90,100,110,120};
  918. const int[] stealExp = { 5, 10, 15, 25, 30, 40, 50, 60, 70, 100 };
  919.  
  920. if(lastStealCrId == cr.Id && thief.Timeout[TO_SK_STEAL] > 0)
  921. {
  922. stealCount++;
  923. if(stealCount > 9)
  924. stealCount = 9;
  925. thief.StatBase[ST_STEAL_COUNT] = stealCount;
  926. }
  927. else
  928. {
  929. thief.StatBase[ST_LAST_STEAL_CR_ID] = cr.Id;
  930. thief.StatBase[ST_STEAL_COUNT] = 0;
  931. }
  932.  
  933. if(_IsFollower(cr) || IsBase(cr.GetMap()) || IsTent(cr.GetMap()))
  934. return success;
  935.  
  936. if(cr.IsNpc())
  937. {
  938. thief.StatBase[ST_EXPERIENCE] += stealExp[stealCount];
  939. LogExperience(thief, stealExp[stealCount], SK_STEAL, cr.GetProtoId());
  940. AddScore(thief, SCORE_THIEF, 1);
  941. }
  942. }
  943. else
  944. {
  945. if(IsTown(thief.GetMap()))
  946. SetCritterIllegalFlag(thief, ILLEGAL_FLAG_STEALING);
  947.  
  948. thief.StatBase[ST_LAST_STEAL_CR_ID] = 0;
  949. thief.StatBase[ST_STEAL_COUNT] = 0;
  950.  
  951. if(cr.IsNpc())
  952. {
  953. int thiefHp = thief.Stat[ST_CURRENT_HP];
  954. if(!(_IsFollower(cr) && IsTown(thief.GetMap())))
  955. AddAttackPlane(cr, 0, thief, thiefHp < 10 || Random(1, 10) > cr.Stat[ST_LUCK] ? __DeadHitPoints : Random(thiefHp / 4, thiefHp / 2));
  956.  
  957. uint crGroup = _GroupIndex(cr);
  958. if(_ValidReputationIndex(crGroup))
  959. SubReputation(thief, crGroup, REP_STEAL_PENALTY * MAX(1, _GroupRank(cr)));
  960. }
  961. else
  962. {
  963. cr.Say(SAY_NETMSG, "|4291317840 " + thief.Name + " failed to steal from you.");
  964. cr.RunClientScript("_ActionStealing", thief.Id, 0, 0, null, null); // send action
  965. }
  966. }
  967.  
  968. return success;
  969. }
  970.  
  971. ////////////////////////////////////////////////////////////////////////////////////////////////////
  972. // Call on something critter use item.
  973. bool critter_use_item(Critter& cr, Item& item, Critter@ targetCr, Item@ targetItem, Scenery@ targetScen, uint param)
  974. {
  975. // scenario
  976. bool isPlayer = cr.IsPlayer();
  977. uint16 pid = item.GetProtoId();
  978. bool useOnSelf = (!valid(targetCr) && !valid(targetItem) && !valid(targetScen));
  979.  
  980. DLog("Using item with pid: " + item.GetProtoId());
  981.  
  982. // Book reading
  983. // disabled
  984. /*
  985. if(useOnSelf && IsReadableBook(pid))
  986. {
  987. TryReadBook(cr,item);
  988. return true;
  989. }
  990. */
  991.  
  992. // Explosion
  993. if(OnUseExplode(cr, item, targetCr, targetItem, targetScen, param))
  994. return true;
  995.  
  996. // Use item on another item
  997. //
  998. if(valid(targetItem))
  999. {
  1000. if(targetItem.GetType() == ITEM_TYPE_CAR && UseItemOnCar(cr, targetItem, item))
  1001. {
  1002. return true;
  1003. }
  1004. }
  1005.  
  1006. // Drugs
  1007. if(item.GetType() == ITEM_TYPE_DRUG)
  1008. {
  1009. if(item.GetProtoId() == PID_CIGARETTES && !HasItem(cr, PID_LIGHTER))
  1010. {
  1011. cr.Say(SAY_NETMSG, "You need a lighter to smoke.");
  1012. return true;
  1013. }
  1014.  
  1015. if(useOnSelf)
  1016. UseDrug(cr, item);
  1017. else if(valid(targetCr))
  1018. UseDrugOn(cr, targetCr, item);
  1019. else
  1020. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1021. return true;
  1022. }
  1023.  
  1024. // Tools (FA and doctor now)
  1025. if(!valid(targetScen) && !valid(targetItem) && item.GetType() == ITEM_TYPE_MISC)
  1026. {
  1027. if(item.Proto.Misc_ToolSkillNum == SK_FIRST_AID)
  1028. {
  1029. if(!valid(targetCr))
  1030. @targetCr = cr;
  1031. if(UseFirstAidOnCritter(cr, targetCr, item))
  1032. return true;
  1033. }
  1034. else if(item.Proto.Misc_ToolSkillNum == SK_DOCTOR)
  1035. {
  1036. if(!valid(targetCr))
  1037. @targetCr = cr;
  1038. if(UseDoctorOnCritter(cr, targetCr, item))
  1039. return true;
  1040. }
  1041. }
  1042.  
  1043. // Radio
  1044. if(FLAG(item.Flags, ITEM_RADIO) && useOnSelf)
  1045. {
  1046. if(isPlayer)
  1047. EditRadioSettings(cr, item);
  1048. return true;
  1049. }
  1050.  
  1051. // capturing
  1052. if(pid == PID_ROPE && valid(targetCr))
  1053. {
  1054. bool sub = false;
  1055. if(CaptureBrahmin(cr, targetCr, sub))
  1056. {
  1057. if(sub)
  1058. cr.DeleteItem(PID_ROPE, 1);
  1059. return true;
  1060. }
  1061. if(CaptureSlave(cr, targetCr, sub))
  1062. {
  1063. if(sub)
  1064. cr.DeleteItem(PID_ROPE, 1);
  1065. return true;
  1066. }
  1067. }
  1068.  
  1069. // Play dice
  1070. if(pid == PID_DICE)
  1071. {
  1072. cr.SayMsg(SAY_EMOTE_ON_HEAD, TEXTMSG_TEXT, STR_DICE_THROW, "$result" + Random(1, 6));
  1073. return true;
  1074. }
  1075. if(pid == PID_LOADED_DICE)
  1076. {
  1077. cr.SayMsg(SAY_EMOTE_ON_HEAD, TEXTMSG_TEXT, STR_DICE_THROW, "$result" + uint((item.Id % 6) + 1));
  1078. return true;
  1079. }
  1080.  
  1081. // Magic ball
  1082. if(pid == PID_MAGIC_8_BALL)
  1083. {
  1084. cr.SayMsg(SAY_EMOTE_ON_HEAD, TEXTMSG_TEXT, Random(1, 2) == 1 ? STR_MAGIC_BALL_YES : STR_MAGIC_BALL_NO);
  1085. return true;
  1086. }
  1087.  
  1088. // Cosmetic
  1089. if(pid == PID_COSMETIC_CASE && cr.Stat[ST_GENDER] == GENDER_FEMALE)
  1090. {
  1091. cr.SayMsg(SAY_EMOTE_ON_HEAD, TEXTMSG_TEXT, STR_COSMETIC_USE);
  1092. return true;
  1093. }
  1094.  
  1095. // prospect map
  1096. if(item.GetProtoId() == PID_PROSPECT_MAP)
  1097. {
  1098. return UseProspectMap(cr, item);
  1099. }
  1100.  
  1101. // Book reading
  1102. if(useOnSelf && IsReadableBook(pid))
  1103. {
  1104. TryReadBook(cr,item);
  1105. return true;
  1106. }
  1107.  
  1108. // Take process to engine
  1109. return false;
  1110. }
  1111.  
  1112. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1113. //
  1114. bool critter_use_skill(Critter& cr, int skill, Critter@ targetCr, Item@ targetItem, Scenery@ targetScen)
  1115. {
  1116. bool isPlayer = cr.IsPlayer();
  1117.  
  1118. if(valid(targetItem))
  1119. {
  1120. if(GuardNotLegit(cr, targetItem, false))
  1121. {
  1122. cr.Say(SAY_NETMSG, "You can't do this with not legit item.");
  1123. return true;
  1124. }
  1125.  
  1126. // Cars
  1127. if(targetItem.GetType() == ITEM_TYPE_CAR && UseSkillOnCar(cr, targetItem, skill))
  1128. return true;
  1129.  
  1130. // Doors or containers
  1131. if((targetItem.GetType() == ITEM_TYPE_DOOR || targetItem.GetType() == ITEM_TYPE_CONTAINER) && UseSkillOnLocker(cr, targetItem, skill))
  1132. return true;
  1133. }
  1134.  
  1135. switch(skill)
  1136. {
  1137. case SKILL_PICK_ON_GROUND: // Pick item or scenery on ground
  1138. {
  1139. // Stairs
  1140. // Scenery
  1141. if(valid(targetScen))
  1142. {
  1143. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1144. return true;
  1145. }
  1146.  
  1147. // Explosives
  1148. if(valid(targetItem) && targetItem.GetProtoId() == PID_ACTIVE_MINE && OnUseExplode(cr, targetItem, null, null, null, 0))
  1149. return true;
  1150.  
  1151. // Pick some item
  1152. if(valid(targetItem))
  1153. {
  1154. Item@ item = targetItem;
  1155. if(!FLAG(item.Flags, ITEM_CAN_PICKUP))
  1156. {
  1157. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1158. break;
  1159. }
  1160.  
  1161. bool picked = true;
  1162. int freeWeight = cr.Stat[ST_CARRY_WEIGHT]*2 - cr.ItemsWeight();
  1163. if(freeWeight >= int(item.Proto.Weight * item.GetCount()))
  1164. {
  1165. // Pick full
  1166. MoveItem(item, 0, cr);
  1167. if(cr.Mode[MODE_HIDE] != 0 && cr.GetAccess() < ACCESS_TESTER)
  1168. {
  1169. int sk = cr.Skill[SK_STEAL] - 10 * GRAMM_TO_LBS(item.Proto.Weight * item.GetCount());
  1170. sk = CLAMP(sk, 5, 95);
  1171. if(sk < Random(1, 100))
  1172. cr.ModeBase[MODE_HIDE] = 0;
  1173. }
  1174. }
  1175. else
  1176. {
  1177. // Pick half
  1178. if(item.IsStackable() && freeWeight >= int(item.Proto.Weight))
  1179. {
  1180. MoveItem(item, freeWeight / item.Proto.Weight, cr);
  1181. if(cr.Mode[MODE_HIDE] != 0 && cr.GetAccess() < ACCESS_TESTER)
  1182. {
  1183. int sk = cr.Skill[SK_STEAL] - 10 * GRAMM_TO_LBS(item.Proto.Weight * MAX(int(freeWeight / item.Proto.Weight), int(item.GetCount())));
  1184. sk = CLAMP(sk, 5, 95);
  1185. if(sk < Random(1, 100))
  1186. cr.ModeBase[MODE_HIDE] = 0;
  1187. }
  1188. }
  1189. // Overweight
  1190. else
  1191. {
  1192. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_OVERWEIGHT);
  1193. picked = false;
  1194. }
  1195. }
  1196. if(picked)
  1197. {
  1198. string sound = PrepareSound_Pick(item);
  1199. if(sound.length() > 4) // .ext
  1200. cr.PlaySound(sound, true);
  1201. }
  1202. }
  1203. }
  1204. break;
  1205. case SKILL_PUT_CONT: // Put item in container, only targetItem is valid
  1206. case SKILL_TAKE_CONT: // Take item from container, only targetItem is valid
  1207. {
  1208. switch(targetItem.GetProtoId())
  1209. {
  1210. case PID_WORKBENCH:
  1211. case PID_WORKBENCH_PRIMITIVE:
  1212. case PID_WORKBENCH_RAIDERS:
  1213. case PID_MFC_MACHINE:
  1214. case PID_MED_MACHINE:
  1215. case PID_AMMO_FACILITY:
  1216. case PID_CAMPFIRE:
  1217. case PID_ADVANCED_BENCH:
  1218. case PID_TOBACCO_BENCH:
  1219. DeleteItem(targetItem);
  1220. return true;
  1221. }
  1222.  
  1223. if(cr.Mode[MODE_HIDE] != 0 && cr.GetAccess() < ACCESS_TESTER)
  1224. {
  1225. int sk = cr.Skill[SK_STEAL] - 10 * GRAMM_TO_LBS(targetItem.Proto.Weight * targetItem.GetCount());
  1226. sk = CLAMP(sk, 5, 95);
  1227. if(sk < Random(1, 100))
  1228. cr.ModeBase[MODE_HIDE] = 0;
  1229. }
  1230. return false;
  1231. }
  1232. case SKILL_TAKE_ALL_CONT: // Take all items from critter or item container
  1233. {
  1234. if(cr.Mode[MODE_HIDE] != 0 && cr.GetAccess() < ACCESS_TESTER)
  1235. cr.ModeBase[MODE_HIDE] = 0;
  1236.  
  1237. return true; // Don't allow - hardcoded take all is replaced by unsafe_TakeAll called from button injected at position of original one
  1238. //return false; // Allow transactions
  1239. }
  1240. case SKILL_LOOT_CRITTER: // Loot critter, only targetCr is valid
  1241. {
  1242. CritterTrophy(targetCr, cr); // Critters like brahmins should drop meat, skin etc cr.ShowContainer(targetCr,null,TRANSFER_CRIT_LOOT);
  1243. cr.Action(ACTION_PICK_CRITTER, 0, null);
  1244. cr.ShowContainer(targetCr, null, TRANSFER_CRIT_LOOT);
  1245. if(cr.Mode[MODE_HIDE] != 0 && Random(0, 1) == 1 && cr.GetAccess() < ACCESS_TESTER)
  1246. cr.ModeBase[MODE_HIDE] = 0;
  1247. return true;
  1248. }
  1249. case SKILL_PUSH_CRITTER: // Push critter, only targetCr is valid
  1250. cr.Action(ACTION_PICK_CRITTER, 2, null);
  1251. if((cr.Timeout[TO_BATTLE] == 0 && targetCr.Timeout[TO_BATTLE] == 0) && (targetCr.IsPlayer() || (targetCr.IsNoPlanes() && targetCr.GetTalkedPlayers(null) == 0)))
  1252. {
  1253. targetCr.MoveRandom();
  1254. if(targetCr.IsPlayer()) targetCr.Wait(450);
  1255. CreateTimeEvent(AFTER(REAL_MS(500)), "e_CritterMove", cr.Id, false);
  1256. }
  1257. return true;
  1258. case SK_SCIENCE: //////////////////////////////////////////////////
  1259. {
  1260. if(valid(targetItem) && (!FLAG(targetItem.Flags, ITEM_CAN_PICKUP) || IsArenaItem(targetItem)))
  1261. {
  1262. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1263. return true;
  1264. }
  1265.  
  1266. if(valid(targetItem) && ReversableItem(targetItem))
  1267. {
  1268. /*if(_IsNotLegit(targetItem.Val9))
  1269. {
  1270. cr.Say(SAY_NETMSG, "You can't disassemble not legit items.");
  1271. return true;
  1272. }*/
  1273.  
  1274. Map@ map = cr.GetMap();
  1275. if(valid(map) && _MapHasMode(map, MAP_MODE_NO_DISMANTLING))
  1276. {
  1277. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1278. return(true);
  1279. }
  1280.  
  1281. // disassembling
  1282. if(cr.Timeout[TO_BATTLE] > 0)
  1283. {
  1284. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_TIMEOUT_BATTLE_WAIT);
  1285. return true;
  1286. }
  1287. if(ReverseItem(cr, targetItem))
  1288. {
  1289. if(_IsSneaking(cr) && _IsRealPlayer(cr))
  1290. {
  1291. _DisableSneak(cr);
  1292. }
  1293. return true;
  1294. }
  1295. }
  1296.  
  1297. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING); // todo "You fail to learn anything."
  1298. }
  1299. break;
  1300. case SK_REPAIR: ///////////////////////////////////////////////////
  1301. {
  1302. // Generic repair
  1303. if(valid(targetItem) && targetItem.Accessory == ACCESSORY_CRITTER && targetItem.IsDeteriorable())
  1304. {
  1305. TryRepairItem(cr, targetItem);
  1306. return true;
  1307. }
  1308.  
  1309. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1310. }
  1311. break;
  1312. case SK_SNEAK: ////////////////////////////////////////////////////
  1313. {
  1314. if(cr.Mode[MODE_HIDE] != 0)
  1315. cr.ModeBase[MODE_HIDE] = 0;
  1316. else if(!isPlayer)
  1317. cr.ModeBase[MODE_HIDE] = 1;
  1318. else
  1319. {
  1320. if(cr.Timeout[TO_SNEAK] > 0)
  1321. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_TIMEOUT_SNEAK_WAIT);
  1322. else if(IS_TURN_BASED_TIMEOUT(cr))
  1323. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_TIMEOUT_BATTLE_WAIT);
  1324. else if(_IsRealPlayer(cr))
  1325. {
  1326. // PA/APA = no sneak
  1327. Item@ armor = _CritGetItemArmor(cr);
  1328. if(valid(armor))
  1329. {
  1330. switch(armor.GetProtoId())
  1331. {
  1332. case PID_POWERED_ARMOR:
  1333. case PID_HARDENED_POWER_ARMOR:
  1334. case PID_ADVANCED_POWER_ARMOR:
  1335. case PID_ADVANCED_POWER_ARMOR_MK2:
  1336. {
  1337. cr.Say(SAY_NETMSG, "You can't sneak while wearing powered armor.");
  1338. return true;
  1339. }
  1340. }
  1341. }
  1342.  
  1343. array<Critter@> critters;
  1344. uint num = cr.GetCritters(true, FIND_LIFE | FIND_ONLY_PLAYERS, critters);
  1345.  
  1346. for(uint i = 0; i < num; i++)
  1347. {
  1348. // You can sneak in front of...
  1349.  
  1350. if(critters[i].Mode[MODE_HIDE] != 0) // ...other sneaking players
  1351. continue;
  1352.  
  1353. if(_IsOffline(critters[i])) // ...disconnected players
  1354. continue;
  1355.  
  1356. if(!_IsRealPlayer(critters[i])) // ...server staff
  1357. continue;
  1358.  
  1359. if(_hasMinigame(cr)) // ...if you have a minigame team, players with the same minigame team
  1360. {
  1361. if(_getMinigameTeamAndId(_minigame(cr)) == _getMinigameTeamAndId(_minigame(critters[i])))
  1362. continue;
  1363. }
  1364. else // If you don't have a minigame team, you can sneak in front of...
  1365. {
  1366. if(_GroupIndex(critters[i]) > 1 && IsGang(_GroupIndex(critters[i])))
  1367. {
  1368. if(_GroupIndex(critters[i]) == _GroupIndex(cr)) // ...players from the same player faction
  1369. continue;
  1370. if(GetStatus(_GroupIndex(critters[i]), cr.Id) == STATUS_FRIEND) // ...players from a faction where you are a friend
  1371. continue;
  1372. }
  1373.  
  1374. if(critters[i].Stat[ST_FOLLOW_CRIT] > 0)
  1375. {
  1376. if(uint(critters[i].Stat[ST_FOLLOW_CRIT]) == cr.Id) // ...players who tagged you
  1377. continue;
  1378.  
  1379. Critter@ taggedCr = GetCritter(critters[i].Stat[ST_FOLLOW_CRIT]);
  1380. if(valid(taggedCr) && uint(taggedCr.Stat[ST_FOLLOW_CRIT]) == cr.Id) // ...players who tagged another player in the game who tagged you
  1381. continue;
  1382. }
  1383. }
  1384. cr.Say(SAY_NETMSG, "You can't enter sneak while being seen by players who aren't in your team, unless they are sneaking.");
  1385. return true;
  1386. }
  1387. cr.ModeBase[MODE_HIDE] = 1;
  1388. }
  1389. else
  1390. {
  1391. cr.ModeBase[MODE_HIDE] = 1;
  1392. }
  1393. }
  1394. }
  1395. break;
  1396. case SK_STEAL: ////////////////////////////////////////////////////
  1397. {
  1398. if(valid(targetItem))
  1399. {
  1400. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1401. }
  1402. else if(valid(targetCr) && valid(cr.GetMap()))
  1403. {
  1404. // Loot
  1405. if(targetCr.IsPlayer() && targetCr.Mode[MODE_KILLER_ADMIN] > 0)
  1406. {
  1407. if(cr.GetAccess() >= ACCESS_TESTER)
  1408. {
  1409. // we need to block auth->auth stealing, accidental unsetting NO_LOOT/NO_STEAL is evil
  1410. cr.Say(SAY_NETMSG, "You sense a bad aura around " + targetCr.Name + " and decided to not steal anything.");
  1411. }
  1412. else
  1413. {
  1414. SetLvar(cr, LVAR_killer_admin, cr.StatBase[ST_CURRENT_HP]);
  1415. _CritSetMode(cr, MODE_NO_LOOT);
  1416. _CritSetMode(cr, MODE_NO_STEAL);
  1417. int deathAnim = ANIM2_DEAD_PULSE;
  1418. if(targetCr.Mode[MODE_KILLER_ADMIN] == 2)
  1419. {
  1420. deathAnim = GetRandomDeathAnimation();
  1421. }
  1422. else if(targetCr.Mode[MODE_KILLER_ADMIN] >= ANIM2_DEAD_BEGIN &&
  1423. targetCr.Mode[MODE_KILLER_ADMIN] < ANIM2_DEAD_END)
  1424. {
  1425. deathAnim = targetCr.Mode[MODE_KILLER_ADMIN];
  1426. }
  1427. cr.ToDead(deathAnim, null);
  1428. }
  1429. }
  1430. else if(targetCr.Cond == COND_DEAD)
  1431. {
  1432. CritterTrophy(targetCr, cr);
  1433. cr.Action(ACTION_PICK_CRITTER, 0, null);
  1434. if(cr.Mode[MODE_HIDE] != 0 && Random(0, 1) == 1 && cr.GetAccess() < ACCESS_TESTER)
  1435. cr.ModeBase[MODE_HIDE] = 0;
  1436. cr.ShowContainer(targetCr, null, TRANSFER_CRIT_LOOT);
  1437. }
  1438. else if(targetCr.Stat[ST_CURRENT_HP] <= 0 || targetCr.Cond == COND_KNOCKOUT)
  1439. {
  1440. cr.Action(ACTION_PICK_CRITTER, 0, null);
  1441. if(cr.Mode[MODE_HIDE] != 0 && Random(0, 1) == 1 && cr.GetAccess() < ACCESS_TESTER)
  1442. cr.ModeBase[MODE_HIDE] = 0;
  1443. cr.ShowContainer(targetCr, null, TRANSFER_CRIT_LOOT);
  1444. }
  1445. // Steal
  1446. else
  1447. {
  1448. if(isPlayer && cr.Timeout[TO_SK_STEAL] > 0)
  1449. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_SKILL_WEARINESS);
  1450. else
  1451. {
  1452. cr.Action(ACTION_PICK_CRITTER, 1, null);
  1453. cr.ShowContainer(targetCr, null, TRANSFER_CRIT_STEAL);
  1454. _SetTimeout(cr, TO_SK_STEAL, STEAL_TIMEOUT(cr));
  1455. cr.StatBase[ST_LAST_STEAL_CR_ID] = 0;
  1456. cr.StatBase[ST_STEAL_COUNT] = 0;
  1457. }
  1458. }
  1459. }
  1460. else
  1461. {
  1462. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1463. }
  1464. }
  1465. break;
  1466. case SK_FIRST_AID: ////////////////////////////////////////////////
  1467. {
  1468. if(valid(targetItem) || valid(targetScen))
  1469. {
  1470. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1471. break;
  1472. }
  1473.  
  1474. if(!valid(targetCr))
  1475. @targetCr = cr;
  1476. if(UseFirstAidOnCritter(cr, targetCr, null))
  1477. return true;
  1478. }
  1479. break;
  1480. case SK_DOCTOR: ///////////////////////////////////////////////////
  1481. {
  1482. if(valid(targetItem) || valid(targetScen))
  1483. {
  1484. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1485. break;
  1486. }
  1487.  
  1488. if(!valid(targetCr))
  1489. @targetCr = cr;
  1490. if(UseDoctorOnCritter(cr, targetCr, null))
  1491. return true;
  1492. }
  1493. break;
  1494. case SK_LOCKPICK: /////////////////////////////////////////////////
  1495. {
  1496. // Lockers processed in lockers.fos
  1497. }
  1498. break;
  1499. case SK_TRAPS: /////////////////////////////////////////////////
  1500. {
  1501. // Explosion
  1502. if(valid(targetItem))
  1503. {
  1504. uint16 pid = targetItem.GetProtoId();
  1505. if((pid == PID_ACTIVE_DYNAMITE || pid == PID_ACTIVE_PLASTIC_EXPLOSIVE || pid == PID_ACTIVE_MINE) &&
  1506. OnUseExplode(cr, targetItem, null, null, null, 0))
  1507. return true;
  1508.  
  1509. if(pid == PID_TESLA_ARMOR)
  1510. {
  1511. TryRechargeItem(cr, targetItem);
  1512. return true;
  1513. }
  1514. }
  1515.  
  1516. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1517. }
  1518. break;
  1519. default: //////////////////////////////////////////////////////////
  1520. {
  1521. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_USE_NOTHING);
  1522. }
  1523. break;
  1524. }
  1525.  
  1526. return true;
  1527. }
  1528.  
  1529. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1530. // Call on something critter reload weapon.
  1531. // If ammo is not valid than only unload.
  1532. void critter_reload_weapon(Critter& cr, Item& weapon, Item@ ammo)
  1533. {
  1534. // Special weapons
  1535. if(weapon.Proto.Weapon_Caliber == 0)
  1536. {
  1537. if(weapon.GetProtoId() == PID_SOLAR_SCORCHER)
  1538. {
  1539. if(IS_NIGHT(__Hour))
  1540. cr.SayMsg(SAY_NETMSG, TEXTMSG_GAME, STR_SOLAR_SCORCHER_NO_LIGHT);
  1541. else
  1542. {
  1543. weapon.AmmoCount = weapon.Proto.Weapon_MaxAmmoCount;
  1544. if(cr.Param[MODE_LAST_WPN_MODE] != 0)
  1545. {
  1546. weapon.SetMode(cr.Param[MODE_LAST_WPN_MODE]);
  1547. cr.ParamBase[MODE_LAST_WPN_MODE] = 0;
  1548. }
  1549. weapon.Update();
  1550. }
  1551. }
  1552.  
  1553. return;
  1554. }
  1555.  
  1556. // Other weapons
  1557. // Unload
  1558. if(!valid(ammo) || (weapon.AmmoCount > 0 && weapon.AmmoPid != ammo.GetProtoId()))
  1559. {
  1560. if(weapon.AmmoPid != 0)
  1561. {
  1562. Item@ existAmmo = cr.GetItem(weapon.AmmoPid, -1);
  1563. if(!valid(existAmmo))
  1564. cr.AddItem(weapon.AmmoPid, weapon.AmmoCount);
  1565. else
  1566. _IncItem(existAmmo, weapon.AmmoCount);
  1567. }
  1568. weapon.AmmoCount = 0;
  1569. }
  1570.  
  1571. // Load
  1572. if(valid(ammo))
  1573. {
  1574. uint count = MIN(ammo.GetCount(), weapon.Proto.Weapon_MaxAmmoCount - weapon.AmmoCount);
  1575. weapon.AmmoCount += count;
  1576. weapon.AmmoPid = ammo.GetProtoId();
  1577. _SubItem(ammo, count);
  1578. if(cr.Param[MODE_LAST_WPN_MODE] != 0)
  1579. {
  1580. weapon.SetMode(cr.Param[MODE_LAST_WPN_MODE]);
  1581. cr.ParamBase[MODE_LAST_WPN_MODE] = 0;
  1582. }
  1583. }
  1584.  
  1585. weapon.Update();
  1586. }
  1587.  
  1588. uint e_CritterInitRun(array<uint>@ values)
  1589. {
  1590. Critter@ cr = GetCritter(values[0]);
  1591. if(!valid(cr))
  1592. return 0;
  1593. cr.RunClientScript("_PingServer", 0, 0, 0, null, null); // check for FOCD
  1594.  
  1595. if(GetLvar(cr, LVAR_authed_char) == 1)
  1596. {
  1597. cr.RunClientScript("client_messages@_Listen", 0, 0, 0, null, null);
  1598. SetCritterEvents(cr);
  1599. }
  1600.  
  1601. return 0;
  1602. }
  1603.  
  1604. uint e_CritterMove(array<uint>@ values)
  1605. {
  1606. Critter@ cr = GetCritter(values[0]);
  1607. if(valid(cr)) cr.MoveToDir(cr.Dir);
  1608. return 0;
  1609. }
  1610.  
  1611. ////////////////////////////////////////////////////////////////////////////////////////////////////
  1612. // Call on player register or login in game.
  1613. // Default start position for players is center of global map.
  1614. import void AddOnlinePlayer(Critter& cr) from "utils";
  1615. import void SetCritterEvents(Critter& cr) from "logging_critter";
  1616.  
  1617. #pragma bindfunc "bool InitWalkProcessing(Critter@+) -> parameters.dll InitWalkProcessing "
  1618. #pragma bindfunc "bool InitWalkProcessing2(Critter@+) -> check_look.dll InitWalkProcessing2"
  1619. #pragma bindfunc "bool Critter::IsMoving() -> parameters.dll Critter_IsMoving " // test
  1620. bool MustInitWalkProcessing = true; // todo: get rid of it when GameTick is in scripts (use wrappers in dlls)
  1621.  
  1622. void critter_init(Critter& cr, bool firstTime)
  1623. {
  1624. cr.ClearExtEvents();
  1625. if(MustInitWalkProcessing)
  1626. {
  1627. MustInitWalkProcessing = false;
  1628. cr.Wait(0);
  1629. InitWalkProcessing(cr);
  1630. cr.Wait(0);
  1631. InitWalkProcessing2(cr);
  1632. }
  1633.  
  1634. FixDrugs(cr);
  1635.  
  1636. if(firstTime)
  1637. {
  1638. GameVar@ var = GetLocalVar(LVAR_kamikaze_fix, cr.Id);
  1639. var = 1;
  1640. }
  1641. if(cr.Trait[TRAIT_KAMIKAZE] != 0)
  1642. {
  1643. GameVar@ var = GetLocalVar(LVAR_kamikaze_fix, cr.Id);
  1644. if(var.GetValue() == 0)
  1645. {
  1646. for(uint i = ST_NORMAL_RESIST; i <= ST_EXPLODE_RESIST; i++)
  1647. cr.StatBase[i] -= 10;
  1648. for(uint i = ST_NORMAL_ABSORB; i <= ST_EXPLODE_ABSORB; i++)
  1649. cr.StatBase[i] += 10;
  1650. var = 1;
  1651. }
  1652. }
  1653.  
  1654. RemoveArenaItems(cr); // keep it here, for future hinkley+followers bugs
  1655.  
  1656. if(cr.IsNpc())
  1657. {
  1658. cr.SetEvent(CRITTER_EVENT_BARTER, "trader@_OnBarter");
  1659. if(_IsFollower(cr))
  1660. {
  1661. cr.SetBagRefreshTime((uint(-1)));
  1662. }
  1663. }
  1664.  
  1665. if(cr.IsPlayer())
  1666. {
  1667. if((GetGroupIndex(cr)<200) && IsLexem(cr,"$faction")) UnsetLexem(cr, "$faction");
  1668.  
  1669. AddOnlinePlayer(cr);
  1670. CreateTimeEvent(AFTER(REAL_SECOND(5)), "e_CritterInitRun", cr.Id, false);
  1671. if(GetLvar(cr, LVAR_authed_char) == 0)
  1672. SetSpectator(cr, false);
  1673.  
  1674. // such paranoic i am!
  1675. CreateTimeEvent(AFTER(REAL_SECOND(5)), "e_CritterInit_Broadcast", cr.Id, false);
  1676. CreateTimeEvent(AFTER(REAL_SECOND(6)), "e_CritterInit_OnlineStats", cr.Id, false);
  1677. CreateTimeEvent(AFTER(REAL_SECOND(7)), "e_CritterInit_XFire", cr.Id, false);
  1678.  
  1679. int modeRun = 0;
  1680. Item@ weap = cr.GetItem(0, SLOT_HAND1);
  1681. if(valid(weap) && weap.GetType() == ITEM_TYPE_WEAPON && weap.Proto.Weapon_NoRunning)
  1682. modeRun++;
  1683. @weap = cr.GetItem(0, SLOT_HAND2);
  1684. if(valid(weap) && weap.GetType() == ITEM_TYPE_WEAPON && weap.Proto.Weapon_NoRunning)
  1685. modeRun++;
  1686.  
  1687. cr.ModeBase[MODE_NO_RUN] = modeRun;
  1688.  
  1689. UpdateFactionsInfo(cr);
  1690. }
  1691.  
  1692. if(firstTime)
  1693. {
  1694. // INITIALIZE PARAMS
  1695. if(cr.IsPlayer())
  1696. {
  1697. if(int(cr.Id) > GetGvar(GVAR_last_registered))
  1698. SetGvar(GVAR_last_registered, cr.Id);
  1699.  
  1700. GameVar@ var = GetLocalVar(LVAR_factions_player_faction, cr.Id);
  1701. var = _GroupIndex(cr);
  1702. GameVar@ var2 = GetLocalVar(LVAR_factions_player_rank, cr.Id);
  1703. var2 = _GroupRank(cr);
  1704.  
  1705. // Input: 7 special, 3 tag skills, 2 traits, age, gender,
  1706. // body type, some 3d layers
  1707. uint traits = 0;
  1708. for(uint i = TRAIT_BEGIN; i <= TRAIT_END; i++)
  1709. {
  1710. if(cr.ParamBase[i] != 0 && traits < 2)
  1711. {
  1712. cr.ParamBase[i] = 1;
  1713. traits++;
  1714. }
  1715. else
  1716. cr.ParamBase[i] = 0;
  1717. }
  1718.  
  1719. if(cr.StatBase[ST_GENDER] < 0 || cr.StatBase[ST_GENDER] > 1)
  1720. cr.StatBase[ST_GENDER] = 0;
  1721. if(cr.StatBase[ST_AGE] < 14 || cr.StatBase[ST_AGE] > 80)
  1722. cr.StatBase[ST_AGE] = 25;
  1723. for(uint i = ST_STRENGTH; i <= ST_LUCK; i++)
  1724. cr.StatBase[i] = CLAMP(cr.StatBase[i], 1, 10);
  1725.  
  1726. if((cr.StatBase[ST_STRENGTH] + cr.StatBase[ST_PERCEPTION] + cr.StatBase[ST_ENDURANCE] +
  1727. cr.StatBase[ST_CHARISMA] + cr.StatBase[ST_INTELLECT] + cr.StatBase[ST_AGILITY] + cr.StatBase[ST_LUCK]) != __StartSpecialPoints)
  1728. {
  1729. for(uint i = ST_STRENGTH; i <= ST_LUCK; i++)
  1730. cr.StatBase[i] = 5;
  1731. }
  1732.  
  1733. cr.StatBase[ST_EMP_RESIST] = 500;
  1734. cr.PerkBase[PE_AWARENESS] = 1;
  1735.  
  1736. #ifdef PLAYERS_3D
  1737. if(cr.StatBase[ST_GENDER] == GENDER_MALE)
  1738. {
  1739. cr.StatBase[ST_BASE_CRTYPE] = CLAMP(cr.StatBase[ST_BASE_CRTYPE], CRTYPE_3D_MALE_NORMAL, CRTYPE_3D_MALE_FAT);
  1740. cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_HAIR] = CLAMP(cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_HAIR], ATTRIBUTE_Hair_Male_Afro, ATTRIBUTE_Hair_Male_Shoulder + ATTRIBUTE_COLOR_RedGrey);
  1741. if(cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_MUSTACHE] != ATTRIBUTE_Mustache_MadMax)
  1742. 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);
  1743. 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);
  1744. }
  1745. else
  1746. {
  1747. cr.StatBase[ST_BASE_CRTYPE] = CLAMP(cr.StatBase[ST_BASE_CRTYPE], CRTYPE_3D_FEMALE_NORMAL, CRTYPE_3D_FEMALE_FAT);
  1748. cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_HAIR] = CLAMP(cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_HAIR], ATTRIBUTE_Hair_Female_Afro, ATTRIBUTE_Hair_Female_Short + ATTRIBUTE_COLOR_RedGrey);
  1749. if(cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_MUSTACHE] != ATTRIBUTE_Mustache_MadMax)
  1750. cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_MUSTACHE] = 0;
  1751. cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_BEARD] = 0;
  1752. }
  1753.  
  1754. 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);
  1755. cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_PONYTAIL] = CLAMP(cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_PONYTAIL], 0, ATTRIBUTE_Ponytail_Ponytail2 + ATTRIBUTE_COLOR_RedGrey);
  1756. cr.StatBase[ST_ANIM3D_LAYERS + ANIM3D_LAYER_ARMLET] = ATTRIBUTE_Armlet_PipBoyClosed;
  1757. cr.ChangeCrType(cr.StatBase[ST_BASE_CRTYPE]);
  1758. #endif
  1759. #ifndef PLAYERS_3D
  1760. cr.StatBase[ST_BASE_CRTYPE] = (cr.Stat[ST_GENDER] == GENDER_MALE ? CRTYPE_MALE_DEFAULT : CRTYPE_FEMALE_DEFAULT);
  1761. cr.ChangeCrType(cr.StatBase[ST_BASE_CRTYPE]);
  1762. #endif
  1763.  
  1764. cr.StatBase[ST_FIXBOY_COUNTER] = 1;
  1765. cr.StatBase[ST_FIXBOY_FILTER] = 0;
  1766. cr.StatBase[ST_FIXBOY_FIXALL] = 0;
  1767. }
  1768.  
  1769. if(cr.TagSkill[TAG_SKILL1] < int(SKILL_BEGIN) || cr.TagSkill[TAG_SKILL1] > int(SKILL_END))
  1770. cr.TagSkillBase[TAG_SKILL1] = 0;
  1771. if(cr.TagSkill[TAG_SKILL2] < int(SKILL_BEGIN) || cr.TagSkill[TAG_SKILL2] > int(SKILL_END))
  1772. cr.TagSkillBase[TAG_SKILL2] = 0;
  1773. if(cr.TagSkill[TAG_SKILL3] < int(SKILL_BEGIN) || cr.TagSkill[TAG_SKILL3] > int(SKILL_END))
  1774. cr.TagSkillBase[TAG_SKILL3] = 0;
  1775. if(cr.TagSkill[TAG_SKILL1] == cr.TagSkill[TAG_SKILL2])
  1776. cr.TagSkillBase[TAG_SKILL1] = 0;
  1777. if(cr.TagSkill[TAG_SKILL2] == cr.TagSkill[TAG_SKILL3])
  1778. cr.TagSkillBase[TAG_SKILL2] = 0;
  1779. if(cr.TagSkill[TAG_SKILL3] == cr.TagSkill[TAG_SKILL1])
  1780. cr.TagSkillBase[TAG_SKILL3] = 0;
  1781.  
  1782. CritterGenerate(cr);
  1783. cr.StatBase[ST_CURRENT_HP] = cr.Stat[ST_MAX_LIFE];
  1784. cr.StatBase[ST_CURRENT_AP] = cr.Stat[ST_ACTION_POINTS] * 100;
  1785.  
  1786. for(uint i = REPUTATION_BEGIN; i <= REPUTATION_END; i++)
  1787. cr.ParamBase[i] = int(0x80000000);
  1788. ReputationsInit(cr);
  1789.  
  1790. if(cr.IsPlayer())
  1791. {
  1792. string charSpecialStr = cr.StatBase[ST_STRENGTH] +" "+ cr.StatBase[ST_PERCEPTION] +" "+ cr.StatBase[ST_ENDURANCE] +" "+ cr.StatBase[ST_CHARISMA] +" "+ cr.StatBase[ST_INTELLECT] +" "+ cr.StatBase[ST_AGILITY] +" "+ cr.StatBase[ST_LUCK];
  1793. string charTagsStr = cr.TagSkill[TAG_SKILL1]+" "+cr.TagSkill[TAG_SKILL2]+" "+cr.TagSkill[TAG_SKILL3];
  1794. string charTraitsStr;
  1795. for(uint i = TRAIT_BEGIN; i <= TRAIT_END; i++)
  1796. if(cr.ParamBase[i] != 0)
  1797. charTraitsStr += " "+i;
  1798.  
  1799. FLog(LOG_CHARS_CREATED, cr.Name+"("+cr.Id+") special: "+charSpecialStr+" traits:"+charTraitsStr+" tags: "+charTagsStr);
  1800. }
  1801.  
  1802. // FURTHER INITIALIZATION
  1803. if(cr.IsPlayer())
  1804. {
  1805. for(uint i = ST_STRENGTH; i <= ST_LUCK; i++)
  1806. cr.StatBase[i] = CLAMP(cr.StatBase[i], 1, 10);
  1807. cr.StatBase[ST_DAMAGE_TYPE] = DAMAGE_NORMAL;
  1808.  
  1809. SetStartLocation(cr);
  1810. // if(_CritCountItem(cr,PID_RADIO)==0) _CritAddItem(cr,PID_RADIO,1);
  1811.  
  1812. array<uint16> pids;
  1813. array<uint> minCounts;
  1814. array<uint> maxCounts;
  1815. array<int> slots;
  1816.  
  1817. uint bagId = 2;
  1818. uint num = 0;
  1819. uint[] bags = { 131, 132, 133, 134, 135, 136, 137, 138, 139, 144, 145 };
  1820. bagId = random_from_array(bags);
  1821. num = GetBagItems(bagId, pids, minCounts, maxCounts, slots);
  1822.  
  1823. // cr.Say(SAY_NETMSG, "Free stuff!");
  1824. for(uint i = 0; i < num; i++)
  1825. {
  1826. Item@ it = cr.AddItem(pids[i], Random(minCounts[i], maxCounts[i]));
  1827. it.Cost = 1;
  1828. }
  1829. SetGroupIndex(cr, FACTION_NONE);
  1830. }
  1831. else
  1832. {
  1833. cr.ChangeCrType(cr.StatBase[ST_BASE_CRTYPE]);
  1834.  
  1835. if(cr.Stat[ST_DEFAULT_ARMOR_PID] != 0)
  1836. {
  1837. Item@ armor = cr.AddItem(cr.Stat[ST_DEFAULT_ARMOR_PID], 1);
  1838. if(valid(armor))
  1839. {
  1840. cr.MoveItem(armor.Id, 1, SLOT_ARMOR);
  1841. if(cr.Stat[ST_OVERRIDE_CRTYPE] != 0)
  1842. cr.ChangeCrType(cr.StatBase[ST_OVERRIDE_CRTYPE]);
  1843. }
  1844. cr.SetFavoriteItem(SLOT_ARMOR, cr.Stat[ST_DEFAULT_ARMOR_PID]);
  1845. }
  1846.  
  1847. if(cr.Stat[ST_DEFAULT_HELMET_PID] != 0)
  1848. {
  1849. Item@ helmet = cr.AddItem(cr.Stat[ST_DEFAULT_HELMET_PID], 1);
  1850. if(valid(helmet))
  1851. cr.MoveItem(helmet.Id, 1, SLOT_HEAD);
  1852. // cr.SetFavoriteItem(SLOT_HEAD,cr.Stat[ST_DEFAULT_HELMET_PID]);
  1853. }
  1854.  
  1855. #define _AddTrophy \
  1856. # (__critter, __pid, __count, __buffer) { Item@__item = __critter.AddItem(__pid, __count); if(valid(__item)) \
  1857. __buffer.insertLast(__item); \
  1858. }
  1859.  
  1860. if(valid(cr.GetMap()) && !cr.GetMap().GetLocation().IsInstancedQuest())
  1861. {
  1862. array<Item@> trophies;
  1863. switch(cr.Stat[ST_BASE_CRTYPE])
  1864. {
  1865. case CRTYPE_BRAHMIN:
  1866. {
  1867. _AddTrophy(cr, PID_BRAHMIN_HIDE_TROPHY, 1, trophies);
  1868. break;
  1869. }
  1870. case CRTYPE_SCORPION:
  1871. case CRTYPE_SCORPION_SMALL:
  1872. _AddTrophy(cr, PID_SCORPION_TAIL, 1, trophies);
  1873. break;
  1874. case CRTYPE_GECKO_SMALL:
  1875. _AddTrophy(cr, PID_GECKO_PELT, 1, trophies);
  1876. break;
  1877. case CRTYPE_GECKO:
  1878. _AddTrophy(cr, PID_GOLDEN_GECKO_PELT, 1, trophies);
  1879. break;
  1880. case CRTYPE_GECKO_FIRE:
  1881. _AddTrophy(cr, PID_FIREGECKO_PELT, 1, trophies);
  1882. break;
  1883. }
  1884. for(uint t = 0, tlen = trophies.length(); t < tlen; t++)
  1885. {
  1886. cr.MoveItem(trophies[t].Id, trophies[t].GetCount(), SLOT_TROPHY);
  1887. }
  1888. }
  1889.  
  1890. if(cr.Stat[ST_LEVEL] != 0)
  1891. NpcProcessLevel(cr);
  1892. // TODO: npcprocessbag ?
  1893. }
  1894. SetBloodType(cr); // arrr
  1895. SetBirthDate(cr);
  1896.  
  1897. #ifdef __DEBUG__ // WIPENIGHT: starter item, dogtag
  1898. // need to be after setting birth date/blood type
  1899. if(cr.IsPlayer())
  1900. {
  1901. Item@ dogtag = DogTag(cr);
  1902. if(valid(dogtag))
  1903. {
  1904. dogtag.Cost = 50;
  1905. for(uint s = SLOT_HEAD; s >= SLOT_HAND1; s--)
  1906. {
  1907. if(s == SLOT_ARMOR)
  1908. continue;
  1909.  
  1910. if(!valid(cr.GetItem(0, s)))
  1911. {
  1912. if(cr.MoveItem(dogtag.Id, dogtag.GetCount(), s))
  1913. break;
  1914. }
  1915. }
  1916. }
  1917. }
  1918. #endif
  1919.  
  1920. #ifdef __DEBUG__ // WIPENIGHT: SPECIAL-based description
  1921. int description1 = 0, description2 = 0;
  1922. CritterDescription_Set(cr, description1, description2);
  1923. if(description1 > 0 && description2 > 0)
  1924. {
  1925. WLog("special_description", "SPECIAL description : " + description1 + "," + description2);
  1926. cr.ParamBase[ST_DESCRIPTION1] = description1;
  1927. cr.ParamBase[ST_DESCRIPTION2] = description2;
  1928. }
  1929. #endif
  1930.  
  1931. // selected NPCs can call it inside own init function
  1932. if(cr.IsPlayer())
  1933. CritterAgeInit(cr);
  1934. }
  1935. else // not first time
  1936. {
  1937. CheckBirthDate(cr);
  1938.  
  1939. // selected NPCs can call it inside own init function
  1940. if(cr.IsPlayer())
  1941. CritterAgeInit(cr);
  1942.  
  1943. if(cr.Trait[TRAIT_FAST_SHOT] != 0)
  1944. cr.ModeBase[MODE_NO_AIM] = 1;
  1945.  
  1946. // Erase zero time events
  1947. cr.EraseTimeEvents(0);
  1948.  
  1949. // Current skin validation
  1950. Item@ armor = cr.GetItem(0, SLOT_ARMOR);
  1951. if(!valid(armor)) // Restore
  1952. {
  1953. uint crType = cr.Stat[ST_BASE_CRTYPE];
  1954. if(crType == 0)
  1955. crType = (cr.Stat[ST_GENDER] == GENDER_MALE ? CRTYPE_MALE_DEFAULT : CRTYPE_FEMALE_DEFAULT);
  1956. if(cr.CrType != crType)
  1957. cr.ChangeCrType(crType);
  1958. }
  1959.  
  1960. int[] slots = { SLOT_ARMOR, ITEM_PERK_ARMOR, SLOT_HEAD, ITEM_PERK_HELMET };
  1961. for(uint s = 0, slen = slots.length(); s < slen; s += 2)
  1962. {
  1963. Item@ item = cr.GetItem(0, slots[s]);
  1964. if(!valid(item) && cr.Stat[slots[s + 1]] != 0)
  1965. DisableItemPerk(cr, slots[s + 1]);
  1966. }
  1967.  
  1968. // group index/rank sync if player
  1969. if(cr.IsPlayer())
  1970. {
  1971. UpdateGroupVars(cr);
  1972. }
  1973. // locations visibility
  1974. if(cr.IsPlayer())
  1975. {
  1976. if(GetLvar(cr, LVAR_tent_id) != 0)
  1977. {
  1978. Location@ loc = GetLocation(GetLvar(cr, LVAR_tent_id));
  1979. if(valid(loc))
  1980. cr.SetKnownLoc(true, loc.Id);
  1981. Map@ map;
  1982. if(valid(loc))
  1983. @map = loc.GetMapByIndex(0);
  1984. if(valid(map))
  1985. map.SetData(MAP_DATA_LAST_ENTERED, ELAPSED_TIME);
  1986. }
  1987. }
  1988. // Clear timeouts if too long (happens when saves got removed)
  1989. for(uint i = TIMEOUT_BEGIN; i <= TIMEOUT_END; i++)
  1990. if(i != TO_BATTLE && cr.Timeout[i] > MAXIMUM_TIMEOUT)
  1991. cr.TimeoutBase[i] = ELAPSED_TIME;
  1992.  
  1993. if(_IsTrueNpc(cr) && _GroupIndex(cr) > 0 && _GroupIndex(cr) < 200 && FACTION_MODE_PASSIVE < _GroupMode(cr) && _GroupMode(cr) < FACTION_MODE_NEVER && cr.GetScriptId() == 0)
  1994. {
  1995. // Scriptless guard; fill with default events
  1996. if(IsTown(cr.GetMap()))
  1997. cr.SetScript("generic_guard@_FactionGuard");
  1998. else
  1999. cr.SetScript("encounter_guard@critter_init");
  2000. }
  2001.  
  2002. // Rejoin group
  2003. if(cr.IsPlayer() && cr.GetMapId() == 0)
  2004. {
  2005. array<Critter@> followers;
  2006. uint n = GetFollowers(cr, true, followers);
  2007. for(uint i = 0; i < n; i++)
  2008. if(followers[i].GetMapId() == 0)
  2009. {
  2010. followers[i].LeaveGlobalGroup();
  2011. TransferToNPCMap(followers[i]); // Can't transfer directly to group when on global
  2012. followers[i].TransitToGlobalGroup(cr.Id);
  2013. followers[i].FollowerVarBase[FV_WM_IDLE] = 0;
  2014. }
  2015. }
  2016. }
  2017.  
  2018. }
  2019.  
  2020. uint e_CritterInit_Broadcast(array<int>@ data)
  2021. {
  2022. Critter@ cr = GetCritter(data[0]);
  2023. if(valid(cr) && cr.IsPlayer())
  2024. DumpBroadcastBuffer(cr);
  2025.  
  2026. return(0);
  2027. }
  2028.  
  2029. uint e_CritterInit_OnlineStats(array<int>@ data)
  2030. {
  2031. Critter@ cr = GetCritter(data[0]);
  2032. if(valid(cr) && cr.IsPlayer())
  2033. OnlineStats_SendSetup(cr);
  2034.  
  2035. return(0);
  2036. }
  2037.  
  2038. uint e_CritterInit_XFire(array<int>@ data)
  2039. {
  2040. Critter@ cr = GetCritter(data[0]);
  2041. if(valid(cr) && cr.IsPlayer())
  2042. XFireClient_Update(cr);
  2043.  
  2044. return(0);
  2045. }
  2046.  
  2047. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2048. // Call on critter exit from game.
  2049.  
  2050. void critter_finish(Critter& cr, bool toDelete)
  2051. {
  2052. if(!(FLAG(_getMinigameFlags(cr.Param[ST_MINIGAME_DATA]), MINIGAME_PERSISTENT)))
  2053. UnsetMinigame(cr, false); // Minigames end along with gaming session IF the minigame team isn't persistent
  2054.  
  2055. if(cr.IsPlayer())
  2056. {
  2057. if(_IsRealPlayer(cr))
  2058. RemoveNotLegit(cr);
  2059. RemoveOnlinePlayer(cr);
  2060. WorldmapRemovePlayer(cr);
  2061. RemoveWorkbenches(cr);
  2062. RemoveAuthed(cr.Id);
  2063. GMTrack(cr.Id, "TARGET logged out.");
  2064. }
  2065. if(toDelete && cr.Stat[ST_DEAD_BLOCKER_ID] != 0)
  2066. {
  2067. Item@ block = ::GetItem(cr.Stat[ST_DEAD_BLOCKER_ID]);
  2068. if(valid(block))
  2069. DeleteItem(block);
  2070. cr.StatBase[ST_DEAD_BLOCKER_ID] = 0;
  2071. }
  2072. if(toDelete)
  2073. {
  2074. // DeleteVars(cr.Id);
  2075. RemoveNpcSchedule(cr);
  2076. GMTrack(cr.Id, "TARGET deleted.");
  2077.  
  2078. if(valid(cr.GetMap()) && (IsBase(cr.GetMap()) || IsTent(cr.GetMap())))
  2079. {
  2080. if(_CritCountItem(cr, PID_BOTTLE_CAPS)>=10000 && cr.Stat[ST_LEVEL]>1)
  2081. {
  2082. Map@ map = cr.GetMap();
  2083. Item@ hintbook = map.AddItem(cr.HexX, cr.HexY, PID_BOOK_OF_ACHIEVEMENT, 1);
  2084. hintbook.Val0 = cr.Stat[ST_LEVEL]-1;
  2085. hintbook.Val2 = cr.Stat[ST_LEVEL];
  2086. }
  2087. }
  2088. }
  2089. }
  2090.  
  2091. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2092. // Call every __CritterIdleTick time.
  2093. #define REPUTATION_DECAY (10.0f) // 100 points per day
  2094. #define REPUTATION_DECAY_TIME (REAL_DAY(1) / REPUTATION_DECAY)
  2095. void critter_idle(Critter& cr)
  2096. {
  2097. // Healing
  2098. if(cr.Timeout[TO_HEALING] == 0)
  2099. {
  2100. if(!cr.IsDead() && cr.Mode[MODE_NO_HEAL] == 0 && cr.Timeout[TO_BATTLE] == 0 && cr.Stat[ST_CURRENT_HP] < cr.Stat[ST_MAX_LIFE])
  2101. {
  2102. if(cr.Stat[ST_HEALING_RATE] > 0)
  2103. cr.StatBase[ST_CURRENT_HP] += MAX(cr.Stat[ST_HEALING_RATE] * cr.Stat[ST_MAX_LIFE] / 100, 3);
  2104. else
  2105. cr.StatBase[ST_CURRENT_HP] += cr.Stat[ST_HEALING_RATE];
  2106. if(cr.StatBase[ST_CURRENT_HP] > cr.Stat[ST_MAX_LIFE])
  2107. cr.StatBase[ST_CURRENT_HP] = cr.Stat[ST_MAX_LIFE];
  2108. }
  2109.  
  2110. _SetTimeout(cr, TO_HEALING, HEALING_TIMEOUT(cr));
  2111. }
  2112.  
  2113. // try to heal injuries
  2114. if(cr.IsInjured() && cr.IsNpc() && IsHumanoid(cr) && cr.Timeout[TO_SK_DOCTOR] <= 0 && cr.Timeout[TO_BATTLE] == 0)
  2115. {
  2116. if(cr.Stat[ST_INTELLECT] >= Random(1, 10))
  2117. {
  2118. array<NpcPlane@> planes;
  2119. cr.GetPlanes(PLANE_DOCTOR_CRITTER, planes);
  2120. if(planes.length() > 0)
  2121. planes[0].IdentifierExt = cr.Id;
  2122. else
  2123. AddDoctorCritterPlane(cr, valid(cr.GetCurPlane()) ? cr.GetCurPlane().Priority + 5 : 0, cr, false); // self
  2124. }
  2125. }
  2126.  
  2127. // cr.Say(SAY_NORM, ""+cr.Stat[ST_TEAM_ID]);
  2128. if(cr.IsPlayer())
  2129. {
  2130. Map@ map = cr.GetMap();
  2131. if(valid(map) && _IsOffline(cr) && !IsBase(map) && !IsTent(map))
  2132. {
  2133. array<Critter@> followers;
  2134. uint num = GetFollowers(cr, map, followers);
  2135. for(uint i = 0; i < num; i++)
  2136. {
  2137. Flee(followers[i]);
  2138. }
  2139.  
  2140. }
  2141.  
  2142. uint diff = ELAPSED_TIME > uint(cr.Stat[ST_REP_DECAY] + REPUTATION_DECAY_TIME) ? ELAPSED_TIME - uint(cr.Stat[ST_REP_DECAY]) : 0;
  2143. if(diff > 0)
  2144. {
  2145. // decay reputations
  2146. float decay = float(REPUTATION_DECAY) * float(diff) / float(REPUTATION_DECAY_TIME); // int overflow warning
  2147. for(uint i = 0; i < REPUTATION_COUNT; i++)
  2148. {
  2149. int rep = cr.Reputation[i];
  2150. if(rep > 200)
  2151. rep = MAX(200, rep - decay);
  2152. else if(rep < -200)
  2153. rep = MIN(-200, rep + decay);
  2154. if(rep == 0)
  2155. rep = int(0x80000000); // just hide it
  2156. cr.ReputationBase[i] = rep;
  2157. }
  2158. }
  2159. cr.StatBase[ST_REP_DECAY] = ELAPSED_TIME;
  2160. }
  2161.  
  2162. // replication
  2163. if(cr.IsDead() && cr.Timeout[TO_REPLICATION] == 0)
  2164. ReplicateCritter(cr);
  2165.  
  2166. if(cr.IsNotValid) // might have been deleted during replication
  2167. return;
  2168.  
  2169. if(cr.IsPlayer())
  2170. {
  2171. // keeping track of worldmap zones
  2172. WorldmapUpdatePlayer(cr);
  2173.  
  2174. UpdateDrugs(cr);
  2175. }
  2176. }
  2177.  
  2178. void CritterTrophy(Critter& critter, Critter& looter)
  2179. {
  2180. uint count = _CritCountItem(critter, PID_BRAHMIN_HIDE_TROPHY);
  2181. if(count > 0)
  2182. {
  2183. _CritDeleteItem(critter, PID_BRAHMIN_HIDE_TROPHY, count);
  2184. _CritAddItem(critter, PID_BRAHMIN_HIDE, count);
  2185. }
  2186.  
  2187. if(looter.Perk[PE_GECKO_SKINNING] <= 0 &&
  2188. (critter.CrType == CRTYPE_GECKO ||
  2189. critter.CrType == CRTYPE_GECKO_SMALL ||
  2190. critter.CrType == CRTYPE_GECKO_FIRE))
  2191. return;
  2192.  
  2193. bool geckoSkinning = false;
  2194. array<Item@> trophies;
  2195. count = critter.GetItems(SLOT_TROPHY, trophies);
  2196. for(uint t = 0; t < count; t++)
  2197. {
  2198. if(!geckoSkinning &&
  2199. (trophies[t].GetProtoId() == PID_GECKO_PELT ||
  2200. trophies[t].GetProtoId() == PID_GOLDEN_GECKO_PELT ||
  2201. trophies[t].GetProtoId() == PID_FIREGECKO_PELT))
  2202. {
  2203. geckoSkinning = true;
  2204. looter.Say(SAY_NETMSG, "You have skinned the gecko.");
  2205. }
  2206.  
  2207. critter.MoveItem(trophies[t].Id, trophies[t].GetCount(), SLOT_INV);
  2208. }
  2209. }
  2210.  
  2211. void TownCitizenKilled(Critter& critter, Critter@ killer)
  2212. {
  2213. if(!valid(killer))
  2214. return;
  2215. uint mapid = critter.GetMapId();
  2216.  
  2217. for(uint i = 1; i < GetTownCount() + 1; i++)
  2218. {
  2219. // DLog("mapid = " + mapid);
  2220. // DLog("town mapid = " + GetTown(i).GetMapID());
  2221.  
  2222. ITown@ town = GetTown(i);
  2223. if(mapid == town.GetMapID())
  2224. {
  2225. uint factionId = town.GetControllingFaction();
  2226. if(GetGroupIndex(killer) == factionId)
  2227. town.ModifyInfluence(factionId, -0.05);
  2228. }
  2229. }
  2230.  
  2231. }
  2232.  
  2233. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2234. // Call on something critter dies.
  2235. // Killer can be null.
  2236. void death(Critter& cr, int p0, int p1, int p2)
  2237. {
  2238. cr.ToDead(p0, null);
  2239. }
  2240.  
  2241. void critter_dead(Critter& cr, Critter@ killer)
  2242. {
  2243. uint crGroup = _GroupIndex(cr);
  2244. Map@ map = cr.GetMap();
  2245. cr.ParamBase[TO_IMMUNITY_TIME] = MAX(uint(cr.Param[TO_IMMUNITY_TIME]), ELAPSED_TIME + SLEEPY_DEATH_IMMUNITY(cr));
  2246. if(valid(killer) && killer.IsPlayer() && !IsFlaggedAsIllegal(cr) && (killer.Id != cr.Id) && (crGroup > 1) && !IsArenaCombatant(killer) && !IsDueling(killer))
  2247. {
  2248. FeedReputations(killer, crGroup, 0);
  2249. if(IsTown(map))
  2250. SubReputation(killer, crGroup, REP_KILL_PENALTY * MAX(1, _GroupRank(cr)));
  2251. else
  2252. TryReportOffense(cr, killer, REP_KILL_PENALTY * MAX(1, _GroupRank(cr)));
  2253. }
  2254.  
  2255. // heal radiation
  2256. // HealRadiation(cr);
  2257.  
  2258. // items 'pre' filtering
  2259. RemoveWorkbenches(cr); // better safe than sorry
  2260.  
  2261. // npc
  2262. if(cr.IsNpc())
  2263. {
  2264. // Clear NPC memory about enemies after it dies.
  2265. cr.ClearEnemyStack();
  2266. }
  2267. else // players
  2268. {
  2269. // If critter is being tracked (by command, not tracking feature) and tracker is online
  2270. GMTrack(cr.Id, "TARGET has died on map" + cr.GetMapId());
  2271. }
  2272. // common item removal/damaging
  2273. // retrieve all critters' items (armor, hand1, hand2, inv...)
  2274. Item@ armor = cr.GetItem(0, SLOT_ARMOR);
  2275. Item@ helmet = cr.GetItem(0, SLOT_HEAD);
  2276. array<Item@> trophies;
  2277. array<Item@> items;
  2278.  
  2279. cr.GetItems(SLOT_TROPHY, trophies);
  2280. cr.GetItems(SLOT_HAND1, items);
  2281. cr.GetItems(SLOT_HAND2, items);
  2282. cr.GetItems(SLOT_INV, items);
  2283.  
  2284. bool isArmor = (valid(armor));
  2285. bool isHelmet = (valid(helmet));
  2286. bool isTrophy = (trophies.length() > 0);
  2287.  
  2288. // remove armor/helmet/trophies on following death types
  2289. if((isArmor || isHelmet || isTrophy) &&
  2290. (cr.Anim2Dead == ANIM2_DEAD_FUSED || cr.Anim2Dead == ANIM2_DEAD_PULSE_DUST || cr.Anim2Dead == ANIM2_DEAD_EXPLODE) &&
  2291. _CritCanDropItemsOnDead(cr))
  2292. {
  2293. if(!valid(map) || !(map.GetLocation().GetProtoId() == LOCATION_Hinkley && IsInsideArena(cr)))
  2294. {
  2295. if(isArmor)
  2296. {
  2297. DeleteItem(armor);
  2298. isArmor = false;
  2299. }
  2300. if(isHelmet)
  2301. {
  2302. DeleteItem(helmet);
  2303. isHelmet = false;
  2304. }
  2305. }
  2306. if(isTrophy && cr.Anim2Dead == ANIM2_DEAD_EXPLODE)
  2307. {
  2308. DeleteItems(trophies);
  2309. isTrophy = false;
  2310. }
  2311. }
  2312. // damage armor on other critical death types
  2313. if((isArmor || isHelmet) &&
  2314. (cr.Anim2Dead == ANIM2_DEAD_BURST || cr.Anim2Dead == ANIM2_DEAD_BLOODY_SINGLE || cr.Anim2Dead == ANIM2_DEAD_BLOODY_BURST ||
  2315. cr.Anim2Dead == ANIM2_DEAD_PULSE || cr.Anim2Dead == ANIM2_DEAD_PULSE_DUST || cr.Anim2Dead == ANIM2_DEAD_LASER ||
  2316. cr.Anim2Dead == ANIM2_DEAD_EXPLODE || cr.Anim2Dead == ANIM2_DEAD_FUSED || cr.Anim2Dead == ANIM2_DEAD_BURN || cr.Anim2Dead == ANIM2_DEAD_BURN_RUN)
  2317. )
  2318. {
  2319. if(isArmor)
  2320. WearItem(cr, armor, (MAX_DETERIORATION - armor.Deterioration) / 5); // 23-07-2013 Cubik: złagodzone psucie sie pancerzy i helmow podczas smierci z 50% na 20% na każda smierc
  2321. if(isHelmet)
  2322. WearItem(cr, helmet, (MAX_DETERIORATION - helmet.Deterioration) / 5);
  2323. }
  2324.  
  2325. if(isArmor)
  2326. items.insertLast(armor);
  2327. if(isHelmet)
  2328. items.insertLast(helmet);
  2329.  
  2330. // move all armor items to inventory of npc. delete it if no drop.
  2331. if(cr.Stat[ST_REPLICATION_TIME] < 0 && cr.IsNpc())
  2332. {
  2333. for(uint i = 0, j = items.length(); i < j; i++)
  2334. if(items[i].GetType() == ITEM_TYPE_ARMOR && items[i].CritSlot != SLOT_INV)
  2335. {
  2336. if(FLAG(items[i].Flags, ITEM_NO_STEAL) || FLAG(items[i].Flags, ITEM_NO_LOOT))
  2337. DeleteItem(items[i]);
  2338. else
  2339. cr.MoveItem(items[i].Id, 1, SLOT_INV);
  2340. }
  2341. }
  2342.  
  2343. // drop items immediately if pulsed
  2344. if(valid(map) && (cr.Anim2Dead == ANIM2_DEAD_PULSE_DUST || cr.Anim2Dead == ANIM2_DEAD_EXPLODE) && _CritCanDropItemsOnDead(cr))
  2345. {
  2346. for(uint i = 0; i < items.length(); i++)
  2347. {
  2348. if(valid(items[i]) && items[i].GetType() == ITEM_TYPE_CONTAINER)
  2349. {
  2350. DeleteItem(items[i]);
  2351. }
  2352. }
  2353. // drop all (armor/helmet/trophies was removed earlier)
  2354. if(!(map.GetLocation().GetProtoId() == LOCATION_Hinkley && IsInsideArena(cr)))
  2355. MoveItems(items, map, cr.HexX, cr.HexY);
  2356. }
  2357. // 'loot-me'
  2358. if(!_CritHasMode(cr, MODE_NO_LOOT))
  2359. {
  2360. uint mappid = 0;
  2361. if(valid(cr.GetMap()))
  2362. mappid = cr.GetMap().GetProtoId();
  2363.  
  2364. // encounters and certain towns
  2365. if(IsEncounterMap(cr.GetMap()) || mappid == MAP_VaultCityDowntown || mappid == MAP_NCR || mappid == MAP_Junktown || mappid == MAP_HubDowntown || mappid == MAP_SanFranChina || mappid == MAP_Gunrunner)
  2366. {
  2367. DPlayerLog(cr, "Having loot here!");
  2368. array<Critter@> crits;
  2369. uint num = cr.GetCritters(true, FIND_LIFE | FIND_ONLY_NPC, crits);
  2370.  
  2371. for(uint i = 0; i < num; i++)
  2372. {
  2373. // loot only players and other humanoids, and only when you're humanoid and you don't have loot plane already
  2374. if((cr.IsPlayer() || IsHumanoid(cr)) && IsHumanoid(crits[i]) && crits[i].GetPlanes(PLANE_LOOT, null) == 0 && _IsTrueNpc(crits[i]))
  2375. {
  2376. string scriptName = "";
  2377. if(crits[i].GetScriptId() > 0)
  2378. scriptName = GetScriptName(crits[i].GetScriptId());
  2379.  
  2380. //caravan guards will not loot
  2381. if(crits[i].GetScriptId() == 0 || !(scriptName == "caravans@_CaravanGuard" || scriptName == "caravans@_CaravanDriver"))
  2382. {
  2383. DPlayerLog(cr, "Critter " + crits[i].Id + " will loot me.");
  2384. AddWalkPlane(crits[i], 0, PLANE_LOOT, cr.Id, cr.HexX, cr.HexY, 6, true, 1);
  2385. break;
  2386. }
  2387. }
  2388. }
  2389. }
  2390. }
  2391.  
  2392. if(valid(killer) && killer.IsPlayer())
  2393. {
  2394. CheckBountyHunters(cr, killer);
  2395.  
  2396. if(cr.Stat[ST_BODY_TYPE] == BT_CHILDREN)
  2397. {
  2398. uint8 current = killer.Karma[KARMA_CHILDKILLER];
  2399. if(current < 200) // Check overflow
  2400. {
  2401. killer.KarmaBase[KARMA_CHILDKILLER] = current + 1;
  2402. }
  2403. }
  2404. }
  2405.  
  2406. SetReplicationTime(cr);
  2407. if(cr.IsNpc())
  2408. cr.DropPlanes(); // Delete all planes
  2409.  
  2410. // additional stuff
  2411. if(cr.IsNpc())
  2412. {
  2413. TownCitizenKilled(cr, killer); // Check if its a town citizen, if so, add npc death penalty
  2414.  
  2415. if(valid(map) && (cr.CrType == CRTYPE_BRAHMIN) && IsBase(map))
  2416. cr.StatBase[ST_REPLICATION_TIME] = REPLICATION_DELETE;
  2417.  
  2418. if(valid(map) && IsCave(map))
  2419. AddCrittersKilled();
  2420.  
  2421. if(valid(map))
  2422. ProcessDeath(map, cr, killer); // reinforcements
  2423. }
  2424. else // IsPlayer
  2425. {
  2426. if(valid(map))
  2427. {
  2428. array<Critter@> followers;
  2429. uint num = GetFollowers(cr, map, followers);
  2430. for(uint i = 0; i < num; i++)
  2431. {
  2432. if(map.GetLocation().IsEncounter())
  2433. {
  2434. if(!Flee(followers[i]))
  2435. {
  2436. followers[i].FollowerVarBase[FV_MODE] = FOLLOWMODE_GUARD;
  2437. _CritUnsetMode(followers[i], MODE_GECK);
  2438. }
  2439. }
  2440. else
  2441. {
  2442. if(followers[i].GetMapId() != 0 && (IsTent(map) || IsBase(map) || !Flee(followers[i])))
  2443. followers[i].FollowerVarBase[FV_MODE] = FOLLOWMODE_GUARD;
  2444. }
  2445. }
  2446.  
  2447. cr.SendMessage(MSG_PLAYER_KILLED, cr.Id, MESSAGE_TO_ALL_ON_MAP);
  2448.  
  2449. if(IsCave(map))
  2450. AddPlayerDeaths();
  2451.  
  2452. // detect spawnkill
  2453. if(valid(killer))
  2454. {
  2455. int locPid = map.GetLocation().GetProtoId();
  2456.  
  2457. if((locPid == LOCATION_Replication1 ||
  2458. locPid == LOCATION_Replication2 ||
  2459. locPid == LOCATION_Replication3 ||
  2460. locPid == LOCATION_Replication4 ||
  2461. locPid == LOCATION_Replication5 ||
  2462. locPid == LOCATION_Replication6 ||
  2463. locPid == LOCATION_Replication7 ||
  2464. locPid == LOCATION_Replication8)
  2465. &&
  2466. (cr.IsPlayer() && cr.GetAccess() == ACCESS_CLIENT &&
  2467. killer.IsPlayer() && killer.GetAccess() == ACCESS_CLIENT)
  2468. )
  2469. {
  2470. Broadcast_Message(
  2471. "Player " + killer.Name + "(" + killer.Id + ") killed " +
  2472. cr.Name + "(" + cr.Id + ") at respawn point (map " + map.Id + ")",
  2473. 0, BROADCAST_FILTER_AUTHENTICATED, true
  2474. );
  2475. }
  2476. }
  2477. }
  2478.  
  2479. #ifdef __DEBUG__
  2480. if(Random(0, 0) == 0)
  2481. #endif
  2482. #ifndef __DEBUG__
  2483. if(Random(0, 2238) == 2238)
  2484. #endif
  2485. {
  2486. CreateTimeEvent(AFTER(REAL_SECOND(Random(4, 8))), "e_DeathSpeech", cr.Id, false);
  2487. }
  2488. }
  2489.  
  2490. if(valid(map) && cr.Mode[MODE_NO_FLATTEN] != 0)
  2491. {
  2492. Item@ blocker = map.AddItem(cr.HexX, cr.HexY, PID_UNVISIBLE_BLOCK, 1);
  2493. if(valid(blocker))
  2494. cr.StatBase[ST_DEAD_BLOCKER_ID] = blocker.Id;
  2495. }
  2496. }
  2497.  
  2498. uint e_DeathSpeech(array<uint>@ values)
  2499. {
  2500. if(!valid(values) || values.length() < 1)
  2501. return(0);
  2502.  
  2503. Critter@ cr = GetCritter(values[0]);
  2504. if(!valid(cr))
  2505. return(0);
  2506.  
  2507. string[] death =
  2508. {
  2509. "4", // The darkness of the afterlife is all that awaits you now. May you find more peace in that world then you found in this one...
  2510. "5", // Not even the carrion eaters are interested in your radiated corpse.
  2511. "6", // Your life ends in the wasteland.
  2512. "dth2", // You have perished.
  2513. "jdd1", // You're dead. Again.
  2514. "jdd3", // Time to reload.
  2515. "jdd4", // Hoped you saved your game, cause you're dead.
  2516. "jdd6" // Boy, are you stupid. And dead.
  2517. "7", // The radiation has taken its toll. Your death was lingering and extremely painful. Your adventure is done.
  2518. };
  2519.  
  2520. PlaySound(cr, "sound\\speech\\narrator\\nar_" + death[Random(0, death.length() - 1)] + ".acm");
  2521.  
  2522. return(0);
  2523. }
  2524.  
  2525. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2526. // Call on something critter reswapned.
  2527. void critter_respawn(Critter& cr)
  2528. {
  2529. if(cr.Stat[ST_DEAD_BLOCKER_ID] != 0)
  2530. {
  2531. Item@ block = ::GetItem(cr.Stat[ST_DEAD_BLOCKER_ID]);
  2532. if(valid(block))
  2533. DeleteItem(block);
  2534. cr.StatBase[ST_DEAD_BLOCKER_ID] = 0;
  2535. }
  2536. }
  2537.  
  2538. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2539. // Called when a critter enters a map.
  2540. void map_critter_in(Map& map, Critter& cr)
  2541. {
  2542. SetWalkRunTimeForNpcs(map, 0);
  2543. RemoveArenaItems(cr);
  2544. Location@ loc = map.GetLocation();
  2545.  
  2546. if(cr.IsPlayer() || _IsFollower(cr))
  2547. {
  2548.  
  2549. if(loc.IsEncounter())
  2550. {
  2551. if(_IsRealPlayer(cr))
  2552. TryStartEncounterMobWave(map);
  2553.  
  2554. if(map.IsTurnBasedAvailability() && map.IsTurnBased() && cr.IsPlayer())
  2555. cr.StatBase[ST_CURRENT_AP] = 0;
  2556. }
  2557. else if(IsTent(map) || IsBase(map))
  2558. {
  2559. cr.ParamBase[TO_IMMUNITY_TIME] = MAX(uint(cr.Param[TO_IMMUNITY_TIME]), ELAPSED_TIME + SLEEPY_BASE_IMMUNITY(cr));
  2560. }
  2561. }
  2562.  
  2563. if(cr.EventExtMapIn(map))
  2564. return;
  2565. if(cr.IsPlayer() && cr.GetAccess() < ACCESS_TESTER)
  2566. map.SetData(MAP_DATA_LAST_ENTERED, ELAPSED_TIME);
  2567.  
  2568. if(cr.IsPlayer() && IsFlaggedAsIllegal(cr))
  2569. cr.SayMsg(SAY_NETMSG, TEXTMSG_TEXT, STR_FLAGGED_ENTERING_MAP);
  2570.  
  2571. if(_IsFollower(cr))
  2572. {
  2573. if(IsTown(map))
  2574. PutAwayItems(cr);
  2575. cr.FollowerVarBase[FV_WM_IDLE] = 0;
  2576. }
  2577.  
  2578. if(GetLvar(cr, LVAR_authed_char) == 0)
  2579. SetSpectator(cr, false);
  2580.  
  2581. if(_MapHasMode(map, MAP_MODE_SPECTATE_ENTER) && (GetLvar(cr, LVAR_authed_char) == 0))
  2582. SetSpectator(cr, true);
  2583.  
  2584. if(_IsTrueNpc(cr) && (_CritHasExtMode(cr, MODE_EXT_GUARD)))
  2585. {
  2586. cr.SetBagRefreshTime(1);
  2587. int bagbug = cr.GetBagRefreshTime(); // this is need right now
  2588. }
  2589. else if(_IsTrueNpc(cr))
  2590. {
  2591. if(map.GetLocation().GetProtoId() == LOCATION_Sierra || map.GetLocation().GetProtoId() == LOCATION_TheGlow)
  2592. cr.SetBagRefreshTime(60);
  2593. else
  2594. cr.SetBagRefreshTime(360);
  2595. int bagbug = cr.GetBagRefreshTime(); // this is need right now
  2596. }
  2597. if(_IsTrueNpc(cr) && IsTown(cr.GetMap()))
  2598.  
  2599. {
  2600. uint bt = cr.Stat[ST_BODY_TYPE];
  2601.  
  2602. if(bt == BT_MEN || bt == BT_WOMEN || bt == BT_CHILDREN || bt == BT_GHOUL || bt == BT_SUPER_MUTANT)
  2603. {
  2604. cr.StatBase[ST_REPLICATION_TIME] = REPLICATION_MINUTE(10);
  2605. }
  2606. }
  2607.  
  2608.  
  2609. // update zone players info
  2610. if(cr.IsPlayer())
  2611. {
  2612. WorldmapUpdatePlayer(cr);
  2613. // tracking stuff - it updates track coords, but it should also
  2614. // set location as known if tracked player still is idling in global
  2615. // at that location
  2616. // TrackLocation(cr);
  2617.  
  2618. // Info about not legit location when entering map by tester+
  2619. if(cr.GetAccess() > ACCESS_CLIENT && _IsNotLegit(map.GetData(MAP_DATA_SPAWNER)))
  2620. {
  2621. cr.Say(SAY_NETMSG, "Warning: This map is not legit (players can use not-legit items here).");
  2622. }
  2623. }
  2624.  
  2625. // If critter is being tracked (by command, not tracking feature) and tracker is online
  2626. GMTrack(cr.Id, "TARGET has entered map" + cr.GetMapId());
  2627.  
  2628. if(GetLvar(cr, LVAR_authed_char) == 1)
  2629. LogAction(cr, GetCritterInfo(cr) + " has entered map " + cr.GetMapId());
  2630.  
  2631. if(cr.IsPlayer())
  2632. {
  2633. GreetPlayer(cr, map);
  2634. XFireClient_Update(cr);
  2635.  
  2636. uint16 locPid = loc.GetProtoId();
  2637.  
  2638. // if(not LOCATION_IS_CITY(locPid)) return;
  2639. // GameVar@ lastCityVar = GetLocalVar(LVAR_last_city, cr.Id);
  2640. // if (lastCityVar is null) return;
  2641. // lastCityVar = locPid;
  2642.  
  2643. if((locPid == LOCATION_Replication1 ||
  2644. locPid == LOCATION_Replication2 ||
  2645. locPid == LOCATION_Replication3 ||
  2646. locPid == LOCATION_Replication4 ||
  2647. locPid == LOCATION_Replication5 ||
  2648. locPid == LOCATION_Replication6 ||
  2649. locPid == LOCATION_Replication7 ||
  2650. locPid == LOCATION_Replication8) &&
  2651. cr.GetAccess() == ACCESS_CLIENT
  2652. )
  2653. {
  2654. array<Item@> items;
  2655. cr.GetItems(SLOT_INV, items);
  2656. Item@ armor = _CritGetItemArmor(cr);
  2657. Item@ hand1 = _CritGetItemHand(cr);
  2658. Item@ hand2 = _CritGetItemHandExt(cr);
  2659. Item@ head = _CritGetItemHead(cr);
  2660.  
  2661. if(valid(armor) || valid(hand1) || valid(hand2) || valid(head) || items.length() > 0)
  2662. {
  2663. Broadcast_Message(
  2664. "Player " + cr.Name + "(" + cr.Id + ") with gear [" +
  2665. (valid(armor) ? "A" : "") +
  2666. (valid(hand1) ? "H1" : "") +
  2667. (valid(hand2) ? "H2" : "") +
  2668. (valid(head) ? "HE" : "") +
  2669. (items.length() > 0 ? "I" : "") +
  2670. "] entered respawn point (map " + map.Id + ")",
  2671. 0, BROADCAST_FILTER_AUTHENTICATED, true
  2672. );
  2673. }
  2674. }
  2675. }
  2676. }
  2677.  
  2678. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2679. // Call on something critter out from map.
  2680. void map_critter_out(Map& map, Critter& cr)
  2681. {
  2682. RemoveArenaItems(cr);
  2683.  
  2684. if(_IsNotLegit(map.GetData(MAP_DATA_SPAWNER)) && (cr.IsNpc() || _IsRealPlayer(cr)))
  2685. {
  2686.  
  2687. Map@ newMap = cr.GetMap();
  2688. if(!valid(newMap) || _IsLegit(newMap.GetData(MAP_DATA_SPAWNER)))
  2689. {
  2690. RemoveNotLegit(cr);
  2691. }
  2692. }
  2693.  
  2694. if(cr.EventExtMapOut(map))
  2695. return;
  2696. if(GetLvar(cr, LVAR_authed_char) == 0)
  2697. SetSpectator(cr, false);
  2698.  
  2699. RemoveWorkbenches(cr); // there
  2700. // If critter is being tracked (by command, not tracking feature) and tracker is online
  2701. GMTrack(cr.Id, "TARGET has entered the worldmap");
  2702.  
  2703. if(GetLvar(cr, LVAR_authed_char) == 1)
  2704. LogAction(cr, GetCritterInfo(cr) + " has entered worldmap");
  2705.  
  2706. // Move the followers
  2707. if(!cr.IsDead())
  2708. {
  2709. array<Critter@> crits;
  2710. int n = GetFollowers(cr, map, crits);
  2711. for(int i = 0; i < n; i++)
  2712. {
  2713. if(!crits[i].IsLife())
  2714. {
  2715. string cond = crits[i].Cond == COND_KNOCKOUT ? "unconscious" : "dead";
  2716. cr.Say(SAY_NETMSG, "You've left your " + cond + " follower " + GetLexem(crits[i], "$name") + " behind.");
  2717. continue;
  2718. }
  2719.  
  2720. if(crits[i].Timeout[TO_BATTLE] > 0)
  2721. {
  2722. cr.Say(SAY_NETMSG, "You've left behind your follower " + GetLexem(crits[i], "$name") + ", who is still fighting.");
  2723. continue;
  2724. }
  2725.  
  2726. if(!(crits[i].FollowerVar[FV_MODE] == FOLLOWMODE_FOLLOW || crits[i].FollowerVar[FV_MODE] == FOLLOWMODE_FOLLOW_CONTROL))
  2727. {
  2728. cr.Say(SAY_NETMSG, "You've left your follower " + GetLexem(crits[i], "$name") + " behind.");
  2729. continue;
  2730. }
  2731.  
  2732. crits[i].ErasePlane(AI_PLANE_WALK, true);
  2733. crits[i].ErasePlane(AI_PLANE_ATTACK, true);
  2734. crits[i].ErasePlane(AI_PLANE_MISC, true);
  2735. if(cr.GetMapId() != 0)
  2736. crits[i].TransitToMap(cr.GetMapId(), cr.HexX, cr.HexY, crits[i].Dir);
  2737. else
  2738. {
  2739. array<Critter@> critsOnGlobal = cr.GetGlobalGroup();
  2740. if(critsOnGlobal.length<10) crits[i].TransitToGlobalGroup(cr.Id);
  2741. }
  2742. }
  2743. }
  2744.  
  2745. if(cr.IsPlayer())
  2746. {
  2747. XFireClient_Update(cr);
  2748. if(cr.GetMapId() == 0)
  2749. {
  2750. ITown@ town = RetrieveTown(cr);
  2751. if(valid(town))
  2752. FlushInfluenceBuffer(cr, town);
  2753.  
  2754. // 33% chance for lost encounter to be recycled (100% in debug)
  2755. //
  2756. Location@ loc = map.GetLocation();
  2757. if(!loc.IsNotValid)
  2758. {
  2759. #ifdef __DEBUG__
  2760. if(IsDisposableEncounter(loc))
  2761. {
  2762. RecycleEncounter(loc);
  2763. }
  2764. #endif
  2765. #ifndef __DEBUG__
  2766. if(IsDisposableEncounter(loc) && Random(0, 2) == 0)
  2767. {
  2768. RecycleEncounter(loc);
  2769. }
  2770. #endif
  2771. }
  2772.  
  2773. array<Critter@>@ players = WorldmapGetPlayers(ZONE_X(cr.WorldX), ZONE_Y(cr.WorldY));
  2774. string characters;
  2775. if(players.length()-1>0)
  2776. {
  2777. characters +="Other characters on worldmap in zone: ";
  2778. for(uint i = 0, j = players.length(); i < j; i++) if(cr.Id!=players[i].Id) characters+=""+(IsLexem(players[i], "$@")?GetLexem(players[i], "$@"):players[i].Name)+",";
  2779. cr.Say(SAY_NETMSG, characters);
  2780. }
  2781.  
  2782. }
  2783. }
  2784. }
  2785.  
  2786. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2787. // Call on something player votes for another.
  2788. // Already checked valid positions and timeout.
  2789. void karma_voting(Critter& crFrom, Critter& crTo, bool valUp)
  2790. {
  2791. // Players karma system (not used)
  2792. // crTo.StatBase[ST_PLAYER_KARMA]+=(valUp?int(5):-10);
  2793. // crFrom.TimeoutBase[TO_KARMA_VOTING]=__FullMinute+__TimeMultiplier*4*60; // 4 hours
  2794.  
  2795. // Good / Evil system
  2796. _SetTimeout(crFrom, TO_KARMA_VOTING, REAL_SECOND(3)); // Some small time to prevent bruteforce
  2797. int crId = int(crTo.Id);
  2798. // Find alredy added
  2799. for(uint i = GOOD_EVIL_LIST_BEGIN; i <= GOOD_EVIL_LIST_END; i++)
  2800. {
  2801. int id = crFrom.GoodEvilList[i];
  2802. if(id != 0)
  2803. {
  2804. bool isEvil = FLAG(id, 0x80000000);
  2805. if(isEvil)
  2806. id ^= 0x80000000;
  2807.  
  2808. if(id == crId)
  2809. {
  2810. if((valUp && !isEvil) || (!valUp && isEvil))
  2811. return; // Already added
  2812. crFrom.GoodEvilListBase[i] = 0; // Erase from list
  2813. return;
  2814. }
  2815. }
  2816. }
  2817. // Add new record
  2818. if(!valUp)
  2819. crId |= 0x80000000;
  2820. for(uint i = GOOD_EVIL_LIST_BEGIN; i <= GOOD_EVIL_LIST_END; i++)
  2821. {
  2822. int id = crFrom.GoodEvilList[i];
  2823. if(id == 0)
  2824. {
  2825. crFrom.GoodEvilListBase[i] = crId;
  2826. return;
  2827. }
  2828. }
  2829. // All places busy, erase first 10
  2830. for(uint i = GOOD_EVIL_LIST_BEGIN; i <= GOOD_EVIL_LIST_END - 10; i++)
  2831. crFrom.GoodEvilListBase[i] = crFrom.GoodEvilListBase[i + 10];
  2832. for(uint i = GOOD_EVIL_LIST_END - 9; i <= GOOD_EVIL_LIST_END; i++)
  2833. crFrom.GoodEvilListBase[i] = 0;
  2834. crFrom.GoodEvilListBase[GOOD_EVIL_LIST_END - 10] = crId;
  2835. }
  2836.  
  2837. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2838. // Call to determine cost of single item.
  2839. // To allow function set __CustomItemCost to true.
  2840. // Don't forgot specify this function in client script.
  2841. uint item_cost(Item& item, Critter& cr, Critter& npc, bool buy)
  2842. {
  2843. return GetItemCost(item, cr, npc, buy);
  2844. }
  2845.  
  2846. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2847. // Call on barter transaction.
  2848. // Return false to cancel transaction.
  2849. bool items_barter(array<Item@>& saleItems, array<uint>& saleItemsCount, array<Item@>& buyItems, array<uint>& buyItemsCount, Critter& player, Critter& npc)
  2850. {
  2851.  
  2852. if(player.GetMapProtoId() == MAP_Arena)
  2853. {
  2854. player.Say(SAY_NETMSG, "You can't trade here.");
  2855. return false;
  2856. }
  2857.  
  2858. if(_IsTrader(npc))
  2859. {
  2860. // reputations
  2861. int profit = 0;
  2862. for(uint i = 0, j = saleItems.length(); i < j; i++)
  2863. {
  2864. profit += GetItemCostPlain(saleItems[i], player, npc) * saleItemsCount[i];
  2865. }
  2866. for(uint i = 0, j = buyItems.length(); i < j; i++)
  2867. {
  2868. profit -= GetItemCostPlain(buyItems[i], player, npc) * buyItemsCount[i];
  2869. }
  2870. ProcessProfitReputation(player, _GroupIndex(npc), profit);
  2871. DPlayerLog(player, "Increased reputation " + _GroupIndex(npc) + " from profit of " + profit);
  2872.  
  2873. // log
  2874. string npcname = GetScriptName(npc.GetScriptId());
  2875. for(uint i = 0, j = saleItems.length(); i < j; i++)
  2876. {
  2877. uint value = GetItemCost(saleItems[i], player, npc, true) * saleItemsCount[i];
  2878. // might aswell hide it under some layer
  2879. dbLog("log_items_barter", "" + player.Id + "|" + npcname + "|" + saleItems[i].GetProtoId() + "|" + saleItemsCount[i] + "|" + value + "|1");
  2880. }
  2881. for(uint i = 0, j = buyItems.length(); i < j; i++)
  2882. {
  2883. uint value = GetItemCost(buyItems[i], player, npc, false) * buyItemsCount[i];
  2884. dbLog("log_items_barter", "" + player.Id + "|" + npcname + "|" + buyItems[i].GetProtoId() + "|" + buyItemsCount[i] + "|" + value + "|0");
  2885. }
  2886. }
  2887. return true;
  2888. }
  2889.  
  2890. // Call on something player craft some items.
  2891. void items_crafted(array<Item@>& items, array<uint>& itemsCount, array<Item@>& resources, Critter& crafter)
  2892. {
  2893. uint8 multiplier = CLAMP(crafter.ParamBase[ST_FIXBOY_COUNTER], 1, 99);
  2894. if(crafter.StatBase[ST_FIXBOY_FIXALL] > 0)
  2895. multiplier = CLAMP(crafter.StatBase[ST_FIXBOY_FIXALL], 1, 100);
  2896.  
  2897. array<uint16> recipePids;
  2898. array<uint> recipeCnt;
  2899.  
  2900. if(multiplier > 1)
  2901. {
  2902. GetItemRecipe(items[0].GetProtoId(), recipePids, recipeCnt);
  2903. }
  2904.  
  2905. for(uint8 m = 0; m < multiplier; ++m)
  2906. {
  2907. if(m > 0)
  2908. {
  2909. for(uint i = 0, j = items.length(); i < j; i++)
  2910. {
  2911. @items[i] = crafter.AddItem(items[i].GetProtoId(), itemsCount[i]);
  2912. }
  2913. for(uint ri = 0, rj = resources.length(); ri < rj; ri++)
  2914. {
  2915. if((resources[ri].GetType()==ITEM_TYPE_WEAPON && !resources[ri].IsStackable()) || resources[ri].GetType()==ITEM_TYPE_ARMOR)
  2916. {
  2917. uint16 oldPid = resources[ri].GetProtoId();
  2918. if(m > 1)
  2919. {
  2920. MoveItem(resources[ri], 1, crafter.GetMap(), 1, 1);
  2921. DeleteItem(resources[ri]);
  2922. }
  2923. Item@ res = crafter.GetItem(oldPid, -1);
  2924. if(valid(res))
  2925. {
  2926. @resources[ri] = res;
  2927. }
  2928. }
  2929. }
  2930. }
  2931.  
  2932. for(uint ii = 0, jj = items.length(); ii < jj; ii++)
  2933. {
  2934. // Unload weapons
  2935. Item@ item = items[ii];
  2936. if(item.GetType() == ITEM_TYPE_WEAPON && item.Proto.Weapon_MaxAmmoCount > 0)
  2937. {
  2938. item.AmmoCount = 0;
  2939. item.Update();
  2940. }
  2941.  
  2942. bool upgradedWeapon=false;
  2943. for(uint ri = 0, rj = resources.length(); ri < rj; ri++)
  2944. {
  2945. if(resources[ri].GetType()==ITEM_TYPE_WEAPON && resources[ri].GetProtoId() != PID_SLEDGEHAMMER)
  2946. {
  2947. upgradedWeapon=true;
  2948. for(uint i=0,j=items.length();i<j;i++)
  2949. {
  2950. items[i].Val0 = resources[i].Val0;
  2951. items[i].Val1 = resources[i].Val1;
  2952. items[i].Val2 = resources[i].Val2;
  2953. items[i].Val3 = resources[i].Val3;
  2954. items[i].Val4 = resources[i].Val4;
  2955. items[i].Val5 = resources[i].Val5;
  2956. items[i].Val6 = resources[i].Val6;
  2957. items[i].Val7 = resources[i].Val7;
  2958. items[i].Val8 = resources[i].Val8;
  2959. items[i].Val9 = resources[i].Val9;
  2960. }
  2961. }
  2962. }
  2963. string@ crafterName=""+crafter.Name;
  2964. if(!upgradedWeapon)
  2965. AddBonuses(item, crafterName);
  2966. else
  2967. if(!item.IsStackable())
  2968. SetLexem(item, "$crafter", crafterName);
  2969.  
  2970. uint score=BonusNumber(item);
  2971. if(item.GetType()==ITEM_TYPE_ARMOR)
  2972. AddScore(crafter, SCORE_ARMORER, score);
  2973. else
  2974. if(item.GetType()==ITEM_TYPE_WEAPON)
  2975. AddScore(crafter, SCORE_GUNSMITH, score);
  2976. }
  2977.  
  2978. for(uint i = 0, j = resources.length(); i < j; i++)
  2979. {
  2980. // Unload weapons
  2981. Item@ item = resources[i];
  2982. if(item.GetType() == ITEM_TYPE_WEAPON && item.Proto.Weapon_MaxAmmoCount > 0)
  2983. {
  2984. if(item.AmmoCount > 0)
  2985. {
  2986. crafter.AddItem(item.AmmoPid, item.AmmoCount);
  2987. item.AmmoCount = 0;
  2988. item.Update();
  2989. }
  2990. }
  2991. }
  2992. ApplyTimeout(items, itemsCount, resources, crafter);
  2993.  
  2994. if(m > 0 && m == multiplier-1)
  2995. {
  2996. for(uint ri = 0, rj = resources.length(); ri < rj; ri++)
  2997. {
  2998. if((resources[ri].GetType()==ITEM_TYPE_WEAPON && !resources[ri].IsStackable()) || resources[ri].GetType()==ITEM_TYPE_ARMOR)
  2999. {
  3000. DeleteItem(resources[ri]);
  3001. }
  3002. }
  3003. }
  3004.  
  3005. if(m > 0)
  3006. {
  3007. for(uint i = 0, j = recipePids.length(); i < j; i++)
  3008. {
  3009. Item@ res = crafter.GetItem(recipePids[i], -1);
  3010. if(valid(res))
  3011. {
  3012. if( !( (res.GetType()==ITEM_TYPE_WEAPON && !res.IsStackable()) || res.GetType()==ITEM_TYPE_ARMOR ) )
  3013. {
  3014. if(res.IsStackable())
  3015. {
  3016. if(res.GetCount() > recipeCnt[i])
  3017. {
  3018. res.SetCount(res.GetCount() - recipeCnt[i]);
  3019. }
  3020. else
  3021. DeleteItem(res);
  3022. }
  3023. else
  3024. DeleteItem(res);
  3025. }
  3026. }
  3027. }
  3028. }
  3029. }
  3030. }
  3031.  
  3032. /**
  3033. * Checks if critter B is out of fov. (used in 'entering sneak mode' check)
  3034. */
  3035. bool FovCheck(Critter& cr, Critter& opponent)
  3036. {
  3037. uint16 cx = cr.HexX;
  3038. uint16 cy = cr.HexY;
  3039. uint16 ox = opponent.HexX;
  3040. uint16 oy = opponent.HexY;
  3041.  
  3042. uint16 dist = GetDistantion(cx, cy, ox, oy);
  3043. uint16 range = __LookNormal + cr.Stat[ST_PERCEPTION] * 3 + _CritGetPerk(cr, PE_SHARPSHOOTER) * 6;
  3044. if(dist > range)
  3045. return false;
  3046.  
  3047. // transform direction from critter A to critter B into "character coord-space"
  3048. uint8 dir = GetDirection(cx, cy, ox, oy);
  3049. dir = (dir > cr.Dir) ? dir - cr.Dir : cr.Dir - dir;
  3050.  
  3051. uint16 nrange = range;
  3052. // adjust distance based on fov
  3053. if(dir == 0) // front
  3054. nrange -= nrange * __LookDir0 / 100;
  3055. else if(dir == 5 || dir == 1) // frontsides
  3056. nrange -= nrange * __LookDir1 / 100;
  3057. else if(dir == 4 || dir == 2) // backsides
  3058. nrange -= nrange * __LookDir2 / 100;
  3059. else // back
  3060. nrange -= nrange * __LookDir3 / 100;
  3061.  
  3062. if(dist > nrange)
  3063. return false;
  3064. else
  3065. return true;
  3066. }
  3067.  
  3068. ////////////////////////////////////////////////////////////////////////////////////////////////////
  3069. // Levelup callback.
  3070. void player_levelup(Critter& player, uint skillIndex, uint skillUp, uint perkIndex)
  3071. {
  3072. if(skillIndex >= SKILL_BEGIN && skillIndex <= SKILL_END)
  3073. {
  3074. for( ; skillUp != 0; skillUp--)
  3075. {
  3076. int skillVal = player.SkillBase[skillIndex];
  3077. if(skillVal >= MAX_SKILL_VAL)
  3078. break;
  3079.  
  3080. bool skillCap=((skillIndex==SK_FIRST_AID || skillIndex==SK_DOCTOR) && skillVal>=200) ||
  3081. (skillIndex==SK_OUTDOORSMAN && skillVal>=175) ||
  3082. ((skillIndex==SK_LOCKPICK || skillIndex==SK_TRAPS || skillIndex==SK_STEAL || skillIndex==SK_BARTER || skillIndex==SK_GAMBLING)&& skillVal>=150) ||
  3083. ((skillIndex==SK_SCIENCE || skillIndex==SK_REPAIR) && skillVal>=125) || skillIndex==SK_MELEE_WEAPONS;
  3084.  
  3085. if(skillCap) break;
  3086.  
  3087. int needPoints = 1;
  3088. if(skillVal > __SkillModAdd6)
  3089. needPoints = 6;
  3090. else if(skillVal > __SkillModAdd5)
  3091. needPoints = 5;
  3092. else if(skillVal > __SkillModAdd4)
  3093. needPoints = 4;
  3094. else if(skillVal > __SkillModAdd3)
  3095. needPoints = 3;
  3096. else if(skillVal > __SkillModAdd2)
  3097. needPoints = 2;
  3098.  
  3099. if(player.StatBase[ST_UNSPENT_SKILL_POINTS] < needPoints)
  3100. break;
  3101.  
  3102. skillVal++;
  3103.  
  3104. skillCap=((skillIndex==SK_FIRST_AID || skillIndex==SK_DOCTOR) && skillVal>=200) ||
  3105. (skillIndex==SK_OUTDOORSMAN && skillVal>=175) ||
  3106. ((skillIndex==SK_LOCKPICK || skillIndex==SK_TRAPS || skillIndex==SK_STEAL || skillIndex==SK_BARTER || skillIndex==SK_GAMBLING)&& skillVal>=150) ||
  3107. ((skillIndex==SK_SCIENCE || skillIndex==SK_REPAIR) && skillVal>=125);
  3108.  
  3109. if(_CritIsTagSkill(player, skillIndex) && skillVal < MAX_SKILL_VAL && !skillCap)
  3110. skillVal++;
  3111. player.SkillBase[skillIndex] = skillVal;
  3112. player.StatBase[ST_UNSPENT_SKILL_POINTS] -= needPoints;
  3113. }
  3114. }
  3115. else if(perkIndex >= PERK_BEGIN && perkIndex <= PERK_END)
  3116. {
  3117. if(PerkCheck(player, perkIndex, false))
  3118. {
  3119. if(player.IsPlayer())
  3120. FLog(LOG_PERKS_PICKED, player.Name+"("+player.Id+") perk: "+perkIndex+" at level "+player.Stat[ST_LEVEL]);
  3121. player.PerkBase[perkIndex]++;
  3122. player.StatBase[ST_UNSPENT_PERKS]--;
  3123. }
  3124. }
  3125.  
  3126. /*
  3127. if(player.IsPlayer()) // JIC
  3128. {
  3129. XFireClient_Update(player);
  3130. }
  3131. */
  3132. }
  3133.  
  3134. ////////////////////////////////////////////////////////////////////////////////////////////////////
  3135. // Turn based callbacks.
  3136. // Called on every round begin, return false to disable turn-based
  3137. void turn_based_begin(Map& map)
  3138. {
  3139. SetWalkRunTimeForNpcs(map, 100);
  3140. array<Critter@> critters;
  3141. uint count = map.GetCritters(0, FIND_LIFE_AND_KO, critters);
  3142.  
  3143. // Try end battle
  3144. if(map.TurnBasedRound > 0)
  3145. {
  3146. array<uint> crittersIds;
  3147. map.GetTurnBasedSequence(crittersIds);
  3148.  
  3149. bool continueBattle = false;
  3150. if(crittersIds.length() >= 2)
  3151. {
  3152. for(uint i = 0, j = crittersIds.length(); i < j; i++)
  3153. {
  3154. Critter@ cr = ::GetCritter(crittersIds[i]);
  3155. if(!(!valid(cr) || cr.IsDead() ||
  3156. (cr.IsNpc() && cr.GetPlanes(AI_PLANE_ATTACK, null) == 0) ||
  3157. (cr.IsPlayer() && (cr.Mode[MODE_END_COMBAT] != 0 || cr.Stat[ST_CURRENT_HP] < 1))))
  3158. {
  3159. continueBattle = true;
  3160. break;
  3161. }
  3162. }
  3163. }
  3164.  
  3165. if(!continueBattle)
  3166. map.EndTurnBased();
  3167. }
  3168. }
  3169.  
  3170. // Call on end turn-based battle
  3171. void turn_based_end(Map& map)
  3172. {
  3173. SetWalkRunTimeForNpcs(map, 0);
  3174. array<Critter@> critters;
  3175. uint count = map.GetCritters(0, FIND_LIFE_AND_KO, critters);
  3176. }
  3177.  
  3178. void SetWalkRunTimeForNpcs(Map& map, uint time)
  3179. {
  3180. Critter@[] critters;
  3181. map.GetCritters(0, FIND_ONLY_NPC | FIND_LIFE_AND_KO, critters);
  3182.  
  3183.  
  3184. for (uint i = 0; i < critters.length(); i++)
  3185. {
  3186. if(valid(critters[i]))
  3187. {
  3188. critters[i].StatBase[ST_WALK_TIME] = time;
  3189. critters[i].StatBase[ST_RUN_TIME] = time;
  3190. }
  3191. }
  3192. }
  3193.  
  3194.  
  3195. // Call on every begin and end turn
  3196. void turn_based_process(Map& map, Critter& cr, bool beginTurn)
  3197. {
  3198. if(beginTurn)
  3199. {
  3200. if(cr.Timeout[TO_BATTLE] == 0)
  3201. cr.TimeoutBase[TO_BATTLE] = 100000000; // setting to initial value (TB_BATTLE_TIMEOUT in fonline.h)
  3202.  
  3203. if(!_IsTrueNpc(cr))
  3204. SetLvar(cr, LVAR_tb_turn_time, ELAPSED_TIME);
  3205.  
  3206.  
  3207. cr.StatBase[ST_MOVE_AP] = cr.Stat[ST_MAX_MOVE_AP];
  3208. cr.StatBase[ST_TURN_BASED_AC] = 0;
  3209. cr.StatBase[ST_TURN_BASED_HEX] = MAKELONG(cr.HexX, cr.HexY);
  3210. if(map.TurnBasedRound > 0)
  3211. PlaySound(cr, "icombat1.acm");
  3212.  
  3213. // Offline and non-real players (staff) skip turns in TB
  3214. if(cr.IsPlayer())
  3215. {
  3216. if(_IsOffline(cr) || !_IsRealPlayer(cr))
  3217. {
  3218. _EndTurn(cr);
  3219. }
  3220. }
  3221. }
  3222. else
  3223. {
  3224. uint dist = GetDistantion(cr.HexX, cr.HexY, LOWORD(cr.Stat[ST_TURN_BASED_HEX]), HIWORD(cr.Stat[ST_TURN_BASED_HEX]));
  3225. cr.StatBase[ST_TURN_BASED_AC] = 2 * (dist + cr.Stat[ST_CURRENT_AP]) >= cr.Stat[ST_ACTION_POINTS] ? 1 : 0;
  3226. if(cr.Stat[ST_TURN_BASED_AC] < 0)
  3227. cr.StatBase[ST_TURN_BASED_AC] = 0;
  3228. cr.StatBase[ST_MOVE_AP] = 0;
  3229. PlaySound(cr, "icombat2.acm");
  3230. }
  3231. }
  3232.  
  3233. // Call when need generate turns sequence
  3234. void turn_based_sequence(Map& map, array<Critter@>& critters, Critter@ firstTurnCrit)
  3235. {
  3236. // Check first turn critter
  3237. if(valid(firstTurnCrit) && (firstTurnCrit.IsDead() || firstTurnCrit.Stat[ST_CURRENT_AP] <= 0))
  3238. @firstTurnCrit = null;
  3239.  
  3240. // Collect critters
  3241. array<SequenceCritter> sequenceCritters;
  3242. for(uint i = 0, j = critters.length(); i < j; i++)
  3243. {
  3244. Critter@ cr = critters[i];
  3245. if(valid(firstTurnCrit) && firstTurnCrit.Id == cr.Id)
  3246. continue;
  3247. if(cr.IsDead())
  3248. continue;
  3249. sequenceCritters.resize(sequenceCritters.length() + 1);
  3250. @sequenceCritters.last().critter = cr;
  3251. }
  3252.  
  3253. // Sort sequence, see SequenceCritter::opCmp below
  3254. SequenceCritterRandom = Random(0, 1);
  3255. sequenceCritters.sortDesc();
  3256.  
  3257. // Fill result
  3258. critters.resize(0);
  3259. if(valid(firstTurnCrit))
  3260. critters.insertLast(firstTurnCrit);
  3261. for(uint i = 0, j = sequenceCritters.length(); i < j; i++)
  3262. critters.insertLast(sequenceCritters[i].critter);
  3263. }
  3264.  
  3265. // Sequence sorter for turn_based_sequence
  3266. int SequenceCritterRandom = 0;
  3267. class SequenceCritter
  3268. {
  3269. Critter@ critter;
  3270. int opCmp(SequenceCritter& in other)
  3271. {
  3272. bool result;
  3273. Critter@ cr1 = critter;
  3274. Critter@ cr2 = other.critter;
  3275. int seq1 = cr1.Stat[ST_SEQUENCE];
  3276. int seq2 = cr2.Stat[ST_SEQUENCE];
  3277. if(seq1 == seq2)
  3278. {
  3279. int ag1 = cr1.Stat[ST_AGILITY];
  3280. int ag2 = cr2.Stat[ST_AGILITY];
  3281. if(ag1 == ag2)
  3282. {
  3283. int lk1 = cr1.Stat[ST_LUCK];
  3284. int lk2 = cr2.Stat[ST_LUCK];
  3285. if(lk1 == lk2)
  3286. {
  3287. if(SequenceCritterRandom == 0)
  3288. result = cr1.Id > cr2.Id;
  3289. else
  3290. result = cr1.Id < cr2.Id;
  3291. }
  3292. else
  3293. result = lk1 > lk2;
  3294. }
  3295. else
  3296. result = ag1 > ag2;
  3297. }
  3298. else
  3299. result = seq1 > seq2;
  3300. return result ? int(1) : int(-1);
  3301. }
  3302. };
  3303.  
  3304. // Call on world saving
  3305. // Range of currentIndex: 1..9999
  3306. void world_save(uint currentIndex, array<uint>& deleteIndexes)
  3307. {
  3308. SaveAlertMaps();
  3309.  
  3310. Log("Saving faction data...");
  3311. for(uint i = 0; i < FACTION_COUNT; i++)
  3312. {
  3313. if(FactionExists(i))
  3314. {
  3315. /*Faction@ f = GetFaction(i);
  3316. if(!valid(f))
  3317. Log("ERROR: FactionType.dll failure, id: " + i);
  3318. else
  3319. f.Save(); // Save params*/
  3320. SaveFactionData(i);
  3321. }
  3322. }
  3323. Log("Faction data saved.");
  3324. Log("Saving TC data...");
  3325. for(uint i = 0; i < TOWN_COUNT; i++)
  3326. {
  3327. for(uint i = 1; i <= TOWN_COUNT; i++)
  3328. {
  3329. ITown@ town = GetTown(i);
  3330. town.SaveData();
  3331. }
  3332. }
  3333. Log("TC data saved.");
  3334. Log("Saving bank data...");
  3335. SaveBankData();
  3336. Log("Bank data saved.");
  3337. Log("Saving brahmin pen data...");
  3338. SaveBrahminPenData();
  3339. Log("Saved brahmin pen data.");
  3340. SaveEventSpawns();
  3341. Log("Saving car price data...");
  3342. SaveCarPriceData();
  3343. Log("Saved car price data.");
  3344.  
  3345. Log("Saving npc schedules");
  3346. SaveNpcSchedules();
  3347. Log("Saved npc schedules");
  3348.  
  3349. Log("Saving companion data...");
  3350. SaveCompanionData();
  3351. Log("Saved companion data");
  3352.  
  3353. // Keep only current and four last saves
  3354. if(currentIndex == 1)
  3355. {
  3356. deleteIndexes.resize(5);
  3357. for(uint i = 0; i < 5; i++)
  3358. deleteIndexes[i] = 9999 - i;
  3359. }
  3360. else if(currentIndex > 4 && ((currentIndex % 24) != 0))
  3361. {
  3362. deleteIndexes.resize(1);
  3363. deleteIndexes[0] = currentIndex - 5;
  3364. }
  3365. Log("Saving weather data...");
  3366. SaveWeather();
  3367. Log("Saved weather data.");
  3368.  
  3369. FLog(LOG_WMLOCATIONS, "SAVE");
  3370. // SaveCheats();
  3371. }
  3372.  
  3373. // Call on player try register
  3374. // Return true to allow, false to disallow
  3375. bool player_registration(uint ip, string& name, uint& textMsg, uint& strNum)
  3376. {
  3377. // check for staff members
  3378. LoadConfig("config/GetAccess.cfg");
  3379.  
  3380. string@ reserved = GetConfigValue("config/GetAccess.cfg", name, "Reserved", false);
  3381.  
  3382. if(!valid(reserved)) // dedicated to RJ
  3383. {
  3384. IConfigFile@ config = GetConfigFile("config/GetAccess.cfg");
  3385. if(valid(config))
  3386. {
  3387. array<string> sections;
  3388. config.GetSections(sections);
  3389. if(sections.length() > 0)
  3390. {
  3391. for(uint s = 0; s < sections.length(); s++)
  3392. {
  3393. string@ alias = GetConfigValue("config/GetAccess.cfg", sections[s], "Nickname", false);
  3394. if(valid(alias) && strlwr(name) == strlwr(alias))
  3395. {
  3396. @reserved = GetConfigValue("config/GetAccess.cfg", sections[s], "Reserved", false);
  3397. break;
  3398. }
  3399. }
  3400. }
  3401. }
  3402. }
  3403.  
  3404. if(valid(reserved))
  3405. {
  3406. if(reserved == "true")
  3407. {
  3408. // kick client
  3409. textMsg = TEXTMSG_GAME;
  3410. strNum = 1049;
  3411. return(false);
  3412. }
  3413. else
  3414. {
  3415. // allow client, reserve again
  3416. IConfigSection@ section = GetConfigSection("config/GetAccess.cfg", name, false);
  3417. SetConfigValue("config/GetAccess.cfg", section.GetName(), "Reserved", "true");
  3418. SaveConfig("config/GetAccess.cfg");
  3419. return(true);
  3420. }
  3421. }
  3422. else
  3423. {
  3424. // check player-reserved names
  3425. LoadConfig("config/ReservedNicknames.cfg");
  3426.  
  3427. @reserved = GetConfigValue("config/ReservedNicknames.cfg", "Reserved", name, false);
  3428.  
  3429. if(valid(reserved) && reserved == "1")
  3430. {
  3431. // kick client
  3432. textMsg = TEXTMSG_GAME;
  3433. strNum = 1050;
  3434. return(false);
  3435. }
  3436. }
  3437.  
  3438. return(true);
  3439. }
  3440.  
  3441. // Call on player try login
  3442. // Return true to allow, false to disallow
  3443. bool player_login(uint ip, string& name, uint id, uint& textMsg, uint& strNum)
  3444. {
  3445. Critter@ player = GetCritter(id);
  3446. if(valid(player))
  3447. {
  3448. // Player is online and the IP is the same, try disconnect the player to solve "character already in game"
  3449. if(_IsOnline(player) && player.GetIp() == ip)
  3450. {
  3451. player.Say(SAY_NETMSG, "Another connection from the same IP incoming, logging off.");
  3452. player.Disconnect();
  3453. textMsg = TEXTMSG_TEXT;
  3454. strNum = 400;
  3455. return false;
  3456. }
  3457. else
  3458. {
  3459. UpdateFactionsInfo(player);
  3460. }
  3461.  
  3462. if(_IsOffline(player))
  3463. CreateTimeEvent(AFTER(REAL_SECOND(5)), "e_CritterInit_Broadcast", player.Id, false);
  3464. }
  3465. uint16 year=0, month=0, day=0, dayofweek=0, hour=0, minute=0, second=0;
  3466. GetGameTime( __FullSecond, year, month, day, dayofweek, hour, minute, second );
  3467. FLog(LOG_LOGIN, GetTimeString(__FullSecond) + " " + name + " " + IntToIp(ip));
  3468. return true;
  3469. }
  3470.  
  3471. uint8 access_level(string& access)
  3472. {
  3473. if(strlwr(access) == "admin")
  3474. return(ACCESS_ADMIN);
  3475. else if(strlwr(access) == "moder")
  3476. return(ACCESS_MODER);
  3477. else if(strlwr(access) == "tester")
  3478. return(ACCESS_TESTER);
  3479. else
  3480. return(ACCESS_CLIENT);
  3481. }
  3482.  
  3483. string access_level(uint8& access)
  3484. {
  3485. switch(access)
  3486. {
  3487. case ACCESS_ADMIN:
  3488. return("admin");
  3489. case ACCESS_MODER:
  3490. return("moder");
  3491. case ACCESS_TESTER:
  3492. return("tester");
  3493. default:
  3494. return("client");
  3495. }
  3496. return("client");
  3497. }
  3498.  
  3499. // Call on player try change access
  3500. // Return true to allow, false to disallow
  3501. bool player_getaccess(Critter& player, int access, string& password)
  3502. {
  3503. FLog(LOG_GETACCESS, "Access changed for player " + GetCritterInfo(player) + ", from " + GetAuthString(player.GetAccess()) + " to " + GetAuthString(access) + ".");
  3504. SetLvar(player, LVAR_authed_char, 1);
  3505. player.RunClientScript("client_messages@_Listen", 0, 0, 0, null, null);
  3506. SetCritterEvents(player);
  3507. Broadcast_CheckRequestHelpBuffer(player);
  3508. return true;
  3509. }
  3510.  
  3511. // Call on player trying to use a command
  3512. // Return true to allow, false to disallow
  3513. bool player_allowcommand(Critter@ cr, string@ adminPanel, uint8 command)
  3514. {
  3515. if(valid(adminPanel))
  3516. return true;
  3517.  
  3518. switch(command)
  3519. {
  3520. /* disabled commands
  3521. when adding another command, don't forget to
  3522. add it to client-side check (client_messages@out_message)
  3523. */
  3524. case COMMAND_ADDITEM:
  3525. case COMMAND_ADDITEM_SELF:
  3526. case COMMAND_ADDLOCATION:
  3527. case COMMAND_ADDNPC:
  3528. case COMMAND_CRASH:
  3529. case COMMAND_PARAM:
  3530. Log("WARN: " + cr.Name + "(" + cr.Id + ") tried to use disabled command (" + command + ")");
  3531. break;
  3532.  
  3533. /* overwritten commands
  3534. hardcoded commands moved to scripts, client should never send them
  3535. */
  3536. case COMMAND_MYINFO:
  3537. Log("WARN: " + cr.Name + "(" + cr.Id + ") tried to use overwritten command (" + command + ")");
  3538. break;
  3539.  
  3540. // ACCESS_CLIENT
  3541. case COMMAND_CHANGE_PASSWORD:
  3542. case COMMAND_DELETE_ACCOUNT:
  3543. if(cr.GetAccess() == ACCESS_CLIENT && cr.Param[PE_MENTAL_BLOCK] == 42)
  3544. {
  3545. cr.Say(SAY_NETMSG, "You are not allowed to " + (command == COMMAND_CHANGE_PASSWORD ? "change password of" : "delete") + " this character.");
  3546. return(false);
  3547. }
  3548. if(command == COMMAND_DELETE_ACCOUNT && cr.Stat[ST_LEVEL] > 1)
  3549. {
  3550. /* TODO:
  3551. add: EXPORT bool PlayerToDelete( Critter* cr ) return( cr && cr->IsPlayer() && cr->ClientToDelete );
  3552. */
  3553. if(valid(cr.GetMap()) && (IsBase(cr.GetMap()) || IsTent(cr.GetMap())))
  3554. {
  3555. if(_CritCountItem(cr, PID_BOTTLE_CAPS)>=10000 && cr.Stat[ST_LEVEL]>1)
  3556. cr.Say(SAY_NETMSG, "|0xBBBBBB INFO: You will recive Achievement Book after your character dissapears from game.");
  3557. else
  3558. cr.Say(SAY_NETMSG, "|0xCC0000 WARNING: You are trying to delete character without 10.000 caps in inventory. You will not get Achievement Book to perform reroll! You can use ~deleteself command again to cancel.");
  3559. }
  3560. else
  3561. cr.Say(SAY_NETMSG, "|0xCC0000 WARNING: You are trying to delete character outside of tent/base. You will not get Achievement Book to perform reroll! You can use ~deleteself command again to cancel.");
  3562. }
  3563. return(true);
  3564.  
  3565. case COMMAND_EXIT:
  3566. case COMMAND_GETACCESS:
  3567. return(true);
  3568.  
  3569. // ACCESS_TESTER
  3570. case COMMAND_DROP_UID:
  3571. case COMMAND_GAMEINFO:
  3572. case COMMAND_TOGLOBAL:
  3573. if(cr.GetAccess() >= ACCESS_TESTER)
  3574. return(true);
  3575. break;
  3576.  
  3577. // ACCESS_MODER
  3578. case COMMAND_BAN:
  3579. case COMMAND_CHECKVAR:
  3580. case COMMAND_CRITID:
  3581. case COMMAND_DISCONCRIT:
  3582. case COMMAND_KILLCRIT:
  3583. case COMMAND_LOG:
  3584. case COMMAND_MOVECRIT:
  3585. case COMMAND_RESPAWN:
  3586. case COMMAND_SETVAR:
  3587. if(cr.GetAccess() >= ACCESS_MODER)
  3588. return(true);
  3589. break;
  3590.  
  3591. // ACCESS_ADMIN
  3592. case COMMAND_LOADDIALOG:
  3593. case COMMAND_LOADLOCATION:
  3594. case COMMAND_LOADMAP:
  3595. case COMMAND_LOADSCRIPT:
  3596. case COMMAND_RELOAD_CLIENT_SCRIPTS:
  3597. case COMMAND_RELOADAI:
  3598. case COMMAND_RELOADDIALOGS:
  3599. case COMMAND_RELOADLOCATIONS:
  3600. case COMMAND_RELOADMAPS:
  3601. case COMMAND_RELOADSCRIPTS:
  3602. case COMMAND_RELOADTEXTS:
  3603. case COMMAND_REGENMAP:
  3604. case COMMAND_RUNSCRIPT:
  3605. case COMMAND_SETTIME:
  3606. if(cr.GetAccess() >= ACCESS_ADMIN)
  3607. return(true);
  3608. break;
  3609.  
  3610. default:
  3611. Log("WARN: " + cr.Name + "(" + cr.Id + ") tried to use unknown command (" + command + ")");
  3612. break;
  3613. }
  3614.  
  3615. cr.Say(SAY_NETMSG, "Unknown command.");
  3616. return(false);
  3617. }
  3618.  
  3619. void server_log(string& message)
  3620. {
  3621. // placeholder
  3622. };
  3623.  
  3624. void CheckScripts(Critter& cr, int p0, int p1, int p2)
  3625. {
  3626. array<Item@> items;
  3627. uint num = cr.GetMap().GetItems(0, items);
  3628. for(uint i = 0; i < num; i++)
  3629. {
  3630. uint16 hx, hy;
  3631. hx = hy = 0;
  3632. Map@ map = items[i].GetMapPosition(hx, hy);
  3633. uint id = items[i].GetScriptId();
  3634. map.SetText(hx, hy, 0, "" + id + " : " + GetScriptName(id));
  3635. }
  3636. array<Critter@> crs;
  3637. num = cr.GetMap().GetCritters(0, FIND_ALL | FIND_ONLY_NPC, crs);
  3638.  
  3639. for(uint i = 0; i < num; i++)
  3640. {
  3641. uint id = cr.GetScriptId();
  3642. crs[i].Say(SAY_NORM, "" + id + " : " + GetScriptName(id));
  3643. }
  3644. }
  3645.  
  3646. void CheckPids(Critter& cr, int p0, int p1, int p2)
  3647. {
  3648. array<Critter@> critters;
  3649. uint num = cr.GetMap().GetCritters(0, FIND_ALL | FIND_ONLY_NPC, critters);
  3650. for(uint i = 0; i < num; i++)
  3651. critters[i].Say(SAY_NORM, "PID: " + critters[i].GetProtoId());
  3652. }
  3653.  
  3654. void CheckItems(Critter& cr, int p0, int p1, int p2)
  3655. {
  3656. Item@ it = cr.AddItem(1, 1);
  3657. cr.Say(SAY_NETMSG, "" + it.Id);
  3658. }
  3659.  
  3660. void reprohang(Critter& cr, int p0, int p1, int p2)
  3661. {
  3662. cr.ParamBase[305] = 1 << 32;
  3663. cr.Say(SAY_NETMSG, "Done");
  3664. cr.Say(SAY_NETMSG, "Param's value: " + cr.ParamBase[305]);
  3665. }
  3666.  
  3667. void spawnjunk(Critter& cr, int, int, int)
  3668. {
  3669. Item@ junk = cr.GetMap().AddItem(cr.HexX, cr.HexY, PID_PUMP_PARTS, 1);
  3670. junk.SetScript("prod_barrel_junk@_Junk");
  3671. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement