Factorio: add option: random tech ingredients
This commit is contained in:
		
							parent
							
								
									73ed18c11d
								
							
						
					
					
						commit
						569e0e3004
					
				| 
						 | 
					@ -143,7 +143,9 @@ class MultiWorld():
 | 
				
			||||||
            import Options
 | 
					            import Options
 | 
				
			||||||
            for hk_option in Options.hollow_knight_options:
 | 
					            for hk_option in Options.hollow_knight_options:
 | 
				
			||||||
                set_player_attr(hk_option, False)
 | 
					                set_player_attr(hk_option, False)
 | 
				
			||||||
 | 
					        self.custom_data = {}
 | 
				
			||||||
 | 
					        for player in range(1, players+1):
 | 
				
			||||||
 | 
					            self.custom_data[player] = {}
 | 
				
			||||||
        # self.worlds = []
 | 
					        # self.worlds = []
 | 
				
			||||||
        # for i in range(players):
 | 
					        # for i in range(players):
 | 
				
			||||||
        #     self.worlds.append(worlds.alttp.ALTTPWorld({}, i))
 | 
					        #     self.worlds.append(worlds.alttp.ALTTPWorld({}, i))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -293,7 +293,8 @@ factorio_options: typing.Dict[str, type(Option)] = {"max_science_pack": MaxScien
 | 
				
			||||||
                                                    "tech_tree_layout": TechTreeLayout,
 | 
					                                                    "tech_tree_layout": TechTreeLayout,
 | 
				
			||||||
                                                    "tech_cost": TechCost,
 | 
					                                                    "tech_cost": TechCost,
 | 
				
			||||||
                                                    "free_samples": FreeSamples,
 | 
					                                                    "free_samples": FreeSamples,
 | 
				
			||||||
                                                    "visibility": Visibility}
 | 
					                                                    "visibility": Visibility,
 | 
				
			||||||
 | 
					                                                    "random_tech_ingredients": Toggle}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minecraft_options: typing.Dict[str, type(Option)] = {}
 | 
					minecraft_options: typing.Dict[str, type(Option)] = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
function filter_ingredients(ingredients)
 | 
					function filter_ingredients(ingredients, ingredient_filter)
 | 
				
			||||||
    local new_ingredient_list = {}
 | 
					    local new_ingredient_list = {}
 | 
				
			||||||
    for _, ingredient_table in pairs(ingredients) do
 | 
					    for _, ingredient_table in pairs(ingredients) do
 | 
				
			||||||
        if allowed_ingredients[ingredient_table[1]] then -- name of ingredient_table
 | 
					        if ingredient_filter[ingredient_table[1]] then -- name of ingredient_table
 | 
				
			||||||
            table.insert(new_ingredient_list, ingredient_table)
 | 
					            table.insert(new_ingredient_list, ingredient_table)
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,8 +5,12 @@ local technologies = data.raw["technology"]
 | 
				
			||||||
local original_tech
 | 
					local original_tech
 | 
				
			||||||
local new_tree_copy
 | 
					local new_tree_copy
 | 
				
			||||||
allowed_ingredients = {}
 | 
					allowed_ingredients = {}
 | 
				
			||||||
{%- for ingredient in allowed_science_packs %}
 | 
					{%- for tech_name, technology in custom_data["custom_technologies"].items() %}
 | 
				
			||||||
