예제 #1
0
 def operation_overrides(locked_args=frozendict()):
     locked_args = dict(locked_args)
     dynamic_skill_locked_args = dict(locked_args)
     if 'chance' not in locked_args:
         locked_args['chance'] = SuccessChance.ONE
     dynamic_skill_locked_args = dict(dynamic_skill_locked_args)
     if 'chance' not in dynamic_skill_locked_args:
         dynamic_skill_locked_args['chance'] = SuccessChance.ONE
     if 'advertise' not in dynamic_skill_locked_args:
         dynamic_skill_locked_args['advertise'] = False
     return {
         'operations':
         TunableList(
             description=
             '\n                A list of statistic operations that occur at each interval.\n                ',
             tunable=TunableStatisticChange(
                 dynamic_skill=DynamicSkillLootOp.TunableFactory(
                     locked_args=dynamic_skill_locked_args),
                 dynamic_variant_skill=DynamicVariantSkillLootOp.
                 TunableFactory(),
                 locked_args=locked_args,
                 gain_type=GAIN_TYPE_RATE,
                 statistic_override=StatisticOperation.
                 get_statistic_override(pack_safe=True),
                 default='statistic_change'))
     }
예제 #2
0
class LiveDragLootActions(LootActions):
    __qualname__ = 'LiveDragLootActions'
    INSTANCE_TUNABLES = {
        'loot_actions':
        TunableList(
            TunableVariant(
                statistics=TunableStatisticChange(
                    locked_args={
                        'advertise': False,
                        'chance': 1,
                        'tests': None
                    },
                    include_relationship_ops=False),
                collectible_shelve_item=CollectibleShelveItem.TunableFactory(),
                inventory_loot=InventoryLoot.TunableFactory(
                    subject_participant_type_options={
                        'description':
                        '\n                            The participant type who has the inventory that the\n                            object goes into during this loot.\n                            ',
                        'optional': False
                    },
                    target_participant_type_options={
                        'description':
                        '\n                            The participant type of the object which gets to\n                            switch inventories in the loot.\n                            ',
                        'default_participant': ParticipantType.LiveDragActor
                    }),
                state_change=StateChangeLootOp.TunableFactory(),
                money_loot=MoneyChange.TunableFactory()))
    }

    def __iter__(self):
        return iter(self.loot_actions)
예제 #3
0
class SituationGoalLootActions(HasTunableReference,
                               metaclass=TunedInstanceMetaclass,
                               manager=services.get_instance_manager(
                                   sims4.resources.Types.ACTION)):
    __qualname__ = 'SituationGoalLootActions'
    INSTANCE_TUNABLES = {
        'goal_loot_actions':
        TunableList(
            TunableVariant(statistics=TunableStatisticChange(
                locked_args={
                    'subject': ParticipantType.Actor,
                    'advertise': False,
                    'chance': 1,
                    'tests': None
                }),
                           money_loot=MoneyChange.TunableFactory(
                               locked_args={
                                   'subject': ParticipantType.Actor,
                                   'chance': 1,
                                   'tests': None,
                                   'display_to_user': None,
                                   'statistic_multipliers': None
                               }),
                           buff=buffs.buff_ops.BuffOp.TunableFactory(
                               locked_args={
                                   'subject': ParticipantType.Actor,
                                   'chance': 1,
                                   'tests': None
                               })))
    }

    def __iter__(self):
        return iter(self.goal_loot_actions)
예제 #4
0
class Party:
    RALLY_FALSE_ADS = TunableList(
        description=
        ' \n        A list of false advertisement for rallyable interactions. Use this\n        tunable to entice Sims to autonomously choose rallyable over non-\n        rallyable interactions.\n        ',
        tunable=TunableStatisticChange(locked_args={
            'subject': ParticipantType.Actor,
            'advertise': True
        }))
예제 #5
0
 def __init__(self, **kwargs):
     super().__init__(
         TunableStatisticChange(
             locked_args={
                 'subject': ParticipantType.Actor,
                 'advertise': True
             },
             statistic_override=StatisticChangeOp.get_statistic_override(
                 pack_safe=True)), **kwargs)
예제 #6
0
class SituationGoalLootActions(HasTunableReference,
                               metaclass=TunedInstanceMetaclass,
                               manager=services.get_instance_manager(
                                   sims4.resources.Types.ACTION)):
    INSTANCE_TUNABLES = {
        'goal_loot_actions':
        TunableList(
            TunableVariant(statistics=TunableStatisticChange(
                locked_args={
                    'subject': ParticipantType.Actor,
                    'advertise': False,
                    'chance': SuccessChance.ONE
                }),
                           money_loot=MoneyChange.TunableFactory(
                               locked_args={
                                   'subject': ParticipantType.Actor,
                                   'chance': SuccessChance.ONE,
                                   'display_to_user': None,
                                   'statistic_multipliers': None
                               }),
                           buff=buffs.buff_ops.BuffOp.TunableFactory(
                               locked_args={
                                   'subject': ParticipantType.Actor,
                                   'chance': SuccessChance.ONE
                               }),
                           notification_and_dialog=DialogLootOp.TunableFactory(
                               locked_args={
                                   'subject': ParticipantType.Actor,
                                   'advertise': False,
                                   'chance': SuccessChance.ONE
                               }),
                           reaction=ReactionLootOp.TunableFactory(
                               locked_args={
                                   'subject': ParticipantType.Actor,
                                   'advertise': False,
                                   'chance': SuccessChance.ONE
                               }),
                           state_change=StateChangeLootOp.TunableFactory(
                               locked_args={
                                   'advertise': False,
                                   'chance': SuccessChance.ONE
                               })))
    }

    def __iter__(self):
        return iter(self.goal_loot_actions)
