Source code for weapons.restrictions

# ../weapons/restrictions.py

"""Provides player weapon restriction functionality."""

# =============================================================================
# >> IMPORTS
# =============================================================================
# Python Imports
#   Collections
from collections import defaultdict

# Source.Python Imports
#   Core
from core import AutoUnload
from core import GAME_NAME
from core import SOURCE_ENGINE
#   Entities
from entities.constants import INVALID_ENTITY_INDEX
from entities.helpers import edict_from_pointer
from entities.hooks import EntityCondition
from entities.hooks import EntityPreHook
#   Filters
from filters.players import PlayerIter
#   Listeners
from listeners import OnLevelInit
#   Memory
from memory import make_object
#   Players
from players.entity import Player
from players.teams import teams_by_name
from players.teams import teams_by_number
#   Weapons
from weapons.entity import Weapon
from weapons.manager import weapon_manager


# =============================================================================
# >> ALL DECLARATION
# =============================================================================
__all__ = ('_WeaponRestrictionManager',
           'WeaponRestrictionHandler',
           'weapon_restriction_handler',
           'weapon_restriction_manager',
           )


# =============================================================================
# >> CLASSES
# =============================================================================
class _WeaponRestrictionManager(set):
    """Class used to store weapon restriction handlers."""

    def on_player_bumping_weapon(self, player, weapon):
        """Return whether the player is restricted from bumping the weapon.

        :param Player player: The player that is bumping the weapon.
        :param str weapon: The name of the weapon the player is bumping.
        :return: Return whether to block bumping the weapon.
            None is used if the bumping should be allowed.
        :rtype: bool/None
        """
        # Get the weapon's basename
        weapon_name = weapon_manager[weapon].basename

        # Loop through all of the current handlers
        for handler in self:

            # If the current handler wishes to restrict, return such
            value = handler.on_player_bumping_weapon(player, weapon_name)
            if not value and value is not None:
                return True

        # If no handlers restrict the weapon, explicitly return None
        return None

    def on_player_purchasing_weapon(self, player, weapon):
        """Return whether the player is restricted from buying the weapon.

        :param Player player: The player that is purchasing the weapon.
        :param str weapon: The name of the weapon the player is purchasing.
        :return: Return whether to block purchasing the weapon.
            None is used if the purchasing should be allowed.
        :rtype: bool/None
        """
        # This conditional prevents an AttributeError when purchasing armor/nvg
        if weapon not in weapon_manager:
            return None

        # Get the weapon's basename
        weapon_name = weapon_manager[weapon].basename

        # Loop through all of the current handlers
        for handler in self:

            # If the current handler wishes to restrict, return such
            value = handler.on_player_purchasing_weapon(player, weapon_name)
            if not value and value is not None:
                return True

        # If no handlers restrict the weapon, explicitly return None
        return None

    def is_player_restricted(self, player, weapon):
        """Return whether the player is restricted from the weapon.

        :param Player player: The player to check for restriction.
        :param str weapon: The name of the weapon to check for restriction.
        :rtype: bool
        """
        # Get the weapon's basename
        weapon_name = weapon_manager[weapon].basename

        # Loop through all of the current handlers
        for handler in self:

            # If the current handler wishes to restrict, return such
            if handler.is_player_restricted(player, weapon_name):
                return True

        # If no handlers restrict the weapon, explicitly return None
        return None

    def is_team_restricted(self, team, weapon):
        """Return whether the team is restricted from the weapon.

        :param int team: The team to check for restriction.
            Value can also be a str alias for the team (ie t/ct).
        :param str weapon: The name of the weapon to check for restriction.
        :rtype: bool
        """
        # Get the weapon's basename
        weapon_name = weapon_manager[weapon].basename

        # Loop through all of the current handlers
        for handler in self:

            # If the current handler wishes to restrict, return such
            if handler.is_team_restricted(team, weapon_name):
                return True

        # If no handlers restrict the weapon, explicitly return None
        return None

    def add_handler(self, handler):
        """Add the handler to the set.

        :param WeaponRestrictionHandler handler: The handler
            to add to the dictionary.
        """
        self.add(handler)

    def remove_handler(self, handler):
        """Remove the handler from the set.

        :param WeaponRestrictionHandler handler: The handler
            to remove from the dictionary.
        """
        self.discard(handler)

    def clear(self):
        """Clear all handlers of any restrictions."""
        for handler in self:
            handler.clear()