allowed_ingredients["{{ingredient}}"]= 1
 | 
					allowed_ingredients["{{ tech_name }}"] = {
 | 
				
			||||||
 | 
					{%- for ingredient in technology.ingredients %}
 | 
				
			||||||
 | 
					["{{ingredient}}"] = 1,
 | 
				
			||||||
 | 
					{%- endfor %}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
{% endfor %}
 | 
					{% endfor %}
 | 
				
			||||||
local template_tech = table.deepcopy(technologies["automation"])
 | 
					local template_tech = table.deepcopy(technologies["automation"])
 | 
				
			||||||
{#-  ensure the copy unlocks nothing #}
 | 
					{#-  ensure the copy unlocks nothing #}
 | 
				
			||||||
| 
						 | 
					@ -18,7 +22,10 @@ template_tech.prerequisites = {}
 | 
				
			||||||
function prep_copy(new_copy, old_tech)
 | 
					function prep_copy(new_copy, old_tech)
 | 
				
			||||||
    old_tech.enabled = false
 | 
					    old_tech.enabled = false
 | 
				
			||||||
    new_copy.unit = table.deepcopy(old_tech.unit)
 | 
					    new_copy.unit = table.deepcopy(old_tech.unit)
 | 
				
			||||||
    new_copy.unit.ingredients = filter_ingredients(new_copy.unit.ingredients)
 | 
					    local ingredient_filter = allowed_ingredients[old_tech.name]
 | 
				
			||||||
 | 
					    if ingredient_filter ~= nil then
 | 
				
			||||||
 | 
					        new_copy.unit.ingredients = filter_ingredients(new_copy.unit.ingredients, ingredient_filter)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,6 +67,9 @@ free_samples:
 | 
				
			||||||
visibility:
 | 
					visibility:
 | 
				
			||||||
  none: 0
 | 
					  none: 0
 | 
				
			||||||
  sending: 1
 | 
					  sending: 1
 | 
				
			||||||
 | 
					random_tech_ingredients:
 | 
				
			||||||
 | 
					  on: 1
 | 
				
			||||||
 | 
					  off: 0
 | 
				
			||||||
# A Link to  the Past options:
 | 
					# A Link to  the Past options:
 | 
				
			||||||
### Logic Section ###
 | 
					### Logic Section ###
 | 
				
			||||||
# Warning: overworld_glitches is not available and minor_glitches is only partially implemented on the door-rando version
 | 
					# Warning: overworld_glitches is not available and minor_glitches is only partially implemented on the door-rando version
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ def generate_mod(world: MultiWorld, player: int, seedname: str):
 | 
				
			||||||
                 6: 10}[world.tech_cost[player].value]
 | 
					                 6: 10}[world.tech_cost[player].value]
 | 
				
			||||||
    template_data = {"locations": locations, "player_names" : player_names, "tech_table": tech_table,
 | 
					    template_data = {"locations": locations, "player_names" : player_names, "tech_table": tech_table,
 | 
				
			||||||
                     "mod_name": mod_name, "allowed_science_packs": world.max_science_pack[player].get_allowed_packs(),
 | 
					                     "mod_name": mod_name, "allowed_science_packs": world.max_science_pack[player].get_allowed_packs(),
 | 
				
			||||||
                     "tech_cost_scale": tech_cost,
 | 
					                     "tech_cost_scale": tech_cost, "custom_data": world.custom_data[player],
 | 
				
			||||||
                     "tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites[player]}
 | 
					                     "tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites[player]}
 | 
				
			||||||
    for factorio_option in Options.factorio_options:
 | 
					    for factorio_option in Options.factorio_options:
 | 
				
			||||||
        template_data[factorio_option] = getattr(world, factorio_option)[player].value
 | 
					        template_data[factorio_option] = getattr(world, factorio_option)[player].value
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,15 +7,16 @@ from worlds.factorio.Technologies import technology_table
 | 
				
			||||||
def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
 | 
					def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
 | 
				
			||||||
    prerequisites: Dict[str, Set[str]] = {}
 | 
					    prerequisites: Dict[str, Set[str]] = {}
 | 
				
			||||||
    layout = world.tech_tree_layout[player].value
 | 
					    layout = world.tech_tree_layout[player].value
 | 
				
			||||||
 | 
					    custom_technologies = world.custom_data[player]["custom_technologies"]
 | 
				
			||||||
    if layout == TechTreeLayout.option_small_diamonds:
 | 
					    if layout == TechTreeLayout.option_small_diamonds:
 | 
				
			||||||
        slice_size = 4
 | 
					        slice_size = 4
 | 
				
			||||||
        tech_names: List[str] = list(set(technology_table) - world._static_nodes)
 | 
					        tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
 | 
				
			||||||
        tech_names.sort()
 | 
					        tech_names.sort()
 | 
				
			||||||
        world.random.shuffle(tech_names)
 | 
					        world.random.shuffle(tech_names)
 | 
				
			||||||
        while len(tech_names) > slice_size:
 | 
					        while len(tech_names) > slice_size:
 | 
				
			||||||
            slice = tech_names[:slice_size]
 | 
					            slice = tech_names[:slice_size]
 | 
				
			||||||
            tech_names = tech_names[slice_size:]
 | 
					            tech_names = tech_names[slice_size:]
 | 
				
			||||||
            slice.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients))
 | 
					            slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients))
 | 
				
			||||||
            diamond_0, diamond_1, diamond_2, diamond_3 = slice
 | 
					            diamond_0, diamond_1, diamond_2, diamond_3 = slice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #   0    |
 | 
					            #   0    |
 | 
				
			||||||