예제 #7
0
 def __init__(self, *args, statistic_pack_safe=False, **kwargs):
     super().__init__(
         *args,
         actions=TunableReference(
             description=
             '\n                Apply a set of loot operations.\n                ',
             manager=services.get_instance_manager(
                 sims4.resources.Types.ACTION),
             class_restrictions=('LootActions', 'RandomWeightedLoot'),
             pack_safe=True),
         apply_canvas_overlay=ApplyCanvasOverlay.TunableFactory(),
         audio=PlayAudioOp.TunableFactory(),
         statistics=TunableStatisticChange(
             statistic_override=StatisticOperation.get_statistic_override(
                 pack_safe=statistic_pack_safe)),
         relationship_bits_loot=RelationshipBitChange.TunableFactory(
             description='A list of relationship bit operations to perform'
         ),
         relationship_bits_lock=UnlockRelationshipBitLock.TunableFactory(),
         relationship_bits_with_filter=RelationshipBitOnFilteredSims.
         TunableFactory(),
         money_loot=MoneyChange.TunableFactory(),
         topic_loot=TopicUpdate.TunableFactory(
             target_participant_type_options={'optional': True}),
         buff=buffs.buff_ops.BuffOp.TunableFactory(),
         buff_removal=buffs.buff_ops.BuffRemovalOp.TunableFactory(),
         buff_transfer=buffs.buff_ops.BuffTransferOp.TunableFactory(
             target_participant_type_options={
                 'description':
                 '\n                    Buffs are transferred from this Sim to the Subject.\n                    ',
                 'default_participant': ParticipantType.Actor
             }),
         normalize_stat=NormalizeStatisticsOp.TunableFactory(
             target_participant_type_options={
                 'description':
                 '\n                    The Sim from which to transfer the listed stats from.\n                    ',
                 'default_participant': ParticipantType.Actor
             }),
         skill_effectiveness=SkillEffectivenessLoot.TunableFactory(),
         take_turn=game_component.TakeTurn.TunableFactory(),
         team_score=game_component.TeamScore.TunableFactory(),
         team_score_points=game_component.TeamScorePoints.TunableFactory(),
         game_over=game_component.GameOver.TunableFactory(),
         reset_high_score=game_component.ResetHighScore.TunableFactory(),
         reset_game=game_component.ResetGame.TunableFactory(),
         setup_game=game_component.SetupGame.TunableFactory(),
         dynamic_skill_loot=DynamicSkillLootOp.TunableFactory(
             locked_args={'exclusive_to_owning_si': False}),
         dynamic_variant_skill_loot=DynamicVariantSkillLootOp.
         TunableFactory(),
         fix_gender_preference=sims.gender_preference.GenderPreferenceOp.
         TunableFactory(),
         inventory_loot=InventoryLoot.TunableFactory(
             subject_participant_type_options={
                 'description':
                 '\n                     The participant type who has the inventory that the\n                     object goes into during this loot.\n                     ',
                 'optional': True
             },
             target_participant_type_options={
                 'description':
                 '\n                    The participant type of the object which would get to\n                    switch inventory in the loot\n                    ',
                 'default_participant': ParticipantType.CarriedObject
             }),
         dynamic_buff_loot=DynamicBuffLootOp.TunableFactory(),
         object_rewards=ObjectRewardsOperation.TunableFactory(),
         reward=RewardOperation.TunableFactory(),
         transfer_ownership=TransferOwnershipLootOp.TunableFactory(),
         create_object=ObjectCreationOp.TunableFactory(),
         create_puddles=CreatePuddlesLootOp.TunableFactory(
             target_participant_type_options={
                 'description':
                 '\n                    The participant of the interaction whom the puddle\n                    should be placed near.\n                    ',
                 'default_participant': ParticipantType.Object
             }),
         create_situation=CreateSituationLootOp.TunableFactory(),
         destroy_situation=DestroySituationLootOp.TunableFactory(),
         life_extension=LifeExtensionLootOp.TunableFactory(),
         notification_and_dialog=DialogLootOp.TunableFactory(),
         state_change=StateChangeLootOp.TunableFactory(),
         trait_add=AddTraitLootOp.TunableFactory(),
         trait_remove=RemoveTraitLootOp.TunableFactory(),
         know_other_sims_trait=KnowOtherSimTraitOp.TunableFactory(
             target_participant_type_options={
                 'description':
                 '\n                    The Sim or Sims whose information the subject Sim is learning.\n                    ',
                 'default_participant': ParticipantType.TargetSim
             }),
         know_other_sims_career=KnowOtherSimCareerOp.TunableFactory(
             target_participant_type_options={
                 'description':
                 '\n                    The Sim or Sims whose information the subject Sim is learning.\n                    ',
                 'default_participant': ParticipantType.TargetSim
             }),
         know_other_sims_statistics=KnowOtherSimsStat.TunableFactory(
             target_participant_type_options={
                 'description':
                 '\n                    The Sim or Sims whose information the subject Sim is learning.\n                    ',
                 'default_participant': ParticipantType.TargetSim
             }),
         know_other_sims_major=KnowOtherSimMajorOp.TunableFactory(
             target_participant_type_options={
                 'description':
                 '\n                    The Sim or Sims whose information the subject Sim is learning.\n                    ',
                 'default_participant': ParticipantType.TargetSim
             }),
         object_relationship=ObjectRelationshipLootOp.TunableFactory(
             target_participant_type_options={
                 'description':
                 '\n                    The object whose relationship to modify.\n                    ',
                 'default_participant': ParticipantType.Object
             }),
         interest_income=HouseholdFundsInterestLootOp.TunableFactory(),
         career_level=CareerLevelOp.TunableFactory(),
         career_loot=CareerLootOp.TunableFactory(
             career_options={'pack_safe': True}),
         fire=FireLootOp.TunableFactory(),
         unlock_item=UnlockLootOp.TunableFactory(),
         fire_deactivate_sprinkler=FireDeactivateSprinklerLootOp.
         TunableFactory(),
         fire_clean_scorch=FloorFeatureRemoveOp.TunableFactory(),
         extinguish_nearby_fire=ExtinguishNearbyFireLootOp.TunableFactory(),
         award_whim_bucks=AwardWhimBucksLootOp.TunableFactory(),
         discover_clue=DiscoverClueLootOp.TunableFactory(),
         new_crime=NewCrimeLootOp.TunableFactory(),
         create_notebook_entry=NotebookEntryLootOp.TunableFactory(),
         breakthrough_moment=BreakThroughLootOperation.TunableFactory(),
         destroy_objects_from_inventory=DestroyObjectsFromInventoryLootOp.
         TunableFactory(),
         destroy_target_objects=DestroyTargetObjectsLootOp.TunableFactory(),
         bucks_loot=BucksLoot.TunableFactory(),
         award_perk=AwardPerkLoot.TunableFactory(),
         refresh_inventory_items_decay_modifiers=
         RefreshInventoryItemsDecayModifiers.TunableFactory(),
         refresh_whims=RefreshWhimsLootOp.TunableFactory(),
         remove_notebook_entry=RemoveNotebookEntry.TunableFactory(),
         lock_door=LockDoor.TunableFactory(),
         unlock_door=UnlockDoor.TunableFactory(),
         utility=UtilityModifierOp.TunableFactory(),
         utility_usage=UtilityUsageOp.TunableFactory(),
         reaction=ReactionLootOp.TunableFactory(),
         greeting=GreetingLootOp.TunableFactory(),
         event=ProcessEventOp.TunableFactory(),
         schedule_drama_node=ScheduleDramaNodeLoot.TunableFactory(),
         cancel_scheduled_drama_node=CancelScheduledDramaNodeLoot.
         TunableFactory(),
         set_club_gathering_vibe=SetClubGatheringVibe.TunableFactory(),
         summon_npc=SummonNPC.TunableFactory(),
         travel_to_target_sim=TravelToTargetSim.TunableFactory(),
         increment_community_challenge_count=IncrementCommunityChallengeCount
         .TunableFactory(),
         unlock_hidden_aspiration_track=UnlockHiddenAspirationTrack.
         TunableFactory(),
         set_primary_aspiration_track=SetPrimaryAspirationTrack.
         TunableFactory(),
         claim_table=ClaimRestaurantTable.TunableFactory(),
         claim_seat=ClaimRestaurantSeat.TunableFactory(),
         release_table=ReleaseRestaurantTable.TunableFactory(),
         restaurant_expedite_order=RestaurantExpediteGroupOrder.
         TunableFactory(),
         business_modify_customer_flow=ModifyCustomerFlow.TunableFactory(),
         butler_state_change=ButlerSituationStateChange.TunableFactory(),
         slot_objects=SlotObjects.TunableFactory(),
         looping_loot_ops=LoopingLootOp.TunableFactory(),
         give_sickness=GiveSicknessLootOp.TunableFactory(),
         remove_sickness=RemoveSicknessLootOp.TunableFactory(),
         apply_tags_to_object=ApplyTagsToObject.TunableFactory(),
         make_pet_missing=MakePetMissing.TunableFactory(),
         name_reset=NameResetLootOp.TunableFactory(),
         post_missing_pet_alert=PostMissingPetAlert.TunableFactory(),
         vfx=PlayVisualEffectLootOp.TunableFactory(),
         add_relic_combo=AddRelicCombo.TunableFactory(),
         oneshot_broadcaster=BroadcasterOneShotLootOp.TunableFactory(),
         store_object_info=StoreObjectInfoLootOp.TunableFactory(),
         remove_object_info=RemoveObjectInfoLootOp.TunableFactory(),
         store_sim_info=StoreSimInfoLootOp.TunableFactory(),
         recycling_bucks_loot=RecyclingBucksLoot.TunableFactory(),
         remove_stored_sim_info=RemoveSimInfoLootOp.TunableFactory(),
         weather_set_override_forecast=WeatherSetOverrideForecastLootOp.
         TunableFactory(locked_args={'subject': ParticipantType.Actor}),
         weather_set_season=WeatherSetSeasonLootOp.TunableFactory(
             locked_args={'subject': ParticipantType.Actor}),
         weather_start_event=WeatherStartEventLootOp.TunableFactory(
             locked_args={'subject': ParticipantType.Actor}),
         hidden_inventory_transfer=HiddenInventoryTransferLoot.
         TunableFactory(),
         transfer_painting_state=TransferPaintingStateLoot.TunableFactory(),
         squad_loot=SquadLootOp.TunableFactory(),
         stored_sim_info_transfer=TransferStoredSimInfo.TunableFactory(),
         custom_tooltip_transfer=TransferCustomTooltip.TunableFactory(),
         transfer_name_loot=TransferNameLootOp.TunableFactory(),
         reset_aspiration=ResetAspiration.TunableFactory(),
         narrative=NarrativeLootOp.TunableFactory(),
         narrative_progression=NarrativeGroupProgression.TunableFactory(),
         scheduled_delivery=ScheduledDeliveryLoot.TunableReference(),
         motherplant_battle_change=MotherplantBattleSituationStateChange.
         TunableFactory(),
         global_policy_add_progress=GlobalPolicyAddProgress.TunableFactory(
             locked_args={'text': None}),
         festival_contest_get_reward=FestivalContestAwardWinners.
         TunableFactory(),
         set_name_from_object_relationship=SetNameFromObjectRelationship.
         TunableFactory(),
         create_plant=CreatePlantAtLocationLootOperation.TunableFactory(),
         set_favorite=SetFavoriteLootOp.TunableFactory(),
         roommate_ops=RoommateLootOp.TunableFactory(),
         university_course_grade_notification=
         UniversityCourseGradeNotification.TunableFactory(),
         university_loot=UniversityLootOp.TunableFactory(),
         organization_membership_loot=OrganizationMembershipLoot.
         TunableFactory(),
         scholarship_show_high_chance_loot=ShowHighChanceScholarshipsLoot.
         TunableFactory(),
         scholarship_apply_loot=ApplyForScholarshipLoot.TunableFactory(),
         scholarship_get_status_loot=GetScholarshipStatusLoot.
         TunableFactory(),
         scholarship_action_loot=ScholarshipActionLoot.TunableFactory(),
         scholarship_show_info_sign=ShowScholarshipDynamicSignLoot.
         TunableFactory(),
         apply_loot_to_hidden_inventory_items=
         ApplyLootToHiddenInventoryItemsLoot.TunableFactory(),
         refund_crafting_process=RefundCraftingProcessLoot.TunableFactory(),
         **kwargs)
