def _get_reward_for_hero(game_state: GameState) -> ItemId: hero_id = game_state.player_state.hero_id if hero_id == HeroId.MAGE: return randomized_item_id(ItemType.STAFF_OF_FIRE) elif hero_id == HeroId.ROGUE: return randomized_item_id(ItemType.THIEFS_MASK) elif hero_id == HeroId.WARRIOR: return randomized_item_id(ItemType.CLEAVER) else: return randomized_item_id(ItemType.LEATHER_ARMOR)
def _put_loot_on_ground(self, enemy_death_position: Tuple[int, int], loot: List[LootEntry]): for loot_entry in loot: if len(loot) > 1: position_offset = (random.randint(-20, 20), random.randint(-20, 20)) else: position_offset = (0, 0) loot_position = sum_of_vectors(enemy_death_position, position_offset) if isinstance(loot_entry, MoneyLootEntry): money_pile_on_ground = create_money_pile_on_ground( loot_entry.amount, loot_position) self.game_state.money_piles_on_ground.append( money_pile_on_ground) elif isinstance(loot_entry, ItemLootEntry): item_id = randomized_item_id(loot_entry.item_type) item_on_ground = create_item_on_ground(item_id, loot_position) self.game_state.items_on_ground.append(item_on_ground) elif isinstance(loot_entry, SuffixedItemLootEntry): item_id = randomized_suffixed_item_id(loot_entry.item_type, loot_entry.suffix_id) item_on_ground = create_item_on_ground(item_id, loot_position) self.game_state.items_on_ground.append(item_on_ground) elif isinstance(loot_entry, ConsumableLootEntry): consumable_on_ground = create_consumable_on_ground( loot_entry.consumable_type, loot_position) self.game_state.consumables_on_ground.append( consumable_on_ground)
def register_challenge_starter_npc(): size = ( 30, 30 ) # Must not align perfectly with grid cell size (pathfinding issues) sprite = Sprite.NEUTRAL_NPC_CHALLENGE_STARTER portrait_icon_sprite = PortraitIconSprite.CHALLENGE_STARTER npc_type = NpcType.NEUTRAL_CHALLENGE_STARTER movement_speed = 0.03 register_npc_data(npc_type, NpcData.neutral(sprite, size, movement_speed)) register_npc_behavior(npc_type, NpcMind) dialog_options = [ buy_item_option(randomized_item_id(ItemType.GLADIATOR_ARMOR), 20), buy_item_option(randomized_item_id(ItemType.HEALING_WAND), 20), buy_item_option(randomized_item_id(ItemType.ZULS_AEGIS), 20), buy_item_option(randomized_item_id(ItemType.WARLOCKS_COWL), 20), buy_item_option(randomized_item_id(ItemType.DRUIDS_RING), 20), DialogOptionData("\"Good bye\"", "cancel", None) ] dialog_text_body = "Choose wisely." dialog_data = DialogData("Vendor", portrait_icon_sprite, dialog_text_body, dialog_options) register_npc_dialog_data(npc_type, dialog_data) sprite_sheet = SpriteSheet("resources/graphics/enemy_sprite_sheet_3.png") original_sprite_size = (32, 32) scaled_sprite_size = (48, 48) x = 6 indices_by_dir = { Direction.DOWN: [(x, 0), (x + 1, 0), (x + 2, 0)], Direction.LEFT: [(x, 1), (x + 1, 1), (x + 2, 1)], Direction.RIGHT: [(x, 2), (x + 1, 2), (x + 2, 2)], Direction.UP: [(x, 3), (x + 1, 3), (x + 2, 3)] } register_entity_sprite_map(sprite, sprite_sheet, original_sprite_size, scaled_sprite_size, indices_by_dir, (-8, -16)) register_portrait_icon_sprite_path( portrait_icon_sprite, 'resources/graphics/ninja_portrait.png')
def on_startup(self, new_hero_was_created: bool): self.game_state.player_state.gain_buff_effect( get_buff_effect(BuffType.BEING_SPAWNED), Millis(1000)) self.info_message.set_message("Challenge starting...") if new_hero_was_created: self.game_state.player_state.modify_money(100) self.game_engine.gain_levels(4) consumables = [ ConsumableType.HEALTH, ConsumableType.HEALTH, ConsumableType.MANA, ConsumableType.MANA, ConsumableType.SPEED, ConsumableType.POWER ] for consumable_type in consumables: self.game_state.player_state.consumable_inventory.add_consumable( consumable_type) items = [ randomized_item_id(ItemType.LEATHER_COWL), randomized_item_id(ItemType.LEATHER_ARMOR), randomized_item_id(ItemType.PRACTICE_SWORD), randomized_item_id(ItemType.WOODEN_SHIELD) ] for item_type in items: self._equip_item_on_startup(item_type)
def print_loot(): for level in range(1, 10): consumable_types = get_consumables_with_level(level) item_types = get_items_with_level(level) if not consumable_types and not item_types: continue print("LEVEL " + str(level)) print("- - - - -") for c_type in consumable_types: print("{:<25}".format(c_type.name) + str(CONSUMABLES[c_type].description)) for i_type in item_types: data = get_item_data_by_type(i_type) description_lines = [line.text for line in create_item_description(randomized_item_id(i_type))] print("{:<25}".format(data.base_name) + ", ".join(description_lines)) print("")
def print_items(): for category in [c for c in ItemEquipmentCategory] + [None]: if category: print(category.name + ":") else: print("PASSIVE:") print("- - - - -") entries: List[Tuple[ItemType, ItemData, Optional[int]]] = [ (i_type, i_data, get_optional_item_level(i_type)) for (i_type, i_data) in get_items_with_category(category) ] entries.sort(key=lambda entry: entry[2] if entry[2] else 9999) for item_type, item_data, item_level in entries: level_text = "level {:<10}".format( item_level) if item_level else " " * 16 item_id = randomized_item_id(item_type) item_name = build_item_name(item_id) print("{:<25}".format(item_name) + level_text + str(create_item_description(item_id))) print("")
def _put_loot_on_ground(self, enemy_death_position: Tuple[int, int], loot: List[LootEntry]): for loot_entry in loot: if len(loot) > 1: position_offset = (random.randint(-20, 20), random.randint(-20, 20)) else: position_offset = (0, 0) loot_position = sum_of_vectors(enemy_death_position, position_offset) if isinstance(loot_entry, MoneyLootEntry): money_pile_on_ground = create_money_pile_on_ground( loot_entry.amount, loot_position) self.game_state.game_world.money_piles_on_ground.append( money_pile_on_ground) elif isinstance(loot_entry, ItemLootEntry): item_id = randomized_item_id(loot_entry.item_type) item_on_ground = create_item_on_ground(item_id, loot_position) self.game_state.game_world.items_on_ground.append( item_on_ground) elif isinstance(loot_entry, AffixedItemLootEntry): item_id = loot_entry.item_id item_on_ground = create_item_on_ground(item_id, loot_position) self.game_state.game_world.items_on_ground.append( item_on_ground) loot_center_pos = (loot_position[0] + ITEM_ENTITY_SIZE[0] // 2, loot_position[1] + ITEM_ENTITY_SIZE[1] // 2) self.game_state.game_world.visual_effects.append( VisualCircle((170, 200, 170), loot_center_pos, 30, 40, Millis(500), 2)) self.game_state.game_world.visual_effects.append( VisualCircle((70, 100, 70), loot_center_pos, 25, 35, Millis(500), 2)) elif isinstance(loot_entry, ConsumableLootEntry): consumable_on_ground = create_consumable_on_ground( loot_entry.consumable_type, loot_position) self.game_state.game_world.consumables_on_ground.append( consumable_on_ground)
def _get_initial_player_state_warrior() -> InitialPlayerStateData: health = 60 mana = 30 mana_regen = 2 health_per_level = 15 mana_per_level = 5 armor_per_level = 1 level_bonus = PlayerLevelBonus(health_per_level, mana_per_level, armor_per_level) armor = 3 dodge_chance = 0.05 consumable_slots = {1: [], 2: [], 3: [], 4: [], 5: []} abilities = [AbilityType.SWORD_SLASH] new_level_abilities = { 2: AbilityType.CHARGE, 5: AbilityType.BLOOD_LUST, 7: AbilityType.STOMP } talents_state = TalentsConfig({ 3: TALENT_CHOICE_ARMOR_DAMAGE, 4: TalentTierConfig( TalentTierOptionConfig( "Close combat", "Your Charge ability deals full damage even when used at close range", HeroUpgradeId.ABILITY_CHARGE_MELEE, UiIconSprite.ABILITY_CHARGE), TalentTierOptionConfig( "Brawl", "Your Slash ability deals max damage if at least 2 enemies are hit", HeroUpgradeId.ABILITY_SLASH_AOE_BONUS_DAMAGE, UiIconSprite.ABILITY_SWORD_SLASH)), 5: TALENT_CHOICE_HEALTH_MANA, 6: TalentTierConfig( TalentTierOptionConfig( "Bloodthirst", "The duration of your Bloodlust ability is now increased by " + "{:.1f}".format( BLOODLUST_UPGRADED_INCREASED_DURATION_FROM_KILL / 1000) + "s on kills", HeroUpgradeId.ABILITY_BLOODLUST_DURATION, UiIconSprite.ABILITY_BLOODLUST), TalentTierOptionConfig( "Berserker", "Reduces the cooldown of your Slash ability to " + "{:.1f}".format(ABILITY_SLASH_UPGRADED_COOLDOWN / 1000) + "s", HeroUpgradeId.ABILITY_SLASH_CD, UiIconSprite.ABILITY_SWORD_SLASH)), 7: TALENT_CHOICE_HEALTH_MANA_REGEN, 8: TalentTierConfig( TalentTierOptionConfig( "Juggernaut", "Hitting an enemy with your Charge ability resets the cooldown of War Stomp", HeroUpgradeId.ABILITY_CHARGE_RESET_STOMP_COOLDOWN, UiIconSprite.ABILITY_STOMP), TalentTierOptionConfig( "Retribution", "Increases your block chance by " + str(int(BUFF_RETRIBUTION_BONUS_BLOCK_CHANCE * 100)) + "%. Blocking an enemy attack gives +" + str(int(BUFF_RETRIBUTION_BONUS_DAMAGE * 100)) + "% damage for " + "{:.1f}".format(BUFF_RETRIBUTION_DURATION / 1000) + "s", RetributionHeroUpgrade(HeroUpgradeId.WARRIOR_RETRIBUTION), UiIconSprite.ITEM_SKULL_SHIELD)), }) block_chance = 0.2 return InitialPlayerStateData( health, mana, mana_regen, consumable_slots, abilities, new_level_abilities, HERO_ID, armor, dodge_chance, level_bonus, talents_state, block_chance, [ randomized_item_id(ItemType.PRACTICE_SWORD), randomized_item_id(ItemType.WOODEN_SHIELD) ])
def _get_initial_player_state_mage() -> InitialPlayerStateData: health = 40 mana = 60 mana_regen = 3.5 health_per_level = 5 mana_per_level = 10 armor_per_level = 0.3 level_bonus = PlayerLevelBonus(health_per_level, mana_per_level, armor_per_level) armor = 1 dodge_chance = 0.05 consumable_slots = { 1: [], 2: [], 3: [], 4: [], 5: [] } abilities = [AbilityType.FIREBALL] new_level_abilities = { 2: AbilityType.WHIRLWIND, 5: AbilityType.ENTANGLING_ROOTS, 7: AbilityType.ARCANE_FIRE } talents_state = TalentsConfig( { 3: TALENT_CHOICE_ARMOR_DAMAGE, 4: TalentTierConfig( TalentTierOptionConfig("Raging fire", "Enemies hit by your fireballs take additional " + str(FIREBALL_TALENT_BURN_TOTAL_DAMAGE) + " damage over " + "{:.1f}".format(FIREBALL_TALENT_BURN_DURATION / 1000) + "s", HeroUpgradeId.ABILITY_FIREBALL_BURN, UiIconSprite.ABILITY_FIREBALL), TalentTierOptionConfig("Hurricane", "Whirlwind has a chance to stun affected enemies for " + "{:.1f}".format(WHIRLWIND_TALENT_STUN_DURATION / 1000) + "s", HeroUpgradeId.ABILITY_WHIRLWIND_STUN, UiIconSprite.ABILITY_WHIRLWIND)), 5: TALENT_CHOICE_HEALTH_MANA, 6: TalentTierConfig( TalentTierOptionConfig("Swift justice", "Reduces the cooldown of your Entangling Roots ability to " + "{:.1f}".format(ENTANGLING_ROOTS_UPGRADED_COOLDOWN / 1000) + "s", HeroUpgradeId.ABILITY_ENTANGLING_ROOTS_COOLDOWN, UiIconSprite.ABILITY_ENTANGLING_ROOTS), TalentTierOptionConfig("Flamethrower", "Reduces the mana-cost of your Fireball ability to " + str(FIREBALL_UPGRADED_MANA_COST), HeroUpgradeId.ABILITY_FIREBALL_MANA_COST, UiIconSprite.ABILITY_FIREBALL)), 7: TALENT_CHOICE_HEALTH_MANA_REGEN, 8: TalentTierConfig( TalentTierOptionConfig("Power hungry", "Reduces the cooldown of your Arcane Fire ability to " + "{:.1f}".format(ARCANE_FIRE_UPGRADED_COOLDOWN / 1000) + "s, but increases its mana-cost to " + str(ARCANE_FIRE_UPGRADED_MANA_COST), HeroUpgradeId.ABILITY_ARCANE_FIRE_COOLDOWN, UiIconSprite.ABILITY_ARCANE_FIRE), TalentTierOptionConfig("Light-footed", "Lets you keep moving while casting Fireball and Whirlwind", HeroUpgradeId.MAGE_LIGHT_FOOTED, UiIconSprite.TALENT_LIGHT_FOOTED)), 9: TALENT_CHOICE_MOVE_SPEED_MAGIC_RESIST, }) block_chance = 0.1 return InitialPlayerStateData( health, mana, mana_regen, consumable_slots, abilities, new_level_abilities, HERO_ID, armor, dodge_chance, level_bonus, talents_state, block_chance, [randomized_item_id(ItemType.NOVICE_WAND)])
def _get_initial_player_state_rogue() -> InitialPlayerStateData: health = 50 mana = 50 mana_regen = 2.5 health_per_level = 10 mana_per_level = 10 armor_per_level = 0.7 level_bonus = PlayerLevelBonus(health_per_level, mana_per_level, armor_per_level) armor = 2 dodge_chance = 0.1 consumable_slots = { 1: [], 2: [], 3: [], 4: [], 5: [] } abilities = [AbilityType.SHIV] new_level_abilities = { 2: AbilityType.STEALTH, 5: AbilityType.DASH, 7: AbilityType.INFUSE_DAGGER } talents_state = TalentsConfig({ 3: TALENT_CHOICE_ARMOR_DAMAGE, 4: TalentTierConfig( TalentTierOptionConfig("Shadowmeld", "Reduces the mana-cost of your Stealth ability to " + str(STEALTH_UPGRADED_MANA_COST), HeroUpgradeId.ABILITY_STEALTH_MANA_COST, UiIconSprite.ABILITY_STEALTH), TalentTierOptionConfig("Ambush", "Increases the damage bonus that Shiv gets from being used from stealth to " + str(int(SHIV_UPGRADED_STEALTH_DAMAGE_MULTIPLIER * 100)) + "%", HeroUpgradeId.ABILITY_SHIV_SNEAK_BONUS_DAMAGE, UiIconSprite.ABILITY_SHIV)), 5: TALENT_CHOICE_HEALTH_MANA, 6: TalentTierConfig( TalentTierOptionConfig("Acrobatics", "The cooldown and mana-cost of your dash ability is reset if it kills an enemy", HeroUpgradeId.ABILITY_DASH_KILL_RESET, UiIconSprite.ABILITY_DASH), TalentTierOptionConfig("First strike", "Shiv deals " + str(int(SHIV_TALENT_FULL_HEALTH_DAMAGE_MULTIPLIER * 100)) + "% damage on enemies that are at full health, unless you're stealthed", HeroUpgradeId.ABILITY_SHIV_FULL_HEALTH_BONUS_DAMAGE, UiIconSprite.ABILITY_SHIV)), 7: TALENT_CHOICE_HEALTH_MANA_REGEN, 8: TalentTierConfig( TalentTierOptionConfig("Master Assassin", "Stealth doesn't reduce movement speed", HeroUpgradeId.ABILITY_STEALTH_MOVEMENT_SPEED, UiIconSprite.ABILITY_STEALTH), TalentTierOptionConfig("Leap", "Dash gives +40% movement speed for 2 seconds", HeroUpgradeId.ABILITY_DASH_MOVEMENT_SPEED, UiIconSprite.ABILITY_DASH)), 9: TALENT_CHOICE_MOVE_SPEED_MAGIC_RESIST, }) block_chance = 0.15 return InitialPlayerStateData( health, mana, mana_regen, consumable_slots, abilities, new_level_abilities, HERO_ID, armor, dodge_chance, level_bonus, talents_state, block_chance, [randomized_item_id(ItemType.PRACTICE_SWORD)])
PYGAME_MOUSE_LEFT_BUTTON = 1 PYGAME_MOUSE_RIGHT_BUTTON = 3 ADVANCED_ENTITIES = [ MapEditorWorldEntity.smart_floor_tile(Sprite.MAP_EDITOR_SMART_FLOOR_1, 25), MapEditorWorldEntity.smart_floor_tile(Sprite.MAP_EDITOR_SMART_FLOOR_2, 50), MapEditorWorldEntity.smart_floor_tile(Sprite.MAP_EDITOR_SMART_FLOOR_3, 75), MapEditorWorldEntity.smart_floor_tile(Sprite.MAP_EDITOR_SMART_FLOOR_4, 100) ] WALL_ENTITIES = [ MapEditorWorldEntity.wall(wall_type) for wall_type in WallType ] NPC_ENTITIES = [MapEditorWorldEntity.npc(npc_type) for npc_type in NpcType] ITEM_ENTITIES = [ MapEditorWorldEntity.item(randomized_item_id(item_type)) for item_type in ItemType ] MISC_ENTITIES: List[MapEditorWorldEntity] = \ [ MapEditorWorldEntity.player(), MapEditorWorldEntity.chest(), MapEditorWorldEntity.money(1), MapEditorWorldEntity.decoration(Sprite.DECORATION_GROUND_STONE), MapEditorWorldEntity.decoration(Sprite.DECORATION_GROUND_STONE_GRAY), MapEditorWorldEntity.decoration(Sprite.DECORATION_PLANT), MapEditorWorldEntity.shrine(), MapEditorWorldEntity.dungeon_entrance() ] + \ [MapEditorWorldEntity.consumable(consumable_type) for consumable_type in ConsumableType] + \ [MapEditorWorldEntity.portal(portal_id) for portal_id in PortalId]