Advertisement
Slowhand-VI

perk_ms.fos

Dec 31st, 2015
264
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  *  Perk: Mysterious Stranger   -   Main code
  3.  *
  4.  *  Description: Check perk_ms_h.fos
  5.  *  Author:     Slowhand
  6.  *  Reviewer:   n/a
  7.  */
  8.  
  9. #include "perk_ms_h.fos"
  10.  
  11. #include "_defines.fos"
  12. #include "_basetypes.fos"
  13. #include "_scripts.fos"
  14. #include "_macros.fos"
  15. #include "_dialogs.fos"
  16. #include "_ai.fos"
  17.  
  18. #include "npc_common.fos"
  19.  
  20.  
  21. /**<
  22.  * Cooldown / Perk availability checks
  23.  */
  24.  
  25. bool isMsReadyToSpawn(uint playerId)
  26. {
  27.     if (isMsSpawned(playerId)) {
  28.         MS_LOG("Mysterious Stranger is already spawned.");
  29.         return false;
  30.     }
  31.     if (!isMsNormalSpawnAvailable(playerId)) {
  32.         MS_LOG("Mysterious Stranger is busy.");
  33.         return false;
  34.     }
  35.     if (isMsDead(playerId)) {
  36.         MS_LOG("Mysterious Stranger is dead.");
  37.         return false;
  38.     }
  39.     return true;
  40. }
  41.  
  42. /**< Check this function to control which maps shall MS appear. By default maps with Town Control should be disabled. */
  43. bool isMsAvailableOnMap(uint playerId)
  44. {
  45.     Critter@ player = GetCritter(playerId);
  46.     if (valid(player))
  47.     {
  48.         Map@ map = player.GetMap();
  49.         string mapName = "";
  50.         map.ProtoName(mapName);
  51.         MS_LOG("Map proto name = " + mapName + ", ID = " + map.GetProtoId());
  52.         //  Adding exceptions where the MS may not spawn.
  53.         for (uint i = 0; i < disabledMsMaps.length(); i++)
  54.         {
  55.             if (mapName == disabledMsMaps[i])
  56.             {
  57.                 MS_LOG("MS is not allowed on this map.");
  58.                 return false;
  59.             }
  60.         }
  61.     }
  62.     return true;
  63. }
  64.  
  65. bool doesPlayerHaveMsPerk(uint playerId)
  66. {
  67.     Critter@ player = GetCritter(playerId);
  68.     if (!valid(player))
  69.         return false;
  70.     if (player.Perk[PE_MYSTERIOUS_STRANGER] > 0)
  71.         return true;
  72.     else
  73.         return false;
  74. }
  75.  
  76. bool shallMsSpawnOnEncounter(uint playerId)
  77. {
  78.     if (isMsReadyToSpawn(playerId)
  79.         && isMsAvailableOnMap(playerId))
  80.     {
  81.         Critter@ player = GetCritter(playerId);
  82.         int chance = MS_ENC_BASE_CHANCE;
  83.         chance += player.Stat[ST_CHARISMA] * MS_ENC_CHA_CHANCE_FACTOR;
  84.         chance += player.Stat[ST_LUCK] * MS_ENC_LK_CHANCE_FACTOR;
  85.         chance = CLAMP(chance, MS_ENC_MIN_CHANCE, MS_ENC_MAX_CHANCE);
  86.         int roll = Random(0, 100);
  87.         MS_LOG("Shall MS spawn for Encounter? (Chance >= Roll) : (" + chance + ", " + roll + ")");
  88.         if (chance >= roll)
  89.             return true;
  90.         else
  91.             return false;
  92.     }
  93.     else
  94.     {
  95.         MS_LOG("MS cannot be spawned for Encounter.");
  96.         return false;
  97.     }
  98. }
  99.  
  100. /**< If this check is called, it will disable positive checks and it will start a timer which will re-enable positives checks. */
  101. bool shallMsSpawnForFirstAid(uint playerId)
  102. {
  103.     if (isMsReadyToSpawn(playerId)
  104.         && isMsFirstAidAvailable(playerId)
  105.         && isMsMedicalSpawnAvailable(playerId)
  106.         && isMsAvailableOnMap(playerId))
  107.     {
  108.         Critter@ player = GetCritter(playerId);
  109.         int chance = MS_FA_BASE_CHANCE;
  110.         chance += player.Stat[ST_CHARISMA] * player.Stat[ST_LUCK] * MS_FA_CHANCE_FACTOR;
  111.         chance = CLAMP(chance, MS_FA_MIN_CHANCE, MS_FA_MAX_CHANCE);
  112.         int roll = Random(0, 100);
  113.         MS_LOG("Shall MS spawn for First Aid? (Chance >= Roll) : (" + chance + ", " + roll + ")");
  114.         if (chance >= roll)
  115.             return true;
  116.         else
  117.             return false;
  118.     }
  119.     else
  120.     {
  121.         MS_LOG("MS cannot be spawned for First Aid call.");
  122.         return false;
  123.     }
  124. }
  125.  
  126. bool shallMsSpawnForDoctor(uint playerId)
  127. {
  128.     if (isMsReadyToSpawn(playerId)
  129.         && isMsDoctorAvailable(playerId)
  130.         && isMsMedicalSpawnAvailable(playerId)
  131.         && isMsAvailableOnMap(playerId))
  132.     {
  133.         Critter@ player = GetCritter(playerId);
  134.         int chance = MS_DOC_BASE_CHANCE;
  135.         chance += player.Stat[ST_CHARISMA] * player.Stat[ST_LUCK] * MS_DOC_CHANCE_FACTOR;
  136.         chance = CLAMP(chance, MS_DOC_MIN_CHANCE, MS_DOC_MAX_CHANCE);
  137.         int roll = Random(0, 100);
  138.         MS_LOG("Shall MS spawn for Doctor? (Chance >= Roll) : (" + chance + ", " + roll + ")");
  139.         if (chance >= roll)
  140.             return true;
  141.         else
  142.             return false;
  143.     }
  144.     else
  145.     {
  146.         MS_LOG("MS cannot be spawned for Doctor call.");
  147.         return false;
  148.     }
  149. }
  150.  
  151. /**<
  152.  * If this check is called, it will disable positive checks on medical call
  153.  * and it will start a timer which will re-enable positives checks.
  154.  * Shall be called on both call success and failure.
  155.  */
  156. void blockMsMedicalSpawnRepeatedChecks(uint playerId)
  157. {
  158.     MS_LOG("Blocking medical spawn availability against call spam.");
  159.     setMsMedicalSpawnAvailability(playerId, false);
  160.     startMsMedicalSpawnTimer(playerId, getMsMedicalSpawnBlockTimerDuration(playerId));
  161. }
  162. /**<  */
  163.  
  164. /**<
  165.  * Init Mysterious Stranger game variables when perk is given the first time.
  166.  */
  167.  
  168. void givePlayerMsPerkInit(uint playerId)
  169. {
  170.     MS_LOG("Give MS perk for player - Init game varialbes.");
  171.     initMsPerk(playerId);
  172.     return;
  173. }
  174.  
  175. void initMsPerk(uint playerId)
  176. {
  177.     setMsDead(playerId, false);
  178.     setMsNormalSpawnAvailability(playerId, true);
  179.     //  These 2 variables are a check for MS not to be able to FA/Doctor more than once in a session.
  180.     setMsDoctorAvailability(playerId, true);
  181.     setMsFirstAidAvailability(playerId, true);
  182.     //  This timer is a protection timer for MS not to trigger a spawn chance whenever crippled/low hp and attacked each time in a short duration.
  183.     setMsMedicalSpawnAvailability(playerId, true);
  184. }
  185. /**<  */
  186.  
  187. /**<
  188.  *  Spawn entry points.
  189.  *  Only use these spawning functions according to purpose, because of timers/variables and other logic is linked to these.
  190.  */
  191.  
  192. void spawnMysteriousStrangerForEncounter(uint playerId)
  193. {
  194.     if (shallMsSpawnOnEncounter(playerId))
  195.     {
  196.         MS_LOG("MS is being spawned for Encounter.");
  197.         spawnMysteriousStranger(playerId);
  198.         startMsNormalSpawnTimer(playerId, getMsNormalSpawnTimerDuration(playerId));
  199.         startMsHelpDurationTimer(playerId, getMsHelpDuration(playerId));
  200.     }
  201.     else
  202.     {
  203.         MS_LOG("MS was not ready to be spawned for encounter.");
  204.     }
  205. }
  206.  
  207. void spawnMysteriousStrangerForFirstAid(uint playerId)
  208. {
  209.     if (shallMsSpawnForFirstAid(playerId))
  210.     {
  211.         MS_LOG("MS is being spawned for First Aid.");
  212.         spawnMysteriousStranger(playerId);
  213.         startMsNormalSpawnTimer(playerId, getMsNormalSpawnTimerDuration(playerId));
  214.         startMsHelpDurationTimer(playerId, getMsHelpDuration(playerId));
  215.         Critter@ ms = GetCritter(getMsId(playerId));
  216.         Critter@ player = GetCritter(playerId);
  217.         if (valid(ms) && valid(player))
  218.         {
  219.             ms.Say(SAY_NORM_ON_HEAD, "Dude! You are bleeding!");
  220.             AddHealCritterPlane(ms, 90, player, true);
  221.             setMsFirstAidAvailability(playerId, false);
  222.         }
  223.         else
  224.         {
  225.             MS_LOG("MS did not spawn for some reason for First Aid call, even though all checks were successful.");
  226.         }
  227.     }
  228.     else
  229.     {
  230.         MS_LOG("MS was not ready to be spawned for First Aid.");
  231.     }
  232.     if (isMsMedicalSpawnAvailable(playerId))
  233.     {
  234.         blockMsMedicalSpawnRepeatedChecks(playerId);
  235.     }
  236. }
  237.  
  238. void spawnMysteriousStrangerForDoctor(uint playerId)
  239. {
  240.     if (shallMsSpawnForDoctor(playerId))
  241.     {
  242.         MS_LOG("MS is being spawned for Doctor.");
  243.         spawnMysteriousStranger(playerId);
  244.         startMsNormalSpawnTimer(playerId, getMsNormalSpawnTimerDuration(playerId));
  245.         startMsHelpDurationTimer(playerId, getMsHelpDuration(playerId));
  246.         Critter@ ms = GetCritter(getMsId(playerId));
  247.         Critter@ player = GetCritter(playerId);
  248.         if (valid(ms) && valid(player))
  249.         {
  250.             ms.Say(SAY_NORM_ON_HEAD, "It's first day at the clinic all over again.");
  251.             AddDoctorCritterPlane(ms, 90, player, true);
  252.             setMsDoctorAvailability(playerId, false);
  253.         }
  254.         else
  255.         {
  256.             MS_LOG("MS did not spawn for some reason for Doctor call, even though all checks were successful.");
  257.         }
  258.     }
  259.     else
  260.     {
  261.         MS_LOG("MS was not ready to be spawned for Doctor.");
  262.     }
  263.     if (isMsMedicalSpawnAvailable(playerId))
  264.     {
  265.         blockMsMedicalSpawnRepeatedChecks(playerId);
  266.     }
  267. }
  268.  
  269.  
  270. /**<
  271.  * Inner spawning and despawning function, do not call these directly, except for tests.
  272.  */
  273.  
  274. /**<
  275.  * Since MS does not support multiple spawning per player, some timer logic and CD check remains here,
  276.  * also these are shared between all the possible spawns, maybe those be regular or special.
  277.  */
  278. bool spawnMysteriousStranger(uint playerId)
  279. {
  280.     Critter@ player = GetCritter(playerId);
  281.     MS_LOG("Mysterious Stranger is ready to be spawned.");
  282.     uint mobX = player.HexX + Random(-2, 2);
  283.     uint mobY = player.HexY + Random(-2, 2);
  284.     int[] params =
  285.     {
  286.         ST_TEAM_ID, 0,
  287.         ST_NPC_ROLE, 0,
  288.         ST_DIALOG_ID, DIALOG_perk_ms_description,
  289.         ST_AI_ID, MS_AI_PACK_USED
  290.     };
  291.     Map@ map = player.GetMap();
  292.     uint16 msPid = getMsModelPid(playerId);
  293.     Critter@ ms = map.AddNpc(msPid, mobX, mobY, 1, params, null, "perk_ms@ms_init");
  294.     if (valid(ms))
  295.     {
  296.         setMsSpawned(playerId, true);
  297.         setMsId(playerId, ms.Id);
  298.         ms.SpawnedBy = playerId;
  299.         personalizeMs(ms);
  300.         ms.Say(SAY_NORM, "Have no fear, The Stranger is here!");
  301.     }
  302.     return true;
  303. }
  304.  
  305. bool despawnMysteriousStranger(uint playerId)
  306. {
  307.     uint msId = getMsId(playerId);
  308.     Critter@ ms = GetCritter(msId);
  309.     if (valid(ms))
  310.     {
  311.         ms.TransitToGlobal(false);
  312.         DeleteNpc(ms);
  313.         MS_LOG("Transiting to Global and recycling.");
  314.     }
  315.     else
  316.     {
  317.         MS_LOG("MS was already recycled!");
  318.     }
  319.     setMsSpawned(playerId, false);
  320.     return true;
  321. }
  322. /**<  */
  323.  
  324. /**< Aux function */
  325.  
  326. uint16 getMsModelPid(uint playerId)
  327. {
  328.     Critter@ player = GetCritter(playerId);
  329.     if (player.Stat[ST_CHARISMA] < 4)
  330.     {
  331.         return MS_PID_LEVEL1;
  332.     }
  333.     if (player.Stat[ST_CHARISMA] < 7)
  334.     {
  335.         return MS_PID_LEVEL2;
  336.     }
  337.     int[] reputations = {FACTION_BOS, FACTION_ENCLAVE, FACTION_NCR, FACTION_GUNRUNNERS};
  338.     int repIndex = findHighestReputationIndexIn(playerId, reputations);
  339.     switch (repIndex)
  340.     {
  341.         case FACTION_BOS:
  342.             return MS_PID_LEVEL3_BOS;
  343.         case FACTION_ENCLAVE:
  344.             return MS_PID_LEVEL3_ENC;
  345.         case FACTION_NCR:
  346.             return MS_PID_LEVEL3_NRC;
  347.         case FACTION_GUNRUNNERS:
  348.             return MS_PID_LEVEL3_GR;
  349.         default:
  350.             return MS_PID_LEVEL3_GR;
  351.     }
  352.     return MS_PID_LEVEL3_GR;
  353. }
  354.  
  355. int findHighestReputationIndexIn(uint playerId, array<int>& params)
  356. {
  357.     Critter@ player = GetCritter(playerId);
  358.     if (!valid(player))
  359.         return 0;
  360.     if (params.length() < 1)
  361.         return 0;
  362.     int highestRepIndex = 0;
  363.     for (uint i = 1; i < params.length(); i++)
  364.     {
  365.         if (player.Reputation[i] > player.Reputation[highestRepIndex])
  366.         {
  367.             highestRepIndex = i;
  368.         }
  369.     }
  370.     return highestRepIndex;
  371. }
  372.  
  373. void personalizeMs(Critter& ms)
  374. {
  375.     Critter@ player = GetCritter(ms.SpawnedBy);
  376.     if (player.Stat[ST_CHARISMA] < 4)
  377.     {
  378.         setBaseStatsForMsLevel(ms, 0);
  379.         setExtraStatsForMsLevel(ms, 0);
  380.         setInventoryForMsLevel1(ms);
  381.         equipMsWeapon(ms, PID_COMBAT_SHOTGUN);
  382.     }
  383.     else if (player.Stat[ST_CHARISMA] < 7)
  384.     {
  385.         setBaseStatsForMsLevel(ms, 1);
  386.         setExtraStatsForMsLevel(ms, 1);
  387.         setInventoryForMsLevel2(ms);
  388.     }
  389.     else
  390.     {
  391.         setBaseStatsForMsLevel(ms, 2);
  392.         setExtraStatsForMsLevel(ms, 2);
  393.         setInventoryForMsLevel3(ms);
  394.     }
  395. }
  396.  
  397. void setBaseStatsForMsLevel(Critter& ms, int bonus)
  398. {
  399.     //  Stats
  400.     ms.StatBase[ST_STRENGTH] = 6 + bonus;
  401.     ms.StatBase[ST_PERCEPTION] = 6 + bonus;
  402.     ms.StatBase[ST_ENDURANCE] = 6 + bonus;
  403.     ms.StatBase[ST_CHARISMA] = 4 + bonus;
  404.     ms.StatBase[ST_INTELLECT] = 6 + bonus;
  405.     ms.StatBase[ST_AGILITY] = 8 + bonus;
  406.     ms.StatBase[ST_LUCK] = 3 + 2 * bonus;
  407.     ms.StatBase[ST_ACTION_POINTS] = 9 + bonus;
  408.     ms.StatBase[ST_CURRENT_AP] = ms.StatBase[ST_ACTION_POINTS];
  409.     //  Skills
  410.     ms.SkillBase[SK_SMALL_GUNS] = 100;
  411.     ms.SkillBase[SK_BIG_GUNS] = 100;
  412.     ms.SkillBase[SK_ENERGY_WEAPONS] = 100;
  413.     ms.SkillBase[SK_DOCTOR] = 50;
  414.     ms.SkillBase[SK_FIRST_AID] = 75;
  415.     //  Traits and perks
  416.     ms.TraitBase[TRAIT_FAST_SHOT] = 1;
  417.     Critter@ player = GetCritter(ms.SpawnedBy);
  418.     if (valid(player) && Random(0, 10) >= player.Stat[ST_LUCK] * 3)
  419.     {
  420.         ms.TraitBase[TRAIT_JINXED] = 1;
  421.     }
  422.     else
  423.     {
  424.         ms.TraitBase[TRAIT_BONEHEAD] = 1;
  425.     }
  426.     //  Absorbs
  427.     ms.StatBase[ST_NORMAL_ABSORB] = 5 + 5 * bonus;
  428.     ms.StatBase[ST_FIRE_ABSORB] = 5 + 5 * bonus;
  429.     ms.StatBase[ST_LASER_ABSORB] = 10 + 5 * bonus;
  430.     ms.StatBase[ST_PLASMA_ABSORB] = 5 + 5 * bonus;
  431.     ms.StatBase[ST_EXPLODE_ABSORB] = 10 + 5 * bonus;
  432.     ms.StatBase[ST_ELECTRO_ABSORB] = 5 + 5 * bonus;
  433.     //  Resists
  434.     ms.StatBase[ST_NORMAL_RESIST] = 5 + 25 * bonus;
  435.     ms.StatBase[ST_FIRE_RESIST] = 15 + 25 * bonus;
  436.     ms.StatBase[ST_LASER_RESIST] = 35 + 25 * bonus;
  437.     ms.StatBase[ST_PLASMA_RESIST] = 5 + 25 * bonus;
  438.     ms.StatBase[ST_EXPLODE_RESIST] = 15 + 25 * bonus;
  439.     ms.StatBase[ST_ELECTRO_RESIST] = 5 + 25 * bonus;
  440. }
  441.  
  442. void setExtraStatsForMsLevel(Critter& ms, int bonus)
  443. {
  444.     Critter@ player = GetCritter(ms.SpawnedBy);
  445.     int level = CLAMP(player.Stat[ST_LEVEL] + 3, 3, 24 + bonus * 3);
  446.     ms.StatBase[ST_MAX_LIFE] += CLAMP(level * (4 + bonus * 3), 0, 300);
  447.     ms.StatBase[ST_CURRENT_HP] = ms.StatBase[ST_MAX_LIFE];
  448.     ms.SkillBase[SK_SMALL_GUNS] += CLAMP(level * (2 + bonus) , 0, 150);
  449.     ms.SkillBase[SK_BIG_GUNS] += CLAMP(level * (2 + bonus), 0, 150);
  450.     ms.SkillBase[SK_ENERGY_WEAPONS] += CLAMP(level * (2 + bonus), 0, 150);
  451.     MS_LOG("MS SKILL(extra setup): SK_SMALL_GUNS = (" + ms.SkillBase[SK_SMALL_GUNS] + ", " + ms.Skill[SK_SMALL_GUNS] + ").");
  452.     ms.SkillBase[SK_FIRST_AID] += CLAMP(level * (2 + bonus), 0, 150);
  453.     MS_LOG("MS SKILL: SK_FIRST_AID = (" + ms.SkillBase[SK_FIRST_AID] + ", " + ms.Skill[SK_FIRST_AID] + ").");
  454.     ms.SkillBase[SK_DOCTOR] += CLAMP(level * (1 + bonus), 0, 100);
  455.     MS_LOG("MS SKILL: SK_DOCTOR = (" + ms.SkillBase[SK_DOCTOR] + ", " + ms.Skill[SK_DOCTOR] + ").");
  456.     ms.StatBase[ST_ACTION_POINTS] += CLAMP(level / 12 * (1+ bonus), 0, 6);
  457.     MS_LOG("MS STATS: ST_ACTION_POINTS = (" + ms.StatBase[ST_ACTION_POINTS] + ", " + ms.Stat[ST_ACTION_POINTS] + ").");
  458.  
  459.     if (level >= 3) ms.PerkBase[PE_ADRENALINE_RUSH] = 1;
  460.     if (level >= 6) ms.PerkBase[PE_IRON_GRIP] = 1;
  461.     if (level >= 9) ms.PerkBase[PE_EVEN_TOUGHER] = 1;
  462.     if (level >= 12) ms.PerkBase[PE_BONUS_RANGED_DAMAGE] = 1;
  463.     if (level >= 15) ms.PerkBase[PE_LIVING_ANATOMY] = 1;
  464.     if (level >= 18) ms.PerkBase[PE_BONUS_RATE_OF_FIRE] = 1;
  465.     if (level >= 21) ms.PerkBase[PE_STONEWALL] = 1;
  466.     if (level >= 24) ms.PerkBase[PE_MORE_RANGED_DAMAGE] = 1;
  467.     if (level >= 27) ms.PerkBase[PE_MORE_CRITICALS] = 1;
  468.     if (level >= 30) ms.PerkBase[PE_EVEN_MORE_CRITICALS] = 1;
  469. }
  470.  
  471. void setInventoryForMsLevel1(Critter& ms)
  472. {
  473.     Critter@ player = GetCritter(ms.SpawnedBy);
  474.     int level = CLAMP(player.Stat[ST_LEVEL] + 3, 3, 24);
  475.  
  476.     if (level < 8)
  477.     {
  478.         ms.AddItem(PID_SHOTGUN, 1);
  479.         ms.AddItem(PID_SHOTGUN_SHELLS, 2 + 2 * level);
  480.     } else if (level < 16)
  481.     {
  482.         ms.AddItem(PID_COMBAT_SHOTGUN, 1);
  483.         ms.AddItem(PID_SHOTGUN_SHELLS, 12 + 3 * level);
  484.     }
  485.     else
  486.     {
  487.         ms.AddItem(PID_HK_CAWS, 1);
  488.         ms.AddItem(PID_SHOTGUN_SHELLS, 12 + 4 * level);
  489.     }
  490.     ms.AddItem(PID_STIMPAK, level / 4);
  491.     ms.AddItem(PID_LEATHER_JACKET, 1);
  492.     ms.AddItem(PID_LEATHER_JACKET_HELMET, 1);
  493. }
  494.  
  495. void setInventoryForMsLevel2(Critter& ms)
  496. {
  497.     Critter@ player = GetCritter(ms.SpawnedBy);
  498.     int level = CLAMP(player.Stat[ST_LEVEL] + 3, 3, 27);
  499.  
  500.     if (level < 9)
  501.     {
  502.         ms.AddItem(PID_ASSAULT_RIFLE, 1);
  503.         ms.AddItem(PID_5MM_AP, 36 + 12 * level);
  504.     } else if (level < 18)
  505.     {
  506.         ms.AddItem(PID_FN_FAL, 1);
  507.         ms.AddItem(PID_7_62MM_AMMO, 20 + 10 * level);
  508.     }
  509.     else
  510.     {
  511.         ms.AddItem(PID_FN_FAL_HPFA, 1);
  512.         ms.AddItem(PID_7_62MM_AMMO, 20 + 20 * level);
  513.     }
  514.     ms.AddItem(PID_SUPER_STIMPAK, level / 8);
  515.     ms.AddItem(PID_LEATHER_ARMOR, 1);
  516.     ms.AddItem(PID_LEATHER_ARMOR_HELMET, 1);
  517. }
  518.  
  519. void setInventoryForMsLevel3(Critter& ms)
  520. {
  521.     Critter@ player = GetCritter(ms.SpawnedBy);
  522.     int level = CLAMP(player.Stat[ST_LEVEL] + 3, 3, 30);
  523.  
  524.     if (level < 10)
  525.     {
  526.         ms.AddItem(PID_LASER_RIFLE, 1);
  527.         ms.AddItem(PID_MICRO_FUSION_CELL, 12 + 3 * level);
  528.     }
  529.     else if (level < 20)
  530.     {
  531.         ms.AddItem(PID_PLASMA_RIFLE, 1);
  532.         ms.AddItem(PID_MICRO_FUSION_CELL, 12 + 3 * level);
  533.     }
  534.     else
  535.     {
  536.         ms.AddItem(PID_GATLING_LASER, 1);
  537.         ms.AddItem(PID_MICRO_FUSION_CELL, 30 + 5 * level);
  538.     }
  539.     ms.AddItem(PID_PSYCHO, 1);
  540.     ms.AddItem(PID_BUFFOUT, 1);
  541.     ms.AddItem(PID_MENTATS, 1);
  542.     ms.AddItem(PID_SUPER_STIMPAK, level / 6);
  543.     ms.AddItem(PID_LEATHER_ARMOR, 1);
  544.     ms.AddItem(PID_LEATHER_ARMOR_HELMET, 1);
  545.  
  546. }
  547.  
  548. bool equipMsWeapon(Critter& ms, uint16 protoId)
  549. {
  550.     array<Item@> items;
  551.     uint num = ms.GetItems(SLOT_INV, items);
  552.     for (uint i = 0; i < items.length(); i++)
  553.     {
  554.         if (items[i].GetProtoId() == protoId)
  555.         {
  556.             ms.MoveItem(items[i].Id, 1, SLOT_HAND1);
  557.             return true;
  558.         }
  559.     }
  560.     return false;
  561. }
  562. /**<  */
  563.  
  564. /**< Timers */
  565.  
  566. /**< Timer creation */
  567. void startMsNormalSpawnTimer(uint playerId, int durationInRealSeconds)
  568. {
  569.     uint[] values = {playerId};
  570.     setMsNormalSpawnAvailability(playerId, false);
  571.     CreateTimeEvent(AFTER(REAL_SECOND(durationInRealSeconds)), "e_resetMsNormalSpawnTimer", values, true);
  572.     MS_LOG("MS normal spawn timer created and started.");
  573. }
  574.  
  575. void startMsMedicalSpawnTimer(uint playerId, int durationInRealSeconds)
  576. {
  577.     uint[] values = {playerId};
  578.     setMsMedicalSpawnAvailability(playerId, false);
  579.     CreateTimeEvent(AFTER(REAL_SECOND(durationInRealSeconds)), "e_resetMsMedicalSpawnTimer", values, true);
  580.     MS_LOG("MS medical spawn timer created and started.");
  581. }
  582.  
  583. void startMsDeathTimer(uint playerId, int durationInRealSeconds)
  584. {
  585.     uint[] values = {playerId};
  586.     setMsDead(playerId, true);
  587.     CreateTimeEvent(AFTER(REAL_SECOND(durationInRealSeconds)), "e_resetMsDeathTimer", values, true);
  588.     MS_LOG("MS death timer created and started.");
  589. }
  590.  
  591. void startMsHelpDurationTimer(uint playerId, int durationInRealSeconds)
  592. {
  593.     uint[] values = {playerId};
  594.     CreateTimeEvent(AFTER(REAL_SECOND(durationInRealSeconds)), "e_despawnMsDueToRtModeTimeout", values, true);
  595.     MS_LOG("MS help duration timer created and started.");
  596. }
  597.  
  598. /**< Timer duration configurations, should be changed for balance at each server */
  599. int getMsNormalSpawnTimerDuration(uint playerId)
  600. {
  601.     Critter@ player = GetCritter(playerId);
  602.     return MS_BASE_RESPAWN_CD / player.Stat[ST_CHARISMA];
  603. }
  604.  
  605. int getMsMedicalSpawnBlockTimerDuration(uint playerId)
  606. {
  607.     Critter@ player = GetCritter(playerId);
  608.     return MS_BASE_MEDICAL_SPAWN_BLOCK_CD / player.Stat[ST_CHARISMA];
  609. }
  610.  
  611. int getMsDeathTimer(uint playerId)
  612. {
  613.     Critter@ player = GetCritter(playerId);
  614.     return MS_BASE_DEATH_CD / player.Stat[ST_CHARISMA];
  615. }
  616.  
  617. int getMsHelpDuration(uint playerId)
  618. {
  619.     Critter@ player = GetCritter(playerId);
  620.     return MS_BASE_RT_DURATION + MS_RT_DURATION_FACTOR * player.Stat[ST_CHARISMA];
  621. }
  622.  
  623. /**<
  624.  * Timer events
  625.  */
  626.  
  627. uint e_resetMsNormalSpawnTimer(array<uint>@ values)
  628. {
  629.     uint playerId = values[0];
  630.     MS_LOG("Normal spawn timer ended. Mysterious Stranger is ready to be spawned again.");
  631.     setMsNormalSpawnAvailability(playerId, true);
  632.     setMsFirstAidAvailability(playerId, true);
  633.     setMsDoctorAvailability(playerId, true);
  634.     return 0;
  635. }
  636.  
  637. uint e_resetMsMedicalSpawnTimer(array<uint>@ values)
  638. {
  639.     uint playerId = values[0];
  640.     MS_LOG("Medical spawn timer ended. Mysterious Stranger is ready to be spawned on FA or Doctor check again.");
  641.     setMsMedicalSpawnAvailability(playerId, true);
  642.     return 0;
  643. }
  644.  
  645. uint e_resetMsDeathTimer(array<uint>@ values)
  646. {
  647.     uint playerId = values[0];
  648.     MS_LOG("Death timer ended. Mysterious Stranger is ready to be spawned again.");
  649.     setMsDead(playerId, false);
  650.     return 0;
  651. }
  652.  
  653. uint e_despawnMsDueToRtModeTimeout(array<uint>@ values)
  654. {
  655.     uint playerId = values[0];
  656.     MS_LOG("MS RT mode availability ended.");
  657.     despawnMysteriousStranger(playerId);
  658.     return 0;
  659. }
  660. /**<  */
  661.  
  662. /**<
  663.  * Mysterious Stranger specific local game variable handling
  664.  */
  665.  
  666. bool isMsSpawned(uint playerId)
  667. {
  668.     return isGameVarSet(playerId, LVAR_perk_ms_spawned);
  669. }
  670.  
  671. void setMsSpawned(uint playerId, bool isSpawned)
  672. {
  673.     setGameVarToBooleanValue(playerId, LVAR_perk_ms_spawned, isSpawned);
  674. }
  675.  
  676. uint getMsId(uint playerId)
  677. {
  678.     return getGameVar(playerId, LVAR_perk_ms_id);
  679. }
  680.  
  681. void setMsId(uint playerId, uint id)
  682. {
  683.     setGameVarToValue(playerId, LVAR_perk_ms_id, id);
  684. }
  685.  
  686. bool isMsNormalSpawnAvailable(uint playerId)
  687. {
  688.     MS_LOG("MS normal spawn availability is " + isGameVarSet(playerId, LVAR_perk_ms_normal_timer));
  689.     return isGameVarSet(playerId, LVAR_perk_ms_normal_timer);
  690. }
  691.  
  692. void setMsNormalSpawnAvailability(uint playerId, bool isAvailable)
  693. {
  694.     setGameVarToBooleanValue(playerId, LVAR_perk_ms_normal_timer, isAvailable);
  695. }
  696.  
  697. bool isMsMedicalSpawnAvailable(uint playerId)
  698. {
  699.     MS_LOG("MS medical spawn availability is " + isGameVarSet(playerId, LVAR_perk_ms_medical_timer));
  700.     return isGameVarSet(playerId, LVAR_perk_ms_medical_timer);
  701. }
  702.  
  703. void setMsMedicalSpawnAvailability(uint playerId, bool isAvailable)
  704. {
  705.     setGameVarToBooleanValue(playerId, LVAR_perk_ms_medical_timer, isAvailable);
  706. }
  707.  
  708. bool isMsDead(uint playerId)
  709. {
  710.     return isGameVarSet(playerId, LVAR_perk_ms_dead);
  711. }
  712.  
  713. void setMsDead(uint playerId, bool isDead)
  714. {
  715.     setGameVarToBooleanValue(playerId, LVAR_perk_ms_dead, isDead);
  716. }
  717.  
  718. bool isMsFirstAidAvailable(uint playerId)
  719. {
  720.     MS_LOG("MS First Aid availability is " + isGameVarSet(playerId, LVAR_perk_ms_first_aid_available));
  721.     return isGameVarSet(playerId, LVAR_perk_ms_first_aid_available);
  722. }
  723.  
  724. void setMsFirstAidAvailability(uint playerId, bool isAvailable)
  725. {
  726.     setGameVarToBooleanValue(playerId, LVAR_perk_ms_first_aid_available, isAvailable);
  727. }
  728.  
  729. bool isMsDoctorAvailable(uint playerId)
  730. {
  731.     MS_LOG("MS Doctor availability is " + isGameVarSet(playerId, LVAR_perk_ms_doctor_available));
  732.     return isGameVarSet(playerId, LVAR_perk_ms_doctor_available);
  733. }
  734.  
  735. void setMsDoctorAvailability(uint playerId, bool isAvailable)
  736. {
  737.     setGameVarToBooleanValue(playerId, LVAR_perk_ms_doctor_available, isAvailable);
  738. }
  739. /**<  */
  740.  
  741. /**<
  742.  * Generic local game variable handlers
  743.  */
  744.  
  745. bool isGameVarSet(uint critterId, uint16 varId)
  746. {
  747.     GameVar@ gameVar = GetLocalVar(varId, critterId);
  748.     if (!valid(gameVar))
  749.         return false;
  750.     if (gameVar.GetValue() == 0)
  751.         return false;
  752.     else
  753.         return true;
  754. }
  755.  
  756. void setGameVarToBooleanValue(uint critterId, uint16 varId, bool value)
  757. {
  758.     GameVar@ gameVar = GetLocalVar(varId, critterId);
  759.     if (!valid(gameVar))
  760.         return;
  761.     if (value)
  762.         gameVar = 1;
  763.     else
  764.         gameVar = 0;
  765. }
  766.  
  767. int getGameVar(uint critterId, uint16 varId)
  768. {
  769.     GameVar@ gameVar = GetLocalVar(varId, critterId);
  770.     if (!valid(gameVar))
  771.         return 0;
  772.     return gameVar.GetValue();
  773. }
  774.  
  775. void setGameVarToValue(uint critterId, uint16 varId, int value)
  776. {
  777.     GameVar@ gameVar = GetLocalVar(varId, critterId);
  778.     if (!valid(gameVar))
  779.         return;
  780.     gameVar = value;
  781. }
  782. /**<  */
  783.  
  784. /**<
  785.  * Script setup
  786.  */
  787.  
  788. void ms_init(Critter& cr, bool firstTime)
  789. {
  790.     cr.StatBase[ST_REPLICATION_TIME] = REPLICATION_DELETE;
  791.     cr.SetEvent(CRITTER_EVENT_SMTH_ATTACKED, "_MsSmthAttacked");
  792.     cr.SetEvent(CRITTER_EVENT_ATTACKED, "_MsAttacked");
  793.     cr.SetEvent(CRITTER_EVENT_DEAD, "_MsDead");
  794.     cr.SetEvent(CRITTER_EVENT_IDLE, "_MsIdle");
  795. }
  796. /**<  */
  797.  
  798. /**<
  799.  * Combat events for Mysterious Stranger NPC.
  800.  */
  801.  
  802. void _MsSmthAttacked(Critter& ms, Critter& fromCr, Critter& attacker)
  803. {
  804.     //  If the attacked target is the owner (Player)
  805.     if(valid(ms) && fromCr.Id == ms.SpawnedBy)
  806.     {
  807.         Critter@ player = fromCr;
  808.         //  cr.Say(SAY_NORM, "Owner under attack!");
  809.         MS_LOG("MS: Owner under attack!");
  810.  
  811.         if (isCritterBelowHalfHp(player))
  812.         {
  813.             if (isMsFirstAidAvailable(player.Id))
  814.             {
  815.                 ms.Say(SAY_NORM_ON_HEAD, "Don't worry, let me heal you " + player.Name + "!");
  816.                 MS_LOG("Owner bellow half HP.");
  817.                 //  add code to heal master here
  818.                 AddHealCritterPlane(ms, 100, player, true);
  819.                 setMsFirstAidAvailability(player.Id, false);
  820.             }
  821.             else
  822.             {
  823.                 if (Random(0, 5) == 0)
  824.                     ms.Say(SAY_NORM_ON_HEAD, "Heal yourself " + player.Name + "!");
  825.                 MS_LOG("Owner bellow half HP, but first aid not available.");
  826.             }
  827.         }
  828.  
  829.         if (isCritterCrippled(player))
  830.         {
  831.             if (isMsDoctorAvailable(player.Id))
  832.             {
  833.                 ms.Say(SAY_NORM_ON_HEAD, "Oh boy, oh boy... Let me patch you up " + player.Name + "!");
  834.                 MS_LOG("Owner is crippled.");
  835.                 //  add code to heal master here
  836.                 AddDoctorCritterPlane(ms, 90, player, true);
  837.                 setMsDoctorAvailability(player.Id, false);
  838.             }
  839.         }
  840.  
  841.         //  Add attacker to target list, if not already there
  842.         if (!ms.CheckEnemyInStack(attacker.Id))
  843.         {
  844.             if (Random(0, 3) == 0)
  845.                 ms.Say(SAY_NORM_ON_HEAD, "Look! Another target!");
  846.             MS_LOG("MS: Adding attacker to enemy stack.");
  847.             ms.AddEnemyInStack(attacker.Id);
  848.             AddAttackPlane(ms, 80, attacker, true);
  849.         }
  850.     }
  851.  
  852.     //  If the attacker is the owner, add target to target list.
  853.     if (valid(ms) && attacker.Id == ms.SpawnedBy)
  854.     {
  855.         MS_LOG("MS: Owner is targeting something!");
  856.         if (!ms.CheckEnemyInStack(fromCr.Id))
  857.         {
  858.             if (Random(0, 2) == 0)
  859.                 ms.Say(SAY_NORM_ON_HEAD, "You point, I punch.. err.. shoot!");
  860.             MS_LOG("MS: Adding target of owner to enemy stack.");
  861.             ms.AddEnemyInStack(fromCr.Id);
  862.             AddAttackPlane(ms, 80, fromCr, true);
  863.         }
  864.     }
  865. }
  866.  
  867. bool _MsAttacked(Critter& ms, Critter& attacker)
  868. {
  869.     ms.AddEnemyInStack(attacker.Id);
  870.     MS_LOG("MS under attack.");
  871.     return true;
  872. }
  873.  
  874. void _MsDead(Critter& ms, Critter@ killer)
  875. {
  876.     Critter@ player = GetCritter(ms.SpawnedBy);
  877.     MS_LOG("My Mysterious Stranger was killed!");
  878.     startMsDeathTimer(player.Id, getMsDeathTimer(player.Id));
  879.     despawnMysteriousStranger(player.Id);
  880. }
  881.  
  882. void _MsIdle(Critter& ms)
  883. {
  884.     //  MS shall check if owner needs help.
  885.     Critter@ player = GetCritter(ms.SpawnedBy);
  886.     if (valid(player))
  887.     {
  888.         if (isCritterBelowHalfHp(player) && isMsFirstAidAvailable(player.Id))
  889.         {
  890.             ms.Say(SAY_NORM_ON_HEAD, "Look at that wound..");
  891.             MS_LOG("MS Owner bellow half HP.");
  892.             AddHealCritterPlane(ms, 100, player, true);
  893.             setMsFirstAidAvailability(player.Id, false);
  894.         }
  895.         if (isCritterCrippled(player) && isMsDoctorAvailable(player.Id))
  896.         {
  897.             ms.Say(SAY_NORM_ON_HEAD, "It's first day at the clinic all over again.");
  898.             MS_LOG("MS Owner is crippled.");
  899.             AddDoctorCritterPlane(ms, 95, player, true);
  900.             setMsDoctorAvailability(player.Id, false);
  901.         }
  902.     }
  903. }
  904. /**<  */
  905.  
  906. /**<
  907.  * Combat aux.
  908.  */
  909.  
  910. bool isCritterCrippled(Critter& cr)
  911. {
  912.     if (!valid(cr))
  913.         return false;
  914.     if (cr.IsDmgEye() || cr.IsDmgLeg() || cr.IsDmgArm())
  915.     {
  916.         return true;
  917.     }
  918.     else
  919.     {
  920.         return false;
  921.     }
  922. }
  923.  
  924. bool isCritterBelowHalfHp(Critter& cr)
  925. {
  926.     if (!valid(cr))
  927.         return false;
  928.     if (cr.Stat[ST_CURRENT_HP] < 0.5 * cr.Stat[ST_MAX_LIFE])
  929.         return true;
  930.     else
  931.         return false;
  932. }
  933. /**<  */
  934.  
  935. /**<
  936.  * Player events for Player and related.
  937.  * Added at map load, if the player has the MS_PERK. This might delete other events added to player, like logging.
  938.  */
  939.  
  940. void addMsSpawnEventsToPlayer(Critter& player)
  941. {
  942.     MS_LOG("MS: Adding player attacked event.");
  943.     player.SetEvent(CRITTER_EVENT_ATTACKED, "_PlayerMsAttacked");
  944. }
  945.  
  946. void removeMsSpawnEventsFromPlayer(Critter& player)
  947. {
  948.     MS_LOG("MS: Removing player attacked event.");
  949.     player.SetEvent(CRITTER_EVENT_ATTACKED, "_PlayerMsAttacked_Removed");
  950. }
  951.  
  952. bool _PlayerMsAttacked(Critter& player, Critter& attacker)
  953. {
  954.     MS_LOG("MS: Player Attacked Event triggered.");
  955.     if (!isMsSpawned(player.Id))
  956.     {
  957.         if (isCritterCrippled(player))
  958.         {
  959.             spawnMysteriousStrangerForDoctor(player.Id);
  960.             Critter@ ms = GetCritter(getMsId(player.Id));
  961.             if (valid(ms) && !ms.CheckEnemyInStack(attacker.Id))
  962.             {
  963.                 MS_LOG("MS: (Spawned for Doctor) Adding attacker of owner to enemy stack.");
  964.                 ms.AddEnemyInStack(attacker.Id);
  965.                 AddAttackPlane(ms, 80, attacker, true);
  966.             }
  967.         }
  968.         else if (isCritterBelowHalfHp(player))
  969.         {
  970.             spawnMysteriousStrangerForFirstAid(player.Id);
  971.             Critter@ ms = GetCritter(getMsId(player.Id));
  972.             if (valid(ms) && !ms.CheckEnemyInStack(attacker.Id))
  973.             {
  974.                 MS_LOG("MS: Adding target of owner to enemy stack.");
  975.                 ms.AddEnemyInStack(attacker.Id);
  976.                 AddAttackPlane(ms, 80, attacker, true);
  977.             }
  978.         }
  979.     }
  980.     return true;
  981. }
  982.  
  983. bool _PlayerMsAttacked_Removed(Critter& player, Critter& attacker)
  984. {
  985.     MS_LOG("MS: Player Attacked Event is empty, because the map is not enabled for MS.");
  986.     return true;
  987. }
Advertisement
RAW Paste Data Copied
Advertisement