예제 #8
0
class LootActions(HasTunableReference, HasTunableSingletonFactory, AutoFactoryInit, metaclass=TunedInstanceMetaclass, manager=services.get_instance_manager(sims4.resources.Types.ACTION)):
    __qualname__ = 'LootActions'
    INSTANCE_TUNABLES = {'run_test_first': Tunable(description='\n           If left unchecked, iterate over the actions and if its test succeeds\n           apply the action at that moment.\n           \n           If checked, run through all the loot actions and collect all actions\n           that passes their test.  Then apply all the actions that succeeded.\n           ', tunable_type=bool, default=False, needs_tuning=True), 'loot_actions': TunableList(tunable=TunableVariant(actions=TunableReference(description='\n                    Apply a set of loot operations.\n                    ', manager=services.get_instance_manager(sims4.resources.Types.ACTION), class_restrictions='LootActions'), statistics=TunableStatisticChange(), relationship_bits_loot=RelationshipBitChange.TunableFactory(description='A list of relationship bit operations to perform'), money_loot=MoneyChange.TunableFactory(), topic_loot=TopicUpdate.TunableFactory(target_participant_type_options={'optional': True}), buff=buffs.buff_ops.BuffOp.TunableFactory(), buff_removal=buffs.buff_ops.BuffRemovalOp.TunableFactory(), buff_transfer=buffs.buff_ops.BuffTransferOp.TunableFactory(target_participant_type_options={'description': '\n                        The Sim from which to transfer buffs from.\n                        ', 'default_participant': ParticipantType.Actor}), normalize_stat=NormalizeStatisticsOp.TunableFactory(target_participant_type_options={'description': '\n                        The Sim from which to transfer the listed stats from.\n                        ', 'default_participant': ParticipantType.Actor}), skill_effectiveness=SkillEffectivenessLoot.TunableFactory(), take_turn=game_component.TakeTurn.TunableFactory(), team_score=game_component.TeamScore.TunableFactory(), game_over=game_component.GameOver.TunableFactory(), reset_game=game_component.ResetGame.TunableFactory(), setup_game=game_component.SetupGame.TunableFactory(), dynamic_skill_loot=DynamicSkillLootOp.TunableFactory(locked_args={'exclusive_to_owning_si': False}), fix_gender_preference=sims.gender_preference.GenderPreferenceOp.TunableFactory(), inventory_loot=InventoryLoot.TunableFactory(subject_participant_type_options={'description': '\n                         The participant type who has the inventory that the\n                         object goes into during this loot.\n                         ', 'optional': True}, target_participant_type_options={'description': '\n                        The participant type of the object which would get to\n                        switch inventory in the loot\n                        ', 'default_participant': ParticipantType.CarriedObject}), dynamic_buff_loot=DynamicBuffLootOp.TunableFactory(), object_rewards=ObjectRewardsOperation.TunableFactory(), transfer_ownership=TransferOwnershipLootOp.TunableFactory(), create_puddles=CreatePuddlesLootOp.TunableFactory(target_participant_type_options={'description': '\n                        The participant of the interaction whom the puddle\n                        should be placed near.\n                        ', 'default_participant': ParticipantType.Object}), life_extension=LifeExtensionLootOp.TunableFactory(), notification=NotificationLootOp.TunableFactory(), state_change=StateChangeLootOp.TunableFactory(), trait_add=AddTraitLootOp.TunableFactory(), trait_remove=RemoveTraitLootOp.TunableFactory(), know_other_sims_trait=KnowOtherSimTraitOp.TunableFactory(target_participant_type_options={'description': '\n                        The Sim or Sims whose information the subject Sim is learning.\n                        ', 'default_participant': ParticipantType.TargetSim}), object_relationship=ObjectRelationshipLootOp.TunableFactory(target_participant_type_options={'description': '\n                        The object whose relationship to modify.\n                        ', 'default_participant': ParticipantType.Object}), interest_income=HouseholdFundsInterestLootOp.TunableFactory(), career_level=CareerLevelOp.TunableFactory(), career_loot=CareerLootOp.TunableFactory(), fire=FireLootOp.TunableFactory(), unlock_item=UnlockLootOp.TunableFactory(), fire_deactivate_sprinkler=FireDeactivateSprinklerLootOp.TunableFactory(), fire_clean_scorch=FireCleanScorchLootOp.TunableFactory(), extinguish_nearby_fire=ExtinguishNearbyFireLootOp.TunableFactory(), award_whim_bucks=AwardWhimBucksLootOp.TunableFactory()))}
    FACTORY_TUNABLES = INSTANCE_TUNABLES
    _simoleon_loot = None

    @classmethod
    def _tuning_loaded_callback(cls):
        cls._simoleon_loot = None
        for action in cls.loot_actions:
            while hasattr(action, 'get_simoleon_delta'):
                if cls._simoleon_loot is None:
                    cls._simoleon_loot = []
                cls._simoleon_loot.append(action)

    @classmethod
    def _verify_tuning_callback(cls):
        cls._validate_recursion()

    @classmethod
    @assertions.not_recursive
    def _validate_recursion(cls):
        for action in cls.loot_actions:
            while action.loot_type == LootType.ACTIONS:
                try:
                    action._validate_recursion()
                except:
                    logger.error('{} is an action in {} but that creates a circular dependency', action, cls, owner='epanero')

    @classproperty
    def loot_type(self):
        return LootType.ACTIONS

    @classmethod
    def get_simoleon_delta(cls, *args, **kwargs):
        if cls._simoleon_loot is not None:
            return sum(action.get_simoleon_delta(*args, **kwargs) for action in cls._simoleon_loot)
        return 0

    @flexmethod
    def get_loot_ops_gen(cls, inst, resolver=None):
        inst_or_cls = inst if inst is not None else cls
        if resolver is None or not inst_or_cls.run_test_first:
            for action in inst_or_cls.loot_actions:
                if action.loot_type == LootType.ACTIONS:
                    yield action.get_loot_ops_gen(resolver=resolver)
                else:
                    yield (action, False)
        else:
            actions_that_can_be_applied = []
            for action in inst_or_cls.loot_actions:
                while action.loot_type == LootType.ACTIONS or action.test_resolver(resolver):
                    actions_that_can_be_applied.append(action)
            for action in actions_that_can_be_applied:
                if action.loot_type == LootType.ACTIONS:
                    yield action.get_loot_ops_gen(resolver=resolver)
                else:
                    yield (action, True)

    @flexmethod
    def apply_to_resolver(cls, inst, resolver, skip_test=False):
        inst_or_cls = inst if inst is not None else cls
        for (action, test_ran) in inst_or_cls.get_loot_ops_gen(resolver):
            try:
                action.apply_to_resolver(resolver, skip_test=test_ran)
            except BaseException as ex:
                logger.error('Exception when applying action {} for loot {}', action, cls)
                raise ex
