1
1
from typing import Callable , Dict , List , Set
2
2
from BaseClasses import MultiWorld , ItemClassification , Item , Location
3
- from .Items import get_full_item_list , spider_mine_sources , second_pass_placeable_items , filler_items
3
+ from .Items import get_full_item_list , spider_mine_sources , second_pass_placeable_items , filler_items , \
4
+ progressive_if_nco
4
5
from .MissionTables import no_build_regions_list , easy_regions_list , medium_regions_list , hard_regions_list ,\
5
6
mission_orders , MissionInfo , alt_final_mission_locations , MissionPools
6
7
from .Options import get_option_value , MissionOrder , FinalMap , MissionProgressLocations , LocationInclusion
15
16
]
16
17
17
18
BARRACKS_UNITS = {"Marine" , "Medic" , "Firebat" , "Marauder" , "Reaper" , "Ghost" , "Spectre" }
18
- FACTORY_UNITS = {"Hellion" , "Vulture" , "Goliath" , "Diamondback" , "Siege Tank" , "Thor" , "Predator" , "Widow Mine" }
19
+ FACTORY_UNITS = {"Hellion" , "Vulture" , "Goliath" , "Diamondback" , "Siege Tank" , "Thor" , "Predator" , "Widow Mine" , "Cyclone" }
19
20
STARPORT_UNITS = {"Medivac" , "Wraith" , "Viking" , "Banshee" , "Battlecruiser" , "Hercules" , "Science Vessel" , "Raven" , "Liberator" , "Valkyrie" }
20
21
21
22
PROTOSS_REGIONS = {"A Sinister Turn" , "Echoes of the Future" , "In Utter Darkness" }
@@ -93,7 +94,10 @@ def get_item_upgrades(inventory: List[Item], parent_item: Item or str):
93
94
]
94
95
95
96
96
- def get_item_quantity (item ):
97
+ def get_item_quantity (item : Item , multiworld : MultiWorld , player : int ):
98
+ if (not get_option_value (multiworld , player , "nco_items" )) \
99
+ and item .name in progressive_if_nco :
100
+ return 1
97
101
return get_full_item_list ()[item .name ].quantity
98
102
99
103
@@ -138,13 +142,13 @@ def attempt_removal(item: Item) -> bool:
138
142
if not all (requirement (self ) for requirement in requirements ):
139
143
# If item cannot be removed, lock or revert
140
144
self .logical_inventory .add (item .name )
141
- for _ in range (get_item_quantity (item )):
145
+ for _ in range (get_item_quantity (item , self . multiworld , self . player )):
142
146
locked_items .append (copy_item (item ))
143
147
return False
144
148
return True
145
-
149
+
146
150
# Limit the maximum number of upgrades
147
- maxUpgrad = get_option_value (self .multiworld , self .player ,
151
+ maxUpgrad = get_option_value (self .multiworld , self .player ,
148
152
"max_number_of_upgrades" )
149
153
if maxUpgrad != - 1 :
150
154
unit_avail_upgrades = {}
@@ -197,15 +201,16 @@ def attempt_removal(item: Item) -> bool:
197
201
# Don't process general upgrades, they may have been pre-locked per-level
198
202
for item in items_to_lock :
199
203
if item in inventory :
204
+ item_quantity = inventory .count (item )
200
205
# Unit upgrades, lock all levels
201
- for _ in range (inventory . count ( item ) ):
206
+ for _ in range (item_quantity ):
202
207
inventory .remove (item )
203
208
if item not in locked_items :
204
209
# Lock all the associated items if not already locked
205
- for _ in range (get_item_quantity ( item ) ):
210
+ for _ in range (item_quantity ):
206
211
locked_items .append (copy_item (item ))
207
- if item in existing_items :
208
- existing_items .remove (item )
212
+ if item in existing_items :
213
+ existing_items .remove (item )
209
214
210
215
if self .min_units_per_structure > 0 and self .has_units_per_structure ():
211
216
requirements .append (lambda state : state .has_units_per_structure ())
@@ -216,7 +221,13 @@ def attempt_removal(item: Item) -> bool:
216
221
217
222
while len (inventory ) + len (locked_items ) > inventory_size :
218
223
if len (inventory ) == 0 :
219
- raise Exception ("Reduced item pool generation failed - not enough locations available to place items." )
224
+ # There are more items than locations and all of them are already locked due to YAML or logic.
225
+ # Random items from locked ones will go to starting items
226
+ self .multiworld .random .shuffle (locked_items )
227
+ while len (locked_items ) > inventory_size :
228
+ item : Item = locked_items .pop ()
229
+ self .multiworld .push_precollected (item )
230
+ break
220
231
# Select random item from removable items
221
232
item = self .multiworld .random .choice (inventory )
222
233
# Cascade removals to associated items
@@ -245,7 +256,7 @@ def attempt_removal(item: Item) -> bool:
245
256
for _ in range (inventory .count (transient_item )):
246
257
inventory .remove (transient_item )
247
258
if transient_item not in locked_items :
248
- for _ in range (get_item_quantity (transient_item )):
259
+ for _ in range (get_item_quantity (transient_item , self . multiworld , self . player )):
249
260
locked_items .append (copy_item (transient_item ))
250
261
if transient_item .classification in (ItemClassification .progression , ItemClassification .progression_skip_balancing ):
251
262
self .logical_inventory .add (transient_item .name )
0 commit comments