# Get the _WeaponRestrictionManager instance
weapon_restriction_manager = _WeaponRestrictionManager()


class _TeamRestrictions(dict):
    """Class used to store team weapon restrictions."""

    def __init__(self):
        """Store all teams and their aliases for the current game."""
        super().__init__()

        # Store all of the aliases
        self.aliases = {**teams_by_name}
        self.update({x: set() for x in self.aliases.values()})

    def __getitem__(self, item):
        """Return the proper set object for the given team."""
        # Was a known team number given?
        if item in self:
            return super().__getitem__(item)

        # Was a known team alias given?
        if item in self.aliases:
            return super().__getitem__(self.aliases[item])

        # Invalid item was passed
        raise KeyError(
            '"{0}" is neither a team number nor an alias.'.format(item))

    def clear(self):
        for x in self.values():
            x.clear()


[docs]class WeaponRestrictionHandler(AutoUnload): """Class used to handle player and team restrictions."""
[docs] def __init__(self): """Initialize the instance and add it to the manager.""" super().__init__() self.team_restrictions = _TeamRestrictions() self.player_restrictions = defaultdict(set) weapon_restriction_manager.add_handler(self)
[docs] def clear(self): """Remove all team and player restrictions.""" self.player_restrictions.clear() self.team_restrictions.clear()
[docs] def add_player_restrictions(self, player, *weapons): """Add the weapons to the player's restriction set. :param Player player: The player to add the restrictions to. :param str weapons: A weapon or any number of weapons to add as restricted for the player. """ # Get the player's current restrictions current_restrictions = self.player_restrictions[player.userid].copy() # Add the weapons to the player's restrictions self.player_restrictions[player.userid].update([ weapon_manager[weapon].basename for weapon in weapons]) # Get all weapons added to the player's restriction set new_restrictions = self.player_restrictions[ player.userid].difference(current_restrictions) # Notify of each new weapon restriction for weapon in new_restrictions: self.on_player_restriction_added(player, weapon)
[docs] def remove_player_restrictions(self, player, *weapons): """Remove the weapons from the player's restriction set. :param Player player: The player to remove the restrictions from. :param str weapons: A weapon or any number of weapons to remove as restricted for the player. """ self.player_restrictions[player.userid].difference_update([ weapon_manager[weapon].basename for weapon in weapons])
[docs] def add_team_restrictions(self, team, *weapons): """Add the weapons to the team's restriction set. :param int team: The team to add the restrictions to. Value can also be a team alias (ie t/ct). :param str weapons: A weapon or any number of weapons to add as restricted for the team. """ # Get the number of the given team if isinstance(team, str): team = teams_by_name[team] # Get the team's current restrictions current_restrictions = set(self.team_restrictions[team]) # Add the weapons to the team's restrictions self.team_restrictions[team].update([ weapon_manager[weapon].basename for weapon in weapons]) # Get all weapons added to the player's restriction set new_restrictions = self.team_restrictions[ team].difference(current_restrictions) # Notify of each new weapon restriction for weapon in new_restrictions: self.on_team_restriction_added(team, weapon)
[docs] def remove_team_restrictions(self, team, *weapons): """Remove the weapons from the team's restriction set. :param int team: The team to remove the restrictions from. Value can also be a team alias (ie t/ct). :param str weapons: A weapon or any number of weapons to remove as restricted for the team. """ self.team_restrictions[team].difference_update([ weapon_manager[weapon].basename for weapon in weapons])
[docs] def on_player_bumping_weapon(self, player, weapon): """Return whether the player can bump the weapon. :param Player player: The player that is bumping the weapon. :param str weapon: The name of the weapon the player is bumping. :rtype: bool """ return not self.is_player_restricted(player, weapon)
[docs] def on_player_purchasing_weapon(self, player, weapon): """Return whether the player can purchase the weapon. :param Player player: The player that is purchasing the weapon. :param str weapon: The name of the weapon the player is purchasing. :rtype: bool """ return not self.is_player_restricted(player, weapon)
[docs] def is_player_restricted(self, player, weapon): """Return whether the player is restricted from the weapon. :param Player player: The player to check against restriction. :param str weapon: The name of the weapon to check against restriction. :rtype: bool """ # Get the weapon's proper basename weapon_name = weapon_manager[weapon].basename # Is the player restricted from the weapon? if weapon_name in self.player_restrictions[player.userid]: return True # Return whether the player's team is restricted from the weapon return self.is_team_restricted(player.team, weapon_name)
[docs] def is_team_restricted(self, team, weapon): """Return whether the team is restricted from the weapon. :param int team: The team to check for restriction. Value can also be a str alias for the team (ie t/ct). :param str weapon: The name of the weapon to check for restriction. :rtype: bool """ return weapon_manager[weapon].basename in self.team_restrictions[team]
[docs] def on_player_restriction_added(self, player, weapon): """Notify the handler if the player is carrying the weapon. :param Player player: The player that just had a restriction added. :param str weapon: The weapon that was just restricted. """ # Does the player own the weapon type? weapon_index = self._get_player_weapon_index(player, weapon) if weapon_index is INVALID_ENTITY_INDEX: return # Notify the player is carrying the weapon self.on_player_carrying_restricted_weapon(player, Weapon(weapon_index))
[docs] def on_team_restriction_added(self, team, weapon): """Notify the handler of each player on the restricted team. :param int team: The team that just had a restriction added. :param str weapon: The weapon that was just restricted. """ for player in PlayerIter(teams_by_number[team]): self.on_player_restriction_added(player, weapon)
@staticmethod
[docs] def on_player_carrying_restricted_weapon(player, weapon): """Force the player to drop the weapon. :param Player player: The player that is carrying a restricted weapon. :param Weapon weapon: The weapon that the player is carrying and is newly restricted. """ player.drop_weapon(weapon.pointer, None, None)
@staticmethod def _get_player_weapon_index(player, weapon): """Return the index of the weapon if the player is carrying one.""" # Return the index for weapon_index in player.weapon_indexes(weapon_manager[weapon].name): return weapon_index # If no index was found, return invalid return INVALID_ENTITY_INDEX def _unload_instance(self): """Remove the instance from the manager.""" weapon_restriction_manager.remove_handler(self)
# Get the default WeaponRestrictionHandler # instance to be used by the Player class. weapon_restriction_handler = WeaponRestrictionHandler() # ============================================================================= # >> FUNCTION HOOKS # ============================================================================= @EntityPreHook(EntityCondition.is_human_player, 'bump_weapon') @EntityPreHook(EntityCondition.is_bot_player, 'bump_weapon') def _on_weapon_bump(args): """Return whether the player is allowed to pickup the weapon.""" return weapon_restriction_manager.on_player_bumping_weapon( make_object(Player, args[0]), make_object(Weapon, args[1]).weapon_name ) if GAME_NAME in ('cstrike', 'csgo'): @EntityPreHook(EntityCondition.is_player, 'buy_internal') def _on_weapon_purchase(args): """Return whether the player is allowed to purchase the weapon.""" # TODO: # In CS:GO it seems like the weapon isn't passed as a string anymore. # Instead it's rather a pointer that might be NULL. If it's not NULL, # the function sets it to some value: #if ( a3 ) # *(_DWORD *)a3 = v16; return weapon_restriction_manager.on_player_purchasing_weapon( make_object(Player, args[0]), args[1 if SOURCE_ENGINE != 'csgo' else 2] ) # ============================================================================= # >> LISTENERS # ============================================================================= @OnLevelInit def _level_init(map_name): """Clear all restrictions.""" weapon_restriction_manager.clear()