Source code for neighborly.components.business

"""Business Components.

This module contains class definitions for components and classes that model businesses
in the settlement and character occupations.

"""

from __future__ import annotations

from typing import Any, Iterable, Mapping, Optional

from neighborly.datetime import SimDate
from neighborly.ecs import Component, GameObject, TagComponent
from neighborly.effects.base_types import Effect
from neighborly.preconditions.base_types import Precondition


[docs]class Occupation(Component): """Information about a character's employment status.""" __slots__ = "_start_date", "_business", "_job_role" _job_role: JobRole """The job role.""" _business: GameObject """The business they work for.""" _start_date: SimDate """The year they started this occupation.""" def __init__( self, job_role: JobRole, business: GameObject, start_date: SimDate ) -> None: """ Parameters ---------- job_role The job role associated with this occupation. business The business that the character is work for. start_date The date they started this occupation. """ super().__init__() self._job_role = job_role self._business = business self._start_date = start_date @property def job_role(self) -> JobRole: """The job role.""" return self._job_role @property def business(self) -> GameObject: """The business they work for.""" return self._business @property def start_date(self) -> SimDate: """The year they started this occupation.""" return self._start_date
[docs] def to_dict(self) -> dict[str, Any]: return { "job_role": self.job_role.gameobject.uid, "business": self.business.uid, "start_date": str(self.start_date), }
def __str__(self) -> str: return ( f"{self.__class__.__name__}(business={self.business}, " f"start_date={self.start_date}, role_id={self.job_role.display_name})" ) def __repr__(self) -> str: return ( f"{self.__class__.__name__}(business={self.business}, " f"start_date={self.start_date}, role_id={self.job_role.display_name})" )
[docs]class Business(Component): """A business where characters work.""" __slots__ = ( "_name", "_owner_role", "_employee_roles", "_district", "_owner", "_employees", ) _name: str """The name of the business.""" _owner_role: JobRole """The role of the business' owner.""" _employee_roles: dict[JobRole, int] """The roles of employees.""" _district: GameObject """The district the residence is in.""" _owner: Optional[GameObject] """Owner and their job role ID.""" _employees: dict[GameObject, JobRole] """Employees mapped to their job role ID.""" def __init__( self, district: GameObject, name: str, owner_role: JobRole, employee_roles: dict[JobRole, int], ) -> None: super().__init__() self._district = district self._name = name self._owner_role = owner_role self._employee_roles = employee_roles self._owner = None self._employees = {} @property def district(self) -> GameObject: """The district the residence is in.""" return self._district @property def name(self) -> str: """The name of the business.""" return self._name @name.setter def name(self, value: str) -> None: """Set the name of the business.""" self._name = value self.gameobject.name = value @property def owner(self) -> Optional[GameObject]: """Owner and their job role ID.""" return self._owner @property def owner_role(self) -> JobRole: """The role of the business' owner.""" return self._owner_role @property def employees(self) -> Mapping[GameObject, JobRole]: """Employees mapped to their job role ID.""" return self._employees
[docs] def add_employee(self, employee: GameObject, role: JobRole) -> None: """Add an employee to the business. Parameters ---------- employee The employee to add. role The employee's job role. """ if self._owner is not None and employee == self._owner: raise ValueError("Business owner cannot be added as an employee.") if employee in self._employees: raise ValueError("Character cannot hold two roles at the same business.") if role not in self._employee_roles: raise ValueError(f"Business does not have employee role with ID: {role}.") remaining_slots = self._employee_roles[role] if remaining_slots == 0: raise ValueError(f"No remaining slots job role with ID: {role}.") self._employee_roles[role] -= 1 self._employees[employee] = role
[docs] def remove_employee(self, employee: GameObject) -> None: """Remove an employee from the business. Parameters ---------- employee The employee to remove. """ if employee not in self._employees: raise ValueError(f"{employee.name} is not an employee of this business.") role = self._employees[employee] del self._employees[employee] self._employee_roles[role] += 1
[docs] def set_owner(self, owner: Optional[GameObject]) -> None: """Set the owner of the business. Parameters ---------- owner The owner of the business. """ self._owner = owner
[docs] def get_open_positions(self) -> Iterable[JobRole]: """Get positions at the business with at least one open slot.""" return [ role_name for role_name, count in self._employee_roles.items() if count > 0 ]
[docs] def to_dict(self) -> dict[str, Any]: return { "name": self.name, "employees": [employee.uid for employee, _ in self._employees.items()], "owner": self._owner.uid if self._owner else -1, "district": self._district.uid, }
[docs]class OpenToPublic(TagComponent): """Tags a business as frequented by characters that don't work there."""
[docs]class PendingOpening(TagComponent): """Tags a business that needs to find a business owner before it can open."""
[docs]class ClosedForBusiness(TagComponent): """Tags a business as closed and no longer active in the simulation."""
[docs]class OpenForBusiness(TagComponent): """Tags a business as actively conducting business in the simulation."""
[docs]class Unemployed(Component): """Tags a character as needing a job, but not having a job.""" __slots__ = ("_timestamp",) _timestamp: SimDate """The date the character became unemployed.""" def __init__(self, timestamp: SimDate) -> None: super().__init__() self._timestamp = timestamp @property def timestamp(self) -> SimDate: """The date the character became unemployed""" return self._timestamp
[docs] def to_dict(self) -> dict[str, Any]: return {"timestamp": str(self.timestamp)}
[docs]class JobRole(Component): """Information about a specific type of job in the world.""" __slots__ = ( "display_name", "description", "job_level", "requirements", "effects", "monthly_effects", "definition_id", ) display_name: str """The name of the role.""" description: str """A description of the role.""" job_level: int """General level of prestige associated with this role.""" requirements: list[Precondition] """Requirement functions for the role.""" effects: list[Effect] """Effects applied when the taking on the role.""" monthly_effects: list[Effect] """Effects applied every month the character has the role.""" definition_id: str """The ID of this job role.""" def __init__( self, display_name: str, description: str, job_level: int, requirements: list[Precondition], effects: list[Effect], monthly_effects: list[Effect], definition_id: str, ) -> None: super().__init__() self.display_name = display_name self.description = description self.job_level = job_level self.requirements = requirements self.effects = effects self.monthly_effects = monthly_effects self.definition_id = definition_id
[docs] def check_requirements(self, gameobject: GameObject) -> bool: """Check if a character passes all the requirements for this job.""" return all([req(gameobject)] for req in self.requirements)
[docs] def to_dict(self) -> dict[str, Any]: return { "display_name": self.display_name, "description": self.description, "job_level": self.job_level, "definition_id": self.definition_id, }