def _get_role_state_overrides(self, sim, job_type, role_state_type,
                               role_affordance_target):
     if self.owner._crafted_object_id != 0:
         target = services.current_zone().inventory_manager.get(
             self.owner._crafted_object_id)
     else:
         obj_def_to_craft = self.owner._object_definition_to_craft
         if obj_def_to_craft == 0:
             possible_recipes = self.owner._service_npc_type.recipe_picker_on_hire.recipes
             if possible_recipes is None:
                 raise ValueError('No recipe for {}'.format(self))
             recipe = random.choice(possible_recipes)
         else:
             recipe = services.recipe_manager().get(obj_def_to_craft)
             if recipe is None:
                 raise ValueError('No recipe for {}'.format(self))
         target = DebugCreateCraftableInteraction.create_craftable(
             recipe,
             self.owner._service_npc,
             owning_household_id_override=self.owner._hiring_household.id,
             place_in_crafter_inventory=True)
         if target is None:
             raise ValueError('No craftable created for {} on {}'.format(
                 recipe, self))
         self.owner._crafted_object_id = target.id
     return (role_state_type, target)
 def on_service_sim_entered_situation(cls, service_sim, situation):
     if situation.role_affordance_target is None:
         recipe = services.recipe_manager().get(situation.object_definition_to_craft)
         if recipe is None:
             return False
         craftable = DebugCreateCraftableInteraction.create_craftable(recipe, service_sim, owning_household_id_override=situation.hiring_household.id, place_in_crafter_inventory=True)
         if craftable is None:
             return False
         situation.set_crafted_object_id(craftable.id)
     return True
 def _get_role_state_overrides(self, sim, job_type, role_state_type, role_affordance_target):
     if self.owner._crafted_object_id != 0:
         target = services.current_zone().inventory_manager.get(self.owner._crafted_object_id)
     else:
         recipe = services.recipe_manager().get(self.owner._object_definition_to_craft)
         if recipe is None:
             raise ValueError('No recipe for {}'.format(self))
         target = DebugCreateCraftableInteraction.create_craftable(recipe, self.owner._service_npc, owning_household_id_override=self.owner._hiring_household.id, place_in_crafter_inventory=True)
         if target is None:
             raise ValueError('No craftable created for {} on {}'.format(recipe, self))
         self.owner._crafted_object_id = target.id
     return (role_state_type, target)
Example #4
0
 def on_service_sim_entered_situation(cls, service_sim, situation):
     if situation.role_affordance_target is None:
         recipe = services.recipe_manager().get(
             situation.object_definition_to_craft)
         if recipe is None:
             return False
         craftable = DebugCreateCraftableInteraction.create_craftable(
             recipe,
             service_sim,
             owning_household_id_override=situation.hiring_household.id,
             place_in_crafter_inventory=True)
         if craftable is None:
             return False
         situation.set_crafted_object_id(craftable.id)
     return True
class RestaurantCourseItemCountTest(HasTunableSingletonFactory,
                                    AutoFactoryInit, test_base.BaseTest):
    FACTORY_TUNABLES = {
        'course':
        TunableEnumWithFilter(
            description=
            '\n            The course to check for this test.\n            ',
            tunable_type=Tag,
            filter_prefixes=['recipe_course'],
            default=Tag.INVALID,
            invalid_enums=(Tag.INVALID, ),
            pack_safe=True),
        'threshold':
        TunableThreshold(
            description=
            '\n            The number of items that should available in this course.\n            '
        ),
        'blacklist_recipes':
        TunableList(
            description=
            '\n            The items from the course to not include in this test.\n            ',
            tunable=TunableReference(manager=services.recipe_manager(),
                                     class_restrictions=('Recipe', ),
                                     pack_safe=True))
    }

    def get_expected_args(self):
        return {}

    def __call__(self):
        zone_director = get_restaurant_zone_director()
        if zone_director is None:
            return TestResult(
                False,
                'Want to test restaurant course item count but not in a restaurant.',
                tooltip=self.tooltip)
        item_count = len([
            recipe for recipe in zone_director.get_menu_for_course(self.course)
            if recipe not in self.blacklist_recipes
        ])
        if not self.threshold.compare(item_count):
            return TestResult(False,
                              'Only {} items in {}'.format(
                                  item_count, self.course),
                              tooltip=self.tooltip)
        return TestResult.TRUE
Example #6
0
    class RecipeDefinition(HasTunableSingletonFactory, AutoFactoryInit):
        __qualname__ = 'ObjectCreationElement.RecipeDefinition'
        FACTORY_TUNABLES = {
            'recipe':
            TunableReference(
                description=
                '\n                The recipe to use to create the object.\n                ',
                manager=services.recipe_manager())
        }

        def get_definition(self):
            return self.recipe.final_product.definition

        def setup_created_object(self, interaction, created_object):
            from crafting.crafting_process import CraftingProcess
            crafting_process = CraftingProcess(crafter=interaction.sim,
                                               recipe=self.recipe)
            crafting_process.setup_crafted_object(created_object,
                                                  is_final_product=True)