예제 #9
0
 def __init__(self, **kwargs):
     super().__init__(
         TunableStatisticChange(locked_args={
             'subject': ParticipantType.Actor,
             'advertise': True
         }), **kwargs)
예제 #10
0
class SituationJob(HasDependentTunableReference,
                   metaclass=HashedTunedInstanceMetaclass,
                   manager=services.situation_job_manager()):
    __qualname__ = 'SituationJob'
    CHANGE_OUTFIT_INTERACTION = interactions.base.super_interaction.SuperInteraction.TunableReference(
        description=
        '\n        A reference that should be tuned to an interaction that will just set\n        sim to their default outfit.\n        '
    )
    INSTANCE_TUNABLES = {
        'display_name':
        TunableLocalizedString(
            description=
            '\n                Localized name of this job. This name is displayed in the situation\n                creation UI where the player is making selection of sims that belong\n                to a specific job. E.g. "Guest", "Bride or Groom", "Bartender".\n                \n                Whenever you add a display name, evaluate whether your design \n                needs or calls out for a tooltip_name.\n                ',
            tuning_group=GroupNames.UI),
        'tooltip_name':
        TunableLocalizedString(
            description=
            '\n                Localized name of this job that is displayed when the player hovers\n                on the sim while the situation is in progress. If this field is absent, \n                there will be no tooltip on the sim.\n                \n                This helps distinguish the cases where we want to display "Bride or Groom" \n                in the situation creation UI but only "Bride" or "Groom" on the \n                sim\'s tooltip when the player is playing with the situation. \n                ',
            tuning_group=GroupNames.UI),
        'icon':
        TunableResourceKey(
            description=
            '\n                Icon to be displayed for the job of the Sim\n                ',
            default=None,
            needs_tuning=True,
            resource_types=sims4.resources.CompoundTypes.IMAGE,
            tuning_group=GroupNames.UI),
        'job_description':
        TunableLocalizedString(
            description=
            '\n                Localized description of this job\n                ',
            tuning_group=GroupNames.UI),
        'tests':
        event_testing.tests.TunableTestSet(),
        'sim_auto_invite':
        TunableInterval(
            description=
            "\n                On situation start it will select a random number of sims in this interval.\n                It will automatically add npcs to the situation so that it has at least\n                that many sims in this job including those the player\n                invites/hires. If the player invites/hires more than the auto\n                invite number, no npcs will be automatically added.\n                \n                Auto invite sims are considered to be invited, so they will be\n                spawned for invite only situations too. For player initiated\n                situations you probably want to set this 0. It is really meant\n                for commercial venues.\n                \n                You can use Churn tuning on this job if you want the number of\n                sims to vary over time. Churn tuning will override this one.\n                \n                For example, an ambient bar situation would have a high auto\n                invite number for the customer job because we want many sims in\n                the bar but the player doesn't invite or hire anybody for an\n                ambient situation.\n                \n                A date would have 0 for this for all jobs because the situation\n                would never spawn anybody to fill jobs, the player would have\n                to invite them all.\n                ",
            tunable_type=int,
            default_lower=0,
            default_upper=0,
            minimum=0),
        'sim_auto_invite_allow_instanced_sim':
        Tunable(
            description=
            '\n                If checked will allow instanced sims to be assigned this job\n                to fulfill auto invite spots instead of forcing the spawning\n                of new sims.\n                \n                NOTE: YOU PROBABLY WANT TO LEAVE THIS AS UNCHECKED.  PLEASE\n                CONSULT A GPE IF YOU PLAN ON TUNING IT.\n                ',
            tunable_type=bool,
            default=False),
        'sim_count':
        TunableInterval(
            description=
            '\n                The number of Sims the player is allowed to invite or hire for\n                this job.  The lower bound is the required number of sims, the\n                upper bound is the maximum.\n                \n                This only affects what the player can do in the Plan an Event UI.\n                It has no affect while the situation is running.\n                ',
            tunable_type=int,
            default_lower=1,
            default_upper=1,
            minimum=0),
        'churn':
        OptionalTunable(
            description=
            'If enabled, produces churn or turnover\n                in the sims holding this job. Periodically sims in the job will leave\n                the lot and other sims will come to fill the job. \n                \n                When a situation is first started it will automatically invite a\n                number of sims appropriate for the time of day. This supercedes\n                sim_auto_invite.\n                \n                This is primarily for commercial venue customers.\n                This is NOT compatible with Sim Shifts.\n                ',
            tunable=SituationJobChurn.TunableFactory(),
            display_name='Sim Churn'),
        'sim_shifts':
        OptionalTunable(
            description=
            'If enabled, creates shifts of\n                sims who replace the sims currently in the job.\n                \n                When a situation is first started it will automatically invite a\n                number of sims appropriate for the time of day. This supercedes\n                sim_auto_invite.\n                \n                This is primarily intended for commercial venue employees.\n                This is NOT compatible with Sim Churn.\n                ',
            tunable=SituationJobShifts.TunableFactory()),
        'goal_scoring':
        Tunable(
            description=
            '\n                The score for completing a goal\n                ',
            tunable_type=int,
            default=1,
            tuning_group=GroupNames.SCORING),
        'interaction_scoring':
        TunableList(
            description=
            '\n                Test for interactions run. Each test can have a separate score.\n                ',
            tunable=TunableTuple(
                description=
                '\n                    Each affordance that satisfies the test will receive the\n                    same score.\n                    ',
                score=Tunable(
                    description=
                    '\n                        Score for passing the test.\n                        ',
                    tunable_type=int,
                    default=1),
                affordance_list=event_testing.tests_with_data.
                TunableParticipantRanInteractionTest(
                    locked_args={
                        'participant': ParticipantType.Actor,
                        'tooltip': None,
                        'running_time': None
                    })),
            tuning_group=GroupNames.SCORING),
        'crafted_object_scoring':
        TunableList(
            description=
            '\n                Test for objects crafted. Each test can have a separate score.\n                ',
            tunable=TunableTuple(
                description=
                '\n                    Test for objects crafted. Each test can have a separate\n                    score.\n                    ',
                score=Tunable(
                    description=
                    '\n                        Score for passing the test.\n                        ',
                    tunable_type=int,
                    default=1),
                object_list=event_testing.test_variants.TunableCraftedItemTest(
                    description=
                    '\n                        A test to see if the crafted item should give score.\n                        ',
                    locked_args={'tooltip': None})),
            tuning_group=GroupNames.SCORING),
        'rewards':
        TunableMapping(
            description=
            '\n                Rewards given to the sim in this job when situation reaches specific medals.\n                ',
            key_type=TunableEnumEntry(
                SituationMedal,
                SituationMedal.TIN,
                description=
                '\n                    Medal to achieve to get the corresponding benefits.\n                    '
            ),
            value_type=SituationJobReward.TunableFactory(
                description=
                '\n                    Reward and LootAction benefits for accomplishing the medal.\n                    '
            ),
            key_name='medal',
            value_name='benefit',
            tuning_group=GroupNames.SCORING),
        'filter':
        TunableReference(manager=services.get_instance_manager(
            sims4.resources.Types.SIM_FILTER),
                         needs_tuning=True,
                         class_restrictions=TunableSimFilter),
        'tags':
        TunableSet(
            description=
            '\n                Designer tagging for making the game more fun.\n                ',
            tunable=TunableEnumEntry(tunable_type=Tag, default=Tag.INVALID)),
        'job_uniform':
        OptionalTunable(
            description=
            '\n                If enabled, when a Sim is assigned this situation job, that Sim\n                will switch into their outfit based on the Outfit Category.\n                \n                If the Outfit Category is SITUATION, then an outfit will be\n                generated based on the passed in tags and the Sim will switch\n                into that outfit.\n                ',
            tunable=TunableTuple(
                description='\n                    ',
                outfit_change_reason=TunableEnumEntry(
                    description=
                    '\n                        An enum that represents a reason for outfit change for\n                        the outfit system.\n                        \n                        An outfit change reason is really a series of tests\n                        that are run to determine which outfit category that\n                        we want to switch into.\n                        \n                        In order to do this, go to the tuning file\n                        sims.sim_outfits.\n                        \n                        Add a new OutfitChangeReason enum entry for your change\n                        reason.\n                        \n                        Then go into\n                        ClothingChangeTunables->Clothing Reasons To Outfits\n                        and add a new entry to the map.\n                        \n                        Set this entry to your new enum entry.\n                        \n                        Then you can add new elements to the list of tests and\n                        outfit categories that you want it to change the sim\n                        into.\n                        ',
                    tunable_type=OutfitChangeReason,
                    default=OutfitChangeReason.Invalid),
                outfit_change_priority=TunableEnumEntry(
                    description=
                    '\n                        The outfit change priority.  Higher priority outfit\n                        changes will override lower priority outfit changes.\n                        ',
                    tunable_type=DefaultOutfitPriority,
                    default=DefaultOutfitPriority.NoPriority),
                playable_sims_change_outfits=Tunable(
                    description=
                    '\n                        If checked, Playable Sims will change outfit when the job is set for the Sim. This\n                        should be checked on things like user facing events,\n                        but not Venue Background Event Jobs.\n                        ',
                    tunable_type=bool,
                    default=True),
                situation_outfit_generation_tags=OptionalTunable(
                    description=
                    "\n                        If enabled, the situation will use the outfit tags\n                        specified to generate an outfit for the sim's\n                        SITUATION outfit category.  If generating an outfit\n                        make sure to set outfit change reason to something that\n                        will put the sim into the SITUATION outfit category or\n                        you will not have the results that you expect.\n                        ",
                    tunable=TunableSet(
                        description=
                        '\n                            Only one of these sets is picked randomly to select the\n                            outfit tags within this set.\n                            E.g. If you want to host a costume party where the guests show\n                            up in either octopus costume or a shark costume, we would have\n                            two sets of tuning that can specify exclusive tags for the \n                            specific costumes. Thus we avoid accidentally generating a \n                            sharktopus costume.\n                            \n                            If you want your guests to always show up in sharktopus costumes \n                            then tune only one set of tags that enlist all the outfit tags\n                            that are associated with either shark or octopus.\n                            ',
                        tunable=TunableSet(
                            description=
                            "\n                                Tags that will be used by CAS to generate an outfit\n                                within the sim's SITUATION outfit category.\n                                ",
                            tunable=TunableEnumWithFilter(
                                tunable_type=Tag,
                                filter_prefixes=['uniform', 'outfitcategory'],
                                default=Tag.INVALID))))),
            disabled_name='no_uniform',
            enabled_name='uniform_specified'),
        'can_be_hired':
        Tunable(description=
                '\n                This job can be hired.\n                ',
                tunable_type=bool,
                default=True),
        'hire_cost':
        Tunable(
            description=
            '\n                The cost to hire a Sim for this job in Simoleons.\n                ',
            tunable_type=int,
            default=0),
        'game_breaker':
        Tunable(
            description=
            '\n                If True then this job must be filled by a sim\n                or the game will be broken. This is for the grim reaper and\n                the social worker.\n                ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.SPECIAL_CASES,
            tuning_filter=FilterTag.EXPERT_MODE),
        'elevated_importance':
        Tunable(
            description=
            '\n                If True, then filling this job with a Sim will be done before\n                filling similar jobs in this situation. This will matter when\n                starting a situation on another lot, when inviting a large number\n                of Sims, visiting commercial venues, or when at the cap on NPCs.\n                \n                Examples:\n                Wedding Situation: the Bethrothed Sims should be spawned before any guests.\n                Birthday Party: the Sims whose birthday it is should be spawned first.\n                Bar Venue: the Bartender should be spawned before the barflies.\n                \n                ',
            tunable_type=bool,
            default=False,
            tuning_group=GroupNames.SPECIAL_CASES,
            tuning_filter=FilterTag.EXPERT_MODE),
        'no_show_action':
        TunableEnumEntry(
            situations.situation_types.JobHolderNoShowAction,
            default=situations.situation_types.JobHolderNoShowAction.
            DO_NOTHING,
            description=
            "\n                                The action to take if no sim shows up to fill this job.\n                                \n                                Examples: \n                                If your usual maid doesn't show up, you want another one (REPLACE_THEM).\n                                If one of your party guests doesn't show up, you don't care (DO_NOTHING)\n                                If the President of the United States doesn't show up for the inauguration, you are hosed (END_SITUATION)\n                                ",
            tuning_group=GroupNames.SPECIAL_CASES),
        'died_or_left_action':
        TunableEnumEntry(
            situations.situation_types.JobHolderDiedOrLeftAction,
            default=situations.situation_types.JobHolderDiedOrLeftAction.
            DO_NOTHING,
            description=
            "\n                                    The action to take if a sim in this job dies or leaves the lot.\n                                    \n                                    Examples: \n                                    If the bartender leaves the ambient bar situation, you need a new one (REPLACE_THEM)\n                                    If your creepy uncle leaves the wedding, you don't care (DO_NOTHING)\n                                    If your maid dies cleaning the iron maiden, you are out of luck for today (END_SITUATION).\n                                    \n                                    NB: Do not use REPLACE_THEM if you are using Sim Churn for this job.\n                                    ",
            tuning_group=GroupNames.SPECIAL_CASES),
        'sim_spawner_tags':
        TunableList(
            description=
            '\n            A list of tags that represent where to spawn Sims for this Job when they come onto the lot.\n            NOTE: Tags will be searched in order of tuning. Tag [0] has priority over Tag [1] and so on.\n            ',
            tunable=TunableEnumEntry(tunable_type=Tag, default=Tag.INVALID)),
        'sim_spawn_action':
        TunableSpawnActionVariant(
            description=
            '\n            Define the methods to show the Sim after spawning on the lot.\n            '
        ),
        'sim_spawner_leave_option':
        TunableEnumEntry(
            description=
            '\n            The method for selecting spawn points for sims that are\n            leaving the lot. \n            \n            TUNED CONSTRAINT TAGS come from the SpawnPointConstraint.\n            SAVED TAGS are the same tags that were used to spawn the sim. \n            SAME vs DIFFERENT vs ANY resolves how to use the spawn point\n            that was saved when the sim entered the lot.\n            ',
            tunable_type=SpawnPointOption,
            default=SpawnPointOption.SPAWN_SAME_POINT),
        'emotional_setup':
        TunableList(
            description=
            '\n                Apply the WeightedSingleSimLootActions on the sim that is assigned this job. These are applied\n                only on NPC sims since the tuning is forcing changes to emotions.\n                \n                E.g. an angry mob at the bar, flirty guests at a wedding party.\n                ',
            tunable=TunableTuple(
                single_sim_loot_actions=WeightedSingleSimLootActions.
                TunableReference(),
                weight=Tunable(
                    int, 1, description='Accompanying weight of the loot.')),
            tuning_group=GroupNames.ON_CREATION),
        'commodities':
        TunableList(
            description=
            '\n                Update the commodities on the sim that is assigned this job. These are applied only on\n                NPC sims since the tuning is forcing changes to statistics that have player facing effects.\n             \n                E.g. The students arrive at the lecture hall with the bored and sleepy commodities.\n                ',
            tunable=TunableStatisticChange(
                locked_args={
                    'subject': ParticipantType.Actor,
                    'advertise': False,
                    'chance': 1,
                    'tests': None
                }),
            tuning_group=GroupNames.ON_CREATION),
        'requirement_text':
        TunableLocalizedString(
            description=
            '\n                A string that will be displayed in the sim picker for this\n                job in the situation window.\n                '
        ),
        'goodbye_notification':
        TunableVariant(
            description=
            '\n                The "goodbye" notification that will be set on Sims with this\n                situation job. This notification will be displayed when the\n                Sim leaves the lot (unless it gets overridden later).\n                Examples: the visitor job sets the "goodbye" notification to\n                something so the player knows when visitors leave; the party\n                guest roles use "No Notification", because we don\'t want 20-odd\n                notifications when a party ends; the leave lot jobs use "Use\n                Previous Notification" because we want leaving Sims to display\n                whatever notification was set earlier.\n                ',
            notification=TunableUiDialogNotificationSnippet(),
            locked_args={
                'no_notification':
                None,
                'never_use_notification_no_matter_what':
                SetGoodbyeNotificationElement.
                NEVER_USE_NOTIFICATION_NO_MATTER_WHAT,
                'use_previous_notification':
                DEFAULT
            },
            default='no_notification'),
        'additional_filter_for_user_selection':
        TunableReference(
            description=
            '\n                An additional filter that will run for the situation job if\n                there should be specific additional requirements for selecting\n                specific sims for the role rather than hiring them.\n                ',
            manager=services.get_instance_manager(
                sims4.resources.Types.SIM_FILTER),
            needs_tuning=True),
        'recommended_objects':
        TunableList(
            description=
            '\n                A list of objects that are recommended to be on a lot to get\n                the most out of this job\n                ',
            tunable=TunableVenueObject(
                description=
                "\n                        Specify object tag(s) that should be on this lot.\n                        Allows you to group objects, i.e. weight bench,\n                        treadmill, and basketball goals are tagged as\n                        'exercise objects.'\n                        "
            ),
            export_modes=ExportModes.All)
    }

    @classmethod
    def _verify_tuning_callback(cls):
        messages = []
        if cls.died_or_left_action == situations.situation_types.JobHolderDiedOrLeftAction.REPLACE_THEM:
            messages.append('Died Or Left Action == REPLACE_THEM')
        if cls.churn is not None:
            messages.append('Sim Churn')
        if cls.sim_shifts is not None:
            messages.append('Sim Shifts')
        if len(messages) > 1:
            message = ', and '.join(messages)
            logger.error('Situation job :{} must use only one of {}', cls,
                         message)

    @classmethod
    def get_score(cls, event, resolver, **kwargs):
        if event == event_testing.test_variants.TestEvent.InteractionComplete:
            for score_list in cls.interaction_scoring:
                while resolver(score_list.affordance_list):
                    return score_list.score
        elif event == event_testing.test_variants.TestEvent.ItemCrafted:
            for score_list in cls.crafted_object_scoring:
                while resolver(score_list.object_list):
                    return score_list.score
        return 0

    @classmethod
    def get_goal_score(cls):
        return cls.goal_scoring

    @classmethod
    def get_auto_invite(cls):
        if cls.churn is not None:
            interval = cls.churn.get_auto_populate_interval()
        else:
            if cls.sim_shifts is not None:
                return cls.sim_shifts.get_shift_staffing()
            if cls.sim_auto_invite.upper_bound > 0:
                interval = AutoPopulateInterval(
                    min=cls.sim_auto_invite.lower_bound,
                    max=cls.sim_auto_invite.upper_bound)
            else:
                return 0
        auto_invite = random.randrange(interval.min, interval.max + 1)
        return auto_invite

    @classmethod
    def can_sim_be_given_job(cls, sim_id, requesting_sim_info):
        if cls.filter is None:
            return True
        household_id = 0
        if requesting_sim_info is not None:
            household_id = requesting_sim_info.household.id
        return services.sim_filter_service().does_sim_match_filter(
            sim_id,
            sim_filter=cls.filter,
            requesting_sim_info=requesting_sim_info,
            household_id=household_id)