| 
						 | 
					@ -25,13 +26,13 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
 | 
				
			||||||
            prerequisites[diamond_2] = prerequisites[diamond_1] = {diamond_0}
 | 
					            prerequisites[diamond_2] = prerequisites[diamond_1] = {diamond_0}
 | 
				
			||||||
    elif layout == TechTreeLayout.option_medium_diamonds:
 | 
					    elif layout == TechTreeLayout.option_medium_diamonds:
 | 
				
			||||||
        slice_size = 9
 | 
					        slice_size = 9
 | 
				
			||||||
        tech_names: List[str] = list(set(technology_table) - world._static_nodes)
 | 
					        tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
 | 
				
			||||||
        tech_names.sort()
 | 
					        tech_names.sort()
 | 
				
			||||||
        world.random.shuffle(tech_names)
 | 
					        world.random.shuffle(tech_names)
 | 
				
			||||||
        while len(tech_names) > slice_size:
 | 
					        while len(tech_names) > slice_size:
 | 
				
			||||||
            slice = tech_names[:slice_size]
 | 
					            slice = tech_names[:slice_size]
 | 
				
			||||||
            tech_names = tech_names[slice_size:]
 | 
					            tech_names = tech_names[slice_size:]
 | 
				
			||||||
            slice.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients))
 | 
					            slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            #     0     |
 | 
					            #     0     |
 | 
				
			||||||
            #   1   2   |
 | 
					            #   1   2   |
 | 
				
			||||||
| 
						 | 
					@ -53,10 +54,10 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    elif layout == TechTreeLayout.option_pyramid:
 | 
					    elif layout == TechTreeLayout.option_pyramid:
 | 
				
			||||||
        slice_size = 1
 | 
					        slice_size = 1
 | 
				
			||||||
        tech_names: List[str] = list(set(technology_table) - world._static_nodes)
 | 
					        tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
 | 
				
			||||||
        tech_names.sort()
 | 
					        tech_names.sort()
 | 
				
			||||||
        world.random.shuffle(tech_names)
 | 
					        world.random.shuffle(tech_names)
 | 
				
			||||||
        tech_names.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients))
 | 
					        tech_names.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients))
 | 
				
			||||||
        previous_slice = []
 | 
					        previous_slice = []
 | 
				
			||||||
        while len(tech_names) > slice_size:
 | 
					        while len(tech_names) > slice_size:
 | 
				
			||||||
            slice = tech_names[:slice_size]
 | 
					            slice = tech_names[:slice_size]
 | 
				
			||||||
