Source code for scripts.engine.internal.definition

from __future__ import annotations

from abc import ABC
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING, Union

from scripts.engine.internal.constant import (
    AfflictionCategory,
    AfflictionCategoryType,
    DamageType,
    DamageTypeType,
    Direction,
    DirectionType,
    EffectType,
    EffectTypeType,
    Height,
    HeightType,
    PrimaryStat,
    PrimaryStatType,
    ProjectileExpiryType,
    ProjectileSpeed,
    ProjectileSpeedType,
    ReactionTriggerType,
    RenderLayer,
    Resource,
    ResourceType,
    SecondaryStat,
    SecondaryStatType,
    Shape,
    ShapeType,
    TargetingMethod,
    TargetingMethodType,
    TerrainCollisionType,
    TileTagType,
    TraitGroup,
    TraitGroupType,
    TravelMethod,
    TravelMethodType,
)
from scripts.engine.internal.extend_json import register_dataclass_with_json

if TYPE_CHECKING:
    import pygame
    from snecs.typedefs import EntityID

    from scripts.engine.core.effect import Effect
    from scripts.engine.internal.action import Skill


#################################################################
# This module is for specifying all defined data sets.
#################################################################


##################### COMPONENT INFO #################################


[docs]@register_dataclass_with_json @dataclass class LightData: """ Data for a light source. Also used to hold and map data from json. """ radius: int = 0 colour: Tuple[int, int, int] = field(default=(0, 0, 0)) alpha: int = 0
[docs]@register_dataclass_with_json @dataclass class TraitSpritesData: """ Possible sprites. """ icon: Optional[pygame.Surface] = None idle: Optional[pygame.Surface] = None attack: Optional[pygame.Surface] = None hit: Optional[pygame.Surface] = None dead: Optional[pygame.Surface] = None move: Optional[pygame.Surface] = None
[docs]@register_dataclass_with_json @dataclass class TraitSpritePathsData: """ Possible sprites paths for a trait. Also used to hold and map data from json. """ render_order: RenderLayer = field(default=RenderLayer.BOTTOM) icon: str = field(default="none") idle: str = field(default="none") attack: str = field(default="none") hit: str = field(default="none") dead: str = field(default="none") move: str = field(default="none")
[docs]@register_dataclass_with_json @dataclass class TraitData: """ Data class for a trait. Also used to hold and map data from json. """ name: str = "none" group: TraitGroupType = TraitGroup.NPC description: str = "none" sprite_paths: TraitSpritePathsData = field(default_factory=TraitSpritePathsData) sight_range: int = 0 vigour: int = 0 clout: int = 0 skullduggery: int = 0 bustle: int = 0 exactitude: int = 0 known_skills: List[str] = field(default_factory=list) permanent_afflictions: List[str] = field(default_factory=list) def __post_init__(self): # update sprite path render order trait_render_order = {"npc": 0, "people": 1, "homeland": 2, "savvy": 3} self.sprite_paths.render_order = trait_render_order.get(self.group)
[docs]@register_dataclass_with_json @dataclass class SecondaryStatModData: """ Data class for secondary stats """ name: str = "none" secondary_stat_type: SecondaryStatType = SecondaryStat.MAX_HEALTH vigour_mod: int = 0 clout_mod: int = 0 skullduggery_mod: int = 0 bustle_mod: int = 0 exactitude_mod: int = 0
####################### ENTITY TYPES ######################
[docs]@register_dataclass_with_json @dataclass class ActorData: """ Data class for an actor. Also used to hold and map data from json. """ key: str = "none" possible_names: List[str] = field(default_factory=list) description: str = "none" position_offsets: List[Tuple[int, int]] = field(default_factory=list) trait_names: List[str] = field(default_factory=list) behaviour_name: str = "none" height: HeightType = Height.MIN def __post_init__(self): # map external str to internal int if isinstance(self.height, str): self.height = getattr(Height, self.height.upper())
[docs]@register_dataclass_with_json @dataclass class ProjectileData: """ Data class for a projectile """ # this will be overwritten or will break, but need defaults to allow passing creator: EntityID = 0 # type: ignore skill_name: str = "none" skill_instance: Optional[Skill] = None # who are we targeting? target_tags: List[TileTagType] = field(default_factory=list) # where are we going? direction: Optional[DirectionType] = None # what does it look like? sprite_paths: TraitSpritePathsData = field(default_factory=TraitSpritePathsData) # how does it travel? speed: ProjectileSpeedType = ProjectileSpeed.SLOW # takes str from json and is converted in post_init travel_method: TravelMethodType = TravelMethod.STANDARD range: int = 1 # how does it interact? terrain_collision: Optional[TerrainCollisionType] = None expiry_type: Optional[ProjectileExpiryType] = None def __post_init__(self): # map external str to internal int if isinstance(self.speed, str): self.speed = getattr(ProjectileSpeed, self.speed.upper())
[docs]@register_dataclass_with_json @dataclass class DelayedSkillData: """ Data class for a Delayed Skill """ # this will be overwritten or will break, but need defaults to allow passing creator: EntityID = 0 # type: ignore skill_name: str = "none" skill_instance: Optional[Skill] = None duration: int = 0 # in rounds sprite_paths: TraitSpritePathsData = TraitSpritePathsData(idle="skills/delayed_skill.png")
[docs]@register_dataclass_with_json @dataclass class TerrainData: """ Data class for terrain. Also used to hold and map data from json. """ name: str = "none" description: str = "none" height: HeightType = Height.MIN blocks_movement: bool = False position_offsets: List[Tuple[int, int]] = field(default_factory=list) sprite_paths: TraitSpritePathsData = field(default_factory=TraitSpritePathsData) reactions: Dict[ReactionTriggerType, ReactionData] = field(default_factory=dict) light: Optional[LightData] = None def __post_init__(self): # map external str to internal int if isinstance(self.height, str): self.height = getattr(Height, self.height.upper())
[docs]@register_dataclass_with_json @dataclass class GodData: """ Data class for a god. If a reaction.reactions is a skill name (a str) then the skill name must also be in known_skills. """ name: str = "none" description: str = "none" attitudes: Dict[ReactionTriggerType, int] = field(default_factory=dict) reactions: Dict[ReactionTriggerType, ReactionData] = field(default_factory=dict)
####################### WORLD GENERATION ######################
[docs]@register_dataclass_with_json @dataclass class MapData: """ Data class for a Map, specifically for generation. A map is a collection of rooms. Defines the rooms on the map, how they are placed and joined up ( with tunnels). Also used to hold and map data from json. """ name: str = "none" key: str = "none" # map size width: int = 0 height: int = 0 # map gen rules min_rooms: int = 0 max_rooms: int = 0 max_tunnel_length: int = 0 min_path_distance_for_shortcut: int = 0 max_room_entrances: int = 0 extra_entrance_chance: int = 0 chance_of_tunnel_winding: int = 0 rooms: Dict[str, float] = field(default_factory=dict) # room name, room weight # aesthetics sprite_paths: Dict[str, str] = field(default_factory=dict) # sprite name, sprite path
[docs]@register_dataclass_with_json @dataclass class RoomConceptData: """ Data class for a RoomConcept. Only used in generation. """ name: str = "none" key: str = "none" # sizes min_width: int = 0 min_height: int = 0 max_width: int = 0 max_height: int = 0 # room parameters design: str = "" max_neighbouring_walls_in_room: int = 0 chance_of_spawning_wall: float = 0.0 # actor generation actors: Dict[str, float] = field(default_factory=dict) # actor name, actor weight min_actors: int = 0 max_actors: int = 0 # aesthetics sprite_paths: Dict[str, str] = field(default_factory=dict) # sprite name, sprite path
######################### ACTIONS ##################################
[docs]@register_dataclass_with_json @dataclass class SkillData: """ Data class for a skill. Also used to hold and map data from json. """ # how do we know it? name: str = "none" description: str = "none" icon_path: str = "none" # when do we use it? cast_tags: List[TileTagType] = field(default_factory=list) target_tags: List[TileTagType] = field(default_factory=list) # what does it cost? resource_type: ResourceType = Resource.STAMINA resource_cost: int = 0 time_cost: int = 0 cooldown: int = 0 # how does it travel from the user? targeting_method: TargetingMethodType = TargetingMethod.TILE target_directions: List[DirectionType] = field(default_factory=list) range: int = 1 # what is the area of effect? shape: ShapeType = Shape.TARGET shape_size: int = 1 # delivery method uses_projectile: bool = False projectile_data: Optional[ProjectileData] = None is_delayed: bool = False delayed_skill_data: Optional[DelayedSkillData] = None # list of types for the skill (used for tracking compatibility with things like blessings) types: List[str] = field(default_factory=list) def __post_init__(self): # convert directions to their constant values _mapped_directions = [] for _direction in self.target_directions: _mapped_directions.append(getattr(Direction, _direction.upper())) self.target_directions = _mapped_directions
[docs]@register_dataclass_with_json @dataclass() class AfflictionData: """ Data class for an Affliction """ name: str = "none" description: str = "none" icon_path: str = "none" category: AfflictionCategoryType = AfflictionCategory.BANE identity_tags: List[EffectTypeType] = field(default_factory=list) triggers: List[ReactionTriggerType] = field(default_factory=list)
[docs]@register_dataclass_with_json @dataclass() class ReactionData: """ Data class for a reaction. """ required_opinion: Optional[int] = None reaction: Union[EffectData, str] = "" # str is skill name chance: int = 100 def __post_init__(self): from scripts.engine.core import utility # ensure clamped between 0-100 self.chance = utility.clamp(self.chance, 0, 100)
################### EFFECTS ###################################################
[docs]@dataclass() class EffectData(ABC): """ Base data class for an effect. """ effect_type: EffectTypeType # not sure but these might come in as effect data which will crash success_effects: List[Effect] = field(default_factory=list) failure_effects: List[Effect] = field(default_factory=list)
[docs]@register_dataclass_with_json @dataclass() class DamageEffectData(EffectData): """ The data for a damage effect. Also used to hold and map data from json. """ effect_type: EffectTypeType = EffectType.DAMAGE stat_to_target: PrimaryStatType = PrimaryStat.EXACTITUDE accuracy: int = 0 potency: float = 1.0 damage: int = 0 damage_type: DamageTypeType = DamageType.MUNDANE mod_stat: PrimaryStatType = PrimaryStat.EXACTITUDE mod_amount: float = 0.0
[docs]@register_dataclass_with_json @dataclass() class MoveEffectData(EffectData): """ The data for a apply affliction effect. Also used to hold and map data from json. """ effect_type: EffectTypeType = EffectType.MOVE direction: DirectionType = Direction.CENTRE move_amount: int = 0
[docs]@register_dataclass_with_json @dataclass() class ApplyAfflictionEffectData(EffectData): """ The data for a apply affliction effect. Also used to hold and map data from json. """ effect_type: EffectTypeType = EffectType.APPLY_AFFLICTION affliction_name: str = "" duration: int = 0
[docs]@register_dataclass_with_json @dataclass() class AffectStatEffectData(EffectData): """ The data for an affect stat effect. Also used to hold and map data from json. """ effect_type: EffectTypeType = EffectType.AFFECT_STAT cause_name: str = "" stat_to_target: PrimaryStatType = PrimaryStat.EXACTITUDE affect_amount: int = 0
[docs]@register_dataclass_with_json @dataclass() class AffectCooldownEffectData(EffectData): """ The data for a apply affliction effect. Also used to hold and map data from json. """ effect_type: EffectTypeType = EffectType.AFFECT_COOLDOWN skill_name: str = "" affect_amount: int = 0
[docs]@register_dataclass_with_json @dataclass() class AlterTerrainEffectData(EffectData): """ The data for an alter terrain effect. Also used to hold and map data from json. """ effect_type: EffectTypeType = EffectType.ALTER_TERRAIN terrain_name: str = "" affect_amount: int = 0
################### CONFIG ###################################################
[docs]@register_dataclass_with_json @dataclass class Dimensions: height: int = 0 width: int = 0
[docs]@register_dataclass_with_json @dataclass class VideoConfigData: base_window: Dimensions = field(default_factory=Dimensions) desired_window: Dimensions = field(default_factory=Dimensions) fps_limit: int = 60
[docs]@register_dataclass_with_json @dataclass class HitInfoData: value: int = 0 modifier: float = 0.0
[docs]@register_dataclass_with_json @dataclass class HitTypeData: graze: HitInfoData = field(default_factory=HitInfoData) hit: HitInfoData = field(default_factory=HitInfoData) crit: HitInfoData = field(default_factory=HitInfoData)
[docs]@register_dataclass_with_json @dataclass class BaseValueData: accuracy: int = 0 damage: int = 0 bustle: int = 0 clout: int = 0 exactitude: int = 0 skullduggery: int = 0 vigour: int = 0 max_health: int = 0 max_stamina: int = 0 resist_astral: int = 0 resist_burn: int = 0 resist_chemical: int = 0 resist_cold: int = 0 resist_mundane: int = 0 rush: int = 0
[docs]@register_dataclass_with_json @dataclass class DefaultValueData: move_cost: int = 0 time_per_round: int = 0 reduced_effectiveness_multi_tile_modifier: float = 0.0
[docs]@register_dataclass_with_json @dataclass class GameConfigData: hit_types: HitTypeData = field(default_factory=HitTypeData) base_values: BaseValueData = field(default_factory=BaseValueData) default_values: DefaultValueData = field(default_factory=DefaultValueData)