예제 #11
0
class PeriodicStatisticChangeElement(HasTunableFactory,
                                     elements.SubclassableGeneratorElement):
    __qualname__ = 'PeriodicStatisticChangeElement'
    FACTORY_TUNABLES = {
        'operations':
        TunableList(
            description=
            '\n            A list of statistic operations that occur at each interval.\n            ',
            tunable=TunableStatisticChange(
                dynamic_skill=DynamicSkillLootOp.TunableFactory(
                    locked_args={
                        'chance': 1,
                        'advertise': False
                    }),
                locked_args={'chance': 1},
                gain_type=GAIN_TYPE_RATE)),
        'operation_actions':
        TunableTuple(
            actions=TunableList(
                description=
                '\n                A list of actions that occur at each interval\n                ',
                tunable=TunableReference(
                    manager=services.
                    get_instance_manager(
                        sims4.resources.
                        Types.ACTION),
                    class_restrictions=('LootActions', ),
                    reload_dependent=
                    True)),
            alarm_interval=Tunable(
                description=
                '\n                Interval in sim minutes that applies operations if operation is\n                not a statistic change operation on a continious statistic and\n                not a skill loot operation.\n                \n                Example: If buff loot is in the operation list and this\n                is set to 5.  Loot op will try to be applied every 5 sim minutes\n                the sim is in the interaction.\n                Another example when this is used:\n                 - Statistic Change Op on statistic_GameForever\n                \n                If there is a statistic in operation is continuous the alarm\n                interval is not used, examples are below.\n                 - Statistic Change Op on motive_hunger\n                 - Dynamic skill on statistic_Skill_AdultMajor_Piano\n                ',
                tunable_type=int,
                default=1)),
        'trigger_gain_on_start':
        Tunable(
            description=
            '\n            If checked then we will trigger a statistic gain when we start the\n            statistic gains.  This is to make sure that things like upgrades\n            will have some statistic progress when the sim is charged for the\n            upgrade instead of loosing the payment.\n            ',
            tunable_type=bool,
            default=False)
    }

    def __init__(self,
                 interaction,
                 operations,
                 operation_actions,
                 trigger_gain_on_start=False,
                 sequence=DEFAULT):
        super().__init__()
        self._interaction = interaction
        self._basic_content_operations = operations
        if operation_actions is not None:
            self._loot_operations = operation_actions.actions
            self._alarm_interval = operation_actions.alarm_interval
        else:
            self._loot_operations = None
            self._alarm_interval = StatisticOperation.STATIC_CHANGE_INTERVAL
        self._alarm_handle = None
        if sequence is DEFAULT:
            sequence = soft_sleep_forever()
        self._sequence = sequence
        self._autonomy_modifiers = weakref.WeakKeyDictionary()
        self._operations_on_alarm = []
        self._change_helper = None
        self._trigger_gain_on_start = trigger_gain_on_start

    def transfer_operation_to_modifier(self, op, autonomy_modifiers):
        self._operations_on_alarm.remove(op)
        for (sim, autonomy_modifier) in autonomy_modifiers.items():
            self._add_autonomy_modifier_to_sim(sim, autonomy_modifier)

    def _add_operation_if_valid(self,
                                resolver,
                                loot_op,
                                periodic_mods_by_participant,
                                exclusive_mods_by_participant,
                                skip_test=False):
        is_dynamic_skill_loot_op = isinstance(loot_op, DynamicSkillLootOp)
        is_exclusive = hasattr(
            loot_op,
            'exclusive_to_owning_si') and loot_op.exclusive_to_owning_si
        stat = loot_op.get_stat(self._interaction)
        if stat is None or not stat.continuous or not is_dynamic_skill_loot_op and not isinstance(
                loot_op, StatisticChangeOp):
            self._operations_on_alarm.append(loot_op)
            return
        if not skip_test and not loot_op.test_resolver(resolver):
            return
        if is_dynamic_skill_loot_op:
            inv_interval = 1 / Skill.DYNAMIC_SKILL_INTERVAL
        else:
            inv_interval = 1
        participants = self._interaction.get_participants(loot_op.subject)
        actor = self._interaction.get_participant(ParticipantType.Actor)
        sims = set()
        sims.add(actor)
        for participant in participants:
            while participant.is_sim:
                sims.add(participant)
        for participant in participants:
            mod_per_sec = loot_op.get_value(obj=participant,
                                            interaction=self._interaction,
                                            sims=sims)
            mod_per_sec *= inv_interval
            if is_exclusive and participant.is_sim:
                self._add_participant_and_mod_to_dict(
                    participant, stat, mod_per_sec,
                    exclusive_mods_by_participant)
            else:
                self._add_participant_and_mod_to_dict(
                    participant, stat, mod_per_sec,
                    periodic_mods_by_participant)

    def _add_participant_and_mod_to_dict(self, participant, stat, mod_per_sec,
                                         mods_by_participant_dict):
        if participant not in mods_by_participant_dict:
            mods_by_participant_dict[participant] = {}
        if stat not in mods_by_participant_dict[participant]:
            mods_by_participant_dict[participant][stat] = 0
        mods_by_participant_dict[participant][stat] += mod_per_sec

    def _start_statistic_gains(self):
        self._end_statistic_gains()
        periodic_mods_by_participant = {}
        exclusive_mods_by_participant = {}
        interaction_resolver = self._interaction.get_resolver()
        if self._basic_content_operations:
            for stat_op in self._basic_content_operations:
                self._add_operation_if_valid(interaction_resolver, stat_op,
                                             periodic_mods_by_participant,
                                             exclusive_mods_by_participant)
        if self._loot_operations:
            for loot in self._loot_operations:
                for (loot_op, test_ran) in loot.get_loot_ops_gen(
                        resolver=interaction_resolver):
                    self._add_operation_if_valid(interaction_resolver,
                                                 loot_op,
                                                 periodic_mods_by_participant,
                                                 exclusive_mods_by_participant,
                                                 skip_test=test_ran)
        self._create_and_add_autonomy_modifier(periodic_mods_by_participant)
        si = self._interaction if self._interaction.is_super else self._interaction.super_interaction
        self._create_and_add_autonomy_modifier(exclusive_mods_by_participant,
                                               si)
        self._change_helper = StatisticChangeHelper(self._interaction,
                                                    self._operations_on_alarm,
                                                    self)
        result = False
        if self._change_helper is not None:
            time_span = clock.interval_in_sim_minutes(self._alarm_interval)
            self._alarm_handle = alarms.add_alarm(self,
                                                  time_span,
                                                  self._do_gain,
                                                  repeating=True)
            if self._trigger_gain_on_start:
                self._apply_all_valid_ops(interaction_resolver)
            result = True
        return result

    def _create_and_add_autonomy_modifier(self,
                                          mods_by_participant_dict,
                                          exclusive_si=None):
        for (participant, mods) in mods_by_participant_dict.items():
            while hasattr(participant, 'add_statistic_modifier'):
                autonomy_modifier = AutonomyModifier(statistic_modifiers=mods,
                                                     exclusive_si=exclusive_si)
                self._add_autonomy_modifier_to_sim(participant,
                                                   autonomy_modifier)

    def _end_statistic_gains(self):
        for (participant, handle_list) in self._autonomy_modifiers.items():
            while participant is not None:
                while True:
                    for handle in handle_list:
                        participant.remove_statistic_modifier(handle)
        if self._alarm_handle is not None:
            alarms.cancel_alarm(self._alarm_handle)
            self._alarm_handle = None
        self._change_helper = None

    def _add_autonomy_modifier_to_sim(self, sim, autonomy_modifier):
        if sim not in self._autonomy_modifiers:
            self._autonomy_modifiers[sim] = []
        handle = sim.add_statistic_modifier(autonomy_modifier)
        self._autonomy_modifiers[sim].append(handle)

    def _apply_all_valid_ops(self, interaction_resolver):
        if self._basic_content_operations:
            for stat_op in self._basic_content_operations:
                stat_op.apply_to_resolver(interaction_resolver)
        if self._loot_operations:
            for loot in self._loot_operations:
                for (loot_op, _) in loot.get_loot_ops_gen(
                        resolver=interaction_resolver):
                    loot_op.apply_to_resolver(interaction_resolver)

    def _do_gain(self, _):
        self._change_helper.apply()

    @property
    def _sim(self):
        return self._interaction.sim

    def _run_gen(self, timeline):
        try:
            self._start_statistic_gains()
            result = yield element_utils.run_child(timeline, self._sequence)
            return result
        finally:
            self._end_statistic_gains()