| 
						 | 
					@ -71,14 +72,14 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
 | 
				
			||||||
    elif layout == TechTreeLayout.option_funnel:
 | 
					    elif layout == TechTreeLayout.option_funnel:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tech_names: List[str] = list(set(technology_table) - world._static_nodes)
 | 
					        tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
 | 
				
			||||||
        # find largest inverse pyramid
 | 
					        # find largest inverse pyramid
 | 
				
			||||||
        # https://www.wolframalpha.com/input/?i=x+=+1/2+(n++++1)+(2++++n)+solve+for+n
 | 
					        # https://www.wolframalpha.com/input/?i=x+=+1/2+(n++++1)+(2++++n)+solve+for+n
 | 
				
			||||||
        import math
 | 
					        import math
 | 
				
			||||||
        slice_size = int(0.5*(math.sqrt(8*len(tech_names)+1)-3))
 | 
					        slice_size = int(0.5*(math.sqrt(8*len(tech_names)+1)-3))
 | 
				
			||||||
        tech_names.sort()
 | 
					        tech_names.sort()
 | 
				
			||||||
        world.random.shuffle(tech_names)
 | 
					        world.random.shuffle(tech_names)
 | 
				
			||||||
        tech_names.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients))
 | 
					        tech_names.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients))
 | 
				
			||||||
        previous_slice = []
 | 
					        previous_slice = []
 | 
				
			||||||
        while slice_size:
 | 
					        while slice_size:
 | 
				
			||||||
            slice = tech_names[:slice_size]
 | 
					            slice = tech_names[:slice_size]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,18 +19,15 @@ always = lambda state: True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Technology():  # maybe make subclass of Location?
 | 
					class Technology():  # maybe make subclass of Location?
 | 
				
			||||||
    def __init__(self, name, ingredients):
 | 
					    def __init__(self, name, ingredients, factorio_id):
 | 
				
			||||||
        self.name = name
 | 
					        self.name = name
 | 
				
			||||||
        global factorio_id
 | 
					 | 
				
			||||||
        self.factorio_id = factorio_id
 | 
					        self.factorio_id = factorio_id
 | 
				
			||||||
        factorio_id += 1
 | 
					 | 
				
			||||||
        self.ingredients = ingredients
 | 
					        self.ingredients = ingredients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def build_rule(self, allowed_packs, player: int):
 | 
					    def build_rule(self, player: int):
 | 
				
			||||||
        logging.debug(f"Building rules for {self.name}")
 | 
					        logging.debug(f"Building rules for {self.name}")
 | 
				
			||||||
        ingredient_rules = []
 | 
					        ingredient_rules = []
 | 
				
			||||||
        for ingredient in self.ingredients:
 | 
					        for ingredient in self.ingredients:
 | 
				
			||||||
            if ingredient in allowed_packs:
 | 
					 | 
				
			||||||
            logging.debug(f"Building rules for ingredient {ingredient}")
 | 
					            logging.debug(f"Building rules for ingredient {ingredient}")
 | 
				
			||||||
            technologies = required_technologies[ingredient]  # technologies that unlock the recipes
 | 
					            technologies = required_technologies[ingredient]  # technologies that unlock the recipes
 | 
				
			||||||
            if technologies:
 | 
					            if technologies:
 | 
				
			||||||
