Skip to content

Commit

Permalink
Support Stayover guests (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
TitanNano authored Jun 11, 2024
1 parent ad86d91 commit badd5e8
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 19 deletions.
92 changes: 77 additions & 15 deletions src/control_any_sim/main.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
"""Main entry point for canys mod."""

from __future__ import annotations

import traceback
from typing import Callable
from typing import TYPE_CHECKING, Callable

import services
from distributor.ops import SetIsNpc
from objects.components.sim_inventory_component import (
SimInventoryComponent,
)
from protocolbuffers import Sims_pb2
from server.client import Client
from sims.sim import Sim
from sims.sim_info import SimInfo
from venues.zone_director_residential import (
ZoneDirectorResidentialBase,
)
from zone import Zone

import control_any_sim.cheats
from control_any_sim import ts4_services
from control_any_sim.services.integrity import IntegrityService
from control_any_sim.services.interactions_service import InteractionsService
from control_any_sim.services.selection_group import SelectionGroupService
Expand All @@ -27,6 +31,10 @@
)
from control_any_sim.util.logger import Logger

if TYPE_CHECKING:
from careers.career_enums import CareerCategory
from zone import Zone


@inject_field_to(SimInfo, "is_npc", (SetIsNpc))
def canys_sim_info_is_npc(original: Callable[[SimInfo], bool], self: SimInfo) -> bool:
Expand Down Expand Up @@ -94,14 +102,33 @@ def canys_sim_info_get_is_enabled_in_skewer(
try:
selection_group = SelectionGroupService.get_existing()

if selection_group and selection_group.is_household_npc(self):
if not selection_group:
return original(self, consider_active_sim)

if selection_group.is_household_npc(self):
return False

return original(self, consider_active_sim)
if selection_group.is_custom_sim(self.id):
return True

return original(self, consider_active_sim and can_consider_active_sim())

except BaseException:
Logger.log(traceback.format_exc())
Logger.error(traceback.format_exc())
return original(self, consider_active_sim)


def can_consider_active_sim() -> bool:
"""Check if the active sim is part of the current household."""
client = ts4_services.client_manager().get_active_client()

if client is None:
return True

active_sim_info: SimInfo = client.active_sim_info

return active_sim_info.household_id == services.active_household_id()


@inject_property_to(Sim, "is_selected")
def canys_sim_info_is_selected(original: Callable[[Sim], bool], self: Sim) -> bool:
Expand All @@ -111,15 +138,13 @@ def canys_sim_info_is_selected(original: Callable[[Sim], bool], self: Sim) -> bo
Reduces the logic to wether the Sim is active in the client or not.
"""
try:
active_household_id = services.active_household_id()
client = services.client_manager().get_client_by_household_id(
active_household_id,
)
client = ts4_services.client_manager().get_active_client()

if client is None:
return False

if client is not None:
return self is client.active_sim
return self is client.active_sim

return False
except BaseException:
Logger.log(traceback.format_exc())
return original(self)
Expand All @@ -131,9 +156,6 @@ def canys_init_services(_zone: Zone, household_id: int, _active_sim_id: int) ->
SelectionGroupService.get(household_id)


InteractionsService.bootstrap()


@inject_method_to(ZoneDirectorResidentialBase, "_is_any_sim_always_greeted")
def canys_zone_director_residential_base_is_any_sim_always_greeted(
original: Callable[[ZoneDirectorResidentialBase], bool],
Expand Down Expand Up @@ -189,4 +211,44 @@ def canys_validate_version(_zone: Zone) -> None:
IntegrityService.check_integrety(control_any_sim.__version__)


@inject_method_to(Client, "_get_selector_visual_type")
def canys_client_get_selector_visual_type(
original: Callable[[Client, SimInfo], tuple[int, CareerCategory]],
self: Client,
sim_info: SimInfo,
) -> tuple[int, CareerCategory]:
"""
Override for Client::_get_selector_visual_type method.
Clear selector visual type override for controled sims.
"""
try:
Logger.log("getting selector visual type")

selection_group = SelectionGroupService.get_existing()

(original_type, original_career_category) = original(self, sim_info)

if not selection_group:
return (original_type, original_career_category)

if original_type == Sims_pb2.SimPB.OTHER:
Logger.log("original type is OTHER")
sim_zone_id = sim_info.zone_id

# Override default behavior if the sim is in the current zone.
if sim_zone_id == services.current_zone_id():
Logger.log("sim is in current zone so return NORMAL")
return (Sims_pb2.SimPB.NORMAL, None)

return (original_type, original_career_category)
except Exception as err:
Logger.error(f"{err}")
Logger.error(traceback.format_exc())

return original(self, sim_info)


Logger.log("starting control_any_sim")

InteractionsService.bootstrap()
19 changes: 15 additions & 4 deletions src/control_any_sim/services/selection_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ def _get_instance(
return cls(household_id)

return instance
except BaseException:
except BaseException as err:
Logger.error(f"Failed to deserialize state: {err}")
return cls(household_id)

@property
Expand Down Expand Up @@ -143,9 +144,13 @@ def persist_state(self: Self) -> None:

def update_selectable_sims(self: Self) -> None:
"""Set selection group to all currently selectable sims."""
selectable_sims = self.client.selectable_sims
selectable_sims: list[SimInfo] = self.client.selectable_sims

self.selectable_sims = [sim_info.id for sim_info in selectable_sims]
self.selectable_sims = [
sim_info.id
for sim_info in selectable_sims
if sim_info.household_id != self.household_id
]

def on_zone_teardown(self: Self, _zone: Zone, _client: Client) -> None:
"""
Expand Down Expand Up @@ -210,12 +215,18 @@ def setup_zone(self: Self) -> None:

def is_selectable(self: Self, sim_id: int) -> bool:
"""Check if the sim id is currently selectable."""
test = sim_id in self.selectable_sims
selectable_sims: list[SimInfo] = self.client.selectable_sims

test = any(sim_info.sim_id == sim_id for sim_info in selectable_sims)

Logger.log(f"is sim {sim_id} in selectable list: {test}")

return test

def is_custom_sim(self: Self, sim_info_id: int) -> bool:
"""Test if a sim is one of the custom sims in the group."""
return sim_info_id in self.selectable_sims

def on_active_sim_changed(self: Self, _old_sim: Sim, _new_sim: Sim) -> None:
"""Event handler for when the active sim changes."""
if self.client is None:
Expand Down
23 changes: 23 additions & 0 deletions src/control_any_sim/ts4_services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Wrapper for service module to add types."""

from typing import TYPE_CHECKING

import services
from sims.sim_info_manager import SimInfoManager

from .clientmanager import ClientManager

if TYPE_CHECKING:
import server


def client_manager() -> ClientManager:
"""Typed version of services.client_manager."""
manager: server.clientmanager.ClientManager = services.client_manager()

return ClientManager(manager)


def sim_info_manager() -> SimInfoManager:
"""Typed version of services.sim_info_manager."""
return services.sim_info_manager()
30 changes: 30 additions & 0 deletions src/control_any_sim/ts4_services/clientmanager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""Wrapper for server.clientmanager."""

from __future__ import annotations

from typing import TYPE_CHECKING

import services

if TYPE_CHECKING:
from server import clientmanager
from server.client import Client
from typing_extensions import Self


class ClientManager:
"""Wrapper for server.clientmanager."""

inner: clientmanager.ClientManager

def __init__(self: Self, inner: clientmanager.ClientManager) -> None:
"""Create a new wrapper instance."""
self.inner = inner

def get_client_by_household_id(self: Self, household_id: int) -> Client | None:
"""Get a game client by household id."""
return self.inner.get_client_by_household_id(household_id)

def get_active_client(self: Self) -> Client | None:
"""Get the client of the active household."""
return self.get_client_by_household_id(services.active_household_id())

0 comments on commit badd5e8

Please sign in to comment.