5
5
from copy import deepcopy
6
6
import itertools
7
7
import operator
8
+ from collections import defaultdict , Counter
8
9
9
10
logger = logging .getLogger ("Hollow Knight" )
10
11
11
12
from .Items import item_table , lookup_type_to_names , item_name_groups
12
13
from .Regions import create_regions
13
14
from .Rules import set_rules , cost_terms , _hk_can_beat_thk , _hk_siblings_ending , _hk_can_beat_radiance
14
15
from .Options import hollow_knight_options , hollow_knight_randomize_options , Goal , WhitePalace , CostSanity , \
15
- shop_to_option , HKOptions
16
+ shop_to_option , HKOptions , GrubHuntGoal
16
17
from .ExtractedData import locations , starts , multi_locations , location_to_region_lookup , \
17
18
event_names , item_effects , connectors , one_ways , vanilla_shop_costs , vanilla_location_costs
18
19
from .Charms import names as charm_names
19
20
20
- from BaseClasses import Region , Location , MultiWorld , Item , LocationProgressType , Tutorial , ItemClassification
21
+ from BaseClasses import Region , Location , MultiWorld , Item , LocationProgressType , Tutorial , ItemClassification , CollectionState
21
22
from worlds .AutoWorld import World , LogicMixin , WebWorld
22
23
23
24
path_of_pain_locations = {
@@ -155,6 +156,7 @@ class HKWorld(World):
155
156
ranges : typing .Dict [str , typing .Tuple [int , int ]]
156
157
charm_costs : typing .List [int ]
157
158
cached_filler_items = {}
159
+ grub_count : int
158
160
159
161
def __init__ (self , multiworld , player ):
160
162
super (HKWorld , self ).__init__ (multiworld , player )
@@ -164,6 +166,7 @@ def __init__(self, multiworld, player):
164
166
self .ranges = {}
165
167
self .created_shop_items = 0
166
168
self .vanilla_shop_costs = deepcopy (vanilla_shop_costs )
169
+ self .grub_count = 0
167
170
168
171
def generate_early (self ):
169
172
options = self .options
@@ -201,7 +204,7 @@ def create_regions(self):
201
204
202
205
# check for any goal that godhome events are relevant to
203
206
all_event_names = event_names .copy ()
204
- if self .options .Goal in [Goal .option_godhome , Goal .option_godhome_flower ]:
207
+ if self .options .Goal in [Goal .option_godhome , Goal .option_godhome_flower , Goal . option_any ]:
205
208
from .GodhomeData import godhome_event_names
206
209
all_event_names .update (set (godhome_event_names ))
207
210
@@ -441,12 +444,67 @@ def set_rules(self):
441
444
multiworld .completion_condition [player ] = lambda state : state .count ("Defeated_Pantheon_5" , player )
442
445
elif goal == Goal .option_godhome_flower :
443
446
multiworld .completion_condition [player ] = lambda state : state .count ("Godhome_Flower_Quest" , player )
447
+ elif goal == Goal .option_grub_hunt :
448
+ pass # will set in stage_pre_fill()
444
449
else :
445
450
# Any goal
446
- multiworld .completion_condition [player ] = lambda state : _hk_can_beat_thk (state , player ) or _hk_can_beat_radiance (state , player )
451
+ multiworld .completion_condition [player ] = lambda state : _hk_siblings_ending (state , player ) and \
452
+ _hk_can_beat_radiance (state , player ) and state .count ("Godhome_Flower_Quest" , player )
447
453
448
454
set_rules (self )
449
455
456
+ @classmethod
457
+ def stage_pre_fill (cls , multiworld : "MultiWorld" ):
458
+ def set_goal (player , grub_rule : typing .Callable [[CollectionState ], bool ]):
459
+ world = multiworld .worlds [player ]
460
+
461
+ if world .options .Goal == "grub_hunt" :
462
+ multiworld .completion_condition [player ] = grub_rule
463
+ else :
464
+ old_rule = multiworld .completion_condition [player ]
465
+ multiworld .completion_condition [player ] = lambda state : old_rule (state ) and grub_rule (state )
466
+
467
+ worlds = [world for world in multiworld .get_game_worlds (cls .game ) if world .options .Goal in ["any" , "grub_hunt" ]]
468
+ if worlds :
469
+ grubs = [item for item in multiworld .get_items () if item .name == "Grub" ]
470
+ all_grub_players = [world .player for world in multiworld .worlds .values () if world .options .GrubHuntGoal == GrubHuntGoal .special_range_names ["all" ]]
471
+
472
+ if all_grub_players :
473
+ group_lookup = defaultdict (set )
474
+ for group_id , group in multiworld .groups .items ():
475
+ for player in group ["players" ]:
476
+ group_lookup [group_id ].add (player )
477
+
478
+ grub_count_per_player = Counter ()
479
+ per_player_grubs_per_player = defaultdict (Counter )
480
+
481
+ for grub in grubs :
482
+ player = grub .player
483
+ if player in group_lookup :
484
+ for real_player in group_lookup [player ]:
485
+ per_player_grubs_per_player [real_player ][player ] += 1
486
+ else :
487
+ per_player_grubs_per_player [player ][player ] += 1
488
+
489
+ if grub .location and grub .location .player in group_lookup .keys ():
490
+ for real_player in group_lookup [grub .location .player ]:
491
+ grub_count_per_player [real_player ] += 1
492
+ else :
493
+ grub_count_per_player [player ] += 1
494
+
495
+ for player , count in grub_count_per_player .items ():
496
+ multiworld .worlds [player ].grub_count = count
497
+
498
+ for player , grub_player_count in per_player_grubs_per_player .items ():
499
+ if player in all_grub_players :
500
+ set_goal (player , lambda state , g = grub_player_count : all (state .has ("Grub" , owner , count ) for owner , count in g .items ()))
501
+
502
+ for world in worlds :
503
+ if world .player not in all_grub_players :
504
+ world .grub_count = world .options .GrubHuntGoal .value
505
+ player = world .player
506
+ set_goal (player , lambda state , p = player , c = world .grub_count : state .has ("Grub" , p , c ))
507
+
450
508
def fill_slot_data (self ):
451
509
slot_data = {}
452
510
@@ -484,6 +542,8 @@ def fill_slot_data(self):
484
542
485
543
slot_data ["notch_costs" ] = self .charm_costs
486
544
545
+ slot_data ["grub_count" ] = self .grub_count
546
+
487
547
return slot_data
488
548
489
549
def create_item (self , name : str ) -> HKItem :
0 commit comments