Example #7
0
class RestaurantTuning:
    MENU_PRESETS = TunableMapping(
        description=
        '\n        The map to tune preset of menus that player to select to use in\n        restaurant customization.\n        ',
        key_type=TunableEnumEntry(tunable_type=MenuPresets,
                                  default=MenuPresets.CUSTOMIZE,
                                  binary_type=EnumBinaryExportType.EnumUint32),
        value_type=TunableTuple(
            description='\n            Menu preset contents.\n            ',
            preset_name=TunableLocalizedString(
                description=
                '\n                Menu preset name that appear in both menu customize UI and in\n                game menu UI.\n                '
            ),
            recipe_map=TunableMapping(
                description=
                "\n                The map that represent a menu preset. It's organized with courses\n                like drink, appetizer, entree etc, and in each course there are\n                options of recipes.\n                ",
                key_type=TunableEnumWithFilter(
                    tunable_type=Tag,
                    filter_prefixes=['recipe_course'],
                    default=Tag.INVALID,
                    invalid_enums=(Tag.INVALID, ),
                    pack_safe=True,
                    binary_type=EnumBinaryExportType.EnumUint32),
                value_type=TunableSet(
                    tunable=TunableReference(manager=services.recipe_manager(),
                                             class_restrictions=('Recipe', ),
                                             pack_safe=True)),
                key_name='course_tags',
                value_name='recipes',
                tuple_name='MenuCourseMappingTuple'),
            show_in_restaurant_menu=Tunable(
                description=
                "\n                If this is enabled, this menu preset will show up on restaurant\n                menus. If not, it won't. Currently, only home-chef menus\n                shouldn't show up on restaurant menus.\n                ",
                tunable_type=bool,
                default=True),
            export_class_name='MenuPresetContentTuple'),
        key_name='preset_enum',
        value_name='preset_contents',
        tuple_name='MenuPresetMappingTuple',
        export_modes=ExportModes.All)
    MENU_TAG_DISPLAY_CONTENTS = TunableMapping(
        description=
        '\n        The map to tune menu tags to display contents.\n        ',
        key_type=TunableEnumWithFilter(
            tunable_type=Tag,
            filter_prefixes=['recipe'],
            default=Tag.INVALID,
            invalid_enums=(Tag.INVALID, ),
            pack_safe=True,
            binary_type=EnumBinaryExportType.EnumUint32),
        value_type=TunableTuple(
            description=
            '\n            menu tag display contents.\n            ',
            menu_tag_name=TunableLocalizedString(),
            menu_tag_icon=TunableResourceKey(
                description=
                '\n                This will display as the filter icon in the course recipe picker UI.\n                ',
                resource_types=sims4.resources.CompoundTypes.IMAGE),
            export_class_name='MenuTagDisplayTuple'),
        key_name='menu_tags',
        value_name='menu_tag_display_contents',
        tuple_name='MenuTagDisplayMappingTuple',
        export_modes=ExportModes.ClientBinary)
    COURSE_SORTING_SEQUENCE = TunableSet(
        description=
        '\n        This set determines the sorting sequence for courses in both menu\n        customize UI and in game menu UI.\n        ',
        tunable=TunableEnumWithFilter(
            tunable_type=Tag,
            filter_prefixes=['recipe_course'],
            default=Tag.INVALID,
            invalid_enums=(Tag.INVALID, ),
            pack_safe=True,
            binary_type=EnumBinaryExportType.EnumUint32),
        export_modes=ExportModes.ClientBinary)
    DAILY_SPECIAL_DISCOUNT = TunablePercent(
        description=
        '\n        The percentage of the base price when an item is the daily special.\n        For example, if the base price is $10 and this is tuned to 80%, the\n        discounted price will be $10 x 80% = $8\n        ',
        default=80)
    INVALID_DAILY_SPECIAL_RECIPES = TunableList(
        description=
        '\n        A list of recipes that should not be considered for daily specials.\n        i.e. Glass of water.\n        ',
        tunable=TunableReference(
            description=
            '\n            The recipe to disallow from being a daily special.\n            ',
            manager=services.recipe_manager(),
            class_restrictions=('Recipe', ),
            pack_safe=True))
    COURSE_TO_FILTER_TAGS_MAPPING = TunableMapping(
        description=
        '\n        Mapping from course to filter tags for food picker UI.\n        ',
        key_type=TunableEnumWithFilter(
            description=
            '\n            The course associated with the list of filters.\n            ',
            tunable_type=Tag,
            filter_prefixes=['recipe_course'],
            default=Tag.INVALID,
            invalid_enums=(Tag.INVALID, ),
            pack_safe=True,
            binary_type=EnumBinaryExportType.EnumUint32),
        value_type=TunableList(
            description=
            '\n            This list of filter tags for the food picker UI for the course\n            specified.\n            ',
            tunable=TunableEnumWithFilter(
                tunable_type=Tag,
                filter_prefixes=['recipe_category'],
                default=Tag.INVALID,
                invalid_enums=(Tag.INVALID, ),
                pack_safe=True,
                binary_type=EnumBinaryExportType.EnumUint32)),
        key_name='course_key',
        value_name='course_filter_tags',
        tuple_name='CourseToFilterTuple',
        export_modes=ExportModes.ClientBinary)
    CUSTOMER_QUALITY_STAT = TunablePackSafeReference(
        description=
        '\n        The Customer Quality stat applied to food/drink the restaurant customer\n        eats/drinks. This is how we apply buffs to the Sim at the time they\n        consume the food/drink.\n        \n        The Customer Quality value is determined by multiplying the Final\n        Quality To Customer Quality Multiplier (found in Final Quality State\n        Data Mapping) by the Food Difficulty To Customer Quality Multiplier\n        (found in the Ingredient Quality State Data Mapping).\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.STATISTIC))
    CUSTOMER_VALUE_STAT = TunablePackSafeReference(
        description=
        '\n        The Customer Value stat applied to food/drink the restaurant customer\n        eats/drinks. This is how we apply buffs to the Sim at the time they\n        consume the food/drink.\n        \n        The Customer Value value is determined by multiplying the Final Quality\n        To Customer Value Multiplier (found in Final Quality State Data Mapping)\n        by the Markup To Customer Value Multiplier (found in the Markup Data\n        Mapping).\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.STATISTIC))
    RECIPE_DIFFICULTY_DATA_MAPPING = TunableMapping(
        description=
        '\n        A mapping of the recipe difficulty for restaurants to the appropriate\n        data.\n        ',
        key_name='recipe_difficulty',
        key_type=TunableEnumEntry(
            description=
            "\n            The recipe difficulty for chef's at a restaurant.\n            ",
            tunable_type=RecipeDifficulty,
            default=RecipeDifficulty.NORMAL),
        value_name='recipe_difficulty_data',
        value_type=TunableTuple(
            description=
            '\n            The tuning associated with the provided recipe difficulty.\n            ',
            recipe_difficulty_to_final_quality_adder=Tunable(
                description=
                '\n                This value is added to the Ingredient Quality To Final Quality Adder\n                and the Cooking Speed To Final Quality Adder to determine the player-\n                facing recipe quality.\n                ',
                tunable_type=float,
                default=0),
            recipe_difficulty_to_customer_quality_multiplier=Tunable(
                description=
                "\n                This value is multiplied by the Final Quality To Customer\n                Quality Multiplier to determine the customer's perceived quality\n                of the recipe.\n                ",
                tunable_type=float,
                default=1)))
    DEFAULT_INGREDIENT_QUALITY = TunableEnumEntry(
        description=
        '\n        The default ingredient quality for a restaurant.\n        ',
        tunable_type=RestaurantIngredientQualityType,
        default=RestaurantIngredientQualityType.INVALID,
        invalid_enums=(RestaurantIngredientQualityType.INVALID, ))
    INGREDIENT_QUALITY_DATA_MAPPING = TunableMapping(
        description=
        '\n        The mapping between ingredient enum and the ingredient data for\n        that type.\n        ',
        key_type=TunableEnumEntry(
            description=
            '\n            The ingredient type. Organic, normal, lousy, etc...\n            ',
            tunable_type=RestaurantIngredientQualityType,
            default=RestaurantIngredientQualityType.INVALID,
            invalid_enums=(RestaurantIngredientQualityType.INVALID, ),
            binary_type=EnumBinaryExportType.EnumUint32),
        value_type=TunableTuple(
            description=
            '\n            Data associated with this type of ingredient.\n            ',
            ingredient_quality_type_name=TunableLocalizedString(
                description=
                '\n                The localized name of this ingredient used in various places in\n                the UI.\n                '
            ),
            ingredient_quality_to_final_quality_adder=Tunable(
                description=
                '\n                This value is added to the Recipe Difficulty To Final Quality\n                Adder and the Cooking Speed To Final Quality Adder to determine\n                the player-facing recipe quality.\n                ',
                tunable_type=float,
                default=0),
            ingredient_quality_to_restaurant_expense_multiplier=TunableRange(
                description=
                '\n                This value is multiplied by the Base Restaurant Price (found in\n                the Recipe tuning) for each recipe served to determine what the\n                cost is to the restaurant for preparing that recipe.\n                ',
                tunable_type=float,
                default=0.5,
                minimum=0),
            export_class_name='IngredientDataTuple'),
        key_name='ingredient_enum',
        value_name='ingredient_data',
        tuple_name='IngredientEnumDataMappingTuple',
        export_modes=ExportModes.All)
    COOKING_SPEED_DATA_MAPPING = TunableMapping(
        description=
        '\n        A mapping from chef cooking speed to the data associated with that\n        cooking speed.\n        ',
        key_name='cooking_speed_buff',
        key_type=TunableReference(
            description=
            '\n            The cooking speed buff that is applied to the chef.\n            ',
            manager=services.get_instance_manager(sims4.resources.Types.BUFF),
            pack_safe=True),
        value_name='cooking_speed_data',
        value_type=TunableTuple(
            description=
            '\n            The data associated with the tuned cooking speed.\n            ',
            cooking_speed_to_final_quality_adder=Tunable(
                description=
                '\n                This value is added to the Recipe Difficulty To Final Quality\n                Adder and the Ingredient Quality To Final Quality Adder to\n                determine the player-facing recipe quality.\n                ',
                tunable_type=float,
                default=0),
            active_cooking_states_delta=Tunable(
                description=
                '\n                The amount by which to adjust the number of active cooking\n                states the chef must complete before completing the order. For\n                instance, if a -1 is tuned here, the chef will have to complete\n                one less state than normal. Regardless of how the buffs are\n                tuned, the chef will always run at least one state before\n                completing the order.\n                ',
                tunable_type=int,
                default=-1)))
    CHEF_SKILL_TO_FOOD_FINAL_QUALITY_ADDER_DATA = TunableTuple(
        description=
        '\n        Pairs a skill with a curve to determine the additional value to add to\n        the final quality of a food made at an owned restaurant.\n        ',
        skill=TunablePackSafeReference(
            description=
            '\n            The skill used to determine the adder for the final quality of food.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.STATISTIC),
            class_restrictions=('Skill', )),
        final_quality_adder_curve=TunableCurve(
            description=
            "\n            Maps the chef's current level of the tuned skill to a value that\n            will be added to the final quality statistic for food recipes cooked\n            at an owned restaurant.\n            ",
            x_axis_name='Skill Level',
            y_axis_name='Food Final Quality Adder'))
    CHEF_SKILL_TO_DRINK_FINAL_QUALITY_ADDER_DATA = TunableTuple(
        description=
        '\n        Pairs a skill with a curve to determine the additional value to add to\n        the final quality of a drink made at an owned restaurant.\n        ',
        skill=TunablePackSafeReference(
            description=
            '\n            The skill used to determine the adder for the final quality of drinks.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.STATISTIC),
            class_restrictions=('Skill', )),
        final_quality_adder_curve=TunableCurve(
            description=
            "\n            Maps the chef's current level of the tuned skill to a value that\n            will be added to the final quality statistic for drink recipes\n            cooked at an owned restaurant.\n            ",
            x_axis_name='Skill Level',
            y_axis_name='Food Final Quality Adder'))
    FINAL_QUALITY_STATE_DATA_MAPPING = TunableMapping(
        description=
        '\n        A mapping of final quality recipe states (Poor, Normal, Outstanding) to\n        the data associated with that recipe quality.\n        ',
        key_name='recipe_quality_state',
        key_type=TunableReference(
            description=
            '\n            The recipe quality state value.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.OBJECT_STATE),
            class_restrictions='ObjectStateValue',
            pack_safe=True),
        value_name='recipe_quality_state_value_data',
        value_type=TunableTuple(
            description=
            '\n            The data associated with the tuned recipe quality state value.\n            ',
            final_quality_to_customer_quality_multiplier=Tunable(
                description=
                '\n                This value is multiplied by the Recipe Difficulty To Customer\n                Quality Multiplier to determine the Customer Quality State value\n                of the recipe.\n                ',
                tunable_type=float,
                default=1),
            final_quality_to_customer_value_multiplier=Tunable(
                description=
                '\n                This value is multiplied by the Markup To Customer Value\n                Multiplier to determine the value of the Customer Value Stat\n                value of the recipe.\n                ',
                tunable_type=float,
                default=1)))
    PRICE_MARKUP_DATA_MAPPING = TunableMapping(
        description=
        '\n        A mapping of the current price markup of the restaurant to the data\n        associated with that markup.\n        ',
        key_name='markup_multiplier',
        key_type=Tunable(
            description=
            '\n            The markup multiplier. this needs to be in line with the available\n            markups tuned on the restaurant business.\n            ',
            tunable_type=float,
            default=1.5),
        value_name='markup_multiplier_data',
        value_type=TunableTuple(
            description=
            '\n            The data associated with the tuned markup multiplier.\n            ',
            markup_to_customer_value_multiplier=Tunable(
                description='\n                ',
                tunable_type=float,
                default=1)))
    BUSINESS_FUNDS_CATEGORY_FOR_COST_OF_INGREDIENTS = TunableEnumEntry(
        description=
        '\n        When a Chef cooks an order, the restaurant has to pay for the\n        ingredients. This is the category for those expenses.\n        ',
        tunable_type=BusinessFundsCategory,
        default=BusinessFundsCategory.NONE,
        invalid_enums=(BusinessFundsCategory.NONE, ))
    ATTIRE = TunableList(
        description=
        '\n        List of attires player can select to apply to the restaurant.\n        ',
        tunable=TunableEnumEntry(tunable_type=OutfitCategory,
                                 default=OutfitCategory.EVERYDAY,
                                 binary_type=EnumBinaryExportType.EnumUint32),
        export_modes=ExportModes.All)
    UNIFORM_CHEF_MALE = TunablePackSafeResourceKey(
        description=
        '\n        The SimInfo file to use to edit male chef uniforms.\n        ',
        default=None,
        resource_types=(sims4.resources.Types.SIMINFO, ),
        export_modes=ExportModes.All)
    UNIFORM_CHEF_FEMALE = TunablePackSafeResourceKey(
        description=
        '\n        The SimInfo file to use to edit female chef uniforms.\n        ',
        default=None,
        resource_types=(sims4.resources.Types.SIMINFO, ),
        export_modes=ExportModes.All)
    UNIFORM_WAITSTAFF_MALE = TunablePackSafeResourceKey(
        description=
        '\n        The SimInfo file to use to edit waiter uniforms.\n        ',
        default=None,
        resource_types=(sims4.resources.Types.SIMINFO, ),
        export_modes=ExportModes.All)
    UNIFORM_WAITSTAFF_FEMALE = TunablePackSafeResourceKey(
        description=
        '\n        The SimInfo file to use to edit waitress uniforms.\n        ',
        default=None,
        resource_types=(sims4.resources.Types.SIMINFO, ),
        export_modes=ExportModes.All)
    UNIFORM_HOST_MALE = TunablePackSafeResourceKey(
        description=
        '\n        The SimInfo file to use to edit male host uniforms.\n        ',
        default=None,
        resource_types=(sims4.resources.Types.SIMINFO, ),
        export_modes=ExportModes.All)
    UNIFORM_HOST_FEMALE = TunablePackSafeResourceKey(
        description=
        '\n        The SimInfo file to use to edit female host uniforms.\n        ',
        default=None,
        resource_types=(sims4.resources.Types.SIMINFO, ),
        export_modes=ExportModes.All)
    RESTAURANT_VENUE = TunablePackSafeReference(
        description=
        '\n        This is a tunable reference to the type of Venue that will describe\n        a Restaurant. To be used for code references to restaurant venue types\n        in code.\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.VENUE))
    HOST_SITUATION = TunablePackSafeReference(
        description=
        '\n        The situation that Sims working as a Host will have.\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.SITUATION))
    WAITSTAFF_SITUATION = TunablePackSafeReference(
        description=
        '\n        The situation that Sims working as a Waiter will have.\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.SITUATION))
    CHEF_SITUATION = TunablePackSafeReference(
        description=
        '\n        The situation that Sims working as a Chef will have.\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.SITUATION))
    HOME_CHEF_SITUATION_TAG = TunableEnumWithFilter(
        description=
        '\n        Tag that we use on all the home chef situations.\n        ',
        tunable_type=Tag,
        filter_prefixes=['situation'],
        default=Tag.INVALID,
        invalid_enums=(Tag.INVALID, ),
        pack_safe=True)
    DINING_SITUATION_TAG = TunableEnumWithFilter(
        description=
        "\n        The tag used to find dining situations. \n        \n        This shouldn't need to be re-tuned after being set initially. If you\n        need to re-tune this you should probably talk to a GPE first.\n        ",
        tunable_type=Tag,
        filter_prefixes=['situation'],
        default=Tag.INVALID,
        pack_safe=True)
    TABLE_FOOD_SLOT_TYPE = TunableReference(
        description=
        '\n        The slot type of the food slot on the dining table.\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.SLOT_TYPE))
    TABLE_DRINK_SLOT_TYPE = TunableReference(
        description=
        '\n        The slot type of the drink slot on the dining table.\n        ',
        manager=services.get_instance_manager(sims4.resources.Types.SLOT_TYPE))
    FOOD_AUTONOMY_PREFERENCE = TunableAutonomyPreference(
        description=
        '\n        The Autonomy Preference for the delivered food items.\n        ',
        is_scoring=False)
    DRINK_AUTONOMY_PREFERENCE = TunableAutonomyPreference(
        description=
        '\n        The Autonomy Preference for the delivered drink items.\n        ',
        is_scoring=False)
    CONSUMABLE_FULL_STATE_VALUE = TunableReference(
        description=
        '\n        The Consumable_Full state value. Food in restaurants will be set to\n        this value instead of defaulting to Consumable_Untouched to avoid other\n        Sims from eating your own food.\n        ',
        manager=services.get_instance_manager(
            sims4.resources.Types.OBJECT_STATE),
        class_restrictions=('ObjectStateValue', ))
    CONSUMABLE_EMPTY_STATE_VALUE = TunableReference(
        description=
        "\n        The Consumable_Empty state value. This is the state we'll use to\n        determine if food/drink is empty or not.\n        ",
        manager=services.get_instance_manager(
            sims4.resources.Types.OBJECT_STATE),
        class_restrictions=('ObjectStateValue', ))
    FOOD_DELIVERED_TO_TABLE_NOTIFICATION = TunableUiDialogNotificationSnippet(
        description=
        "\n        The notification shown when the food is delivered to the player's table.\n        "
    )
    FOOD_STILL_ON_TABLE_NOTIFICATION = TunableUiDialogNotificationSnippet(
        description=
        "\n        The notification that the player will see if the waitstaff try and\n        deliver food but there's still food on the table.\n        "
    )
    STAND_UP_INTERACTION = TunableReference(
        description=
        '\n        A reference to sim-stand so that sim-stand can be pushed on every sim\n        that is sitting at a table that is abandoned.\n        ',
        manager=services.get_instance_manager(
            sims4.resources.Types.INTERACTION))
    DEFAULT_MENU = TunableEnumEntry(
        description=
        '\n        The default menu setting for a brand new restaurant.\n        ',
        tunable_type=MenuPresets,
        default=MenuPresets.CUSTOMIZE,
        export_modes=ExportModes.All,
        binary_type=EnumBinaryExportType.EnumUint32)
    SWITCH_SEAT_INTERACTION = TunableReference(
        description=
        '\n        This is a reference to the interaction that gets pushed on whichever Sim\n        is sitting in the seat that the Actor is switching to. The interaction \n        will be pushed onto the sseated Sim and will target the Actor Sims \n        current seat before the switch.\n        ',
        manager=services.get_instance_manager(
            sims4.resources.Types.INTERACTION))
    RECOMMENDED_ORDER_INTERACTION = TunableReference(
        description=
        '\n        This is a reference to the interaction that will get pushed on the active Sim\n        to recommend orders to the Sim AFTER the having gone through the Menu UI.\n        \n        It will continue to retain the previous target.\n        ',
        manager=services.get_instance_manager(
            sims4.resources.Types.INTERACTION),
        pack_safe=True)
    INGREDIENT_PRICE_PERK_MAP = TunableMapping(
        description=
        '\n        Maps the various ingredient price perks with their corresponding\n        discount.\n        ',
        key_name='Ingredient Price Perk',
        key_type=TunableReference(
            description=
            '\n            A perk that gives a tunable multiplier to the price of ingredients\n            for restaurants.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.BUCKS_PERK),
            pack_safe=True),
        value_name='Ingredient Price Multiplier',
        value_type=TunableRange(
            description=
            '\n            If the household has the corresponding perk, this value will be\n            multiplied by the final cost of each recipe to the restaurant.\n            ',
            tunable_type=float,
            default=1,
            minimum=0))
    CUSTOMERS_ORDER_EXPENSIVE_FOOD_PERK_DATA = TunableTuple(
        description=
        '\n        The perk that makes customers order more expensive food, and the off-lot\n        multiplier for that perk.\n        ',
        perk=TunablePackSafeReference(
            description=
            '\n            If the owning household has this perk, customers will pick two dishes to\n            order and then pick the most expensive of the two.\n            ',
            manager=services.get_instance_manager(
                sims4.resources.Types.BUCKS_PERK)),
        off_lot_multiplier=TunableRange(
            description=
            '\n            When calculating off-lot profits, this is applied if the household\n            has this perk.\n            ',
            tunable_type=float,
            default=1.1,
            minimum=1))
    UNOWNED_RESTAURANT_PRICE_MULTIPLIER = TunableRange(
        description=
        '\n        The amount each item in the menu will be multiplied by on unowned\n        restaurant lots.\n        ',
        tunable_type=float,
        default=1.2,
        minimum=0,
        export_modes=ExportModes.All)
    CHEF_NOT_SKILLED_ENOUGH_THRESHOLD = Tunable(
        description=
        '\n        This is the value that a chef must reach when preparing a meal for a\n        customer without displaying the "Chef isn\'t skilled enough to make \n        receiver X" \n        \n        The number that must reach this value is the skill adder\n        of the chef and recipe difficulty adder.\n        ',
        tunable_type=int,
        default=-30)
    CHEF_NOT_SKILLED_ENOUGH_NOTIFICATION = TunableUiDialogNotificationSnippet(
        description=
        '\n        The notification shown when the chef is working on a recipe that is \n        too difficult for their skill.\n        '
    )
    DEFAULT_PROFIT_PER_MEAL_FOR_OFF_LOT_SIMULATION = TunableRange(
        description=
        '\n        This is used as the default profit for a meal for off-lot simulation. Once\n        enough actual meals have been sold, this value becomes irrelevant and\n        the MEAL_COUNT_FOR_OFF_LOT_PROFIT_PER_MEAL tunable comes into use.\n        ',
        tunable_type=int,
        default=20,
        minimum=1)
    MEAL_COUNT_FOR_OFF_LOT_PROFIT_PER_MEAL = TunableRange(
        description=
        '\n        The number of meals to keep a running average of for the profit per meal\n        calculations during off lot simulations.\n        ',
        tunable_type=int,
        default=10,
        minimum=2)
    ADVERTISING_DATA_MAP = TunableMapping(
        description=
        '\n        The mapping between advertising type and the data for that type.\n        ',
        key_name='Advertising_Type',
        key_type=TunableEnumEntry(
            description='\n            The Advertising Type .\n            ',
            tunable_type=BusinessAdvertisingType,
            default=BusinessAdvertisingType.INVALID,
            invalid_enums=(BusinessAdvertisingType.INVALID, ),
            binary_type=EnumBinaryExportType.EnumUint32),
        value_name='Advertising_Data',
        value_type=TunableTuple(
            description=
            '\n            Data associated with this advertising type.\n            ',
            cost_per_hour=TunableRange(
                description=
                '\n                How much, per hour, it costs to use this advertising type.\n                ',
                tunable_type=int,
                default=10,
                minimum=0),
            customer_count_multiplier=TunableRange(
                description=
                '\n                This amount is multiplied by the ideal customer count for owned\n                restaurants.\n                ',
                tunable_type=float,
                default=0.8,
                minimum=0),
            ui_sort_order=TunableRange(
                description=
                '\n                Value representing how map entries will be sorted in the UI.\n                1 represents the first entry.  Avoid duplicate values\n                within the map.\n                ',
                tunable_type=int,
                minimum=1,
                default=1),
            export_class_name='RestaurantAdvertisingData'),
        tuple_name='RestaurantAdvertisingDataMapping',
        export_modes=ExportModes.All)
    TODDLER_SENT_TO_DAYCARE_FOR_RESTAURANTS = TunableUiDialogNotificationSnippet(
        description=
        '\n        The notification shown when a toddler is sent to daycare upon traveling\n        to a restaurant venue.\n        '
    )
    TIME_OF_DAY_TO_CUSTOMER_COUNT_MULTIPLIER_CURVE = TunableCurve(
        description=
        '\n        A curve that lets you tune a specific customer count multiplier\n        based on the time of day. Time of day should range between 0 and 23,\n        0 being midnight.\n        ',
        x_axis_name='time_of_day',
        y_axis_name='customer_count_multiplier')
class CraftingStationComponent(Component, HasTunableFactory, component_name=types.CRAFTING_STATION_COMPONENT):
    __qualname__ = 'CraftingStationComponent'
    FACTORY_TUNABLES = {'crafting_station_type': TunableReference(services.recipe_manager(), class_restrictions=CraftingObjectType, description='This specifies the crafting object type that is satisfied by this object.'), 'children_invalidate_crafting_cache': Tunable(description="\n                If this is True, anything that is attached as a child of this object will cause \n                the crafting cache to be invalidated.  If it's False, children will be ignored\n                for the purposes of the crafting cache.\n                ", tunable_type=bool, default=True)}

    def __init__(self, owner, *, crafting_station_type, children_invalidate_crafting_cache):
        super().__init__(owner)
        self.crafting_station_type = crafting_station_type
        self._children_invalidate_crafting_cache = children_invalidate_crafting_cache
        self._cached = False
        self._cached_for_autonomy = False

    def on_add(self):
        if self.crafting_station_type is not None:
            self.add_to_crafting_cache()
            self._add_state_changed_callback()

    def on_remove(self):
        if self.crafting_station_type is not None:
            self.remove_from_crafting_cache()
            self._remove_state_changed_callback()

    def on_child_added(self, child):
        if self._children_invalidate_crafting_cache and len(self.owner.children) == 1:
            self.remove_from_crafting_cache(user_directed=False)

    def on_child_removed(self, child):
        if self._children_invalidate_crafting_cache and len(self.owner.children) == 0:
            self.add_to_crafting_cache(user_directed=False)

    @componentmethod_with_fallback(lambda : None)
    def add_to_crafting_cache(self, user_directed=True, autonomy=True):
        if self.crafting_station_type is not None:
            self._add_to_cache(user_directed=user_directed, autonomy=autonomy)

    @componentmethod_with_fallback(lambda : None)
    def remove_from_crafting_cache(self, user_directed=True, autonomy=True):
        if self.crafting_station_type is not None:
            self._remove_from_cache(user_directed=user_directed, autonomy=autonomy)

    def _add_to_cache(self, user_directed=True, autonomy=True):
        user_directed &= not self._cached
        autonomy &= not self._cached_for_autonomy
        services.object_manager().crafting_cache.add_type(self.crafting_station_type, user_directed=user_directed, autonomy=autonomy)
        if autonomy:
            self._cached_for_autonomy = True
        if user_directed:
            self._cached = True

    def _remove_from_cache(self, user_directed=True, autonomy=True):
        user_directed &= self._cached
        autonomy &= self._cached_for_autonomy
        services.object_manager().crafting_cache.remove_type(self.crafting_station_type, user_directed=user_directed, autonomy=autonomy)
        if autonomy:
            self._cached_for_autonomy = False
        if user_directed:
            self._cached = False

    def _add_state_changed_callback(self):
        if self.owner.has_component(types.STATE_COMPONENT):
            self.owner.add_state_changed_callback(self._on_crafting_object_state_changed)

    def _remove_state_changed_callback(self):
        if self.owner.has_component(types.STATE_COMPONENT):
            self.owner.remove_state_changed_callback(self._on_crafting_object_state_changed)

    def _on_crafting_object_state_changed(self, owner, state, old_value, new_value):
        if not old_value.remove_from_crafting_cache and new_value.remove_from_crafting_cache:
            self.remove_from_crafting_cache()
        elif old_value.remove_from_crafting_cache and not new_value.remove_from_crafting_cache:
            self.add_to_crafting_cache()
Example #9
0
class ChefsChoice:
    FOOD_COURSES = TunableList(
        description=
        '\n        A List of all the courses to search through in order to find what an \n        NPC will order.\n        ',
        tunable=TunableEnumWithFilter(
            description=
            '\n            A food course that an NPC can order.\n            ',
            tunable_type=Tag,
            filter_prefixes=['recipe_course'],
            default=Tag.INVALID,
            invalid_enums=(Tag.INVALID, ),
            pack_safe=True))
    DRINK_COURSE = TunableEnumWithFilter(
        description=
        '\n        The drink course so Sims can order drinks with their meals.\n        ',
        tunable_type=Tag,
        filter_prefixes=['recipe_course'],
        default=Tag.INVALID,
        invalid_enums=(Tag.INVALID, ),
        pack_safe=True)
    DESSERT_COURSE = TunableEnumWithFilter(
        description=
        '\n        The dessert course so Sims can order dessert with their meals.\n        ',
        tunable_type=Tag,
        filter_prefixes=['recipe_course'],
        default=Tag.INVALID,
        invalid_enums=(Tag.INVALID, ),
        pack_safe=True)
    NPC_ORDER_MAP = TunableMapping(
        description=
        '\n        A mapping of tags to weighted tests. If an item on the menu has the\n        designated tag, it will start with the tuned base weight and then each\n        passing test will add the tested-weight to the total weight for that\n        food object. Once all food objects have been weighed for a given\n        category (apps, entrees, etc.), a weighted random determines the\n        winner.\n        ',
        key_type=TunableEnumEntry(
            description=
            '\n            If the food item has this tag, we will apply the corresponding base\n            weight to it and the sum of the weights of any passing tests run on\n            this object.\n            ',
            tunable_type=Tag,
            default=Tag.INVALID,
            invalid_enums=(Tag.INVALID, ),
            pack_safe=True),
        value_type=TunableTuple(
            description=
            '\n            The base weight and weighted tests to run.\n            ',
            base_weight=TunableRange(
                description=
                '\n                The base weight of this food object. Even if no tests pass,\n                this weight will be applied for use with the weighted random\n                selection.\n                ',
                tunable_type=float,
                default=1.0,
                minimum=0),
            weighted_tests=TunableList(
                description=
                '\n                A list of tests and weights. For each passed test, the\n                corresponding weight is added to the base weight of the food\n                object.\n                ',
                tunable=TunableTuple(
                    description=
                    '\n                    Tests and weights. If the test passes, the weight is added\n                    to the base weight of the food object.\n                    ',
                    tests=TunableTestSet(),
                    weight=Tunable(
                        description=
                        '\n                        The weight to add to the base weight of the food object\n                        if the corresponding tests pass. A negative value is\n                        valid.\n                        ',
                        tunable_type=float,
                        default=1.0)))))
    WATER_ORDER_FOR_BACKUP = TunablePackSafeReference(
        description=
        '\n        A reference to the water order that should be available when nothing\n        else is available.\n        ',
        manager=services.recipe_manager(),
        class_restrictions=('Recipe', ))

    @classmethod
    def get_choice_for_npc_sim(cls, sim, course):
        zone_director = get_restaurant_zone_director()
        menu_items = zone_director.get_menu_for_course(course)
        possible_items = cls.get_possible_orders(sim, menu_items)
        if not possible_items:
            return
        choice = random.weighted_random_item(list(possible_items.items()),
                                             flipped=True)
        return choice

    @classmethod
    def get_order_for_npc_sim(cls, sim):
        zone_director = get_restaurant_zone_director()
        if zone_director is None:
            logger.error(
                'Trying to get an order for an NPC sim but there is no restaurant zone director.'
            )
            return
        menu_items = []
        for course in cls.FOOD_COURSES:
            menu_items.extend(zone_director.get_menu_for_course(course))
        possible_orders = list(
            cls.get_possible_orders(sim, menu_items).items())
        food_choice = random.weighted_random_item(possible_orders,
                                                  flipped=True)
        business_manager = services.business_service(
        ).get_business_manager_for_zone()
        if business_manager is not None:
            bucks_tracker = services.active_household().bucks_tracker
            if bucks_tracker.is_perk_unlocked(
                    RestaurantTuning.CUSTOMERS_ORDER_EXPENSIVE_FOOD_PERK_DATA.
                    perk):
                food_choice_2 = random.weighted_random_item(possible_orders,
                                                            flipped=True)
                if food_choice_2 is not food_choice:
                    choice_1_price = business_manager.get_value_with_markup(
                        food_choice.restaurant_base_price)
                    choice_2_price = business_manager.get_value_with_markup(
                        food_choice_2.restaurant_base_price)
                    if choice_2_price > choice_1_price:
                        food_choice = food_choice_2
        drink_choice = cls.get_choice_for_npc_sim(sim, cls.DRINK_COURSE)
        if food_choice is None and drink_choice is None:
            return (None, cls.WATER_ORDER_FOR_BACKUP)
        return (food_choice, drink_choice)

    @classmethod
    def get_order_for_npc_sim_with_menu(cls, sim, menu_preset):
        chef_menu = RestaurantTuning.MENU_PRESETS[menu_preset]
        menu_items = []
        for course in cls.FOOD_COURSES:
            menu_items.extend(chef_menu.recipe_map.get(course, {}))
        possible_orders = cls.get_possible_orders(sim, menu_items)
        food_order = random.weighted_random_item(list(possible_orders.items()),
                                                 flipped=True)
        return food_order

    @classmethod
    def get_possible_orders(cls, sim, menu_items):
        resolver = SingleSimResolver(sim)
        possible_orders = {}
        for (order_data_tag, order_data) in cls.NPC_ORDER_MAP.items():
            for recipe in menu_items:
                if order_data_tag in recipe.recipe_tags:
                    if recipe not in possible_orders:
                        possible_orders[recipe] = 0
                    possible_orders[recipe] += order_data.base_weight
                    for weighted_test in order_data.weighted_tests:
                        if weighted_test.tests.run_tests(resolver):
                            possible_orders[recipe] += weighted_test.weight
        return possible_orders
Example #10
0
class CraftingStationComponent(Component,
                               HasTunableFactory,
                               component_name=types.CRAFTING_STATION_COMPONENT
                               ):
    FACTORY_TUNABLES = {
        'crafting_station_types':
        TunableList(
            description=
            '\n            Crafting Object Types that this object supports.\n            ',
            tunable=TunableReference(
                description=
                '\n                This specifies the crafting object type that is satisfied by\n                this object.\n                ',
                manager=services.recipe_manager(),
                class_restrictions=('CraftingObjectType', ))),
        'children_invalidate_crafting_cache':
        Tunable(
            description=
            "\n            If this is True, anything that is attached as a child of this\n            object will cause the crafting cache to be invalidated.  If\n            it's False, children will be ignored for the purposes of the\n            crafting cache.\n            ",
            tunable_type=bool,
            default=True)
    }

    def __init__(self, owner, *, crafting_station_types,
                 children_invalidate_crafting_cache):
        super().__init__(owner)
        self.tuned_crafting_station_types = crafting_station_types
        self._children_invalidate_crafting_cache = children_invalidate_crafting_cache
        self._cached_user_directed = {}
        self._cached_for_autonomy = {}
        self._state_value_crafting_types = Counter()
        self._should_be_in_cache = True

    @property
    def crafting_station_types(self):
        return list(self.tuned_crafting_station_types) + list(
            self._state_value_crafting_types.keys())

    def on_add(self):
        if self.crafting_station_types:
            self.add_to_crafting_cache()
            self._add_state_changed_callback()

    def on_remove(self):
        if self.crafting_station_types:
            self.remove_from_crafting_cache()
            self._remove_state_changed_callback()

    def on_child_added(self, child, location):
        if self._children_invalidate_crafting_cache and len(
                self.owner.children) == 1:
            self.remove_from_crafting_cache(user_directed=False)

    def on_child_removed(self, child, new_parent=None):
        if self._children_invalidate_crafting_cache and len(
                self.owner.children) == 0:
            self.add_to_crafting_cache(user_directed=False)

    def on_added_to_inventory(self):
        self.remove_from_crafting_cache()

    def on_removed_from_inventory(self):
        self.add_to_crafting_cache()

    @componentmethod_with_fallback(lambda: None)
    def add_to_crafting_cache(self, user_directed=True, autonomy=True):
        if self.crafting_station_types:
            self._should_be_in_cache = True
            for crafting_type in self.crafting_station_types:
                self._add_crafting_type_to_cache(crafting_type,
                                                 user_directed=user_directed,
                                                 autonomy=autonomy)

    @componentmethod_with_fallback(lambda: None)
    def remove_from_crafting_cache(self, user_directed=True, autonomy=True):
        if self.crafting_station_types:
            self._should_be_in_cache = False
            for crafting_type in self.crafting_station_types:
                self._remove_crafting_type_from_cache(
                    crafting_type,
                    user_directed=user_directed,
                    autonomy=autonomy)

    def _add_crafting_type_to_cache(self,
                                    crafting_type,
                                    user_directed=True,
                                    autonomy=True):
        user_directed &= not self._cached_user_directed.get(
            crafting_type, False)
        autonomy &= not self._cached_for_autonomy.get(crafting_type, False)
        services.object_manager().crafting_cache.add_type(
            crafting_type, user_directed=user_directed, autonomy=autonomy)
        if autonomy:
            self._cached_for_autonomy[crafting_type] = True
        if user_directed:
            self._cached_user_directed[crafting_type] = True

    def _remove_crafting_type_from_cache(self,
                                         crafting_type,
                                         user_directed=True,
                                         autonomy=True):
        user_directed &= self._cached_user_directed.get(crafting_type, False)
        autonomy &= self._cached_for_autonomy.get(crafting_type, False)
        services.object_manager().crafting_cache.remove_type(
            crafting_type, user_directed=user_directed, autonomy=autonomy)
        if autonomy:
            self._cached_for_autonomy[crafting_type] = False
        if user_directed:
            self._cached_user_directed[crafting_type] = False

    def _add_state_changed_callback(self):
        if self.owner.has_component(types.STATE_COMPONENT):
            self.owner.add_state_changed_callback(
                self._on_crafting_object_state_changed)

    def _remove_state_changed_callback(self):
        if self.owner.has_component(types.STATE_COMPONENT):
            self.owner.remove_state_changed_callback(
                self._on_crafting_object_state_changed)

    def _on_crafting_object_state_changed(self, owner, state, old_value,
                                          new_value):
        if not old_value.remove_from_crafting_cache and new_value.remove_from_crafting_cache:
            self._should_be_in_cache = False
            self.remove_from_crafting_cache()
        elif old_value.remove_from_crafting_cache and not new_value.remove_from_crafting_cache:
            self._should_be_in_cache = True
            self.add_to_crafting_cache()
        if old_value.crafting_types is not None:
            for crafting_type in old_value.crafting_types:
                self._state_value_crafting_types[crafting_type] -= 1
                if self._should_be_in_cache:
                    self._remove_crafting_type_from_cache(crafting_type)
        if new_value.crafting_types is not None:
            for crafting_type in new_value.crafting_types:
                self._state_value_crafting_types[crafting_type] += 1
                if self._should_be_in_cache:
                    self._add_crafting_type_to_cache(crafting_type)
Example #11
0
class ServiceNpcHireableCrafter(ServiceNpcHireable):
    INSTANCE_TUNABLES = {
        'recipe_picker_on_hire':
        TunableTuple(
            description=
            '\n            The recipe picker dialog that is shown when hiring this service.\n            The dialog will be shown when we request this service using the\n            request service npc basic extra.\n            \n            Ex: when requesting pizza delivery, we can choose the type of\n            pizza.\n            ',
            picker_dialog=UiRecipePicker.TunableFactory(
                description='Tuning for what type of picker dialog to show'),
            recipes=TunableList(
                description=
                '\n                The recipes to display in the picker dialog\n                ',
                tunable=TunableReference(
                    description=
                    '\n                    Recipe to craft.\n                    ',
                    manager=services.recipe_manager(),
                    pack_safe=True)))
    }

    @classmethod
    def on_chosen_from_service_picker(cls,
                                      picker_interaction,
                                      recurring=False):
        sim = picker_interaction.sim
        dialog = cls.recipe_picker_on_hire.picker_dialog(
            sim, picker_interaction.get_resolver())

        def on_recipe_selected(dialog):
            recipe = dialog.get_single_result_tag()
            if recipe is None or cls.hire_interaction is None:
                return
            push_affordance = picker_interaction.generate_continuation_affordance(
                cls.hire_interaction)
            for aop in push_affordance.potential_interactions(
                    sim,
                    picker_interaction.context,
                    service_npc_user_specified_data_id=recipe.guid64,
                    service_npc_recurring_request=recurring):
                aop.test_and_execute(picker_interaction.context)

        for recipe in cls.recipe_picker_on_hire.recipes:
            (_, price) = recipe.get_price(is_retail=True)
            description = recipe.recipe_description(sim)
            if recipe.has_final_product_definition:
                recipe_icon = IconInfoData(
                    icon_resource=recipe.icon_override,
                    obj_def_id=recipe.final_product_definition_id,
                    obj_geo_hash=recipe.final_product_geo_hash,
                    obj_material_hash=recipe.final_product_material_hash)
            else:
                recipe_icon = IconInfoData(recipe.icon_override)
            if sim.family_funds.money < price:
                error_list = [CraftingTuning.INSUFFICIENT_FUNDS_TOOLTIP(sim)]
                result = RecipeTestResult(enabled=False,
                                          visible=False,
                                          errors=error_list)
            else:
                result = True
            row = RecipePickerRow(
                name=recipe.get_recipe_name(sim),
                price=price,
                icon=recipe.icon_override,
                row_description=description,
                skill_level=recipe.required_skill_level,
                is_enable=result,
                linked_recipe=recipe.base_recipe,
                display_name=recipe.get_recipe_picker_name(sim),
                icon_info=recipe_icon,
                tag=recipe)
            dialog.add_row(row)
        dialog.set_target_sim(sim)
        dialog.show_dialog(on_response=on_recipe_selected)

    @classmethod
    def on_service_sim_entered_situation(cls, service_sim, situation):
        if situation.role_affordance_target is None:
            recipe = services.recipe_manager().get(
                situation.object_definition_to_craft)
            if recipe is None:
                return False
            craftable = DebugCreateCraftableInteraction.create_craftable(
                recipe,
                service_sim,
                owning_household_id_override=situation.hiring_household.id,
                place_in_crafter_inventory=True)
            if craftable is None:
                return False
            situation.set_crafted_object_id(craftable.id)
        return True

    @classmethod
    def get_default_user_specified_data_id(cls):
        if cls.recipe_picker_on_hire.recipes:
            return random.choice(cls.recipe_picker_on_hire.recipes).guid64
Example #12
0
class Recipe(metaclass=HashedTunedInstanceMetaclass,
             manager=services.get_instance_manager(
                 sims4.resources.Types.RECIPE)):
    __qualname__ = 'Recipe'
    INSTANCE_TUNABLES = {
        'name':
        TunableLocalizedStringFactory(
            description='\n            The name of this recipe.\n            '
        ),
        'phase_interaction_name':
        TunableLocalizedStringFactory(
            description=
            "\n            The name of each phase's interaction.\n            "
        ),
        'final_product':
        TunableRecipeObjectInfo(
            description=
            '\n            The final product of the crafting process.\n            ',
            optional_create=False),
        '_first_phases':
        TunableList(
            description=
            '\n            The names of the phases that can be done first.  This cannot be empty.\n            ',
            tunable=TunableEnumEntry(PhaseName, default=None)),
        '_phases':
        TunableMapping(
            description=
            '\n            The phases that make up this recipe.\n            ',
            key_type=TunableEnumEntry(PhaseName, None),
            value_type=TunablePhaseVariant()),
        'resume_affordance':
        OptionalTunable(
            description=
            '\n            The interaction to use when resuming crafting this recipe.\n            ',
            tunable=TunableReference(
                manager=services.get_instance_manager(
                    sims4.resources.Types.INTERACTION),
                class_restrictions=('CraftingResumeInteraction', ))),
        'skill_test':
        OptionalTunable(
            description=
            '\n            The skill level required to use this recipe.\n            ',
            tunable=event_testing.test_variants.SkillRangeTest.TunableFactory(
            )),
        'additional_tests':
        event_testing.tests.TunableTestSetWithTooltip(),
        'additional_tests_ignored_on_resume':
        Tunable(
            description=
            '\n            If set, additional tests are ignored when testing to see if a recipe\n            can be resumed.\n            ',
            tunable_type=bool,
            default=False),
        'utility_info':
        OptionalTunable(TunableList(
            description=
            '\n            Tuning that specifies which utilities this recipe requires to\n            be made/cooked.\n            ',
            tunable=TunableEnumEntry(Utilities, None)),
                        needs_tuning=True),
        '_retail_price':
        Tunable(
            description=
            '\n            Retail price of the recipe. \n            This is not the total price of the recipe.  The total price of the \n            recipe will be _retail_price+delta_ingredient_price\n            ',
            tunable_type=int,
            default=0),
        'crafting_cost':
        TunableVariant(
            description=
            '\n            This determines the amount of money the Sim will have to pay in\n            order to craft this recipe.\n            \n            Crafting Discount is multiplied by the Retail Price to determine the\n            amount of money the Sim must pay to craft this.\n            \n            Flat Fee is just a flat fee the Sim must pay to craft this.\n            ',
            discount=Tunable(
                description=
                '\n                This number is multiplied by the Retail Price to to determine\n                the amount of money the Sim will have to pay in order to craft this.\n                ',
                tunable_type=float,
                default=0.8),
            flat_fee=Tunable(
                description=
                '\n                This is a flat amount of simoleons that the Sim will have to pay\n                in order to craft this.\n                ',
                tunable_type=int,
                default=0),
            default='discount'),
        'delta_ingredient_price':
        Tunable(
            description=
            '\n            Delta price of a recipe will be a delta value that will increase\n            or decrease depending on how many ingredients for the recipe the \n            sim has.\n            e.g  For a 3 ingredient recipe:\n            - If no ingredient is found,  price will be retail_price + delta\n            - If 1 ingredient is found,  price will be retail_price + 2/3 of \n            delta\n            - If 2 ingredient is found,  price will be retail_price + 1/3 of \n            delta\n            - If 3 ingredient is found,  price will be retail_price\n            ',
            tunable_type=int,
            default=0),
        '_no_initial_charge':
        Tunable(
            description=
            '\n            If set, there is no initial charge for making this recipe\n            ',
            tunable_type=bool,
            needs_tuning=True,
            default=False),
        'icon_override':
        TunableResourceKey(
            description=
            "\n            This will override the default icon for this recipe. If left blank, the thumbnail of the recipe's final product will be used.\n            ",
            default=None,
            resource_types=sims4.resources.CompoundTypes.IMAGE),
        'recipe_description':
        TunableLocalizedStringFactory(description='recipe description'),
        'autonomy_weight':
        Tunable(
            description=
            '\n            The relative weight for autonomy to choose to make or order this recipe\n            ',
            tunable_type=float,
            default=0),
        'buff_weight_multipliers':
        TunableBuffWeightMultipliers(),
        'base_recipe':
        OptionalTunable(
            description=
            '\n            If set, the single serving counterpart for this recipe\n            ',
            tunable=TunableReference(services.recipe_manager())),
        'visible_as_subrow':
        Tunable(
            description=
            '\n            If this recipe has base recipe, this boolean will decide whether or\n            not this recipe will show up in the subrow entry of the base\n            recipe.\n            ',
            tunable_type=bool,
            default=True),
        'base_recipe_category':
        OptionalTunable(
            description=
            '\n            The pie menu category of the base recipe if the picker dialog to\n            choose recipes is tuned to use pie menu formation.\n            ',
            tunable=TunableReference(
                description=
                '\n                Pie menu category for pie menu mixers.\n                ',
                manager=services.get_instance_manager(
                    sims4.resources.Types.PIE_MENU_CATEGORY))),
        'multi_serving_name':
        OptionalTunable(
            TunableLocalizedStringFactory(
                description=
                'The name of multiple serving to show in the sub row of ObjectPicker.'
            ),
            enabled_name
            ='use_multi_serving_name',
            disabled_name='use_recipe_name',
            description=
            "The string that shows up in the ObjectPicker when this recipe is a multi_serving. Please use 'use_recipe_name' when it's a single serving"
        ),
        'push_consume':
        Tunable(
            description=
            '\n            Whether to push the consume after finish the recipe.\n            ',
            tunable_type=bool,
            needs_tuning=True,
            default=True),
        'push_consume_threshold':
        OptionalTunable(
            description=
            '\n            If Push Consume is checked and this threshold is enabled, the consume affordance will\n            only be pushed if the threshold is met.\n            ',
            tunable=TunableTuple(
                description=
                '\n                The commodity/threshold pair.\n                ',
                commodity=TunableReference(
                    description=
                    '\n                    The commodity to be tested.\n                    ',
                    manager=services.statistic_manager(),
                    class_restrictions='Commodity'),
                threshold=TunableThreshold(
                    description=
                    '\n                    The threshold at which to remove this bit.\n                    '
                ))),
        'skill_loot_data':
        TunableSkillLootData(
            description=
            'Loot Data for DynamicSkillLootOp. This will only be used if in the loot list of the outcome there is a dynamic loot op.'
        ),
        'masterwork_name':
        TunableLocalizedString(
            description=
            'If this can be a masterwork, what should its masterwork state be called? Usually this is just Masterpiece.'
        ),
        'resumable':
        Tunable(
            description=
            '\n            If set to False, this recipe is not resumable, for example drinks made at bar.\n            ',
            tunable_type=bool,
            default=True),
        'resumable_by_different_sim':
        Tunable(
            description=
            '\n            If set, this recipe can be resumed by a sim other than the one that started it\n            ',
            tunable_type=bool,
            needs_tuning=True,
            default=False),
        'entitlement':
        TunableEntitlement(
            description=
            '\n            Entitlement required to use this recipe.\n            '
        ),
        'hidden_until_unlock':
        Tunable(
            description=
            "\n            If checked, this recipe will not show up in picker or piemenu if\n            it's not unlocked, either by skill up, or unlock outcome, or\n            entitlement.\n            ",
            tunable_type=bool,
            needs_tuning=True,
            default=True),
        'use_ingredients':
        OptionalTunable(
            description=
            '\n            If checked recipe will have the ability to use ingredients to \n            improve its quality when prepared.\n            ',
            tunable=TunableTuple(
                description=
                '\n                Ingredient data for a recipe.\n                ',
                all_ingredients_required=Tunable(
                    description=
                    '\n                    If checked recipe will not be available unless all \n                    ingredients are found on the sim inventory or the fridge\n                    inventory.\n                    ',
                    tunable_type=bool,
                    default=False),
                ingredient_list=TunableList(
                    description=
                    '\n                    List of ingredients the recipe can use\n                    ',
                    tunable=TunableVariant(
                        description=
                        '\n                        Possible ingredient mapping by object definition of by \n                        catalog object Tag.\n                        ',
                        ingredient_by_definition=TunableIngredientByDefFactory(
                        ),
                        ingredient_by_tag=TunableIngredientByTagFactory())))),
        'crafted_by_text':
        TunableLocalizedStringFactory(
            description=
            "\n            Text that describes who made it, e.g. 'Made By: <Sim>'. Loc\n            parameter 0 is the Sim crafter.\n            "
        ),
        'value_text':
        TunableLocalizedStringFactory(
            description=
            "\n            Text (if any) that describes the value in the tooltip.  \n            e.g. 'Value: $500'. Loc parameter 0 is the value.\n            "
        ),
        'mood_list':
        TunableList(
            description=
            '\n            A list of possible moods this Recipe may associate with. Note that\n            this list is for coloring-purposes only. Interaction availability\n            testing is relegated to the individual mood tests on the\n            interaction. Future refactoring necessary.\n            ',
            tunable=TunableReference(manager=services.mood_manager()))
    }
    _tuning_loaded = False
    _referenced_by_start_crafting = False

    @classmethod
    def _tuning_loaded_callback(cls):
        cls._visible_phase_sequence = []
        cls.phases = {
            phase_id: phase(recipe=cls, phase_id=phase_id)
            for (phase_id, phase) in cls._phases.items()
        }
        first_phases = []
        for name in cls._first_phases:
            if name in cls.phases:
                first_phases.append(cls.phases[name])
            else:
                logger.error(
                    "Unknown phase '{}' specified in first_phases for recipe '{}'.",
                    name, cls)
        cls.first_phases = first_phases
        for phase in cls.phases.values():
            phase.recipe_tuning_loaded()
        if cls.first_phases:
            cls._build_visible_phase_sequence(cls.first_phases[0])
        cls._tuning_loaded = True
        if cls._referenced_by_start_crafting:
            cls.validate_for_start_crafting()

    @classmethod
    def _verify_tuning_callback(cls):
        if cls.use_ingredients:
            for ingredient in cls.use_ingredients.ingredient_list:
                while ingredient is None:
                    logger.error('Recipe {} has unset ingredients',
                                 cls.__name__)
            if not cls.use_ingredients.ingredient_list:
                logger.error(
                    'Recipe {} has an empty ingredient list and its tuned to use ingredients.',
                    cls.__name__)
        if not cls.name:
            logger.error('Recipe {} does not have a name set', cls.__name__)
        if not cls.phase_interaction_name:
            logger.error(
                'Recipe {} does not have a phase interaction name set',
                cls.__name__)
        cls._validate_final_product()
        services.get_instance_manager(
            sims4.resources.Types.RECIPE).add_on_load_complete(
                cls.validate_base_recipe)

    @classmethod
    def _validate_final_product(cls):
        if cls.final_product_definition is None:
            return
        supported_states = objects.components.state.get_supported_state(
            cls.final_product.definition)
        unsupported_values = []
        for state_value in itertools.chain(cls.final_product.initial_states,
                                           cls.final_product.apply_states):
            while supported_states is None or state_value.state not in supported_states:
                unsupported_values.append(state_value)
        if supported_states is None:
            error = "\n    A recipe wants to set one or more state value on its final product, but that\n    object doesn't have a StateComponent.  The recipe shouldn't be trying to set\n    any state values, or the object's tuning should be updated to add these\n    states."
        else:
            error = "\n    A recipe wants to set a state value on its final product, but that object's\n    state component tuning doesn't have an entry for that state.  The recipe\n    shouldn't be trying to set these state values, or the object's tuning should\n    be updated to add these states."
        logger.warn(
            'Recipe tuning error:{}\n        Recipe: {}\n        Missing States: {}\n        Final Product: {} ({})'
            .format(
                error, cls.__name__, ', '.join(
                    sorted({e.state.__name__
                            for e in unsupported_values})),
                cls.final_product.definition.name,
                cls.final_product.definition.cls.__name__))
        for sa in cls.final_product.super_affordances:
            while sa.consumes_object() or sa.contains_stat(
                    CraftingTuning.CONSUME_STATISTIC):
                logger.error(
                    'Recipe: Interaction {} on {} is consume affordance, should tune on ConsumableComponent of the object.',
                    sa.__name__,
                    cls.__name__,
                    owner='tastle/cjiang')

    @classmethod
    def validate_for_start_crafting(cls):
        if not cls._tuning_loaded:
            cls._referenced_by_start_crafting = True
        elif not cls.first_phases:
            logger.error(
                'Recipe is tuned to be craftable but has no first phases defined: {}',
                cls)

    @classmethod
    def validate_base_recipe(cls, manager):
        base_recipe = cls.base_recipe
        if base_recipe is not None and cls.hidden_until_unlock != base_recipe.hidden_until_unlock:
            logger.error(
                "Recipe({})'s hidden_until_unlock({}) != base_recipe({})'s hidden_until_unlock({})",
                cls.__name__, cls.hidden_until_unlock, base_recipe.__name__,
                base_recipe.hidden_until_unlock)

    @classmethod
    def _build_visible_phase_sequence(cls, phase):
        if phase.is_visible:
            cls._visible_phase_sequence.append(phase)
        if phase.next_phases:
            next_phase = phase.next_phases[0]
            cls._build_visible_phase_sequence(next_phase)

    @classproperty
    def final_product_definition(cls):
        return cls.final_product.definition

    @classproperty
    def final_product_definition_id(cls):
        return cls.final_product.definition.id

    @classproperty
    def has_final_product_definition(cls):
        return cls.final_product.definition is not None

    @classproperty
    def final_product_geo_hash(cls):
        if cls.final_product.thumbnail_geo_state is not None:
            return sims4.hash_util.hash32(
                cls.final_product.thumbnail_geo_state)
        return cls.final_product_definition.thumbnail_geo_state_hash

    @classproperty
    def final_product_material_hash(cls):
        if cls.final_product.thumbnail_material_state is not None:
            return sims4.hash_util.hash32(
                cls.final_product.thumbnail_material_state)
        return 0

    @classproperty
    def final_product_type(cls):
        return cls.final_product.definition.cls

    @classproperty
    def apply_tags(cls):
        obj_info = cls.final_product
        if obj_info is not None:
            return obj_info.apply_tags
        return set()

    @classmethod
    def get_final_product_quality_adjustment(cls, effective_skill):
        quality_adjustment = cls.final_product.quality_adjustment
        skill_delta = effective_skill - cls.required_skill_level
        quality_value = quality_adjustment.base_quality + skill_delta * quality_adjustment.skill_adjustment
        return quality_value

    @classmethod
    def setup_crafted_object(cls, crafted_object, crafter, is_final_product):
        pass

    @classproperty
    def crafting_price(cls):
        if cls._no_initial_charge:
            return 0
        if isinstance(cls.crafting_cost, int):
            return cls.crafting_cost
        return int(cls._retail_price * cls.crafting_cost)

    @classproperty
    def retail_price(cls):
        return cls._retail_price

    @classproperty
    def simoleon_value_modifiers(cls):
        return cls.final_product.simoleon_value_modifiers_map

    @classproperty
    def simoleon_value_skill_curve(cls):
        return cls.final_product.simoleon_value_skill_curve

    @classproperty
    def masterworks_data(cls):
        return cls.final_product.masterworks

    @classproperty
    def required_skill_level(cls):
        if cls.skill_test is not None:
            return cls.skill_test.skill_range_min
        return 0

    @classproperty
    def required_skill(cls):
        if cls.skill_test is not None:
            return cls.skill_test.skill

    @classmethod
    def get_base_recipe(cls):
        return cls.base_recipe or cls

    @classmethod
    def get_recipe_picker_name(cls, *args):
        if cls.multi_serving_name is not None:
            return cls.multi_serving_name(*args)
        return cls.name(*args)

    @classmethod
    def get_recipe_name(cls, *args):
        return cls.name(*args)

    @classmethod
    def get_price(cls, is_retail=False, ingredient_modifier=1):
        if is_retail:
            if cls.retail_price != 0:
                return cls.retail_price
            return cls.crafting_price
        return cls.crafting_price + int(
            cls.delta_ingredient_price * ingredient_modifier)

    @classmethod
    def calculate_autonomy_weight(cls, sim):
        total_weight = cls.autonomy_weight
        for (buff, weight) in cls.buff_weight_multipliers.items():
            while sim.has_buff(buff):
                total_weight *= weight
        return total_weight

    @classproperty
    def total_visible_phases(cls):
        return len(cls._visible_phase_sequence)

    @classmethod
    def get_visible_phase_index(cls, phase):
        if phase in cls._visible_phase_sequence:
            return cls._visible_phase_sequence.index(phase) + 1
        return 0

    @classproperty
    def is_single_phase_recipe(cls):
        return len(cls._phases) == 1

    @classmethod
    def update_hovertip(cls, owner, crafter=None):
        description = cls.recipe_description(crafter)
        genre = Genre.get_genre_localized_string(owner)
        if genre is not None:
            description = LocalizationHelperTuning.get_new_line_separated_strings(
                description, genre)
        value_text = cls.value_text
        if value_text:
            localized_value = value_text(owner.current_value)
            description = LocalizationHelperTuning.get_new_line_separated_strings(
                description, localized_value)
        objects.components.crafting_component._set_recipe_decription(
            owner, description)

    @property
    def debug_name(self):
        return type(self).__name__

    @classmethod
    def debug_dump(cls, dump=dump_logger.warn):
        dump('Recipe Name: {}'.format(type(cls).__name__))
        dump('Phases: {}'.format(len(cls.phases)))
        for phase in cls.phases.values():
            dump('  Phase {}:'.format(phase.id))
            while phase.num_turns > 0:
                dump('    Turns: {}'.format(phase.turns))
Example #13
0
def generate_restaurant_order_data(zone_id: int = None):
    all_orders = []
    zone_director = get_restaurant_zone_director()
    if zone_director is None:
        return all_orders
    sim_info_manager = services.sim_info_manager()
    recipe_manager = services.recipe_manager()
    for group_order in zone_director._group_orders.values():
        sim_order_data = []
        for (sim_id, sim_order) in group_order._sim_orders.items():
            sim_info = sim_info_manager.get(sim_id)
            food_recipe_id = sim_order.food_recipe_id
            drink_recipe_id = sim_order.drink_recipe_id
            food_str = ''
            drink_str = ''
            if food_recipe_id is not None:
                food_recipe = recipe_manager.get(food_recipe_id, None)
                if food_recipe is not None:
                    food_str = '{}({})'.format(food_recipe.__name__,
                                               food_recipe_id)
            if drink_recipe_id is not None:
                drink_recipe = recipe_manager.get(drink_recipe_id, None)
                if drink_recipe is not None:
                    drink_str = '{}({})'.format(drink_recipe.__name__,
                                                drink_recipe_id)
            sim_order_data.append({
                'sim_name':
                str(sim_info),
                'food_recipe':
                food_str,
                'drink_recipe':
                drink_str,
                'recommendation_state':
                str(sim_order.recommendation_state)
            })
        assigned_waiter = group_order.assigned_waitstaff
        assigned_chef = group_order.assigned_chef
        food_platter = group_order.serving_from_chef
        all_orders.append({
            'order_id':
            str(group_order.order_id),
            'situation_id':
            str(group_order._situation_id),
            'order_status':
            str(group_order.order_status),
            'current_bucket':
            str(group_order.current_bucket),
            'table_ids':
            ','.join([str(table_id) for table_id in group_order._table_ids]),
            'sim_order_count':
            str(group_order.sim_order_count),
            'assigned_waiter':
            str(assigned_waiter),
            'assigned_chef':
            str(assigned_chef),
            'food_platter':
            str(food_platter),
            'complimentary':
            str(group_order.is_complimentary),
            'SimOrders':
            sim_order_data
        })
    return all_orders
Example #14
0
 def load_favorite(self, favorite_data):
     recipe_manager = services.recipe_manager()
     for recipe_id in favorite_data.recipe_ids:
         recipe = recipe_manager.get(recipe_id)
         if recipe is not None:
             self.set_favorite_recipe(recipe)