| 
						 | 
					@ -58,6 +55,23 @@ class Technology():  # maybe make subclass of Location?
 | 
				
			||||||
    def __repr__(self):
 | 
					    def __repr__(self):
 | 
				
			||||||
        return f"{self.__class__.__name__}({self.name})"
 | 
					        return f"{self.__class__.__name__}({self.name})"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_custom(self, world, allowed_packs: Set[str], player: int) -> CustomTechnology:
 | 
				
			||||||
 | 
					        return CustomTechnology(self, world, allowed_packs, player)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CustomTechnology(Technology):
 | 
				
			||||||
 | 
					    """A particularly configured Technology for a world."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, origin: Technology, world, allowed_packs: Set[str], player: int):
 | 
				
			||||||
 | 
					        ingredients = origin.ingredients & allowed_packs
 | 
				
			||||||
 | 
					        self.player = player
 | 
				
			||||||
 | 
					        if world.random_tech_ingredients[player]:
 | 
				
			||||||
 | 
					            ingredients = list(ingredients)
 | 
				
			||||||
 | 
					            ingredients.sort() # deterministic sample
 | 
				
			||||||
 | 
					            ingredients = world.random.sample(ingredients, world.random.randint(1, len(ingredients)))
 | 
				
			||||||
 | 
					        super(CustomTechnology, self).__init__(origin.name, ingredients, origin.factorio_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Recipe():
 | 
					class Recipe():
 | 
				
			||||||
    def __init__(self, name, category, ingredients, products):
 | 
					    def __init__(self, name, category, ingredients, products):
 | 
				
			||||||
| 
						 | 
					@ -80,7 +94,8 @@ for technology_name in sorted(raw):
 | 
				
			||||||
    data = raw[technology_name]
 | 
					    data = raw[technology_name]
 | 
				
			||||||
    factorio_id += 1
 | 
					    factorio_id += 1
 | 
				
			||||||
    current_ingredients = set(data["ingredients"])
 | 
					    current_ingredients = set(data["ingredients"])
 | 
				
			||||||
    technology = Technology(technology_name, current_ingredients)
 | 
					    technology = Technology(technology_name, current_ingredients, factorio_id)
 | 
				
			||||||
 | 
					    factorio_id += 1
 | 
				
			||||||
    tech_table[technology_name] = technology.factorio_id
 | 
					    tech_table[technology_name] = technology.factorio_id
 | 
				
			||||||
    technology_table[technology_name] = technology
 | 
					    technology_table[technology_name] = technology
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,8 @@ def gen_factorio(world: MultiWorld, player: int):
 | 
				
			||||||
            loc.event = tech_item.advancement
 | 
					            loc.event = tech_item.advancement
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            world.itempool.append(tech_item)
 | 
					            world.itempool.append(tech_item)
 | 
				
			||||||
    set_rules(world, player)
 | 
					    world.custom_data[player]["custom_technologies"] = custom_technologies = set_custom_technologies(world, player)
 | 
				
			||||||
 | 
					    set_rules(world, player, custom_technologies)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def factorio_create_regions(world: MultiWorld, player: int):
 | 
					def factorio_create_regions(world: MultiWorld, player: int):
 | 
				
			||||||
| 
						 | 
					@ -31,22 +32,30 @@ def factorio_create_regions(world: MultiWorld, player: int):
 | 
				
			||||||
    crash.connect(nauvis)
 | 
					    crash.connect(nauvis)
 | 
				
			||||||
    world.regions += [menu, nauvis]
 | 
					    world.regions += [menu, nauvis]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def set_custom_technologies(world: MultiWorld, player: int):
 | 
				
			||||||
 | 
					    custom_technologies = {}
 | 
				
			||||||
 | 
					    world_custom = getattr(world, "_custom_technologies", {})
 | 
				
			||||||
 | 
					    world_custom[player] = custom_technologies
 | 
				
			||||||
 | 
					    world._custom_technologies = world_custom
 | 
				
			||||||
 | 
					    allowed_packs = world.max_science_pack[player].get_allowed_packs()
 | 
				
			||||||
 | 
					    for technology_name, technology in technology_table.items():
 | 
				
			||||||
 | 
					        custom_technologies[technology_name] = technology.get_custom(world, allowed_packs, player)
 | 
				
			||||||
 | 
					    return custom_technologies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def set_rules(world: MultiWorld, player: int):
 | 
					def set_rules(world: MultiWorld, player: int, custom_technologies):
 | 
				
			||||||
    shapes = get_shapes(world, player)
 | 
					    shapes = get_shapes(world, player)
 | 
				
			||||||
    if world.logic[player] != 'nologic':
 | 
					    if world.logic[player] != 'nologic':
 | 
				
			||||||
        from worlds.generic import Rules
 | 
					        from worlds.generic import Rules
 | 
				
			||||||
        allowed_packs = world.max_science_pack[player].get_allowed_packs()
 | 
					
 | 
				
			||||||
        for tech_name, technology in technology_table.items():
 | 
					        for tech_name, technology in custom_technologies.items():
 | 
				
			||||||
            # loose nodes
 | 
					 | 
				
			||||||
            location = world.get_location(tech_name, player)
 | 
					            location = world.get_location(tech_name, player)
 | 
				
			||||||
            Rules.set_rule(location, technology.build_rule(allowed_packs, player))
 | 
					            Rules.set_rule(location, technology.build_rule(player))
 | 
				
			||||||
            prequisites = shapes.get(tech_name)
 | 
					            prequisites = shapes.get(tech_name)
 | 
				
			||||||
            if prequisites:
 | 
					            if prequisites:
 | 
				
			||||||
                locations = {world.get_location(requisite, player) for requisite in prequisites}
 | 
					                locations = {world.get_location(requisite, player) for requisite in prequisites}
 | 
				
			||||||
                Rules.add_rule(location, lambda state,
 | 
					                Rules.add_rule(location, lambda state,
 | 
				
			||||||
                                                locations=locations: all(state.can_reach(loc) for loc in locations))
 | 
					                                                locations=locations: all(state.can_reach(loc) for loc in locations))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # get all technologies
 | 
					        # get all science pack technologies (but not the ability to craft them)
 | 
				
			||||||
        world.completion_condition[player] = lambda state: all(state.has(technology, player)
 | 
					        world.completion_condition[player] = lambda state: all(state.has(technology, player)
 | 
				
			||||||
                                                               for technology in advancement_technologies)
 | 
					                                                               for technology in advancement_technologies)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue