Skip to content

Commit

Permalink
Add Rifle parts as items to reduce power of having early Shadow Rifle.
Browse files Browse the repository at this point in the history
Resolve oddities with locked locations towards the pool.
Add easy boss logic option to require a weapon available in the stage.
Add easy craft logic requiring Shadow Rifle to destroy crafts.
Add frequency percentage fields to reduce number of checks for improved balance vs other games / overload of junk items.
Fix issue with Boss Tokens not checking completion boss rule.
Add additional handling for disabling Devil Doom due to softlock.
Handle level progression decreasing, such as disabling FH Shields or using the Heal Cannon.
Fix logic issue with weapon access due to lambda capture issue.
  • Loading branch information
choatix committed Dec 31, 2024
1 parent 97254aa commit 7f8d116
Show file tree
Hide file tree
Showing 8 changed files with 411 additions and 77 deletions.
69 changes: 53 additions & 16 deletions worlds/shadow_the_hedgehog/Items.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
BASE_ID = 1743800000
ITEM_ID_START_AT_WEAPONS = 2000
ITEM_ID_START_AT_VEHICLES = 2500
ITEM_ID_START_AT_RIFLE = 2600
ITEM_ID_START_AT_JUNK = 3000
ITEM_ID_START_AT_MISSION = 1000
ITEM_ID_START_AT_IMPORTANT = 10
Expand Down Expand Up @@ -62,6 +63,13 @@ class Progression:
#ObjectiveDarkToken = "Objective Dark Token"
#ObjectiveHeroToken = "Objective Hero Token"

class ShadowRifleComponents:
ShadowRifleBarrel = "Shadow Rifle Barrel"
ShadowRifleAction = "Shadow Rifle Action"
ShadowRifleStock = "Shadow Rifle Stock"
ShadowRifleReceiver = "Shadow Rifle Receiver"
ShadowRifleMagazine = "Shadow Rifle Magazine"

TOKENS = [
Progression.StandardHeroToken, Progression.StandardDarkToken, Progression.StandardMissionToken,
#Progression.FinalHeroToken, Progression.ObjectiveDarkToken,
Expand Down Expand Up @@ -348,6 +356,18 @@ def GetVehicles():
return vehicles


def GetRifleComponents():
id_s = ITEM_ID_START_AT_RIFLE
rifle_components = []
for rifle_name in ShadowRifleComponents.__dict__.keys():
if "ShadowRifle" in rifle_name:
rifle_components.append(ItemInfo(id_s + len(rifle_components), ShadowRifleComponents.__dict__[rifle_name], ItemClassification.progression,
None, None, "Rifle Component", None)
)

return rifle_components


def GetJunkItemInfo():
junk_items = []

Expand Down Expand Up @@ -400,8 +420,10 @@ def GetAllItemInfo():
weapon_items = GetWeapons()
vehicle_items = GetVehicles()

rifle_components = GetRifleComponents()

return (emerald_items, key_items, level_unlock_items, stage_objective_items, junk_items,
token_items, weapon_items, vehicle_items, level_warp_items)
token_items, weapon_items, vehicle_items, level_warp_items, rifle_components)

useful_to_count = {
"Egg Vacuum": 2,
Expand Down Expand Up @@ -439,14 +461,17 @@ def ChooseJunkItems(random, junk, options, junk_count):

def CountItems(world: World):
(emerald_items, key_items, level_unlock_items, stage_objective_items,
junk_items, token_items, weapon_items, vehicle_items, warp_items) = GetAllItemInfo()
junk_items, token_items, weapon_items, vehicle_items, warp_items, rifle_components) = GetAllItemInfo()

if not world.options.objective_sanity:
stage_objective_items = []

if not world.options.secret_story_progression:
warp_items = []

if not world.options.rifle_components:
rifle_components = []

stage_objective_items = [ s for s in stage_objective_items if s.stageId in world.available_levels ]

# Don't use level unlocks for stages you start with!
Expand Down Expand Up @@ -501,7 +526,7 @@ def GetPotentialDowngradeItems(world, mw_stage_items=None):
to_remove = []
if mw_stage_items is None:
(emerald_items, key_items, level_unlock_items, stage_objective_items,
junk_items, token_items, weapon_items, vehicle_items, warp_items) = GetAllItemInfo()
junk_items, token_items, weapon_items, vehicle_items, warp_items, rifle_components) = GetAllItemInfo()

# Handle available

Expand Down Expand Up @@ -540,10 +565,16 @@ def GetPotentialDowngradeItems(world, mw_stage_items=None):

return potential_downgrade, to_remove

def GetShadowRifle():
(emerald_items, key_items, level_unlock_items, stage_objective_items,
junk_items, token_items, weapon_items, vehicle_items, warp_items, rifle_components) = GetAllItemInfo()

return [ w for w in weapon_items if w.name == 'Shadow Rifle' or w.name == 'Weapon:Shadow Rifle' ][0]

def PopulateItemPool(world : World, first_regions):
# TODO: Do not add item for stages you start with
(emerald_items, key_items, level_unlock_items, stage_objective_items,
junk_items, token_items, weapon_items, vehicle_items, warp_items) = GetAllItemInfo()
junk_items, token_items, weapon_items, vehicle_items, warp_items, rifle_components) = GetAllItemInfo()

if not world.options.objective_sanity:
stage_objective_items = []
Expand Down Expand Up @@ -606,18 +637,27 @@ def PopulateItemPool(world : World, first_regions):
for remove in to_remove:
mw_stage_items.remove(remove)


weapon_dict = Weapons.GetWeaponDict()
special_weapon_extras = [w for w in weapon_items if
Weapons.WeaponAttributes.SPECIAL in weapon_dict[w.name].attributes and
w.name != 'Shadow Rifle' and w.name != 'Weapon:Shadow Rifle']

weapon_items = [w for w in weapon_items if
w.name != "Weapon:Shadow Rifle" and
w.name != "Shadow Rifle"]

weapon_items.extend(special_weapon_extras)
mw_weapon_items = [ ShadowTheHedgehogItem(w, world.player) for w in weapon_items ]
shadow_rifle = GetShadowRifle()
if not world.options.rifle_components:
special_weapon_extras.append(shadow_rifle)
weapon_items.append(shadow_rifle)
else:
special_weapon_extras.extend(rifle_components)
weapon_items.extend(rifle_components)

mw_weapon_items = [ ShadowTheHedgehogItem(w, world.player) for w in weapon_items]

mw_weapon_special_only = [ ShadowTheHedgehogItem(w, world.player) for w in special_weapon_extras ]
mw_weapon_special_only.extend( [ShadowTheHedgehogItem(w, world.player) for w in weapon_items\
if w.name == 'Shadow Rifle' or w.name == 'Weapon:Shadow Rifle'])
mw_weapon_special_only_dupes = [ShadowTheHedgehogItem(w, world.player) for w in special_weapon_extras]
mw_weapon_special_only.extend(mw_weapon_special_only_dupes)

Expand All @@ -626,6 +666,7 @@ def PopulateItemPool(world : World, first_regions):
if world.options.weapon_sanity_unlock and \
world.options.weapon_sanity_hold != Options.WeaponsanityHold.option_unlocked:
for weapon in mw_weapon_items:

matching_w = [ w for w in Weapons.WEAPON_INFO if w.name == weapon.name ]
if len(matching_w) == 0:
continue
Expand All @@ -634,7 +675,7 @@ def PopulateItemPool(world : World, first_regions):
weapon.classification = ItemClassification.filler
#print(weapon.name, "is now filler")

item_count = (len(mw_level_unlock_items) + len(mw_stage_items) + 1) # end item
item_count = (len(mw_level_unlock_items) + len(mw_stage_items))
if world.options.goal_chaos_emeralds:
item_count += len(mw_em_items)

Expand All @@ -654,10 +695,7 @@ def PopulateItemPool(world : World, first_regions):
if item.name in useful_to_count:
mw_useful_items.extend([ ShadowTheHedgehogItem(item, world.player) for _ in range(0, useful_to_count[item.name])])

#junk_items = [ ShadowTheHedgehogItem(

junk_count = location_count - item_count - len(mw_useful_items)
#print("Create junk items:", junk_count)
mw_junk_items = [ ShadowTheHedgehogItem(i, world.player) for i in ChooseJunkItems(world.random, junk_items, world.options, junk_count) ]

if world.options.goal_chaos_emeralds:
Expand All @@ -668,8 +706,6 @@ def PopulateItemPool(world : World, first_regions):
world.multiworld.itempool += mw_useful_items
world.multiworld.itempool += mw_junk_items

# TODO: Make this work!
#if False:
if world.options.weapon_sanity_unlock:
world.multiworld.itempool += mw_weapon_items
else:
Expand All @@ -680,14 +716,15 @@ def PopulateItemPool(world : World, first_regions):

def get_item_groups():
(emerald_items, key_items, level_unlock_items, stage_objective_items,
junk_items, token_items, weapon_items, vehicle_items, warp_items) = GetAllItemInfo()
junk_items, token_items, weapon_items, vehicle_items, warp_items, rifle_components) = GetAllItemInfo()

item_groups: typing.Dict[str, list] = {
"Chaos Emeralds": [ e.name for e in emerald_items],
"Stage Items": [e.name for e in level_unlock_items],
"Weapons": [e.name for e in weapon_items],
"Vehicles": [e.name for e in vehicle_items],
"Vacuums": [w.name for w in weapon_items if "Vacuum" in w.name ]
"Vacuums": [w.name for w in weapon_items if "Vacuum" in w.name ],
"Rifles": [w.name for w in rifle_components]
}

return item_groups
Expand Down
11 changes: 10 additions & 1 deletion worlds/shadow_the_hedgehog/Levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
BOSS_DEVIL_DOOM = 710

LOCATION_ID_PLUS = 100066
LOCATION_ID_SHADOW_RIFLE_COMPLETE = 100067

LEVEL_ID_TO_LEVEL = {
STAGE_WESTOPOLIS: "Westopolis",
Expand Down Expand Up @@ -171,11 +172,19 @@ class REGION_RESTRICTION_TYPES:
BlackArmsTurret = 14
GunTurret = 15
ShootOrTurret = 16
AnyStageWeapon = 17
ShadowRifle = 18


class REGION_RESTRICTION_REFERENCE_TYPES:
BaseLogic = 1
BossLogic = 2
CraftLogic = 3

def IsWeaponsanityRestriction(restriction_type):
weapons = [REGION_RESTRICTION_TYPES.Torch, REGION_RESTRICTION_TYPES.LongRangeGun,
REGION_RESTRICTION_TYPES.Vacuum, REGION_RESTRICTION_TYPES.Gun,
REGION_RESTRICTION_TYPES.Heal]
REGION_RESTRICTION_TYPES.Heal, REGION_RESTRICTION_TYPES.AnyStageWeapon]
return restriction_type in weapons

def IsVeichleSanityRestriction(restriction_type):
Expand Down
Loading

0 comments on commit 7f8d116

Please sign in to comment.